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/app_settings/settings.xml | 17 +- indra/newview/llfloatertranslationsettings.cpp | 120 +++++--- indra/newview/llfloatertranslationsettings.h | 18 +- indra/newview/lltranslate.cpp | 317 +++++++++++++++------ indra/newview/lltranslate.h | 14 +- .../xui/en/floater_translation_settings.xml | 95 ++++-- 6 files changed, 420 insertions(+), 161 deletions(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 411f77e6a7..ddd4f57f3f 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -12929,13 +12929,13 @@ TranslationService Comment - Translation API to use. (google|bing) + Translation API to use. (google|azure) Persist 1 Type String Value - bing + azure GoogleTranslateAPIKey @@ -12951,7 +12951,7 @@ BingTranslateAPIKey Comment - Bing AppID to use with the Microsoft Translator API + (Deprecated) Bing AppID to use with the Microsoft Translator API Persist 1 Type @@ -12959,6 +12959,17 @@ Value + AzureTranslateAPIKey + + Comment + Azure Translation service data to use with the MS Azure Translator API + Persist + 1 + Type + LLSD + Value + + TutorialURL Comment diff --git a/indra/newview/llfloatertranslationsettings.cpp b/indra/newview/llfloatertranslationsettings.cpp index 082bb888b1..45f46aacf5 100644 --- a/indra/newview/llfloatertranslationsettings.cpp +++ b/indra/newview/llfloatertranslationsettings.cpp @@ -45,14 +45,7 @@ LLFloaterTranslationSettings::LLFloaterTranslationSettings(const LLSD& key) : LLFloater(key) , mMachineTranslationCB(NULL) -, mLanguageCombo(NULL) -, mTranslationServiceRadioGroup(NULL) -, mBingAPIKeyEditor(NULL) -, mGoogleAPIKeyEditor(NULL) -, mBingVerifyBtn(NULL) -, mGoogleVerifyBtn(NULL) -, mOKBtn(NULL) -, mBingKeyVerified(false) +, mAzureKeyVerified(false) , mGoogleKeyVerified(false) { } @@ -63,9 +56,11 @@ BOOL LLFloaterTranslationSettings::postBuild() mMachineTranslationCB = getChild("translate_chat_checkbox"); mLanguageCombo = getChild("translate_language_combo"); mTranslationServiceRadioGroup = getChild("translation_service_rg"); - mBingAPIKeyEditor = getChild("bing_api_key"); + mAzureAPIEndpointEditor = getChild("azure_api_endpoint_combo"); + mAzureAPIKeyEditor = getChild("azure_api_key"); + mAzureAPIRegionEditor = getChild("azure_api_region"); mGoogleAPIKeyEditor = getChild("google_api_key"); - mBingVerifyBtn = getChild("verify_bing_api_key_btn"); + mAzureVerifyBtn = getChild("verify_azure_api_key_btn"); mGoogleVerifyBtn = getChild("verify_google_api_key_btn"); mOKBtn = getChild("ok_btn"); @@ -73,11 +68,17 @@ BOOL LLFloaterTranslationSettings::postBuild() mTranslationServiceRadioGroup->setCommitCallback(boost::bind(&LLFloaterTranslationSettings::updateControlsEnabledState, this)); mOKBtn->setClickedCallback(boost::bind(&LLFloaterTranslationSettings::onBtnOK, this)); getChild("cancel_btn")->setClickedCallback(boost::bind(&LLFloater::closeFloater, this, false)); - mBingVerifyBtn->setClickedCallback(boost::bind(&LLFloaterTranslationSettings::onBtnBingVerify, this)); + mAzureVerifyBtn->setClickedCallback(boost::bind(&LLFloaterTranslationSettings::onBtnAzureVerify, this)); mGoogleVerifyBtn->setClickedCallback(boost::bind(&LLFloaterTranslationSettings::onBtnGoogleVerify, this)); - mBingAPIKeyEditor->setFocusReceivedCallback(boost::bind(&LLFloaterTranslationSettings::onEditorFocused, this, _1)); - mBingAPIKeyEditor->setKeystrokeCallback(boost::bind(&LLFloaterTranslationSettings::onBingKeyEdited, this), NULL); + mAzureAPIKeyEditor->setFocusReceivedCallback(boost::bind(&LLFloaterTranslationSettings::onEditorFocused, this, _1)); + mAzureAPIKeyEditor->setKeystrokeCallback(boost::bind(&LLFloaterTranslationSettings::onAzureKeyEdited, this), NULL); + mAzureAPIRegionEditor->setFocusReceivedCallback(boost::bind(&LLFloaterTranslationSettings::onEditorFocused, this, _1)); + mAzureAPIRegionEditor->setKeystrokeCallback(boost::bind(&LLFloaterTranslationSettings::onAzureKeyEdited, this), NULL); + + mAzureAPIEndpointEditor->setFocusLostCallback(boost::bind(&LLFloaterTranslationSettings::onAzureKeyEdited, this)); + mAzureAPIEndpointEditor->setCommitCallback(boost::bind(&LLFloaterTranslationSettings::onAzureKeyEdited, this)); + mGoogleAPIKeyEditor->setFocusReceivedCallback(boost::bind(&LLFloaterTranslationSettings::onEditorFocused, this, _1)); mGoogleAPIKeyEditor->setKeystrokeCallback(boost::bind(&LLFloaterTranslationSettings::onGoogleKeyEdited, this), NULL); @@ -92,17 +93,27 @@ void LLFloaterTranslationSettings::onOpen(const LLSD& key) mLanguageCombo->setSelectedByValue(gSavedSettings.getString("TranslateLanguage"), TRUE); mTranslationServiceRadioGroup->setSelectedByValue(gSavedSettings.getString("TranslationService"), TRUE); - std::string bing_key = gSavedSettings.getString("BingTranslateAPIKey"); - if (!bing_key.empty()) + LLSD azure_key = gSavedSettings.getLLSD("AzureTranslateAPIKey"); + if (azure_key.isMap()) { - mBingAPIKeyEditor->setText(bing_key); - mBingAPIKeyEditor->setTentative(FALSE); - verifyKey(LLTranslate::SERVICE_BING, bing_key, false); + mAzureAPIKeyEditor->setText(azure_key["id"].asString()); + mAzureAPIKeyEditor->setTentative(false); + if (azure_key.has("region")) + { + mAzureAPIRegionEditor->setText(azure_key["region"].asString()); + mAzureAPIRegionEditor->setTentative(false); + } + else + { + mAzureAPIRegionEditor->setTentative(true); + } + mAzureAPIEndpointEditor->setValue(azure_key["endpoint"]); + verifyKey(LLTranslate::SERVICE_AZURE, azure_key, false); } else { - mBingAPIKeyEditor->setTentative(TRUE); - mBingKeyVerified = FALSE; + mAzureAPIKeyEditor->setTentative(TRUE); + mAzureKeyVerified = FALSE; } std::string google_key = gSavedSettings.getString("GoogleTranslateAPIKey"); @@ -121,14 +132,14 @@ void LLFloaterTranslationSettings::onOpen(const LLSD& key) updateControlsEnabledState(); } -void LLFloaterTranslationSettings::setBingVerified(bool ok, bool alert) +void LLFloaterTranslationSettings::setAzureVerified(bool ok, bool alert) { if (alert) { - showAlert(ok ? "bing_api_key_verified" : "bing_api_key_not_verified"); + showAlert(ok ? "azure_api_key_verified" : "azure_api_key_not_verified"); } - mBingKeyVerified = ok; + mAzureKeyVerified = ok; updateControlsEnabledState(); } @@ -148,9 +159,19 @@ std::string LLFloaterTranslationSettings::getSelectedService() const return mTranslationServiceRadioGroup->getSelectedValue().asString(); } -std::string LLFloaterTranslationSettings::getEnteredBingKey() const +LLSD LLFloaterTranslationSettings::getEnteredAzureKey() const { - return mBingAPIKeyEditor->getTentative() ? LLStringUtil::null : mBingAPIKeyEditor->getText(); + LLSD key; + if (!mAzureAPIKeyEditor->getTentative()) + { + key["endpoint"] = mAzureAPIEndpointEditor->getValue(); + key["id"] = mAzureAPIKeyEditor->getText(); + if (!mAzureAPIRegionEditor->getTentative()) + { + key["region"] = mAzureAPIRegionEditor->getText(); + } + } + return key; } std::string LLFloaterTranslationSettings::getEnteredGoogleKey() const @@ -170,27 +191,31 @@ void LLFloaterTranslationSettings::updateControlsEnabledState() // Enable/disable controls based on the checkbox value. bool on = mMachineTranslationCB->getValue().asBoolean(); std::string service = getSelectedService(); - bool bing_selected = service == "bing"; + bool azure_selected = service == "azure"; bool google_selected = service == "google"; mTranslationServiceRadioGroup->setEnabled(on); mLanguageCombo->setEnabled(on); - getChild("bing_api_key_label")->setEnabled(on); - mBingAPIKeyEditor->setEnabled(on); + getChild("azure_api_endoint_label")->setEnabled(on); + mAzureAPIEndpointEditor->setEnabled(on); + getChild("azure_api_key_label")->setEnabled(on); + mAzureAPIKeyEditor->setEnabled(on); + getChild("azure_api_region_label")->setEnabled(on); + mAzureAPIRegionEditor->setEnabled(on); getChild("google_api_key_label")->setEnabled(on); mGoogleAPIKeyEditor->setEnabled(on); - mBingAPIKeyEditor->setEnabled(on && bing_selected); + mAzureAPIKeyEditor->setEnabled(on && azure_selected); mGoogleAPIKeyEditor->setEnabled(on && google_selected); - mBingVerifyBtn->setEnabled(on && bing_selected && - !mBingKeyVerified && !getEnteredBingKey().empty()); + mAzureVerifyBtn->setEnabled(on && azure_selected && + !mAzureKeyVerified && getEnteredAzureKey().isMap()); mGoogleVerifyBtn->setEnabled(on && google_selected && !mGoogleKeyVerified && !getEnteredGoogleKey().empty()); - bool service_verified = (bing_selected && mBingKeyVerified) || (google_selected && mGoogleKeyVerified); + bool service_verified = (azure_selected && mAzureKeyVerified) || (google_selected && mGoogleKeyVerified); gSavedPerAccountSettings.setBOOL("TranslatingEnabled", service_verified); mOKBtn->setEnabled(!on || service_verified); @@ -210,8 +235,8 @@ void LLFloaterTranslationSettings::setVerificationStatus(int service, bool ok, b switch (service) { - case LLTranslate::SERVICE_BING: - floater->setBingVerified(ok, alert); + case LLTranslate::SERVICE_AZURE: + floater->setAzureVerified(ok, alert); break; case LLTranslate::SERVICE_GOOGLE: floater->setGoogleVerified(ok, alert); @@ -220,7 +245,7 @@ void LLFloaterTranslationSettings::setVerificationStatus(int service, bool ok, b } -void LLFloaterTranslationSettings::verifyKey(int service, const std::string& key, bool alert) +void LLFloaterTranslationSettings::verifyKey(int service, const LLSD& key, bool alert) { LLTranslate::verifyKey(static_cast(service), key, boost::bind(&LLFloaterTranslationSettings::setVerificationStatus, _1, _2, alert)); @@ -239,11 +264,14 @@ void LLFloaterTranslationSettings::onEditorFocused(LLFocusableElement* control) } } -void LLFloaterTranslationSettings::onBingKeyEdited() +void LLFloaterTranslationSettings::onAzureKeyEdited() { - if (mBingAPIKeyEditor->isDirty()) + if (mAzureAPIKeyEditor->isDirty() + || mAzureAPIRegionEditor->isDirty() + || mAzureAPIEndpointEditor->getValue().isString()) { - setBingVerified(false, false); + // todo: verify mAzureAPIEndpointEditor url + setAzureVerified(false, false); } } @@ -255,12 +283,12 @@ void LLFloaterTranslationSettings::onGoogleKeyEdited() } } -void LLFloaterTranslationSettings::onBtnBingVerify() +void LLFloaterTranslationSettings::onBtnAzureVerify() { - std::string key = getEnteredBingKey(); - if (!key.empty()) + LLSD key = getEnteredAzureKey(); + if (key.isMap()) { - verifyKey(LLTranslate::SERVICE_BING, key); + verifyKey(LLTranslate::SERVICE_AZURE, key); } } @@ -269,16 +297,16 @@ void LLFloaterTranslationSettings::onBtnGoogleVerify() std::string key = getEnteredGoogleKey(); if (!key.empty()) { - verifyKey(LLTranslate::SERVICE_GOOGLE, key); + verifyKey(LLTranslate::SERVICE_GOOGLE, LLSD(key)); } } void LLFloaterTranslationSettings::onClose(bool app_quitting) { std::string service = gSavedSettings.getString("TranslationService"); - bool bing_selected = service == "bing"; + bool azure_selected = service == "azure"; bool google_selected = service == "google"; - bool service_verified = (bing_selected && mBingKeyVerified) || (google_selected && mGoogleKeyVerified); + bool service_verified = (azure_selected && mAzureKeyVerified) || (google_selected && mGoogleKeyVerified); gSavedPerAccountSettings.setBOOL("TranslatingEnabled", service_verified); } @@ -287,7 +315,7 @@ void LLFloaterTranslationSettings::onBtnOK() gSavedSettings.setBOOL("TranslateChat", mMachineTranslationCB->getValue().asBoolean()); gSavedSettings.setString("TranslateLanguage", mLanguageCombo->getSelectedValue().asString()); gSavedSettings.setString("TranslationService", getSelectedService()); - gSavedSettings.setString("BingTranslateAPIKey", getEnteredBingKey()); + gSavedSettings.setLLSD("AzureTranslateAPIKey", getEnteredAzureKey()); gSavedSettings.setString("GoogleTranslateAPIKey", getEnteredGoogleKey()); closeFloater(false); diff --git a/indra/newview/llfloatertranslationsettings.h b/indra/newview/llfloatertranslationsettings.h index 2a15eacded..52523f4d4a 100644 --- a/indra/newview/llfloatertranslationsettings.h +++ b/indra/newview/llfloatertranslationsettings.h @@ -42,22 +42,22 @@ public: /*virtual*/ BOOL postBuild(); /*virtual*/ void onOpen(const LLSD& key); - void setBingVerified(bool ok, bool alert); + void setAzureVerified(bool ok, bool alert); void setGoogleVerified(bool ok, bool alert); void onClose(bool app_quitting); private: std::string getSelectedService() const; - std::string getEnteredBingKey() const; + LLSD getEnteredAzureKey() const; std::string getEnteredGoogleKey() const; void showAlert(const std::string& msg_name) const; void updateControlsEnabledState(); - void verifyKey(int service, const std::string& key, bool alert = true); + void verifyKey(int service, const LLSD& key, bool alert = true); void onEditorFocused(LLFocusableElement* control); - void onBingKeyEdited(); + void onAzureKeyEdited(); void onGoogleKeyEdited(); - void onBtnBingVerify(); + void onBtnAzureVerify(); void onBtnGoogleVerify(); void onBtnOK(); @@ -65,14 +65,16 @@ private: LLCheckBoxCtrl* mMachineTranslationCB; LLComboBox* mLanguageCombo; - LLLineEditor* mBingAPIKeyEditor; + LLComboBox* mAzureAPIEndpointEditor;; + LLLineEditor* mAzureAPIKeyEditor; + LLLineEditor* mAzureAPIRegionEditor; LLLineEditor* mGoogleAPIKeyEditor; LLRadioGroup* mTranslationServiceRadioGroup; - LLButton* mBingVerifyBtn; + LLButton* mAzureVerifyBtn; LLButton* mGoogleVerifyBtn; LLButton* mOKBtn; - bool mBingKeyVerified; + bool mAzureKeyVerified; bool mGoogleKeyVerified; }; 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; } diff --git a/indra/newview/lltranslate.h b/indra/newview/lltranslate.h index e0722fbd83..870fd54441 100644 --- a/indra/newview/lltranslate.h +++ b/indra/newview/lltranslate.h @@ -55,7 +55,7 @@ class LLTranslate public : typedef enum e_service { - SERVICE_BING, + SERVICE_AZURE, SERVICE_GOOGLE, } EService; @@ -74,12 +74,12 @@ public : static void translateMessage(const std::string &from_lang, const std::string &to_lang, const std::string &mesg, TranslationSuccess_fn success, TranslationFailure_fn failure); /** - * Verify given API key of a translation service. - * - * @param receiver Object to pass verification result to. - * @param key Key to verify. - */ - static void verifyKey(EService service, const std::string &key, KeyVerificationResult_fn fnc); + * Verify given API key of a translation service. + * + * @param receiver Object to pass verification result to. + * @param key Key to verify. + */ + static void verifyKey(EService service, const LLSD &key, KeyVerificationResult_fn fnc); /** * @return translation target language diff --git a/indra/newview/skins/default/xui/en/floater_translation_settings.xml b/indra/newview/skins/default/xui/en/floater_translation_settings.xml index a212ce7889..b0e47798e9 100644 --- a/indra/newview/skins/default/xui/en/floater_translation_settings.xml +++ b/indra/newview/skins/default/xui/en/floater_translation_settings.xml @@ -1,7 +1,7 @@ - Bing appID not verified. Please try again. + Azure service identifier not verified. Please try again. Google API key not verified. Please try again. - Bing appID verified. + Azure service identifier verified. Google API key verified. + name="azure" /> + top_pad="115" /> + + Endpoint: + + + + + + + + + - Bing [http://www.bing.com/developers/createapp.aspx AppID]: + Azure Key: