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/app_settings/settings.xml | 33 ++++ indra/newview/lltranslate.cpp | 312 ++++++++++++++++++++++++++------ indra/newview/lltranslate.h | 87 ++------- indra/newview/llviewermessage.cpp | 7 +- 4 files changed, 316 insertions(+), 123 deletions(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 0996f75fbb..2549538df2 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -10922,6 +10922,39 @@ Value 0 + TranslationService + + Comment + Translation API to use. (google_v1|google_v2|bing) + Persist + 1 + Type + String + Value + google_v1 + + GoogleTranslateAPIv2Key + + Comment + Google Translate API v2 key + Persist + 1 + Type + String + Value + + + BingTranslateAPIKey + + Comment + Bing AppID to use with the Microsoft Translator V2 API + Persist + 1 + Type + String + Value + + TutorialURL Comment 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; +} diff --git a/indra/newview/lltranslate.h b/indra/newview/lltranslate.h index e85a42e878..1dee792f7b 100644 --- a/indra/newview/lltranslate.h +++ b/indra/newview/lltranslate.h @@ -30,89 +30,42 @@ #include "llhttpclient.h" #include "llbufferstream.h" +class LLTranslationAPIHandler; + class LLTranslate { LOG_CLASS(LLTranslate); + public : class TranslationReceiver: public LLHTTPClient::Responder { - protected: - TranslationReceiver(const std::string &from_lang, const std::string &to_lang) - : m_fromLang(from_lang), - m_toLang(to_lang) - { - } - - virtual void handleResponse(const std::string &translation, const std::string &recognized_lang) {}; - virtual void handleFailure() {}; - public: - ~TranslationReceiver() - { - } - - virtual void completedRaw( U32 status, - const std::string& reason, - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) - { - if (200 <= status && status < 300) - { - LLBufferStream istr(channels, buffer.get()); - std::stringstream strstrm; - strstrm << istr.rdbuf(); + /*virtual*/ void completedRaw( + U32 http_status, + const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer); - const std::string result = strstrm.str(); - std::string translation; - std::string detected_language; + protected: + friend class LLTranslate; - if (!parseGoogleTranslate(result, translation, detected_language)) - { - handleFailure(); - return; - } - - // Fix up the response - LLStringUtil::replaceString(translation, "<", "<"); - LLStringUtil::replaceString(translation, ">",">"); - LLStringUtil::replaceString(translation, ""","\""); - LLStringUtil::replaceString(translation, "'","'"); - LLStringUtil::replaceString(translation, "&","&"); - LLStringUtil::replaceString(translation, "'","'"); + TranslationReceiver(const std::string& from_lang, const std::string& to_lang); - handleResponse(translation, detected_language); - } - else - { - LL_WARNS("Translate") << "HTTP request for Google Translate failed with status " << status << ", reason: " << reason << LL_ENDL; - handleFailure(); - } - } + virtual void handleResponse(const std::string &translation, const std::string &recognized_lang) = 0; + virtual void handleFailure(int status, const std::string& err_msg) = 0; - protected: - const std::string m_toLang; - const std::string m_fromLang; + std::string mFromLang; + std::string mToLang; + const LLTranslationAPIHandler& mHandler; }; - static void translateMessage(LLHTTPClient::ResponderPtr &result, const std::string &from_lang, const std::string &to_lang, const std::string &mesg); - static float m_GoogleTimeout; + typedef boost::intrusive_ptr TranslationReceiverPtr; + + static void translateMessage(TranslationReceiverPtr &receiver, const std::string &from_lang, const std::string &to_lang, const std::string &mesg); static std::string getTranslateLanguage(); private: - static void getTranslateUrl(std::string &translate_url, const std::string &from_lang, const std::string &to_lang, const std::string &text); - static bool parseGoogleTranslate(const std::string& body, std::string &translation, std::string &detected_language); - - static LLSD m_Header; - static const char* m_GoogleURL; - static const char* m_GoogleLangSpec; - static const char* m_AcceptHeader; - static const char* m_AcceptType; - static const char* m_AgentHeader; - static const char* m_UserAgent; - - static const char* m_GoogleData; - static const char* m_GoogleTranslation; - static const char* m_GoogleLanguage; + static const LLTranslationAPIHandler& getPreferredHandler(); }; #endif diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 68745d5aeb..ff02214194 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -3138,7 +3138,7 @@ protected: { // filter out non-interesting responeses if ( !translation.empty() - && (m_toLang != detected_language) + && (mToLang != detected_language) && (LLStringUtil::compareInsensitive(translation, m_origMesg) != 0) ) { m_chat.mText += " (" + translation + ")"; @@ -3147,9 +3147,8 @@ protected: LLNotificationsUI::LLNotificationManager::instance().onChat(m_chat, m_toastArgs); } - void handleFailure() + void handleFailure(int status, const std::string& err_msg) { - LLTranslate::TranslationReceiver::handleFailure(); m_chat.mText += " (?)"; LLNotificationsUI::LLNotificationManager::instance().onChat(m_chat, m_toastArgs); @@ -3388,7 +3387,7 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) const std::string from_lang = ""; // leave empty to trigger autodetect const std::string to_lang = LLTranslate::getTranslateLanguage(); - LLHTTPClient::ResponderPtr result = ChatTranslationReceiver::build(from_lang, to_lang, mesg, chat, args); + LLTranslate::TranslationReceiverPtr result = ChatTranslationReceiver::build(from_lang, to_lang, mesg, chat, args); LLTranslate::translateMessage(result, from_lang, to_lang, mesg); } else -- cgit v1.2.3 From e23ecf311c729be7e6611ef2fe21badaf9f1c3ed Mon Sep 17 00:00:00 2001 From: Vadim ProductEngine Date: Wed, 7 Sep 2011 00:09:49 +0300 Subject: STORM-1577 WIP Implemented chat translation preferences management. --- indra/newview/CMakeLists.txt | 2 + indra/newview/llfloaterpreference.cpp | 9 + indra/newview/llfloaterpreference.h | 1 + indra/newview/llfloatertranslationsettings.cpp | 146 ++++++++++++++ indra/newview/llfloatertranslationsettings.h | 59 ++++++ indra/newview/llviewerfloaterreg.cpp | 2 + .../xui/en/floater_translation_settings.xml | 222 +++++++++++++++++++++ .../default/xui/en/panel_preferences_chat.xml | 12 ++ 8 files changed, 453 insertions(+) create mode 100644 indra/newview/llfloatertranslationsettings.cpp create mode 100644 indra/newview/llfloatertranslationsettings.h create mode 100644 indra/newview/skins/default/xui/en/floater_translation_settings.xml diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index a117d9a593..e7ca2a4294 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -237,6 +237,7 @@ set(viewer_SOURCE_FILES llfloatertools.cpp llfloatertopobjects.cpp llfloatertos.cpp + llfloatertranslationsettings.cpp llfloateruipreview.cpp llfloaterurlentry.cpp llfloatervoiceeffect.cpp @@ -799,6 +800,7 @@ set(viewer_HEADER_FILES llfloatertools.h llfloatertopobjects.h llfloatertos.h + llfloatertranslationsettings.h llfloateruipreview.h llfloaterurlentry.h llfloatervoiceeffect.h diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index d65928e385..07c07d608a 100755 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -345,6 +345,7 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key) mCommitCallbackRegistrar.add("Pref.MaturitySettings", boost::bind(&LLFloaterPreference::onChangeMaturity, this)); mCommitCallbackRegistrar.add("Pref.BlockList", boost::bind(&LLFloaterPreference::onClickBlockList, this)); mCommitCallbackRegistrar.add("Pref.Proxy", boost::bind(&LLFloaterPreference::onClickProxySettings, this)); + mCommitCallbackRegistrar.add("Pref.TranslationSettings", boost::bind(&LLFloaterPreference::onClickTranslationSettings, this)); sSkin = gSavedSettings.getString("SkinCurrent"); @@ -602,6 +603,9 @@ void LLFloaterPreference::cancel() } // hide joystick pref floater LLFloaterReg::hideInstance("pref_joystick"); + + // hide translation settings floater + LLFloaterReg::hideInstance("prefs_translation"); // cancel hardware menu LLFloaterHardwareSettings* hardware_settings = LLFloaterReg::getTypedInstance("prefs_hardware_settings"); @@ -1553,6 +1557,11 @@ void LLFloaterPreference::onClickProxySettings() LLFloaterReg::showInstance("prefs_proxy"); } +void LLFloaterPreference::onClickTranslationSettings() +{ + LLFloaterReg::showInstance("prefs_translation"); +} + void LLFloaterPreference::updateDoubleClickControls() { // check is one of double-click actions settings enabled diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index ef9bc2dd53..ee6bb235be 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -157,6 +157,7 @@ public: void onChangeMaturity(); void onClickBlockList(); void onClickProxySettings(); + void onClickTranslationSettings(); void applyUIColor(LLUICtrl* ctrl, const LLSD& param); void getUIColor(LLUICtrl* ctrl, const LLSD& param); diff --git a/indra/newview/llfloatertranslationsettings.cpp b/indra/newview/llfloatertranslationsettings.cpp new file mode 100644 index 0000000000..56f101d149 --- /dev/null +++ b/indra/newview/llfloatertranslationsettings.cpp @@ -0,0 +1,146 @@ +/** + * @file llfloatertranslationsettings.cpp + * @brief Machine translation settings for chat + * + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloatertranslationsettings.h" + +// Viewer includes +#include "llviewercontrol.h" // for gSavedSettings + +// Linden library includes +#include "llbutton.h" +#include "llcheckboxctrl.h" +#include "llcombobox.h" +#include "llfloaterreg.h" +#include "lllineeditor.h" +#include "llnotificationsutil.h" +#include "llradiogroup.h" + +LLFloaterTranslationSettings::LLFloaterTranslationSettings(const LLSD& key) +: LLFloater(key) +, mMachineTranslationCB(NULL) +, mLanguageCombo(NULL) +, mTranslationServiceRadioGroup(NULL) +, mBingAPIKeyEditor(NULL) +, mGoogleAPIKeyEditor(NULL) +{ +} + +// virtual +BOOL LLFloaterTranslationSettings::postBuild() +{ + mMachineTranslationCB = getChild("translate_chat_checkbox"); + mLanguageCombo = getChild("translate_language_combo"); + mTranslationServiceRadioGroup = getChild("translation_service_rg"); + mBingAPIKeyEditor = getChild("bing_api_key"); + mGoogleAPIKeyEditor = getChild("google_api_key"); + + mMachineTranslationCB->setCommitCallback(boost::bind(&LLFloaterTranslationSettings::updateControlsEnabledState, this)); + mTranslationServiceRadioGroup->setCommitCallback(boost::bind(&LLFloaterTranslationSettings::updateControlsEnabledState, this)); + getChild("ok_btn")->setClickedCallback(boost::bind(&LLFloaterTranslationSettings::onBtnOK, this)); + getChild("cancel_btn")->setClickedCallback(boost::bind(&LLFloater::closeFloater, this, false)); + + center(); + return TRUE; +} + +// virtual +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("GoogleTranslateAPIv2Key")); + + updateControlsEnabledState(); +} + +std::string LLFloaterTranslationSettings::getSelectedService() const +{ + return mTranslationServiceRadioGroup->getSelectedValue().asString(); +} + +void LLFloaterTranslationSettings::showError(const std::string& err_name) +{ + LLSD args; + args["MESSAGE"] = getString(err_name); + LLNotificationsUtil::add("GenericAlert", args); +} + +bool LLFloaterTranslationSettings::validate() +{ + bool translate_chat = mMachineTranslationCB->getValue().asBoolean(); + if (!translate_chat) return true; + + std::string service = getSelectedService(); + if (service == "bing" && mBingAPIKeyEditor->getText().empty()) + { + showError("no_bing_api_key"); + return false; + } + + if (service == "google_v2" && mGoogleAPIKeyEditor->getText().empty()) + { + showError("no_google_api_key"); + return false; + } + + return true; +} + +void LLFloaterTranslationSettings::updateControlsEnabledState() +{ + // Enable/disable controls based on the checkbox value. + bool on = mMachineTranslationCB->getValue().asBoolean(); + std::string service = getSelectedService(); + + mTranslationServiceRadioGroup->setEnabled(on); + mLanguageCombo->setEnabled(on); + + getChild("bing_api_key_label")->setEnabled(on); + mBingAPIKeyEditor->setEnabled(on); + + getChild("google_api_key_label")->setEnabled(on); + mGoogleAPIKeyEditor->setEnabled(on); + + mBingAPIKeyEditor->setEnabled(service == "bing"); + mGoogleAPIKeyEditor->setEnabled(service == "google_v2"); +} + +void LLFloaterTranslationSettings::onBtnOK() +{ + if (validate()) + { + gSavedSettings.setBOOL("TranslateChat", mMachineTranslationCB->getValue().asBoolean()); + gSavedSettings.setString("TranslateLanguage", mLanguageCombo->getSelectedValue().asString()); + gSavedSettings.setString("TranslationService", getSelectedService()); + gSavedSettings.setString("BingTranslateAPIKey", mBingAPIKeyEditor->getText()); + gSavedSettings.setString("GoogleTranslateAPIv2Key", mGoogleAPIKeyEditor->getText()); + closeFloater(false); + } +} diff --git a/indra/newview/llfloatertranslationsettings.h b/indra/newview/llfloatertranslationsettings.h new file mode 100644 index 0000000000..1c03b86f4d --- /dev/null +++ b/indra/newview/llfloatertranslationsettings.h @@ -0,0 +1,59 @@ +/** + * @file llfloatertranslationsettings.h + * @brief Machine translation settings for chat + * + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLFLOATERTRANSLATIONSETTINGS_H +#define LL_LLFLOATERTRANSLATIONSETTINGS_H + +#include "llfloater.h" + +class LLCheckBoxCtrl; +class LLComboBox; +class LLLineEditor; +class LLRadioGroup; + +class LLFloaterTranslationSettings : public LLFloater +{ +public: + LLFloaterTranslationSettings(const LLSD& key); + /*virtual*/ BOOL postBuild(); + /*virtual*/ void onOpen(const LLSD& key); + +private: + std::string getSelectedService() const; + void showError(const std::string& err_name); + bool validate(); + void updateControlsEnabledState(); + void onMachineTranslationToggle(); + void onBtnOK(); + + LLCheckBoxCtrl* mMachineTranslationCB; + LLComboBox* mLanguageCombo; + LLLineEditor* mBingAPIKeyEditor; + LLLineEditor* mGoogleAPIKeyEditor; + LLRadioGroup* mTranslationServiceRadioGroup; +}; + +#endif // LL_LLFLOATERTRANSLATIONSETTINGS_H diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index fecc6d91bd..3be26d87e2 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -102,6 +102,7 @@ #include "llfloatertools.h" #include "llfloatertos.h" #include "llfloatertopobjects.h" +#include "llfloatertranslationsettings.h" #include "llfloateruipreview.h" #include "llfloatervoiceeffect.h" #include "llfloaterwhitelistentry.h" @@ -234,6 +235,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("preferences", "floater_preferences.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("prefs_proxy", "floater_preferences_proxy.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("prefs_hardware_settings", "floater_hardware_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("prefs_translation", "floater_translation_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("perm_prefs", "floater_perm_prefs.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("pref_joystick", "floater_joystick.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("preview_anim", "floater_preview_animation.xml", (LLFloaterBuildFunc)&LLFloaterReg::build, "preview"); diff --git a/indra/newview/skins/default/xui/en/floater_translation_settings.xml b/indra/newview/skins/default/xui/en/floater_translation_settings.xml new file mode 100644 index 0000000000..40a176830c --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_translation_settings.xml @@ -0,0 +1,222 @@ + + + + Bing Translator requires and appID to function. + Google Translate requires an API key to function. + + + + Translate chat into: + + + + + + + + + + + + + + + + + + + + + + + + Choose translation service to use: + + + + + + + + + Bing [http://www.bing.com/developers/createapp.aspx AppID]: + + + + + Google [http://code.google.com/apis/language/translate/v2/pricing.html API key]: + + + + + ([http://code.google.com/apis/language/translate/v2/pricing.html pricing]) + + + \ No newline at end of file -- cgit v1.2.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(-) 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.2.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(-) 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.2.3 From ef01821337a0dc428fd090ae94c8cc9d9a13bdb5 Mon Sep 17 00:00:00 2001 From: Vadim ProductEngine Date: Wed, 7 Sep 2011 19:29:57 +0300 Subject: STORM-1577 WIP Removed old translation settings controls. They are now superceded with a separate floater. --- .../default/xui/en/panel_preferences_chat.xml | 119 +-------------------- 1 file changed, 2 insertions(+), 117 deletions(-) diff --git a/indra/newview/skins/default/xui/en/panel_preferences_chat.xml b/indra/newview/skins/default/xui/en/panel_preferences_chat.xml index 3fbf484ab2..28db34f4d4 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_chat.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_chat.xml @@ -204,129 +204,14 @@ name="nearby_toasts_fadingtime" top_pad="3" width="325" /> - - - - - Use machine translation while chatting (powered by Google) - - - Translate chat into: - - - - - - - - - - - - - - - - - - - - - -- cgit v1.2.3 From 56b2e4ac7c7cc4f27f08b4024ecbeace4c3a3e51 Mon Sep 17 00:00:00 2001 From: Vadim ProductEngine Date: Tue, 25 Oct 2011 16:36:27 +0200 Subject: STORM-1577 WIP Indented floater contents. --- .../default/xui/en/floater_translation_settings.xml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) 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 1eb75e40f3..40fdaaed66 100644 --- a/indra/newview/skins/default/xui/en/floater_translation_settings.xml +++ b/indra/newview/skins/default/xui/en/floater_translation_settings.xml @@ -7,7 +7,7 @@ help_topic="environment_editor_floater" save_rect="true" title="CHAT TRANSLATION SETTINGS" - width="480"> + width="485"> Bing appID not verified. Please try again. Google API key not verified. Please try again. @@ -27,7 +27,7 @@ height="20" follows="left|top" layout="topleft" - left="10" + left="40" name="translate_language_label" top_pad="20" width="130"> @@ -118,7 +118,7 @@ follows="top|left|right" height="15" layout="topleft" - left="10" + left="40" name="tip" top_pad="20" width="330" @@ -153,10 +153,10 @@ follows="top|right" height="20" layout="topleft" - left="40" + left="70" name="bing_api_key_label" top_pad="-55" - width="100"> + width="85"> Bing [http://www.bing.com/developers/createapp.aspx AppID]: + width="210" />