File indexing completed on 2024-11-24 04:16:57

0001 /*
0002   SPDX-FileCopyrightText: 2022-2024 Laurent Montel <montel@kde.org>
0003 
0004   SPDX-License-Identifier: GPL-2.0-or-later
0005 
0006   based on digikam onlinetranslator code
0007 */
0008 
0009 #include "yandexengineplugin.h"
0010 
0011 #include <TextTranslator/TranslatorEngineAccessManager>
0012 
0013 #include <KLocalizedString>
0014 
0015 #include <QJsonArray>
0016 #include <QJsonDocument>
0017 #include <QJsonObject>
0018 #include <QUrlQuery>
0019 
0020 QString YandexEnginePlugin::sYandexKey;
0021 
0022 YandexEnginePlugin::YandexEnginePlugin(QObject *parent)
0023     : TextTranslator::TranslatorEnginePlugin(parent)
0024 {
0025 }
0026 
0027 YandexEnginePlugin::~YandexEnginePlugin() = default;
0028 
0029 void YandexEnginePlugin::translate()
0030 {
0031     if (sYandexKey.isEmpty()) {
0032         const QUrl url(QStringLiteral("https://translate.yandex.com"));
0033 
0034         QNetworkReply *reply = TextTranslator::TranslatorEngineAccessManager::self()->networkManager()->get(QNetworkRequest(url));
0035         connect(reply, &QNetworkReply::finished, this, [this, reply]() {
0036             parseCredentials(reply);
0037         });
0038         connect(reply, &QNetworkReply::errorOccurred, this, [this, reply](QNetworkReply::NetworkError error) {
0039             slotError(error);
0040             reply->deleteLater();
0041         });
0042     } else {
0043         translateText();
0044     }
0045 }
0046 
0047 void YandexEnginePlugin::parseCredentials(QNetworkReply *reply)
0048 {
0049     // Check availability of service
0050     const QByteArray webSiteData = reply->readAll();
0051     reply->deleteLater();
0052     if (webSiteData.isEmpty() || webSiteData.contains("<title>Oops!</title>") || webSiteData.contains("<title>302 Found</title>")) {
0053         Q_EMIT translateFailed(
0054             i18n("Error: Engine systems have detected suspicious traffic "
0055                  "from your computer network. Please try your request again later."));
0056         return;
0057     }
0058     // qCDebug(TRANSLATOR_YANDEX_LOG) << "webSiteData  " << webSiteData;
0059     const QByteArray sidBeginString = "SID: '";
0060     const int sidBeginStringPos = webSiteData.indexOf(sidBeginString);
0061 
0062     if (sidBeginStringPos == -1) {
0063         Q_EMIT translateFailed(i18n("Error: Unable to find Yandex SID in web version."));
0064         return;
0065     }
0066 
0067     const int sidBeginPosition = sidBeginStringPos + sidBeginString.size();
0068     const int sidEndPosition = webSiteData.indexOf('\'', sidBeginPosition);
0069 
0070     if (sidEndPosition == -1) {
0071         Q_EMIT translateFailed(i18n("Error: Unable to extract Yandex SID from web version."));
0072         return;
0073     }
0074 
0075     // Yandex show reversed parts of session ID, need to decode
0076 
0077     const QString sid = QString::fromUtf8(webSiteData.mid(sidBeginPosition, sidEndPosition - sidBeginPosition));
0078 
0079     QStringList sidParts = sid.split(QLatin1Char('.'));
0080 
0081     for (int i = 0, total = sidParts.size(); i < total; ++i) {
0082         std::reverse(sidParts[i].begin(), sidParts[i].end());
0083     }
0084 
0085     sYandexKey = sidParts.join(QLatin1Char('.'));
0086     translateText();
0087 }
0088 
0089 void YandexEnginePlugin::translateText()
0090 {
0091     if (verifyFromAndToLanguage()) {
0092         return;
0093     }
0094 
0095     clear();
0096     QString lang;
0097     if (from() == QLatin1String("auto")) {
0098         lang = languageCode(to());
0099     } else {
0100         lang = languageCode(from()) + QLatin1Char('-') + languageCode(to());
0101     }
0102     // qDebug() << " lang " << lang;
0103     // Generate API url
0104     QUrl url(QStringLiteral("https://translate.yandex.net/api/v1/tr.json/translate"));
0105     QUrlQuery query;
0106     query.addQueryItem(QStringLiteral("id"), sYandexKey + QLatin1String("-2-0"));
0107     query.addQueryItem(QStringLiteral("srv"), QStringLiteral("tr-text"));
0108     query.addQueryItem(QStringLiteral("text"), inputText());
0109     query.addQueryItem(QStringLiteral("lang"), lang);
0110     url.setQuery(query);
0111 
0112     // Setup request
0113     QNetworkRequest request;
0114     request.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/x-www-form-urlencoded"));
0115     request.setUrl(url);
0116 
0117     // Make reply
0118     QNetworkReply *reply = TextTranslator::TranslatorEngineAccessManager::self()->networkManager()->post(request, QByteArray());
0119     connect(reply, &QNetworkReply::errorOccurred, this, [this, reply](QNetworkReply::NetworkError error) {
0120         slotError(error);
0121         reply->deleteLater();
0122     });
0123 
0124     connect(reply, &QNetworkReply::finished, this, [this, reply]() {
0125         reply->deleteLater();
0126         parseTranslation(reply);
0127     });
0128 }
0129 
0130 void YandexEnginePlugin::parseTranslation(QNetworkReply *reply)
0131 {
0132     const QJsonDocument jsonResponse = QJsonDocument::fromJson(reply->readAll());
0133     if (hasDebug()) {
0134         setJsonDebug(QString::fromUtf8(jsonResponse.toJson(QJsonDocument::Indented)));
0135     }
0136     const QJsonObject jsonData = jsonResponse.object();
0137     reply->deleteLater();
0138 
0139     // Parse language
0140     if (from() == QLatin1String("auto")) {
0141         QString sourceCode = jsonData.value(QStringLiteral("lang")).toString();
0142         sourceCode = sourceCode.left(sourceCode.indexOf(QLatin1Char('-')));
0143         qDebug() << " sourceCode " << sourceCode;
0144     }
0145 
0146     // qDebug() << "jsonData  " << jsonData;
0147     appendResult(jsonData.value(QStringLiteral("text")).toArray().at(0).toString());
0148     Q_EMIT translateDone();
0149 }
0150 
0151 QString YandexEnginePlugin::languageCode(const QString &langStr)
0152 {
0153     if (langStr == QLatin1String("zh")) {
0154         return QStringLiteral("zn");
0155     } else if (langStr == QLatin1String("ja")) {
0156         return QStringLiteral("jv");
0157     }
0158     return langStr;
0159 }
0160 
0161 #include "moc_yandexengineplugin.cpp"