feat: import custom language implementation from 64Gram

This commit is contained in:
ZavaruKitsu 2023-06-04 03:06:59 +03:00
parent 2e9f8bfec4
commit d24411fde5
13 changed files with 296 additions and 30 deletions

View file

@ -100,6 +100,8 @@ PRIVATE
ayu/ayu_state.h
ayu/ayu_settings.cpp
ayu/ayu_settings.h
ayu/ayu_lang.cpp
ayu/ayu_lang.h
ayu/boxes/confirmation_box.cpp
ayu/boxes/confirmation_box.h
ayu/boxes/edit_deleted_mark.cpp

View file

@ -3842,3 +3842,37 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_mac_hold_to_quit" = "Hold {text} to Quit";
// Keys finished
// AyuGram keys generated
"ayu_AyuPreferences" = "AyuGram Preferences";
"ayu_GhostEssentialsHeader" = "Ghost essentials";
"ayu_SendReadPackets" = "Send read status";
"ayu_SendOnlinePackets" = "Send online status";
"ayu_SendUploadProgress" = "Send typing & upload status";
"ayu_SendOfflinePacketAfterOnline" = "Immediate offline after online";
"ayu_MarkReadAfterSend" = "Send read status after reply";
"ayu_UseScheduledMessages" = "Schedule messages";
"ayu_SpyEssentialsHeader" = "Spy essentials";
"ayu_KeepDeletedMessages" = "Keep deleted messages";
"ayu_KeepMessagesHistory" = "Keep edits history";
"ayu_QoLTogglesHeader" = "QoL toggles";
"ayu_RealForwardTime" = "Show real forward time";
"ayu_ShowFromChannel" = "Show «channel» label";
"ayu_KeepAliveService" = "Keep Alive Service";
"ayu_EnableAds" = "Enable ads";
"ayu_CustomizationHeader" = "Customization";
"ayu_DeletedMarkText" = "Deleted mark";
"ayu_ShowGhostToggleInDrawer" = "Show ghost mode toggle";
"ayu_CleanDatabase" = "Clean database";
"ayu_CleanDatabaseNotification" = "AyuGram database cleaned";
"ayu_EnableGhostMode" = "Enable Ghost";
"ayu_DisableGhostMode" = "Disable Ghost";
"ayu_GhostModeEnabled" = "Ghost mode turned on";
"ayu_GhostModeDisabled" = "Ghost mode turned off";
"ayu_EditsHistoryTitle" = "Edits history";
"ayu_EditsHistoryMenuText" = "History";
"ayu_ReadUntilMenuText" = "Read until";
"ayu_LikelyOfflineStatus" = "offline ?";
"ayu_SettingsWatermark" = "AyuGram developed and maintained by Radolyn Labs";

View file

@ -0,0 +1,119 @@
#include "ayu_lang.h"
#include "qjsondocument.h"
#include "lang/lang_instance.h"
#include "core/application.h"
#include "core/core_settings.h"
CustomLangPack *CustomLangPack::instance = nullptr;
CustomLangPack::CustomLangPack() = default;
void CustomLangPack::initInstance() {
if (!instance)
instance = new CustomLangPack;
}
CustomLangPack *CustomLangPack::currentInstance() {
return instance;
}
void CustomLangPack::fetchCustomLangPack(const QString& langPackId, const QString& langPackBaseId) {
LOG(("Current Language pack ID: %1, Base ID: %2").arg(langPackId, langPackBaseId));
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 = MTP::ToNetworkProxy(MTP::ToDirectIpProxy(proxy));
networkManager.setProxy(LocaleProxy);
}
QUrl url;
if (!langPackId.isEmpty() && !langPackBaseId.isEmpty() && !needFallback) {
url.setUrl(qsl("https://raw.githubusercontent.com/AyuGram/localization/translations/desktop/%1.json").arg(langPackId));
} else {
url.setUrl(qsl("https://raw.githubusercontent.com/AyuGram/localization/translations/desktop/%1.json").arg(needFallback ? langPackBaseId : langPackId));
}
_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() ? langPackId : langPackBaseId) : langPackId));
}
void CustomLangPack::fetchFinished() {
if (!_chkReply) return;
QString langPackBaseId = Lang::GetInstance().baseId();
QString langPackId = Lang::GetInstance().id();
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));
needFallback = true;
_chkReply->disconnect();
fetchCustomLangPack("", langPackBaseId);
}
else {
QByteArray result = _chkReply->readAll().trimmed();
QJsonParseError error{};
QJsonDocument str = QJsonDocument::fromJson(result, &error);
if (error.error == QJsonParseError::NoError) {
parseLangFile(str);
} else {
LOG(("Incorrect JSON File. Fallback to default language: English..."));
loadDefaultLangFile();
}
_chkReply = nullptr;
}
}
void CustomLangPack::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();
if (!langPackId.isEmpty() && !langPackBaseId.isEmpty() && !needFallback) {
LOG(("AyuGram Language pack not found! Fallback to main language: %1...").arg(langPackBaseId));
needFallback = true;
_chkReply->disconnect();
fetchCustomLangPack("", langPackBaseId);
} else {
LOG(("AyuGram Language pack not found! Fallback to default language: English..."));
loadDefaultLangFile();
_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();
for (const QString& brokenKey : json.keys()) {
auto key = qsl("ayu_") + brokenKey;
auto val = json.value(brokenKey).toString().toUtf8();
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();
}

View file

@ -0,0 +1,30 @@
#pragma once
#include <QtNetwork/QNetworkReply>
#include <QtXml/QDomDocument>
class CustomLangPack : public QObject {
Q_OBJECT
Q_DISABLE_COPY(CustomLangPack)
public:
static CustomLangPack *currentInstance();
static void initInstance();
static CustomLangPack *instance;
void fetchCustomLangPack(const QString& langPackId, const QString& langPackBaseId);
void loadDefaultLangFile();
void parseLangFile(QJsonDocument str);
public Q_SLOTS:
void fetchFinished();
void fetchError(QNetworkReply::NetworkError e);
private:
CustomLangPack();
~CustomLangPack() = default;
QNetworkAccessManager networkManager;
QNetworkReply *_chkReply = nullptr;
bool needFallback = false;
};

View file

@ -1,5 +1,6 @@
#include "context_menu.h"
#include "history/history_inner_widget.h"
#include "lang_auto.h"
#include "ui/widgets/popup_menu.h"
#include "base/unixtime.h"
#include "styles/style_chat.h"
@ -36,7 +37,7 @@ namespace AyuUi {
void AyuPopupMenu::addReadUntilAction(HistoryItem *item) const {
const auto history = item->history();
_ayuSubMenu->addAction(QString("Read until"), [=]() {
_ayuSubMenu->addAction(tr::ayu_ReadUntilMenuText(tr::now), [=]() {
AyuState::setAllowSendReadPacket(true);
history->session().data().histories().readInboxOnNewMessage(item);
}, &st::menuIconShowInChat);

View file

@ -3,6 +3,7 @@
#include "ayu/ayu_settings.h"
#include "ayu/settings/settings_ayu.h"
#include "lang_auto.h"
#include "mainwindow.h"
#include "settings/settings_common.h"
#include "ui/wrap/vertical_layout.h"
@ -24,7 +25,7 @@
namespace Settings {
rpl::producer<QString> Ayu::title() {
return rpl::single(QString("AyuGram Settings"));
return tr::ayu_AyuPreferences();
}
Ayu::Ayu(
@ -38,11 +39,12 @@ namespace Settings {
not_null<Window::SessionController *> controller) {
auto settings = &AyuSettings::getInstance();
AddSubsectionTitle(container, rpl::single(QString("General")));
AddSkip(container);
AddSubsectionTitle(container, tr::ayu_GhostEssentialsHeader());
AddButton(
container,
rpl::single(QString("Send read packets")),
tr::ayu_SendReadPackets(),
st::settingsButtonNoIcon
)->toggleOn(
rpl::single(settings->sendReadPackets)
@ -56,7 +58,7 @@ namespace Settings {
AddButton(
container,
rpl::single(QString("Send online packets")),
tr::ayu_SendOnlinePackets(),
st::settingsButtonNoIcon
)->toggleOn(
rpl::single(settings->sendOnlinePackets)
@ -70,21 +72,7 @@ namespace Settings {
AddButton(
container,
rpl::single(QString("Send offline packet after online")),
st::settingsButtonNoIcon
)->toggleOn(
rpl::single(settings->sendOfflinePacketAfterOnline)
)->toggledValue(
) | rpl::filter([=](bool enabled) {
return (enabled != settings->sendOfflinePacketAfterOnline);
}) | rpl::start_with_next([=](bool enabled) {
settings->set_sendOfflinePacketAfterOnline(enabled);
Local::writeSettings();
}, container->lifetime());
AddButton(
container,
rpl::single(QString("Send typing & upload progress")),
tr::ayu_SendUploadProgress(),
st::settingsButtonNoIcon
)->toggleOn(
rpl::single(settings->sendUploadProgress)
@ -98,7 +86,21 @@ namespace Settings {
AddButton(
container,
rpl::single(QString("Use scheduled messages to keep offline")),
tr::ayu_SendOfflinePacketAfterOnline(),
st::settingsButtonNoIcon
)->toggleOn(
rpl::single(settings->sendOfflinePacketAfterOnline)
)->toggledValue(
) | rpl::filter([=](bool enabled) {
return (enabled != settings->sendOfflinePacketAfterOnline);
}) | rpl::start_with_next([=](bool enabled) {
settings->set_sendOfflinePacketAfterOnline(enabled);
Local::writeSettings();
}, container->lifetime());
AddButton(
container,
tr::ayu_UseScheduledMessages(),
st::settingsButtonNoIcon
)->toggleOn(
rpl::single(settings->useScheduledMessages)
@ -110,9 +112,14 @@ namespace Settings {
Local::writeSettings();
}, container->lifetime());
AddDivider(container);
AddSkip(container);
AddSubsectionTitle(container, tr::ayu_SpyEssentialsHeader());
AddButton(
container,
rpl::single(QString("Keep deleted messages")),
tr::ayu_KeepDeletedMessages(),
st::settingsButtonNoIcon
)->toggleOn(
rpl::single(settings->keepDeletedMessages)
@ -126,7 +133,7 @@ namespace Settings {
AddButton(
container,
rpl::single(QString("Keep messages' history")),
tr::ayu_KeepMessagesHistory(),
st::settingsButtonNoIcon
)->toggleOn(
rpl::single(settings->keepMessagesHistory)
@ -138,11 +145,16 @@ namespace Settings {
Local::writeSettings();
}, container->lifetime());
AddDivider(container);
AddSkip(container);
AddSubsectionTitle(container, tr::ayu_CustomizationHeader());
auto currentDeletedMark = lifetime().make_state<rpl::variable<QString>>();
auto btn = AddButtonWithLabel(
container,
rpl::single(QString("Deleted Mark")),
tr::ayu_DeletedMarkText(),
currentDeletedMark->changes(),
st::settingsButtonNoIcon
);
@ -160,7 +172,7 @@ namespace Settings {
auto btn2 = AddButtonWithLabel(
container,
rpl::single(QString("Edited Mark")),
rpl::single(QString("Edited mark")),
currentEditedMark->changes(),
st::settingsButtonNoIcon
);
@ -174,7 +186,7 @@ namespace Settings {
});
*currentEditedMark = settings->editedMark;
AddDividerText(container, rpl::single(QString("AyuGram developed and maintained by Radolyn Labs")));
AddDividerText(container, tr::ayu_SettingsWatermark());
}
void Ayu::setupContent(not_null<Window::SessionController *> controller) {

View file

@ -105,12 +105,14 @@ public:
|| (_base && _base->isNonDefaultPlural(key));
}
void resetValue(const QByteArray &key);
void applyValue(const QByteArray &key, const QByteArray &value);
void updatePluralRules();
private:
void setBaseId(const QString &baseId, const QString &pluralId);
void applyDifferenceToMe(const MTPDlangPackDifference &difference);
void applyValue(const QByteArray &key, const QByteArray &value);
void resetValue(const QByteArray &key);
void reset(const Language &language);
void fillFromCustomContent(
const QString &absolutePath,
@ -122,7 +124,6 @@ private:
const QString &absolutePath,
const QString &relativePath,
const QByteArray &content);
void updatePluralRules();
void updateChoosingStickerReplacement();
Instance *_derived = nullptr;

View file

@ -395,7 +395,7 @@ void SetupSections(
Calls::Id(),
{ &st::settingsIconCalls, kIconGreen });
addSection(
rpl::single(QString("AyuGram Settings")),
tr::ayu_AyuPreferences(),
Ayu::Id(),
{ &st::settingsPremiumIconStar, kIconPurple });

View file

@ -33,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtCore/QDirIterator>
#include "ayu/ayu_settings.h"
#include "ayu/ayu_lang.h"
#ifndef Q_OS_WIN
#include <unistd.h>
@ -1131,6 +1132,14 @@ void readLangPack() {
if (langpack.stream.status() == QDataStream::Ok) {
Lang::GetInstance().fillFromSerialized(data, langpack.version);
}
QString langPackBaseId = Lang::GetInstance().baseId();
QString langPackId = Lang::GetInstance().id();
if (langPackId.isEmpty()) {
LOG(("Lang ID not found! Re-use old language pack..."));
return;
}
CustomLangPack::initInstance();
CustomLangPack::currentInstance()->fetchCustomLangPack(langPackId, langPackBaseId);
}
void writeLangPack() {

1
ayu-scripts/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
venv

View file

@ -0,0 +1,3 @@
{
"ayu_SettingsWatermark": "AyuGram developed and maintained by Radolyn Labs"
}

View file

@ -0,0 +1,9 @@
beautifulsoup4==4.12.2
bs4==0.0.1
certifi==2023.5.7
charset-normalizer==3.1.0
idna==3.4
lxml==4.9.2
requests==2.31.0
soupsieve==2.4.1
urllib3==2.0.2

View file

@ -0,0 +1,45 @@
import json
import os.path
import sys
import bs4
import requests
if os.path.exists('desktop-specific.json'):
os.chdir('../')
req = requests.get(
'https://raw.githubusercontent.com/AyuGram/AyuGram4A/rewrite/TMessagesProj/src/main/res/values/ayu.xml'
)
tree = bs4.BeautifulSoup(req.text, 'xml')
strings = {}
for string in tree.find_all('string'):
t = string.attrs['name']
strings[f'ayu_{t}'] = string.text
with open('./ayu-scripts/desktop-specific.json') as f:
data = json.load(f)
strings.update(data)
req = requests.get(
'https://raw.githubusercontent.com/telegramdesktop/tdesktop/dev/Telegram/Resources/langs/lang.strings'
)
data = req.text
data += '''
// AyuGram keys generated
'''
for k, v in strings.items():
data += f'"{k}" = "{v}";\n'
with open(os.path.realpath('./Telegram/Resources/langs/lang.strings'), 'w', encoding='utf-8') as f:
f.write(data)
print('Done.')