File indexing completed on 2025-04-27 03:58:38

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2018-02-22
0007  * Description : A text translator using web-services - Google methods.
0008  *
0009  * SPDX-FileCopyrightText: 2018-2022 by Hennadii Chernyshchyk <genaloner at gmail dot com>
0010  * SPDX-FileCopyrightText: 2021-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0011  *
0012  * SPDX-License-Identifier: GPL-2.0-or-later
0013  *
0014  * ============================================================ */
0015 
0016 #include "donlinetranslator_p.h"
0017 
0018 namespace Digikam
0019 {
0020 
0021 void DOnlineTranslator::slotRequestGoogleTranslate()
0022 {
0023     const QString sourceText = sender()->property(Private::s_textProperty).toString();
0024 
0025     // Generate API url
0026 
0027     QUrl url(QStringLiteral("https://translate.googleapis.com/translate_a/single"));
0028 
0029     url.setQuery(QStringLiteral("client=gtx&ie=UTF-8&oe=UTF-8&dt=bd&dt=ex&dt=ld&dt=md&dt=rw&dt=rm&dt=ss&dt=t&dt=at&dt=qc&sl=%1&tl=%2&hl=%3&q=%4")
0030                      .arg(languageApiCode(Google, d->sourceLang),
0031                           languageApiCode(Google, d->translationLang),
0032                           languageApiCode(Google, d->uiLang),
0033                           QString::fromUtf8(QUrl::toPercentEncoding(sourceText))));
0034 
0035     d->currentReply = d->networkManager->get(QNetworkRequest(url));
0036 }
0037 
0038 void DOnlineTranslator::slotParseGoogleTranslate()
0039 {
0040     if ((quintptr)d->currentReply.data() != sender()->property("QNetworkReply").value<quintptr>())
0041     {
0042         return;
0043     }
0044 
0045     d->currentReply->deleteLater();
0046 
0047     // Check for error
0048 
0049     if (d->currentReply->error() != QNetworkReply::NoError)
0050     {
0051         if (d->currentReply->error() == QNetworkReply::ServiceUnavailableError)
0052         {
0053             resetData(ServiceError, i18n("Error: Engine systems have detected suspicious traffic from your "
0054                                          "computer network. Please try your request again later."));
0055         }
0056         else
0057         {
0058             resetData(NetworkError, d->currentReply->errorString());
0059         }
0060 
0061         return;
0062     }
0063 
0064     // Check availability of service
0065 
0066     const QByteArray data = d->currentReply->readAll();
0067 
0068     if (data.startsWith('<'))
0069     {
0070         resetData(ServiceError, i18n("Error: Engine systems have detected suspicious traffic from your "
0071                                      "computer network. Please try your request again later."));
0072         return;
0073     }
0074 
0075     // Read Json
0076 
0077     const QJsonDocument jsonResponse = QJsonDocument::fromJson(data);
0078     const QJsonArray jsonData        = jsonResponse.array();
0079 
0080     if (d->sourceLang == Auto)
0081     {
0082         // Parse language
0083 
0084         d->sourceLang = language(Google, jsonData.at(2).toString());
0085 
0086         if (d->sourceLang == NoLanguage)
0087         {
0088             resetData(ParsingError, i18n("Error: Unable to parse autodetected language"));
0089             return;
0090         }
0091 
0092         if (d->onlyDetectLanguage)
0093         {
0094             return;
0095         }
0096     }
0097 
0098     addSpaceBetweenParts(d->translation);
0099     addSpaceBetweenParts(d->translationTranslit);
0100     addSpaceBetweenParts(d->sourceTranslit);
0101 
0102     for (const QJsonValueRef translationData : jsonData.at(0).toArray())
0103     {
0104         const QJsonArray translationArray = translationData.toArray();
0105         d->translation.append(translationArray.at(0).toString());
0106 
0107         if (d->translationTranslitEnabled)
0108         {
0109             d->translationTranslit.append(translationArray.at(2).toString());
0110         }
0111 
0112         if (d->sourceTranslitEnabled)
0113         {
0114             d->sourceTranslit.append(translationArray.at(3).toString());
0115         }
0116     }
0117 
0118     if (d->source.size() >= Private::s_googleTranslateLimit)
0119     {
0120         return;
0121     }
0122 
0123     // Translation options
0124 
0125     if (d->translationOptionsEnabled)
0126     {
0127         for (const QJsonValueRef typeOfSpeechData : jsonData.at(1).toArray())
0128         {
0129             const QJsonArray typeOfSpeechDataArray = typeOfSpeechData.toArray();
0130             const QString typeOfSpeech             = typeOfSpeechDataArray.at(0).toString();
0131 
0132             for (const QJsonValueRef wordData : typeOfSpeechDataArray.at(2).toArray())
0133             {
0134                 const QJsonArray wordDataArray     = wordData.toArray();
0135                 const QString word                 = wordDataArray.at(0).toString();
0136                 const QString gender               = wordDataArray.at(4).toString();
0137                 const QJsonArray translationsArray = wordDataArray.at(1).toArray();
0138                 QStringList translations;
0139                 translations.reserve(translationsArray.size());
0140 
0141                 for (const QJsonValue &wordTranslation : translationsArray)
0142                 {
0143                     translations.append(wordTranslation.toString());
0144                 }
0145 
0146                 d->translationOptions[typeOfSpeech].append({word, gender, translations});
0147             }
0148         }
0149     }
0150 }
0151 
0152 void DOnlineTranslator::buildGoogleStateMachine()
0153 {
0154     // States (Google sends translation, translit and dictionary in one request,
0155     // that will be splited into several by the translation limit)
0156 
0157     auto* translationState = new QState(d->stateMachine);
0158     auto* finalState       = new QFinalState(d->stateMachine);
0159     d->stateMachine->setInitialState(translationState);
0160 
0161     translationState->addTransition(translationState, &QState::finished, finalState);
0162 
0163     // Setup translation state
0164 
0165     buildSplitNetworkRequest(translationState,
0166                              &DOnlineTranslator::slotRequestGoogleTranslate,
0167                              &DOnlineTranslator::slotParseGoogleTranslate,
0168                              d->source,
0169                              Private::s_googleTranslateLimit);
0170 }
0171 
0172 void DOnlineTranslator::buildGoogleDetectStateMachine()
0173 {
0174     // States
0175 
0176     auto* detectState  = new QState(d->stateMachine);
0177     auto* finalState   = new QFinalState(d->stateMachine);
0178     d->stateMachine->setInitialState(detectState);
0179 
0180     detectState->addTransition(detectState, &QState::finished, finalState);
0181 
0182     // Setup detect state
0183 
0184     const QString text = d->source.left(getSplitIndex(d->source, Private::s_googleTranslateLimit));
0185 
0186     buildNetworkRequestState(detectState,
0187                              &DOnlineTranslator::slotRequestGoogleTranslate                             ,
0188                              &DOnlineTranslator::slotParseGoogleTranslate,
0189                              text);
0190 }
0191 
0192 } // namespace Digikam