From 3daccc5d2344404af1b3501c35b7cff96468602f Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 23 Feb 2023 22:43:53 +0200 Subject: SL-19209 WIP Switch MS Bing to MS Azure --- indra/newview/lltranslate.cpp | 117 +++++++++++++++++++++++++++++++----------- 1 file changed, 88 insertions(+), 29 deletions(-) (limited to 'indra/newview/lltranslate.cpp') diff --git a/indra/newview/lltranslate.cpp b/indra/newview/lltranslate.cpp index a2c696c762..3e6bf4fe82 100644 --- a/indra/newview/lltranslate.cpp +++ b/indra/newview/lltranslate.cpp @@ -41,7 +41,7 @@ #include "llurlregistry.h" -static const std::string BING_NOTRANSLATE_OPENING_TAG("
"); +static const std::string BING_NOTRANSLATE_OPENING_TAG("
"); static const std::string BING_NOTRANSLATE_CLOSING_TAG("
"); /** @@ -113,6 +113,14 @@ public: void verifyKeyCoro(LLTranslate::EService service, std::string key, LLTranslate::KeyVerificationResult_fn fnc); void translateMessageCoro(LanguagePair_t fromTo, std::string msg, LLTranslate::TranslationSuccess_fn success, LLTranslate::TranslationFailure_fn failure); + + virtual void initHttpHeader(LLCore::HttpHeaders::ptr_t headers, const std::string& user_agent) const = 0; + virtual LLSD sendMessageAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, + LLCore::HttpRequest::ptr_t request, + LLCore::HttpOptions::ptr_t options, + LLCore::HttpHeaders::ptr_t headers, + const std::string & url, + const std::string & msg) const = 0; }; void LLTranslationAPIHandler::translateMessage(LanguagePair_t fromTo, std::string msg, LLTranslate::TranslationSuccess_fn success, LLTranslate::TranslationFailure_fn failure) @@ -184,8 +192,7 @@ void LLTranslationAPIHandler::translateMessageCoro(LanguagePair_t fromTo, std::s LLVersionInfo::instance().getPatch(), LLVersionInfo::instance().getBuild()); - httpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_TEXT_PLAIN); - httpHeaders->append(HTTP_OUT_HEADER_USER_AGENT, user_agent); + initHttpHeader(httpHeaders, user_agent); httpOpts->setSSLVerifyPeer(false); std::string url = this->getTranslateURL(fromTo.first, fromTo.second, msg); @@ -194,8 +201,10 @@ void LLTranslationAPIHandler::translateMessageCoro(LanguagePair_t fromTo, std::s LL_INFOS("Translate") << "No translation URL" << LL_ENDL; return; } + LL_INFOS() << "Message: " << msg << LL_ENDL; + LL_INFOS() << "Requesting: " << url << LL_ENDL; - LLSD result = httpAdapter->getRawAndSuspend(httpRequest, url, httpOpts, httpHeaders); + LLSD result = sendMessageAndSuspend(httpAdapter, httpRequest, httpOpts, httpHeaders, url, msg); if (LLApp::isQuitting()) { @@ -280,6 +289,14 @@ public: /*virtual*/ void verifyKey(const std::string &key, LLTranslate::KeyVerificationResult_fn fnc); + void initHttpHeader(LLCore::HttpHeaders::ptr_t headers, const std::string& user_agent) const override; + LLSD sendMessageAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, + LLCore::HttpRequest::ptr_t request, + LLCore::HttpOptions::ptr_t options, + LLCore::HttpHeaders::ptr_t headers, + const std::string & url, + const std::string & msg) const override; + private: static void parseErrorResponse( const Json::Value& root, @@ -417,31 +434,55 @@ void LLGoogleTranslationHandler::verifyKey(const std::string &key, LLTranslate:: this, LLTranslate::SERVICE_GOOGLE, key, fnc)); } +/*virtual*/ +void LLGoogleTranslationHandler::initHttpHeader(LLCore::HttpHeaders::ptr_t headers, const std::string& user_agent) const +{ + headers->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_TEXT_PLAIN); + headers->append(HTTP_OUT_HEADER_USER_AGENT, user_agent); +} + +LLSD LLGoogleTranslationHandler::sendMessageAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, + LLCore::HttpRequest::ptr_t request, + LLCore::HttpOptions::ptr_t options, + LLCore::HttpHeaders::ptr_t headers, + const std::string & url, + const std::string & msg) const +{ + return adapter->getRawAndSuspend(request, url, options, headers); +} //========================================================================= /// Microsoft Translator v2 API handler. -class LLBingTranslationHandler : public LLTranslationAPIHandler +class LLAzureTranslationHandler : public LLTranslationAPIHandler { - LOG_CLASS(LLBingTranslationHandler); + LOG_CLASS(LLAzureTranslationHandler); public: - /*virtual*/ std::string getTranslateURL( + std::string getTranslateURL( const std::string &from_lang, const std::string &to_lang, - const std::string &text) const; - /*virtual*/ std::string getKeyVerificationURL( - const std::string &key) const; - /*virtual*/ bool parseResponse( + const std::string &text) const override; + std::string getKeyVerificationURL( + const std::string &key) const override; + bool parseResponse( int& status, const std::string& body, std::string& translation, std::string& detected_lang, - std::string& err_msg) const; - /*virtual*/ bool isConfigured() const; + std::string& err_msg) const override; + bool isConfigured() const override; - /*virtual*/ LLTranslate::EService getCurrentService() { return LLTranslate::EService::SERVICE_BING; } + LLTranslate::EService getCurrentService() override { return LLTranslate::EService::SERVICE_BING; } - /*virtual*/ void verifyKey(const std::string &key, LLTranslate::KeyVerificationResult_fn fnc); + void verifyKey(const std::string &key, LLTranslate::KeyVerificationResult_fn fnc) override; + + void initHttpHeader(LLCore::HttpHeaders::ptr_t headers, const std::string& user_agent) const override; + LLSD sendMessageAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, + LLCore::HttpRequest::ptr_t request, + LLCore::HttpOptions::ptr_t options, + LLCore::HttpHeaders::ptr_t headers, + const std::string & url, + const std::string & msg) const override; private: static std::string getAPIKey(); static std::string getAPILanguageCode(const std::string& lang); @@ -450,23 +491,20 @@ private: //------------------------------------------------------------------------- // virtual -std::string LLBingTranslationHandler::getTranslateURL( +std::string LLAzureTranslationHandler::getTranslateURL( const std::string &from_lang, const std::string &to_lang, const std::string &text) const { - std::string url = std::string("http://api.microsofttranslator.com/v2/Http.svc/Translate?appId=") - + getAPIKey() + "&text=" + LLURI::escape(text) + "&to=" + getAPILanguageCode(to_lang); - if (!from_lang.empty()) - { - url += "&from=" + getAPILanguageCode(from_lang); - } + // Global service. Alternatively regional services exist. + std::string url = std::string("https://api.cognitive.microsofttranslator.com/translate?api-version=3.0&to=") + + getAPILanguageCode(to_lang) + "&Subscription-Key=" + getAPIKey(); return url; } // virtual -std::string LLBingTranslationHandler::getKeyVerificationURL( +std::string LLAzureTranslationHandler::getKeyVerificationURL( const std::string& key) const { std::string url = std::string("http://api.microsofttranslator.com/v2/Http.svc/GetLanguagesForTranslate?appId=") @@ -475,7 +513,7 @@ std::string LLBingTranslationHandler::getKeyVerificationURL( } // virtual -bool LLBingTranslationHandler::parseResponse( +bool LLAzureTranslationHandler::parseResponse( int& status, const std::string& body, std::string& translation, @@ -521,30 +559,51 @@ bool LLBingTranslationHandler::parseResponse( } // virtual -bool LLBingTranslationHandler::isConfigured() const +bool LLAzureTranslationHandler::isConfigured() const { return !getAPIKey().empty(); } // static -std::string LLBingTranslationHandler::getAPIKey() +std::string LLAzureTranslationHandler::getAPIKey() { return gSavedSettings.getString("BingTranslateAPIKey"); } // static -std::string LLBingTranslationHandler::getAPILanguageCode(const std::string& lang) +std::string LLAzureTranslationHandler::getAPILanguageCode(const std::string& lang) { return lang == "zh" ? "zh-CHT" : lang; // treat Chinese as Traditional Chinese } /*virtual*/ -void LLBingTranslationHandler::verifyKey(const std::string &key, LLTranslate::KeyVerificationResult_fn fnc) +void LLAzureTranslationHandler::verifyKey(const std::string &key, LLTranslate::KeyVerificationResult_fn fnc) { LLCoros::instance().launch("Bing /Verify Key", boost::bind(&LLTranslationAPIHandler::verifyKeyCoro, this, LLTranslate::SERVICE_BING, key, fnc)); } +/*virtual*/ +void LLAzureTranslationHandler::initHttpHeader(LLCore::HttpHeaders::ptr_t headers, const std::string& user_agent) const +{ + headers->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_JSON); + // Token based autorization exists + //headers->append("Ocp-Apim-Subscription-Key", getAPIKey()); + headers->append(HTTP_OUT_HEADER_USER_AGENT, user_agent); +} + +LLSD LLAzureTranslationHandler::sendMessageAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, + LLCore::HttpRequest::ptr_t request, + LLCore::HttpOptions::ptr_t options, + LLCore::HttpHeaders::ptr_t headers, + const std::string & url, + const std::string & msg) const +{ + LLSD body; + body["text"] = "Hello, what is your name?"; + return adapter->postJsonAndSuspend(request, url, body, headers); +} + //========================================================================= /*static*/ void LLTranslate::translateMessage(const std::string &from_lang, const std::string &to_lang, @@ -652,7 +711,7 @@ LLTranslationAPIHandler& LLTranslate::getPreferredHandler() LLTranslationAPIHandler& LLTranslate::getHandler(EService service) { static LLGoogleTranslationHandler google; - static LLBingTranslationHandler bing; + static LLAzureTranslationHandler bing; if (service == SERVICE_GOOGLE) { -- cgit v1.2.3 From 3fc3627f2d27b181269416f346f29d6451f7009a Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 24 Feb 2023 11:59:48 +0200 Subject: SL-19209 WIP Switch MS Bing to MS Azure #2 --- indra/newview/lltranslate.cpp | 317 +++++++++++++++++++++++++++++++----------- 1 file changed, 236 insertions(+), 81 deletions(-) (limited to 'indra/newview/lltranslate.cpp') diff --git a/indra/newview/lltranslate.cpp b/indra/newview/lltranslate.cpp index 3e6bf4fe82..706b4cc6ee 100644 --- a/indra/newview/lltranslate.cpp +++ b/indra/newview/lltranslate.cpp @@ -80,7 +80,18 @@ public: * @param[in] key Key to verify. */ virtual std::string getKeyVerificationURL( - const std::string &key) const = 0; + const LLSD &key) const = 0; + + /** + * Check API verification response. + * + * @param[out] bool true if valid. + * @param[in] response + * @param[in] status + */ + virtual bool checkVerificationResponse( + const LLSD &response, + int status) const = 0; /** * Parse translation response. @@ -105,22 +116,28 @@ public: virtual LLTranslate::EService getCurrentService() = 0; - virtual void verifyKey(const std::string &key, LLTranslate::KeyVerificationResult_fn fnc) = 0; + virtual void verifyKey(const LLSD &key, LLTranslate::KeyVerificationResult_fn fnc) = 0; virtual void translateMessage(LanguagePair_t fromTo, std::string msg, LLTranslate::TranslationSuccess_fn success, LLTranslate::TranslationFailure_fn failure); virtual ~LLTranslationAPIHandler() {} - void verifyKeyCoro(LLTranslate::EService service, std::string key, LLTranslate::KeyVerificationResult_fn fnc); + void verifyKeyCoro(LLTranslate::EService service, LLSD key, LLTranslate::KeyVerificationResult_fn fnc); void translateMessageCoro(LanguagePair_t fromTo, std::string msg, LLTranslate::TranslationSuccess_fn success, LLTranslate::TranslationFailure_fn failure); virtual void initHttpHeader(LLCore::HttpHeaders::ptr_t headers, const std::string& user_agent) const = 0; + virtual void initHttpHeader(LLCore::HttpHeaders::ptr_t headers, const std::string& user_agent, const LLSD &key) const = 0; virtual LLSD sendMessageAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, LLCore::HttpRequest::ptr_t request, LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers, const std::string & url, const std::string & msg) const = 0; + virtual LLSD verifyAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, + LLCore::HttpRequest::ptr_t request, + LLCore::HttpOptions::ptr_t options, + LLCore::HttpHeaders::ptr_t headers, + const std::string & url) const = 0; }; void LLTranslationAPIHandler::translateMessage(LanguagePair_t fromTo, std::string msg, LLTranslate::TranslationSuccess_fn success, LLTranslate::TranslationFailure_fn failure) @@ -130,8 +147,7 @@ void LLTranslationAPIHandler::translateMessage(LanguagePair_t fromTo, std::strin } - -void LLTranslationAPIHandler::verifyKeyCoro(LLTranslate::EService service, std::string key, LLTranslate::KeyVerificationResult_fn fnc) +void LLTranslationAPIHandler::verifyKeyCoro(LLTranslate::EService service, LLSD key, LLTranslate::KeyVerificationResult_fn fnc) { LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t @@ -148,8 +164,7 @@ void LLTranslationAPIHandler::verifyKeyCoro(LLTranslate::EService service, std:: LLVersionInfo::instance().getPatch(), LLVersionInfo::instance().getBuild()); - httpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_TEXT_PLAIN); - httpHeaders->append(HTTP_OUT_HEADER_USER_AGENT, user_agent); + initHttpHeader(httpHeaders, user_agent, key); httpOpts->setFollowRedirects(true); httpOpts->setSSLVerifyPeer(false); @@ -161,17 +176,22 @@ void LLTranslationAPIHandler::verifyKeyCoro(LLTranslate::EService service, std:: return; } - LLSD result = httpAdapter->getAndSuspend(httpRequest, url, httpOpts, httpHeaders); + LLSD result = verifyAndSuspend(httpAdapter, httpRequest, httpOpts, httpHeaders, url); LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); bool bOk = true; - if (!status) + int parseResult = status.getType(); + if (!checkVerificationResponse(httpResults, parseResult)) + { bOk = false; + } if (!fnc.empty()) + { fnc(service, bOk); + } } void LLTranslationAPIHandler::translateMessageCoro(LanguagePair_t fromTo, std::string msg, @@ -192,7 +212,7 @@ void LLTranslationAPIHandler::translateMessageCoro(LanguagePair_t fromTo, std::s LLVersionInfo::instance().getPatch(), LLVersionInfo::instance().getBuild()); - initHttpHeader(httpHeaders, user_agent); + initHttpHeader(httpHeaders, user_agent); httpOpts->setSSLVerifyPeer(false); std::string url = this->getTranslateURL(fromTo.first, fromTo.second, msg); @@ -251,6 +271,11 @@ void LLTranslationAPIHandler::translateMessageCoro(LanguagePair_t fromTo, std::s } else { + if (err_msg.empty() && httpResults.has("error_body")) + { + err_msg = httpResults["error_body"].asString(); + } + if (err_msg.empty()) { err_msg = LLTrans::getString("TranslationResponseParseError"); @@ -271,25 +296,29 @@ class LLGoogleTranslationHandler : public LLTranslationAPIHandler LOG_CLASS(LLGoogleTranslationHandler); public: - /*virtual*/ std::string getTranslateURL( + std::string getTranslateURL( const std::string &from_lang, const std::string &to_lang, - const std::string &text) const; - /*virtual*/ std::string getKeyVerificationURL( - const std::string &key) const; - /*virtual*/ bool parseResponse( + const std::string &text) const override; + std::string getKeyVerificationURL( + const LLSD &key) const override; + bool checkVerificationResponse( + const LLSD &response, + int status) const override; + bool parseResponse( int& status, const std::string& body, std::string& translation, std::string& detected_lang, - std::string& err_msg) const; - /*virtual*/ bool isConfigured() const; + std::string& err_msg) const override; + bool isConfigured() const override; - /*virtual*/ LLTranslate::EService getCurrentService() { return LLTranslate::EService::SERVICE_GOOGLE; } + LLTranslate::EService getCurrentService() override { return LLTranslate::EService::SERVICE_GOOGLE; } - /*virtual*/ void verifyKey(const std::string &key, LLTranslate::KeyVerificationResult_fn fnc); + void verifyKey(const LLSD &key, LLTranslate::KeyVerificationResult_fn fnc) override; void initHttpHeader(LLCore::HttpHeaders::ptr_t headers, const std::string& user_agent) const override; + void initHttpHeader(LLCore::HttpHeaders::ptr_t headers, const std::string& user_agent, const LLSD &key) const override; LLSD sendMessageAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, LLCore::HttpRequest::ptr_t request, LLCore::HttpOptions::ptr_t options, @@ -297,6 +326,12 @@ public: const std::string & url, const std::string & msg) const override; + LLSD verifyAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, + LLCore::HttpRequest::ptr_t request, + LLCore::HttpOptions::ptr_t options, + LLCore::HttpHeaders::ptr_t headers, + const std::string & url) const override; + private: static void parseErrorResponse( const Json::Value& root, @@ -328,13 +363,21 @@ std::string LLGoogleTranslationHandler::getTranslateURL( // virtual std::string LLGoogleTranslationHandler::getKeyVerificationURL( - const std::string& key) const + const LLSD& key) const { - std::string url = std::string("https://www.googleapis.com/language/translate/v2/languages?key=") - + key + "&target=en"; + std::string url = std::string("https://www.googleapis.com/language/translate/v2/languages?key=") + + key.asString() +"&target=en"; return url; } +//virtual +bool LLGoogleTranslationHandler::checkVerificationResponse( + const LLSD &response, + int status) const +{ + return status == HTTP_OK; +} + // virtual bool LLGoogleTranslationHandler::parseResponse( int& status, @@ -424,11 +467,12 @@ bool LLGoogleTranslationHandler::parseTranslation( // static std::string LLGoogleTranslationHandler::getAPIKey() { - return gSavedSettings.getString("GoogleTranslateAPIKey"); + static LLCachedControl google_key(gSavedSettings, "GoogleTranslateAPIKey"); + return google_key; } /*virtual*/ -void LLGoogleTranslationHandler::verifyKey(const std::string &key, LLTranslate::KeyVerificationResult_fn fnc) +void LLGoogleTranslationHandler::verifyKey(const LLSD &key, LLTranslate::KeyVerificationResult_fn fnc) { LLCoros::instance().launch("Google /Verify Key", boost::bind(&LLTranslationAPIHandler::verifyKeyCoro, this, LLTranslate::SERVICE_GOOGLE, key, fnc)); @@ -441,6 +485,16 @@ void LLGoogleTranslationHandler::initHttpHeader(LLCore::HttpHeaders::ptr_t heade headers->append(HTTP_OUT_HEADER_USER_AGENT, user_agent); } +/*virtual*/ +void LLGoogleTranslationHandler::initHttpHeader( + LLCore::HttpHeaders::ptr_t headers, + const std::string& user_agent, + const LLSD &key) const +{ + headers->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_TEXT_PLAIN); + headers->append(HTTP_OUT_HEADER_USER_AGENT, user_agent); +} + LLSD LLGoogleTranslationHandler::sendMessageAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, LLCore::HttpRequest::ptr_t request, LLCore::HttpOptions::ptr_t options, @@ -451,6 +505,15 @@ LLSD LLGoogleTranslationHandler::sendMessageAndSuspend(LLCoreHttpUtil::HttpCorou return adapter->getRawAndSuspend(request, url, options, headers); } +LLSD LLGoogleTranslationHandler::verifyAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, + LLCore::HttpRequest::ptr_t request, + LLCore::HttpOptions::ptr_t options, + LLCore::HttpHeaders::ptr_t headers, + const std::string & url) const +{ + return adapter->getAndSuspend(request, url, options, headers); +} + //========================================================================= /// Microsoft Translator v2 API handler. class LLAzureTranslationHandler : public LLTranslationAPIHandler @@ -463,7 +526,10 @@ public: const std::string &to_lang, const std::string &text) const override; std::string getKeyVerificationURL( - const std::string &key) const override; + const LLSD &key) const override; + bool checkVerificationResponse( + const LLSD &response, + int status) const override; bool parseResponse( int& status, const std::string& body, @@ -472,19 +538,26 @@ public: std::string& err_msg) const override; bool isConfigured() const override; - LLTranslate::EService getCurrentService() override { return LLTranslate::EService::SERVICE_BING; } + LLTranslate::EService getCurrentService() override { return LLTranslate::EService::SERVICE_AZURE; } - void verifyKey(const std::string &key, LLTranslate::KeyVerificationResult_fn fnc) override; + void verifyKey(const LLSD &key, LLTranslate::KeyVerificationResult_fn fnc) override; void initHttpHeader(LLCore::HttpHeaders::ptr_t headers, const std::string& user_agent) const override; + void initHttpHeader(LLCore::HttpHeaders::ptr_t headers, const std::string& user_agent, const LLSD &key) const override; LLSD sendMessageAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, LLCore::HttpRequest::ptr_t request, LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers, const std::string & url, const std::string & msg) const override; + + LLSD verifyAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, + LLCore::HttpRequest::ptr_t request, + LLCore::HttpOptions::ptr_t options, + LLCore::HttpHeaders::ptr_t headers, + const std::string & url) const override; private: - static std::string getAPIKey(); + static LLSD getAPIKey(); static std::string getAPILanguageCode(const std::string& lang); }; @@ -496,22 +569,49 @@ std::string LLAzureTranslationHandler::getTranslateURL( const std::string &to_lang, const std::string &text) const { - // Global service. Alternatively regional services exist. - std::string url = std::string("https://api.cognitive.microsofttranslator.com/translate?api-version=3.0&to=") - + getAPILanguageCode(to_lang) + "&Subscription-Key=" + getAPIKey(); + std::string url; + LLSD key = getAPIKey(); + if (key.isMap()) + { + std::string endpoint = key["endpoint"].asString(); + // todo: validate url + if (*endpoint.rbegin() != '/') + { + endpoint += "/"; + } + url = endpoint + std::string("translate?api-version=3.0&to=") + + getAPILanguageCode(to_lang); + } return url; } // virtual std::string LLAzureTranslationHandler::getKeyVerificationURL( - const std::string& key) const + const LLSD& key) const { - std::string url = std::string("http://api.microsofttranslator.com/v2/Http.svc/GetLanguagesForTranslate?appId=") - + key; + std::string url; + if (key.isMap()) + { + std::string endpoint = key["endpoint"].asString(); + // todo: validate url + if (*endpoint.rbegin() != '/') + { + endpoint += "/"; + } + url = endpoint + std::string("translate?api-version=3.0&to=en"); + } return url; } +//virtual +bool LLAzureTranslationHandler::checkVerificationResponse( + const LLSD &response, + int status) const +{ + return status == HTTP_BAD_REQUEST; // would have been 401 if id was wrong +} + // virtual bool LLAzureTranslationHandler::parseResponse( int& status, @@ -522,52 +622,71 @@ bool LLAzureTranslationHandler::parseResponse( { if (status != HTTP_OK) { - static const std::string MSG_BEGIN_MARKER = "Message: "; - size_t begin = body.find(MSG_BEGIN_MARKER); - if (begin != std::string::npos) - { - begin += MSG_BEGIN_MARKER.size(); - } - else - { - begin = 0; - err_msg.clear(); - } - size_t end = body.find("

", begin); - err_msg = body.substr(begin, end-begin); - LLStringUtil::replaceString(err_msg, " ", ""); // strip CR return false; } - // Sample response: Hola - size_t begin = body.find(">"); - if (begin == std::string::npos || begin >= (body.size() - 1)) - { - begin = 0; - } - else - { - ++begin; - } + //Example: + // "[{\"detectedLanguage\":{\"language\":\"en\",\"score\":1.0},\"translations\":[{\"text\":\"Hello, what is your name?\",\"to\":\"en\"}]}]" - size_t end = body.find("", begin); + Json::Value root; + Json::Reader reader; - detected_lang = ""; // unsupported by this API - translation = body.substr(begin, end-begin); - LLStringUtil::replaceString(translation, " ", ""); // strip CR - return true; + if (!reader.parse(body, root)) + { + err_msg = reader.getFormatedErrorMessages(); + return false; + } + + if (!root.isArray()) // empty response? should not happen + { + return false; + } + + // Request succeeded, extract translation from the response. + + const Json::Value& data = root[0U]; + if (!data.isObject() + || !data.isMember("detectedLanguage") + || !data.isMember("translations")) + { + return false; + } + + const Json::Value& detectedLanguage = data["detectedLanguage"]; + if (!detectedLanguage.isObject() || !detectedLanguage.isMember("language")) + { + return false; + } + detected_lang = detectedLanguage["language"].asString(); + + const Json::Value& translations = data["translations"]; + if (!translations.isArray() || translations.size() == 0) + { + return false; + } + + const Json::Value& first = translations[0U]; + if (!first.isObject() || !first.isMember("text")) + { + return false; + } + + translation = first["text"].asString(); + + return true; } // virtual bool LLAzureTranslationHandler::isConfigured() const { - return !getAPIKey().empty(); + return !getAPIKey().isMap(); } // static -std::string LLAzureTranslationHandler::getAPIKey() +LLSD LLAzureTranslationHandler::getAPIKey() { - return gSavedSettings.getString("BingTranslateAPIKey"); + static LLCachedControl azure_key(gSavedSettings, "AzureTranslateAPIKey"); + return azure_key; } // static @@ -577,19 +696,38 @@ std::string LLAzureTranslationHandler::getAPILanguageCode(const std::string& lan } /*virtual*/ -void LLAzureTranslationHandler::verifyKey(const std::string &key, LLTranslate::KeyVerificationResult_fn fnc) +void LLAzureTranslationHandler::verifyKey(const LLSD &key, LLTranslate::KeyVerificationResult_fn fnc) { - LLCoros::instance().launch("Bing /Verify Key", boost::bind(&LLTranslationAPIHandler::verifyKeyCoro, - this, LLTranslate::SERVICE_BING, key, fnc)); + LLCoros::instance().launch("Azure /Verify Key", boost::bind(&LLTranslationAPIHandler::verifyKeyCoro, + this, LLTranslate::SERVICE_AZURE, key, fnc)); +} +/*virtual*/ +void LLAzureTranslationHandler::initHttpHeader( + LLCore::HttpHeaders::ptr_t headers, + const std::string& user_agent) const +{ + initHttpHeader(headers, user_agent, getAPIKey()); } /*virtual*/ -void LLAzureTranslationHandler::initHttpHeader(LLCore::HttpHeaders::ptr_t headers, const std::string& user_agent) const +void LLAzureTranslationHandler::initHttpHeader( + LLCore::HttpHeaders::ptr_t headers, + const std::string& user_agent, + const LLSD &key) const { headers->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_JSON); - // Token based autorization exists - //headers->append("Ocp-Apim-Subscription-Key", getAPIKey()); headers->append(HTTP_OUT_HEADER_USER_AGENT, user_agent); + + if (key.has("id")) + { + // Token based autorization + headers->append("Ocp-Apim-Subscription-Key", key["id"].asString()); + } + if (key.has("region")) + { + // ex: "westeurope" + headers->append("Ocp-Apim-Subscription-Region", key["region"].asString()); + } } LLSD LLAzureTranslationHandler::sendMessageAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, @@ -599,9 +737,26 @@ LLSD LLAzureTranslationHandler::sendMessageAndSuspend(LLCoreHttpUtil::HttpCorout const std::string & url, const std::string & msg) const { - LLSD body; - body["text"] = "Hello, what is your name?"; - return adapter->postJsonAndSuspend(request, url, body, headers); + LLCore::BufferArray::ptr_t rawbody(new LLCore::BufferArray); + LLCore::BufferArrayStream outs(rawbody.get()); + outs << "[{\"text\":\""; + outs << msg; + outs << "\"}]"; + + return adapter->postRawAndSuspend(request, url, rawbody, options, headers); +} + +LLSD LLAzureTranslationHandler::verifyAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, + LLCore::HttpRequest::ptr_t request, + LLCore::HttpOptions::ptr_t options, + LLCore::HttpHeaders::ptr_t headers, + const std::string & url) const +{ + LLCore::BufferArray::ptr_t rawbody(new LLCore::BufferArray); + LLCore::BufferArrayStream outs(rawbody.get()); + outs << "[{\"intentionally_invalid_400\"}]"; + + return adapter->postRawAndSuspend(request, url, rawbody, options, headers); } //========================================================================= @@ -616,7 +771,7 @@ void LLTranslate::translateMessage(const std::string &from_lang, const std::stri std::string LLTranslate::addNoTranslateTags(std::string mesg) { - if (getPreferredHandler().getCurrentService() != SERVICE_BING) + if (getPreferredHandler().getCurrentService() != SERVICE_AZURE) { return mesg; } @@ -637,7 +792,7 @@ std::string LLTranslate::addNoTranslateTags(std::string mesg) std::string LLTranslate::removeNoTranslateTags(std::string mesg) { - if (getPreferredHandler().getCurrentService() != SERVICE_BING) + if (getPreferredHandler().getCurrentService() != SERVICE_AZURE) { return mesg; } @@ -667,7 +822,7 @@ std::string LLTranslate::removeNoTranslateTags(std::string mesg) } /*static*/ -void LLTranslate::verifyKey(EService service, const std::string &key, KeyVerificationResult_fn fnc) +void LLTranslate::verifyKey(EService service, const LLSD &key, KeyVerificationResult_fn fnc) { LLTranslationAPIHandler& handler = getHandler(service); @@ -696,7 +851,7 @@ bool LLTranslate::isTranslationConfigured() // static LLTranslationAPIHandler& LLTranslate::getPreferredHandler() { - EService service = SERVICE_BING; + EService service = SERVICE_AZURE; std::string service_str = gSavedSettings.getString("TranslationService"); if (service_str == "google") @@ -711,12 +866,12 @@ LLTranslationAPIHandler& LLTranslate::getPreferredHandler() LLTranslationAPIHandler& LLTranslate::getHandler(EService service) { static LLGoogleTranslationHandler google; - static LLAzureTranslationHandler bing; + static LLAzureTranslationHandler azure; if (service == SERVICE_GOOGLE) { return google; } - return bing; + return azure; } -- cgit v1.2.3 From f7210db06ee677231b93b750c205cd0317a9901f Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 28 Feb 2023 00:13:31 +0200 Subject: SL-19209 Switch MS Bing to MS Azure #3 --- indra/newview/lltranslate.cpp | 168 ++++++++++++++++++++++++++++++++---------- 1 file changed, 130 insertions(+), 38 deletions(-) (limited to 'indra/newview/lltranslate.cpp') diff --git a/indra/newview/lltranslate.cpp b/indra/newview/lltranslate.cpp index 706b4cc6ee..b0c4418ae9 100644 --- a/indra/newview/lltranslate.cpp +++ b/indra/newview/lltranslate.cpp @@ -41,8 +41,8 @@ #include "llurlregistry.h" -static const std::string BING_NOTRANSLATE_OPENING_TAG("
"); -static const std::string BING_NOTRANSLATE_CLOSING_TAG("
"); +static const std::string AZURE_NOTRANSLATE_OPENING_TAG("
"); +static const std::string AZURE_NOTRANSLATE_CLOSING_TAG("
"); /** * Handler of an HTTP machine translation service. @@ -103,6 +103,7 @@ public: * @param[out] err_msg Error message (in case of error). */ virtual bool parseResponse( + const LLSD& http_response, int& status, const std::string& body, std::string& translation, @@ -176,6 +177,13 @@ void LLTranslationAPIHandler::verifyKeyCoro(LLTranslate::EService service, LLSD return; } + std::string::size_type delim_pos = url.find("://"); + if (delim_pos == std::string::npos) + { + LL_INFOS("Translate") << "URL is missing a scheme" << LL_ENDL; + return; + } + LLSD result = verifyAndSuspend(httpAdapter, httpRequest, httpOpts, httpHeaders, url); LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; @@ -190,7 +198,7 @@ void LLTranslationAPIHandler::verifyKeyCoro(LLTranslate::EService service, LLSD if (!fnc.empty()) { - fnc(service, bOk); + fnc(service, bOk, parseResult); } } @@ -221,8 +229,8 @@ void LLTranslationAPIHandler::translateMessageCoro(LanguagePair_t fromTo, std::s LL_INFOS("Translate") << "No translation URL" << LL_ENDL; return; } - LL_INFOS() << "Message: " << msg << LL_ENDL; - LL_INFOS() << "Requesting: " << url << LL_ENDL; + LL_DEBUGS("Translate") << "Message: " << msg << LL_ENDL; + LL_DEBUGS("Translate") << "Requesting: " << url << LL_ENDL; LLSD result = sendMessageAndSuspend(httpAdapter, httpRequest, httpOpts, httpHeaders, url, msg); @@ -245,7 +253,7 @@ void LLTranslationAPIHandler::translateMessageCoro(LanguagePair_t fromTo, std::s try { - res = this->parseResponse(parseResult, body, translation, detected_lang, err_msg); + res = this->parseResponse(httpResults, parseResult, body, translation, detected_lang, err_msg); } catch (std::out_of_range&) { @@ -306,6 +314,7 @@ public: const LLSD &response, int status) const override; bool parseResponse( + const LLSD& http_response, int& status, const std::string& body, std::string& translation, @@ -380,6 +389,7 @@ bool LLGoogleTranslationHandler::checkVerificationResponse( // virtual bool LLGoogleTranslationHandler::parseResponse( + const LLSD& http_response, int& status, const std::string& body, std::string& translation, @@ -531,6 +541,7 @@ public: const LLSD &response, int status) const override; bool parseResponse( + const LLSD& http_response, int& status, const std::string& body, std::string& translation, @@ -557,6 +568,8 @@ public: LLCore::HttpHeaders::ptr_t headers, const std::string & url) const override; private: + static std::string parseErrorResponse( + const std::string& body); static LLSD getAPIKey(); static std::string getAPILanguageCode(const std::string& lang); @@ -574,7 +587,7 @@ std::string LLAzureTranslationHandler::getTranslateURL( if (key.isMap()) { std::string endpoint = key["endpoint"].asString(); - // todo: validate url + if (*endpoint.rbegin() != '/') { endpoint += "/"; @@ -594,7 +607,6 @@ std::string LLAzureTranslationHandler::getKeyVerificationURL( if (key.isMap()) { std::string endpoint = key["endpoint"].asString(); - // todo: validate url if (*endpoint.rbegin() != '/') { endpoint += "/"; @@ -609,11 +621,48 @@ bool LLAzureTranslationHandler::checkVerificationResponse( const LLSD &response, int status) const { - return status == HTTP_BAD_REQUEST; // would have been 401 if id was wrong + if (status == HTTP_UNAUTHORIZED) + { + LL_DEBUGS("Translate") << "Key unathorised" << LL_ENDL; + return false; + } + + if (status == HTTP_NOT_FOUND) + { + LL_DEBUGS("Translate") << "Either endpoint doesn't have requested resource" << LL_ENDL; + return false; + } + + if (status != HTTP_BAD_REQUEST) + { + LL_DEBUGS("Translate") << "Unexpected error code" << LL_ENDL; + return false; + } + + if (!response.has("error_body")) + { + LL_DEBUGS("Translate") << "Unexpected response, no error returned" << LL_ENDL; + return false; + } + + // Expected: "{\"error\":{\"code\":400000,\"message\":\"One of the request inputs is not valid.\"}}" + // But for now just verify response is a valid json with an error + + Json::Value root; + Json::Reader reader; + + if (!reader.parse(response["error_body"].asString(), root)) + { + LL_DEBUGS("Translate") << "Failed to parse error_body:" << reader.getFormatedErrorMessages() << LL_ENDL; + return false; + } + + return true; } // virtual bool LLAzureTranslationHandler::parseResponse( + const LLSD& http_response, int& status, const std::string& body, std::string& translation, @@ -622,6 +671,8 @@ bool LLAzureTranslationHandler::parseResponse( { if (status != HTTP_OK) { + if (http_response.has("error_body")) + err_msg = parseErrorResponse(http_response["error_body"].asString()); return false; } @@ -682,6 +733,36 @@ bool LLAzureTranslationHandler::isConfigured() const return !getAPIKey().isMap(); } +//static +std::string LLAzureTranslationHandler::parseErrorResponse( + const std::string& body) +{ + // Expected: "{\"error\":{\"code\":400000,\"message\":\"One of the request inputs is not valid.\"}}" + // But for now just verify response is a valid json with an error + + Json::Value root; + Json::Reader reader; + + if (!reader.parse(body, root)) + { + return std::string(); + } + + if (!root.isObject() || !root.isMember("error")) + { + return std::string(); + } + + const Json::Value& error_map = root["error"]; + + if (!error_map.isObject() || !error_map.isMember("message")) + { + return std::string(); + } + + return error_map["message"].asString(); +} + // static LLSD LLAzureTranslationHandler::getAPIKey() { @@ -771,54 +852,65 @@ void LLTranslate::translateMessage(const std::string &from_lang, const std::stri std::string LLTranslate::addNoTranslateTags(std::string mesg) { - if (getPreferredHandler().getCurrentService() != SERVICE_AZURE) + if (getPreferredHandler().getCurrentService() == SERVICE_GOOGLE) { return mesg; } - std::string upd_msg(mesg); - LLUrlMatch match; - S32 dif = 0; - //surround all links (including SLURLs) with 'no-translate' tags to prevent unnecessary translation - while (LLUrlRegistry::instance().findUrl(mesg, match)) + if (getPreferredHandler().getCurrentService() == SERVICE_AZURE) { - upd_msg.insert(dif + match.getStart(), BING_NOTRANSLATE_OPENING_TAG); - upd_msg.insert(dif + BING_NOTRANSLATE_OPENING_TAG.size() + match.getEnd() + 1, BING_NOTRANSLATE_CLOSING_TAG); - mesg.erase(match.getStart(), match.getEnd() - match.getStart()); - dif += match.getEnd() - match.getStart() + BING_NOTRANSLATE_OPENING_TAG.size() + BING_NOTRANSLATE_CLOSING_TAG.size(); + // https://learn.microsoft.com/en-us/azure/cognitive-services/translator/prevent-translation + std::string upd_msg(mesg); + LLUrlMatch match; + S32 dif = 0; + //surround all links (including SLURLs) with 'no-translate' tags to prevent unnecessary translation + while (LLUrlRegistry::instance().findUrl(mesg, match)) + { + upd_msg.insert(dif + match.getStart(), AZURE_NOTRANSLATE_OPENING_TAG); + upd_msg.insert(dif + AZURE_NOTRANSLATE_OPENING_TAG.size() + match.getEnd() + 1, AZURE_NOTRANSLATE_CLOSING_TAG); + mesg.erase(match.getStart(), match.getEnd() - match.getStart()); + dif += match.getEnd() - match.getStart() + AZURE_NOTRANSLATE_OPENING_TAG.size() + AZURE_NOTRANSLATE_CLOSING_TAG.size(); + } + return upd_msg; } - return upd_msg; + return mesg; } std::string LLTranslate::removeNoTranslateTags(std::string mesg) { - if (getPreferredHandler().getCurrentService() != SERVICE_AZURE) + if (getPreferredHandler().getCurrentService() == SERVICE_GOOGLE) { return mesg; } - std::string upd_msg(mesg); - LLUrlMatch match; - S32 opening_tag_size = BING_NOTRANSLATE_OPENING_TAG.size(); - S32 closing_tag_size = BING_NOTRANSLATE_CLOSING_TAG.size(); - S32 dif = 0; - //remove 'no-translate' tags we added to the links before - while (LLUrlRegistry::instance().findUrl(mesg, match)) + + if (getPreferredHandler().getCurrentService() == SERVICE_AZURE) { - if (upd_msg.substr(dif + match.getStart() - opening_tag_size, opening_tag_size) == BING_NOTRANSLATE_OPENING_TAG) + std::string upd_msg(mesg); + LLUrlMatch match; + S32 opening_tag_size = AZURE_NOTRANSLATE_OPENING_TAG.size(); + S32 closing_tag_size = AZURE_NOTRANSLATE_CLOSING_TAG.size(); + S32 dif = 0; + //remove 'no-translate' tags we added to the links before + while (LLUrlRegistry::instance().findUrl(mesg, match)) { - upd_msg.erase(dif + match.getStart() - opening_tag_size, opening_tag_size); - dif -= opening_tag_size; - - if (upd_msg.substr(dif + match.getEnd() + 1, closing_tag_size) == BING_NOTRANSLATE_CLOSING_TAG) + if (upd_msg.substr(dif + match.getStart() - opening_tag_size, opening_tag_size) == AZURE_NOTRANSLATE_OPENING_TAG) { - upd_msg.replace(dif + match.getEnd() + 1, closing_tag_size, " "); - dif -= closing_tag_size - 1; + upd_msg.erase(dif + match.getStart() - opening_tag_size, opening_tag_size); + dif -= opening_tag_size; + + if (upd_msg.substr(dif + match.getEnd() + 1, closing_tag_size) == AZURE_NOTRANSLATE_CLOSING_TAG) + { + upd_msg.replace(dif + match.getEnd() + 1, closing_tag_size, " "); + dif -= closing_tag_size - 1; + } } + mesg.erase(match.getStart(), match.getUrl().size()); + dif += match.getUrl().size(); } - mesg.erase(match.getStart(), match.getUrl().size()); - dif += match.getUrl().size(); + return upd_msg; } - return upd_msg; + + return mesg; } /*static*/ -- cgit v1.2.3 From b2a06908859caa3d816ab1791fe2062142e2ccce Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 28 Feb 2023 00:51:25 +0200 Subject: SL-19209 Add default text for region field --- indra/newview/lltranslate.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'indra/newview/lltranslate.cpp') diff --git a/indra/newview/lltranslate.cpp b/indra/newview/lltranslate.cpp index b0c4418ae9..f84c660c0d 100644 --- a/indra/newview/lltranslate.cpp +++ b/indra/newview/lltranslate.cpp @@ -229,8 +229,6 @@ void LLTranslationAPIHandler::translateMessageCoro(LanguagePair_t fromTo, std::s LL_INFOS("Translate") << "No translation URL" << LL_ENDL; return; } - LL_DEBUGS("Translate") << "Message: " << msg << LL_ENDL; - LL_DEBUGS("Translate") << "Requesting: " << url << LL_ENDL; LLSD result = sendMessageAndSuspend(httpAdapter, httpRequest, httpOpts, httpHeaders, url, msg); @@ -646,7 +644,7 @@ bool LLAzureTranslationHandler::checkVerificationResponse( } // Expected: "{\"error\":{\"code\":400000,\"message\":\"One of the request inputs is not valid.\"}}" - // But for now just verify response is a valid json with an error + // But for now just verify response is a valid json Json::Value root; Json::Reader reader; -- cgit v1.2.3 From 1d1a63abe4a3d3a6191172c1693ffbdb0ffb2d71 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 19 May 2023 19:56:57 +0300 Subject: SL-19635 Implement DeepL tranlation support --- indra/newview/lltranslate.cpp | 311 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 299 insertions(+), 12 deletions(-) (limited to 'indra/newview/lltranslate.cpp') diff --git a/indra/newview/lltranslate.cpp b/indra/newview/lltranslate.cpp index 6589ce06c4..70a40921ef 100644 --- a/indra/newview/lltranslate.cpp +++ b/indra/newview/lltranslate.cpp @@ -133,7 +133,9 @@ public: LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers, const std::string & url, - const std::string & msg) const = 0; + const std::string & msg, + const std::string& from_lang, + const std::string& to_lang) const = 0; virtual LLSD verifyAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, LLCore::HttpRequest::ptr_t request, LLCore::HttpOptions::ptr_t options, @@ -230,7 +232,7 @@ void LLTranslationAPIHandler::translateMessageCoro(LanguagePair_t fromTo, std::s return; } - LLSD result = sendMessageAndSuspend(httpAdapter, httpRequest, httpOpts, httpHeaders, url, msg); + LLSD result = sendMessageAndSuspend(httpAdapter, httpRequest, httpOpts, httpHeaders, url, msg, fromTo.first, fromTo.second); if (LLApp::isQuitting()) { @@ -331,7 +333,9 @@ public: LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers, const std::string & url, - const std::string & msg) const override; + const std::string & msg, + const std::string& from_lang, + const std::string& to_lang) const override; LLSD verifyAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, LLCore::HttpRequest::ptr_t request, @@ -508,7 +512,9 @@ LLSD LLGoogleTranslationHandler::sendMessageAndSuspend(LLCoreHttpUtil::HttpCorou LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers, const std::string & url, - const std::string & msg) const + const std::string & msg, + const std::string& from_lang, + const std::string& to_lang) const { return adapter->getRawAndSuspend(request, url, options, headers); } @@ -558,7 +564,9 @@ public: LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers, const std::string & url, - const std::string & msg) const override; + const std::string & msg, + const std::string& from_lang, + const std::string& to_lang) const override; LLSD verifyAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, LLCore::HttpRequest::ptr_t request, @@ -728,7 +736,7 @@ bool LLAzureTranslationHandler::parseResponse( // virtual bool LLAzureTranslationHandler::isConfigured() const { - return !getAPIKey().isMap(); + return getAPIKey().isMap(); } //static @@ -814,7 +822,9 @@ LLSD LLAzureTranslationHandler::sendMessageAndSuspend(LLCoreHttpUtil::HttpCorout LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers, const std::string & url, - const std::string & msg) const + const std::string & msg, + const std::string& from_lang, + const std::string& to_lang) const { LLCore::BufferArray::ptr_t rawbody(new LLCore::BufferArray); LLCore::BufferArrayStream outs(rawbody.get()); @@ -838,6 +848,259 @@ LLSD LLAzureTranslationHandler::verifyAndSuspend(LLCoreHttpUtil::HttpCoroutineAd return adapter->postRawAndSuspend(request, url, rawbody, options, headers); } +//========================================================================= +/// DeepL Translator API handler. +class LLDeepLTranslationHandler: public LLTranslationAPIHandler +{ + LOG_CLASS(LLDeepLTranslationHandler); + +public: + std::string getTranslateURL( + const std::string& from_lang, + const std::string& to_lang, + const std::string& text) const override; + std::string getKeyVerificationURL( + const LLSD& key) const override; + bool checkVerificationResponse( + const LLSD& response, + int status) const override; + bool parseResponse( + const LLSD& http_response, + int& status, + const std::string& body, + std::string& translation, + std::string& detected_lang, + std::string& err_msg) const override; + bool isConfigured() const override; + + LLTranslate::EService getCurrentService() override + { + return LLTranslate::EService::SERVICE_DEEPL; + } + + void verifyKey(const LLSD& key, LLTranslate::KeyVerificationResult_fn fnc) override; + + void initHttpHeader(LLCore::HttpHeaders::ptr_t headers, const std::string& user_agent) const override; + void initHttpHeader(LLCore::HttpHeaders::ptr_t headers, const std::string& user_agent, const LLSD& key) const override; + LLSD sendMessageAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, + LLCore::HttpRequest::ptr_t request, + LLCore::HttpOptions::ptr_t options, + LLCore::HttpHeaders::ptr_t headers, + const std::string& url, + const std::string& msg, + const std::string& from_lang, + const std::string& to_lang) const override; + + LLSD verifyAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, + LLCore::HttpRequest::ptr_t request, + LLCore::HttpOptions::ptr_t options, + LLCore::HttpHeaders::ptr_t headers, + const std::string& url) const override; +private: + static std::string parseErrorResponse( + const std::string& body); + static LLSD getAPIKey(); + static std::string getAPILanguageCode(const std::string& lang); +}; + +//------------------------------------------------------------------------- +// virtual +std::string LLDeepLTranslationHandler::getTranslateURL( + const std::string& from_lang, + const std::string& to_lang, + const std::string& text) const +{ + std::string url; + LLSD key = getAPIKey(); + if (key.isMap()) + { + url = key["domain"].asString(); + + if (*url.rbegin() != '/') + { + url += "/"; + } + url += std::string("v2/translate"); + } + return url; +} + + +// virtual +std::string LLDeepLTranslationHandler::getKeyVerificationURL( + const LLSD& key) const +{ + std::string url; + if (key.isMap()) + { + url = key["domain"].asString(); + + if (*url.rbegin() != '/') + { + url += "/"; + } + url += std::string("v2/translate"); + } + return url; +} + +//virtual +bool LLDeepLTranslationHandler::checkVerificationResponse( + const LLSD& response, + int status) const +{ + return status == HTTP_OK; +} + +// virtual +bool LLDeepLTranslationHandler::parseResponse( + const LLSD& http_response, + int& status, + const std::string& body, + std::string& translation, + std::string& detected_lang, + std::string& err_msg) const +{ + if (status != HTTP_OK) + { + if (http_response.has("error_body")) + err_msg = parseErrorResponse(http_response["error_body"].asString()); + return false; + } + + //Example: + // "{\"translations\":[{\"detected_source_language\":\"EN\",\"text\":\"test\"}]}" + + Json::Value root; + Json::Reader reader; + + if (!reader.parse(body, root)) + { + err_msg = reader.getFormatedErrorMessages(); + return false; + } + + if (!root.isObject() + || !root.isMember("translations")) // empty response? should not happen + { + return false; + } + + // Request succeeded, extract translation from the response. + const Json::Value& translations = root["translations"]; + if (!translations.isArray() || translations.size() == 0) + { + return false; + } + + const Json::Value& data= translations[0U]; + if (!data.isObject() + || !data.isMember("detected_source_language") + || !data.isMember("text")) + { + return false; + } + + + detected_lang = data["detected_source_language"].asString(); + LLStringUtil::toLower(detected_lang); + translation = data["text"].asString(); + + return true; +} + +// virtual +bool LLDeepLTranslationHandler::isConfigured() const +{ + return getAPIKey().isMap(); +} + +//static +std::string LLDeepLTranslationHandler::parseErrorResponse( + const std::string& body) +{ + // DeepL doesn't seem to have any error handling beyoun http codes + return std::string(); +} + +// static +LLSD LLDeepLTranslationHandler::getAPIKey() +{ + static LLCachedControl deepl_key(gSavedSettings, "DeepLTranslateAPIKey"); + return deepl_key; +} + +// static +std::string LLDeepLTranslationHandler::getAPILanguageCode(const std::string& lang) +{ + return lang == "zh" ? "zh-CHT" : lang; // treat Chinese as Traditional Chinese +} + +/*virtual*/ +void LLDeepLTranslationHandler::verifyKey(const LLSD& key, LLTranslate::KeyVerificationResult_fn fnc) +{ + LLCoros::instance().launch("DeepL /Verify Key", boost::bind(&LLTranslationAPIHandler::verifyKeyCoro, + this, LLTranslate::SERVICE_DEEPL, key, fnc)); +} +/*virtual*/ +void LLDeepLTranslationHandler::initHttpHeader( + LLCore::HttpHeaders::ptr_t headers, + const std::string& user_agent) const +{ + initHttpHeader(headers, user_agent, getAPIKey()); +} + +/*virtual*/ +void LLDeepLTranslationHandler::initHttpHeader( + LLCore::HttpHeaders::ptr_t headers, + const std::string& user_agent, + const LLSD& key) const +{ + headers->append(HTTP_OUT_HEADER_CONTENT_TYPE, "application/x-www-form-urlencoded"); + headers->append(HTTP_OUT_HEADER_USER_AGENT, user_agent); + + if (key.has("id")) + { + std::string authkey = "DeepL-Auth-Key " + key["id"].asString(); + headers->append(HTTP_OUT_HEADER_AUTHORIZATION, authkey); + } +} + +LLSD LLDeepLTranslationHandler::sendMessageAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, + LLCore::HttpRequest::ptr_t request, + LLCore::HttpOptions::ptr_t options, + LLCore::HttpHeaders::ptr_t headers, + const std::string& url, + const std::string& msg, + const std::string& from_lang, + const std::string& to_lang) const +{ + LLCore::BufferArray::ptr_t rawbody(new LLCore::BufferArray); + LLCore::BufferArrayStream outs(rawbody.get()); + outs << "text="; + std::string escaped_string = LLURI::escape(msg); + outs << escaped_string; + outs << "&target_lang="; + std::string lang = to_lang; + LLStringUtil::toUpper(lang); + outs << lang; + + return adapter->postRawAndSuspend(request, url, rawbody, options, headers); +} + +LLSD LLDeepLTranslationHandler::verifyAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, + LLCore::HttpRequest::ptr_t request, + LLCore::HttpOptions::ptr_t options, + LLCore::HttpHeaders::ptr_t headers, + const std::string& url) const +{ + LLCore::BufferArray::ptr_t rawbody(new LLCore::BufferArray); + LLCore::BufferArrayStream outs(rawbody.get()); + outs << "text=&target_lang=EN"; + + return adapter->postRawAndSuspend(request, url, rawbody, options, headers); +} + //========================================================================= LLTranslate::LLTranslate(): mCharsSeen(0), @@ -867,6 +1130,11 @@ std::string LLTranslate::addNoTranslateTags(std::string mesg) return mesg; } + if (getPreferredHandler().getCurrentService() == SERVICE_DEEPL) + { + return mesg; + } + if (getPreferredHandler().getCurrentService() == SERVICE_AZURE) { // https://learn.microsoft.com/en-us/azure/cognitive-services/translator/prevent-translation @@ -892,6 +1160,10 @@ std::string LLTranslate::removeNoTranslateTags(std::string mesg) { return mesg; } + if (getPreferredHandler().getCurrentService() == SERVICE_DEEPL) + { + return mesg; + } if (getPreferredHandler().getCurrentService() == SERVICE_AZURE) { @@ -997,6 +1269,14 @@ LLTranslationAPIHandler& LLTranslate::getPreferredHandler() { service = SERVICE_GOOGLE; } + if (service_str == "azure") + { + service = SERVICE_AZURE; + } + if (service_str == "deepl") + { + service = SERVICE_DEEPL; + } return getHandler(service); } @@ -1006,11 +1286,18 @@ LLTranslationAPIHandler& LLTranslate::getHandler(EService service) { static LLGoogleTranslationHandler google; static LLAzureTranslationHandler azure; + static LLDeepLTranslationHandler deepl; - if (service == SERVICE_GOOGLE) - { - return google; - } + switch (service) + { + case SERVICE_AZURE: + return azure; + case SERVICE_GOOGLE: + return google; + case SERVICE_DEEPL: + return deepl; + } + + return azure; - return azure; } -- cgit v1.2.3 From 6f7cba0f9111c85d32d9ec4e9f52be5e7fbaaf9e Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 23 May 2023 23:06:41 +0300 Subject: SL-19635 Better error handling --- indra/newview/lltranslate.cpp | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'indra/newview/lltranslate.cpp') diff --git a/indra/newview/lltranslate.cpp b/indra/newview/lltranslate.cpp index 70a40921ef..c37c955e8d 100644 --- a/indra/newview/lltranslate.cpp +++ b/indra/newview/lltranslate.cpp @@ -949,6 +949,8 @@ bool LLDeepLTranslationHandler::checkVerificationResponse( const LLSD& response, int status) const { + // Might need to parse body to make sure we got + // a valid response and not a message return status == HTTP_OK; } @@ -1000,7 +1002,6 @@ bool LLDeepLTranslationHandler::parseResponse( { return false; } - detected_lang = data["detected_source_language"].asString(); LLStringUtil::toLower(detected_lang); @@ -1019,8 +1020,22 @@ bool LLDeepLTranslationHandler::isConfigured() const std::string LLDeepLTranslationHandler::parseErrorResponse( const std::string& body) { - // DeepL doesn't seem to have any error handling beyoun http codes - return std::string(); + // Example: "{\"message\":\"One of the request inputs is not valid.\"}" + + Json::Value root; + Json::Reader reader; + + if (!reader.parse(body, root)) + { + return std::string(); + } + + if (!root.isObject() || !root.isMember("message")) + { + return std::string(); + } + + return root["message"].asString(); } // static -- cgit v1.2.3