/* 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 GeneratePluralRulesMap() { auto result = QMap(); 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::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