KTextAddons

yandexengineplugin.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 "yandexengineplugin.h"
10using namespace Qt::Literals::StringLiterals;
11
12#include <TextTranslator/TranslatorEngineAccessManager>
13
14#include <KLocalizedString>
15
16#include <QJsonArray>
17#include <QJsonDocument>
18#include <QJsonObject>
19#include <QUrlQuery>
20
21QString YandexEnginePlugin::sYandexKey;
22
23YandexEnginePlugin::YandexEnginePlugin(QObject *parent)
24 : TextTranslator::TranslatorEnginePlugin(parent)
25{
26}
27
28YandexEnginePlugin::~YandexEnginePlugin() = default;
29
30void YandexEnginePlugin::translate()
31{
32 if (sYandexKey.isEmpty()) {
33 const QUrl url(QStringLiteral("https://translate.yandex.com"));
34
35 QNetworkReply *reply = TextTranslator::TranslatorEngineAccessManager::self()->networkManager()->get(QNetworkRequest(url));
36 connect(reply, &QNetworkReply::finished, this, [this, reply]() {
37 parseCredentials(reply);
38 });
39 connect(reply, &QNetworkReply::errorOccurred, this, [this, reply](QNetworkReply::NetworkError error) {
40 slotError(error);
41 reply->deleteLater();
42 });
43 } else {
44 translateText();
45 }
46}
47
48void YandexEnginePlugin::parseCredentials(QNetworkReply *reply)
49{
50 // Check availability of service
51 const QByteArray webSiteData = reply->readAll();
52 reply->deleteLater();
53 if (webSiteData.isEmpty() || webSiteData.contains("<title>Oops!</title>") || webSiteData.contains("<title>302 Found</title>")) {
54 Q_EMIT translateFailed(
55 i18n("Error: Engine systems have detected suspicious traffic "
56 "from your computer network. Please try your request again later."));
57 return;
58 }
59 // qCDebug(TRANSLATOR_YANDEX_LOG) << "webSiteData " << webSiteData;
60 const QByteArray sidBeginString = "SID: '";
61 const int sidBeginStringPos = webSiteData.indexOf(sidBeginString);
62
63 if (sidBeginStringPos == -1) {
64 Q_EMIT translateFailed(i18n("Error: Unable to find Yandex SID in web version."));
65 return;
66 }
67
68 const int sidBeginPosition = sidBeginStringPos + sidBeginString.size();
69 const int sidEndPosition = webSiteData.indexOf('\'', sidBeginPosition);
70
71 if (sidEndPosition == -1) {
72 Q_EMIT translateFailed(i18n("Error: Unable to extract Yandex SID from web version."));
73 return;
74 }
75
76 // Yandex show reversed parts of session ID, need to decode
77
78 const QString sid = QString::fromUtf8(webSiteData.mid(sidBeginPosition, sidEndPosition - sidBeginPosition));
79
80 QStringList sidParts = sid.split(QLatin1Char('.'));
81
82 for (int i = 0, total = sidParts.size(); i < total; ++i) {
83 std::reverse(sidParts[i].begin(), sidParts[i].end());
84 }
85
86 sYandexKey = sidParts.join(QLatin1Char('.'));
87 translateText();
88}
89
90void YandexEnginePlugin::translateText()
91{
92 if (verifyFromAndToLanguage()) {
93 return;
94 }
95
96 clear();
97 QString lang;
98 if (from() == "auto"_L1) {
99 lang = languageCode(to());
100 } else {
101 lang = languageCode(from()) + QLatin1Char('-') + languageCode(to());
102 }
103 // qDebug() << " lang " << lang;
104 // Generate API url
105 QUrl url(QStringLiteral("https://translate.yandex.net/api/v1/tr.json/translate"));
107 query.addQueryItem(QStringLiteral("id"), sYandexKey + "-2-0"_L1);
108 query.addQueryItem(QStringLiteral("srv"), QStringLiteral("tr-text"));
109 query.addQueryItem(QStringLiteral("text"), inputText());
110 query.addQueryItem(QStringLiteral("lang"), lang);
111 url.setQuery(query);
112
113 // Setup request
114 QNetworkRequest request;
115 request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"_L1);
116 request.setUrl(url);
117
118 // Make reply
119 QNetworkReply *reply = TextTranslator::TranslatorEngineAccessManager::self()->networkManager()->post(request, QByteArray());
120 connect(reply, &QNetworkReply::errorOccurred, this, [this, reply](QNetworkReply::NetworkError error) {
121 slotError(error);
122 reply->deleteLater();
123 });
124
125 connect(reply, &QNetworkReply::finished, this, [this, reply]() {
126 reply->deleteLater();
127 parseTranslation(reply);
128 });
129}
130
131void YandexEnginePlugin::parseTranslation(QNetworkReply *reply)
132{
133 const QJsonDocument jsonResponse = QJsonDocument::fromJson(reply->readAll());
134 if (hasDebug()) {
135 setJsonDebug(QString::fromUtf8(jsonResponse.toJson(QJsonDocument::Indented)));
136 }
137 const QJsonObject jsonData = jsonResponse.object();
138 reply->deleteLater();
139
140 // Parse language
141 if (from() == "auto"_L1) {
142 QString sourceCode = jsonData.value(QStringLiteral("lang")).toString();
143 sourceCode = sourceCode.left(sourceCode.indexOf(QLatin1Char('-')));
144 qDebug() << " sourceCode " << sourceCode;
145 }
146
147 // qDebug() << "jsonData " << jsonData;
148 appendResult(jsonData.value(QStringLiteral("text")).toArray().at(0).toString());
149 Q_EMIT translateDone();
150}
151
152QString YandexEnginePlugin::languageCode(const QString &langStr)
153{
154 if (langStr == "zh"_L1) {
155 return QStringLiteral("zn");
156 } else if (langStr == "ja"_L1) {
157 return QStringLiteral("jv");
158 }
159 return langStr;
160}
161
162#include "moc_yandexengineplugin.cpp"
QString i18n(const char *text, const TYPE &arg...)
char * toString(const EngineQuery &query)
KSERVICE_EXPORT KService::List query(FilterFunc filterFunc)
const QList< QKeySequence > & begin()
const QList< QKeySequence > & end()
bool contains(QByteArrayView bv) const const
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()
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QJsonObject object() const const
QByteArray toJson(JsonFormat format) const const
QJsonValue value(QLatin1StringView key) const const
QString toString() const const
qsizetype size() const const
QNetworkReply * get(const QNetworkRequest &request)
QNetworkReply * post(const QNetworkRequest &request, QHttpMultiPart *multiPart)
void errorOccurred(QNetworkReply::NetworkError code)
void setHeader(KnownHeaders header, const QVariant &value)
void setUrl(const QUrl &url)
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
QString fromUtf8(QByteArrayView str)
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString left(qsizetype n) const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QString join(QChar separator) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:19:49 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.