File indexing completed on 2024-11-24 04:16:55
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 "bingengineplugin.h" 0010 #include "translator/plugins/bing/bingtranslator_debug.h" 0011 #include <KLocalizedString> 0012 #include <QCoreApplication> 0013 #include <QJsonArray> 0014 #include <QJsonDocument> 0015 #include <QJsonObject> 0016 #include <QNetworkReply> 0017 #include <QNetworkRequest> 0018 #include <TextTranslator/TranslatorEngineAccessManager> 0019 0020 QByteArray BingEnginePlugin::sBingKey; 0021 QByteArray BingEnginePlugin::sBingToken; 0022 QString BingEnginePlugin::sBingIg; 0023 QString BingEnginePlugin::sBingIid; 0024 0025 BingEnginePlugin::BingEnginePlugin(QObject *parent) 0026 : TextTranslator::TranslatorEnginePlugin(parent) 0027 { 0028 } 0029 0030 BingEnginePlugin::~BingEnginePlugin() = default; 0031 0032 void BingEnginePlugin::translate() 0033 { 0034 if (sBingKey.isEmpty() || sBingToken.isEmpty()) { 0035 const QUrl url(QStringLiteral("https://www.bing.com/translator")); 0036 QNetworkReply *reply = TextTranslator::TranslatorEngineAccessManager::self()->networkManager()->get(QNetworkRequest(url)); 0037 connect(reply, &QNetworkReply::finished, this, [this, reply]() { 0038 parseCredentials(reply); 0039 }); 0040 connect(reply, &QNetworkReply::errorOccurred, this, [this, reply](QNetworkReply::NetworkError error) { 0041 slotError(error); 0042 reply->deleteLater(); 0043 }); 0044 } else { 0045 translateText(); 0046 } 0047 } 0048 0049 void BingEnginePlugin::parseCredentials(QNetworkReply *reply) 0050 { 0051 const QByteArray webSiteData = reply->readAll(); 0052 reply->deleteLater(); 0053 const QByteArray credentialsBeginString = QByteArrayLiteral("var params_RichTranslateHelper = ["); 0054 const int credentialsBeginPos = webSiteData.indexOf(credentialsBeginString); 0055 0056 if (credentialsBeginPos == -1) { 0057 Q_EMIT translateFailed(i18n("Error: Unable to find Bing credentials in web version.")); 0058 return; 0059 } 0060 0061 const int keyBeginPos = credentialsBeginPos + credentialsBeginString.size(); 0062 const int keyEndPos = webSiteData.indexOf(',', keyBeginPos); 0063 0064 if (keyEndPos == -1) { 0065 Q_EMIT translateFailed(i18n("Error: Unable to extract Bing key from web version.")); 0066 return; 0067 } 0068 0069 sBingKey = webSiteData.mid(keyBeginPos, keyEndPos - keyBeginPos); 0070 const int tokenBeginPos = keyEndPos + 2; // Skip two symbols instead of one because the value is enclosed in quotes 0071 const int tokenEndPos = webSiteData.indexOf('"', tokenBeginPos); 0072 0073 if (tokenEndPos == -1) { 0074 Q_EMIT translateFailed(i18n("Error: Unable to extract Bing token from web version.")); 0075 return; 0076 } 0077 0078 sBingToken = webSiteData.mid(tokenBeginPos, tokenEndPos - tokenBeginPos); 0079 const int igBeginPos = webSiteData.indexOf("IG"); 0080 const int igEndPos = webSiteData.indexOf('"', igBeginPos + 2); 0081 0082 if (igEndPos == -1) { 0083 Q_EMIT translateFailed(i18n("Error: Unable to extract additional Bing information from web version.")); 0084 return; 0085 } 0086 0087 sBingIg = QString::fromUtf8(webSiteData.mid(igBeginPos, igEndPos - igBeginPos)); 0088 const int iidBeginPos = webSiteData.indexOf("data-iid"); 0089 const int iidEndPos = webSiteData.indexOf('"', iidBeginPos + 2); 0090 0091 if (iidEndPos == -1) { 0092 Q_EMIT translateFailed(i18n("Error: Unable to extract additional Bing information from web version.")); 0093 return; 0094 } 0095 0096 sBingIid = QString::fromUtf8(webSiteData.mid(iidBeginPos, iidEndPos - iidBeginPos)); 0097 0098 // qCDebug(TRANSLATOR_BING_LOG) << "sBingIid " << sBingIid << " sBingIg " << sBingIg << " sBingToken " << sBingToken << " sBingKey " << sBingKey; 0099 translateText(); 0100 } 0101 0102 void BingEnginePlugin::translateText() 0103 { 0104 if (verifyFromAndToLanguage()) { 0105 return; 0106 } 0107 clear(); 0108 0109 const QByteArray postData = "&text=" + QUrl::toPercentEncoding(inputText()) + "&fromLang=" + languageCode(from()).toUtf8() 0110 + "&to=" + languageCode(to()).toUtf8() + "&token=" + sBingToken + "&key=" + sBingKey; 0111 0112 qCDebug(TRANSLATOR_BING_LOG) << " postData " << postData; 0113 QUrl url(QStringLiteral("https://www.bing.com/ttranslatev3")); 0114 url.setQuery(QStringLiteral("IG=%1&IID=%2").arg(sBingIg, sBingIid)); 0115 qCDebug(TRANSLATOR_BING_LOG) << " url " << url; 0116 0117 QNetworkRequest request(url); 0118 request.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/x-www-form-urlencoded")); 0119 request.setHeader(QNetworkRequest::UserAgentHeader, 0120 QStringLiteral("%1/%2").arg(QCoreApplication::applicationName(), QCoreApplication::applicationVersion())); 0121 0122 QNetworkReply *reply = TextTranslator::TranslatorEngineAccessManager::self()->networkManager()->post(request, postData); 0123 connect(reply, &QNetworkReply::errorOccurred, this, [this, reply](QNetworkReply::NetworkError error) { 0124 slotError(error); 0125 reply->deleteLater(); 0126 }); 0127 0128 connect(reply, &QNetworkReply::finished, this, [this, reply]() { 0129 reply->deleteLater(); 0130 parseTranslation(reply); 0131 }); 0132 } 0133 0134 void BingEnginePlugin::parseTranslation(QNetworkReply *reply) 0135 { 0136 // Parse translation data 0137 const QJsonDocument jsonResponse = QJsonDocument::fromJson(reply->readAll()); 0138 qCDebug(TRANSLATOR_BING_LOG) << " jsonResponse " << jsonResponse; 0139 const QJsonObject responseObject = jsonResponse.array().first().toObject(); 0140 if (from() == QLatin1String("auto")) { 0141 const QString langCode = responseObject.value(QStringLiteral("detectedLanguage")).toObject().value(QStringLiteral("language")).toString(); 0142 setFrom(langCode); 0143 // if (m_sourceLang == NoLanguage) 0144 // { 0145 // resetData(ParsingError, i18n("Error: Unable to parse autodetected language")); 0146 // return; 0147 // } 0148 } 0149 0150 const QJsonObject translationsObject = responseObject.value(QStringLiteral("translations")).toArray().first().toObject(); 0151 appendResult(translationsObject.value(QStringLiteral("text")).toString()); 0152 if (hasDebug()) { 0153 setJsonDebug(QString::fromUtf8(jsonResponse.toJson(QJsonDocument::Indented))); 0154 } 0155 0156 qCDebug(TRANSLATOR_BING_LOG) << " mResult " << result(); 0157 // m_translationTranslit += translationsObject.value(QStringLiteral("transliteration")).toObject().value(QStringLiteral("text")).toString(); 0158 reply->deleteLater(); 0159 Q_EMIT translateDone(); 0160 } 0161 0162 QString BingEnginePlugin::languageCode(const QString &langStr) 0163 { 0164 if (langStr == QLatin1String("auto")) { 0165 return QStringLiteral("auto-detect"); 0166 } else if (langStr == QLatin1String("sr")) { 0167 return QStringLiteral("sr-Cyrl"); 0168 } else if (langStr == QLatin1String("bs")) { 0169 return QStringLiteral("bs-Latn"); 0170 } else if (langStr == QLatin1String("hmn")) { 0171 return QStringLiteral("mww"); 0172 } else if (langStr == QLatin1String("zh")) { 0173 return QStringLiteral("zh-Hans"); 0174 } else if (langStr == QLatin1String("zt")) { 0175 return QStringLiteral("zh-Hant"); 0176 } 0177 return langStr; 0178 } 0179 0180 #include "moc_bingengineplugin.cpp"