From e2552ec6737fe734ffd5b4768193c6a890d66f70 Mon Sep 17 00:00:00 2001 From: Vadim ProductEngine Date: Tue, 6 Sep 2011 17:45:47 +0300 Subject: STORM-1577 WIP Implemented translation via Microsoft Translator and Google Translate v2 APIs. --- indra/newview/lltranslate.cpp | 312 +++++++++++++++++++++++++++++++++++------- 1 file changed, 260 insertions(+), 52 deletions(-) (limited to 'indra/newview/lltranslate.cpp') diff --git a/indra/newview/lltranslate.cpp b/indra/newview/lltranslate.cpp index 2f60b6b90b..e29ea373ce 100644 --- a/indra/newview/lltranslate.cpp +++ b/indra/newview/lltranslate.cpp @@ -37,74 +37,263 @@ #include "reader.h" -// These two are concatenated with the language specifiers to form a complete Google Translate URL -const char* LLTranslate::m_GoogleURL = "http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q="; -const char* LLTranslate::m_GoogleLangSpec = "&langpair="; -float LLTranslate::m_GoogleTimeout = 5; - -LLSD LLTranslate::m_Header; -// These constants are for the GET header. -const char* LLTranslate::m_AcceptHeader = "Accept"; -const char* LLTranslate::m_AcceptType = "text/plain"; -const char* LLTranslate::m_AgentHeader = "User-Agent"; - -// These constants are in the JSON returned from Google -const char* LLTranslate::m_GoogleData = "responseData"; -const char* LLTranslate::m_GoogleTranslation = "translatedText"; -const char* LLTranslate::m_GoogleLanguage = "detectedSourceLanguage"; +class LLTranslationAPIHandler +{ +public: + virtual void getTranslateURL( + std::string &url, + const std::string &from_lang, + const std::string &to_lang, + const std::string &text) const = 0; -//static -void LLTranslate::translateMessage(LLHTTPClient::ResponderPtr &result, const std::string &from_lang, const std::string &to_lang, const std::string &mesg) + virtual bool parseResponse( + int& status, + const std::string& body, + std::string& translation, + std::string& detected_lang, + std::string& err_msg) const = 0; + + virtual ~LLTranslationAPIHandler() {} + +protected: + static const int STATUS_OK = 200; +}; + +class LLGoogleV1Handler : public LLTranslationAPIHandler { - std::string url; - getTranslateUrl(url, from_lang, to_lang, mesg); + LOG_CLASS(LLGoogleV1Handler); + +public: + /*virtual*/ void getTranslateURL( + std::string &url, + const std::string &from_lang, + const std::string &to_lang, + const std::string &text) const + { + url = "http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=" + + LLURI::escape(text) + + "&langpair=" + from_lang + "%7C" + to_lang; + } + + /*virtual*/ bool parseResponse( + int& status, + const std::string& body, + std::string& translation, + std::string& detected_lang, + std::string& err_msg) const + { + Json::Value root; + Json::Reader reader; + + if (!reader.parse(body, root)) + { + err_msg = reader.getFormatedErrorMessages(); + return false; + } + + // This API doesn't return proper status in the HTTP response header, + // but it is in the body. + status = root["responseStatus"].asInt(); + if (status != STATUS_OK) + { + err_msg = root["responseDetails"].asString(); + return false; + } + + const Json::Value& response_data = root["responseData"]; + translation = response_data.get("translatedText", "").asString(); + detected_lang = response_data.get("detectedSourceLanguage", "").asString(); + return true; + } +}; + +class LLGoogleV2Handler : public LLTranslationAPIHandler +{ + LOG_CLASS(LLGoogleV2Handler); + +public: + /*virtual*/ void getTranslateURL( + std::string &url, + const std::string &from_lang, + const std::string &to_lang, + const std::string &text) const + { + url = std::string("https://www.googleapis.com/language/translate/v2?key=") + + getAPIKey() + "&q=" + LLURI::escape(text) + "&target=" + to_lang; + if (!from_lang.empty()) + { + url += "&source=" + from_lang; + } + } + + /*virtual*/ bool parseResponse( + int& status, + const std::string& body, + std::string& translation, + std::string& detected_lang, + std::string& err_msg) const + { + Json::Value root; + Json::Reader reader; + + if (!reader.parse(body, root)) + { + err_msg = reader.getFormatedErrorMessages(); + return false; + } + + if (status != STATUS_OK) + { + const Json::Value& error = root["error"]; + err_msg = error["message"].asString(); + status = error["code"].asInt(); + return false; + } + + const Json::Value& response_data = root["data"]["translations"][0U]; + translation = response_data["translatedText"].asString(); + detected_lang = response_data["detectedSourceLanguage"].asString(); + return true; + } + +private: + static std::string getAPIKey() + { + return gSavedSettings.getString("GoogleTranslateAPIv2Key"); + } +}; + +class LLBingHandler : public LLTranslationAPIHandler +{ + LOG_CLASS(LLBingHandler); - std::string user_agent = llformat("%s %d.%d.%d (%d)", - LLVersionInfo::getChannel().c_str(), - LLVersionInfo::getMajor(), - LLVersionInfo::getMinor(), - LLVersionInfo::getPatch(), - LLVersionInfo::getBuild()); +public: + /*virtual*/ void getTranslateURL( + std::string &url, + const std::string &from_lang, + const std::string &to_lang, + const std::string &text) const + { + url = std::string("http://api.microsofttranslator.com/v2/Http.svc/Translate?appId=") + + getAPIKey() + "&text=" + LLURI::escape(text) + "&to=" + to_lang; + if (!from_lang.empty()) + { + url += "&from=" + from_lang; + } + } - if (!m_Header.size()) + /*virtual*/ bool parseResponse( + int& status, + const std::string& body, + std::string& translation, + std::string& detected_lang, + std::string& err_msg) const { - m_Header.insert(m_AcceptHeader, LLSD(m_AcceptType)); - m_Header.insert(m_AgentHeader, LLSD(user_agent)); + if (status != STATUS_OK) + { + size_t begin = body.find("Message: "); + size_t end = body.find("

", begin); + err_msg = body.substr(begin, end-begin); + LLStringUtil::replaceString(err_msg, " ", ""); // strip CR + return false; + } + + // Sample response: Hola + size_t begin = body.find(">"); + if (begin == std::string::npos || begin >= (body.size() - 1)) + { + return false; + } + + size_t end = body.find("", ++begin); + if (end == std::string::npos || end < begin) + { + return false; + } + + detected_lang = ""; // unsupported by this API + translation = body.substr(begin, end-begin); + LLStringUtil::replaceString(translation, " ", ""); // strip CR + return true; } - LLHTTPClient::get(url, result, m_Header, m_GoogleTimeout); +private: + static std::string getAPIKey() + { + return gSavedSettings.getString("BingTranslateAPIKey"); + } +}; + +LLTranslate::TranslationReceiver::TranslationReceiver(const std::string& from_lang, const std::string& to_lang) +: mFromLang(from_lang) +, mToLang(to_lang) +, mHandler(LLTranslate::getPreferredHandler()) +{ } -//static -void LLTranslate::getTranslateUrl(std::string &translate_url, const std::string &from_lang, const std::string &to_lang, const std::string &mesg) +// virtual +void LLTranslate::TranslationReceiver::completedRaw( + U32 http_status, + const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) { - char * curl_str = curl_escape(mesg.c_str(), mesg.size()); - std::string const escaped_mesg(curl_str); - curl_free(curl_str); - - translate_url = m_GoogleURL - + escaped_mesg + m_GoogleLangSpec - + from_lang // 'from' language; empty string for auto - + "%7C" // | - + to_lang; // 'to' language + LLBufferStream istr(channels, buffer.get()); + std::stringstream strstrm; + strstrm << istr.rdbuf(); + + const std::string body = strstrm.str(); + std::string translation, detected_lang, err_msg; + int status = http_status; + if (mHandler.parseResponse(status, body, translation, detected_lang, err_msg)) + { + // Fix up the response + LLStringUtil::replaceString(translation, "<", "<"); + LLStringUtil::replaceString(translation, ">",">"); + LLStringUtil::replaceString(translation, ""","\""); + LLStringUtil::replaceString(translation, "'","'"); + LLStringUtil::replaceString(translation, "&","&"); + LLStringUtil::replaceString(translation, "'","'"); + + handleResponse(translation, detected_lang); + } + else + { + llwarns << "Translation request failed: " << err_msg << llendl; + LL_DEBUGS("Translate") << "HTTP status: " << status << " " << reason << LL_ENDL; + LL_DEBUGS("Translate") << "Error response body: " << body << LL_ENDL; + handleFailure(status, err_msg); + } } //static -bool LLTranslate::parseGoogleTranslate(const std::string& body, std::string &translation, std::string &detected_language) +void LLTranslate::translateMessage( + TranslationReceiverPtr &receiver, + const std::string &from_lang, + const std::string &to_lang, + const std::string &mesg) { - Json::Value root; - Json::Reader reader; - - bool success = reader.parse(body, root); - if (!success) + std::string url; + receiver->mHandler.getTranslateURL(url, from_lang, to_lang, mesg); + + static const float REQUEST_TIMEOUT = 5; + static LLSD sHeader; + + if (!sHeader.size()) { - LL_WARNS("Translate") << "Non valid response from Google Translate API: '" << reader.getFormatedErrorMessages() << "'" << LL_ENDL; - return false; + std::string user_agent = llformat("%s %d.%d.%d (%d)", + LLVersionInfo::getChannel().c_str(), + LLVersionInfo::getMajor(), + LLVersionInfo::getMinor(), + LLVersionInfo::getPatch(), + LLVersionInfo::getBuild()); + + sHeader.insert("Accept", "text/plain"); + sHeader.insert("User-Agent", user_agent); } - - translation = root[m_GoogleData].get(m_GoogleTranslation, "").asString(); - detected_language = root[m_GoogleData].get(m_GoogleLanguage, "").asString(); - return true; + + LL_DEBUGS("Translate") << "Sending translation request: " << url << LL_ENDL; + LLHTTPClient::get(url, receiver, sHeader, REQUEST_TIMEOUT); } //static @@ -119,3 +308,22 @@ std::string LLTranslate::getTranslateLanguage() return language; } +// static +const LLTranslationAPIHandler& LLTranslate::getPreferredHandler() +{ + static LLGoogleV1Handler google_v1; + static LLGoogleV2Handler google_v2; + static LLBingHandler bing; + + std::string service = gSavedSettings.getString("TranslationService"); + if (service == "google_v2") + { + return google_v2; + } + else if (service == "google_v1") + { + return google_v1; + } + + return bing; +} -- cgit v1.3 From 7975ab138b6ac54fc831613e4d3dfb913c5efbd2 Mon Sep 17 00:00:00 2001 From: Vadim ProductEngine Date: Wed, 7 Sep 2011 16:14:47 +0300 Subject: STORM-1577 Removed support for Google Translate v1 API. --- indra/newview/app_settings/settings.xml | 10 ++-- indra/newview/llfloatertranslationsettings.cpp | 8 +-- indra/newview/lltranslate.cpp | 67 +++------------------- .../xui/en/floater_translation_settings.xml | 4 +- 4 files changed, 18 insertions(+), 71 deletions(-) (limited to 'indra/newview/lltranslate.cpp') diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 2549538df2..2f1a2093b2 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -10925,18 +10925,18 @@ TranslationService Comment - Translation API to use. (google_v1|google_v2|bing) + Translation API to use. (google|bing) Persist 1 Type String Value - google_v1 + bing - GoogleTranslateAPIv2Key + GoogleTranslateAPIKey Comment - Google Translate API v2 key + Google Translate API key Persist 1 Type @@ -10947,7 +10947,7 @@ BingTranslateAPIKey Comment - Bing AppID to use with the Microsoft Translator V2 API + Bing AppID to use with the Microsoft Translator API Persist 1 Type diff --git a/indra/newview/llfloatertranslationsettings.cpp b/indra/newview/llfloatertranslationsettings.cpp index 56f101d149..107205aed3 100644 --- a/indra/newview/llfloatertranslationsettings.cpp +++ b/indra/newview/llfloatertranslationsettings.cpp @@ -75,7 +75,7 @@ void LLFloaterTranslationSettings::onOpen(const LLSD& key) mLanguageCombo->setSelectedByValue(gSavedSettings.getString("TranslateLanguage"), TRUE); mTranslationServiceRadioGroup->setSelectedByValue(gSavedSettings.getString("TranslationService"), TRUE); mBingAPIKeyEditor->setText(gSavedSettings.getString("BingTranslateAPIKey")); - mGoogleAPIKeyEditor->setText(gSavedSettings.getString("GoogleTranslateAPIv2Key")); + mGoogleAPIKeyEditor->setText(gSavedSettings.getString("GoogleTranslateAPIKey")); updateControlsEnabledState(); } @@ -104,7 +104,7 @@ bool LLFloaterTranslationSettings::validate() return false; } - if (service == "google_v2" && mGoogleAPIKeyEditor->getText().empty()) + if (service == "google" && mGoogleAPIKeyEditor->getText().empty()) { showError("no_google_api_key"); return false; @@ -129,7 +129,7 @@ void LLFloaterTranslationSettings::updateControlsEnabledState() mGoogleAPIKeyEditor->setEnabled(on); mBingAPIKeyEditor->setEnabled(service == "bing"); - mGoogleAPIKeyEditor->setEnabled(service == "google_v2"); + mGoogleAPIKeyEditor->setEnabled(service == "google"); } void LLFloaterTranslationSettings::onBtnOK() @@ -140,7 +140,7 @@ void LLFloaterTranslationSettings::onBtnOK() gSavedSettings.setString("TranslateLanguage", mLanguageCombo->getSelectedValue().asString()); gSavedSettings.setString("TranslationService", getSelectedService()); gSavedSettings.setString("BingTranslateAPIKey", mBingAPIKeyEditor->getText()); - gSavedSettings.setString("GoogleTranslateAPIv2Key", mGoogleAPIKeyEditor->getText()); + gSavedSettings.setString("GoogleTranslateAPIKey", mGoogleAPIKeyEditor->getText()); closeFloater(false); } } diff --git a/indra/newview/lltranslate.cpp b/indra/newview/lltranslate.cpp index e29ea373ce..6576cbbe64 100644 --- a/indra/newview/lltranslate.cpp +++ b/indra/newview/lltranslate.cpp @@ -59,57 +59,9 @@ protected: static const int STATUS_OK = 200; }; -class LLGoogleV1Handler : public LLTranslationAPIHandler +class LLGoogleHandler : public LLTranslationAPIHandler { - LOG_CLASS(LLGoogleV1Handler); - -public: - /*virtual*/ void getTranslateURL( - std::string &url, - const std::string &from_lang, - const std::string &to_lang, - const std::string &text) const - { - url = "http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=" - + LLURI::escape(text) - + "&langpair=" + from_lang + "%7C" + to_lang; - } - - /*virtual*/ bool parseResponse( - int& status, - const std::string& body, - std::string& translation, - std::string& detected_lang, - std::string& err_msg) const - { - Json::Value root; - Json::Reader reader; - - if (!reader.parse(body, root)) - { - err_msg = reader.getFormatedErrorMessages(); - return false; - } - - // This API doesn't return proper status in the HTTP response header, - // but it is in the body. - status = root["responseStatus"].asInt(); - if (status != STATUS_OK) - { - err_msg = root["responseDetails"].asString(); - return false; - } - - const Json::Value& response_data = root["responseData"]; - translation = response_data.get("translatedText", "").asString(); - detected_lang = response_data.get("detectedSourceLanguage", "").asString(); - return true; - } -}; - -class LLGoogleV2Handler : public LLTranslationAPIHandler -{ - LOG_CLASS(LLGoogleV2Handler); + LOG_CLASS(LLGoogleHandler); public: /*virtual*/ void getTranslateURL( @@ -159,7 +111,7 @@ public: private: static std::string getAPIKey() { - return gSavedSettings.getString("GoogleTranslateAPIv2Key"); + return gSavedSettings.getString("GoogleTranslateAPIKey"); } }; @@ -311,18 +263,13 @@ std::string LLTranslate::getTranslateLanguage() // static const LLTranslationAPIHandler& LLTranslate::getPreferredHandler() { - static LLGoogleV1Handler google_v1; - static LLGoogleV2Handler google_v2; - static LLBingHandler bing; + static LLGoogleHandler google; + static LLBingHandler bing; std::string service = gSavedSettings.getString("TranslationService"); - if (service == "google_v2") - { - return google_v2; - } - else if (service == "google_v1") + if (service == "google") { - return google_v1; + return google; } return bing; 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 40a176830c..f21f64fcf6 100644 --- a/indra/newview/skins/default/xui/en/floater_translation_settings.xml +++ b/indra/newview/skins/default/xui/en/floater_translation_settings.xml @@ -137,10 +137,10 @@ layout="topleft" name="bing" /> -- cgit v1.3 From 1fad7d997d99715cc88b6e69ae325f28be413206 Mon Sep 17 00:00:00 2001 From: Vadim ProductEngine Date: Wed, 7 Sep 2011 19:12:35 +0300 Subject: STORM-1577 Made parsing translation responses more robust. JsonCpp is prone to aborting the program on failed assertions, so be super-careful and verify the response format. --- indra/newview/lltranslate.cpp | 72 +++++++++++++++++++++++--- indra/newview/skins/default/xui/en/strings.xml | 3 ++ 2 files changed, 67 insertions(+), 8 deletions(-) (limited to 'indra/newview/lltranslate.cpp') diff --git a/indra/newview/lltranslate.cpp b/indra/newview/lltranslate.cpp index 6576cbbe64..895d8f78eb 100644 --- a/indra/newview/lltranslate.cpp +++ b/indra/newview/lltranslate.cpp @@ -31,6 +31,7 @@ #include #include "llbufferstream.h" +#include "lltrans.h" #include "llui.h" #include "llversioninfo.h" #include "llviewercontrol.h" @@ -94,21 +95,66 @@ public: return false; } + if (!root.isObject()) // empty response? should not happen + { + return false; + } + if (status != STATUS_OK) { - const Json::Value& error = root["error"]; - err_msg = error["message"].asString(); - status = error["code"].asInt(); + // Request failed. Extract error message from the response. + parseErrorResponse(root, status, err_msg); return false; } - const Json::Value& response_data = root["data"]["translations"][0U]; - translation = response_data["translatedText"].asString(); - detected_lang = response_data["detectedSourceLanguage"].asString(); - return true; + // Request succeeded, extract translation from the response. + return parseTranslation(root, translation, detected_lang); } private: + static void parseErrorResponse( + const Json::Value& root, + int& status, + std::string& err_msg) + { + const Json::Value& error = root.get("error", 0); + if (!error.isObject() || !error.isMember("message") || !error.isMember("code")) + { + return; + } + + err_msg = error["message"].asString(); + status = error["code"].asInt(); + } + + static bool parseTranslation( + const Json::Value& root, + std::string& translation, + std::string& detected_lang) + { + const Json::Value& data = root.get("data", 0); + if (!data.isObject() || !data.isMember("translations")) + { + return false; + } + + 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("translatedText")) + { + return false; + } + + translation = first["translatedText"].asString(); + detected_lang = first.get("detectedSourceLanguage", "").asString(); + return true; + } + static std::string getAPIKey() { return gSavedSettings.getString("GoogleTranslateAPIKey"); @@ -143,7 +189,12 @@ public: { if (status != STATUS_OK) { - size_t begin = body.find("Message: "); + 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(); + } size_t end = body.find("

", begin); err_msg = body.substr(begin, end-begin); LLStringUtil::replaceString(err_msg, " ", ""); // strip CR @@ -211,6 +262,11 @@ void LLTranslate::TranslationReceiver::completedRaw( } else { + if (err_msg.empty()) + { + err_msg = LLTrans::getString("TranslationResponseParseError"); + } + llwarns << "Translation request failed: " << err_msg << llendl; LL_DEBUGS("Translate") << "HTTP status: " << status << " " << reason << LL_ENDL; LL_DEBUGS("Translate") << "Error response body: " << body << LL_ENDL; diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 2094275bed..146665b47d 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -3502,6 +3502,9 @@ Try enclosing path to the editor with double quotes. Error parsing the external editor command. External editor failed to run. + + Error parsing translation response. + Esc Space -- cgit v1.3 From a21a55482a076aa690ca947411f439dd14d59443 Mon Sep 17 00:00:00 2001 From: Vadim ProductEngine Date: Fri, 9 Sep 2011 20:09:01 +0300 Subject: STORM-1577 WIP Added unit tests. By the way, fixed minor parsing bugs. --- indra/newview/CMakeLists.txt | 7 + indra/newview/lltranslate.cpp | 295 +++++++++++++++++++----------------------- indra/newview/lltranslate.h | 77 ++++++++++- 3 files changed, 219 insertions(+), 160 deletions(-) (limited to 'indra/newview/lltranslate.cpp') diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index e7ca2a4294..635f425540 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1985,12 +1985,19 @@ if (LL_TESTS) llmediadataclient.cpp lllogininstance.cpp llremoteparcelrequest.cpp + lltranslate.cpp llviewerhelputil.cpp llversioninfo.cpp llworldmap.cpp llworldmipmap.cpp ) + set_source_files_properties( + lltranslate.cpp + PROPERTIES + LL_TEST_ADDITIONAL_LIBRARIES "${JSONCPP_LIBRARIES}" + ) + ################################################## # DISABLING PRECOMPILED HEADERS USAGE FOR TESTS ################################################## diff --git a/indra/newview/lltranslate.cpp b/indra/newview/lltranslate.cpp index 895d8f78eb..a74b252c68 100644 --- a/indra/newview/lltranslate.cpp +++ b/indra/newview/lltranslate.cpp @@ -38,194 +38,171 @@ #include "reader.h" -class LLTranslationAPIHandler -{ -public: - virtual void getTranslateURL( - std::string &url, - const std::string &from_lang, - const std::string &to_lang, - const std::string &text) const = 0; - - virtual bool parseResponse( - int& status, - const std::string& body, - std::string& translation, - std::string& detected_lang, - std::string& err_msg) const = 0; - - virtual ~LLTranslationAPIHandler() {} - -protected: - static const int STATUS_OK = 200; -}; - -class LLGoogleHandler : public LLTranslationAPIHandler +// virtual +void LLGoogleTranslationHandler::getTranslateURL( + std::string &url, + const std::string &from_lang, + const std::string &to_lang, + const std::string &text) const { - LOG_CLASS(LLGoogleHandler); - -public: - /*virtual*/ void getTranslateURL( - std::string &url, - const std::string &from_lang, - const std::string &to_lang, - const std::string &text) const + url = std::string("https://www.googleapis.com/language/translate/v2?key=") + + getAPIKey() + "&q=" + LLURI::escape(text) + "&target=" + to_lang; + if (!from_lang.empty()) { - url = std::string("https://www.googleapis.com/language/translate/v2?key=") - + getAPIKey() + "&q=" + LLURI::escape(text) + "&target=" + to_lang; - if (!from_lang.empty()) - { - url += "&source=" + from_lang; - } + url += "&source=" + from_lang; } +} - /*virtual*/ bool parseResponse( - int& status, - const std::string& body, - std::string& translation, - std::string& detected_lang, - std::string& err_msg) const - { - Json::Value root; - Json::Reader reader; - - if (!reader.parse(body, root)) - { - err_msg = reader.getFormatedErrorMessages(); - return false; - } - - if (!root.isObject()) // empty response? should not happen - { - return false; - } - - if (status != STATUS_OK) - { - // Request failed. Extract error message from the response. - parseErrorResponse(root, status, err_msg); - return false; - } +// virtual +bool LLGoogleTranslationHandler::parseResponse( + int& status, + const std::string& body, + std::string& translation, + std::string& detected_lang, + std::string& err_msg) const +{ + Json::Value root; + Json::Reader reader; - // Request succeeded, extract translation from the response. - return parseTranslation(root, translation, detected_lang); + if (!reader.parse(body, root)) + { + err_msg = reader.getFormatedErrorMessages(); + return false; } -private: - static void parseErrorResponse( - const Json::Value& root, - int& status, - std::string& err_msg) + if (!root.isObject()) // empty response? should not happen { - const Json::Value& error = root.get("error", 0); - if (!error.isObject() || !error.isMember("message") || !error.isMember("code")) - { - return; - } + return false; + } - err_msg = error["message"].asString(); - status = error["code"].asInt(); + if (status != STATUS_OK) + { + // Request failed. Extract error message from the response. + parseErrorResponse(root, status, err_msg); + return false; } - static bool parseTranslation( - const Json::Value& root, - std::string& translation, - std::string& detected_lang) + // Request succeeded, extract translation from the response. + return parseTranslation(root, translation, detected_lang); +} + +// static +void LLGoogleTranslationHandler::parseErrorResponse( + const Json::Value& root, + int& status, + std::string& err_msg) +{ + const Json::Value& error = root.get("error", 0); + if (!error.isObject() || !error.isMember("message") || !error.isMember("code")) { - const Json::Value& data = root.get("data", 0); - if (!data.isObject() || !data.isMember("translations")) - { - return false; - } + return; + } - const Json::Value& translations = data["translations"]; - if (!translations.isArray() || translations.size() == 0) - { - return false; - } + err_msg = error["message"].asString(); + status = error["code"].asInt(); +} - const Json::Value& first = translations[0U]; - if (!first.isObject() || !first.isMember("translatedText")) - { - return false; - } +// static +bool LLGoogleTranslationHandler::parseTranslation( + const Json::Value& root, + std::string& translation, + std::string& detected_lang) +{ + const Json::Value& data = root.get("data", 0); + if (!data.isObject() || !data.isMember("translations")) + { + return false; + } - translation = first["translatedText"].asString(); - detected_lang = first.get("detectedSourceLanguage", "").asString(); - return true; + const Json::Value& translations = data["translations"]; + if (!translations.isArray() || translations.size() == 0) + { + return false; } - static std::string getAPIKey() + const Json::Value& first = translations[0U]; + if (!first.isObject() || !first.isMember("translatedText")) { - return gSavedSettings.getString("GoogleTranslateAPIKey"); + return false; } -}; -class LLBingHandler : public LLTranslationAPIHandler + translation = first["translatedText"].asString(); + detected_lang = first.get("detectedSourceLanguage", "").asString(); + return true; +} + +// static +std::string LLGoogleTranslationHandler::getAPIKey() +{ + return gSavedSettings.getString("GoogleTranslateAPIKey"); +} + +// virtual +void LLBingTranslarionHandler::getTranslateURL( + std::string &url, + const std::string &from_lang, + const std::string &to_lang, + const std::string &text) const { - LOG_CLASS(LLBingHandler); - -public: - /*virtual*/ void getTranslateURL( - std::string &url, - const std::string &from_lang, - const std::string &to_lang, - const std::string &text) const + url = std::string("http://api.microsofttranslator.com/v2/Http.svc/Translate?appId=") + + getAPIKey() + "&text=" + LLURI::escape(text) + "&to=" + to_lang; + if (!from_lang.empty()) { - url = std::string("http://api.microsofttranslator.com/v2/Http.svc/Translate?appId=") - + getAPIKey() + "&text=" + LLURI::escape(text) + "&to=" + to_lang; - if (!from_lang.empty()) - { - url += "&from=" + from_lang; - } + url += "&from=" + from_lang; } +} - /*virtual*/ bool parseResponse( - int& status, - const std::string& body, - std::string& translation, - std::string& detected_lang, - std::string& err_msg) const +// virtual +bool LLBingTranslarionHandler::parseResponse( + int& status, + const std::string& body, + std::string& translation, + std::string& detected_lang, + std::string& err_msg) const +{ + if (status != STATUS_OK) { - if (status != STATUS_OK) + static const std::string MSG_BEGIN_MARKER = "Message: "; + size_t begin = body.find(MSG_BEGIN_MARKER); + if (begin != std::string::npos) { - 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(); - } - size_t end = body.find("

", begin); - err_msg = body.substr(begin, end-begin); - LLStringUtil::replaceString(err_msg, " ", ""); // strip CR - return false; + begin += MSG_BEGIN_MARKER.size(); } - - // Sample response: Hola - size_t begin = body.find(">"); - if (begin == std::string::npos || begin >= (body.size() - 1)) + else { - return false; + begin = 0; + err_msg.clear(); } - - size_t end = body.find("", ++begin); - if (end == std::string::npos || end < begin) - { - return false; - } - - detected_lang = ""; // unsupported by this API - translation = body.substr(begin, end-begin); - LLStringUtil::replaceString(translation, " ", ""); // strip CR - return true; + size_t end = body.find("

", begin); + err_msg = body.substr(begin, end-begin); + LLStringUtil::replaceString(err_msg, " ", ""); // strip CR + return false; } -private: - static std::string getAPIKey() + // Sample response: Hola + size_t begin = body.find(">"); + if (begin == std::string::npos || begin >= (body.size() - 1)) { - return gSavedSettings.getString("BingTranslateAPIKey"); + begin = 0; } -}; + else + { + ++begin; + } + + size_t end = body.find("", begin); + + detected_lang = ""; // unsupported by this API + translation = body.substr(begin, end-begin); + LLStringUtil::replaceString(translation, " ", ""); // strip CR + return true; +} + +// static +std::string LLBingTranslarionHandler::getAPIKey() +{ + return gSavedSettings.getString("BingTranslateAPIKey"); +} LLTranslate::TranslationReceiver::TranslationReceiver(const std::string& from_lang, const std::string& to_lang) : mFromLang(from_lang) @@ -248,6 +225,8 @@ void LLTranslate::TranslationReceiver::completedRaw( const std::string body = strstrm.str(); std::string translation, detected_lang, err_msg; int status = http_status; + LL_DEBUGS("Translate") << "HTTP status: " << status << " " << reason << LL_ENDL; + LL_DEBUGS("Translate") << "Response body: " << body << LL_ENDL; if (mHandler.parseResponse(status, body, translation, detected_lang, err_msg)) { // Fix up the response @@ -268,8 +247,6 @@ void LLTranslate::TranslationReceiver::completedRaw( } llwarns << "Translation request failed: " << err_msg << llendl; - LL_DEBUGS("Translate") << "HTTP status: " << status << " " << reason << LL_ENDL; - LL_DEBUGS("Translate") << "Error response body: " << body << LL_ENDL; handleFailure(status, err_msg); } } @@ -319,8 +296,8 @@ std::string LLTranslate::getTranslateLanguage() // static const LLTranslationAPIHandler& LLTranslate::getPreferredHandler() { - static LLGoogleHandler google; - static LLBingHandler bing; + static LLGoogleTranslationHandler google; + static LLBingTranslarionHandler bing; std::string service = gSavedSettings.getString("TranslationService"); if (service == "google") diff --git a/indra/newview/lltranslate.h b/indra/newview/lltranslate.h index 1dee792f7b..1bf6965fd4 100644 --- a/indra/newview/lltranslate.h +++ b/indra/newview/lltranslate.h @@ -30,7 +30,82 @@ #include "llhttpclient.h" #include "llbufferstream.h" -class LLTranslationAPIHandler; +namespace Json +{ + class Value; +} + +class LLTranslationAPIHandler +{ +public: + virtual void getTranslateURL( + std::string &url, + const std::string &from_lang, + const std::string &to_lang, + const std::string &text) const = 0; + + virtual bool parseResponse( + int& status, + const std::string& body, + std::string& translation, + std::string& detected_lang, + std::string& err_msg) const = 0; + + virtual ~LLTranslationAPIHandler() {} + +protected: + static const int STATUS_OK = 200; +}; + +class LLGoogleTranslationHandler : public LLTranslationAPIHandler +{ + LOG_CLASS(LLGoogleTranslationHandler); + +public: + /*virtual*/ void getTranslateURL( + std::string &url, + const std::string &from_lang, + const std::string &to_lang, + const std::string &text) const; + /*virtual*/ bool parseResponse( + int& status, + const std::string& body, + std::string& translation, + std::string& detected_lang, + std::string& err_msg) const; + +private: + static void parseErrorResponse( + const Json::Value& root, + int& status, + std::string& err_msg); + static bool parseTranslation( + const Json::Value& root, + std::string& translation, + std::string& detected_lang); + static std::string getAPIKey(); +}; + +class LLBingTranslarionHandler : public LLTranslationAPIHandler +{ + LOG_CLASS(LLBingTranslarionHandler); + +public: + /*virtual*/ void getTranslateURL( + std::string &url, + const std::string &from_lang, + const std::string &to_lang, + const std::string &text) const; + /*virtual*/ bool parseResponse( + int& status, + const std::string& body, + std::string& translation, + std::string& detected_lang, + std::string& err_msg) const; +private: + static std::string getAPIKey(); +}; + class LLTranslate { -- cgit v1.3 From 8652b2d1052b989e32f4462a07372901d37f9586 Mon Sep 17 00:00:00 2001 From: Vadim ProductEngine Date: Tue, 13 Sep 2011 03:05:57 +0300 Subject: STORM-1577 WIP Added API key verification to the translation settings floater; new layout. --- indra/newview/llfloatertranslationsettings.cpp | 214 +++++++++++++++++---- indra/newview/llfloatertranslationsettings.h | 22 ++- indra/newview/lltranslate.cpp | 107 +++++++++-- indra/newview/lltranslate.h | 37 ++++ .../xui/en/floater_translation_settings.xml | 52 +++-- 5 files changed, 363 insertions(+), 69 deletions(-) (limited to 'indra/newview/lltranslate.cpp') diff --git a/indra/newview/llfloatertranslationsettings.cpp b/indra/newview/llfloatertranslationsettings.cpp index 4079fdf482..ac3e7ac8fa 100644 --- a/indra/newview/llfloatertranslationsettings.cpp +++ b/indra/newview/llfloatertranslationsettings.cpp @@ -29,6 +29,7 @@ #include "llfloatertranslationsettings.h" // Viewer includes +#include "lltranslate.h" #include "llviewercontrol.h" // for gSavedSettings // Linden library includes @@ -40,6 +41,41 @@ #include "llnotificationsutil.h" #include "llradiogroup.h" +class EnteredKeyVerifier : public LLTranslate::KeyVerificationReceiver +{ +public: + EnteredKeyVerifier(LLTranslate::EService service, bool alert) + : LLTranslate::KeyVerificationReceiver(service) + , mAlert(alert) + { + } + +private: + /*virtual*/ void setVerificationStatus(bool ok) + { + LLFloaterTranslationSettings* floater = + LLFloaterReg::getTypedInstance("prefs_translation"); + + if (!floater) + { + llwarns << "Cannot find translation settings floater" << llendl; + return; + } + + switch (getService()) + { + case LLTranslate::SERVICE_BING: + floater->setBingVerified(ok, mAlert); + break; + case LLTranslate::SERVICE_GOOGLE: + floater->setGoogleVerified(ok, mAlert); + break; + } + } + + bool mAlert; +}; + LLFloaterTranslationSettings::LLFloaterTranslationSettings(const LLSD& key) : LLFloater(key) , mMachineTranslationCB(NULL) @@ -47,6 +83,11 @@ LLFloaterTranslationSettings::LLFloaterTranslationSettings(const LLSD& key) , mTranslationServiceRadioGroup(NULL) , mBingAPIKeyEditor(NULL) , mGoogleAPIKeyEditor(NULL) +, mBingVerifyBtn(NULL) +, mGoogleVerifyBtn(NULL) +, mOKBtn(NULL) +, mBingKeyVerified(false) +, mGoogleKeyVerified(false) { } @@ -58,11 +99,21 @@ BOOL LLFloaterTranslationSettings::postBuild() mTranslationServiceRadioGroup = getChild("translation_service_rg"); mBingAPIKeyEditor = getChild("bing_api_key"); mGoogleAPIKeyEditor = getChild("google_api_key"); + mBingVerifyBtn = getChild("verify_bing_api_key_btn"); + mGoogleVerifyBtn = getChild("verify_google_api_key_btn"); + mOKBtn = getChild("ok_btn"); mMachineTranslationCB->setCommitCallback(boost::bind(&LLFloaterTranslationSettings::updateControlsEnabledState, this)); mTranslationServiceRadioGroup->setCommitCallback(boost::bind(&LLFloaterTranslationSettings::updateControlsEnabledState, this)); - getChild("ok_btn")->setClickedCallback(boost::bind(&LLFloaterTranslationSettings::onBtnOK, this)); + mOKBtn->setClickedCallback(boost::bind(&LLFloaterTranslationSettings::onBtnOK, this)); getChild("cancel_btn")->setClickedCallback(boost::bind(&LLFloater::closeFloater, this, false)); + mBingVerifyBtn->setClickedCallback(boost::bind(&LLFloaterTranslationSettings::onBtnBingVerify, this)); + mGoogleVerifyBtn->setClickedCallback(boost::bind(&LLFloaterTranslationSettings::onBtnGoogleVerify, this)); + + mBingAPIKeyEditor->setFocusReceivedCallback(boost::bind(&LLFloaterTranslationSettings::onEditorFocused, this, _1)); + mBingAPIKeyEditor->setKeystrokeCallback(boost::bind(&LLFloaterTranslationSettings::onBingKeyEdited, this), NULL); + mGoogleAPIKeyEditor->setFocusReceivedCallback(boost::bind(&LLFloaterTranslationSettings::onEditorFocused, this, _1)); + mGoogleAPIKeyEditor->setKeystrokeCallback(boost::bind(&LLFloaterTranslationSettings::onGoogleKeyEdited, this), NULL); center(); return TRUE; @@ -74,43 +125,78 @@ void LLFloaterTranslationSettings::onOpen(const LLSD& key) mMachineTranslationCB->setValue(gSavedSettings.getBOOL("TranslateChat")); mLanguageCombo->setSelectedByValue(gSavedSettings.getString("TranslateLanguage"), TRUE); mTranslationServiceRadioGroup->setSelectedByValue(gSavedSettings.getString("TranslationService"), TRUE); - mBingAPIKeyEditor->setText(gSavedSettings.getString("BingTranslateAPIKey")); - mGoogleAPIKeyEditor->setText(gSavedSettings.getString("GoogleTranslateAPIKey")); + + std::string bing_key = gSavedSettings.getString("BingTranslateAPIKey"); + if (!bing_key.empty()) + { + mBingAPIKeyEditor->setText(bing_key); + mBingAPIKeyEditor->setTentative(FALSE); + verifyKey(LLTranslate::SERVICE_BING, bing_key, false); + } + else + { + mBingAPIKeyEditor->setTentative(TRUE); + mBingKeyVerified = FALSE; + } + + std::string google_key = gSavedSettings.getString("GoogleTranslateAPIKey"); + if (!google_key.empty()) + { + mGoogleAPIKeyEditor->setText(google_key); + mGoogleAPIKeyEditor->setTentative(FALSE); + verifyKey(LLTranslate::SERVICE_GOOGLE, google_key, false); + } + else + { + mGoogleAPIKeyEditor->setTentative(TRUE); + mGoogleKeyVerified = FALSE; + } updateControlsEnabledState(); } -std::string LLFloaterTranslationSettings::getSelectedService() const +void LLFloaterTranslationSettings::setBingVerified(bool ok, bool alert) { - return mTranslationServiceRadioGroup->getSelectedValue().asString(); + if (alert) + { + showAlert(ok ? "bing_api_key_verified" : "bing_api_key_not_verified"); + } + + mBingKeyVerified = ok; + updateControlsEnabledState(); } -void LLFloaterTranslationSettings::showError(const std::string& err_name) const +void LLFloaterTranslationSettings::setGoogleVerified(bool ok, bool alert) { - LLSD args; - args["MESSAGE"] = getString(err_name); - LLNotificationsUtil::add("GenericAlert", args); + if (alert) + { + showAlert(ok ? "google_api_key_verified" : "google_api_key_not_verified"); + } + + mGoogleKeyVerified = ok; + updateControlsEnabledState(); } -bool LLFloaterTranslationSettings::validate() const +std::string LLFloaterTranslationSettings::getSelectedService() const { - bool translate_chat = mMachineTranslationCB->getValue().asBoolean(); - if (!translate_chat) return true; + return mTranslationServiceRadioGroup->getSelectedValue().asString(); +} - std::string service = getSelectedService(); - if (service == "bing" && mBingAPIKeyEditor->getText().empty()) - { - showError("no_bing_api_key"); - return false; - } +std::string LLFloaterTranslationSettings::getEnteredBingKey() const +{ + return mBingAPIKeyEditor->getTentative() ? LLStringUtil::null : mBingAPIKeyEditor->getText(); +} - if (service == "google" && mGoogleAPIKeyEditor->getText().empty()) - { - showError("no_google_api_key"); - return false; - } +std::string LLFloaterTranslationSettings::getEnteredGoogleKey() const +{ + return mGoogleAPIKeyEditor->getTentative() ? LLStringUtil::null : mGoogleAPIKeyEditor->getText(); +} - return true; +void LLFloaterTranslationSettings::showAlert(const std::string& msg_name) const +{ + LLSD args; + args["MESSAGE"] = getString(msg_name); + LLNotificationsUtil::add("GenericAlert", args); } void LLFloaterTranslationSettings::updateControlsEnabledState() @@ -118,6 +204,8 @@ 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 google_selected = service == "google"; mTranslationServiceRadioGroup->setEnabled(on); mLanguageCombo->setEnabled(on); @@ -128,19 +216,77 @@ void LLFloaterTranslationSettings::updateControlsEnabledState() getChild("google_api_key_label")->setEnabled(on); mGoogleAPIKeyEditor->setEnabled(on); - mBingAPIKeyEditor->setEnabled(on && service == "bing"); - mGoogleAPIKeyEditor->setEnabled(on && service == "google"); + mBingAPIKeyEditor->setEnabled(on && bing_selected); + mGoogleAPIKeyEditor->setEnabled(on && google_selected); + + mBingVerifyBtn->setEnabled(on && bing_selected && + !mBingKeyVerified && !getEnteredBingKey().empty()); + mGoogleVerifyBtn->setEnabled(on && google_selected && + !mGoogleKeyVerified && !getEnteredGoogleKey().empty()); + + mOKBtn->setEnabled( + !on || ( + (bing_selected && mBingKeyVerified) || + (google_selected && mGoogleKeyVerified) + )); } -void LLFloaterTranslationSettings::onBtnOK() +void LLFloaterTranslationSettings::verifyKey(int service, const std::string& key, bool alert) { - if (validate()) + LLTranslate::KeyVerificationReceiverPtr receiver = + new EnteredKeyVerifier((LLTranslate::EService) service, alert); + LLTranslate::verifyKey(receiver, key); +} + +void LLFloaterTranslationSettings::onEditorFocused(LLFocusableElement* control) +{ + LLLineEditor* editor = dynamic_cast(control); + if (editor) { - gSavedSettings.setBOOL("TranslateChat", mMachineTranslationCB->getValue().asBoolean()); - gSavedSettings.setString("TranslateLanguage", mLanguageCombo->getSelectedValue().asString()); - gSavedSettings.setString("TranslationService", getSelectedService()); - gSavedSettings.setString("BingTranslateAPIKey", mBingAPIKeyEditor->getText()); - gSavedSettings.setString("GoogleTranslateAPIKey", mGoogleAPIKeyEditor->getText()); - closeFloater(false); + if (editor->getTentative()) + { + editor->setText(LLStringUtil::null); + editor->setTentative(FALSE); + } } } + +void LLFloaterTranslationSettings::onBingKeyEdited() +{ + mBingAPIKeyEditor->setTentative(FALSE); + setBingVerified(false, false); +} + +void LLFloaterTranslationSettings::onGoogleKeyEdited() +{ + mGoogleAPIKeyEditor->setTentative(FALSE); + setGoogleVerified(false, false); +} + +void LLFloaterTranslationSettings::onBtnBingVerify() +{ + std::string key = getEnteredBingKey(); + if (!key.empty()) + { + verifyKey(LLTranslate::SERVICE_BING, key); + } +} + +void LLFloaterTranslationSettings::onBtnGoogleVerify() +{ + std::string key = getEnteredGoogleKey(); + if (!key.empty()) + { + verifyKey(LLTranslate::SERVICE_GOOGLE, key); + } +} + +void LLFloaterTranslationSettings::onBtnOK() +{ + gSavedSettings.setBOOL("TranslateChat", mMachineTranslationCB->getValue().asBoolean()); + gSavedSettings.setString("TranslateLanguage", mLanguageCombo->getSelectedValue().asString()); + gSavedSettings.setString("TranslationService", getSelectedService()); + gSavedSettings.setString("BingTranslateAPIKey", getEnteredBingKey()); + gSavedSettings.setString("GoogleTranslateAPIKey", getEnteredGoogleKey()); + closeFloater(false); +} diff --git a/indra/newview/llfloatertranslationsettings.h b/indra/newview/llfloatertranslationsettings.h index 47e2fc80e6..9b47ad72ed 100644 --- a/indra/newview/llfloatertranslationsettings.h +++ b/indra/newview/llfloatertranslationsettings.h @@ -29,6 +29,7 @@ #include "llfloater.h" +class LLButton; class LLCheckBoxCtrl; class LLComboBox; class LLLineEditor; @@ -41,11 +42,22 @@ public: /*virtual*/ BOOL postBuild(); /*virtual*/ void onOpen(const LLSD& key); + void setBingVerified(bool ok, bool alert); + void setGoogleVerified(bool ok, bool alert); + private: std::string getSelectedService() const; - void showError(const std::string& err_name) const; - bool validate() const; + std::string getEnteredBingKey() 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 onEditorFocused(LLFocusableElement* control); + void onBingKeyEdited(); + void onGoogleKeyEdited(); + void onBtnBingVerify(); + void onBtnGoogleVerify(); void onBtnOK(); LLCheckBoxCtrl* mMachineTranslationCB; @@ -53,6 +65,12 @@ private: LLLineEditor* mBingAPIKeyEditor; LLLineEditor* mGoogleAPIKeyEditor; LLRadioGroup* mTranslationServiceRadioGroup; + LLButton* mBingVerifyBtn; + LLButton* mGoogleVerifyBtn; + LLButton* mOKBtn; + + bool mBingKeyVerified; + bool mGoogleKeyVerified; }; #endif // LL_LLFLOATERTRANSLATIONSETTINGS_H diff --git a/indra/newview/lltranslate.cpp b/indra/newview/lltranslate.cpp index a74b252c68..7b99c20a58 100644 --- a/indra/newview/lltranslate.cpp +++ b/indra/newview/lltranslate.cpp @@ -53,6 +53,15 @@ void LLGoogleTranslationHandler::getTranslateURL( } } +// virtual +void LLGoogleTranslationHandler::getKeyVerificationURL( + std::string& url, + const std::string& key) const +{ + url = std::string("https://www.googleapis.com/language/translate/v2/languages?key=") + + key + "&target=en"; +} + // virtual bool LLGoogleTranslationHandler::parseResponse( int& status, @@ -152,6 +161,15 @@ void LLBingTranslarionHandler::getTranslateURL( } } +// virtual +void LLBingTranslarionHandler::getKeyVerificationURL( + std::string& url, + const std::string& key) const +{ + url = std::string("http://api.microsofttranslator.com/v2/Http.svc/GetLanguagesForTranslate?appId=") + + key; +} + // virtual bool LLBingTranslarionHandler::parseResponse( int& status, @@ -251,6 +269,27 @@ void LLTranslate::TranslationReceiver::completedRaw( } } +LLTranslate::KeyVerificationReceiver::KeyVerificationReceiver(EService service) +: mService(service) +{ +} + +LLTranslate::EService LLTranslate::KeyVerificationReceiver::getService() const +{ + return mService; +} + +// virtual +void LLTranslate::KeyVerificationReceiver::completedRaw( + U32 http_status, + const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) +{ + bool ok = (http_status == 200); + setVerificationStatus(ok); +} + //static void LLTranslate::translateMessage( TranslationReceiverPtr &receiver, @@ -261,24 +300,21 @@ void LLTranslate::translateMessage( std::string url; receiver->mHandler.getTranslateURL(url, from_lang, to_lang, mesg); - static const float REQUEST_TIMEOUT = 5; - static LLSD sHeader; - - if (!sHeader.size()) - { - std::string user_agent = llformat("%s %d.%d.%d (%d)", - LLVersionInfo::getChannel().c_str(), - LLVersionInfo::getMajor(), - LLVersionInfo::getMinor(), - LLVersionInfo::getPatch(), - LLVersionInfo::getBuild()); + LL_DEBUGS("Translate") << "Sending translation request: " << url << LL_ENDL; + sendRequest(url, receiver); +} - sHeader.insert("Accept", "text/plain"); - sHeader.insert("User-Agent", user_agent); - } +// static +void LLTranslate::verifyKey( + KeyVerificationReceiverPtr& receiver, + const std::string& key) +{ + std::string url; + const LLTranslationAPIHandler& handler = getHandler(receiver->getService()); + handler.getKeyVerificationURL(url, key); - LL_DEBUGS("Translate") << "Sending translation request: " << url << LL_ENDL; - LLHTTPClient::get(url, receiver, sHeader, REQUEST_TIMEOUT); + LL_DEBUGS("Translate") << "Sending key verification request: " << url << LL_ENDL; + sendRequest(url, receiver); } //static @@ -295,15 +331,50 @@ std::string LLTranslate::getTranslateLanguage() // static const LLTranslationAPIHandler& LLTranslate::getPreferredHandler() +{ + EService service = SERVICE_BING; + + std::string service_str = gSavedSettings.getString("TranslationService"); + if (service_str == "google") + { + service = SERVICE_GOOGLE; + } + + return getHandler(service); +} + +// static +const LLTranslationAPIHandler& LLTranslate::getHandler(EService service) { static LLGoogleTranslationHandler google; static LLBingTranslarionHandler bing; - std::string service = gSavedSettings.getString("TranslationService"); - if (service == "google") + if (service == SERVICE_GOOGLE) { return google; } return bing; } + +// static +void LLTranslate::sendRequest(const std::string& url, LLHTTPClient::ResponderPtr responder) +{ + static const float REQUEST_TIMEOUT = 5; + static LLSD sHeader; + + if (!sHeader.size()) + { + std::string user_agent = llformat("%s %d.%d.%d (%d)", + LLVersionInfo::getChannel().c_str(), + LLVersionInfo::getMajor(), + LLVersionInfo::getMinor(), + LLVersionInfo::getPatch(), + LLVersionInfo::getBuild()); + + sHeader.insert("Accept", "text/plain"); + sHeader.insert("User-Agent", user_agent); + } + + LLHTTPClient::get(url, responder, sHeader, REQUEST_TIMEOUT); +} diff --git a/indra/newview/lltranslate.h b/indra/newview/lltranslate.h index 1bf6965fd4..672a56af8b 100644 --- a/indra/newview/lltranslate.h +++ b/indra/newview/lltranslate.h @@ -44,6 +44,10 @@ public: const std::string &to_lang, const std::string &text) const = 0; + virtual void getKeyVerificationURL( + std::string &url, + const std::string &key) const = 0; + virtual bool parseResponse( int& status, const std::string& body, @@ -67,6 +71,9 @@ public: const std::string &from_lang, const std::string &to_lang, const std::string &text) const; + /*virtual*/ void getKeyVerificationURL( + std::string &url, + const std::string &key) const; /*virtual*/ bool parseResponse( int& status, const std::string& body, @@ -96,6 +103,9 @@ public: const std::string &from_lang, const std::string &to_lang, const std::string &text) const; + /*virtual*/ void getKeyVerificationURL( + std::string &url, + const std::string &key) const; /*virtual*/ bool parseResponse( int& status, const std::string& body, @@ -112,6 +122,12 @@ class LLTranslate LOG_CLASS(LLTranslate); public : + + typedef enum e_service { + SERVICE_BING, + SERVICE_GOOGLE, + } EService; + class TranslationReceiver: public LLHTTPClient::Responder { public: @@ -134,13 +150,34 @@ public : const LLTranslationAPIHandler& mHandler; }; + class KeyVerificationReceiver: public LLHTTPClient::Responder + { + public: + EService getService() const; + + protected: + KeyVerificationReceiver(EService service); + /*virtual*/ void completedRaw( + U32 http_status, + const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer); + virtual void setVerificationStatus(bool ok) = 0; + + EService mService; + }; + typedef boost::intrusive_ptr TranslationReceiverPtr; + typedef boost::intrusive_ptr KeyVerificationReceiverPtr; static void translateMessage(TranslationReceiverPtr &receiver, const std::string &from_lang, const std::string &to_lang, const std::string &mesg); + static void verifyKey(KeyVerificationReceiverPtr& receiver, const std::string& key); static std::string getTranslateLanguage(); private: static const LLTranslationAPIHandler& getPreferredHandler(); + static const LLTranslationAPIHandler& getHandler(EService service); + static void sendRequest(const std::string& url, LLHTTPClient::ResponderPtr responder); }; #endif 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 f21f64fcf6..e13c810820 100644 --- a/indra/newview/skins/default/xui/en/floater_translation_settings.xml +++ b/indra/newview/skins/default/xui/en/floater_translation_settings.xml @@ -1,16 +1,19 @@ + width="480"> - Bing Translator requires and appID to function. - Google Translate requires an API key to function. + Bing appID not verified. Please try again. + Google API key not verified. Please try again. + + Bing appID verified. + Google API key verified. + top_pad="55" /> Bing [http://www.bing.com/developers/createapp.aspx AppID]: +