From 157b25ea4305ea6ffc4dc442ad9e1843c2f48b32 Mon Sep 17 00:00:00 2001
From: AlexeyZavar <sltkval1@gmail.com>
Date: Wed, 25 Sep 2024 19:05:57 +0300
Subject: [PATCH] chore: refactor languages

---
 Telegram/Resources/langs/lang.strings         | 68 ++++---------
 Telegram/SourceFiles/ayu/ayu_infra.cpp        | 12 +--
 Telegram/SourceFiles/ayu/ayu_lang.cpp         | 99 ++++++++-----------
 Telegram/SourceFiles/ayu/ayu_lang.h           | 19 ++--
 .../SourceFiles/ayu/data/ayu_database.cpp     |  1 +
 Telegram/SourceFiles/ayu/data/entities.h      |  1 +
 .../SourceFiles/ayu/data/messages_storage.cpp |  2 +-
 .../SourceFiles/ayu/data/messages_storage.h   |  2 +-
 Telegram/SourceFiles/data/data_session.cpp    |  2 +-
 9 files changed, 80 insertions(+), 126 deletions(-)

diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 1ca622721..bd4508f87 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -5726,18 +5726,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "ayu_ImportDataSuccess" = "Database imported successfully.";
 "ayu_ImportDataFailure" = "Failed to import database.";
 "ayu_RegexFilters" = "Message Filters";
-"ayu_RegexFiltersAmount_zero" = "%1$d filters";
-"ayu_RegexFiltersAmount_one" = "1 filter";
-"ayu_RegexFiltersAmount_two" = "%1$d filters";
-"ayu_RegexFiltersAmount_few" = "%1$d filters";
-"ayu_RegexFiltersAmount_many" = "%1$d filters";
-"ayu_RegexFiltersAmount_other" = "%1$d filters";
-"ayu_RegexFiltersExcludedAmount_zero" = "%1$d excluded";
-"ayu_RegexFiltersExcludedAmount_one" = "1 excluded";
-"ayu_RegexFiltersExcludedAmount_two" = "%1$d excluded";
-"ayu_RegexFiltersExcludedAmount_few" = "%1$d excluded";
-"ayu_RegexFiltersExcludedAmount_many" = "%1$d excluded";
-"ayu_RegexFiltersExcludedAmount_other" = "%1$d excluded";
+"ayu_RegexFiltersAmount#one" = "1 filter";
+"ayu_RegexFiltersAmount#other" = "{count} filters";
+"ayu_RegexFiltersExcludedAmount#one" = "1 excluded";
+"ayu_RegexFiltersExcludedAmount#other" = "{count} excluded";
 "ayu_RegexFiltersHeader" = "Filters";
 "ayu_RegexFiltersShared" = "Shared Filters";
 "ayu_RegexFiltersExcluded" = "Excluded Filters";
@@ -5772,42 +5764,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "ayu_FiltersToastFailNoChanges" = "Filters are the same as current.";
 "ayu_FiltersToastSuccess" = "Filters successfully imported.";
 "ayu_FiltersSheetTitle" = "Apply filter changes?";
-"ayu_FiltersSheetNewFilters_zero" = "**%1$d** new filters";
-"ayu_FiltersSheetNewFilters_one" = "**1** new filter";
-"ayu_FiltersSheetNewFilters_two" = "**%1$d** new filters";
-"ayu_FiltersSheetNewFilters_few" = "**%1$d** new filters";
-"ayu_FiltersSheetNewFilters_many" = "**%1$d** new filters";
-"ayu_FiltersSheetNewFilters_other" = "**%1$d** new filters";
-"ayu_FiltersSheetRemovedFilters_zero" = "**%1$d** removed filters";
-"ayu_FiltersSheetRemovedFilters_one" = "**1** removed filter";
-"ayu_FiltersSheetRemovedFilters_two" = "**%1$d** removed filters";
-"ayu_FiltersSheetRemovedFilters_few" = "**%1$d** removed filters";
-"ayu_FiltersSheetRemovedFilters_many" = "**%1$d** removed filters";
-"ayu_FiltersSheetRemovedFilters_other" = "**%1$d** removed filters";
-"ayu_FiltersSheetUpdatedFilters_zero" = "**%1$d** updated filters";
-"ayu_FiltersSheetUpdatedFilters_one" = "**1** updated filter";
-"ayu_FiltersSheetUpdatedFilters_two" = "**%1$d** updated filters";
-"ayu_FiltersSheetUpdatedFilters_few" = "**%1$d** updated filters";
-"ayu_FiltersSheetUpdatedFilters_many" = "**%1$d** updated filters";
-"ayu_FiltersSheetUpdatedFilters_other" = "**%1$d** updated filters";
-"ayu_FiltersSheetNewExclusions_zero" = "**%1$d** new exclusions";
-"ayu_FiltersSheetNewExclusions_one" = "**1** new exclusion";
-"ayu_FiltersSheetNewExclusions_two" = "**%1$d** new exclusions";
-"ayu_FiltersSheetNewExclusions_few" = "**%1$d** new exclusions";
-"ayu_FiltersSheetNewExclusions_many" = "**%1$d** new exclusions";
-"ayu_FiltersSheetNewExclusions_other" = "**%1$d** new exclusions";
-"ayu_FiltersSheetRemovedExclusions_zero" = "**%1$d** removed exclusions";
-"ayu_FiltersSheetRemovedExclusions_one" = "**1** removed exclusion";
-"ayu_FiltersSheetRemovedExclusions_two" = "**%1$d** removed exclusions";
-"ayu_FiltersSheetRemovedExclusions_few" = "**%1$d** removed exclusions";
-"ayu_FiltersSheetRemovedExclusions_many" = "**%1$d** removed exclusions";
-"ayu_FiltersSheetRemovedExclusions_other" = "**%1$d** removed exclusions";
-"ayu_FiltersSheetDialogsToResolve_zero" = "**%1$d** dialogs to resolve";
-"ayu_FiltersSheetDialogsToResolve_one" = "**1** dialog to resolve";
-"ayu_FiltersSheetDialogsToResolve_two" = "**%1$d** dialogs to resolve";
-"ayu_FiltersSheetDialogsToResolve_few" = "**%1$d** dialogs to resolve";
-"ayu_FiltersSheetDialogsToResolve_many" = "**%1$d** dialogs to resolve";
-"ayu_FiltersSheetDialogsToResolve_other" = "**%1$d** dialogs to resolve";
+"ayu_FiltersSheetNewFilters#one" = "**1** new filter";
+"ayu_FiltersSheetNewFilters#other" = "**{count}** new filters";
+"ayu_FiltersSheetRemovedFilters#one" = "**1** removed filter";
+"ayu_FiltersSheetRemovedFilters#other" = "**{count}** removed filters";
+"ayu_FiltersSheetUpdatedFilters#one" = "**1** updated filter";
+"ayu_FiltersSheetUpdatedFilters#other" = "**{count}** updated filters";
+"ayu_FiltersSheetNewExclusions#one" = "**1** new exclusion";
+"ayu_FiltersSheetNewExclusions#other" = "**{count}** new exclusions";
+"ayu_FiltersSheetRemovedExclusions#one" = "**1** removed exclusion";
+"ayu_FiltersSheetRemovedExclusions#other" = "**{count}** removed exclusions";
+"ayu_FiltersSheetDialogsToResolve#one" = "**1** dialog to resolve";
+"ayu_FiltersSheetDialogsToResolve#other" = "**{count}** dialogs to resolve";
 "ayu_FiltersClearPopupTitle" = "Clear filters";
 "ayu_FiltersClearPopupText" = "Are you sure you want to clear all filters?";
 "ayu_FiltersClearPopupActionText" = "Clear";
@@ -5912,8 +5880,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "ayu_AyuForwardStatusLoadingMedia" = "Loading media";
 "ayu_AyuForwardStatusForwarding" = "Forwarding messages";
 "ayu_AyuForwardStatusFinished" = "Done";
-"ayu_AyuForwardStatusSentCount" = "sent %1$d of %2$d";
-"ayu_AyuForwardStatusChunkCount" = "chunk %1$d of %2$d";
+"ayu_AyuForwardStatusSentCount" = "sent {count1} of {count2}";
+"ayu_AyuForwardStatusChunkCount" = "chunk {count1} of {count2}";
 "ayu_JumpToBeginning" = "To Beginning";
 "ayu_ExpireMediaContextMenuText" = "Burn";
 "ayu_ExpiringVoiceMessageNote" = "This voice message can be played as many times as you want.";
diff --git a/Telegram/SourceFiles/ayu/ayu_infra.cpp b/Telegram/SourceFiles/ayu/ayu_infra.cpp
index cb92fc822..28f270b79 100644
--- a/Telegram/SourceFiles/ayu/ayu_infra.cpp
+++ b/Telegram/SourceFiles/ayu/ayu_infra.cpp
@@ -17,14 +17,14 @@
 namespace AyuInfra {
 
 void initLang() {
-	QString langPackBaseId = Lang::GetInstance().baseId();
-	QString langPackId = Lang::GetInstance().id();
-	if (langPackId.isEmpty()) {
-		LOG(("Lang ID not found! Re-use old language pack..."));
+	QString id = Lang::GetInstance().id();
+	QString baseId = Lang::GetInstance().baseId();
+	if (id.isEmpty()) {
+		LOG(("Language ID not found!"));
 		return;
 	}
-	CustomLangPack::initInstance();
-	CustomLangPack::currentInstance()->fetchCustomLangPack(langPackId, langPackBaseId);
+	AyuLanguage::init();
+	AyuLanguage::currentInstance()->fetchLanguage(id, baseId);
 }
 
 void initFonts() {
diff --git a/Telegram/SourceFiles/ayu/ayu_lang.cpp b/Telegram/SourceFiles/ayu/ayu_lang.cpp
index 892cf5d00..678765965 100644
--- a/Telegram/SourceFiles/ayu/ayu_lang.cpp
+++ b/Telegram/SourceFiles/ayu/ayu_lang.cpp
@@ -20,49 +20,45 @@ std::map<QString, QString> langMapping = {
 	{"zh-hant-raw", "zh-hant"},
 };
 
-CustomLangPack *CustomLangPack::instance = nullptr;
+AyuLanguage *AyuLanguage::instance = nullptr;
 
-CustomLangPack::CustomLangPack() = default;
+AyuLanguage::AyuLanguage() = default;
 
-void CustomLangPack::initInstance() {
-	if (!instance) instance = new CustomLangPack;
+void AyuLanguage::init() {
+	if (!instance) instance = new AyuLanguage;
 }
 
-CustomLangPack *CustomLangPack::currentInstance() {
+AyuLanguage *AyuLanguage::currentInstance() {
 	return instance;
 }
 
-void CustomLangPack::fetchCustomLangPack(const QString &langPackId, const QString &langPackBaseId) {
-	LOG(("Current Language pack ID: %1, Base ID: %2").arg(langPackId, langPackBaseId));
+void AyuLanguage::fetchLanguage(const QString &id, const QString &baseId) {
+	auto finalLangPackId = langMapping.contains(id) ? langMapping[id] : id;
 
-	auto finalLangPackId = langMapping.contains(langPackId) ? langMapping[langPackId] : langPackId;
-
-	const auto proxy = Core::App().settings().proxy().isEnabled()
-						   ? Core::App().settings().proxy().selected()
-						   : MTP::ProxyData();
-	if (proxy.type == MTP::ProxyData::Type::Socks5 || proxy.type == MTP::ProxyData::Type::Http) {
-		QNetworkProxy LocaleProxy = ToNetworkProxy(ToDirectIpProxy(proxy));
-		networkManager.setProxy(LocaleProxy);
+	if (Core::App().settings().proxy().isEnabled()) {
+		const auto proxy = Core::App().settings().proxy().selected();
+		if (proxy.type == MTP::ProxyData::Type::Socks5 || proxy.type == MTP::ProxyData::Type::Http) {
+			const auto networkProxy = ToNetworkProxy(ToDirectIpProxy(Core::App().settings().proxy().selected()));
+			networkManager.setProxy(networkProxy);
+		}
 	}
 
 	// using `jsdelivr` since China (...and maybe other?) users have some problems with GitHub
 	// https://crowdin.com/project/ayugram/discussions/6
 	QUrl url;
-	if (!finalLangPackId.isEmpty() && !langPackBaseId.isEmpty() && !needFallback) {
+	if (!finalLangPackId.isEmpty() && !baseId.isEmpty() && !needFallback) {
 		url.setUrl(qsl("https://cdn.jsdelivr.net/gh/AyuGram/Languages@l10n_main/values/langs/%1/Shared.json").arg(
 			finalLangPackId));
 	} else {
 		url.setUrl(qsl("https://cdn.jsdelivr.net/gh/AyuGram/Languages@l10n_main/values/langs/%1/Shared.json").arg(
-			needFallback ? langPackBaseId : finalLangPackId));
+			needFallback ? baseId : finalLangPackId));
 	}
 	_chkReply = networkManager.get(QNetworkRequest(url));
 	connect(_chkReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(fetchError(QNetworkReply::NetworkError)));
 	connect(_chkReply, SIGNAL(finished()), this, SLOT(fetchFinished()));
-	LOG(("Fetching %1 lang pack...").arg(
-		needFallback ? (langPackBaseId.isEmpty() ? finalLangPackId : langPackBaseId) : finalLangPackId));
 }
 
-void CustomLangPack::fetchFinished() {
+void AyuLanguage::fetchFinished() {
 	if (!_chkReply) return;
 
 	QString langPackBaseId = Lang::GetInstance().baseId();
@@ -70,72 +66,61 @@ void CustomLangPack::fetchFinished() {
 	auto statusCode = _chkReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
 
 	if (statusCode == 404 && !langPackId.isEmpty() && !langPackBaseId.isEmpty() && !needFallback) {
-		LOG(("AyuGram Language pack not found! Fallback to main language: %1...").arg(langPackBaseId));
+		LOG(("AyuGram Language not found! Fallback to main language: %1...").arg(langPackBaseId));
 		needFallback = true;
 		_chkReply->disconnect();
-		fetchCustomLangPack("", langPackBaseId);
+		fetchLanguage("", langPackBaseId);
 	} else {
-		QByteArray result = _chkReply->readAll().trimmed();
+		const auto result = _chkReply->readAll().trimmed();
 		QJsonParseError error{};
-		QJsonDocument str = QJsonDocument::fromJson(result, &error);
+		const auto doc = QJsonDocument::fromJson(result, &error);
 		if (error.error == QJsonParseError::NoError) {
-			parseLangFile(str);
+			applyLanguageJson(doc);
 		} else {
-			LOG(("Incorrect JSON File. Fallback to default language: English..."));
-			loadDefaultLangFile();
+			LOG(("Incorrect language JSON File."));
 		}
 
 		_chkReply = nullptr;
 	}
 }
 
-void CustomLangPack::fetchError(QNetworkReply::NetworkError e) {
+void AyuLanguage::fetchError(QNetworkReply::NetworkError e) {
 	LOG(("Network error: %1").arg(e));
 
 	if (e == QNetworkReply::NetworkError::ContentNotFoundError) {
-		QString langPackBaseId = Lang::GetInstance().baseId();
-		QString langPackId = Lang::GetInstance().id();
+		const auto baseId = Lang::GetInstance().baseId();
+		const auto id = Lang::GetInstance().id();
 
-		if (!langPackId.isEmpty() && !langPackBaseId.isEmpty() && !needFallback) {
-			LOG(("AyuGram Language pack not found! Fallback to main language: %1...").arg(langPackBaseId));
+		if (!id.isEmpty() && !baseId.isEmpty() && !needFallback) {
+			LOG(("AyuGram Language not found! Fallback to main language: %1...").arg(baseId));
 			needFallback = true;
 			_chkReply->disconnect();
-			fetchCustomLangPack("", langPackBaseId);
+			fetchLanguage("", baseId);
 		} else {
-			LOG(("AyuGram Language pack not found! Fallback to default language: English..."));
-			loadDefaultLangFile();
+			LOG(("AyuGram Language not found!"));
 			_chkReply = nullptr;
 		}
 	}
 }
 
-void CustomLangPack::loadDefaultLangFile() {
-	QFile file(":/localization/en.json");
-	if (file.open(QIODevice::ReadOnly)) {
-		QJsonDocument str = QJsonDocument::fromJson(file.readAll());
-		QJsonObject json = str.object();
-		for (const QString &key : json.keys()) {
-			Lang::GetInstance().applyValue(key.toUtf8(), json.value(key).toString().toUtf8());
-		}
-		Lang::GetInstance().updatePluralRules();
-		file.close();
-	}
-}
-
-void CustomLangPack::parseLangFile(QJsonDocument str) {
-	QJsonObject json = str.object();
+void AyuLanguage::applyLanguageJson(QJsonDocument doc) {
+	const auto json = doc.object();
 	for (const QString &brokenKey : json.keys()) {
 		auto key = qsl("ayu_") + brokenKey;
-		auto val = json.value(brokenKey).toString().replace(qsl("&amp;"), qsl("&")).toUtf8();
+		const auto val = json.value(brokenKey).toString().replace(qsl("&amp;"), qsl("&")).toUtf8();
+
+		if (key.endsWith("_zero") || key.endsWith("_two") || key.endsWith("_few") || key.endsWith("_many")) {
+			continue;
+		}
+
+		if (key.endsWith("_one")) {
+			key = key.replace("_one", "#one");
+		} else if (key.endsWith("_other")) {
+			key = key.replace("_other", "#other");
+		}
 
 		Lang::GetInstance().resetValue(key.toUtf8());
 		Lang::GetInstance().applyValue(key.toUtf8(), val);
-		if (key.contains("#other")) {
-			Lang::GetInstance().resetValue(key.toUtf8().replace("#other", "#few"));
-			Lang::GetInstance().resetValue(key.toUtf8().replace("#other", "#few"));
-			Lang::GetInstance().applyValue(key.toUtf8().replace("#other", "#few"), val);
-			Lang::GetInstance().applyValue(key.toUtf8().replace("#other", "#many"), val);
-		}
 	}
 	Lang::GetInstance().updatePluralRules();
 }
diff --git a/Telegram/SourceFiles/ayu/ayu_lang.h b/Telegram/SourceFiles/ayu/ayu_lang.h
index 82c58aa7a..353b6e4c2 100644
--- a/Telegram/SourceFiles/ayu/ayu_lang.h
+++ b/Telegram/SourceFiles/ayu/ayu_lang.h
@@ -9,27 +9,26 @@
 #include <QtNetwork/QNetworkReply>
 #include <QtXml/QDomDocument>
 
-class CustomLangPack : public QObject
+class AyuLanguage : public QObject
 {
 	Q_OBJECT
-	Q_DISABLE_COPY(CustomLangPack)
+	Q_DISABLE_COPY(AyuLanguage)
 
 public:
-	static CustomLangPack *currentInstance();
-	static void initInstance();
-	static CustomLangPack *instance;
+	static AyuLanguage *currentInstance();
+	static void init();
+	static AyuLanguage *instance;
 
-	void fetchCustomLangPack(const QString &langPackId, const QString &langPackBaseId);
-	void loadDefaultLangFile();
-	void parseLangFile(QJsonDocument str);
+	void fetchLanguage(const QString &id, const QString &baseId);
+	void applyLanguageJson(QJsonDocument doc);
 
 public Q_SLOTS:
 	void fetchFinished();
 	void fetchError(QNetworkReply::NetworkError e);
 
 private:
-	CustomLangPack();
-	~CustomLangPack() override = default;
+	AyuLanguage();
+	~AyuLanguage() override = default;
 
 	QNetworkAccessManager networkManager;
 	QNetworkReply *_chkReply = nullptr;
diff --git a/Telegram/SourceFiles/ayu/data/ayu_database.cpp b/Telegram/SourceFiles/ayu/data/ayu_database.cpp
index aba6ede3d..4fff86e8c 100644
--- a/Telegram/SourceFiles/ayu/data/ayu_database.cpp
+++ b/Telegram/SourceFiles/ayu/data/ayu_database.cpp
@@ -105,6 +105,7 @@ auto storage = make_storage(
 		make_column("id", &RegexFilter::id),
 		make_column("text", &RegexFilter::text),
 		make_column("enabled", &RegexFilter::enabled),
+		make_column("reversed", &RegexFilter::reversed),
 		make_column("caseInsensitive", &RegexFilter::caseInsensitive),
 		make_column("dialogId", &RegexFilter::dialogId)
 	),
diff --git a/Telegram/SourceFiles/ayu/data/entities.h b/Telegram/SourceFiles/ayu/data/entities.h
index 06d4dee04..c67066e6b 100644
--- a/Telegram/SourceFiles/ayu/data/entities.h
+++ b/Telegram/SourceFiles/ayu/data/entities.h
@@ -78,6 +78,7 @@ public:
 	std::vector<char> id;
 	std::string text;
 	bool enabled;
+	bool reversed;
 	bool caseInsensitive;
 	std::unique_ptr<ID> dialogId; // nullable
 };
diff --git a/Telegram/SourceFiles/ayu/data/messages_storage.cpp b/Telegram/SourceFiles/ayu/data/messages_storage.cpp
index 8e9ab9fed..48bc04e80 100644
--- a/Telegram/SourceFiles/ayu/data/messages_storage.cpp
+++ b/Telegram/SourceFiles/ayu/data/messages_storage.cpp
@@ -86,7 +86,7 @@ void map(not_null<HistoryItem*> item, AyuMessageBase &message) {
 	// message.mimeType
 }
 
-void addEditedMessage(HistoryMessageEdition &edition, not_null<HistoryItem*> item) {
+void addEditedMessage(not_null<HistoryItem *> item) {
 	EditedMessage message;
 	map(item, message);
 
diff --git a/Telegram/SourceFiles/ayu/data/messages_storage.h b/Telegram/SourceFiles/ayu/data/messages_storage.h
index dd04afb3d..02993a628 100644
--- a/Telegram/SourceFiles/ayu/data/messages_storage.h
+++ b/Telegram/SourceFiles/ayu/data/messages_storage.h
@@ -12,7 +12,7 @@
 
 namespace AyuMessages {
 
-void addEditedMessage(HistoryMessageEdition &edition, not_null<HistoryItem*> item);
+void addEditedMessage(not_null<HistoryItem *> item);
 std::vector<AyuMessageBase> getEditedMessages(not_null<HistoryItem*> item, ID minId, ID maxId, int totalLimit);
 bool hasRevisions(not_null<HistoryItem*> item);
 
diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp
index 7211aa87e..899aab1fa 100644
--- a/Telegram/SourceFiles/data/data_session.cpp
+++ b/Telegram/SourceFiles/data/data_session.cpp
@@ -2378,7 +2378,7 @@ void Session::updateEditedMessage(const MTPMessage &data) {
 			goto proceed;
 		}
 
-		AyuMessages::addEditedMessage(edit, existing);
+		AyuMessages::addEditedMessage(existing);
 	}
 
 proceed: