diff options
| author | Andrey Kleshchev <andreykproductengine@lindenlab.com> | 2023-05-19 19:56:57 +0300 | 
|---|---|---|
| committer | akleshchev <117672381+akleshchev@users.noreply.github.com> | 2023-05-22 20:25:27 +0300 | 
| commit | 1d1a63abe4a3d3a6191172c1693ffbdb0ffb2d71 (patch) | |
| tree | d4c5ee7583210f4cef1d95115e961c17c5f58146 /indra | |
| parent | 7140640b6963dacfa012dfec679798fc4dd13a17 (diff) | |
SL-19635 Implement DeepL tranlation support
Diffstat (limited to 'indra')
| -rw-r--r-- | indra/newview/app_settings/settings.xml | 11 | ||||
| -rw-r--r-- | indra/newview/llfloatertranslationsettings.cpp | 132 | ||||
| -rw-r--r-- | indra/newview/llfloatertranslationsettings.h | 10 | ||||
| -rw-r--r-- | indra/newview/lltranslate.cpp | 311 | ||||
| -rw-r--r-- | indra/newview/lltranslate.h | 1 | ||||
| -rw-r--r-- | indra/newview/skins/default/xui/en/floater_translation_settings.xml | 97 | 
6 files changed, 527 insertions, 35 deletions
| diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index fa241dc30c..ca1b1e2f20 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -13014,6 +13014,17 @@        <key>Value</key>        <string></string>      </map> +    <key>DeepLTranslateAPIKey</key> +    <map> +        <key>Comment</key> +        <string>DeepL Translation service data to use with the DeepL Translator API</string> +        <key>Persist</key> +        <integer>1</integer> +        <key>Type</key> +        <string>LLSD</string> +        <key>Value</key> +        <string></string> +    </map>      <key>TutorialURL</key>      <map>        <key>Comment</key> diff --git a/indra/newview/llfloatertranslationsettings.cpp b/indra/newview/llfloatertranslationsettings.cpp index e14227f490..d29ecbbf95 100644 --- a/indra/newview/llfloatertranslationsettings.cpp +++ b/indra/newview/llfloatertranslationsettings.cpp @@ -47,6 +47,7 @@ LLFloaterTranslationSettings::LLFloaterTranslationSettings(const LLSD& key)  ,	mMachineTranslationCB(NULL)  ,	mAzureKeyVerified(false)  ,	mGoogleKeyVerified(false) +,	mDeepLKeyVerified(false)  {  } @@ -60,8 +61,11 @@ BOOL LLFloaterTranslationSettings::postBuild()  	mAzureAPIKeyEditor = getChild<LLLineEditor>("azure_api_key");      mAzureAPIRegionEditor = getChild<LLLineEditor>("azure_api_region");  	mGoogleAPIKeyEditor = getChild<LLLineEditor>("google_api_key"); +    mDeepLAPIDomainCombo = getChild<LLComboBox>("deepl_api_domain_combo"); +    mDeepLAPIKeyEditor = getChild<LLLineEditor>("deepl_api_key");  	mAzureVerifyBtn = getChild<LLButton>("verify_azure_api_key_btn");  	mGoogleVerifyBtn = getChild<LLButton>("verify_google_api_key_btn"); +    mDeepLVerifyBtn = getChild<LLButton>("verify_deepl_api_key_btn");  	mOKBtn = getChild<LLButton>("ok_btn");  	mMachineTranslationCB->setCommitCallback(boost::bind(&LLFloaterTranslationSettings::updateControlsEnabledState, this)); @@ -70,18 +74,37 @@ BOOL LLFloaterTranslationSettings::postBuild()  	getChild<LLButton>("cancel_btn")->setClickedCallback(boost::bind(&LLFloater::closeFloater, this, false));  	mAzureVerifyBtn->setClickedCallback(boost::bind(&LLFloaterTranslationSettings::onBtnAzureVerify, this));  	mGoogleVerifyBtn->setClickedCallback(boost::bind(&LLFloaterTranslationSettings::onBtnGoogleVerify, this)); +    mDeepLVerifyBtn->setClickedCallback(boost::bind(&LLFloaterTranslationSettings::onBtnDeepLVerify, this));  	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)); +    mAzureAPIEndpointEditor->setFocusLostCallback([this](LLFocusableElement*) +                                                  { +                                                      setAzureVerified(false, false, 0); +                                                  }); +    mAzureAPIEndpointEditor->setCommitCallback([this](LLUICtrl* ctrl, const LLSD& param) +                                               { +                                                   setAzureVerified(false, false, 0); +                                               });  	mGoogleAPIKeyEditor->setFocusReceivedCallback(boost::bind(&LLFloaterTranslationSettings::onEditorFocused, this, _1));  	mGoogleAPIKeyEditor->setKeystrokeCallback(boost::bind(&LLFloaterTranslationSettings::onGoogleKeyEdited, this), NULL); +    mDeepLAPIKeyEditor->setFocusReceivedCallback(boost::bind(&LLFloaterTranslationSettings::onEditorFocused, this, _1)); +    mDeepLAPIKeyEditor->setKeystrokeCallback(boost::bind(&LLFloaterTranslationSettings::onDeepLKeyEdited, this), NULL); + +    mDeepLAPIDomainCombo->setFocusLostCallback([this](LLFocusableElement*) +                                                  { +                                                      setDeepLVerified(false, false, 0); +                                                  }); +    mDeepLAPIDomainCombo->setCommitCallback([this](LLUICtrl* ctrl, const LLSD& param) +                                               { +                                                setDeepLVerified(false, false, 0); +                                               }); +  	center();  	return TRUE;  } @@ -130,6 +153,20 @@ void LLFloaterTranslationSettings::onOpen(const LLSD& key)  		mGoogleKeyVerified = FALSE;  	} +    LLSD deepl_key = gSavedSettings.getLLSD("DeepLTranslateAPIKey"); +    if (deepl_key.isMap() && !deepl_key["id"].asString().empty()) +    { +        mDeepLAPIKeyEditor->setText(deepl_key["id"].asString()); +        mDeepLAPIKeyEditor->setTentative(false); +        mDeepLAPIDomainCombo->setValue(deepl_key["domain"]); +        verifyKey(LLTranslate::SERVICE_DEEPL, deepl_key, false); +    } +    else +    { +        mDeepLAPIKeyEditor->setTentative(TRUE); +        mDeepLKeyVerified = FALSE; +    } +  	updateControlsEnabledState();  } @@ -155,6 +192,17 @@ void LLFloaterTranslationSettings::setGoogleVerified(bool ok, bool alert, S32 st  	updateControlsEnabledState();  } +void LLFloaterTranslationSettings::setDeepLVerified(bool ok, bool alert, S32 status) +{ +    if (alert) +    { +        showAlert(ok ? "deepl_api_key_verified" : "deepl_api_key_not_verified", status); +    } + +    mDeepLKeyVerified = ok; +    updateControlsEnabledState(); +} +  std::string LLFloaterTranslationSettings::getSelectedService() const  {  	return mTranslationServiceRadioGroup->getSelectedValue().asString(); @@ -180,6 +228,17 @@ std::string LLFloaterTranslationSettings::getEnteredGoogleKey() const  	return mGoogleAPIKeyEditor->getTentative() ? LLStringUtil::null : mGoogleAPIKeyEditor->getText();  } +LLSD LLFloaterTranslationSettings::getEnteredDeepLKey() const +{ +    LLSD key; +    if (!mDeepLAPIKeyEditor->getTentative()) +    { +        key["domain"] = mDeepLAPIDomainCombo->getValue(); +        key["id"] = mDeepLAPIKeyEditor->getText(); +    } +    return key; +} +  void LLFloaterTranslationSettings::showAlert(const std::string& msg_name, S32 status) const  {      LLStringUtil::format_map_t string_args; @@ -199,29 +258,42 @@ void LLFloaterTranslationSettings::updateControlsEnabledState()  	std::string service = getSelectedService();  	bool azure_selected = service == "azure";  	bool google_selected = service == "google"; +    bool deepl_selected = service == "deepl";  	mTranslationServiceRadioGroup->setEnabled(on);  	mLanguageCombo->setEnabled(on); -	getChild<LLTextBox>("azure_api_endoint_label")->setEnabled(on); -	mAzureAPIEndpointEditor->setEnabled(on); +    // MS Azure +    getChild<LLTextBox>("azure_api_endoint_label")->setEnabled(on); +    mAzureAPIEndpointEditor->setEnabled(on && azure_selected);      getChild<LLTextBox>("azure_api_key_label")->setEnabled(on); -    mAzureAPIKeyEditor->setEnabled(on); +    mAzureAPIKeyEditor->setEnabled(on && azure_selected);      getChild<LLTextBox>("azure_api_region_label")->setEnabled(on); -    mAzureAPIRegionEditor->setEnabled(on); +    mAzureAPIRegionEditor->setEnabled(on && azure_selected); -	getChild<LLTextBox>("google_api_key_label")->setEnabled(on); -	mGoogleAPIKeyEditor->setEnabled(on); +    mAzureVerifyBtn->setEnabled(on && azure_selected && +                                !mAzureKeyVerified && getEnteredAzureKey().isMap()); -	mAzureAPIKeyEditor->setEnabled(on && azure_selected); -	mGoogleAPIKeyEditor->setEnabled(on && google_selected); +    // Google +    getChild<LLTextBox>("google_api_key_label")->setEnabled(on); +    mGoogleAPIKeyEditor->setEnabled(on && google_selected); -	mAzureVerifyBtn->setEnabled(on && azure_selected && -		!mAzureKeyVerified && getEnteredAzureKey().isMap());  	mGoogleVerifyBtn->setEnabled(on && google_selected &&  		!mGoogleKeyVerified && !getEnteredGoogleKey().empty()); -	bool service_verified = (azure_selected && mAzureKeyVerified) || (google_selected && mGoogleKeyVerified); +    // DeepL +    getChild<LLTextBox>("deepl_api_domain_label")->setEnabled(on); +    mDeepLAPIDomainCombo->setEnabled(on && deepl_selected); +    getChild<LLTextBox>("deepl_api_key_label")->setEnabled(on); +    mDeepLAPIKeyEditor->setEnabled(on && deepl_selected); + +    mDeepLVerifyBtn->setEnabled(on && deepl_selected && +                                 !mDeepLKeyVerified && getEnteredDeepLKey().isMap()); + +    bool service_verified = +        (azure_selected && mAzureKeyVerified)  +        || (google_selected && mGoogleKeyVerified) +        || (deepl_selected && mDeepLKeyVerified);  	gSavedPerAccountSettings.setBOOL("TranslatingEnabled", service_verified);  	mOKBtn->setEnabled(!on || service_verified); @@ -247,6 +319,9 @@ void LLFloaterTranslationSettings::setVerificationStatus(int service, bool ok, b      case LLTranslate::SERVICE_GOOGLE:          floater->setGoogleVerified(ok, alert, status);          break; +    case LLTranslate::SERVICE_DEEPL: +        floater->setDeepLVerified(ok, alert, status); +        break;      }  } @@ -273,8 +348,7 @@ void LLFloaterTranslationSettings::onEditorFocused(LLFocusableElement* control)  void LLFloaterTranslationSettings::onAzureKeyEdited()  {  	if (mAzureAPIKeyEditor->isDirty() -        || mAzureAPIRegionEditor->isDirty() -        || mAzureAPIEndpointEditor->getValue().isString()) +        || mAzureAPIRegionEditor->isDirty())  	{          // todo: verify mAzureAPIEndpointEditor url  		setAzureVerified(false, false, 0); @@ -289,6 +363,14 @@ void LLFloaterTranslationSettings::onGoogleKeyEdited()  	}  } +void LLFloaterTranslationSettings::onDeepLKeyEdited() +{ +    if (mDeepLAPIKeyEditor->isDirty()) +    { +        setDeepLVerified(false, false, 0); +    } +} +  void LLFloaterTranslationSettings::onBtnAzureVerify()  {  	LLSD key = getEnteredAzureKey(); @@ -306,15 +388,28 @@ void LLFloaterTranslationSettings::onBtnGoogleVerify()  		verifyKey(LLTranslate::SERVICE_GOOGLE, LLSD(key));  	}  } + +void LLFloaterTranslationSettings::onBtnDeepLVerify() +{ +    LLSD key = getEnteredDeepLKey(); +    if (key.isMap()) +    { +        verifyKey(LLTranslate::SERVICE_DEEPL, key); +    } +} +  void LLFloaterTranslationSettings::onClose(bool app_quitting)  {  	std::string service = gSavedSettings.getString("TranslationService");  	bool azure_selected = service == "azure";  	bool google_selected = service == "google"; +    bool deepl_selected = service == "deepl"; -	bool service_verified = (azure_selected && mAzureKeyVerified) || (google_selected && mGoogleKeyVerified); -	gSavedPerAccountSettings.setBOOL("TranslatingEnabled", service_verified); - +    bool service_verified = +        (azure_selected && mAzureKeyVerified) +        || (google_selected && mGoogleKeyVerified) +        || (deepl_selected && mDeepLKeyVerified); +    gSavedPerAccountSettings.setBOOL("TranslatingEnabled", service_verified);  }  void LLFloaterTranslationSettings::onBtnOK()  { @@ -323,6 +418,7 @@ void LLFloaterTranslationSettings::onBtnOK()  	gSavedSettings.setString("TranslationService", getSelectedService());  	gSavedSettings.setLLSD("AzureTranslateAPIKey", getEnteredAzureKey());  	gSavedSettings.setString("GoogleTranslateAPIKey", getEnteredGoogleKey()); +    gSavedSettings.setLLSD("DeepLTranslateAPIKey", getEnteredDeepLKey());  	closeFloater(false);  } diff --git a/indra/newview/llfloatertranslationsettings.h b/indra/newview/llfloatertranslationsettings.h index f039d90e27..eff0803fdd 100644 --- a/indra/newview/llfloatertranslationsettings.h +++ b/indra/newview/llfloatertranslationsettings.h @@ -44,12 +44,14 @@ public:  	void setAzureVerified(bool ok, bool alert, S32 status);  	void setGoogleVerified(bool ok, bool alert, S32 status); +    void setDeepLVerified(bool ok, bool alert, S32 status);  	void onClose(bool app_quitting);  private:  	std::string getSelectedService() const;  	LLSD getEnteredAzureKey() const;  	std::string getEnteredGoogleKey() const; +    LLSD getEnteredDeepLKey() const;  	void showAlert(const std::string& msg_name, S32 status) const;  	void updateControlsEnabledState();      void verifyKey(int service, const LLSD& key, bool alert = true); @@ -57,25 +59,31 @@ private:  	void onEditorFocused(LLFocusableElement* control);  	void onAzureKeyEdited();  	void onGoogleKeyEdited(); +    void onDeepLKeyEdited();  	void onBtnAzureVerify();  	void onBtnGoogleVerify(); +    void onBtnDeepLVerify();  	void onBtnOK();      static void setVerificationStatus(int service, bool alert, bool ok, S32 status);  	LLCheckBoxCtrl* mMachineTranslationCB;  	LLComboBox* mLanguageCombo; -    LLComboBox* mAzureAPIEndpointEditor;; +    LLComboBox* mAzureAPIEndpointEditor;  	LLLineEditor* mAzureAPIKeyEditor;      LLLineEditor* mAzureAPIRegionEditor;  	LLLineEditor* mGoogleAPIKeyEditor; +    LLComboBox* mDeepLAPIDomainCombo; +    LLLineEditor* mDeepLAPIKeyEditor;  	LLRadioGroup* mTranslationServiceRadioGroup;  	LLButton* mAzureVerifyBtn;  	LLButton* mGoogleVerifyBtn; +    LLButton* mDeepLVerifyBtn;  	LLButton* mOKBtn;  	bool mAzureKeyVerified;  	bool mGoogleKeyVerified; +    bool mDeepLKeyVerified;  };  #endif // LL_LLFLOATERTRANSLATIONSETTINGS_H 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()); @@ -839,6 +849,259 @@ LLSD LLAzureTranslationHandler::verifyAndSuspend(LLCoreHttpUtil::HttpCoroutineAd  }  //========================================================================= +/// 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<LLSD> 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),  	mCharsSent(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;  } diff --git a/indra/newview/lltranslate.h b/indra/newview/lltranslate.h index ffbbb05e62..4a5d80737c 100644 --- a/indra/newview/lltranslate.h +++ b/indra/newview/lltranslate.h @@ -61,6 +61,7 @@ public :  	typedef enum e_service {  		SERVICE_AZURE,  		SERVICE_GOOGLE, +		SERVICE_DEEPL,  	} EService;      typedef boost::function<void(EService, bool, S32)> KeyVerificationResult_fn; 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 dc3e072adf..8a97d5e5d9 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 @@  <?xml version="1.0" encoding="utf-8" standalone="yes" ?>  <floater   legacy_header_height="18" - height="370" + height="470"   layout="topleft"   name="floater_translation_settings"   help_topic="translation_settings" @@ -11,9 +11,11 @@   <string name="azure_api_key_not_verified">Azure service identifier not verified. Status: [STATUS]. Please check your settings and try again.</string>   <string name="google_api_key_not_verified">Google API key not verified. Status: [STATUS]. Please check your key and try again.</string> + <string name="deepl_api_key_not_verified">DeepL Auth Key key not verified. Status: [STATUS]. Please check your key and try again.</string>   <string name="azure_api_key_verified">Azure service identifier verified.</string>   <string name="google_api_key_verified">Google API key verified.</string> + <string name="deepl_api_key_verified">DeepL API key verified.</string>   <check_box    height="16"      @@ -128,7 +130,7 @@   <radio_group    follows="top|left" -  height="140" +  height="260"    layout="topleft"    left_delta="10"    name="translation_service_rg" @@ -145,6 +147,12 @@     layout="topleft"     name="google"     top_pad="115" /> +  <radio_item +   initial_value="deepl" +   label="DeepL Translator" +   layout="topleft" +   name="deepl" +   top_pad="61" />   </radio_group>    <text @@ -154,7 +162,7 @@     left="185"     length="1"     name="google_links_text" -   top_pad="-142" +   top_pad="-262"     type="string"     width="100">      [https://learn.microsoft.com/en-us/azure/cognitive-services/translator/create-translator-resource Setup] @@ -286,7 +294,7 @@    left_pad="10"    name="verify_google_api_key_btn"    top_delta="-2" -  width="90" />	 +  width="90" />   <text    follows="top|right" @@ -301,6 +309,87 @@    [http://code.google.com/apis/language/translate/v2/pricing.html Pricing] | [https://code.google.com/apis/console Stats]    </text> +    <text +     type="string" +     length="1" +     follows="top|right" +     height="20" +     layout="topleft" +     left="70" +     name="deepl_api_domain_label" +     top_pad="80" +     width="85"> +        Domain: +    </text> + +    <combo_box +      allow_text_entry="false" +      follows="left|top" +      name="deepl_api_domain_combo" +      height="23" +      left_pad="10" +      width="140" +      top_delta="-4" +      max_chars="512" +      value="https://api-free.deepl.com" +      combo_button.scale_image="true"> +        <combo_box.item +          label="DeepL Free" +          name="global" +          value="https://api-free.deepl.com" /> +        <combo_box.item +          label="DeepL Pro" +          name="api-apc" +          value="https://api.deepl.com" /> +    </combo_box> +     +    <text +     follows="top|right" +     height="20" +     layout="topleft" +     left="70" +     length="1" +     name="deepl_api_key_label" +     top_pad="11" +     type="string" +     width="85"> +      DeepL API key: +    </text> + +    <line_editor +     default_text="Enter DeepL API key and click "Verify"" +     follows="top|left" +     height="20" +     layout="topleft" +     left_pad="10" +     max_length_chars="50" +     top_delta="-4" +     name="deepl_api_key" +     width="210" /> +     +    <button +     follows="left|top" +     height="23" +     label="Verify" +     layout="topleft" +     left_pad="10" +     name="verify_deepl_api_key_btn" +     top_delta="-2" +     width="90" /> + +    <text +     follows="top|right" +     height="20" +     layout="topleft" +     left="185" +     length="1" +     name="deepl_links_text" +     top_delta="-53" +     type="string" +     width="100"> +        [https://www.deepl.com/pro/select-country?cta=header-prices Pricing] +    </text> +   <button    follows="left|top"    height="23" | 
