mirror of https://github.com/procxx/kepka.git
Use QString + Lang::Tag() instead of Lang::String.
This commit is contained in:
parent
110e7c8074
commit
2334ba1fe1
|
@ -88,11 +88,11 @@ void AboutBox::keyPressEvent(QKeyEvent *e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QString telegramFaqLink() {
|
QString telegramFaqLink() {
|
||||||
QString result = qsl("https://telegram.org/faq");
|
auto result = qsl("https://telegram.org/faq");
|
||||||
if (cLang() > languageDefault && cLang() < languageCount) {
|
if (cLang() > languageDefault && cLang() < languageCount) {
|
||||||
const char *code = LanguageCodes[cLang()].c_str();
|
const char *code = LanguageCodes[cLang()].c_str();
|
||||||
if (qstr("de") == code || qstr("es") == code || qstr("it") == code || qstr("ko") == code) {
|
if (qstr("de") == code || qstr("es") == code || qstr("it") == code || qstr("ko") == code) {
|
||||||
result += qsl("/") + code;
|
result += '/' + code;
|
||||||
} else if (qstr("pt_BR") == code) {
|
} else if (qstr("pt_BR") == code) {
|
||||||
result += qsl("/br");
|
result += qsl("/br");
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,9 +46,9 @@ void LanguageBox::prepare() {
|
||||||
y += _langs.back()->heightNoMargins() + st::boxOptionListSkip;
|
y += _langs.back()->heightNoMargins() + st::boxOptionListSkip;
|
||||||
}
|
}
|
||||||
for (auto i = 0; i != languageCount; ++i) {
|
for (auto i = 0; i != languageCount; ++i) {
|
||||||
LangLoaderResult result;
|
Lang::FileParser::Result result;
|
||||||
if (i) {
|
if (i) {
|
||||||
LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[i].c_str() + qsl(".strings"), { lng_language_name });
|
Lang::FileParser loader(qsl(":/langs/lang_") + LanguageCodes[i].c_str() + qsl(".strings"), { lng_language_name });
|
||||||
result = loader.found();
|
result = loader.found();
|
||||||
} else {
|
} else {
|
||||||
result.insert(lng_language_name, langOriginal(lng_language_name));
|
result.insert(lng_language_name, langOriginal(lng_language_name));
|
||||||
|
@ -66,12 +66,12 @@ void LanguageBox::prepare() {
|
||||||
void LanguageBox::mousePressEvent(QMouseEvent *e) {
|
void LanguageBox::mousePressEvent(QMouseEvent *e) {
|
||||||
if ((e->modifiers() & Qt::CTRL) && (e->modifiers() & Qt::ALT) && (e->modifiers() & Qt::SHIFT)) {
|
if ((e->modifiers() & Qt::CTRL) && (e->modifiers() & Qt::ALT) && (e->modifiers() & Qt::SHIFT)) {
|
||||||
for (int32 i = 1; i < languageCount; ++i) {
|
for (int32 i = 1; i < languageCount; ++i) {
|
||||||
LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[i].c_str() + qsl(".strings"), { lngkeys_cnt });
|
Lang::FileParser loader(qsl(":/langs/lang_") + LanguageCodes[i].c_str() + qsl(".strings"), { kLangKeysCount });
|
||||||
if (!loader.errors().isEmpty()) {
|
if (!loader.errors().isEmpty()) {
|
||||||
Ui::show(Box<InformBox>(qsl("Lang \"") + LanguageCodes[i].c_str() + qsl("\" error :(\n\nError: ") + loader.errors()));
|
Ui::show(Box<InformBox>(qsl("Lang \"") + LanguageCodes[i].c_str() + qsl("\" error :(\n\nError: ") + loader.errors()));
|
||||||
return;
|
return;
|
||||||
} else if (!loader.warnings().isEmpty()) {
|
} else if (!loader.warnings().isEmpty()) {
|
||||||
QString warn = loader.warnings();
|
auto warn = loader.warnings();
|
||||||
if (warn.size() > 256) warn = warn.mid(0, 253) + qsl("...");
|
if (warn.size() > 256) warn = warn.mid(0, 253) + qsl("...");
|
||||||
Ui::show(Box<InformBox>(qsl("Lang \"") + LanguageCodes[i].c_str() + qsl("\" warnings :(\n\nWarnings: ") + warn));
|
Ui::show(Box<InformBox>(qsl("Lang \"") + LanguageCodes[i].c_str() + qsl("\" warnings :(\n\nWarnings: ") + warn));
|
||||||
return;
|
return;
|
||||||
|
@ -88,12 +88,12 @@ void LanguageBox::languageChanged(int languageId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LangLoaderResult result;
|
Lang::FileParser::Result result;
|
||||||
if (languageId > 0) {
|
if (languageId > 0) {
|
||||||
LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[languageId].c_str() + qsl(".strings"), { lng_sure_save_language, lng_cancel, lng_box_ok });
|
Lang::FileParser loader(qsl(":/langs/lang_") + LanguageCodes[languageId].c_str() + qsl(".strings"), { lng_sure_save_language, lng_cancel, lng_box_ok });
|
||||||
result = loader.found();
|
result = loader.found();
|
||||||
} else if (languageId == languageTest) {
|
} else if (languageId == languageTest) {
|
||||||
LangLoaderPlain loader(cLangFile(), { lng_sure_save_language, lng_cancel, lng_box_ok });
|
Lang::FileParser loader(cLangFile(), { lng_sure_save_language, lng_cancel, lng_box_ok });
|
||||||
result = loader.found();
|
result = loader.found();
|
||||||
}
|
}
|
||||||
auto text = result.value(lng_sure_save_language, langOriginal(lng_sure_save_language)),
|
auto text = result.value(lng_sure_save_language, langOriginal(lng_sure_save_language)),
|
||||||
|
|
|
@ -118,71 +118,71 @@ Generator::Generator(const Langpack &langpack, const QString &destBasePath, cons
|
||||||
|
|
||||||
bool Generator::writeHeader() {
|
bool Generator::writeHeader() {
|
||||||
header_ = std::make_unique<common::CppFile>(basePath_ + ".h", project_);
|
header_ = std::make_unique<common::CppFile>(basePath_ + ".h", project_);
|
||||||
header_->stream() << "\
|
header_->include("lang/lang_tag.h").newline().pushNamespace("Lang").stream() << "\
|
||||||
class LangString : public QString {\n\
|
|
||||||
public:\n\
|
|
||||||
LangString() = default;\n\
|
|
||||||
LangString(const QString &str) : QString(str) {\n\
|
|
||||||
}\n\
|
|
||||||
LangString &operator=(const QString &str) {\n\
|
|
||||||
QString::operator=(str);\n\
|
|
||||||
return *this;\n\
|
|
||||||
}\n\
|
|
||||||
\n\
|
\n\
|
||||||
LangString tag(ushort tag, const QString &replacement);\n\
|
constexpr auto kTagsCount = " << langpack_.tags.size() << ";\n\
|
||||||
\n\
|
constexpr auto kTagsPluralVariants = " << kMaxPluralVariants << ";\n\
|
||||||
};\n\
|
|
||||||
\n\
|
|
||||||
LangString langCounted(ushort key0, ushort tag, float64 value);\n\
|
|
||||||
\n";
|
\n";
|
||||||
|
|
||||||
|
header_->popNamespace().newline();
|
||||||
auto index = 0;
|
auto index = 0;
|
||||||
for (auto &tag : langpack_.tags) {
|
for (auto &tag : langpack_.tags) {
|
||||||
header_->stream() << "enum lngtag_" << tag.tag << " { lt_" << tag.tag << " = " << index++ << " };\n";
|
header_->stream() << "enum lngtag_" << tag.tag << " { lt_" << tag.tag << " = " << index++ << " };\n";
|
||||||
}
|
}
|
||||||
header_->stream() << "\
|
header_->stream() << "\
|
||||||
\n\
|
\n\
|
||||||
constexpr auto lngtags_cnt = " << langpack_.tags.size() << ";\n\
|
|
||||||
constexpr auto lngtags_max_counted_values = " << kMaxPluralVariants << ";\n\
|
|
||||||
\n\
|
|
||||||
enum LangKey {\n";
|
enum LangKey {\n";
|
||||||
for (auto &entry : langpack_.entries) {
|
for (auto &entry : langpack_.entries) {
|
||||||
header_->stream() << "\t" << getFullKey(entry) << ",\n";
|
header_->stream() << "\t" << getFullKey(entry) << ",\n";
|
||||||
}
|
}
|
||||||
header_->stream() << "\
|
header_->stream() << "\
|
||||||
\n\
|
\n\
|
||||||
lngkeys_cnt,\n\
|
kLangKeysCount,\n\
|
||||||
};\n\
|
};\n\
|
||||||
\n\
|
\n\
|
||||||
LangString lang(LangKey key);\n\
|
QString lang(LangKey key);\n\
|
||||||
\n\
|
\n\
|
||||||
LangString langOriginal(LangKey key);\n\
|
QString langOriginal(LangKey key);\n\
|
||||||
\n";
|
\n";
|
||||||
for (auto &entry : langpack_.entries) {
|
for (auto &entry : langpack_.entries) {
|
||||||
if (!entry.tags.empty()) {
|
if (!entry.tags.empty()) {
|
||||||
auto &key = entry.key;
|
auto &key = entry.key;
|
||||||
auto params = QStringList();
|
auto params = QStringList();
|
||||||
auto invokations = QStringList();
|
auto applyTags = QStringList();
|
||||||
for (auto &tagData : entry.tags) {
|
for (auto &tagData : entry.tags) {
|
||||||
auto &tag = tagData.tag;
|
auto &tag = tagData.tag;
|
||||||
auto isPlural = isTagPlural(key, tag);
|
auto isPlural = isTagPlural(key, tag);
|
||||||
params.push_back("lngtag_" + tag + ", " + (isPlural ? "float64 " : "const QString &") + tag + "__val");
|
params.push_back("lngtag_" + tag + ", " + (isPlural ? "float64 " : "const QString &") + tag + "__val");
|
||||||
invokations.push_back("tag(lt_" + tag + ", " + (isPlural ? ("langCounted(" + key + "__" + tag + "0, lt_" + tag + ", " + tag + "__val)") : (tag + "__val")) + ")");
|
applyTags.push_back("\tresult = Lang::Tag(result, lt_" + tag + ", " + (isPlural ? ("Lang::Plural(" + key + "__" + tag + "0, lt_" + tag + ", " + tag + "__val)") : (tag + "__val")) + ");");
|
||||||
}
|
}
|
||||||
header_->stream() << "\
|
header_->stream() << "\
|
||||||
inline LangString " << entry.key << "(" << params.join(QString(", ")) << ") {\n\
|
inline QString " << entry.key << "(" << params.join(QString(", ")) << ") {\n\
|
||||||
return lang(" << entry.key << "__tagged)." << invokations.join('.') << ";\n\
|
auto result = lang(" << entry.key << "__tagged);\n\
|
||||||
|
" << applyTags.join('\n') << ";\n\
|
||||||
|
return result;\n\
|
||||||
}\n\
|
}\n\
|
||||||
\n";
|
\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
header_->pushNamespace("Lang").stream() << "\
|
||||||
|
\n\
|
||||||
|
const char *GetKeyName(LangKey key);\n\
|
||||||
|
ushort GetTagIndex(QLatin1String tag);\n\
|
||||||
|
LangKey GetKeyIndex(QLatin1String key);\n\
|
||||||
|
LangKey GetSubkeyIndex(LangKey key, ushort tag, ushort index);\n\
|
||||||
|
bool IsTagReplaced(LangKey key, ushort tag);\n\
|
||||||
|
void FeedKeyValue(LangKey key, const QString &value);\n\
|
||||||
|
\n";
|
||||||
|
|
||||||
return header_->finalize();
|
return header_->finalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Generator::writeSource() {
|
bool Generator::writeSource() {
|
||||||
source_ = std::make_unique<common::CppFile>(basePath_ + ".cpp", project_);
|
source_ = std::make_unique<common::CppFile>(basePath_ + ".cpp", project_);
|
||||||
|
|
||||||
source_->include("lang/lang_keys.h").pushNamespace().stream() << "\
|
source_->include("lang/lang_keys.h").pushNamespace("Lang").pushNamespace().stream() << "\
|
||||||
const char *_langKeyNames[lngkeys_cnt] = {\n\
|
const char *KeyNames[kLangKeysCount] = {\n\
|
||||||
\n";
|
\n";
|
||||||
for (auto &entry : langpack_.entries) {
|
for (auto &entry : langpack_.entries) {
|
||||||
source_->stream() << "\"" << entry.key << "\",\n";
|
source_->stream() << "\"" << entry.key << "\",\n";
|
||||||
|
@ -191,15 +191,15 @@ const char *_langKeyNames[lngkeys_cnt] = {\n\
|
||||||
\n\
|
\n\
|
||||||
};\n\
|
};\n\
|
||||||
\n\
|
\n\
|
||||||
LangString _langValues[lngkeys_cnt], _langValuesOriginal[lngkeys_cnt];\n\
|
QString Values[kLangKeysCount], OriginalValues[kLangKeysCount];\n\
|
||||||
\n\
|
\n\
|
||||||
void set(LangKey key, const QString &val) {\n\
|
void set(LangKey key, const QString &val) {\n\
|
||||||
_langValues[key] = val;\n\
|
Values[key] = val;\n\
|
||||||
}\n\
|
}\n\
|
||||||
\n\
|
\n\
|
||||||
class LangInit {\n\
|
class Initializer {\n\
|
||||||
public:\n\
|
public:\n\
|
||||||
LangInit() {\n";
|
Initializer() {\n";
|
||||||
for (auto &entry : langpack_.entries) {
|
for (auto &entry : langpack_.entries) {
|
||||||
source_->stream() << "\t\tset(" << getFullKey(entry) << ", QString::fromUtf8(" << stringToEncodedString(entry.value) << "));\n";
|
source_->stream() << "\t\tset(" << getFullKey(entry) << ", QString::fromUtf8(" << stringToEncodedString(entry.value) << "));\n";
|
||||||
}
|
}
|
||||||
|
@ -208,24 +208,16 @@ public:\n\
|
||||||
\n\
|
\n\
|
||||||
};\n\
|
};\n\
|
||||||
\n\
|
\n\
|
||||||
LangInit _langInit;\n\
|
Initializer Instance;\n\
|
||||||
\n";
|
\n";
|
||||||
|
|
||||||
source_->popNamespace().stream() << "\
|
source_->popNamespace().stream() << "\
|
||||||
\n\
|
\n\
|
||||||
LangString lang(LangKey key) {\n\
|
const char *GetKeyName(LangKey key) {\n\
|
||||||
return (key < 0 || key > lngkeys_cnt) ? QString() : _langValues[key];\n\
|
return (key < 0 || key >= kLangKeysCount) ? \"\" : KeyNames[key];\n\
|
||||||
}\n\
|
}\n\
|
||||||
\n\
|
\n\
|
||||||
LangString langOriginal(LangKey key) {\n\
|
ushort GetTagIndex(QLatin1String tag) {\n\
|
||||||
return (key < 0 || key > lngkeys_cnt || _langValuesOriginal[key] == qsl(\"{}\")) ? QString() : (_langValuesOriginal[key].isEmpty() ? _langValues[key] : _langValuesOriginal[key]);\n\
|
|
||||||
}\n\
|
|
||||||
\n\
|
|
||||||
const char *langKeyName(LangKey key) {\n\
|
|
||||||
return (key < 0 || key > lngkeys_cnt) ? \"\" : _langKeyNames[key];\n\
|
|
||||||
}\n\
|
|
||||||
\n\
|
|
||||||
ushort LangLoader::tagIndex(QLatin1String tag) const {\n\
|
|
||||||
auto size = tag.size();\n\
|
auto size = tag.size();\n\
|
||||||
auto data = tag.data();\n";
|
auto data = tag.data();\n";
|
||||||
|
|
||||||
|
@ -236,12 +228,12 @@ ushort LangLoader::tagIndex(QLatin1String tag) const {\n\
|
||||||
|
|
||||||
writeSetSearch(tagsSet, [](const QString &tag) {
|
writeSetSearch(tagsSet, [](const QString &tag) {
|
||||||
return "lt_" + tag;
|
return "lt_" + tag;
|
||||||
}, "lngtags_cnt");
|
}, "kTagsCount");
|
||||||
|
|
||||||
source_->stream() << "\
|
source_->stream() << "\
|
||||||
}\n\
|
}\n\
|
||||||
\n\
|
\n\
|
||||||
LangKey LangLoader::keyIndex(QLatin1String key) const {\n\
|
LangKey GetKeyIndex(QLatin1String key) {\n\
|
||||||
auto size = key.size();\n\
|
auto size = key.size();\n\
|
||||||
auto data = key.data();\n";
|
auto data = key.data();\n";
|
||||||
|
|
||||||
|
@ -262,12 +254,41 @@ LangKey LangLoader::keyIndex(QLatin1String key) const {\n\
|
||||||
writeSetSearch(keysSet, [&taggedKeys](const QString &key) {
|
writeSetSearch(keysSet, [&taggedKeys](const QString &key) {
|
||||||
auto it = taggedKeys.find(key);
|
auto it = taggedKeys.find(key);
|
||||||
return (it != taggedKeys.end()) ? it->second : key;
|
return (it != taggedKeys.end()) ? it->second : key;
|
||||||
}, "lngkeys_cnt");
|
}, "kLangKeysCount");
|
||||||
|
|
||||||
source_->stream() << "\
|
source_->stream() << "\
|
||||||
}\n\
|
}\n\
|
||||||
\n\
|
\n\
|
||||||
bool LangLoader::tagReplaced(LangKey key, ushort tag) const {\n\
|
LangKey GetSubkeyIndex(LangKey key, ushort tag, ushort index) {\n\
|
||||||
|
if (index >= kTagsPluralVariants) return kLangKeysCount;\n\
|
||||||
|
\n\
|
||||||
|
switch (key) {\n";
|
||||||
|
|
||||||
|
for (auto &entry : langpack_.entries) {
|
||||||
|
auto cases = QString();
|
||||||
|
for (auto &tag : entry.tags) {
|
||||||
|
if (isTagPlural(entry.key, tag.tag)) {
|
||||||
|
cases += "\t\t\tcase lt_" + tag.tag + ": return LangKey(" + entry.key + "__" + tag.tag + "0 + index);\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cases.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
source_->stream() << "\
|
||||||
|
case " << entry.key << "__tagged: {\n\
|
||||||
|
switch (tag) {\n\
|
||||||
|
" << cases << "\
|
||||||
|
}\n\
|
||||||
|
} break;\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
source_->stream() << "\
|
||||||
|
}\n\
|
||||||
|
\n\
|
||||||
|
return kLangKeysCount;\n\
|
||||||
|
}\n\
|
||||||
|
\n\
|
||||||
|
bool IsTagReplaced(LangKey key, ushort tag) {\n\
|
||||||
switch (key) {\n";
|
switch (key) {\n";
|
||||||
|
|
||||||
for (auto &entry : langpack_.entries) {
|
for (auto &entry : langpack_.entries) {
|
||||||
|
@ -293,45 +314,23 @@ bool LangLoader::tagReplaced(LangKey key, ushort tag) const {\n\
|
||||||
return false;\n\
|
return false;\n\
|
||||||
}\n\
|
}\n\
|
||||||
\n\
|
\n\
|
||||||
LangKey LangLoader::subkeyIndex(LangKey key, ushort tag, ushort index) const {\n\
|
void FeedKeyValue(LangKey key, const QString &value) {\n\
|
||||||
if (index >= lngtags_max_counted_values) return lngkeys_cnt;\n\
|
Expects(key >= 0 && key < kLangKeysCount);\n\
|
||||||
\n\
|
if (OriginalValues[key].isEmpty()) {\n\
|
||||||
switch (key) {\n";
|
OriginalValues[key] = Values[key].isEmpty() ? qsl(\"{}\") : Values[key];\n\
|
||||||
|
|
||||||
for (auto &entry : langpack_.entries) {
|
|
||||||
auto cases = QString();
|
|
||||||
for (auto &tag : entry.tags) {
|
|
||||||
if (isTagPlural(entry.key, tag.tag)) {
|
|
||||||
cases += "\t\t\tcase lt_" + tag.tag + ": return LangKey(" + entry.key + "__" + tag.tag + "0 + index);\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (cases.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
source_->stream() << "\
|
|
||||||
case " << entry.key << "__tagged: {\n\
|
|
||||||
switch (tag) {\n\
|
|
||||||
" << cases << "\
|
|
||||||
}\n\
|
|
||||||
} break;\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
source_->stream() << "\
|
|
||||||
}\n\
|
}\n\
|
||||||
|
Values[key] = value;\n\
|
||||||
|
}\n\
|
||||||
|
\n";
|
||||||
|
|
||||||
|
source_->popNamespace().stream() << "\
|
||||||
\n\
|
\n\
|
||||||
return lngkeys_cnt;\n\
|
QString lang(LangKey key) {\n\
|
||||||
|
return (key < 0 || key >= kLangKeysCount) ? QString() : Lang::Values[key];\n\
|
||||||
}\n\
|
}\n\
|
||||||
\n\
|
\n\
|
||||||
bool LangLoader::feedKeyValue(LangKey key, const QString &value) {\n\
|
QString langOriginal(LangKey key) {\n\
|
||||||
if (key < lngkeys_cnt) {\n\
|
return (key < 0 || key >= kLangKeysCount || Lang::OriginalValues[key] == qsl(\"{}\")) ? QString() : (Lang::OriginalValues[key].isEmpty() ? Lang::Values[key] : Lang::OriginalValues[key]);\n\
|
||||||
_found[key] = 1;\n\
|
|
||||||
if (_langValuesOriginal[key].isEmpty()) {\n\
|
|
||||||
_langValuesOriginal[key] = _langValues[key].isEmpty() ? qsl(\"{}\") : _langValues[key];\n\
|
|
||||||
}\n\
|
|
||||||
_langValues[key] = value;\n\
|
|
||||||
return true;\n\
|
|
||||||
}\n\
|
|
||||||
return false;\n\
|
|
||||||
}\n";
|
}\n";
|
||||||
|
|
||||||
return source_->finalize();
|
return source_->finalize();
|
||||||
|
|
|
@ -353,7 +353,7 @@ bool HistoryHider::offerPeer(PeerId peer) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
_offered = App::peer(peer);
|
_offered = App::peer(peer);
|
||||||
LangString phrase;
|
auto phrase = QString();
|
||||||
QString recipient = _offered->isUser() ? _offered->name : '\xAB' + _offered->name + '\xBB';
|
QString recipient = _offered->isUser() ? _offered->name : '\xAB' + _offered->name + '\xBB';
|
||||||
if (_sharedContact) {
|
if (_sharedContact) {
|
||||||
phrase = lng_forward_share_contact(lt_recipient, recipient);
|
phrase = lng_forward_share_contact(lt_recipient, recipient);
|
||||||
|
|
|
@ -64,8 +64,8 @@ Widget::Widget(QWidget *parent) : TWidget(parent)
|
||||||
if (cLang() == languageDefault) {
|
if (cLang() == languageDefault) {
|
||||||
auto systemLangId = Sandbox::LangSystem();
|
auto systemLangId = Sandbox::LangSystem();
|
||||||
if (systemLangId != languageDefault) {
|
if (systemLangId != languageDefault) {
|
||||||
LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[systemLangId].c_str() + qsl(".strings"), { lng_switch_to_this });
|
Lang::FileParser loader(qsl(":/langs/lang_") + LanguageCodes[systemLangId].c_str() + qsl(".strings"), { lng_switch_to_this });
|
||||||
QString text = loader.found().value(lng_switch_to_this);
|
auto text = loader.found().value(lng_switch_to_this);
|
||||||
if (!text.isEmpty()) {
|
if (!text.isEmpty()) {
|
||||||
_changeLanguage.create(this, object_ptr<Ui::LinkButton>(this, text), st::introCoverDuration);
|
_changeLanguage.create(this, object_ptr<Ui::LinkButton>(this, text), st::introCoverDuration);
|
||||||
_changeLanguage->entity()->setClickedCallback([this, systemLangId] { changeLanguage(systemLangId); });
|
_changeLanguage->entity()->setClickedCallback([this, systemLangId] { changeLanguage(systemLangId); });
|
||||||
|
|
|
@ -22,192 +22,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
|
|
||||||
#include "base/parse_helper.h"
|
#include "base/parse_helper.h"
|
||||||
|
|
||||||
bool LangLoaderPlain::readKeyValue(const char *&from, const char *end) {
|
namespace Lang {
|
||||||
using base::parse::skipWhitespaces;
|
|
||||||
if (!skipWhitespaces(from, end)) return false;
|
|
||||||
|
|
||||||
if (*from != '"') throw Exception(QString("Expected quote before key name!"));
|
FileParser::FileParser(const QString &file, const std::set<LangKey> &request)
|
||||||
++from;
|
: _filePath(file)
|
||||||
const char *nameStart = from;
|
, _request(request)
|
||||||
while (from < end && ((*from >= 'a' && *from <= 'z') || (*from >= 'A' && *from <= 'Z') || *from == '_' || (*from >= '0' && *from <= '9'))) {
|
, _readingAll(request.find(kLangKeysCount) != request.end()) {
|
||||||
++from;
|
QFile f(_filePath);
|
||||||
}
|
|
||||||
|
|
||||||
auto varName = QLatin1String(nameStart, from - nameStart);
|
|
||||||
|
|
||||||
if (from == end || *from != '"') throw Exception(QString("Expected quote after key name '%1'!").arg(varName));
|
|
||||||
++from;
|
|
||||||
|
|
||||||
if (!skipWhitespaces(from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(varName));
|
|
||||||
if (*from != '=') throw Exception(QString("'=' expected in key '%1'!").arg(varName));
|
|
||||||
|
|
||||||
if (!skipWhitespaces(++from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(varName));
|
|
||||||
if (*from != '"') throw Exception(QString("Expected string after '=' in key '%1'!").arg(varName));
|
|
||||||
|
|
||||||
LangKey varKey = keyIndex(varName);
|
|
||||||
bool feedingValue = request.empty();
|
|
||||||
if (feedingValue) {
|
|
||||||
if (varKey == lngkeys_cnt) {
|
|
||||||
warning(QString("Unknown key '%1'!").arg(varName));
|
|
||||||
}
|
|
||||||
} else if (!readingAll && request.find(varKey) == request.end()) {
|
|
||||||
varKey = lngkeys_cnt;
|
|
||||||
}
|
|
||||||
bool readingValue = (varKey != lngkeys_cnt);
|
|
||||||
|
|
||||||
QByteArray varValue;
|
|
||||||
QMap<ushort, bool> tagsUsed;
|
|
||||||
const char *start = ++from;
|
|
||||||
while (from < end && *from != '"') {
|
|
||||||
if (*from == '\n') {
|
|
||||||
throw Exception(QString("Unexpected end of string in key '%1'!").arg(varName));
|
|
||||||
}
|
|
||||||
if (*from == '\\') {
|
|
||||||
if (from + 1 >= end) throw Exception(QString("Unexpected end of file in key '%1'!").arg(varName));
|
|
||||||
if (*(from + 1) == '"' || *(from + 1) == '\\' || *(from + 1) == '{') {
|
|
||||||
if (readingValue && from > start) varValue.append(start, from - start);
|
|
||||||
start = ++from;
|
|
||||||
} else if (*(from + 1) == 'n') {
|
|
||||||
if (readingValue) {
|
|
||||||
if (from > start) varValue.append(start, int(from - start));
|
|
||||||
varValue.append('\n');
|
|
||||||
}
|
|
||||||
start = (++from) + 1;
|
|
||||||
}
|
|
||||||
} else if (readingValue && *from == '{') {
|
|
||||||
if (from > start) varValue.append(start, int(from - start));
|
|
||||||
|
|
||||||
const char *tagStart = ++from;
|
|
||||||
while (from < end && ((*from >= 'a' && *from <= 'z') || (*from >= 'A' && *from <= 'Z') || *from == '_' || (*from >= '0' && *from <= '9'))) {
|
|
||||||
++from;
|
|
||||||
}
|
|
||||||
if (from == tagStart) {
|
|
||||||
readingValue = false;
|
|
||||||
warning(QString("Expected tag name in key '%1'!").arg(varName));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
auto tagName = QLatin1String(tagStart, int(from - tagStart));
|
|
||||||
|
|
||||||
if (from == end || (*from != '}' && *from != ':')) throw Exception(QString("Expected '}' or ':' after tag name in key '%1'!").arg(varName));
|
|
||||||
|
|
||||||
ushort index = tagIndex(tagName);
|
|
||||||
if (index == lngtags_cnt) {
|
|
||||||
readingValue = false;
|
|
||||||
warning(QString("Tag '%1' not found in key '%2', not using value.").arg(tagName).arg(varName));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!tagReplaced(varKey, index)) {
|
|
||||||
readingValue = false;
|
|
||||||
warning(QString("Unexpected tag '%1' in key '%2', not using value.").arg(tagName).arg(varName));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (tagsUsed.contains(index)) throw Exception(QString("Tag '%1' double used in key '%2'!").arg(tagName).arg(varName));
|
|
||||||
tagsUsed.insert(index, true);
|
|
||||||
|
|
||||||
QString tagReplacer(4, TextCommand);
|
|
||||||
tagReplacer[1] = TextCommandLangTag;
|
|
||||||
tagReplacer[2] = QChar(0x0020 + index);
|
|
||||||
varValue.append(tagReplacer.toUtf8());
|
|
||||||
|
|
||||||
if (*from == ':') {
|
|
||||||
start = ++from;
|
|
||||||
|
|
||||||
QByteArray subvarValue;
|
|
||||||
bool foundtag = false;
|
|
||||||
int countedIndex = 0;
|
|
||||||
while (from < end && *from != '"' && *from != '}') {
|
|
||||||
if (*from == '|') {
|
|
||||||
if (from > start) subvarValue.append(start, int(from - start));
|
|
||||||
if (countedIndex >= lngtags_max_counted_values) throw Exception(QString("Too many values inside counted tag '%1' in '%2' key!").arg(tagName).arg(varName));
|
|
||||||
LangKey subkey = subkeyIndex(varKey, index, countedIndex++);
|
|
||||||
if (subkey == lngkeys_cnt) {
|
|
||||||
readingValue = false;
|
|
||||||
warning(QString("Unexpected counted tag '%1' in key '%2', not using value.").arg(tagName).arg(varName));
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
if (feedingValue) {
|
|
||||||
if (!feedKeyValue(subkey, QString::fromUtf8(subvarValue))) throw Exception(QString("Tag '%1' is not counted in key '%2'!").arg(tagName).arg(varName));
|
|
||||||
} else {
|
|
||||||
foundKeyValue(subkey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
subvarValue = QByteArray();
|
|
||||||
foundtag = false;
|
|
||||||
start = from + 1;
|
|
||||||
}
|
|
||||||
if (*from == '\n') {
|
|
||||||
throw Exception(QString("Unexpected end of string inside counted tag '%1' in '%2' key!").arg(tagName).arg(varName));
|
|
||||||
}
|
|
||||||
if (*from == '\\') {
|
|
||||||
if (from + 1 >= end) throw Exception(QString("Unexpected end of file inside counted tag '%1' in '%2' key!").arg(tagName).arg(varName));
|
|
||||||
if (*(from + 1) == '"' || *(from + 1) == '\\' || *(from + 1) == '{' || *(from + 1) == '#') {
|
|
||||||
if (from > start) subvarValue.append(start, int(from - start));
|
|
||||||
start = ++from;
|
|
||||||
} else if (*(from + 1) == 'n') {
|
|
||||||
if (from > start) subvarValue.append(start, int(from - start));
|
|
||||||
|
|
||||||
subvarValue.append('\n');
|
|
||||||
|
|
||||||
start = (++from) + 1;
|
|
||||||
}
|
|
||||||
} else if (*from == '{') {
|
|
||||||
throw Exception(QString("Unexpected tag inside counted tag '%1' in '%2' key!").arg(tagName).arg(varName));
|
|
||||||
} else if (*from == '#') {
|
|
||||||
if (foundtag) throw Exception(QString("Replacement '#' double used inside counted tag '%1' in '%2' key!").arg(tagName).arg(varName));
|
|
||||||
foundtag = true;
|
|
||||||
if (from > start) subvarValue.append(start, int(from - start));
|
|
||||||
subvarValue.append(tagReplacer.toUtf8());
|
|
||||||
start = from + 1;
|
|
||||||
}
|
|
||||||
++from;
|
|
||||||
}
|
|
||||||
if (!readingValue) continue;
|
|
||||||
if (from >= end) throw Exception(QString("Unexpected end of file inside counted tag '%1' in '%2' key!").arg(tagName).arg(varName));
|
|
||||||
if (*from == '"') throw Exception(QString("Unexpected end of string inside counted tag '%1' in '%2' key!").arg(tagName).arg(varName));
|
|
||||||
|
|
||||||
if (from > start) subvarValue.append(start, int(from - start));
|
|
||||||
if (countedIndex >= lngtags_max_counted_values) throw Exception(QString("Too many values inside counted tag '%1' in '%2' key!").arg(tagName).arg(varName));
|
|
||||||
|
|
||||||
LangKey subkey = subkeyIndex(varKey, index, countedIndex++);
|
|
||||||
if (subkey == lngkeys_cnt) {
|
|
||||||
readingValue = false;
|
|
||||||
warning(QString("Unexpected counted tag '%1' in key '%2', not using value.").arg(tagName).arg(varName));
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
if (feedingValue) {
|
|
||||||
if (!feedKeyValue(subkey, QString::fromUtf8(subvarValue))) throw Exception(QString("Tag '%1' is not counted in key '%2'!").arg(tagName).arg(varName));
|
|
||||||
} else {
|
|
||||||
foundKeyValue(subkey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
start = from + 1;
|
|
||||||
}
|
|
||||||
++from;
|
|
||||||
}
|
|
||||||
if (from >= end) throw Exception(QString("Unexpected end of file in key '%1'!").arg(varName));
|
|
||||||
if (readingValue && from > start) varValue.append(start, from - start);
|
|
||||||
|
|
||||||
if (!skipWhitespaces(++from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(varName));
|
|
||||||
if (*from != ';') throw Exception(QString("';' expected after \"value\" in key '%1'!").arg(varName));
|
|
||||||
|
|
||||||
skipWhitespaces(++from, end);
|
|
||||||
|
|
||||||
if (readingValue) {
|
|
||||||
if (feedingValue) {
|
|
||||||
if (!feedKeyValue(varKey, QString::fromUtf8(varValue))) throw Exception(QString("Could not write value in key '%1'!").arg(varName));
|
|
||||||
} else {
|
|
||||||
foundKeyValue(varKey);
|
|
||||||
result.insert(varKey, QString::fromUtf8(varValue));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
LangLoaderPlain::LangLoaderPlain(const QString &file, const std::set<LangKey> &request) : file(file), request(request), readingAll(request.find(lngkeys_cnt) != request.end()) {
|
|
||||||
QFile f(file);
|
|
||||||
if (!f.open(QIODevice::ReadOnly)) {
|
if (!f.open(QIODevice::ReadOnly)) {
|
||||||
error(qsl("Could not open input file!"));
|
error(qsl("Could not open input file!"));
|
||||||
return;
|
return;
|
||||||
|
@ -271,3 +92,226 @@ LangLoaderPlain::LangLoaderPlain(const QString &file, const std::set<LangKey> &r
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QString &FileParser::errors() const {
|
||||||
|
if (_errors.isEmpty() && !_errorsList.isEmpty()) {
|
||||||
|
_errors = _errorsList.join('\n');
|
||||||
|
}
|
||||||
|
return _errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &FileParser::warnings() const {
|
||||||
|
if (!_checked) {
|
||||||
|
for (auto i = 0; i < kLangKeysCount; ++i) {
|
||||||
|
if (!_found[i]) {
|
||||||
|
_warningsList.push_back(qsl("No value found for key '%1'").arg(GetKeyName(LangKey(i))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_checked = true;
|
||||||
|
}
|
||||||
|
if (_warnings.isEmpty() && !_warningsList.isEmpty()) {
|
||||||
|
_warnings = _warningsList.join('\n');
|
||||||
|
}
|
||||||
|
return _warnings;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileParser::foundKeyValue(LangKey key) {
|
||||||
|
if (key < kLangKeysCount) {
|
||||||
|
_found[key] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileParser::readKeyValue(const char *&from, const char *end) {
|
||||||
|
using base::parse::skipWhitespaces;
|
||||||
|
if (!skipWhitespaces(from, end)) return false;
|
||||||
|
|
||||||
|
if (*from != '"') throw Exception(QString("Expected quote before key name!"));
|
||||||
|
++from;
|
||||||
|
const char *nameStart = from;
|
||||||
|
while (from < end && ((*from >= 'a' && *from <= 'z') || (*from >= 'A' && *from <= 'Z') || *from == '_' || (*from >= '0' && *from <= '9'))) {
|
||||||
|
++from;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto varName = QLatin1String(nameStart, from - nameStart);
|
||||||
|
|
||||||
|
if (from == end || *from != '"') throw Exception(QString("Expected quote after key name '%1'!").arg(varName));
|
||||||
|
++from;
|
||||||
|
|
||||||
|
if (!skipWhitespaces(from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(varName));
|
||||||
|
if (*from != '=') throw Exception(QString("'=' expected in key '%1'!").arg(varName));
|
||||||
|
|
||||||
|
if (!skipWhitespaces(++from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(varName));
|
||||||
|
if (*from != '"') throw Exception(QString("Expected string after '=' in key '%1'!").arg(varName));
|
||||||
|
|
||||||
|
auto varKey = GetKeyIndex(varName);
|
||||||
|
bool feedingValue = _request.empty();
|
||||||
|
if (feedingValue) {
|
||||||
|
if (varKey == kLangKeysCount) {
|
||||||
|
warning(QString("Unknown key '%1'!").arg(varName));
|
||||||
|
}
|
||||||
|
} else if (!_readingAll && _request.find(varKey) == _request.end()) {
|
||||||
|
varKey = kLangKeysCount;
|
||||||
|
}
|
||||||
|
bool readingValue = (varKey != kLangKeysCount);
|
||||||
|
|
||||||
|
QByteArray varValue;
|
||||||
|
QMap<ushort, bool> tagsUsed;
|
||||||
|
const char *start = ++from;
|
||||||
|
while (from < end && *from != '"') {
|
||||||
|
if (*from == '\n') {
|
||||||
|
throw Exception(QString("Unexpected end of string in key '%1'!").arg(varName));
|
||||||
|
}
|
||||||
|
if (*from == '\\') {
|
||||||
|
if (from + 1 >= end) throw Exception(QString("Unexpected end of file in key '%1'!").arg(varName));
|
||||||
|
if (*(from + 1) == '"' || *(from + 1) == '\\' || *(from + 1) == '{') {
|
||||||
|
if (readingValue && from > start) varValue.append(start, from - start);
|
||||||
|
start = ++from;
|
||||||
|
} else if (*(from + 1) == 'n') {
|
||||||
|
if (readingValue) {
|
||||||
|
if (from > start) varValue.append(start, int(from - start));
|
||||||
|
varValue.append('\n');
|
||||||
|
}
|
||||||
|
start = (++from) + 1;
|
||||||
|
}
|
||||||
|
} else if (readingValue && *from == '{') {
|
||||||
|
if (from > start) varValue.append(start, int(from - start));
|
||||||
|
|
||||||
|
const char *tagStart = ++from;
|
||||||
|
while (from < end && ((*from >= 'a' && *from <= 'z') || (*from >= 'A' && *from <= 'Z') || *from == '_' || (*from >= '0' && *from <= '9'))) {
|
||||||
|
++from;
|
||||||
|
}
|
||||||
|
if (from == tagStart) {
|
||||||
|
readingValue = false;
|
||||||
|
warning(QString("Expected tag name in key '%1'!").arg(varName));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto tagName = QLatin1String(tagStart, int(from - tagStart));
|
||||||
|
|
||||||
|
if (from == end || (*from != '}' && *from != ':')) throw Exception(QString("Expected '}' or ':' after tag name in key '%1'!").arg(varName));
|
||||||
|
|
||||||
|
auto index = GetTagIndex(tagName);
|
||||||
|
if (index == kTagsCount) {
|
||||||
|
readingValue = false;
|
||||||
|
warning(QString("Tag '%1' not found in key '%2', not using value.").arg(tagName).arg(varName));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsTagReplaced(varKey, index)) {
|
||||||
|
readingValue = false;
|
||||||
|
warning(QString("Unexpected tag '%1' in key '%2', not using value.").arg(tagName).arg(varName));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (tagsUsed.contains(index)) throw Exception(QString("Tag '%1' double used in key '%2'!").arg(tagName).arg(varName));
|
||||||
|
tagsUsed.insert(index, true);
|
||||||
|
|
||||||
|
QString tagReplacer(4, TextCommand);
|
||||||
|
tagReplacer[1] = TextCommandLangTag;
|
||||||
|
tagReplacer[2] = QChar(0x0020 + index);
|
||||||
|
varValue.append(tagReplacer.toUtf8());
|
||||||
|
|
||||||
|
if (*from == ':') {
|
||||||
|
start = ++from;
|
||||||
|
|
||||||
|
QByteArray subvarValue;
|
||||||
|
bool foundtag = false;
|
||||||
|
int countedIndex = 0;
|
||||||
|
while (from < end && *from != '"' && *from != '}') {
|
||||||
|
if (*from == '|') {
|
||||||
|
if (from > start) subvarValue.append(start, int(from - start));
|
||||||
|
if (countedIndex >= kTagsPluralVariants) throw Exception(QString("Too many values inside counted tag '%1' in '%2' key!").arg(tagName).arg(varName));
|
||||||
|
auto subkey = GetSubkeyIndex(varKey, index, countedIndex++);
|
||||||
|
if (subkey == kLangKeysCount) {
|
||||||
|
readingValue = false;
|
||||||
|
warning(QString("Unexpected counted tag '%1' in key '%2', not using value.").arg(tagName).arg(varName));
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (feedingValue) {
|
||||||
|
if (!feedKeyValue(subkey, QString::fromUtf8(subvarValue))) throw Exception(QString("Tag '%1' is not counted in key '%2'!").arg(tagName).arg(varName));
|
||||||
|
} else {
|
||||||
|
foundKeyValue(subkey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subvarValue = QByteArray();
|
||||||
|
foundtag = false;
|
||||||
|
start = from + 1;
|
||||||
|
}
|
||||||
|
if (*from == '\n') {
|
||||||
|
throw Exception(QString("Unexpected end of string inside counted tag '%1' in '%2' key!").arg(tagName).arg(varName));
|
||||||
|
}
|
||||||
|
if (*from == '\\') {
|
||||||
|
if (from + 1 >= end) throw Exception(QString("Unexpected end of file inside counted tag '%1' in '%2' key!").arg(tagName).arg(varName));
|
||||||
|
if (*(from + 1) == '"' || *(from + 1) == '\\' || *(from + 1) == '{' || *(from + 1) == '#') {
|
||||||
|
if (from > start) subvarValue.append(start, int(from - start));
|
||||||
|
start = ++from;
|
||||||
|
} else if (*(from + 1) == 'n') {
|
||||||
|
if (from > start) subvarValue.append(start, int(from - start));
|
||||||
|
|
||||||
|
subvarValue.append('\n');
|
||||||
|
|
||||||
|
start = (++from) + 1;
|
||||||
|
}
|
||||||
|
} else if (*from == '{') {
|
||||||
|
throw Exception(QString("Unexpected tag inside counted tag '%1' in '%2' key!").arg(tagName).arg(varName));
|
||||||
|
} else if (*from == '#') {
|
||||||
|
if (foundtag) throw Exception(QString("Replacement '#' double used inside counted tag '%1' in '%2' key!").arg(tagName).arg(varName));
|
||||||
|
foundtag = true;
|
||||||
|
if (from > start) subvarValue.append(start, int(from - start));
|
||||||
|
subvarValue.append(tagReplacer.toUtf8());
|
||||||
|
start = from + 1;
|
||||||
|
}
|
||||||
|
++from;
|
||||||
|
}
|
||||||
|
if (!readingValue) continue;
|
||||||
|
if (from >= end) throw Exception(QString("Unexpected end of file inside counted tag '%1' in '%2' key!").arg(tagName).arg(varName));
|
||||||
|
if (*from == '"') throw Exception(QString("Unexpected end of string inside counted tag '%1' in '%2' key!").arg(tagName).arg(varName));
|
||||||
|
|
||||||
|
if (from > start) subvarValue.append(start, int(from - start));
|
||||||
|
if (countedIndex >= kTagsPluralVariants) throw Exception(QString("Too many values inside counted tag '%1' in '%2' key!").arg(tagName).arg(varName));
|
||||||
|
|
||||||
|
auto subkey = GetSubkeyIndex(varKey, index, countedIndex++);
|
||||||
|
if (subkey == kLangKeysCount) {
|
||||||
|
readingValue = false;
|
||||||
|
warning(QString("Unexpected counted tag '%1' in key '%2', not using value.").arg(tagName).arg(varName));
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (feedingValue) {
|
||||||
|
if (!feedKeyValue(subkey, QString::fromUtf8(subvarValue))) throw Exception(QString("Tag '%1' is not counted in key '%2'!").arg(tagName).arg(varName));
|
||||||
|
} else {
|
||||||
|
foundKeyValue(subkey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
start = from + 1;
|
||||||
|
}
|
||||||
|
++from;
|
||||||
|
}
|
||||||
|
if (from >= end) throw Exception(QString("Unexpected end of file in key '%1'!").arg(varName));
|
||||||
|
if (readingValue && from > start) varValue.append(start, from - start);
|
||||||
|
|
||||||
|
if (!skipWhitespaces(++from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(varName));
|
||||||
|
if (*from != ';') throw Exception(QString("';' expected after \"value\" in key '%1'!").arg(varName));
|
||||||
|
|
||||||
|
skipWhitespaces(++from, end);
|
||||||
|
|
||||||
|
if (readingValue) {
|
||||||
|
if (feedingValue) {
|
||||||
|
if (!feedKeyValue(varKey, QString::fromUtf8(varValue))) throw Exception(QString("Could not write value in key '%1'!").arg(varName));
|
||||||
|
} else {
|
||||||
|
foundKeyValue(varKey);
|
||||||
|
_result.insert(varKey, QString::fromUtf8(varValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileParser::feedKeyValue(LangKey key, const QString &value) {
|
||||||
|
if (key < kLangKeysCount) {
|
||||||
|
_found[key] = 1;
|
||||||
|
FeedKeyValue(key, value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Lang
|
||||||
|
|
|
@ -22,22 +22,44 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
|
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
|
|
||||||
using LangLoaderResult = QMap<LangKey, LangString>;
|
namespace Lang {
|
||||||
class LangLoaderPlain : public LangLoader {
|
|
||||||
public:
|
|
||||||
LangLoaderPlain(const QString &file, const std::set<LangKey> &request = std::set<LangKey>());
|
|
||||||
|
|
||||||
LangLoaderResult found() const {
|
class FileParser {
|
||||||
return result;
|
public:
|
||||||
|
using Result = QMap<LangKey, QString>;
|
||||||
|
|
||||||
|
FileParser(const QString &file, const std::set<LangKey> &request = std::set<LangKey>());
|
||||||
|
|
||||||
|
const QString &errors() const;
|
||||||
|
const QString &warnings() const;
|
||||||
|
|
||||||
|
Result found() const {
|
||||||
|
return _result;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
private:
|
||||||
QString file;
|
bool feedKeyValue(LangKey key, const QString &value);
|
||||||
std::set<LangKey> request;
|
void foundKeyValue(LangKey key);
|
||||||
|
|
||||||
|
void error(const QString &text) {
|
||||||
|
_errorsList.push_back(text);
|
||||||
|
}
|
||||||
|
void warning(const QString &text) {
|
||||||
|
_warningsList.push_back(text);
|
||||||
|
}
|
||||||
bool readKeyValue(const char *&from, const char *end);
|
bool readKeyValue(const char *&from, const char *end);
|
||||||
|
|
||||||
bool readingAll;
|
mutable QStringList _errorsList, _warningsList;
|
||||||
LangLoaderResult result;
|
mutable QString _errors, _warnings;
|
||||||
|
mutable bool _checked = false;
|
||||||
|
std::array<bool, kLangKeysCount> _found = { { false } };
|
||||||
|
|
||||||
|
QString _filePath;
|
||||||
|
std::set<LangKey> _request;
|
||||||
|
|
||||||
|
bool _readingAll = false;
|
||||||
|
Result _result;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace Lang
|
||||||
|
|
|
@ -22,58 +22,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
|
|
||||||
#include "lang/lang_file_parser.h"
|
#include "lang/lang_file_parser.h"
|
||||||
|
|
||||||
LangString LangString::tag(ushort tag, const QString &replacement) {
|
|
||||||
for (const QChar *s = constData(), *ch = s, *e = ch + size(); ch != e;) {
|
|
||||||
if (*ch == TextCommand) {
|
|
||||||
if (ch + 3 < e && (ch + 1)->unicode() == TextCommandLangTag && *(ch + 3) == TextCommand) {
|
|
||||||
if ((ch + 2)->unicode() == 0x0020 + tag) {
|
|
||||||
LangString result;
|
|
||||||
result.reserve(size() + replacement.size() - 4);
|
|
||||||
if (ch > s) result.append(midRef(0, ch - s));
|
|
||||||
result.append(replacement);
|
|
||||||
if (ch + 4 < e) result.append(midRef(ch - s + 4));
|
|
||||||
return result;
|
|
||||||
} else {
|
|
||||||
ch += 4;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const QChar *next = textSkipCommand(ch, e);
|
|
||||||
if (next == ch) {
|
|
||||||
++ch;
|
|
||||||
} else {
|
|
||||||
ch = next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
++ch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
LangString langCounted(ushort key0, ushort tag, float64 value) { // current lang dependent
|
|
||||||
int v = qFloor(value);
|
|
||||||
QString sv;
|
|
||||||
ushort key = key0;
|
|
||||||
if (v != qCeil(value)) {
|
|
||||||
key += 2;
|
|
||||||
sv = QString::number(value);
|
|
||||||
} else {
|
|
||||||
if (v == 1) {
|
|
||||||
key += 1;
|
|
||||||
} else if (v) {
|
|
||||||
key += 2;
|
|
||||||
}
|
|
||||||
sv = QString::number(v);
|
|
||||||
}
|
|
||||||
while (key > key0) {
|
|
||||||
LangString v = lang(LangKey(key));
|
|
||||||
if (!v.isEmpty()) return v.tag(tag, sv);
|
|
||||||
--key;
|
|
||||||
}
|
|
||||||
return lang(LangKey(key0)).tag(tag, sv);
|
|
||||||
}
|
|
||||||
|
|
||||||
//#define NEW_VER_TAG lt_link
|
//#define NEW_VER_TAG lt_link
|
||||||
//#define NEW_VER_TAG_VALUE "https://telegram.org/blog/desktop-1-0"
|
//#define NEW_VER_TAG_VALUE "https://telegram.org/blog/desktop-1-0"
|
||||||
|
|
||||||
|
@ -85,84 +33,21 @@ QString langNewVersionText() {
|
||||||
#endif // NEW_VER_TAG
|
#endif // NEW_VER_TAG
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef NEW_VER_TAG
|
|
||||||
#define NEW_VER_KEY lng_new_version_text__tagged
|
|
||||||
#define NEW_VER_POSTFIX .tag(NEW_VER_TAG, QString::fromUtf8(NEW_VER_TAG_VALUE))
|
|
||||||
#else // NEW_VER_TAG
|
|
||||||
#define NEW_VER_KEY lng_new_version_text
|
|
||||||
#define NEW_VER_POSTFIX
|
|
||||||
#endif // NEW_VER_TAG
|
|
||||||
|
|
||||||
QString langNewVersionTextForLang(int langId) {
|
|
||||||
LangLoaderResult result;
|
|
||||||
if (langId) {
|
|
||||||
LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[langId].c_str() + qsl(".strings"), { lng_language_name, NEW_VER_KEY });
|
|
||||||
result = loader.found();
|
|
||||||
} else {
|
|
||||||
result.insert(lng_language_name, langOriginal(lng_language_name));
|
|
||||||
result.insert(NEW_VER_KEY, langOriginal(NEW_VER_KEY));
|
|
||||||
}
|
|
||||||
return result.value(lng_language_name, LanguageCodes[langId].c_str() + qsl(" language")) + qsl(":\n\n") + LangString(result.value(NEW_VER_KEY, qsl("--none--")))NEW_VER_POSTFIX;
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef NEW_VER_POSTFIX
|
|
||||||
#undef NEW_VER_KEY
|
|
||||||
|
|
||||||
#undef NEW_VER_TAG_VALUE
|
#undef NEW_VER_TAG_VALUE
|
||||||
#undef NEW_VER_TAG
|
#undef NEW_VER_TAG
|
||||||
|
|
||||||
const QString &LangLoader::errors() const {
|
bool langFirstNameGoesSecond() {
|
||||||
if (_errors.isEmpty() && !_err.isEmpty()) {
|
auto fullname = lang(lng_full_name__tagged);
|
||||||
_errors = _err.join('\n');
|
for (auto begin = fullname.constData(), ch = begin, end = ch + fullname.size(); ch != end;) {
|
||||||
}
|
if (*ch == TextCommand) {
|
||||||
return _errors;
|
if (ch + 3 < end && (ch + 1)->unicode() == TextCommandLangTag && *(ch + 3) == TextCommand) {
|
||||||
}
|
if ((ch + 2)->unicode() == 0x0020 + lt_last_name) {
|
||||||
|
return true;
|
||||||
const QString &LangLoader::warnings() const {
|
} else if ((ch + 2)->unicode() == 0x0020 + lt_first_name) {
|
||||||
if (!_checked) {
|
break;
|
||||||
for (int32 i = 0; i < lngkeys_cnt; ++i) {
|
}
|
||||||
if (!_found[i]) {
|
|
||||||
_warn.push_back(qsl("No value found for key '%1'").arg(langKeyName(LangKey(i))));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_checked = true;
|
|
||||||
}
|
}
|
||||||
if (_warnings.isEmpty() && !_warn.isEmpty()) {
|
return false;
|
||||||
_warnings = _warn.join('\n');
|
|
||||||
}
|
|
||||||
return _warnings;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LangLoader::foundKeyValue(LangKey key) {
|
|
||||||
if (key < lngkeys_cnt) {
|
|
||||||
_found[key] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Translator::translate(const char *context, const char *sourceText, const char *disambiguation, int n) const {
|
|
||||||
if (qstr("QMenuBar") == context) {
|
|
||||||
if (qstr("Services") == sourceText) return lang(lng_mac_menu_services);
|
|
||||||
if (qstr("Hide %1") == sourceText) return lng_mac_menu_hide_telegram(lt_telegram, qsl("%1"));
|
|
||||||
if (qstr("Hide Others") == sourceText) return lang(lng_mac_menu_hide_others);
|
|
||||||
if (qstr("Show All") == sourceText) return lang(lng_mac_menu_show_all);
|
|
||||||
if (qstr("Preferences...") == sourceText) return lang(lng_mac_menu_preferences);
|
|
||||||
if (qstr("Quit %1") == sourceText) return lng_mac_menu_quit_telegram(lt_telegram, qsl("%1"));
|
|
||||||
if (qstr("About %1") == sourceText) return lng_mac_menu_about_telegram(lt_telegram, qsl("%1"));
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
if (qstr("QWidgetTextControl") == context || qstr("QLineEdit") == context) {
|
|
||||||
if (qstr("&Undo") == sourceText) return lang((cPlatform() == dbipWindows) ? lng_wnd_menu_undo : ((cPlatform() == dbipMac || cPlatform() == dbipMacOld) ? lng_mac_menu_undo : lng_linux_menu_undo));
|
|
||||||
if (qstr("&Redo") == sourceText) return lang((cPlatform() == dbipWindows) ? lng_wnd_menu_redo : ((cPlatform() == dbipMac || cPlatform() == dbipMacOld) ? lng_mac_menu_redo : lng_linux_menu_redo));
|
|
||||||
if (qstr("Cu&t") == sourceText) return lang(lng_mac_menu_cut);
|
|
||||||
if (qstr("&Copy") == sourceText) return lang(lng_mac_menu_copy);
|
|
||||||
if (qstr("&Paste") == sourceText) return lang(lng_mac_menu_paste);
|
|
||||||
if (qstr("Delete") == sourceText) return lang(lng_mac_menu_delete);
|
|
||||||
if (qstr("Select All") == sourceText) return lang(lng_mac_menu_select_all);
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
if (qstr("QUnicodeControlCharacterMenu") == context) {
|
|
||||||
if (qstr("Insert Unicode control character") == sourceText) return lang(lng_menu_insert_unicode);
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
return QString();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,10 +33,8 @@ constexpr const str_const LanguageCodes[] = {
|
||||||
};
|
};
|
||||||
constexpr const int languageTest = -1, languageDefault = 0, languageCount = base::array_size(LanguageCodes);
|
constexpr const int languageTest = -1, languageDefault = 0, languageCount = base::array_size(LanguageCodes);
|
||||||
|
|
||||||
const char *langKeyName(LangKey key);
|
|
||||||
|
|
||||||
template <typename WithYear, typename WithoutYear>
|
template <typename WithYear, typename WithoutYear>
|
||||||
inline LangString langDateMaybeWithYear(QDate date, WithYear withYear, WithoutYear withoutYear) {
|
inline QString langDateMaybeWithYear(QDate date, WithYear withYear, WithoutYear withoutYear) {
|
||||||
auto month = date.month();
|
auto month = date.month();
|
||||||
if (month <= 0 || month > 12) {
|
if (month <= 0 || month > 12) {
|
||||||
return qsl("MONTH_ERR");
|
return qsl("MONTH_ERR");
|
||||||
|
@ -63,7 +61,7 @@ inline LangString langDateMaybeWithYear(QDate date, WithYear withYear, WithoutYe
|
||||||
return withoutYear(month, year);
|
return withoutYear(month, year);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline LangString langDayOfMonth(const QDate &date) {
|
inline QString langDayOfMonth(const QDate &date) {
|
||||||
auto day = date.day();
|
auto day = date.day();
|
||||||
return langDateMaybeWithYear(date, [day](int month, int year) {
|
return langDateMaybeWithYear(date, [day](int month, int year) {
|
||||||
return lng_month_day_year(lt_month, lang(LangKey(lng_month1_small + month - 1)), lt_day, QString::number(day), lt_year, QString::number(year));
|
return lng_month_day_year(lt_month, lang(LangKey(lng_month1_small + month - 1)), lt_day, QString::number(day), lt_year, QString::number(year));
|
||||||
|
@ -72,7 +70,7 @@ inline LangString langDayOfMonth(const QDate &date) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
inline LangString langDayOfMonthFull(const QDate &date) {
|
inline QString langDayOfMonthFull(const QDate &date) {
|
||||||
auto day = date.day();
|
auto day = date.day();
|
||||||
return langDateMaybeWithYear(date, [day](int month, int year) {
|
return langDateMaybeWithYear(date, [day](int month, int year) {
|
||||||
return lng_month_day_year(lt_month, lang(LangKey(lng_month1 + month - 1)), lt_day, QString::number(day), lt_year, QString::number(year));
|
return lng_month_day_year(lt_month, lang(LangKey(lng_month1 + month - 1)), lt_day, QString::number(day), lt_year, QString::number(year));
|
||||||
|
@ -81,11 +79,11 @@ inline LangString langDayOfMonthFull(const QDate &date) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
inline LangString langMonthOfYear(int month, int year) {
|
inline QString langMonthOfYear(int month, int year) {
|
||||||
return (month > 0 && month <= 12) ? lng_month_year(lt_month, lang(LangKey(lng_month1_small + month - 1)), lt_year, QString::number(year)) : qsl("MONTH_ERR");
|
return (month > 0 && month <= 12) ? lng_month_year(lt_month, lang(LangKey(lng_month1_small + month - 1)), lt_year, QString::number(year)) : qsl("MONTH_ERR");
|
||||||
}
|
}
|
||||||
|
|
||||||
inline LangString langMonth(const QDate &date) {
|
inline QString langMonth(const QDate &date) {
|
||||||
return langDateMaybeWithYear(date, [](int month, int year) {
|
return langDateMaybeWithYear(date, [](int month, int year) {
|
||||||
return langMonthOfYear(month, year);
|
return langMonthOfYear(month, year);
|
||||||
}, [](int month, int year) {
|
}, [](int month, int year) {
|
||||||
|
@ -93,11 +91,11 @@ inline LangString langMonth(const QDate &date) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
inline LangString langMonthOfYearFull(int month, int year) {
|
inline QString langMonthOfYearFull(int month, int year) {
|
||||||
return (month > 0 && month <= 12) ? lng_month_year(lt_month, lang(LangKey(lng_month1 + month - 1)), lt_year, QString::number(year)) : qsl("MONTH_ERR");
|
return (month > 0 && month <= 12) ? lng_month_year(lt_month, lang(LangKey(lng_month1 + month - 1)), lt_year, QString::number(year)) : qsl("MONTH_ERR");
|
||||||
}
|
}
|
||||||
|
|
||||||
inline LangString langMonthFull(const QDate &date) {
|
inline QString langMonthFull(const QDate &date) {
|
||||||
return langDateMaybeWithYear(date, [](int month, int year) {
|
return langDateMaybeWithYear(date, [](int month, int year) {
|
||||||
return langMonthOfYearFull(month, year);
|
return langMonthOfYearFull(month, year);
|
||||||
}, [](int month, int year) {
|
}, [](int month, int year) {
|
||||||
|
@ -105,87 +103,30 @@ inline LangString langMonthFull(const QDate &date) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
inline LangString langDayOfWeek(int index) {
|
inline QString langDayOfWeek(int index) {
|
||||||
return (index > 0 && index <= 7) ? lang(LangKey(lng_weekday1 + index - 1)) : qsl("DAY_ERR");
|
return (index > 0 && index <= 7) ? lang(LangKey(lng_weekday1 + index - 1)) : qsl("DAY_ERR");
|
||||||
}
|
}
|
||||||
|
|
||||||
inline LangString langDayOfWeek(const QDate &date) {
|
inline QString langDayOfWeek(const QDate &date) {
|
||||||
return langDayOfWeek(date.dayOfWeek());
|
return langDayOfWeek(date.dayOfWeek());
|
||||||
}
|
}
|
||||||
|
|
||||||
inline LangString langDayOfWeekFull(int index) {
|
inline QString langDayOfWeekFull(int index) {
|
||||||
return (index > 0 && index <= 7) ? lang(LangKey(lng_weekday1_full + index - 1)) : qsl("DAY_ERR");
|
return (index > 0 && index <= 7) ? lang(LangKey(lng_weekday1_full + index - 1)) : qsl("DAY_ERR");
|
||||||
}
|
}
|
||||||
|
|
||||||
inline LangString langDayOfWeekFull(const QDate &date) {
|
inline QString langDayOfWeekFull(const QDate &date) {
|
||||||
return langDayOfWeekFull(date.dayOfWeek());
|
return langDayOfWeekFull(date.dayOfWeek());
|
||||||
}
|
}
|
||||||
|
|
||||||
inline LangString langDateTime(const QDateTime &date) {
|
inline QString langDateTime(const QDateTime &date) {
|
||||||
return lng_mediaview_date_time(lt_date, langDayOfMonth(date.date()), lt_time, date.time().toString(cTimeFormat()));
|
return lng_mediaview_date_time(lt_date, langDayOfMonth(date.date()), lt_time, date.time().toString(cTimeFormat()));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline LangString langDateTimeFull(const QDateTime &date) {
|
inline QString langDateTimeFull(const QDateTime &date) {
|
||||||
return lng_mediaview_date_time(lt_date, langDayOfMonthFull(date.date()), lt_time, date.time().toString(cTimeFormat()));
|
return lng_mediaview_date_time(lt_date, langDayOfMonthFull(date.date()), lt_time, date.time().toString(cTimeFormat()));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString langNewVersionText();
|
QString langNewVersionText();
|
||||||
QString langNewVersionTextForLang(int langId);
|
|
||||||
|
|
||||||
class LangLoader {
|
bool langFirstNameGoesSecond();
|
||||||
public:
|
|
||||||
const QString &errors() const;
|
|
||||||
const QString &warnings() const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
LangLoader() : _checked(false) {
|
|
||||||
memset(_found, 0, sizeof(_found));
|
|
||||||
}
|
|
||||||
|
|
||||||
ushort tagIndex(QLatin1String tag) const;
|
|
||||||
LangKey keyIndex(QLatin1String key) const;
|
|
||||||
bool tagReplaced(LangKey key, ushort tag) const;
|
|
||||||
LangKey subkeyIndex(LangKey key, ushort tag, ushort index) const;
|
|
||||||
|
|
||||||
bool feedKeyValue(LangKey key, const QString &value);
|
|
||||||
void foundKeyValue(LangKey key);
|
|
||||||
|
|
||||||
void error(const QString &text) {
|
|
||||||
_err.push_back(text);
|
|
||||||
}
|
|
||||||
void warning(const QString &text) {
|
|
||||||
_warn.push_back(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
mutable QStringList _err, _warn;
|
|
||||||
mutable QString _errors, _warnings;
|
|
||||||
mutable bool _checked;
|
|
||||||
bool _found[lngkeys_cnt];
|
|
||||||
|
|
||||||
LangLoader(const LangLoader &);
|
|
||||||
LangLoader &operator=(const LangLoader &);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
class Translator : public QTranslator {
|
|
||||||
public:
|
|
||||||
QString translate(const char *context, const char *sourceText, const char *disambiguation = 0, int n = -1) const override;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
inline bool langFirstNameGoesSecond() {
|
|
||||||
QString fullname = lang(lng_full_name__tagged);
|
|
||||||
for (const QChar *s = fullname.constData(), *ch = s, *e = ch + fullname.size(); ch != e;) {
|
|
||||||
if (*ch == TextCommand) {
|
|
||||||
if (ch + 3 < e && (ch + 1)->unicode() == TextCommandLangTag && *(ch + 3) == TextCommand) {
|
|
||||||
if ((ch + 2)->unicode() == 0x0020 + lt_last_name) {
|
|
||||||
return true;
|
|
||||||
} else if ((ch + 2)->unicode() == 0x0020 + lt_first_name) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||||
|
|
||||||
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
It is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
In addition, as a special exception, the copyright holders give permission
|
||||||
|
to link the code of portions of this program with the OpenSSL library.
|
||||||
|
|
||||||
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||||
|
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
|
*/
|
||||||
|
#include "lang/lang_tag.h"
|
||||||
|
|
||||||
|
#include "lang/lang_keys.h"
|
||||||
|
|
||||||
|
namespace Lang {
|
||||||
|
|
||||||
|
QString Tag(const QString &original, ushort tag, const QString &replacement) {
|
||||||
|
for (auto s = original.constData(), ch = s, e = ch + original.size(); ch != e;) {
|
||||||
|
if (*ch == TextCommand) {
|
||||||
|
if (ch + 3 < e && (ch + 1)->unicode() == TextCommandLangTag && *(ch + 3) == TextCommand) {
|
||||||
|
if ((ch + 2)->unicode() == 0x0020 + tag) {
|
||||||
|
auto result = QString();
|
||||||
|
result.reserve(original.size() + replacement.size() - 4);
|
||||||
|
if (ch > s) result.append(original.midRef(0, ch - s));
|
||||||
|
result.append(replacement);
|
||||||
|
if (ch + 4 < e) result.append(original.midRef(ch - s + 4));
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
ch += 4;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto next = textSkipCommand(ch, e);
|
||||||
|
if (next == ch) {
|
||||||
|
++ch;
|
||||||
|
} else {
|
||||||
|
ch = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
++ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Plural(ushort key0, ushort tag, float64 value) { // current lang dependent
|
||||||
|
int v = qFloor(value);
|
||||||
|
QString sv;
|
||||||
|
ushort key = key0;
|
||||||
|
if (v != qCeil(value)) {
|
||||||
|
key += 2;
|
||||||
|
sv = QString::number(value);
|
||||||
|
} else {
|
||||||
|
if (v == 1) {
|
||||||
|
key += 1;
|
||||||
|
} else if (v) {
|
||||||
|
key += 2;
|
||||||
|
}
|
||||||
|
sv = QString::number(v);
|
||||||
|
}
|
||||||
|
while (key > key0) {
|
||||||
|
auto v = lang(LangKey(key));
|
||||||
|
if (!v.isEmpty()) {
|
||||||
|
return Tag(v, tag, sv);
|
||||||
|
}
|
||||||
|
--key;
|
||||||
|
}
|
||||||
|
return Tag(lang(LangKey(key0)), tag, sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Lang
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||||
|
|
||||||
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
It is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
In addition, as a special exception, the copyright holders give permission
|
||||||
|
to link the code of portions of this program with the OpenSSL library.
|
||||||
|
|
||||||
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||||
|
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Lang {
|
||||||
|
|
||||||
|
QString Tag(const QString &original, ushort tag, const QString &replacement);
|
||||||
|
QString Plural(ushort key0, ushort tag, float64 value);
|
||||||
|
|
||||||
|
} // namespace Lang
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||||
|
|
||||||
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
It is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
In addition, as a special exception, the copyright holders give permission
|
||||||
|
to link the code of portions of this program with the OpenSSL library.
|
||||||
|
|
||||||
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||||
|
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
|
*/
|
||||||
|
#include "lang/lang_translator.h"
|
||||||
|
|
||||||
|
#include "lang/lang_keys.h"
|
||||||
|
|
||||||
|
namespace Lang {
|
||||||
|
|
||||||
|
QString Translator::translate(const char *context, const char *sourceText, const char *disambiguation, int n) const {
|
||||||
|
if (qstr("QMenuBar") == context) {
|
||||||
|
if (qstr("Services") == sourceText) return lang(lng_mac_menu_services);
|
||||||
|
if (qstr("Hide %1") == sourceText) return lng_mac_menu_hide_telegram(lt_telegram, qsl("%1"));
|
||||||
|
if (qstr("Hide Others") == sourceText) return lang(lng_mac_menu_hide_others);
|
||||||
|
if (qstr("Show All") == sourceText) return lang(lng_mac_menu_show_all);
|
||||||
|
if (qstr("Preferences...") == sourceText) return lang(lng_mac_menu_preferences);
|
||||||
|
if (qstr("Quit %1") == sourceText) return lng_mac_menu_quit_telegram(lt_telegram, qsl("%1"));
|
||||||
|
if (qstr("About %1") == sourceText) return lng_mac_menu_about_telegram(lt_telegram, qsl("%1"));
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
if (qstr("QWidgetTextControl") == context || qstr("QLineEdit") == context) {
|
||||||
|
if (qstr("&Undo") == sourceText) return lang((cPlatform() == dbipWindows) ? lng_wnd_menu_undo : ((cPlatform() == dbipMac || cPlatform() == dbipMacOld) ? lng_mac_menu_undo : lng_linux_menu_undo));
|
||||||
|
if (qstr("&Redo") == sourceText) return lang((cPlatform() == dbipWindows) ? lng_wnd_menu_redo : ((cPlatform() == dbipMac || cPlatform() == dbipMacOld) ? lng_mac_menu_redo : lng_linux_menu_redo));
|
||||||
|
if (qstr("Cu&t") == sourceText) return lang(lng_mac_menu_cut);
|
||||||
|
if (qstr("&Copy") == sourceText) return lang(lng_mac_menu_copy);
|
||||||
|
if (qstr("&Paste") == sourceText) return lang(lng_mac_menu_paste);
|
||||||
|
if (qstr("Delete") == sourceText) return lang(lng_mac_menu_delete);
|
||||||
|
if (qstr("Select All") == sourceText) return lang(lng_mac_menu_select_all);
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
if (qstr("QUnicodeControlCharacterMenu") == context) {
|
||||||
|
if (qstr("Insert Unicode control character") == sourceText) return lang(lng_menu_insert_unicode);
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Lang
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||||
|
|
||||||
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
It is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
In addition, as a special exception, the copyright holders give permission
|
||||||
|
to link the code of portions of this program with the OpenSSL library.
|
||||||
|
|
||||||
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||||
|
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Lang {
|
||||||
|
|
||||||
|
class Translator : public QTranslator {
|
||||||
|
public:
|
||||||
|
QString translate(const char *context, const char *sourceText, const char *disambiguation = 0, int n = -1) const override;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Lang
|
|
@ -1455,38 +1455,6 @@ Dialogs::IndexedList *MainWidget::contactsNoDialogsList() {
|
||||||
return _dialogs->contactsNoDialogsList();
|
return _dialogs->contactsNoDialogsList();
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
|
||||||
QString parseCommandFromMessage(History *history, const QString &message) {
|
|
||||||
if (history->peer->id != peerFromUser(ServiceUserId)) {
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
if (message.size() < 3 || message.at(0) != '*' || message.at(message.size() - 1) != '*') {
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
QString command = message.mid(1, message.size() - 2);
|
|
||||||
QStringList commands;
|
|
||||||
commands.push_back(qsl("new_version_text"));
|
|
||||||
commands.push_back(qsl("all_new_version_texts"));
|
|
||||||
if (commands.indexOf(command) < 0) {
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
return command;
|
|
||||||
}
|
|
||||||
|
|
||||||
void executeParsedCommand(const QString &command) {
|
|
||||||
if (command.isEmpty() || !App::wnd()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (command == qsl("new_version_text")) {
|
|
||||||
App::wnd()->serviceNotificationLocal(langNewVersionText());
|
|
||||||
} else if (command == qsl("all_new_version_texts")) {
|
|
||||||
for (int i = 0; i < languageCount; ++i) {
|
|
||||||
App::wnd()->serviceNotificationLocal(langNewVersionTextForLang(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
void MainWidget::sendMessage(const MessageToSend &message) {
|
void MainWidget::sendMessage(const MessageToSend &message) {
|
||||||
auto history = message.history;
|
auto history = message.history;
|
||||||
auto &textWithTags = message.textWithTags;
|
auto &textWithTags = message.textWithTags;
|
||||||
|
@ -1503,11 +1471,10 @@ void MainWidget::sendMessage(const MessageToSend &message) {
|
||||||
auto prepareFlags = itemTextOptions(history, App::self()).flags;
|
auto prepareFlags = itemTextOptions(history, App::self()).flags;
|
||||||
QString sendingText, leftText = prepareTextWithEntities(textWithTags.text, prepareFlags, &leftEntities);
|
QString sendingText, leftText = prepareTextWithEntities(textWithTags.text, prepareFlags, &leftEntities);
|
||||||
|
|
||||||
QString command = parseCommandFromMessage(history, textWithTags.text);
|
|
||||||
HistoryItem *lastMessage = nullptr;
|
HistoryItem *lastMessage = nullptr;
|
||||||
|
|
||||||
MsgId replyTo = (message.replyTo < 0) ? _history->replyToId() : message.replyTo;
|
MsgId replyTo = (message.replyTo < 0) ? _history->replyToId() : message.replyTo;
|
||||||
while (command.isEmpty() && textSplit(sendingText, sendingEntities, leftText, leftEntities, MaxMessageSize)) {
|
while (textSplit(sendingText, sendingEntities, leftText, leftEntities, MaxMessageSize)) {
|
||||||
FullMsgId newId(peerToChannel(history->peer->id), clientMsgId());
|
FullMsgId newId(peerToChannel(history->peer->id), clientMsgId());
|
||||||
uint64 randomId = rand_value<uint64>();
|
uint64 randomId = rand_value<uint64>();
|
||||||
|
|
||||||
|
@ -1561,8 +1528,6 @@ void MainWidget::sendMessage(const MessageToSend &message) {
|
||||||
history->lastSentMsg = lastMessage;
|
history->lastSentMsg = lastMessage;
|
||||||
|
|
||||||
finishForwarding(history, message.silent);
|
finishForwarding(history, message.silent);
|
||||||
|
|
||||||
executeParsedCommand(command);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::saveRecentHashtags(const QString &text) {
|
void MainWidget::saveRecentHashtags(const QString &text) {
|
||||||
|
|
|
@ -30,6 +30,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
#include "calls/calls_instance.h"
|
#include "calls/calls_instance.h"
|
||||||
#include "lang/lang_file_parser.h"
|
#include "lang/lang_file_parser.h"
|
||||||
|
#include "lang/lang_translator.h"
|
||||||
#include "observer_peer.h"
|
#include "observer_peer.h"
|
||||||
#include "storage/file_upload.h"
|
#include "storage/file_upload.h"
|
||||||
#include "mainwidget.h"
|
#include "mainwidget.h"
|
||||||
|
@ -380,7 +381,7 @@ void Messenger::loadLanguage() {
|
||||||
}
|
}
|
||||||
if (cLang() == languageTest) {
|
if (cLang() == languageTest) {
|
||||||
if (QFileInfo(cLangFile()).exists()) {
|
if (QFileInfo(cLangFile()).exists()) {
|
||||||
LangLoaderPlain loader(cLangFile());
|
Lang::FileParser loader(cLangFile());
|
||||||
cSetLangErrors(loader.errors());
|
cSetLangErrors(loader.errors());
|
||||||
if (!cLangErrors().isEmpty()) {
|
if (!cLangErrors().isEmpty()) {
|
||||||
LOG(("Lang load errors: %1").arg(cLangErrors()));
|
LOG(("Lang load errors: %1").arg(cLangErrors()));
|
||||||
|
@ -391,14 +392,14 @@ void Messenger::loadLanguage() {
|
||||||
cSetLang(languageDefault);
|
cSetLang(languageDefault);
|
||||||
}
|
}
|
||||||
} else if (cLang() > languageDefault && cLang() < languageCount) {
|
} else if (cLang() > languageDefault && cLang() < languageCount) {
|
||||||
LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[cLang()].c_str() + qsl(".strings"));
|
Lang::FileParser loader(qsl(":/langs/lang_") + LanguageCodes[cLang()].c_str() + qsl(".strings"));
|
||||||
if (!loader.errors().isEmpty()) {
|
if (!loader.errors().isEmpty()) {
|
||||||
LOG(("Lang load errors: %1").arg(loader.errors()));
|
LOG(("Lang load errors: %1").arg(loader.errors()));
|
||||||
} else if (!loader.warnings().isEmpty()) {
|
} else if (!loader.warnings().isEmpty()) {
|
||||||
LOG(("Lang load warnings: %1").arg(loader.warnings()));
|
LOG(("Lang load warnings: %1").arg(loader.warnings()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_translator = std::make_unique<Translator>();
|
_translator = std::make_unique<Lang::Translator>();
|
||||||
QCoreApplication::instance()->installTranslator(_translator.get());
|
QCoreApplication::instance()->installTranslator(_translator.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,10 @@ class Instance;
|
||||||
} // namespace Audio
|
} // namespace Audio
|
||||||
} // namespace Media
|
} // namespace Media
|
||||||
|
|
||||||
|
namespace Lang {
|
||||||
|
class Translator;
|
||||||
|
} // namespace Lang
|
||||||
|
|
||||||
class Messenger final : public QObject, public RPCSender, private base::Subscriber {
|
class Messenger final : public QObject, public RPCSender, private base::Subscriber {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
@ -189,7 +193,7 @@ private:
|
||||||
std::unique_ptr<MainWindow> _window;
|
std::unique_ptr<MainWindow> _window;
|
||||||
FileUploader *_uploader = nullptr;
|
FileUploader *_uploader = nullptr;
|
||||||
|
|
||||||
std::unique_ptr<Translator> _translator;
|
std::unique_ptr<Lang::Translator> _translator;
|
||||||
std::unique_ptr<MTP::DcOptions> _dcOptions;
|
std::unique_ptr<MTP::DcOptions> _dcOptions;
|
||||||
std::unique_ptr<MTP::Instance> _mtproto;
|
std::unique_ptr<MTP::Instance> _mtproto;
|
||||||
std::unique_ptr<MTP::Instance> _mtprotoForKeysDestroy;
|
std::unique_ptr<MTP::Instance> _mtprotoForKeysDestroy;
|
||||||
|
|
|
@ -776,7 +776,7 @@ void MainWindow::updateIconCounters() {
|
||||||
iconOverlay.addPixmap(App::pixmapFromImageInPlace(iconWithCounter(-32, counter, bg, fg, false)));
|
iconOverlay.addPixmap(App::pixmapFromImageInPlace(iconWithCounter(-32, counter, bg, fg, false)));
|
||||||
ps_iconOverlay = createHIconFromQIcon(iconOverlay, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
|
ps_iconOverlay = createHIconFromQIcon(iconOverlay, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
|
||||||
}
|
}
|
||||||
auto description = (counter > 0) ? lng_unread_bar(lt_count, counter) : LangString();
|
auto description = (counter > 0) ? lng_unread_bar(lt_count, counter) : QString();
|
||||||
taskbarList->SetOverlayIcon(ps_hWnd, ps_iconOverlay, description.toStdWString().c_str());
|
taskbarList->SetOverlayIcon(ps_hWnd, ps_iconOverlay, description.toStdWString().c_str());
|
||||||
}
|
}
|
||||||
SetWindowPos(ps_hWnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
|
SetWindowPos(ps_hWnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
|
||||||
|
|
|
@ -213,9 +213,9 @@ void GeneralWidget::chooseCustomLang() {
|
||||||
}
|
}
|
||||||
|
|
||||||
_testLanguage = QFileInfo(result.paths.front()).absoluteFilePath();
|
_testLanguage = QFileInfo(result.paths.front()).absoluteFilePath();
|
||||||
LangLoaderPlain loader(_testLanguage, { lng_sure_save_language, lng_cancel, lng_box_ok });
|
Lang::FileParser loader(_testLanguage, { lng_sure_save_language, lng_cancel, lng_box_ok });
|
||||||
if (loader.errors().isEmpty()) {
|
if (loader.errors().isEmpty()) {
|
||||||
LangLoaderResult result = loader.found();
|
auto result = loader.found();
|
||||||
auto text = result.value(lng_sure_save_language, langOriginal(lng_sure_save_language)),
|
auto text = result.value(lng_sure_save_language, langOriginal(lng_sure_save_language)),
|
||||||
save = result.value(lng_box_ok, langOriginal(lng_box_ok)),
|
save = result.value(lng_box_ok, langOriginal(lng_box_ok)),
|
||||||
cancel = result.value(lng_cancel, langOriginal(lng_cancel));
|
cancel = result.value(lng_cancel, langOriginal(lng_cancel));
|
||||||
|
|
|
@ -121,6 +121,10 @@ void fillCodes() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Codes.insert(qsl("newversiontext"), [] {
|
||||||
|
App::wnd()->serviceNotificationLocal(langNewVersionText());
|
||||||
|
});
|
||||||
|
|
||||||
auto audioFilters = qsl("Audio files (*.wav *.mp3);;") + FileDialog::AllFilesFilter();
|
auto audioFilters = qsl("Audio files (*.wav *.mp3);;") + FileDialog::AllFilesFilter();
|
||||||
auto audioKeys = {
|
auto audioKeys = {
|
||||||
qsl("msg_incoming"),
|
qsl("msg_incoming"),
|
||||||
|
|
|
@ -177,10 +177,14 @@
|
||||||
<(src_loc)/intro/introsignup.h
|
<(src_loc)/intro/introsignup.h
|
||||||
<(src_loc)/intro/introstart.cpp
|
<(src_loc)/intro/introstart.cpp
|
||||||
<(src_loc)/intro/introstart.h
|
<(src_loc)/intro/introstart.h
|
||||||
<(src_loc)/lang/lang_keys.cpp
|
|
||||||
<(src_loc)/lang/lang_keys.h
|
|
||||||
<(src_loc)/lang/lang_file_parser.cpp
|
<(src_loc)/lang/lang_file_parser.cpp
|
||||||
<(src_loc)/lang/lang_file_parser.h
|
<(src_loc)/lang/lang_file_parser.h
|
||||||
|
<(src_loc)/lang/lang_keys.cpp
|
||||||
|
<(src_loc)/lang/lang_keys.h
|
||||||
|
<(src_loc)/lang/lang_tag.cpp
|
||||||
|
<(src_loc)/lang/lang_tag.h
|
||||||
|
<(src_loc)/lang/lang_translator.cpp
|
||||||
|
<(src_loc)/lang/lang_translator.h
|
||||||
<(src_loc)/media/player/media_player_button.cpp
|
<(src_loc)/media/player/media_player_button.cpp
|
||||||
<(src_loc)/media/player/media_player_button.h
|
<(src_loc)/media/player/media_player_button.h
|
||||||
<(src_loc)/media/player/media_player_cover.cpp
|
<(src_loc)/media/player/media_player_cover.cpp
|
||||||
|
|
Loading…
Reference in New Issue