PageRenderTime 105ms CodeModel.GetById 40ms app.highlight 38ms RepoModel.GetById 23ms app.codeStats 0ms

/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
 27#include "llviewerprecompiledheaders.h"
 28
 29#include "lltranslate.h"
 30
 31#include <curl/curl.h>
 32
 33#include "llbufferstream.h"
 34#include "lltrans.h"
 35#include "llui.h"
 36#include "llversioninfo.h"
 37#include "llviewercontrol.h"
 38
 39#include "reader.h"
 40
 41// virtual
 42void LLGoogleTranslationHandler::getTranslateURL(
 43	std::string &url,
 44	const std::string &from_lang,
 45	const std::string &to_lang,
 46	const std::string &text) const
 47{
 48	url = std::string("https://www.googleapis.com/language/translate/v2?key=")
 49		+ getAPIKey() + "&q=" + LLURI::escape(text) + "&target=" + to_lang;
 50	if (!from_lang.empty())
 51	{
 52		url += "&source=" + from_lang;
 53	}
 54}
 55
 56// virtual
 57void LLGoogleTranslationHandler::getKeyVerificationURL(
 58	std::string& url,
 59	const std::string& key) const
 60{
 61	url = std::string("https://www.googleapis.com/language/translate/v2/languages?key=")
 62		+ key + "&target=en";
 63}
 64
 65// virtual
 66bool LLGoogleTranslationHandler::parseResponse(
 67	int& status,
 68	const std::string& body,
 69	std::string& translation,
 70	std::string& detected_lang,
 71	std::string& err_msg) const
 72{
 73	Json::Value root;
 74	Json::Reader reader;
 75
 76	if (!reader.parse(body, root))
 77	{
 78		err_msg = reader.getFormatedErrorMessages();
 79		return false;
 80	}
 81
 82	if (!root.isObject()) // empty response? should not happen
 83	{
 84		return false;
 85	}
 86
 87	if (status != STATUS_OK)
 88	{
 89		// Request failed. Extract error message from the response.
 90		parseErrorResponse(root, status, err_msg);
 91		return false;
 92	}
 93
 94	// Request succeeded, extract translation from the response.
 95	return parseTranslation(root, translation, detected_lang);
 96}
 97
 98// virtual
 99bool LLGoogleTranslationHandler::isConfigured() const
100{
101	return !getAPIKey().empty();
102}
103
104// static
105void LLGoogleTranslationHandler::parseErrorResponse(
106	const Json::Value& root,
107	int& status,
108	std::string& err_msg)
109{
110	const Json::Value& error = root.get("error", 0);
111	if (!error.isObject() || !error.isMember("message") || !error.isMember("code"))
112	{
113		return;
114	}
115
116	err_msg = error["message"].asString();
117	status = error["code"].asInt();
118}
119
120// static
121bool LLGoogleTranslationHandler::parseTranslation(
122	const Json::Value& root,
123	std::string& translation,
124	std::string& detected_lang)
125{
126	// JsonCpp is prone to aborting the program on failed assertions,
127	// so be super-careful and verify the response format.
128	const Json::Value& data = root.get("data", 0);
129	if (!data.isObject() || !data.isMember("translations"))
130	{
131		return false;
132	}
133
134	const Json::Value& translations = data["translations"];
135	if (!translations.isArray() || translations.size() == 0)
136	{
137		return false;
138	}
139
140	const Json::Value& first = translations[0U];
141	if (!first.isObject() || !first.isMember("translatedText"))
142	{
143		return false;
144	}
145
146	translation = first["translatedText"].asString();
147	detected_lang = first.get("detectedSourceLanguage", "").asString();
148	return true;
149}
150
151// static
152std::string LLGoogleTranslationHandler::getAPIKey()
153{
154	return gSavedSettings.getString("GoogleTranslateAPIKey");
155}
156
157// virtual
158void LLBingTranslationHandler::getTranslateURL(
159	std::string &url,
160	const std::string &from_lang,
161	const std::string &to_lang,
162	const std::string &text) const
163{
164	url = std::string("http://api.microsofttranslator.com/v2/Http.svc/Translate?appId=")
165		+ getAPIKey() + "&text=" + LLURI::escape(text) + "&to=" + to_lang;
166	if (!from_lang.empty())
167	{
168		url += "&from=" + from_lang;
169	}
170}
171
172// virtual
173void LLBingTranslationHandler::getKeyVerificationURL(
174	std::string& url,
175	const std::string& key) const
176{
177	url = std::string("http://api.microsofttranslator.com/v2/Http.svc/GetLanguagesForTranslate?appId=")
178		+ key;
179}
180
181// virtual
182bool LLBingTranslationHandler::parseResponse(
183	int& status,
184	const std::string& body,
185	std::string& translation,
186	std::string& detected_lang,
187	std::string& err_msg) const
188{
189	if (status != STATUS_OK)
190	{
191		static const std::string MSG_BEGIN_MARKER = "Message: ";
192		size_t begin = body.find(MSG_BEGIN_MARKER);
193		if (begin != std::string::npos)
194		{
195			begin += MSG_BEGIN_MARKER.size();
196		}
197		else
198		{
199			begin = 0;
200			err_msg.clear();
201		}
202		size_t end = body.find("</p>", begin);
203		err_msg = body.substr(begin, end-begin);
204		LLStringUtil::replaceString(err_msg, "&#xD;", ""); // strip CR
205		return false;
206	}
207
208	// Sample response: <string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">Hola</string>
209	size_t begin = body.find(">");
210	if (begin == std::string::npos || begin >= (body.size() - 1))
211	{
212		begin = 0;
213	}
214	else
215	{
216		++begin;
217	}
218
219	size_t end = body.find("</string>", begin);
220
221	detected_lang = ""; // unsupported by this API
222	translation = body.substr(begin, end-begin);
223	LLStringUtil::replaceString(translation, "&#xD;", ""); // strip CR
224	return true;
225}
226
227// virtual
228bool LLBingTranslationHandler::isConfigured() const
229{
230	return !getAPIKey().empty();
231}
232
233// static
234std::string LLBingTranslationHandler::getAPIKey()
235{
236	return gSavedSettings.getString("BingTranslateAPIKey");
237}
238
239LLTranslate::TranslationReceiver::TranslationReceiver(const std::string& from_lang, const std::string& to_lang)
240:	mFromLang(from_lang)
241,	mToLang(to_lang)
242,	mHandler(LLTranslate::getPreferredHandler())
243{
244}
245
246// virtual
247void LLTranslate::TranslationReceiver::completedRaw(
248	U32 http_status,
249	const std::string& reason,
250	const LLChannelDescriptors& channels,
251	const LLIOPipe::buffer_ptr_t& buffer)
252{
253	LLBufferStream istr(channels, buffer.get());
254	std::stringstream strstrm;
255	strstrm << istr.rdbuf();
256
257	const std::string body = strstrm.str();
258	std::string translation, detected_lang, err_msg;
259	int status = http_status;
260	LL_DEBUGS("Translate") << "HTTP status: " << status << " " << reason << LL_ENDL;
261	LL_DEBUGS("Translate") << "Response body: " << body << LL_ENDL;
262	if (mHandler.parseResponse(status, body, translation, detected_lang, err_msg))
263	{
264		// Fix up the response
265		LLStringUtil::replaceString(translation, "&lt;", "<");
266		LLStringUtil::replaceString(translation, "&gt;",">");
267		LLStringUtil::replaceString(translation, "&quot;","\"");
268		LLStringUtil::replaceString(translation, "&#39;","'");
269		LLStringUtil::replaceString(translation, "&amp;","&");
270		LLStringUtil::replaceString(translation, "&apos;","'");
271
272		handleResponse(translation, detected_lang);
273	}
274	else
275	{
276		if (err_msg.empty())
277		{
278			err_msg = LLTrans::getString("TranslationResponseParseError");
279		}
280
281		llwarns << "Translation request failed: " << err_msg << llendl;
282		handleFailure(status, err_msg);
283	}
284}
285
286LLTranslate::KeyVerificationReceiver::KeyVerificationReceiver(EService service)
287:	mService(service)
288{
289}
290
291LLTranslate::EService LLTranslate::KeyVerificationReceiver::getService() const
292{
293	return mService;
294}
295
296// virtual
297void LLTranslate::KeyVerificationReceiver::completedRaw(
298	U32 http_status,
299	const std::string& reason,
300	const LLChannelDescriptors& channels,
301	const LLIOPipe::buffer_ptr_t& buffer)
302{
303	bool ok = (http_status == 200);
304	setVerificationStatus(ok);
305}
306
307//static
308void LLTranslate::translateMessage(
309	TranslationReceiverPtr &receiver,
310	const std::string &from_lang,
311	const std::string &to_lang,
312	const std::string &mesg)
313{
314	std::string url;
315	receiver->mHandler.getTranslateURL(url, from_lang, to_lang, mesg);
316
317	LL_DEBUGS("Translate") << "Sending translation request: " << url << LL_ENDL;
318	sendRequest(url, receiver);
319}
320
321// static
322void LLTranslate::verifyKey(
323	KeyVerificationReceiverPtr& receiver,
324	const std::string& key)
325{
326	std::string url;
327	const LLTranslationAPIHandler& handler = getHandler(receiver->getService());
328	handler.getKeyVerificationURL(url, key);
329
330	LL_DEBUGS("Translate") << "Sending key verification request: " << url << LL_ENDL;
331	sendRequest(url, receiver);
332}
333
334//static
335std::string LLTranslate::getTranslateLanguage()
336{
337	std::string language = gSavedSettings.getString("TranslateLanguage");
338	if (language.empty() || language == "default")
339	{
340		language = LLUI::getLanguage();
341	}
342	language = language.substr(0,2);
343	return language;
344}
345
346// static
347bool LLTranslate::isTranslationConfigured()
348{
349	return getPreferredHandler().isConfigured();
350}
351
352// static
353const LLTranslationAPIHandler& LLTranslate::getPreferredHandler()
354{
355	EService service = SERVICE_BING;
356
357	std::string service_str = gSavedSettings.getString("TranslationService");
358	if (service_str == "google")
359	{
360		service = SERVICE_GOOGLE;
361	}
362
363	return getHandler(service);
364}
365
366// static
367const LLTranslationAPIHandler& LLTranslate::getHandler(EService service)
368{
369	static LLGoogleTranslationHandler google;
370	static LLBingTranslationHandler bing;
371
372	if (service == SERVICE_GOOGLE)
373	{
374		return google;
375	}
376
377	return bing;
378}
379
380// static
381void LLTranslate::sendRequest(const std::string& url, LLHTTPClient::ResponderPtr responder)
382{
383	static const float REQUEST_TIMEOUT = 5;
384	static LLSD sHeader;
385
386	if (!sHeader.size())
387	{
388	    std::string user_agent = llformat("%s %d.%d.%d (%d)",
389			LLVersionInfo::getChannel().c_str(),
390			LLVersionInfo::getMajor(),
391			LLVersionInfo::getMinor(),
392			LLVersionInfo::getPatch(),
393			LLVersionInfo::getBuild());
394
395		sHeader.insert("Accept", "text/plain");
396		sHeader.insert("User-Agent", user_agent);
397	}
398
399	LLHTTPClient::get(url, responder, sHeader, REQUEST_TIMEOUT);
400}