tdesktop/Telegram/SourceFiles/lang/lang_tag.cpp

196 lines
5.7 KiB
C++

/*
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 {
namespace {
//
// http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html
//
constexpr auto kShiftZero = ushort(0);
constexpr auto kShiftOne = ushort(1);
constexpr auto kShiftTwo = ushort(2);
constexpr auto kShiftFew = ushort(3);
constexpr auto kShiftMany = ushort(4);
constexpr auto kShiftOther = ushort(5);
//
// n absolute value of the source number (integer and decimals).
// i integer digits of n.
// v number of visible fraction digits in n, with trailing zeros.
// w number of visible fraction digits in n, without trailing zeros.
// f visible fractional digits in n, with trailing zeros.
// t visible fractional digits in n, without trailing zeros.
//
// Let n be int, being -1 for non-integer numbers and n == i for integer numbers.
// It is fine while in the rules we compare n only to integers.
//
// -123.450: n = -1, i = 123, v = 3, w = 2, f = 450, t = 45
//
using ChoosePluralMethod = ushort (*)(int n, int i, int v, int w, int f, int t);
ushort ChoosePluralAr(int n, int i, int v, int w, int f, int t) {
if (n == 0) {
return kShiftZero;
} else if (n == 1) {
return kShiftOne;
} else if (n == 2) {
return kShiftTwo;
} else if (n < 0) {
return kShiftOther;
}
auto mod100 = (n % 100);
if (mod100 >= 3 && mod100 <= 10) {
return kShiftFew;
} else if (mod100 >= 11 && mod100 <= 99) {
return kShiftMany;
}
return kShiftOther;
}
ushort ChoosePluralEn(int n, int i, int v, int w, int f, int t) {
if (i == 1 && v == 0) {
return kShiftOne;
}
return kShiftOther;
}
ushort ChoosePluralPt(int n, int i, int v, int w, int f, int t) {
if (i == 0 || i == 1) {
return kShiftOne;
}
return kShiftOther;
}
ushort ChoosePluralEs(int n, int i, int v, int w, int f, int t) {
if (n == 1) {
return kShiftOne;
}
return kShiftOther;
}
ushort ChoosePluralKo(int n, int i, int v, int w, int f, int t) {
return kShiftOther;
}
ushort ChoosePluralRu(int n, int i, int v, int w, int f, int t) {
if (v == 0) {
auto mod10 = (i % 10);
auto mod100 = (i % 100);
if ((mod10 == 1) && (mod100 != 11)) {
return kShiftOne;
} else if ((mod10 >= 2) && (mod10 <= 4) && (mod100 < 12 || mod100 > 14)) {
return kShiftFew;
} else {
return kShiftMany;
}
}
return kShiftMany;// kShiftOther;
}
QMap<QString, ChoosePluralMethod> GeneratePluralRulesMap() {
auto result = QMap<QString, ChoosePluralMethod>();
result.insert(qsl("ar"), ChoosePluralAr);
// result.insert(qsl("de"), ChoosePluralEn);
// result.insert(qsl("en"), ChoosePluralEn); // En is default, so we don't fill it inside the map.
result.insert(qsl("es"), ChoosePluralEs);
// result.insert(qsl("it"), ChoosePluralEn);
result.insert(qsl("ko"), ChoosePluralKo);
// result.insert(qsl("nl"), ChoosePluralEn);
result.insert(qsl("pt"), ChoosePluralPt);
result.insert(qsl("ru"), ChoosePluralRu);
return result;
}
ChoosePluralMethod ChoosePlural = ChoosePluralEn;
} // namespace
int FindTagReplacementPosition(const QString &original, ushort tag) {
for (auto s = original.constData(), ch = s, e = ch + original.size(); ch != e;) {
if (*ch == TextCommand) {
if (ch + kTagReplacementSize <= e && (ch + 1)->unicode() == TextCommandLangTag && *(ch + 3) == TextCommand) {
if ((ch + 2)->unicode() == 0x0020 + tag) {
return ch - s;
} else {
ch += kTagReplacementSize;
}
} else {
auto next = textSkipCommand(ch, e);
if (next == ch) {
++ch;
} else {
ch = next;
}
}
} else {
++ch;
}
}
return -1;
}
PluralResult Plural(ushort keyBase, float64 value) {
// Simplified.
auto n = qAbs(value);
auto i = qFloor(n);
auto integer = (qCeil(n) == i);
auto v = integer ? 0 : 6;
auto w = v;
auto f = integer ? 0 : 111111;
auto t = integer ? 0 : 111111;
auto &langpack = Lang::Current();
auto useNonDefaultPlural = (ChoosePlural != ChoosePluralEn && langpack.isNonDefaultPlural(LangKey(keyBase)));
auto shift = (useNonDefaultPlural ? ChoosePlural : ChoosePluralEn)((integer ? i : -1), i, v, w, f, t);
auto string = langpack.getValue(LangKey(keyBase + shift));
if (i == qCeil(n)) {
return { string, QString::number(qRound(value)) };
}
return { string, QString::number(value) };
}
void UpdatePluralRules(const QString &languageId) {
static auto kMap = GeneratePluralRulesMap();
ChoosePlural = kMap.value(languageId.toLower(), ChoosePluralEn);
}
QString ReplaceTag<QString>::Replace(QString &&original, const QString &replacement, int replacementPosition) {
auto result = QString();
result.reserve(original.size() + replacement.size() - kTagReplacementSize);
if (replacementPosition > 0) {
result.append(original.midRef(0, replacementPosition));
}
result.append(replacement);
if (replacementPosition + kTagReplacementSize < original.size()) {
result.append(original.midRef(replacementPosition + kTagReplacementSize));
}
return result;
}
} // namespace Lang