Use QString + Lang::Tag() instead of Lang::String.

This commit is contained in:
John Preston 2017-04-13 11:45:58 +03:00
parent 110e7c8074
commit 2334ba1fe1
20 changed files with 598 additions and 534 deletions

View File

@ -88,11 +88,11 @@ void AboutBox::keyPressEvent(QKeyEvent *e) {
}
QString telegramFaqLink() {
QString result = qsl("https://telegram.org/faq");
auto result = qsl("https://telegram.org/faq");
if (cLang() > languageDefault && cLang() < languageCount) {
const char *code = LanguageCodes[cLang()].c_str();
if (qstr("de") == code || qstr("es") == code || qstr("it") == code || qstr("ko") == code) {
result += qsl("/") + code;
result += '/' + code;
} else if (qstr("pt_BR") == code) {
result += qsl("/br");
}

View File

@ -46,9 +46,9 @@ void LanguageBox::prepare() {
y += _langs.back()->heightNoMargins() + st::boxOptionListSkip;
}
for (auto i = 0; i != languageCount; ++i) {
LangLoaderResult result;
Lang::FileParser::Result result;
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();
} else {
result.insert(lng_language_name, langOriginal(lng_language_name));
@ -66,12 +66,12 @@ void LanguageBox::prepare() {
void LanguageBox::mousePressEvent(QMouseEvent *e) {
if ((e->modifiers() & Qt::CTRL) && (e->modifiers() & Qt::ALT) && (e->modifiers() & Qt::SHIFT)) {
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()) {
Ui::show(Box<InformBox>(qsl("Lang \"") + LanguageCodes[i].c_str() + qsl("\" error :(\n\nError: ") + loader.errors()));
return;
} else if (!loader.warnings().isEmpty()) {
QString warn = loader.warnings();
auto warn = loader.warnings();
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));
return;
@ -88,12 +88,12 @@ void LanguageBox::languageChanged(int languageId) {
return;
}
LangLoaderResult result;
Lang::FileParser::Result result;
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();
} 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();
}
auto text = result.value(lng_sure_save_language, langOriginal(lng_sure_save_language)),

View File

@ -118,71 +118,71 @@ Generator::Generator(const Langpack &langpack, const QString &destBasePath, cons
bool Generator::writeHeader() {
header_ = std::make_unique<common::CppFile>(basePath_ + ".h", project_);
header_->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\
header_->include("lang/lang_tag.h").newline().pushNamespace("Lang").stream() << "\
\n\
LangString tag(ushort tag, const QString &replacement);\n\
\n\
};\n\
\n\
LangString langCounted(ushort key0, ushort tag, float64 value);\n\
constexpr auto kTagsCount = " << langpack_.tags.size() << ";\n\
constexpr auto kTagsPluralVariants = " << kMaxPluralVariants << ";\n\
\n";
header_->popNamespace().newline();
auto index = 0;
for (auto &tag : langpack_.tags) {
header_->stream() << "enum lngtag_" << tag.tag << " { lt_" << tag.tag << " = " << index++ << " };\n";
}
header_->stream() << "\
\n\
constexpr auto lngtags_cnt = " << langpack_.tags.size() << ";\n\
constexpr auto lngtags_max_counted_values = " << kMaxPluralVariants << ";\n\
\n\
enum LangKey {\n";
for (auto &entry : langpack_.entries) {
header_->stream() << "\t" << getFullKey(entry) << ",\n";
}
header_->stream() << "\
\n\
lngkeys_cnt,\n\
kLangKeysCount,\n\
};\n\
\n\
LangString lang(LangKey key);\n\
QString lang(LangKey key);\n\
\n\
LangString langOriginal(LangKey key);\n\
QString langOriginal(LangKey key);\n\
\n";
for (auto &entry : langpack_.entries) {
if (!entry.tags.empty()) {
auto &key = entry.key;
auto params = QStringList();
auto invokations = QStringList();
auto applyTags = QStringList();
for (auto &tagData : entry.tags) {
auto &tag = tagData.tag;
auto isPlural = isTagPlural(key, tag);
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() << "\
inline LangString " << entry.key << "(" << params.join(QString(", ")) << ") {\n\
return lang(" << entry.key << "__tagged)." << invokations.join('.') << ";\n\
inline QString " << entry.key << "(" << params.join(QString(", ")) << ") {\n\
auto result = lang(" << entry.key << "__tagged);\n\
" << applyTags.join('\n') << ";\n\
return result;\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();
}
bool Generator::writeSource() {
source_ = std::make_unique<common::CppFile>(basePath_ + ".cpp", project_);
source_->include("lang/lang_keys.h").pushNamespace().stream() << "\
const char *_langKeyNames[lngkeys_cnt] = {\n\
source_->include("lang/lang_keys.h").pushNamespace("Lang").pushNamespace().stream() << "\
const char *KeyNames[kLangKeysCount] = {\n\
\n";
for (auto &entry : langpack_.entries) {
source_->stream() << "\"" << entry.key << "\",\n";
@ -191,15 +191,15 @@ const char *_langKeyNames[lngkeys_cnt] = {\n\
\n\
};\n\
\n\
LangString _langValues[lngkeys_cnt], _langValuesOriginal[lngkeys_cnt];\n\
QString Values[kLangKeysCount], OriginalValues[kLangKeysCount];\n\
\n\
void set(LangKey key, const QString &val) {\n\
_langValues[key] = val;\n\
Values[key] = val;\n\
}\n\
\n\
class LangInit {\n\
class Initializer {\n\
public:\n\
LangInit() {\n";
Initializer() {\n";
for (auto &entry : langpack_.entries) {
source_->stream() << "\t\tset(" << getFullKey(entry) << ", QString::fromUtf8(" << stringToEncodedString(entry.value) << "));\n";
}
@ -208,24 +208,16 @@ public:\n\
\n\
};\n\
\n\
LangInit _langInit;\n\
Initializer Instance;\n\
\n";
source_->popNamespace().stream() << "\
\n\
LangString lang(LangKey key) {\n\
return (key < 0 || key > lngkeys_cnt) ? QString() : _langValues[key];\n\
const char *GetKeyName(LangKey key) {\n\
return (key < 0 || key >= kLangKeysCount) ? \"\" : KeyNames[key];\n\
}\n\
\n\
LangString langOriginal(LangKey key) {\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\
ushort GetTagIndex(QLatin1String tag) {\n\
auto size = tag.size();\n\
auto data = tag.data();\n";
@ -236,12 +228,12 @@ ushort LangLoader::tagIndex(QLatin1String tag) const {\n\
writeSetSearch(tagsSet, [](const QString &tag) {
return "lt_" + tag;
}, "lngtags_cnt");
}, "kTagsCount");
source_->stream() << "\
}\n\
\n\
LangKey LangLoader::keyIndex(QLatin1String key) const {\n\
LangKey GetKeyIndex(QLatin1String key) {\n\
auto size = key.size();\n\
auto data = key.data();\n";
@ -262,12 +254,41 @@ LangKey LangLoader::keyIndex(QLatin1String key) const {\n\
writeSetSearch(keysSet, [&taggedKeys](const QString &key) {
auto it = taggedKeys.find(key);
return (it != taggedKeys.end()) ? it->second : key;
}, "lngkeys_cnt");
}, "kLangKeysCount");
source_->stream() << "\
}\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";
for (auto &entry : langpack_.entries) {
@ -293,45 +314,23 @@ bool LangLoader::tagReplaced(LangKey key, ushort tag) const {\n\
return false;\n\
}\n\
\n\
LangKey LangLoader::subkeyIndex(LangKey key, ushort tag, ushort index) const {\n\
if (index >= lngtags_max_counted_values) return lngkeys_cnt;\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() << "\
void FeedKeyValue(LangKey key, const QString &value) {\n\
Expects(key >= 0 && key < kLangKeysCount);\n\
if (OriginalValues[key].isEmpty()) {\n\
OriginalValues[key] = Values[key].isEmpty() ? qsl(\"{}\") : Values[key];\n\
}\n\
Values[key] = value;\n\
}\n\
\n";
source_->popNamespace().stream() << "\
\n\
return lngkeys_cnt;\n\
QString lang(LangKey key) {\n\
return (key < 0 || key >= kLangKeysCount) ? QString() : Lang::Values[key];\n\
}\n\
\n\
bool LangLoader::feedKeyValue(LangKey key, const QString &value) {\n\
if (key < lngkeys_cnt) {\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\
QString langOriginal(LangKey key) {\n\
return (key < 0 || key >= kLangKeysCount || Lang::OriginalValues[key] == qsl(\"{}\")) ? QString() : (Lang::OriginalValues[key].isEmpty() ? Lang::Values[key] : Lang::OriginalValues[key]);\n\
}\n";
return source_->finalize();

View File

@ -353,7 +353,7 @@ bool HistoryHider::offerPeer(PeerId peer) {
return false;
}
_offered = App::peer(peer);
LangString phrase;
auto phrase = QString();
QString recipient = _offered->isUser() ? _offered->name : '\xAB' + _offered->name + '\xBB';
if (_sharedContact) {
phrase = lng_forward_share_contact(lt_recipient, recipient);

View File

@ -64,8 +64,8 @@ Widget::Widget(QWidget *parent) : TWidget(parent)
if (cLang() == languageDefault) {
auto systemLangId = Sandbox::LangSystem();
if (systemLangId != languageDefault) {
LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[systemLangId].c_str() + qsl(".strings"), { lng_switch_to_this });
QString text = loader.found().value(lng_switch_to_this);
Lang::FileParser loader(qsl(":/langs/lang_") + LanguageCodes[systemLangId].c_str() + qsl(".strings"), { lng_switch_to_this });
auto text = loader.found().value(lng_switch_to_this);
if (!text.isEmpty()) {
_changeLanguage.create(this, object_ptr<Ui::LinkButton>(this, text), st::introCoverDuration);
_changeLanguage->entity()->setClickedCallback([this, systemLangId] { changeLanguage(systemLangId); });

View File

@ -22,192 +22,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "base/parse_helper.h"
bool LangLoaderPlain::readKeyValue(const char *&from, const char *end) {
using base::parse::skipWhitespaces;
if (!skipWhitespaces(from, end)) return false;
namespace Lang {
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));
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);
FileParser::FileParser(const QString &file, const std::set<LangKey> &request)
: _filePath(file)
, _request(request)
, _readingAll(request.find(kLangKeysCount) != request.end()) {
QFile f(_filePath);
if (!f.open(QIODevice::ReadOnly)) {
error(qsl("Could not open input file!"));
return;
@ -271,3 +92,226 @@ LangLoaderPlain::LangLoaderPlain(const QString &file, const std::set<LangKey> &r
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

View File

@ -22,22 +22,44 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "lang/lang_keys.h"
using LangLoaderResult = QMap<LangKey, LangString>;
class LangLoaderPlain : public LangLoader {
public:
LangLoaderPlain(const QString &file, const std::set<LangKey> &request = std::set<LangKey>());
namespace Lang {
LangLoaderResult found() const {
return result;
class FileParser {
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:
QString file;
std::set<LangKey> request;
private:
bool feedKeyValue(LangKey key, const QString &value);
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 readingAll;
LangLoaderResult result;
mutable QStringList _errorsList, _warningsList;
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

View File

@ -22,58 +22,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#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_VALUE "https://telegram.org/blog/desktop-1-0"
@ -85,84 +33,21 @@ QString langNewVersionText() {
#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
const QString &LangLoader::errors() const {
if (_errors.isEmpty() && !_err.isEmpty()) {
_errors = _err.join('\n');
}
return _errors;
}
const QString &LangLoader::warnings() const {
if (!_checked) {
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))));
bool langFirstNameGoesSecond() {
auto fullname = lang(lng_full_name__tagged);
for (auto begin = fullname.constData(), ch = begin, end = ch + fullname.size(); ch != end;) {
if (*ch == TextCommand) {
if (ch + 3 < end && (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;
}
}
}
_checked = true;
}
if (_warnings.isEmpty() && !_warn.isEmpty()) {
_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();
return false;
}

View File

@ -33,10 +33,8 @@ constexpr const str_const LanguageCodes[] = {
};
constexpr const int languageTest = -1, languageDefault = 0, languageCount = base::array_size(LanguageCodes);
const char *langKeyName(LangKey key);
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();
if (month <= 0 || month > 12) {
return qsl("MONTH_ERR");
@ -63,7 +61,7 @@ inline LangString langDateMaybeWithYear(QDate date, WithYear withYear, WithoutYe
return withoutYear(month, year);
}
inline LangString langDayOfMonth(const QDate &date) {
inline QString langDayOfMonth(const QDate &date) {
auto day = date.day();
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));
@ -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();
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));
@ -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");
}
inline LangString langMonth(const QDate &date) {
inline QString langMonth(const QDate &date) {
return langDateMaybeWithYear(date, [](int month, int year) {
return langMonthOfYear(month, 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");
}
inline LangString langMonthFull(const QDate &date) {
inline QString langMonthFull(const QDate &date) {
return langDateMaybeWithYear(date, [](int month, int year) {
return langMonthOfYearFull(month, 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");
}
inline LangString langDayOfWeek(const QDate &date) {
inline QString langDayOfWeek(const QDate &date) {
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");
}
inline LangString langDayOfWeekFull(const QDate &date) {
inline QString langDayOfWeekFull(const QDate &date) {
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()));
}
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()));
}
QString langNewVersionText();
QString langNewVersionTextForLang(int langId);
class LangLoader {
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;
}
bool langFirstNameGoesSecond();

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1455,38 +1455,6 @@ Dialogs::IndexedList *MainWidget::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) {
auto history = message.history;
auto &textWithTags = message.textWithTags;
@ -1503,11 +1471,10 @@ void MainWidget::sendMessage(const MessageToSend &message) {
auto prepareFlags = itemTextOptions(history, App::self()).flags;
QString sendingText, leftText = prepareTextWithEntities(textWithTags.text, prepareFlags, &leftEntities);
QString command = parseCommandFromMessage(history, textWithTags.text);
HistoryItem *lastMessage = nullptr;
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());
uint64 randomId = rand_value<uint64>();
@ -1561,8 +1528,6 @@ void MainWidget::sendMessage(const MessageToSend &message) {
history->lastSentMsg = lastMessage;
finishForwarding(history, message.silent);
executeParsedCommand(command);
}
void MainWidget::saveRecentHashtags(const QString &text) {

View File

@ -30,6 +30,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "apiwrap.h"
#include "calls/calls_instance.h"
#include "lang/lang_file_parser.h"
#include "lang/lang_translator.h"
#include "observer_peer.h"
#include "storage/file_upload.h"
#include "mainwidget.h"
@ -380,7 +381,7 @@ void Messenger::loadLanguage() {
}
if (cLang() == languageTest) {
if (QFileInfo(cLangFile()).exists()) {
LangLoaderPlain loader(cLangFile());
Lang::FileParser loader(cLangFile());
cSetLangErrors(loader.errors());
if (!cLangErrors().isEmpty()) {
LOG(("Lang load errors: %1").arg(cLangErrors()));
@ -391,14 +392,14 @@ void Messenger::loadLanguage() {
cSetLang(languageDefault);
}
} 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()) {
LOG(("Lang load errors: %1").arg(loader.errors()));
} else if (!loader.warnings().isEmpty()) {
LOG(("Lang load warnings: %1").arg(loader.warnings()));
}
}
_translator = std::make_unique<Translator>();
_translator = std::make_unique<Lang::Translator>();
QCoreApplication::instance()->installTranslator(_translator.get());
}

View File

@ -47,6 +47,10 @@ class Instance;
} // namespace Audio
} // namespace Media
namespace Lang {
class Translator;
} // namespace Lang
class Messenger final : public QObject, public RPCSender, private base::Subscriber {
Q_OBJECT
@ -189,7 +193,7 @@ private:
std::unique_ptr<MainWindow> _window;
FileUploader *_uploader = nullptr;
std::unique_ptr<Translator> _translator;
std::unique_ptr<Lang::Translator> _translator;
std::unique_ptr<MTP::DcOptions> _dcOptions;
std::unique_ptr<MTP::Instance> _mtproto;
std::unique_ptr<MTP::Instance> _mtprotoForKeysDestroy;

View File

@ -776,7 +776,7 @@ void MainWindow::updateIconCounters() {
iconOverlay.addPixmap(App::pixmapFromImageInPlace(iconWithCounter(-32, counter, bg, fg, false)));
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());
}
SetWindowPos(ps_hWnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);

View File

@ -213,9 +213,9 @@ void GeneralWidget::chooseCustomLang() {
}
_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()) {
LangLoaderResult result = loader.found();
auto result = loader.found();
auto text = result.value(lng_sure_save_language, langOriginal(lng_sure_save_language)),
save = result.value(lng_box_ok, langOriginal(lng_box_ok)),
cancel = result.value(lng_cancel, langOriginal(lng_cancel));

View File

@ -121,6 +121,10 @@ void fillCodes() {
}
});
});
Codes.insert(qsl("newversiontext"), [] {
App::wnd()->serviceNotificationLocal(langNewVersionText());
});
auto audioFilters = qsl("Audio files (*.wav *.mp3);;") + FileDialog::AllFilesFilter();
auto audioKeys = {
qsl("msg_incoming"),

View File

@ -177,10 +177,14 @@
<(src_loc)/intro/introsignup.h
<(src_loc)/intro/introstart.cpp
<(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.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.h
<(src_loc)/media/player/media_player_cover.cpp