diff --git a/Telegram/SourceFiles/ayu/features/translator/implementations/google.cpp b/Telegram/SourceFiles/ayu/features/translator/implementations/google.cpp index 71dbcf34d2..9284b7deaa 100644 --- a/Telegram/SourceFiles/ayu/features/translator/implementations/google.cpp +++ b/Telegram/SourceFiles/ayu/features/translator/implementations/google.cpp @@ -8,17 +8,60 @@ #include #include +#include +#include +#include #include +#include #include #include #include #include -#include +#include #include "ayu/features/translator/html_parser.h" namespace Ayu::Translator { +namespace { + +constexpr auto kGoogleTranslateUrl = "https://translate-pa.googleapis.com/v1/translateHtml"; +constexpr auto kGoogleContentType = "application/json+protobuf"; +constexpr auto kGoogleDefaultApiKey = "AIzaSyATBXajvzQLTDHEQbcpq0Ihe0vWDHmO520"; + +QString decodeHtmlEntities(const QString &text) { + QTextDocument doc; + doc.setHtml(text); + return doc.toPlainText(); +} + +QStringList collectStrings(const QJsonValue &value) { + QStringList result; + if (value.isString()) { + result.push_back(value.toString()); + return result; + } + if (value.isArray()) { + const auto arr = value.toArray(); + for (const auto &item : arr) { + result.append(collectStrings(item)); + } + return result; + } + if (value.isObject()) { + const auto obj = value.toObject(); + if (obj.contains(QStringLiteral("text"))) { + result.append(collectStrings(obj.value(QStringLiteral("text")))); + } + if (obj.contains(QStringLiteral("trans"))) { + result.append(collectStrings(obj.value(QStringLiteral("trans")))); + } + } + return result; +} + +} // namespace + GoogleTranslator &GoogleTranslator::instance() { static GoogleTranslator inst; return inst; @@ -45,24 +88,26 @@ QPointer GoogleTranslator::startSingleTranslation( const auto from = fromLang.trimmed().isEmpty() ? QStringLiteral("auto") : fromLang.trimmed(); const auto to = toLang.trimmed(); - QUrl url(QStringLiteral("https://translate.googleapis.com/translate_a/single")); - QUrlQuery query; - query.addQueryItem(QStringLiteral("dj"), QStringLiteral("1")); - query.addQueryItem(QStringLiteral("q"), shouldWrapInHtml() ? Html::entitiesToHtml(text) : text.text); - query.addQueryItem(QStringLiteral("sl"), from); - query.addQueryItem(QStringLiteral("tl"), to); - query.addQueryItem(QStringLiteral("ie"), QStringLiteral("UTF-8")); - query.addQueryItem(QStringLiteral("oe"), QStringLiteral("UTF-8")); - query.addQueryItem(QStringLiteral("client"), QStringLiteral("at")); - query.addQueryItem(QStringLiteral("dt"), QStringLiteral("t")); - query.addQueryItem(QStringLiteral("otf"), QStringLiteral("2")); - url.setQuery(query); + const auto preparedText = shouldWrapInHtml() ? Html::entitiesToHtml(text) : text.text; + QJsonArray requestRoot; + QJsonArray requestPayload; + QJsonArray requestText; + requestText.append(preparedText); + requestPayload.append(requestText); + requestPayload.append(from); + requestPayload.append(to); + requestRoot.append(requestPayload); + requestRoot.append(QStringLiteral("wt_lib")); + const auto body = QJsonDocument(requestRoot).toJson(QJsonDocument::Compact); - QNetworkRequest req(url); + QNetworkRequest req(QUrl(QString::fromLatin1(kGoogleTranslateUrl))); const auto userAgent = randomDesktopUserAgent(); req.setHeader(QNetworkRequest::UserAgentHeader, userAgent); + req.setHeader(QNetworkRequest::ContentTypeHeader, QString::fromLatin1(kGoogleContentType)); + req.setRawHeader(QByteArrayLiteral("Accept"), QByteArrayLiteral("application/json")); + req.setRawHeader(QByteArrayLiteral("X-Goog-Api-Key"), QByteArray(kGoogleDefaultApiKey)); - QPointer reply = _nam.get(req); + QPointer reply = _nam.post(req, body); auto timer = new QTimer(reply); timer->setSingleShot(true); @@ -92,15 +137,27 @@ QPointer GoogleTranslator::startSingleTranslation( return; } const auto body = reply->readAll(); - bool ok = false; - const auto textOut = parseJsonPath(body, QStringLiteral("sentences"), &ok); - if (!ok) { + QJsonParseError parseError{}; + const auto doc = QJsonDocument::fromJson(body, &parseError); + if (parseError.error != QJsonParseError::NoError || !doc.isArray()) { if (onFail) onFail(); return; } + const auto root = doc.array(); + if (root.isEmpty()) { + if (onFail) onFail(); + return; + } + const auto translatedItems = collectStrings(root.at(0)); + const auto textOutCombined = translatedItems.join(QStringLiteral(" ")); + if (textOutCombined.trimmed().isEmpty()) { + if (onFail) onFail(); + return; + } + const auto decodedText = decodeHtmlEntities(textOutCombined); if (onSuccess) onSuccess(shouldWrapInHtml() - ? Html::htmlToEntities(textOut) - : TextWithEntities{textOut}); + ? Html::htmlToEntities(decodedText) + : TextWithEntities{decodedText}); }); return reply;