diff --git a/td/telegram/ConfigManager.cpp b/td/telegram/ConfigManager.cpp index 38d1aa1e..d359b3b2 100644 --- a/td/telegram/ConfigManager.cpp +++ b/td/telegram/ConfigManager.cpp @@ -823,9 +823,11 @@ void ConfigManager::process_config(tl_object_ptr config) { if ((config->flags_ & telegram_api::config::SUGGESTED_LANG_CODE_MASK) != 0) { shared_config.set_option_string("suggested_language_pack_id", config->suggested_lang_code_); shared_config.set_option_integer("language_pack_version", config->lang_pack_version_); + shared_config.set_option_integer("base_language_pack_version", config->base_lang_pack_version_); } else { shared_config.set_option_empty("suggested_language_pack_id"); shared_config.set_option_empty("language_pack_version"); + shared_config.set_option_empty("base_language_pack_version"); } } diff --git a/td/telegram/LanguagePackManager.cpp b/td/telegram/LanguagePackManager.cpp index 6b98b37a..f739e944 100644 --- a/td/telegram/LanguagePackManager.cpp +++ b/td/telegram/LanguagePackManager.cpp @@ -46,6 +46,7 @@ struct LanguagePackManager::Language { std::mutex mutex_; std::atomic version_{-1}; std::atomic key_count_{0}; + std::string base_language_code_; bool is_full_ = false; bool was_loaded_full_ = false; bool has_get_difference_query_ = false; @@ -145,6 +146,14 @@ static int32 load_database_language_key_count(SqliteKeyValue *kv) { return to_integer(str_key_count); } +static string load_database_language_base_language_code(SqliteKeyValue *kv) { + CHECK(kv != nullptr); + if (kv->empty()) { + return string(); + } + return kv->get("!base_language_code"); +} + LanguagePackManager::LanguageDatabase *LanguagePackManager::add_language_database(const string &path) { auto it = language_databases_.find(path); if (it != language_databases_.end()) { @@ -169,7 +178,7 @@ LanguagePackManager::LanguageDatabase *LanguagePackManager::add_language_databas } void LanguagePackManager::start_up() { - std::lock_guard lock(language_database_mutex_); + std::lock_guard database_lock(language_database_mutex_); manager_count_++; language_pack_ = G()->shared_config().get_option_string("localization_target"); language_code_ = G()->shared_config().get_option_string("language_pack_id"); @@ -179,12 +188,26 @@ void LanguagePackManager::start_up() { database_ = add_language_database(G()->shared_config().get_option_string("language_pack_database_path")); if (!language_pack_.empty() && !language_code_.empty()) { auto language = add_language(database_, language_pack_, language_code_); - if (!is_custom_language_code(language_code_) && language->version_ == -1) { - get_language_pack_strings(language_code_, vector(), Auto()); + if (language->version_ == -1) { + load_empty_language_pack(language_code_); + } + + std::lock_guard language_lock(language->mutex_); + base_language_code_ = language->base_language_code_; + if (!check_language_code_name(base_language_code_)) { + LOG(ERROR) << "Have invalid base language code name \"" << base_language_code_ << '"'; + base_language_code_.clear(); + } + if (!base_language_code_.empty()) { + auto base_language = add_language(database_, language_pack_, base_language_code_); + if (base_language->version_ == -1) { + load_empty_language_pack(base_language_code_); + } } LOG(INFO) << "Use localization target \"" << language_pack_ << "\" with language pack \"" << language_code_ - << "\" of version " << language->version_.load() << " with database \"" << database_->path_ << '"'; + << "\" based on \"" << base_language_code_ << "\" of version " << language->version_.load() + << " with database \"" << database_->path_ << '"'; } } @@ -220,21 +243,41 @@ void LanguagePackManager::on_language_code_changed() { inc_generation(); } -void LanguagePackManager::on_language_pack_version_changed(int32 new_version) { - if (is_custom_language_code(language_code_) || language_pack_.empty() || language_code_.empty()) { +void LanguagePackManager::on_language_pack_version_changed(bool is_base, int32 new_version) { + if (language_pack_.empty() || language_code_.empty()) { return; } Language *language = get_language(database_, language_pack_, language_code_); int32 version = language == nullptr ? static_cast(-1) : language->version_.load(); if (version == -1) { - get_language_pack_strings(language_code_, vector(), Auto()); + return load_empty_language_pack(language_code_); + } + + if (new_version < 0) { + Slice version_key = is_base ? Slice("base_language_pack_version") : Slice("language_pack_version"); + new_version = G()->shared_config().get_option_integer(version_key, -1); + } + if (new_version <= 0) { return; } - auto new_language_pack_version = - new_version >= 0 ? new_version : G()->shared_config().get_option_integer("language_pack_version", -1); - if (new_language_pack_version <= version) { + string language_code; + if (is_base) { + language_code = base_language_code_; + if (language_code.empty()) { + LOG(ERROR) << "Have no base language, but received new version " << new_version; + return; + } + language = get_language(database_, language_pack_, language_code); + version = language == nullptr ? static_cast(-1) : language->version_.load(); + if (version == -1) { + return load_empty_language_pack(language_code); + } + } else { + language_code = language_code_; + } + if (is_custom_language_code(language_code) || new_version <= version) { return; } @@ -245,7 +288,7 @@ void LanguagePackManager::on_language_pack_version_changed(int32 new_version) { language->has_get_difference_query_ = true; auto request_promise = - PromiseCreator::lambda([actor_id = actor_id(this), language_pack = language_pack_, language_code = language_code_, + PromiseCreator::lambda([actor_id = actor_id(this), language_pack = language_pack_, language_code, from_version = version](Result r_query) mutable { auto r_result = fetch_result(std::move(r_query)); if (r_result.is_error()) { @@ -267,17 +310,20 @@ void LanguagePackManager::on_language_pack_version_changed(int32 new_version) { Promise>()); }); send_with_promise( - G()->net_query_creator().create(create_storer(telegram_api::langpack_getDifference(language_code_, version)), + G()->net_query_creator().create(create_storer(telegram_api::langpack_getDifference(language_code, version)), DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off), std::move(request_promise)); } void LanguagePackManager::on_language_pack_too_long(string language_code) { if (language_code == language_code_) { - return on_language_pack_version_changed(std::numeric_limits::max()); + return on_language_pack_version_changed(false, std::numeric_limits::max()); + } + if (language_code == base_language_code_) { + return on_language_pack_version_changed(true, std::numeric_limits::max()); } LOG(WARNING) << "Receive languagePackTooLong for language " << language_code << ", but use language " - << language_code_; + << language_code_ << " with base language " << base_language_code_; } void LanguagePackManager::on_update_language_pack(tl_object_ptr difference) { @@ -289,7 +335,7 @@ void LanguagePackManager::on_update_language_pack(tl_object_ptrlang_code_ != language_code_) { + if (difference->lang_code_ != language_code_ && difference->lang_code_ != base_language_code_) { LOG(WARNING) << "Ignore difference for language pack " << difference->lang_code_; return; } @@ -298,7 +344,7 @@ void LanguagePackManager::on_update_language_pack(tl_object_ptrlang_code_); int32 version = language == nullptr ? static_cast(-1) : language->version_.load(); if (difference->version_ <= version) { LOG(INFO) << "Skip applying already applied language pack updates"; @@ -306,7 +352,7 @@ void LanguagePackManager::on_update_language_pack(tl_object_ptrfrom_version_) { LOG(INFO) << "Can't apply language pack difference"; - return on_language_pack_version_changed(difference->version_); + return on_language_pack_version_changed(difference->lang_code_ != language_code_, difference->version_); } on_get_language_pack_strings(language_pack_, std::move(difference->lang_code_), difference->version_, true, @@ -316,7 +362,24 @@ void LanguagePackManager::on_update_language_pack(tl_object_ptrshared_config().set_option_empty("language_pack_version"); - on_language_pack_version_changed(std::numeric_limits::max()); + G()->shared_config().set_option_empty("base_language_pack_version"); + + if (!language_pack_.empty() && !language_code_.empty()) { + CHECK(check_language_code_name(language_code_)); + auto language = add_language(database_, language_pack_, language_code_); + on_language_pack_version_changed(false, std::numeric_limits::max()); + + std::lock_guard lock(language->mutex_); + base_language_code_ = language->base_language_code_; + if (!check_language_code_name(base_language_code_)) { + LOG(ERROR) << "Have invalid base language code name \"" << base_language_code_ << '"'; + base_language_code_.clear(); + } + if (!base_language_code_.empty()) { + add_language(database_, language_pack_, base_language_code_); + on_language_pack_version_changed(true, std::numeric_limits::max()); + } + } } LanguagePackManager::Language *LanguagePackManager::get_language(LanguageDatabase *database, @@ -394,6 +457,7 @@ LanguagePackManager::Language *LanguagePackManager::add_language(LanguageDatabas .ensure(); language->version_ = load_database_language_version(&language->kv_); language->key_count_ = load_database_language_key_count(&language->kv_); + language->base_language_code_ = load_database_language_base_language_code(&language->kv_); } code_it = pack->languages_.emplace(language_code, std::move(language)).first; } @@ -638,6 +702,10 @@ void LanguagePackManager::on_get_languages(vectorlang_code_ << " from server"; continue; } + if (is_custom_language_code(language->lang_code_)) { + LOG(ERROR) << "Receive custom language pack ID " << language->lang_code_ << " from server"; + continue; + } results->language_packs_.push_back( make_tl_object(language->lang_code_, language->name_, language->native_name_, 0)); @@ -651,6 +719,7 @@ void LanguagePackManager::on_get_languages(vectorlanguage_packs_) { auto language = add_language(database_, language_pack, language_info->id_); language_info->local_string_count_ = language->key_count_; + // TODO if (language_info->base_language_pack_name != language->base_language_pack_name) update it } if (!only_local) { @@ -759,6 +828,13 @@ void LanguagePackManager::get_language_pack_strings(string language_code, vector } } +void LanguagePackManager::load_empty_language_pack(const string &language_code) { + if (is_custom_language_code(language_code)) { + return; + } + get_language_pack_strings(language_code, vector(), Auto()); +} + static td_api::object_ptr copy_language_pack_string_value( const td_api::LanguagePackStringValue *value) { switch (value->get_id()) { @@ -1049,8 +1125,10 @@ void LanguagePackManager::on_get_language_pack_strings( is_version_changed = true; } } - if (is_version_changed && language_pack == language_pack_ && language_code == language_code_) { - send_closure_later(actor_id(this), &LanguagePackManager::on_language_pack_version_changed, -1); + if (is_version_changed && language_pack == language_pack_ && + (language_code == language_code_ || language_code == base_language_code_)) { + send_closure_later(actor_id(this), &LanguagePackManager::on_language_pack_version_changed, + language_code != language_code_, -1); } if (promise) { @@ -1064,8 +1142,9 @@ void LanguagePackManager::on_failed_get_difference(string language_pack, string std::lock_guard lock(language->mutex_); if (language->has_get_difference_query_) { language->has_get_difference_query_ = false; - if (language_pack == language_pack_ && language_code == language_code_) { - send_closure_later(actor_id(this), &LanguagePackManager::on_language_pack_version_changed, -1); + if (language_pack == language_pack_ && (language_code == language_code_ || language_code == base_language_code_)) { + send_closure_later(actor_id(this), &LanguagePackManager::on_language_pack_version_changed, + language_code != language_code_, -1); } } } @@ -1227,7 +1306,7 @@ void LanguagePackManager::delete_language(string language_code, Promise && if (language_code.empty()) { return promise.set_error(Status::Error(400, "Language pack ID is empty")); } - if (language_code_ == language_code) { + if (language_code_ == language_code || base_language_code_ == language_code) { return promise.set_error(Status::Error(400, "Currently used language pack can't be deleted")); } diff --git a/td/telegram/LanguagePackManager.h b/td/telegram/LanguagePackManager.h index a67e058b..5456828a 100644 --- a/td/telegram/LanguagePackManager.h +++ b/td/telegram/LanguagePackManager.h @@ -46,7 +46,7 @@ class LanguagePackManager : public NetQueryCallback { void on_language_code_changed(); - void on_language_pack_version_changed(int32 new_version); + void on_language_pack_version_changed(bool is_base, int32 new_version); void on_language_pack_too_long(string language_code); @@ -83,6 +83,7 @@ class LanguagePackManager : public NetQueryCallback { string language_pack_; string language_code_; + string base_language_code_; LanguageDatabase *database_ = nullptr; struct PendingQueries { @@ -139,6 +140,8 @@ class LanguagePackManager : public NetQueryCallback { void save_strings_to_database(SqliteKeyValue *kv, int32 new_version, bool new_is_full, int32 new_key_count, vector> strings); + void load_empty_language_pack(const string &language_code); + void on_get_language_pack_strings(string language_pack, string language_code, int32 version, bool is_diff, vector keys, vector> results, Promise> promise); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index c5d7b87f..ad9e7861 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -3430,6 +3430,8 @@ bool Td::is_internal_config_option(Slice name) { switch (name[0]) { case 'a': return name == "auth"; + case 'b': + return name == "base_language_pack_version"; case 'c': return name == "call_ring_timeout_ms" || name == "call_receive_timeout_ms" || name == "channels_read_media_period"; @@ -3499,8 +3501,9 @@ void Td::on_config_option_updated(const string &name) { G()->net_query_dispatcher().update_mtproto_header(); } } else if (name == "language_pack_version") { - send_closure(language_pack_manager_, &LanguagePackManager::on_language_pack_version_changed, -1); - return; + return send_closure(language_pack_manager_, &LanguagePackManager::on_language_pack_version_changed, false, -1); + } else if (name == "base_language_pack_version") { + return send_closure(language_pack_manager_, &LanguagePackManager::on_language_pack_version_changed, true, -1); } else if (name == "notification_group_count_max") { send_closure(notification_manager_actor_, &NotificationManager::on_notification_group_count_max_changed, true); } else if (name == "notification_group_size_max") {