SL-19209 WIP Switch MS Bing to MS Azure #2

meow-7.2.2
Andrey Kleshchev 2023-02-24 11:59:48 +02:00 committed by akleshchev
parent 3daccc5d23
commit 3fc3627f2d
6 changed files with 420 additions and 161 deletions

View File

@ -12929,13 +12929,13 @@
<key>TranslationService</key>
<map>
<key>Comment</key>
<string>Translation API to use. (google|bing)</string>
<string>Translation API to use. (google|azure)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>String</string>
<key>Value</key>
<string>bing</string>
<string>azure</string>
</map>
<key>GoogleTranslateAPIKey</key>
<map>
@ -12951,7 +12951,7 @@
<key>BingTranslateAPIKey</key>
<map>
<key>Comment</key>
<string>Bing AppID to use with the Microsoft Translator API</string>
<string>(Deprecated) Bing AppID to use with the Microsoft Translator API</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
@ -12959,6 +12959,17 @@
<key>Value</key>
<string></string>
</map>
<key>AzureTranslateAPIKey</key>
<map>
<key>Comment</key>
<string>Azure Translation service data to use with the MS Azure 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>

View File

@ -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<LLCheckBoxCtrl>("translate_chat_checkbox");
mLanguageCombo = getChild<LLComboBox>("translate_language_combo");
mTranslationServiceRadioGroup = getChild<LLRadioGroup>("translation_service_rg");
mBingAPIKeyEditor = getChild<LLLineEditor>("bing_api_key");
mAzureAPIEndpointEditor = getChild<LLComboBox>("azure_api_endpoint_combo");
mAzureAPIKeyEditor = getChild<LLLineEditor>("azure_api_key");
mAzureAPIRegionEditor = getChild<LLLineEditor>("azure_api_region");
mGoogleAPIKeyEditor = getChild<LLLineEditor>("google_api_key");
mBingVerifyBtn = getChild<LLButton>("verify_bing_api_key_btn");
mAzureVerifyBtn = getChild<LLButton>("verify_azure_api_key_btn");
mGoogleVerifyBtn = getChild<LLButton>("verify_google_api_key_btn");
mOKBtn = getChild<LLButton>("ok_btn");
@ -73,11 +68,17 @@ BOOL LLFloaterTranslationSettings::postBuild()
mTranslationServiceRadioGroup->setCommitCallback(boost::bind(&LLFloaterTranslationSettings::updateControlsEnabledState, this));
mOKBtn->setClickedCallback(boost::bind(&LLFloaterTranslationSettings::onBtnOK, this));
getChild<LLButton>("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<LLTextBox>("bing_api_key_label")->setEnabled(on);
mBingAPIKeyEditor->setEnabled(on);
getChild<LLTextBox>("azure_api_endoint_label")->setEnabled(on);
mAzureAPIEndpointEditor->setEnabled(on);
getChild<LLTextBox>("azure_api_key_label")->setEnabled(on);
mAzureAPIKeyEditor->setEnabled(on);
getChild<LLTextBox>("azure_api_region_label")->setEnabled(on);
mAzureAPIRegionEditor->setEnabled(on);
getChild<LLTextBox>("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<LLTranslate::EService>(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);

View File

@ -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;
};

View File

@ -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<std::string> 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("</p>", begin);
err_msg = body.substr(begin, end-begin);
LLStringUtil::replaceString(err_msg, "&#xD;", ""); // strip CR
return false;
}
// Sample response: <string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">Hola</string>
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("</string>", begin);
Json::Value root;
Json::Reader reader;
detected_lang = ""; // unsupported by this API
translation = body.substr(begin, end-begin);
LLStringUtil::replaceString(translation, "&#xD;", ""); // 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<LLSD> 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;
}

View File

@ -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

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<floater
legacy_header_height="18"
height="310"
height="370"
layout="topleft"
name="floater_translation_settings"
help_topic="translation_settings"
@ -9,10 +9,10 @@
title="CHAT TRANSLATION SETTINGS"
width="485">
<string name="bing_api_key_not_verified">Bing appID not verified. Please try again.</string>
<string name="azure_api_key_not_verified">Azure service identifier not verified. Please try again.</string>
<string name="google_api_key_not_verified">Google API key not verified. Please try again.</string>
<string name="bing_api_key_verified">Bing appID verified.</string>
<string name="azure_api_key_verified">Azure service identifier verified.</string>
<string name="google_api_key_verified">Google API key verified.</string>
<check_box
@ -128,25 +128,67 @@
<radio_group
follows="top|left"
height="80"
height="140"
layout="topleft"
left_delta="10"
name="translation_service_rg"
top_pad="20"
width="320">
<radio_item
initial_value="bing"
label="Bing Translator"
initial_value="azure"
label="Azure Translator"
layout="topleft"
name="bing" />
name="azure" />
<radio_item
initial_value="google"
label="Google Translate"
layout="topleft"
name="google"
top_pad="55" />
top_pad="115" />
</radio_group>
<text
type="string"
length="1"
follows="top|right"
height="20"
layout="topleft"
left="70"
name="azure_api_endoint_label"
top_pad="-115"
width="85">
Endpoint:
</text>
<combo_box
allow_text_entry="true"
follows="left|top"
name="azure_api_endpoint_combo"
height="23"
left_pad="10"
right="-10"
top_delta="-4"
max_chars="512"
value="https://api.cognitive.microsofttranslator.com"
combo_button.scale_image="true">
<combo_box.item
label="https://api.cognitive.microsofttranslator.com"
name="global"
value="https://api.cognitive.microsofttranslator.com" />
<combo_box.item
label="https://api-apc.cognitive.microsofttranslator.com"
name="api-apc"
value="https://api-apc.cognitive.microsofttranslator.com" />
<combo_box.item
label="https://api-eur.cognitive.microsofttranslator.com"
name="api-eur"
value="https://api-eur.cognitive.microsofttranslator.com" />
<combo_box.item
label="https://api-nam.cognitive.microsofttranslator.com"
name="api-nam"
value="https://api-nam.cognitive.microsofttranslator.com" />
</combo_box>
<text
type="string"
length="1"
@ -154,20 +196,20 @@
height="20"
layout="topleft"
left="70"
name="bing_api_key_label"
top_pad="-55"
name="azure_api_key_label"
top_pad="10"
width="85">
Bing [http://www.bing.com/developers/createapp.aspx AppID]:
Azure Key:
</text>
<line_editor
default_text="Enter Bing AppID and click &quot;Verify&quot;"
default_text="Enter Translator Key and click &quot;Verify&quot;"
follows="top|left"
height="20"
layout="topleft"
left_pad="10"
max_length_chars="50"
top_delta="-4"
name="bing_api_key"
name="azure_api_key"
width="210" />
<button
follows="left|top"
@ -175,9 +217,30 @@
label="Verify"
layout="topleft"
left_pad="10"
name="verify_bing_api_key_btn"
name="verify_azure_api_key_btn"
top_delta="-2"
width="90" />
width="90" />
<text
type="string"
length="1"
follows="top|right"
height="20"
layout="topleft"
left="70"
name="azure_api_region_label"
top_pad="10"
width="85">
Region:
</text>
<line_editor
follows="top|left"
height="20"
layout="topleft"
left_pad="10"
max_length_chars="50"
top_delta="-4"
name="azure_api_region"
width="210" />
<text
follows="top|right"
@ -186,7 +249,7 @@
left="70"
length="1"
name="google_api_key_label"
top_pad="50"
top_pad="55"
type="string"
width="85">
Google [http://code.google.com/apis/language/translate/v2/getting_started.html#auth API key]: