1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/translate/core/browser/translate_manager.h"
8 #include "base/command_line.h"
9 #include "base/metrics/field_trial.h"
10 #include "base/metrics/histogram.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/strings/string_split.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/time/time.h"
15 #include "components/translate/core/browser/language_state.h"
16 #include "components/translate/core/browser/page_translated_details.h"
17 #include "components/translate/core/browser/translate_accept_languages.h"
18 #include "components/translate/core/browser/translate_browser_metrics.h"
19 #include "components/translate/core/browser/translate_client.h"
20 #include "components/translate/core/browser/translate_download_manager.h"
21 #include "components/translate/core/browser/translate_driver.h"
22 #include "components/translate/core/browser/translate_error_details.h"
23 #include "components/translate/core/browser/translate_language_list.h"
24 #include "components/translate/core/browser/translate_prefs.h"
25 #include "components/translate/core/browser/translate_script.h"
26 #include "components/translate/core/browser/translate_url_util.h"
27 #include "components/translate/core/common/language_detection_details.h"
28 #include "components/translate/core/common/translate_constants.h"
29 #include "components/translate/core/common/translate_pref_names.h"
30 #include "components/translate/core/common/translate_switches.h"
31 #include "net/base/url_util.h"
32 #include "net/http/http_status_code.h"
36 // Callbacks for translate errors.
37 TranslateManager::TranslateErrorCallbackList* g_callback_list_ = NULL;
39 const char kReportLanguageDetectionErrorURL[] =
40 "https://translate.google.com/translate_error?client=cr&action=langidc";
42 // Used in kReportLanguageDetectionErrorURL to specify the original page
44 const char kSourceLanguageQueryName[] = "sl";
46 // Used in kReportLanguageDetectionErrorURL to specify the page URL.
47 const char kUrlQueryName[] = "u";
49 // Notifies |g_callback_list_| of translate errors.
50 void NotifyTranslateError(const TranslateErrorDetails& details) {
51 if (!g_callback_list_)
54 g_callback_list_->Notify(details);
59 TranslateManager::~TranslateManager() {}
62 scoped_ptr<TranslateManager::TranslateErrorCallbackList::Subscription>
63 TranslateManager::RegisterTranslateErrorCallback(
64 const TranslateManager::TranslateErrorCallback& callback) {
65 if (!g_callback_list_)
66 g_callback_list_ = new TranslateErrorCallbackList;
67 return g_callback_list_->Add(callback);
70 TranslateManager::TranslateManager(
71 TranslateClient* translate_client,
72 const std::string& accept_languages_pref_name)
73 : accept_languages_pref_name_(accept_languages_pref_name),
74 translate_client_(translate_client),
75 translate_driver_(translate_client_->GetTranslateDriver()),
76 weak_method_factory_(this) {}
78 void TranslateManager::InitiateTranslation(const std::string& page_lang) {
79 // Short-circuit out if not in a state where initiating translation makes
80 // sense (this method may be called muhtiple times for a given page).
81 LanguageState& language_state = translate_driver_->GetLanguageState();
82 if (!language_state.page_needs_translation() ||
83 language_state.translation_pending() ||
84 language_state.translation_declined() ||
85 language_state.IsPageTranslated()) {
89 PrefService* prefs = translate_client_->GetPrefs();
90 if (!prefs->GetBoolean(prefs::kEnableTranslate)) {
91 TranslateBrowserMetrics::ReportInitiationStatus(
92 TranslateBrowserMetrics::INITIATION_STATUS_DISABLED_BY_PREFS);
93 const std::string& locale =
94 TranslateDownloadManager::GetInstance()->application_locale();
95 TranslateBrowserMetrics::ReportLocalesOnDisabledByPrefs(locale);
99 // Allow disabling of translate from the command line to assist with
100 // automated browser testing.
101 if (CommandLine::ForCurrentProcess()->HasSwitch(
102 translate::switches::kDisableTranslate)) {
103 TranslateBrowserMetrics::ReportInitiationStatus(
104 TranslateBrowserMetrics::INITIATION_STATUS_DISABLED_BY_SWITCH);
108 // MHTML pages currently cannot be translated.
110 if (translate_driver_->GetContentsMimeType() == "multipart/related") {
111 TranslateBrowserMetrics::ReportInitiationStatus(
112 TranslateBrowserMetrics::INITIATION_STATUS_MIME_TYPE_IS_NOT_SUPPORTED);
116 // Don't translate any Chrome specific page, e.g., New Tab Page, Download,
117 // History, and so on.
118 const GURL& page_url = translate_driver_->GetVisibleURL();
119 if (!translate_client_->IsTranslatableURL(page_url)) {
120 TranslateBrowserMetrics::ReportInitiationStatus(
121 TranslateBrowserMetrics::INITIATION_STATUS_URL_IS_NOT_SUPPORTED);
125 // Get the accepted languages list.
126 std::vector<std::string> accept_languages_list;
127 base::SplitString(prefs->GetString(accept_languages_pref_name_.c_str()), ',',
128 &accept_languages_list);
130 std::string target_lang = GetTargetLanguage(accept_languages_list);
131 std::string language_code =
132 TranslateDownloadManager::GetLanguageCode(page_lang);
134 // Don't translate similar languages (ex: en-US to en).
135 if (language_code == target_lang) {
136 TranslateBrowserMetrics::ReportInitiationStatus(
137 TranslateBrowserMetrics::INITIATION_STATUS_SIMILAR_LANGUAGES);
141 // Nothing to do if either the language Chrome is in or the language of the
142 // page is not supported by the translation server.
143 if (target_lang.empty() ||
144 !TranslateDownloadManager::IsSupportedLanguage(language_code)) {
145 TranslateBrowserMetrics::ReportInitiationStatus(
146 TranslateBrowserMetrics::INITIATION_STATUS_LANGUAGE_IS_NOT_SUPPORTED);
147 TranslateBrowserMetrics::ReportUnsupportedLanguageAtInitiation(
152 scoped_ptr<TranslatePrefs> translate_prefs(
153 translate_client_->GetTranslatePrefs());
155 TranslateAcceptLanguages* accept_languages =
156 translate_client_->GetTranslateAcceptLanguages();
157 // Don't translate any user black-listed languages.
158 if (!translate_prefs->CanTranslateLanguage(accept_languages,
160 TranslateBrowserMetrics::ReportInitiationStatus(
161 TranslateBrowserMetrics::INITIATION_STATUS_DISABLED_BY_CONFIG);
165 // Don't translate any user black-listed URLs.
166 if (translate_prefs->IsSiteBlacklisted(page_url.HostNoBrackets())) {
167 TranslateBrowserMetrics::ReportInitiationStatus(
168 TranslateBrowserMetrics::INITIATION_STATUS_DISABLED_BY_CONFIG);
172 // If the user has previously selected "always translate" for this language we
173 // automatically translate. Note that in incognito mode we disable that
174 // feature; the user will get an infobar, so they can control whether the
175 // page's text is sent to the translate server.
176 if (!translate_driver_->IsOffTheRecord()) {
177 scoped_ptr<TranslatePrefs> translate_prefs =
178 translate_client_->GetTranslatePrefs();
179 std::string auto_target_lang =
180 GetAutoTargetLanguage(language_code, translate_prefs.get());
181 if (!auto_target_lang.empty()) {
182 TranslateBrowserMetrics::ReportInitiationStatus(
183 TranslateBrowserMetrics::INITIATION_STATUS_AUTO_BY_CONFIG);
184 TranslatePage(language_code, auto_target_lang, false);
189 std::string auto_translate_to = language_state.AutoTranslateTo();
190 if (!auto_translate_to.empty()) {
191 // This page was navigated through a click from a translated page.
192 TranslateBrowserMetrics::ReportInitiationStatus(
193 TranslateBrowserMetrics::INITIATION_STATUS_AUTO_BY_LINK);
194 TranslatePage(language_code, auto_translate_to, false);
198 TranslateBrowserMetrics::ReportInitiationStatus(
199 TranslateBrowserMetrics::INITIATION_STATUS_SHOW_INFOBAR);
201 // Prompts the user if he/she wants the page translated.
202 translate_client_->ShowTranslateUI(translate::TRANSLATE_STEP_BEFORE_TRANSLATE,
205 TranslateErrors::NONE,
209 void TranslateManager::TranslatePage(const std::string& original_source_lang,
210 const std::string& target_lang,
211 bool triggered_from_menu) {
212 if (!translate_driver_->HasCurrentPage()) {
217 // Translation can be kicked by context menu against unsupported languages.
218 // Unsupported language strings should be replaced with
219 // kUnknownLanguageCode in order to send a translation request with enabling
220 // server side auto language detection.
221 std::string source_lang(original_source_lang);
222 if (!TranslateDownloadManager::IsSupportedLanguage(source_lang))
223 source_lang = std::string(translate::kUnknownLanguageCode);
225 translate_client_->ShowTranslateUI(translate::TRANSLATE_STEP_TRANSLATING,
228 TranslateErrors::NONE,
229 triggered_from_menu);
231 TranslateScript* script = TranslateDownloadManager::GetInstance()->script();
232 DCHECK(script != NULL);
234 const std::string& script_data = script->data();
235 if (!script_data.empty()) {
236 DoTranslatePage(script_data, source_lang, target_lang);
240 // The script is not available yet. Queue that request and query for the
241 // script. Once it is downloaded we'll do the translate.
242 TranslateScript::RequestCallback callback =
243 base::Bind(&TranslateManager::OnTranslateScriptFetchComplete,
244 weak_method_factory_.GetWeakPtr(),
245 translate_driver_->GetCurrentPageID(),
249 script->Request(callback);
252 void TranslateManager::RevertTranslation() {
253 translate_driver_->RevertTranslation();
254 translate_driver_->GetLanguageState().SetCurrentLanguage(
255 translate_driver_->GetLanguageState().original_language());
258 void TranslateManager::ReportLanguageDetectionError() {
259 TranslateBrowserMetrics::ReportLanguageDetectionError();
261 GURL report_error_url = GURL(kReportLanguageDetectionErrorURL);
264 net::AppendQueryParameter(report_error_url,
266 translate_driver_->GetActiveURL().spec());
268 report_error_url = net::AppendQueryParameter(
270 kSourceLanguageQueryName,
271 translate_driver_->GetLanguageState().original_language());
273 report_error_url = TranslateURLUtil::AddHostLocaleToUrl(report_error_url);
274 report_error_url = TranslateURLUtil::AddApiKeyToUrl(report_error_url);
276 translate_client_->ShowReportLanguageDetectionErrorUI(report_error_url);
279 void TranslateManager::DoTranslatePage(const std::string& translate_script,
280 const std::string& source_lang,
281 const std::string& target_lang) {
282 translate_driver_->GetLanguageState().set_translation_pending(true);
283 translate_driver_->TranslatePage(translate_script, source_lang, target_lang);
286 void TranslateManager::PageTranslated(const std::string& source_lang,
287 const std::string& target_lang,
288 TranslateErrors::Type error_type) {
289 translate_driver_->GetLanguageState().SetCurrentLanguage(target_lang);
290 translate_driver_->GetLanguageState().set_translation_pending(false);
292 if ((error_type == TranslateErrors::NONE) &&
293 source_lang != translate::kUnknownLanguageCode &&
294 !TranslateDownloadManager::IsSupportedLanguage(source_lang)) {
295 error_type = TranslateErrors::UNSUPPORTED_LANGUAGE;
298 translate_client_->ShowTranslateUI(translate::TRANSLATE_STEP_AFTER_TRANSLATE,
304 if (error_type != TranslateErrors::NONE &&
305 !translate_driver_->IsOffTheRecord()) {
306 TranslateErrorDetails error_details;
307 error_details.time = base::Time::Now();
308 error_details.url = translate_driver_->GetLastCommittedURL();
309 error_details.error = error_type;
310 NotifyTranslateError(error_details);
314 void TranslateManager::OnTranslateScriptFetchComplete(
316 const std::string& source_lang,
317 const std::string& target_lang,
319 const std::string& data) {
320 if (!translate_driver_->HasCurrentPage() ||
321 translate_driver_->GetCurrentPageID() != page_id) {
322 // We navigated away from the page the translation was triggered on.
327 // Translate the page.
328 TranslateScript* translate_script =
329 TranslateDownloadManager::GetInstance()->script();
330 DCHECK(translate_script);
331 DoTranslatePage(translate_script->data(), source_lang, target_lang);
333 translate_client_->ShowTranslateUI(
334 translate::TRANSLATE_STEP_TRANSLATE_ERROR,
337 TranslateErrors::NETWORK,
339 if (!translate_driver_->IsOffTheRecord()) {
340 TranslateErrorDetails error_details;
341 error_details.time = base::Time::Now();
342 error_details.url = translate_driver_->GetActiveURL();
343 error_details.error = TranslateErrors::NETWORK;
344 NotifyTranslateError(error_details);
350 std::string TranslateManager::GetTargetLanguage(
351 const std::vector<std::string>& accept_languages_list) {
352 std::string ui_lang = TranslatePrefs::ConvertLangCodeForTranslation(
353 TranslateDownloadManager::GetLanguageCode(
354 TranslateDownloadManager::GetInstance()->application_locale()));
356 if (TranslateDownloadManager::IsSupportedLanguage(ui_lang))
359 // Will translate to the first supported language on the Accepted Language
360 // list or not at all if no such candidate exists
361 std::vector<std::string>::const_iterator iter;
362 for (iter = accept_languages_list.begin();
363 iter != accept_languages_list.end(); ++iter) {
364 std::string lang_code = TranslateDownloadManager::GetLanguageCode(*iter);
365 if (TranslateDownloadManager::IsSupportedLanguage(lang_code))
368 return std::string();
372 std::string TranslateManager::GetAutoTargetLanguage(
373 const std::string& original_language,
374 TranslatePrefs* translate_prefs) {
375 std::string auto_target_lang;
376 if (translate_prefs->ShouldAutoTranslate(original_language,
377 &auto_target_lang)) {
378 // We need to confirm that the saved target language is still supported.
379 // Also, GetLanguageCode will take care of removing country code if any.
381 TranslateDownloadManager::GetLanguageCode(auto_target_lang);
382 if (TranslateDownloadManager::IsSupportedLanguage(auto_target_lang))
383 return auto_target_lang;
385 return std::string();