/indra/newview/lltranslate.cpp

https://bitbucket.org/lindenlab/viewer-beta/ · C++ · 400 lines · 296 code · 52 blank · 52 comment · 34 complexity · 814cad99cda58d0ab5ca5d61a8ca19c8 MD5 · raw file

  1. /**
  2. * @file lltranslate.cpp
  3. * @brief Functions for translating text via Google Translate.
  4. *
  5. * $LicenseInfo:firstyear=2009&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. #include "llviewerprecompiledheaders.h"
  27. #include "lltranslate.h"
  28. #include <curl/curl.h>
  29. #include "llbufferstream.h"
  30. #include "lltrans.h"
  31. #include "llui.h"
  32. #include "llversioninfo.h"
  33. #include "llviewercontrol.h"
  34. #include "reader.h"
  35. // virtual
  36. void LLGoogleTranslationHandler::getTranslateURL(
  37. std::string &url,
  38. const std::string &from_lang,
  39. const std::string &to_lang,
  40. const std::string &text) const
  41. {
  42. url = std::string("https://www.googleapis.com/language/translate/v2?key=")
  43. + getAPIKey() + "&q=" + LLURI::escape(text) + "&target=" + to_lang;
  44. if (!from_lang.empty())
  45. {
  46. url += "&source=" + from_lang;
  47. }
  48. }
  49. // virtual
  50. void LLGoogleTranslationHandler::getKeyVerificationURL(
  51. std::string& url,
  52. const std::string& key) const
  53. {
  54. url = std::string("https://www.googleapis.com/language/translate/v2/languages?key=")
  55. + key + "&target=en";
  56. }
  57. // virtual
  58. bool LLGoogleTranslationHandler::parseResponse(
  59. int& status,
  60. const std::string& body,
  61. std::string& translation,
  62. std::string& detected_lang,
  63. std::string& err_msg) const
  64. {
  65. Json::Value root;
  66. Json::Reader reader;
  67. if (!reader.parse(body, root))
  68. {
  69. err_msg = reader.getFormatedErrorMessages();
  70. return false;
  71. }
  72. if (!root.isObject()) // empty response? should not happen
  73. {
  74. return false;
  75. }
  76. if (status != STATUS_OK)
  77. {
  78. // Request failed. Extract error message from the response.
  79. parseErrorResponse(root, status, err_msg);
  80. return false;
  81. }
  82. // Request succeeded, extract translation from the response.
  83. return parseTranslation(root, translation, detected_lang);
  84. }
  85. // virtual
  86. bool LLGoogleTranslationHandler::isConfigured() const
  87. {
  88. return !getAPIKey().empty();
  89. }
  90. // static
  91. void LLGoogleTranslationHandler::parseErrorResponse(
  92. const Json::Value& root,
  93. int& status,
  94. std::string& err_msg)
  95. {
  96. const Json::Value& error = root.get("error", 0);
  97. if (!error.isObject() || !error.isMember("message") || !error.isMember("code"))
  98. {
  99. return;
  100. }
  101. err_msg = error["message"].asString();
  102. status = error["code"].asInt();
  103. }
  104. // static
  105. bool LLGoogleTranslationHandler::parseTranslation(
  106. const Json::Value& root,
  107. std::string& translation,
  108. std::string& detected_lang)
  109. {
  110. // JsonCpp is prone to aborting the program on failed assertions,
  111. // so be super-careful and verify the response format.
  112. const Json::Value& data = root.get("data", 0);
  113. if (!data.isObject() || !data.isMember("translations"))
  114. {
  115. return false;
  116. }
  117. const Json::Value& translations = data["translations"];
  118. if (!translations.isArray() || translations.size() == 0)
  119. {
  120. return false;
  121. }
  122. const Json::Value& first = translations[0U];
  123. if (!first.isObject() || !first.isMember("translatedText"))
  124. {
  125. return false;
  126. }
  127. translation = first["translatedText"].asString();
  128. detected_lang = first.get("detectedSourceLanguage", "").asString();
  129. return true;
  130. }
  131. // static
  132. std::string LLGoogleTranslationHandler::getAPIKey()
  133. {
  134. return gSavedSettings.getString("GoogleTranslateAPIKey");
  135. }
  136. // virtual
  137. void LLBingTranslationHandler::getTranslateURL(
  138. std::string &url,
  139. const std::string &from_lang,
  140. const std::string &to_lang,
  141. const std::string &text) const
  142. {
  143. url = std::string("http://api.microsofttranslator.com/v2/Http.svc/Translate?appId=")
  144. + getAPIKey() + "&text=" + LLURI::escape(text) + "&to=" + to_lang;
  145. if (!from_lang.empty())
  146. {
  147. url += "&from=" + from_lang;
  148. }
  149. }
  150. // virtual
  151. void LLBingTranslationHandler::getKeyVerificationURL(
  152. std::string& url,
  153. const std::string& key) const
  154. {
  155. url = std::string("http://api.microsofttranslator.com/v2/Http.svc/GetLanguagesForTranslate?appId=")
  156. + key;
  157. }
  158. // virtual
  159. bool LLBingTranslationHandler::parseResponse(
  160. int& status,
  161. const std::string& body,
  162. std::string& translation,
  163. std::string& detected_lang,
  164. std::string& err_msg) const
  165. {
  166. if (status != STATUS_OK)
  167. {
  168. static const std::string MSG_BEGIN_MARKER = "Message: ";
  169. size_t begin = body.find(MSG_BEGIN_MARKER);
  170. if (begin != std::string::npos)
  171. {
  172. begin += MSG_BEGIN_MARKER.size();
  173. }
  174. else
  175. {
  176. begin = 0;
  177. err_msg.clear();
  178. }
  179. size_t end = body.find("</p>", begin);
  180. err_msg = body.substr(begin, end-begin);
  181. LLStringUtil::replaceString(err_msg, "&#xD;", ""); // strip CR
  182. return false;
  183. }
  184. // Sample response: <string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">Hola</string>
  185. size_t begin = body.find(">");
  186. if (begin == std::string::npos || begin >= (body.size() - 1))
  187. {
  188. begin = 0;
  189. }
  190. else
  191. {
  192. ++begin;
  193. }
  194. size_t end = body.find("</string>", begin);
  195. detected_lang = ""; // unsupported by this API
  196. translation = body.substr(begin, end-begin);
  197. LLStringUtil::replaceString(translation, "&#xD;", ""); // strip CR
  198. return true;
  199. }
  200. // virtual
  201. bool LLBingTranslationHandler::isConfigured() const
  202. {
  203. return !getAPIKey().empty();
  204. }
  205. // static
  206. std::string LLBingTranslationHandler::getAPIKey()
  207. {
  208. return gSavedSettings.getString("BingTranslateAPIKey");
  209. }
  210. LLTranslate::TranslationReceiver::TranslationReceiver(const std::string& from_lang, const std::string& to_lang)
  211. : mFromLang(from_lang)
  212. , mToLang(to_lang)
  213. , mHandler(LLTranslate::getPreferredHandler())
  214. {
  215. }
  216. // virtual
  217. void LLTranslate::TranslationReceiver::completedRaw(
  218. U32 http_status,
  219. const std::string& reason,
  220. const LLChannelDescriptors& channels,
  221. const LLIOPipe::buffer_ptr_t& buffer)
  222. {
  223. LLBufferStream istr(channels, buffer.get());
  224. std::stringstream strstrm;
  225. strstrm << istr.rdbuf();
  226. const std::string body = strstrm.str();
  227. std::string translation, detected_lang, err_msg;
  228. int status = http_status;
  229. LL_DEBUGS("Translate") << "HTTP status: " << status << " " << reason << LL_ENDL;
  230. LL_DEBUGS("Translate") << "Response body: " << body << LL_ENDL;
  231. if (mHandler.parseResponse(status, body, translation, detected_lang, err_msg))
  232. {
  233. // Fix up the response
  234. LLStringUtil::replaceString(translation, "&lt;", "<");
  235. LLStringUtil::replaceString(translation, "&gt;",">");
  236. LLStringUtil::replaceString(translation, "&quot;","\"");
  237. LLStringUtil::replaceString(translation, "&#39;","'");
  238. LLStringUtil::replaceString(translation, "&amp;","&");
  239. LLStringUtil::replaceString(translation, "&apos;","'");
  240. handleResponse(translation, detected_lang);
  241. }
  242. else
  243. {
  244. if (err_msg.empty())
  245. {
  246. err_msg = LLTrans::getString("TranslationResponseParseError");
  247. }
  248. llwarns << "Translation request failed: " << err_msg << llendl;
  249. handleFailure(status, err_msg);
  250. }
  251. }
  252. LLTranslate::KeyVerificationReceiver::KeyVerificationReceiver(EService service)
  253. : mService(service)
  254. {
  255. }
  256. LLTranslate::EService LLTranslate::KeyVerificationReceiver::getService() const
  257. {
  258. return mService;
  259. }
  260. // virtual
  261. void LLTranslate::KeyVerificationReceiver::completedRaw(
  262. U32 http_status,
  263. const std::string& reason,
  264. const LLChannelDescriptors& channels,
  265. const LLIOPipe::buffer_ptr_t& buffer)
  266. {
  267. bool ok = (http_status == 200);
  268. setVerificationStatus(ok);
  269. }
  270. //static
  271. void LLTranslate::translateMessage(
  272. TranslationReceiverPtr &receiver,
  273. const std::string &from_lang,
  274. const std::string &to_lang,
  275. const std::string &mesg)
  276. {
  277. std::string url;
  278. receiver->mHandler.getTranslateURL(url, from_lang, to_lang, mesg);
  279. LL_DEBUGS("Translate") << "Sending translation request: " << url << LL_ENDL;
  280. sendRequest(url, receiver);
  281. }
  282. // static
  283. void LLTranslate::verifyKey(
  284. KeyVerificationReceiverPtr& receiver,
  285. const std::string& key)
  286. {
  287. std::string url;
  288. const LLTranslationAPIHandler& handler = getHandler(receiver->getService());
  289. handler.getKeyVerificationURL(url, key);
  290. LL_DEBUGS("Translate") << "Sending key verification request: " << url << LL_ENDL;
  291. sendRequest(url, receiver);
  292. }
  293. //static
  294. std::string LLTranslate::getTranslateLanguage()
  295. {
  296. std::string language = gSavedSettings.getString("TranslateLanguage");
  297. if (language.empty() || language == "default")
  298. {
  299. language = LLUI::getLanguage();
  300. }
  301. language = language.substr(0,2);
  302. return language;
  303. }
  304. // static
  305. bool LLTranslate::isTranslationConfigured()
  306. {
  307. return getPreferredHandler().isConfigured();
  308. }
  309. // static
  310. const LLTranslationAPIHandler& LLTranslate::getPreferredHandler()
  311. {
  312. EService service = SERVICE_BING;
  313. std::string service_str = gSavedSettings.getString("TranslationService");
  314. if (service_str == "google")
  315. {
  316. service = SERVICE_GOOGLE;
  317. }
  318. return getHandler(service);
  319. }
  320. // static
  321. const LLTranslationAPIHandler& LLTranslate::getHandler(EService service)
  322. {
  323. static LLGoogleTranslationHandler google;
  324. static LLBingTranslationHandler bing;
  325. if (service == SERVICE_GOOGLE)
  326. {
  327. return google;
  328. }
  329. return bing;
  330. }
  331. // static
  332. void LLTranslate::sendRequest(const std::string& url, LLHTTPClient::ResponderPtr responder)
  333. {
  334. static const float REQUEST_TIMEOUT = 5;
  335. static LLSD sHeader;
  336. if (!sHeader.size())
  337. {
  338. std::string user_agent = llformat("%s %d.%d.%d (%d)",
  339. LLVersionInfo::getChannel().c_str(),
  340. LLVersionInfo::getMajor(),
  341. LLVersionInfo::getMinor(),
  342. LLVersionInfo::getPatch(),
  343. LLVersionInfo::getBuild());
  344. sHeader.insert("Accept", "text/plain");
  345. sHeader.insert("User-Agent", user_agent);
  346. }
  347. LLHTTPClient::get(url, responder, sHeader, REQUEST_TIMEOUT);
  348. }