summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrey Kleshchev <andreykproductengine@lindenlab.com>2023-05-19 19:56:57 +0300
committerakleshchev <117672381+akleshchev@users.noreply.github.com>2023-05-22 20:25:27 +0300
commit1d1a63abe4a3d3a6191172c1693ffbdb0ffb2d71 (patch)
treed4c5ee7583210f4cef1d95115e961c17c5f58146
parent7140640b6963dacfa012dfec679798fc4dd13a17 (diff)
SL-19635 Implement DeepL tranlation support
-rw-r--r--indra/newview/app_settings/settings.xml11
-rw-r--r--indra/newview/llfloatertranslationsettings.cpp132
-rw-r--r--indra/newview/llfloatertranslationsettings.h10
-rw-r--r--indra/newview/lltranslate.cpp311
-rw-r--r--indra/newview/lltranslate.h1
-rw-r--r--indra/newview/skins/default/xui/en/floater_translation_settings.xml97
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 &quot;Verify&quot;"
+ 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"