Support new languagePackInfo fields.

GitOrigin-RevId: ad99afe9694458448fddc84803a8beeb33a64a70
This commit is contained in:
levlam 2019-02-11 20:57:35 +03:00
parent 9995e9aa5b
commit 350e7fd6fc
6 changed files with 267 additions and 83 deletions

View File

@ -91,7 +91,7 @@ authorizationStateWaitPhoneNumber = AuthorizationState;
//@description TDLib needs the user's authentication code to finalize authorization @is_registered True, if the user is already registered @terms_of_service Telegram terms of service, which should be accepted before user can continue registration; may be null @code_info Information about the authorization code that was sent //@description TDLib needs the user's authentication code to finalize authorization @is_registered True, if the user is already registered @terms_of_service Telegram terms of service, which should be accepted before user can continue registration; may be null @code_info Information about the authorization code that was sent
authorizationStateWaitCode is_registered:Bool terms_of_service:termsOfService code_info:authenticationCodeInfo = AuthorizationState; authorizationStateWaitCode is_registered:Bool terms_of_service:termsOfService code_info:authenticationCodeInfo = AuthorizationState;
//@description The user has been authorized, but needs to enter a password to start using the application @password_hint Hint for the password; can be empty @has_recovery_email_address True if a recovery email address has been set up //@description The user has been authorized, but needs to enter a password to start using the application @password_hint Hint for the password; may be empty @has_recovery_email_address True if a recovery email address has been set up
//@recovery_email_address_pattern Pattern of the email address to which the recovery email was sent; empty until a recovery email has been sent //@recovery_email_address_pattern Pattern of the email address to which the recovery email was sent; empty until a recovery email has been sent
authorizationStateWaitPassword password_hint:string has_recovery_email_address:Bool recovery_email_address_pattern:string = AuthorizationState; authorizationStateWaitPassword password_hint:string has_recovery_email_address:Bool recovery_email_address_pattern:string = AuthorizationState;
@ -109,7 +109,7 @@ authorizationStateClosing = AuthorizationState;
authorizationStateClosed = AuthorizationState; authorizationStateClosed = AuthorizationState;
//@description Represents the current state of 2-step verification @has_password True, if a 2-step verification password is set @password_hint Hint for the password; can be empty //@description Represents the current state of 2-step verification @has_password True, if a 2-step verification password is set @password_hint Hint for the password; may be empty
//@has_recovery_email_address True, if a recovery email is set @has_passport_data True, if some Telegram Passport elements were saved //@has_recovery_email_address True, if a recovery email is set @has_passport_data True, if some Telegram Passport elements were saved
//@recovery_email_address_code_info Information about the recovery email address to which the confirmation email was sent; may be null //@recovery_email_address_code_info Information about the recovery email address to which the confirmation email was sent; may be null
passwordState has_password:Bool password_hint:string has_recovery_email_address:Bool has_passport_data:Bool recovery_email_address_code_info:emailAddressAuthenticationCodeInfo = PasswordState; passwordState has_password:Bool password_hint:string has_recovery_email_address:Bool has_passport_data:Bool recovery_email_address_code_info:emailAddressAuthenticationCodeInfo = PasswordState;
@ -743,8 +743,8 @@ pageBlockVerticalAlignmentBottom = PageBlockVerticalAlignment;
//@align Horizontal cell content alignment @valign Vertical cell content alignment //@align Horizontal cell content alignment @valign Vertical cell content alignment
pageBlockTableCell text:RichText is_header:Bool colspan:int32 rowspan:int32 align:PageBlockHorizontalAlignment valign:PageBlockVerticalAlignment = PageBlockTableCell; pageBlockTableCell text:RichText is_header:Bool colspan:int32 rowspan:int32 align:PageBlockHorizontalAlignment valign:PageBlockVerticalAlignment = PageBlockTableCell;
//@description Contains information about a related article @url Related article URL @title Article title; can be empty @param_description Article description, can be empty //@description Contains information about a related article @url Related article URL @title Article title; may be empty @param_description Article description; may be empty
//@photo Article photo; may be null @author Article author, can be empty @published_date Article publish date; 0 if unknown //@photo Article photo; may be null @author Article author; may be empty @published_date Article publish date; 0 if unknown
pageBlockRelatedArticle url:string title:string description:string photo:photo author:string published_date:int32 = PageBlockRelatedArticle; pageBlockRelatedArticle url:string title:string description:string photo:photo author:string published_date:int32 = PageBlockRelatedArticle;
@ -1118,7 +1118,7 @@ passportRequiredElement suitable_elements:vector<passportSuitableElement> = Pass
//@description Contains information about a Telegram Passport authorization form that was requested @id Unique identifier of the authorization form //@description Contains information about a Telegram Passport authorization form that was requested @id Unique identifier of the authorization form
//@required_elements Information about the Telegram Passport elements that need to be provided to complete the form //@required_elements Information about the Telegram Passport elements that need to be provided to complete the form
//@privacy_policy_url URL for the privacy policy of the service; can be empty //@privacy_policy_url URL for the privacy policy of the service; may be empty
passportAuthorizationForm id:int32 required_elements:vector<passportRequiredElement> privacy_policy_url:string = PassportAuthorizationForm; passportAuthorizationForm id:int32 required_elements:vector<passportRequiredElement> privacy_policy_url:string = PassportAuthorizationForm;
//@description Contains information about a Telegram Passport elements and corresponding errors @elements Telegram Passport elements @errors Errors in the elements that are already available //@description Contains information about a Telegram Passport elements and corresponding errors @elements Telegram Passport elements @errors Errors in the elements that are already available
@ -1265,7 +1265,7 @@ messageChatSetTtl ttl:int32 = MessageContent;
//@description A non-standard action has happened in the chat @text Message text to be shown in the chat //@description A non-standard action has happened in the chat @text Message text to be shown in the chat
messageCustomServiceAction text:string = MessageContent; messageCustomServiceAction text:string = MessageContent;
//@description A new high score was achieved in a game @game_message_id Identifier of the message with the game, can be an identifier of a deleted message @game_id Identifier of the game, may be different from the games presented in the message with the game @score New score //@description A new high score was achieved in a game @game_message_id Identifier of the message with the game, can be an identifier of a deleted message @game_id Identifier of the game; may be different from the games presented in the message with the game @score New score
messageGameScore game_message_id:int53 game_id:int64 score:int32 = MessageContent; messageGameScore game_message_id:int53 game_id:int64 score:int32 = MessageContent;
//@description A payment has been completed @invoice_message_id Identifier of the message with the corresponding invoice; can be an identifier of a deleted message @currency Currency for the price of the product @total_amount Total price for the product, in the minimal quantity of the currency //@description A payment has been completed @invoice_message_id Identifier of the message with the corresponding invoice; can be an identifier of a deleted message @currency Currency for the price of the product @total_amount Total price for the product, in the minimal quantity of the currency
@ -1804,7 +1804,8 @@ chatEventLogFilters message_edits:Bool message_deletions:Bool message_pins:Bool
//@description An ordinary language pack string @value String value //@description An ordinary language pack string @value String value
languagePackStringValueOrdinary value:string = LanguagePackStringValue; languagePackStringValueOrdinary value:string = LanguagePackStringValue;
//@description A language pack string which has different forms based on the number of some object it mentions @zero_value Value for zero objects @one_value Value for one object @two_value Value for two objects //@description A language pack string which has different forms based on the number of some object it mentions. See https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html for more info
//@zero_value Value for zero objects @one_value Value for one object @two_value Value for two objects
//@few_value Value for few objects @many_value Value for many objects @other_value Default value //@few_value Value for few objects @many_value Value for many objects @other_value Default value
languagePackStringValuePluralized zero_value:string one_value:string two_value:string few_value:string many_value:string other_value:string = LanguagePackStringValue; languagePackStringValuePluralized zero_value:string one_value:string two_value:string few_value:string many_value:string other_value:string = LanguagePackStringValue;
@ -1818,8 +1819,14 @@ languagePackString key:string value:LanguagePackStringValue = LanguagePackString
//@description Contains a list of language pack strings @strings A list of language pack strings //@description Contains a list of language pack strings @strings A list of language pack strings
languagePackStrings strings:vector<languagePackString> = LanguagePackStrings; languagePackStrings strings:vector<languagePackString> = LanguagePackStrings;
//@description Contains information about a language pack @id Unique language pack identifier @name Language name @native_name Name of the language in that language @local_string_count Total number of non-deleted strings from the language pack available locally //@description Contains information about a language pack @id Unique language pack identifier
languagePackInfo id:string name:string native_name:string local_string_count:int32 = LanguagePackInfo; //@base_language_pack_id Identifier of a base language pack; may be empty. If a string is missed in the language pack, then it should be fetched from base language pack. Unsupported in custom language packs
//@name Language name @native_name Name of the language in that language
//@plural_code A language code to be used to apply plural forms. See https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html for more info
//@is_official True, if the language pack is official @is_rtl True, if the language pack strings are RTL @is_beta True, if the language pack is a beta language pack
//@total_string_count Total number of non-deleted strings from the language pack @translated_string_count Total number of translated strings from the language pack
//@local_string_count Total number of non-deleted strings from the language pack available locally @translation_url Link to language translation interface; empty for custom local language packs
languagePackInfo id:string base_language_pack_id:string name:string native_name:string plural_code:string is_official:Bool is_rtl:Bool is_beta:Bool total_string_count:int32 translated_string_count:int32 local_string_count:int32 translation_url:string = LanguagePackInfo;
//@description Contains information about the current localization target @language_packs List of available language packs for this application //@description Contains information about the current localization target @language_packs List of available language packs for this application
localizationTargetInfo language_packs:vector<languagePackInfo> = LocalizationTargetInfo; localizationTargetInfo language_packs:vector<languagePackInfo> = LocalizationTargetInfo;
@ -3132,7 +3139,7 @@ importContacts contacts:vector<contact> = ImportedContacts;
//@description Returns all user contacts //@description Returns all user contacts
getContacts = Users; getContacts = Users;
//@description Searches for the specified query in the first names, last names and usernames of the known user contacts @query Query to search for; can be empty to return all contacts @limit Maximum number of users to be returned //@description Searches for the specified query in the first names, last names and usernames of the known user contacts @query Query to search for; may be empty to return all contacts @limit Maximum number of users to be returned
searchContacts query:string limit:int32 = Users; searchContacts query:string limit:int32 = Users;
//@description Removes users from the contact list @user_ids Identifiers of users to be deleted //@description Removes users from the contact list @user_ids Identifiers of users to be deleted

Binary file not shown.

View File

@ -14,6 +14,7 @@
#include "td/telegram/net/NetQueryDispatcher.h" #include "td/telegram/net/NetQueryDispatcher.h"
#include "td/telegram/Td.h" #include "td/telegram/Td.h"
#include "td/telegram/misc.h"
#include "td/telegram/td_api.h" #include "td/telegram/td_api.h"
#include "td/telegram/td_api.hpp" #include "td/telegram/td_api.hpp"
#include "td/telegram/telegram_api.h" #include "td/telegram/telegram_api.h"
@ -57,11 +58,23 @@ struct LanguagePackManager::Language {
}; };
struct LanguagePackManager::LanguageInfo { struct LanguagePackManager::LanguageInfo {
string name; string name_;
string native_name; string native_name_;
string base_language_code_;
string plural_code_;
bool is_official_ = false;
bool is_rtl_ = false;
bool is_beta_ = false;
int32 total_string_count_ = 0; // TODO move to LanguagePack and calculate as max(total_string_count)
int32 translated_string_count_ = 0;
string translation_url_;
friend bool operator==(const LanguageInfo &lhs, const LanguageInfo &rhs) { friend bool operator==(const LanguageInfo &lhs, const LanguageInfo &rhs) {
return lhs.name == rhs.name && lhs.native_name == rhs.native_name; return lhs.name_ == rhs.name_ && lhs.native_name_ == rhs.native_name_ &&
lhs.base_language_code_ == rhs.base_language_code_ && lhs.plural_code_ == rhs.plural_code_ &&
lhs.is_official_ == rhs.is_official_ && lhs.is_rtl_ == rhs.is_rtl_ && lhs.is_beta_ == rhs.is_beta_ &&
lhs.total_string_count_ == rhs.total_string_count_ &&
lhs.translated_string_count_ == rhs.translated_string_count_ && lhs.translation_url_ == rhs.translation_url_;
} }
}; };
@ -195,7 +208,7 @@ void LanguagePackManager::start_up() {
std::lock_guard<std::mutex> language_lock(language->mutex_); std::lock_guard<std::mutex> language_lock(language->mutex_);
base_language_code_ = language->base_language_code_; base_language_code_ = language->base_language_code_;
if (!check_language_code_name(base_language_code_)) { if (!check_language_code_name(base_language_code_)) {
LOG(ERROR) << "Have invalid base language code name \"" << base_language_code_ << '"'; LOG(ERROR) << "Have invalid base language pack ID \"" << base_language_code_ << '"';
base_language_code_.clear(); base_language_code_.clear();
} }
if (!base_language_code_.empty()) { if (!base_language_code_.empty()) {
@ -372,7 +385,7 @@ void LanguagePackManager::inc_generation() {
std::lock_guard<std::mutex> lock(language->mutex_); std::lock_guard<std::mutex> lock(language->mutex_);
base_language_code_ = language->base_language_code_; base_language_code_ = language->base_language_code_;
if (!check_language_code_name(base_language_code_)) { if (!check_language_code_name(base_language_code_)) {
LOG(ERROR) << "Have invalid base language code name \"" << base_language_code_ << '"'; LOG(ERROR) << "Have invalid base language pack ID \"" << base_language_code_ << '"';
base_language_code_.clear(); base_language_code_.clear();
} }
if (!base_language_code_.empty()) { if (!base_language_code_.empty()) {
@ -420,27 +433,68 @@ LanguagePackManager::Language *LanguagePackManager::add_language(LanguageDatabas
if (!database->database_.empty()) { if (!database->database_.empty()) {
pack->pack_kv_.init_with_connection(database->database_.clone(), get_database_table_name(language_pack, "0")) pack->pack_kv_.init_with_connection(database->database_.clone(), get_database_table_name(language_pack, "0"))
.ensure(); .ensure();
bool need_drop_server = false;
for (auto &lang : pack->pack_kv_.get_all()) { for (auto &lang : pack->pack_kv_.get_all()) {
auto as_bool = [](Slice data) {
if (data == "true") {
return true;
}
if (data != "false") {
LOG(ERROR) << "Have invalid boolean value \"" << data << "\" in the database";
}
return false;
};
if (lang.first == "!server") { if (lang.first == "!server") {
// legacy info format, drop cache
need_drop_server = true;
continue;
}
if (lang.first == "!server2") {
auto all_infos = full_split(lang.second, '\x00'); auto all_infos = full_split(lang.second, '\x00');
if (all_infos.size() % 3 == 0) { if (all_infos.size() % 11 == 0) {
for (size_t i = 0; i < all_infos.size(); i += 3) { for (size_t i = 0; i < all_infos.size(); i += 11) {
LanguageInfo info; LanguageInfo info;
info.name = std::move(all_infos[i + 1]); info.name_ = std::move(all_infos[i + 1]);
info.native_name = std::move(all_infos[i + 2]); info.native_name_ = std::move(all_infos[i + 2]);
info.base_language_code_ = std::move(all_infos[i + 3]);
info.plural_code_ = std::move(all_infos[i + 4]);
info.is_official_ = as_bool(all_infos[i + 5]);
info.is_rtl_ = as_bool(all_infos[i + 6]);
info.is_beta_ = as_bool(all_infos[i + 7]);
info.total_string_count_ = to_integer<int32>(all_infos[i + 8]);
info.translated_string_count_ = to_integer<int32>(all_infos[i + 9]);
info.translation_url_ = std::move(all_infos[i + 10]);
pack->server_language_pack_infos_.emplace_back(std::move(all_infos[i]), std::move(info)); pack->server_language_pack_infos_.emplace_back(std::move(all_infos[i]), std::move(info));
} }
} else { } else {
LOG(ERROR) << "Have wrong language pack info \"" << lang.second << "\" in the database"; LOG(ERROR) << "Have wrong language pack info \"" << lang.second << "\" in the database";
} }
continue; continue;
} }
auto names = split(lang.second, '\x00'); auto all_infos = full_split(lang.second, '\x00');
if (all_infos.size() < 2) {
LOG(ERROR) << "Have wrong custom language pack info \"" << lang.second << '"';
continue;
}
auto &info = pack->custom_language_pack_infos_[lang.first]; auto &info = pack->custom_language_pack_infos_[lang.first];
info.name = std::move(names.first); info.name_ = std::move(all_infos[0]);
info.native_name = std::move(names.second); info.native_name_ = std::move(all_infos[1]);
if (all_infos.size() >= 2) {
CHECK(all_infos.size() == 10);
info.base_language_code_ = std::move(all_infos[2]);
info.plural_code_ = std::move(all_infos[3]);
info.is_official_ = as_bool(all_infos[4]);
info.is_rtl_ = as_bool(all_infos[5]);
info.is_beta_ = as_bool(all_infos[6]);
info.total_string_count_ = to_integer<int32>(all_infos[7]);
info.translated_string_count_ = to_integer<int32>(all_infos[8]);
info.translation_url_ = std::move(all_infos[9]);
}
}
if (need_drop_server) {
pack->pack_kv_.erase("!server");
} }
} }
pack_it = database->language_packs_.emplace(language_pack, std::move(pack)).first; pack_it = database->language_packs_.emplace(language_pack, std::move(pack)).first;
@ -673,10 +727,31 @@ void LanguagePackManager::get_languages(bool only_local,
std::move(request_promise)); std::move(request_promise));
} }
td_api::object_ptr<td_api::languagePackInfo> LanguagePackManager::get_language_pack_info_object(
const string &language_code, const LanguageInfo &info) {
return td_api::make_object<td_api::languagePackInfo>(
language_code, info.base_language_code_, info.name_, info.native_name_, info.plural_code_, info.is_official_,
info.is_rtl_, info.is_beta_, info.total_string_count_, info.translated_string_count_, 0, info.translation_url_);
}
string LanguagePackManager::get_language_info_string(const LanguageInfo &info) {
return PSTRING() << info.name_ << '\x00' << info.native_name_ << '\x00' << info.base_language_code_ << '\x00'
<< info.plural_code_ << '\x00' << info.is_official_ << '\x00' << info.is_rtl_ << '\x00'
<< info.is_beta_ << '\x00' << info.total_string_count_ << '\x00' << info.translated_string_count_
<< '\x00' << info.translation_url_;
}
void LanguagePackManager::on_get_languages(vector<tl_object_ptr<telegram_api::langPackLanguage>> languages, void LanguagePackManager::on_get_languages(vector<tl_object_ptr<telegram_api::langPackLanguage>> languages,
string language_pack, bool only_local, string language_pack, bool only_local,
Promise<td_api::object_ptr<td_api::localizationTargetInfo>> promise) { Promise<td_api::object_ptr<td_api::localizationTargetInfo>> promise) {
auto results = td_api::make_object<td_api::localizationTargetInfo>(); auto results = td_api::make_object<td_api::localizationTargetInfo>();
std::unordered_set<string> added_languages;
auto add_language_info = [&results, &added_languages](const string &language_code, const LanguageInfo &info) {
if (added_languages.insert(language_code).second) {
results->language_packs_.push_back(get_language_pack_info_object(language_code, info));
}
};
{ {
std::lock_guard<std::mutex> packs_lock(database_->mutex_); std::lock_guard<std::mutex> packs_lock(database_->mutex_);
@ -684,13 +759,11 @@ void LanguagePackManager::on_get_languages(vector<tl_object_ptr<telegram_api::la
if (pack_it != database_->language_packs_.end()) { if (pack_it != database_->language_packs_.end()) {
LanguagePack *pack = pack_it->second.get(); LanguagePack *pack = pack_it->second.get();
for (auto &info : pack->custom_language_pack_infos_) { for (auto &info : pack->custom_language_pack_infos_) {
results->language_packs_.push_back( add_language_info(info.first, info.second);
make_tl_object<td_api::languagePackInfo>(info.first, info.second.name, info.second.native_name, 0));
} }
if (only_local) { if (only_local) {
for (auto &info : pack->server_language_pack_infos_) { for (auto &info : pack->server_language_pack_infos_) {
results->language_packs_.push_back( add_language_info(info.first, info.second);
make_tl_object<td_api::languagePackInfo>(info.first, info.second.name, info.second.native_name, 0));
} }
} }
} }
@ -703,23 +776,68 @@ void LanguagePackManager::on_get_languages(vector<tl_object_ptr<telegram_api::la
continue; continue;
} }
if (is_custom_language_code(language->lang_code_)) { if (is_custom_language_code(language->lang_code_)) {
LOG(ERROR) << "Receive custom language pack ID " << language->lang_code_ << " from server"; LOG(ERROR) << "Receive custom language pack ID \"" << language->lang_code_ << "\" from server";
continue; continue;
} }
results->language_packs_.push_back(
make_tl_object<td_api::languagePackInfo>(language->lang_code_, language->name_, language->native_name_, 0));
LanguageInfo info; LanguageInfo info;
info.name = std::move(language->name_); info.name_ = std::move(language->name_);
info.native_name = std::move(language->native_name_); info.native_name_ = std::move(language->native_name_);
info.base_language_code_ = std::move(language->base_lang_code_);
info.plural_code_ = std::move(language->plural_code_);
info.is_official_ = (language->flags_ & telegram_api::langPackLanguage::OFFICIAL_MASK) != 0;
info.is_rtl_ = (language->flags_ & telegram_api::langPackLanguage::RTL_MASK) != 0;
info.is_beta_ = (language->flags_ & telegram_api::langPackLanguage::BETA_MASK) != 0;
info.total_string_count_ = language->strings_count_;
info.translated_string_count_ = language->translated_count_;
info.translation_url_ = language->translations_url_;
if (!check_language_code_name(info.base_language_code_)) {
LOG(ERROR) << "Have invalid base language pack ID \"" << info.base_language_code_ << '"';
info.base_language_code_.clear();
}
if (is_custom_language_code(info.base_language_code_)) {
LOG(ERROR) << "Receive custom base language pack ID \"" << info.base_language_code_ << "\" from server";
info.base_language_code_.clear();
}
if (info.base_language_code_ == language->lang_code_) {
LOG(ERROR) << "Receive language pack \"" << info.base_language_code_ << "\"based on self";
info.base_language_code_.clear();
}
add_language_info(language->lang_code_, info);
all_server_infos.emplace_back(std::move(language->lang_code_), std::move(info)); all_server_infos.emplace_back(std::move(language->lang_code_), std::move(info));
} }
for (auto &language_info : results->language_packs_) { for (auto &language_info : results->language_packs_) {
auto language = add_language(database_, language_pack, language_info->id_); auto language = add_language(database_, language_pack, language_info->id_);
language_info->local_string_count_ = language->key_count_; language_info->local_string_count_ = language->key_count_;
// TODO if (language_info->base_language_pack_name != language->base_language_pack_name) update it SqliteKeyValue *kv = nullptr;
bool was_updated_base_language_code = false;
{
std::lock_guard<std::mutex> lock(language->mutex_);
if (language_info->base_language_pack_id_ != language->base_language_code_) {
language->base_language_code_ = language_info->base_language_pack_id_;
if (language_info->id_ == language_code_) {
base_language_code_ = language->base_language_code_;
was_updated_base_language_code = true;
}
if (!language->kv_.empty()) {
kv = &language->kv_;
}
}
}
if (was_updated_base_language_code) {
G()->shared_config().set_option_empty("base_language_pack_version");
if (!base_language_code_.empty()) {
add_language(database_, language_pack_, base_language_code_);
on_language_pack_version_changed(true, std::numeric_limits<int32>::max());
}
}
if (kv != nullptr) {
std::lock_guard<std::mutex> lock(database_->mutex_);
kv->set("!base_language_code", language_info->base_language_pack_id_);
}
} }
if (!only_local) { if (!only_local) {
@ -732,14 +850,13 @@ void LanguagePackManager::on_get_languages(vector<tl_object_ptr<telegram_api::la
if (!pack->pack_kv_.empty()) { if (!pack->pack_kv_.empty()) {
vector<string> all_strings; vector<string> all_strings;
all_strings.reserve(3 * pack->server_language_pack_infos_.size()); all_strings.reserve(2 * pack->server_language_pack_infos_.size());
for (auto &info : pack->server_language_pack_infos_) { for (auto &info : pack->server_language_pack_infos_) {
all_strings.push_back(info.first); all_strings.push_back(info.first);
all_strings.push_back(info.second.name); all_strings.push_back(get_language_info_string(info.second));
all_strings.push_back(info.second.native_name);
} }
pack->pack_kv_.set("!server", implode(all_strings, '\x00')); pack->pack_kv_.set("!server2", implode(all_strings, '\x00'));
} }
} }
} }
@ -1191,15 +1308,75 @@ Result<tl_object_ptr<telegram_api::LangPackString>> LanguagePackManager::convert
} }
} }
void LanguagePackManager::set_custom_language(string language_code, string language_name, string language_native_name, Result<LanguagePackManager::LanguageInfo> LanguagePackManager::get_language_info(
td_api::languagePackInfo *language_pack_info) {
if (language_pack_info == nullptr) {
return Status::Error(400, "Language pack info must not be empty");
}
if (!clean_input_string(language_pack_info->id_)) {
return Status::Error(400, "Language pack ID must be encoded in UTF-8");
}
if (!clean_input_string(language_pack_info->base_language_pack_id_)) {
return Status::Error(400, "Base language pack ID must be encoded in UTF-8");
}
if (!clean_input_string(language_pack_info->name_)) {
return Status::Error(400, "Language pack name must be encoded in UTF-8");
}
if (!clean_input_string(language_pack_info->native_name_)) {
return Status::Error(400, "Language pack native name must be encoded in UTF-8");
}
if (!clean_input_string(language_pack_info->plural_code_)) {
return Status::Error(400, "Language pack plural code must be encoded in UTF-8");
}
if (!clean_input_string(language_pack_info->translation_url_)) {
return Status::Error(400, "Language pack translation url must be encoded in UTF-8");
}
if (language_pack_info->total_string_count_ < 0) {
language_pack_info->total_string_count_ = 0;
}
if (language_pack_info->translated_string_count_ < 0) {
language_pack_info->translated_string_count_ = 0;
}
if (!check_language_code_name(language_pack_info->id_)) {
return Status::Error(400, "Language pack ID must contain only letters, digits and hyphen");
}
if (is_custom_language_code(language_pack_info->id_)) {
language_pack_info->base_language_pack_id_.clear();
language_pack_info->is_official_ = false;
language_pack_info->is_rtl_ = false;
language_pack_info->is_beta_ = false;
language_pack_info->translation_url_.clear();
}
LanguageInfo info;
info.name_ = std::move(language_pack_info->name_);
info.native_name_ = std::move(language_pack_info->native_name_);
info.base_language_code_ = std::move(language_pack_info->base_language_pack_id_);
info.plural_code_ = std::move(language_pack_info->plural_code_);
info.is_official_ = language_pack_info->is_official_;
info.is_rtl_ = language_pack_info->is_rtl_;
info.is_beta_ = language_pack_info->is_beta_;
info.total_string_count_ = language_pack_info->total_string_count_;
info.translated_string_count_ = language_pack_info->translated_string_count_;
info.translation_url_ = std::move(language_pack_info->translation_url_);
return std::move(info);
}
void LanguagePackManager::set_custom_language(td_api::object_ptr<td_api::languagePackInfo> &&language_pack_info,
vector<tl_object_ptr<td_api::languagePackString>> strings, vector<tl_object_ptr<td_api::languagePackString>> strings,
Promise<Unit> &&promise) { Promise<Unit> &&promise) {
if (language_pack_.empty()) { if (language_pack_.empty()) {
return promise.set_error(Status::Error(400, "Option \"localization_target\" needs to be set first")); return promise.set_error(Status::Error(400, "Option \"localization_target\" needs to be set first"));
} }
if (!check_language_code_name(language_code)) {
return promise.set_error(Status::Error(400, "Language pack ID must contain only letters, digits and hyphen")); auto r_info = get_language_info(language_pack_info.get());
if (r_info.is_error()) {
return promise.set_error(r_info.move_as_error());
} }
auto language_code = std::move(language_pack_info->id_);
if (!is_custom_language_code(language_code)) { if (!is_custom_language_code(language_code)) {
return promise.set_error(Status::Error(400, "Custom language pack ID must begin with 'X'")); return promise.set_error(Status::Error(400, "Custom language pack ID must begin with 'X'"));
} }
@ -1222,23 +1399,25 @@ void LanguagePackManager::set_custom_language(string language_code, string langu
CHECK(pack_it != database_->language_packs_.end()); CHECK(pack_it != database_->language_packs_.end());
LanguagePack *pack = pack_it->second.get(); LanguagePack *pack = pack_it->second.get();
auto &info = pack->custom_language_pack_infos_[language_code]; auto &info = pack->custom_language_pack_infos_[language_code];
info.name = std::move(language_name); info = r_info.move_as_ok();
info.native_name = std::move(language_native_name);
if (!pack->pack_kv_.empty()) { if (!pack->pack_kv_.empty()) {
pack->pack_kv_.set(language_code, PSLICE() << info.name << '\x00' << info.native_name); pack->pack_kv_.set(language_code, get_language_info_string(info));
} }
promise.set_value(Unit()); promise.set_value(Unit());
} }
void LanguagePackManager::edit_custom_language_info(string language_code, string language_name, void LanguagePackManager::edit_custom_language_info(td_api::object_ptr<td_api::languagePackInfo> &&language_pack_info,
string language_native_name, Promise<Unit> &&promise) { Promise<Unit> &&promise) {
if (language_pack_.empty()) { if (language_pack_.empty()) {
return promise.set_error(Status::Error(400, "Option \"localization_target\" needs to be set first")); return promise.set_error(Status::Error(400, "Option \"localization_target\" needs to be set first"));
} }
if (!check_language_code_name(language_code)) {
return promise.set_error(Status::Error(400, "Language pack ID must contain only letters, digits and hyphen")); auto r_info = get_language_info(language_pack_info.get());
if (r_info.is_error()) {
return promise.set_error(r_info.move_as_error());
} }
auto language_code = std::move(language_pack_info->id_);
if (!is_custom_language_code(language_code)) { if (!is_custom_language_code(language_code)) {
return promise.set_error(Status::Error(400, "Custom language pack ID must begin with 'X'")); return promise.set_error(Status::Error(400, "Custom language pack ID must begin with 'X'"));
} }
@ -1252,10 +1431,9 @@ void LanguagePackManager::edit_custom_language_info(string language_code, string
return promise.set_error(Status::Error(400, "Custom language pack is not found")); return promise.set_error(Status::Error(400, "Custom language pack is not found"));
} }
auto &info = language_info_it->second; auto &info = language_info_it->second;
info.name = std::move(language_name); info = r_info.move_as_ok();
info.native_name = std::move(language_native_name);
if (!pack->pack_kv_.empty()) { if (!pack->pack_kv_.empty()) {
pack->pack_kv_.set(language_code, PSLICE() << info.name << '\x00' << info.native_name); pack->pack_kv_.set(language_code, get_language_info_string(info));
} }
promise.set_value(Unit()); promise.set_value(Unit());
@ -1349,12 +1527,10 @@ Status LanguagePackManager::do_delete_language(string language_code) {
language->pluralized_strings_.clear(); language->pluralized_strings_.clear();
language->deleted_strings_.clear(); language->deleted_strings_.clear();
if (is_custom_language_code(language_code)) { if (!pack->pack_kv_.empty()) {
if (!pack->pack_kv_.empty()) { pack->pack_kv_.erase(language_code);
pack->pack_kv_.erase(language_code);
}
pack->custom_language_pack_infos_.erase(language_code);
} }
pack->custom_language_pack_infos_.erase(language_code);
return Status::OK(); return Status::OK();
} }

View File

@ -61,10 +61,10 @@ class LanguagePackManager : public NetQueryCallback {
void on_update_language_pack(tl_object_ptr<telegram_api::langPackDifference> difference); void on_update_language_pack(tl_object_ptr<telegram_api::langPackDifference> difference);
void set_custom_language(string language_code, string language_name, string language_native_name, void set_custom_language(td_api::object_ptr<td_api::languagePackInfo> &&language_pack_info,
vector<tl_object_ptr<td_api::languagePackString>> strings, Promise<Unit> &&promise); vector<tl_object_ptr<td_api::languagePackString>> strings, Promise<Unit> &&promise);
void edit_custom_language_info(string language_code, string language_name, string language_native_name, void edit_custom_language_info(td_api::object_ptr<td_api::languagePackInfo> &&language_pack_info,
Promise<Unit> &&promise); Promise<Unit> &&promise);
void set_custom_language_string(string language_code, tl_object_ptr<td_api::languagePackString> str, void set_custom_language_string(string language_code, tl_object_ptr<td_api::languagePackString> str,
@ -130,6 +130,13 @@ class LanguagePackManager : public NetQueryCallback {
static td_api::object_ptr<td_api::languagePackStrings> get_language_pack_strings_object(Language *language, static td_api::object_ptr<td_api::languagePackStrings> get_language_pack_strings_object(Language *language,
const vector<string> &keys); const vector<string> &keys);
static td_api::object_ptr<td_api::languagePackInfo> get_language_pack_info_object(const string &language_code,
const LanguageInfo &info);
static Result<LanguageInfo> get_language_info(td_api::languagePackInfo *language_pack_info);
static string get_language_info_string(const LanguageInfo &info);
static Result<tl_object_ptr<telegram_api::LangPackString>> convert_to_telegram_api( static Result<tl_object_ptr<telegram_api::LangPackString>> convert_to_telegram_api(
tl_object_ptr<td_api::languagePackString> &&str); tl_object_ptr<td_api::languagePackString> &&str);

View File

@ -6196,29 +6196,16 @@ void Td::on_request(uint64 id, td_api::getLanguagePackStrings &request) {
void Td::on_request(uint64 id, td_api::setCustomLanguagePack &request) { void Td::on_request(uint64 id, td_api::setCustomLanguagePack &request) {
CHECK_IS_USER(); CHECK_IS_USER();
if (request.info_ == nullptr) {
return send_error_raw(id, 400, "Language pack info must not be empty");
}
CLEAN_INPUT_STRING(request.info_->id_);
CLEAN_INPUT_STRING(request.info_->name_);
CLEAN_INPUT_STRING(request.info_->native_name_);
CREATE_OK_REQUEST_PROMISE(); CREATE_OK_REQUEST_PROMISE();
send_closure(language_pack_manager_, &LanguagePackManager::set_custom_language, std::move(request.info_->id_), send_closure(language_pack_manager_, &LanguagePackManager::set_custom_language, std::move(request.info_),
std::move(request.info_->name_), std::move(request.info_->native_name_), std::move(request.strings_), std::move(request.strings_), std::move(promise));
std::move(promise));
} }
void Td::on_request(uint64 id, td_api::editCustomLanguagePackInfo &request) { void Td::on_request(uint64 id, td_api::editCustomLanguagePackInfo &request) {
CHECK_IS_USER(); CHECK_IS_USER();
if (request.info_ == nullptr) {
return send_error_raw(id, 400, "Language pack info must not be empty");
}
CLEAN_INPUT_STRING(request.info_->id_);
CLEAN_INPUT_STRING(request.info_->name_);
CLEAN_INPUT_STRING(request.info_->native_name_);
CREATE_OK_REQUEST_PROMISE(); CREATE_OK_REQUEST_PROMISE();
send_closure(language_pack_manager_, &LanguagePackManager::edit_custom_language_info, std::move(request.info_->id_), send_closure(language_pack_manager_, &LanguagePackManager::edit_custom_language_info, std::move(request.info_),
std::move(request.info_->name_), std::move(request.info_->native_name_), std::move(promise)); std::move(promise));
} }
void Td::on_request(uint64 id, td_api::setCustomLanguagePackString &request) { void Td::on_request(uint64 id, td_api::setCustomLanguagePackString &request) {

View File

@ -485,7 +485,7 @@ class CliClient final : public Actor {
return transform(full_split(user_ids, delimiter), [this](Slice str) { return as_user_id(str); }); return transform(full_split(user_ids, delimiter), [this](Slice str) { return as_user_id(str); });
} }
int32 as_basic_group_id(Slice str) const { static int32 as_basic_group_id(Slice str) {
str = trim(str); str = trim(str);
auto result = to_integer<int32>(str); auto result = to_integer<int32>(str);
if (result < 0) { if (result < 0) {
@ -494,7 +494,7 @@ class CliClient final : public Actor {
return result; return result;
} }
int32 as_supergroup_id(Slice str) const { static int32 as_supergroup_id(Slice str) {
str = trim(str); str = trim(str);
auto result = to_integer<int64>(str); auto result = to_integer<int64>(str);
int64 shift = static_cast<int64>(-1000000000000ll); int64 shift = static_cast<int64>(-1000000000000ll);
@ -504,7 +504,7 @@ class CliClient final : public Actor {
return static_cast<int32>(result); return static_cast<int32>(result);
} }
int32 as_secret_chat_id(Slice str) const { static int32 as_secret_chat_id(Slice str) {
str = trim(str); str = trim(str);
auto result = to_integer<int64>(str); auto result = to_integer<int64>(str);
int64 shift = static_cast<int64>(-2000000000000ll); int64 shift = static_cast<int64>(-2000000000000ll);
@ -882,7 +882,7 @@ class CliClient final : public Actor {
return as_formatted_text(caption, std::move(entities)); return as_formatted_text(caption, std::move(entities));
} }
tl_object_ptr<td_api::NotificationSettingsScope> get_notification_settings_scope(Slice scope) const { static tl_object_ptr<td_api::NotificationSettingsScope> get_notification_settings_scope(Slice scope) {
if (scope == "channels" || scope == "ch") { if (scope == "channels" || scope == "ch") {
return make_tl_object<td_api::notificationSettingsScopeChannelChats>(); return make_tl_object<td_api::notificationSettingsScopeChannelChats>();
} }
@ -988,7 +988,7 @@ class CliClient final : public Actor {
return nullptr; return nullptr;
} }
tl_object_ptr<td_api::TopChatCategory> get_top_chat_category(MutableSlice category) { static tl_object_ptr<td_api::TopChatCategory> get_top_chat_category(MutableSlice category) {
category = trim(category); category = trim(category);
to_lower_inplace(category); to_lower_inplace(category);
if (!category.empty() && category.back() == 's') { if (!category.empty() && category.back() == 's') {
@ -1177,6 +1177,13 @@ class CliClient final : public Actor {
return nullptr; return nullptr;
} }
static td_api::object_ptr<td_api::languagePackInfo> as_language_pack_info(const string &language_code,
const string &name,
const string &native_name) {
return td_api::make_object<td_api::languagePackInfo>(language_code, "test", name, native_name, "en", true, true,
true, -1, 5, 3, "abacaba");
}
static td_api::object_ptr<td_api::Object> execute(tl_object_ptr<td_api::Function> f) { static td_api::object_ptr<td_api::Object> execute(tl_object_ptr<td_api::Function> f) {
if (GET_VERBOSITY_LEVEL() < VERBOSITY_NAME(td_requests)) { if (GET_VERBOSITY_LEVEL() < VERBOSITY_NAME(td_requests)) {
LOG(ERROR) << "Execute request: " << to_string(f); LOG(ERROR) << "Execute request: " << to_string(f);
@ -1859,7 +1866,7 @@ class CliClient final : public Actor {
"DELETED", make_tl_object<td_api::languagePackStringValueDeleted>())); "DELETED", make_tl_object<td_api::languagePackStringValueDeleted>()));
send_request(make_tl_object<td_api::setCustomLanguagePack>( send_request(make_tl_object<td_api::setCustomLanguagePack>(
make_tl_object<td_api::languagePackInfo>(language_code, name, native_name, 3), std::move(strings))); as_language_pack_info(language_code, name, native_name), std::move(strings)));
} else if (op == "eclpi") { } else if (op == "eclpi") {
string language_code; string language_code;
string name; string name;
@ -1868,8 +1875,8 @@ class CliClient final : public Actor {
std::tie(language_code, args) = split(args); std::tie(language_code, args) = split(args);
std::tie(name, native_name) = split(args); std::tie(name, native_name) = split(args);
send_request(make_tl_object<td_api::editCustomLanguagePackInfo>( send_request(
make_tl_object<td_api::languagePackInfo>(language_code, name, native_name, 3))); make_tl_object<td_api::editCustomLanguagePackInfo>(as_language_pack_info(language_code, name, native_name)));
} else if (op == "sclpsv" || op == "sclpsp" || op == "sclpsd") { } else if (op == "sclpsv" || op == "sclpsp" || op == "sclpsd") {
string language_code; string language_code;
string key; string key;