KTextAddons

bingengineplugin.cpp
1/*
2 SPDX-FileCopyrightText: 2022-2024 Laurent Montel <montel@kde.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5
6 based on digikam onlinetranslator code
7*/
8
9#include "bingengineplugin.h"
10using namespace Qt::Literals::StringLiterals;
11
12#include "translator/plugins/bing/bingtranslator_debug.h"
13#include <KLocalizedString>
14#include <QCoreApplication>
15#include <QJsonArray>
16#include <QJsonDocument>
17#include <QJsonObject>
18#include <QNetworkReply>
19#include <QNetworkRequest>
20#include <TextTranslator/TranslatorEngineAccessManager>
21
22QByteArray BingEnginePlugin::sBingKey;
23QByteArray BingEnginePlugin::sBingToken;
24QString BingEnginePlugin::sBingIg;
25QString BingEnginePlugin::sBingIid;
26
27BingEnginePlugin::BingEnginePlugin(QObject *parent)
28 : TextTranslator::TranslatorEnginePlugin(parent)
29{
30}
31
32BingEnginePlugin::~BingEnginePlugin() = default;
33
34void BingEnginePlugin::translate()
35{
36 if (sBingKey.isEmpty() || sBingToken.isEmpty()) {
37 const QUrl url(QStringLiteral("https://www.bing.com/translator"));
38 QNetworkReply *reply = TextTranslator::TranslatorEngineAccessManager::self()->networkManager()->get(QNetworkRequest(url));
39 connect(reply, &QNetworkReply::finished, this, [this, reply]() {
40 parseCredentials(reply);
41 });
42 connect(reply, &QNetworkReply::errorOccurred, this, [this, reply](QNetworkReply::NetworkError error) {
43 slotError(error);
44 reply->deleteLater();
45 });
46 } else {
47 translateText();
48 }
49}
50
51void BingEnginePlugin::parseCredentials(QNetworkReply *reply)
52{
53 const QByteArray webSiteData = reply->readAll();
54 reply->deleteLater();
55 const QByteArray credentialsBeginString = QByteArrayLiteral("var params_RichTranslateHelper = [");
56 const int credentialsBeginPos = webSiteData.indexOf(credentialsBeginString);
57
58 if (credentialsBeginPos == -1) {
59 Q_EMIT translateFailed(i18n("Error: Unable to find Bing credentials in web version."));
60 return;
61 }
62
63 const int keyBeginPos = credentialsBeginPos + credentialsBeginString.size();
64 const int keyEndPos = webSiteData.indexOf(',', keyBeginPos);
65
66 if (keyEndPos == -1) {
67 Q_EMIT translateFailed(i18n("Error: Unable to extract Bing key from web version."));
68 return;
69 }
70
71 sBingKey = webSiteData.mid(keyBeginPos, keyEndPos - keyBeginPos);
72 const int tokenBeginPos = keyEndPos + 2; // Skip two symbols instead of one because the value is enclosed in quotes
73 const int tokenEndPos = webSiteData.indexOf('"', tokenBeginPos);
74
75 if (tokenEndPos == -1) {
76 Q_EMIT translateFailed(i18n("Error: Unable to extract Bing token from web version."));
77 return;
78 }
79
80 sBingToken = webSiteData.mid(tokenBeginPos, tokenEndPos - tokenBeginPos);
81 const int igBeginPos = webSiteData.indexOf("IG");
82 const int igEndPos = webSiteData.indexOf('"', igBeginPos + 2);
83
84 if (igEndPos == -1) {
85 Q_EMIT translateFailed(i18n("Error: Unable to extract additional Bing information from web version."));
86 return;
87 }
88
89 sBingIg = QString::fromUtf8(webSiteData.mid(igBeginPos, igEndPos - igBeginPos));
90 const int iidBeginPos = webSiteData.indexOf("data-iid");
91 const int iidEndPos = webSiteData.indexOf('"', iidBeginPos + 2);
92
93 if (iidEndPos == -1) {
94 Q_EMIT translateFailed(i18n("Error: Unable to extract additional Bing information from web version."));
95 return;
96 }
97
98 sBingIid = QString::fromUtf8(webSiteData.mid(iidBeginPos, iidEndPos - iidBeginPos));
99
100 // qCDebug(TRANSLATOR_BING_LOG) << "sBingIid " << sBingIid << " sBingIg " << sBingIg << " sBingToken " << sBingToken << " sBingKey " << sBingKey;
101 translateText();
102}
103
104void BingEnginePlugin::translateText()
105{
106 if (verifyFromAndToLanguage()) {
107 return;
108 }
109 clear();
110
111 const QByteArray postData = "&text=" + QUrl::toPercentEncoding(inputText()) + "&fromLang=" + languageCode(from()).toUtf8()
112 + "&to=" + languageCode(to()).toUtf8() + "&token=" + sBingToken + "&key=" + sBingKey;
113
114 qCDebug(TRANSLATOR_BING_LOG) << " postData " << postData;
115 QUrl url(QStringLiteral("https://www.bing.com/ttranslatev3"));
116 url.setQuery(QStringLiteral("IG=%1&IID=%2").arg(sBingIg, sBingIid));
117 qCDebug(TRANSLATOR_BING_LOG) << " url " << url;
118
119 QNetworkRequest request(url);
120 request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"_L1);
121 request.setHeader(QNetworkRequest::UserAgentHeader,
123
124 QNetworkReply *reply = TextTranslator::TranslatorEngineAccessManager::self()->networkManager()->post(request, postData);
125 connect(reply, &QNetworkReply::errorOccurred, this, [this, reply](QNetworkReply::NetworkError error) {
126 slotError(error);
127 reply->deleteLater();
128 });
129
130 connect(reply, &QNetworkReply::finished, this, [this, reply]() {
131 reply->deleteLater();
132 parseTranslation(reply);
133 });
134}
135
136void BingEnginePlugin::parseTranslation(QNetworkReply *reply)
137{
138 // Parse translation data
139 const QJsonDocument jsonResponse = QJsonDocument::fromJson(reply->readAll());
140 qCDebug(TRANSLATOR_BING_LOG) << " jsonResponse " << jsonResponse;
141 const QJsonObject responseObject = jsonResponse.array().first().toObject();
142 if (from() == "auto"_L1) {
143 const QString langCode = responseObject.value(QStringLiteral("detectedLanguage")).toObject().value(QStringLiteral("language")).toString();
144 setFrom(langCode);
145 // if (m_sourceLang == NoLanguage)
146 // {
147 // resetData(ParsingError, i18n("Error: Unable to parse autodetected language"));
148 // return;
149 // }
150 }
151
152 const QJsonObject translationsObject = responseObject.value(QStringLiteral("translations")).toArray().first().toObject();
153 appendResult(translationsObject.value(QStringLiteral("text")).toString());
154 if (hasDebug()) {
155 setJsonDebug(QString::fromUtf8(jsonResponse.toJson(QJsonDocument::Indented)));
156 }
157
158 qCDebug(TRANSLATOR_BING_LOG) << " mResult " << result();
159 // m_translationTranslit += translationsObject.value(QStringLiteral("transliteration")).toObject().value(QStringLiteral("text")).toString();
160 reply->deleteLater();
161 Q_EMIT translateDone();
162}
163
164QString BingEnginePlugin::languageCode(const QString &langStr)
165{
166 if (langStr == "auto"_L1) {
167 return QStringLiteral("auto-detect");
168 } else if (langStr == "sr"_L1) {
169 return QStringLiteral("sr-Cyrl");
170 } else if (langStr == "bs"_L1) {
171 return QStringLiteral("bs-Latn");
172 } else if (langStr == "hmn"_L1) {
173 return QStringLiteral("mww");
174 } else if (langStr == "zh"_L1) {
175 return QStringLiteral("zh-Hans");
176 } else if (langStr == "zt"_L1) {
177 return QStringLiteral("zh-Hant");
178 }
179 return langStr;
180}
181
182#include "moc_bingengineplugin.cpp"
QString i18n(const char *text, const TYPE &arg...)
char * toString(const EngineQuery &query)
qsizetype indexOf(QByteArrayView bv, qsizetype from) const const
bool isEmpty() const const
QByteArray mid(qsizetype pos, qsizetype len) const const
qsizetype size() const const
QByteArray readAll()
QJsonValue first() const const
QJsonArray array() const const
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QByteArray toJson(JsonFormat format) const const
QJsonValue value(QLatin1StringView key) const const
QJsonArray toArray() const const
QJsonObject toObject() const const
QString toString() const const
QNetworkReply * get(const QNetworkRequest &request)
QNetworkReply * post(const QNetworkRequest &request, QHttpMultiPart *multiPart)
void errorOccurred(QNetworkReply::NetworkError code)
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
QString fromUtf8(QByteArrayView str)
QByteArray toUtf8() const const
QByteArray toPercentEncoding(const QString &input, const QByteArray &exclude, const QByteArray &include)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 4 2024 16:29:59 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.