1 // Copyright (c) 2011 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.
4
5 #include "chrome/browser/translate/translate_manager.h"
6
7 #include "base/command_line.h"
8 #include "base/compiler_specific.h"
9 #include "base/memory/singleton.h"
10 #include "base/metrics/histogram.h"
11 #include "base/string_split.h"
12 #include "base/string_util.h"
13 #include "chrome/browser/autofill/autofill_manager.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/prefs/pref_service.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/tab_contents/language_state.h"
18 #include "chrome/browser/tab_contents/tab_util.h"
19 #include "chrome/browser/tabs/tab_strip_model.h"
20 #include "chrome/browser/translate/page_translated_details.h"
21 #include "chrome/browser/translate/translate_infobar_delegate.h"
22 #include "chrome/browser/translate/translate_tab_helper.h"
23 #include "chrome/browser/translate/translate_prefs.h"
24 #include "chrome/browser/ui/browser.h"
25 #include "chrome/browser/ui/browser_list.h"
26 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
27 #include "chrome/common/chrome_switches.h"
28 #include "chrome/common/pref_names.h"
29 #include "chrome/common/render_messages.h"
30 #include "chrome/common/translate_errors.h"
31 #include "chrome/common/url_constants.h"
32 #include "content/browser/renderer_host/render_process_host.h"
33 #include "content/browser/renderer_host/render_view_host.h"
34 #include "content/browser/tab_contents/navigation_controller.h"
35 #include "content/browser/tab_contents/navigation_entry.h"
36 #include "content/browser/tab_contents/tab_contents.h"
37 #include "content/common/notification_details.h"
38 #include "content/common/notification_service.h"
39 #include "content/common/notification_source.h"
40 #include "content/common/notification_type.h"
41 #include "grit/browser_resources.h"
42 #include "net/base/escape.h"
43 #include "net/url_request/url_request_status.h"
44 #include "ui/base/resource/resource_bundle.h"
45
46 namespace {
47
48 // Mapping from a locale name to a language code name.
49 // Locale names not included are translated as is.
50 struct LocaleToCLDLanguage {
51 const char* locale_language; // Language Chrome locale is in.
52 const char* cld_language; // Language the CLD reports.
53 };
54 LocaleToCLDLanguage kLocaleToCLDLanguages[] = {
55 { "en-GB", "en" },
56 { "en-US", "en" },
57 { "es-419", "es" },
58 { "pt-BR", "pt" },
59 { "pt-PT", "pt" },
60 };
61
62 // The list of languages the Google translation server supports.
63 // For information, here is the list of languages that Chrome can be run in
64 // but that the translation server does not support:
65 // am Amharic
66 // bn Bengali
67 // gu Gujarati
68 // kn Kannada
69 // ml Malayalam
70 // mr Marathi
71 // ta Tamil
72 // te Telugu
73 const char* kSupportedLanguages[] = {
74 "af", // Afrikaans
75 "az", // Azerbaijani
76 "sq", // Albanian
77 "ar", // Arabic
78 "hy", // Armenian
79 "eu", // Basque
80 "be", // Belarusian
81 "bg", // Bulgarian
82 "ca", // Catalan
83 "zh-CN", // Chinese (Simplified)
84 "zh-TW", // Chinese (Traditional)
85 "hr", // Croatian
86 "cs", // Czech
87 "da", // Danish
88 "nl", // Dutch
89 "en", // English
90 "et", // Estonian
91 "fi", // Finnish
92 "fil", // Filipino
93 "fr", // French
94 "gl", // Galician
95 "de", // German
96 "el", // Greek
97 "ht", // Haitian Creole
98 "he", // Hebrew
99 "hi", // Hindi
100 "hu", // Hungarian
101 "is", // Icelandic
102 "id", // Indonesian
103 "it", // Italian
104 "ga", // Irish
105 "ja", // Japanese
106 "ka", // Georgian
107 "ko", // Korean
108 "lv", // Latvian
109 "lt", // Lithuanian
110 "mk", // Macedonian
111 "ms", // Malay
112 "mt", // Maltese
113 "nb", // Norwegian
114 "fa", // Persian
115 "pl", // Polish
116 "pt", // Portuguese
117 "ro", // Romanian
118 "ru", // Russian
119 "sr", // Serbian
120 "sk", // Slovak
121 "sl", // Slovenian
122 "es", // Spanish
123 "sw", // Swahili
124 "sv", // Swedish
125 "th", // Thai
126 "tr", // Turkish
127 "uk", // Ukrainian
128 "ur", // Urdu
129 "vi", // Vietnamese
130 "cy", // Welsh
131 "yi", // Yiddish
132 };
133
134 const char* const kTranslateScriptURL =
135 "http://translate.google.com/translate_a/element.js?"
136 "cb=cr.googleTranslate.onTranslateElementLoad";
137 const char* const kTranslateScriptHeader =
138 "Google-Translate-Element-Mode: library";
139 const char* const kReportLanguageDetectionErrorURL =
140 "http://translate.google.com/translate_error";
141
142 const int kTranslateScriptExpirationDelayMS = 24 * 60 * 60 * 1000; // 1 day.
143
144 } // namespace
145
146 // static
147 base::LazyInstance<std::set<std::string> >
148 TranslateManager::supported_languages_(base::LINKER_INITIALIZED);
149
~TranslateManager()150 TranslateManager::~TranslateManager() {
151 }
152
153 // static
GetInstance()154 TranslateManager* TranslateManager::GetInstance() {
155 return Singleton<TranslateManager>::get();
156 }
157
158 // static
IsTranslatableURL(const GURL & url)159 bool TranslateManager::IsTranslatableURL(const GURL& url) {
160 // A URLs is translatable unless it is one of the following:
161 // - an internal URL (chrome:// and others)
162 // - the devtools (which is considered UI)
163 // - an FTP page (as FTP pages tend to have long lists of filenames that may
164 // confuse the CLD)
165 return !url.SchemeIs(chrome::kChromeUIScheme) &&
166 !url.SchemeIs(chrome::kChromeDevToolsScheme) &&
167 !url.SchemeIs(chrome::kFtpScheme);
168 }
169
170 // static
GetSupportedLanguages(std::vector<std::string> * languages)171 void TranslateManager::GetSupportedLanguages(
172 std::vector<std::string>* languages) {
173 DCHECK(languages && languages->empty());
174 for (size_t i = 0; i < arraysize(kSupportedLanguages); ++i)
175 languages->push_back(kSupportedLanguages[i]);
176 }
177
178 // static
GetLanguageCode(const std::string & chrome_locale)179 std::string TranslateManager::GetLanguageCode(
180 const std::string& chrome_locale) {
181 for (size_t i = 0; i < arraysize(kLocaleToCLDLanguages); ++i) {
182 if (chrome_locale == kLocaleToCLDLanguages[i].locale_language)
183 return kLocaleToCLDLanguages[i].cld_language;
184 }
185 return chrome_locale;
186 }
187
188 // static
IsSupportedLanguage(const std::string & page_language)189 bool TranslateManager::IsSupportedLanguage(const std::string& page_language) {
190 if (supported_languages_.Pointer()->empty()) {
191 for (size_t i = 0; i < arraysize(kSupportedLanguages); ++i)
192 supported_languages_.Pointer()->insert(kSupportedLanguages[i]);
193 }
194 return supported_languages_.Pointer()->find(page_language) !=
195 supported_languages_.Pointer()->end();
196 }
197
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)198 void TranslateManager::Observe(NotificationType type,
199 const NotificationSource& source,
200 const NotificationDetails& details) {
201 switch (type.value) {
202 case NotificationType::NAV_ENTRY_COMMITTED: {
203 NavigationController* controller =
204 Source<NavigationController>(source).ptr();
205 NavigationController::LoadCommittedDetails* load_details =
206 Details<NavigationController::LoadCommittedDetails>(details).ptr();
207 NavigationEntry* entry = controller->GetActiveEntry();
208 if (!entry) {
209 NOTREACHED();
210 return;
211 }
212
213 TabContentsWrapper* wrapper =
214 TabContentsWrapper::GetCurrentWrapperForContents(
215 controller->tab_contents());
216 if (!wrapper || !wrapper->translate_tab_helper())
217 return;
218
219 TranslateTabHelper* helper = wrapper->translate_tab_helper();
220 if (!load_details->is_main_frame &&
221 helper->language_state().translation_declined()) {
222 // Some sites (such as Google map) may trigger sub-frame navigations
223 // when the user interacts with the page. We don't want to show a new
224 // infobar if the user already dismissed one in that case.
225 return;
226 }
227 if (entry->transition_type() != PageTransition::RELOAD &&
228 load_details->type != NavigationType::SAME_PAGE) {
229 return;
230 }
231 // When doing a page reload, we don't get a TAB_LANGUAGE_DETERMINED
232 // notification. So we need to explictly initiate the translation.
233 // Note that we delay it as the TranslateManager gets this notification
234 // before the TabContents and the TabContents processing might remove the
235 // current infobars. Since InitTranslation might add an infobar, it must
236 // be done after that.
237 MessageLoop::current()->PostTask(FROM_HERE,
238 method_factory_.NewRunnableMethod(
239 &TranslateManager::InitiateTranslationPosted,
240 controller->tab_contents()->render_view_host()->process()->id(),
241 controller->tab_contents()->render_view_host()->routing_id(),
242 helper->language_state().original_language()));
243 break;
244 }
245 case NotificationType::TAB_LANGUAGE_DETERMINED: {
246 TabContents* tab = Source<TabContents>(source).ptr();
247 // We may get this notifications multiple times. Make sure to translate
248 // only once.
249 TabContentsWrapper* wrapper =
250 TabContentsWrapper::GetCurrentWrapperForContents(tab);
251 LanguageState& language_state =
252 wrapper->translate_tab_helper()->language_state();
253 if (language_state.page_translatable() &&
254 !language_state.translation_pending() &&
255 !language_state.translation_declined() &&
256 !language_state.IsPageTranslated()) {
257 std::string language = *(Details<std::string>(details).ptr());
258 InitiateTranslation(tab, language);
259 }
260 break;
261 }
262 case NotificationType::PAGE_TRANSLATED: {
263 // Only add translate infobar if it doesn't exist; if it already exists,
264 // just update the state, the actual infobar would have received the same
265 // notification and update the visual display accordingly.
266 TabContents* tab = Source<TabContents>(source).ptr();
267 PageTranslatedDetails* page_translated_details =
268 Details<PageTranslatedDetails>(details).ptr();
269 PageTranslated(tab, page_translated_details);
270 break;
271 }
272 case NotificationType::PROFILE_DESTROYED: {
273 Profile* profile = Source<Profile>(source).ptr();
274 notification_registrar_.Remove(this, NotificationType::PROFILE_DESTROYED,
275 source);
276 size_t count = accept_languages_.erase(profile->GetPrefs());
277 // We should know about this profile since we are listening for
278 // notifications on it.
279 DCHECK(count > 0);
280 pref_change_registrar_.Remove(prefs::kAcceptLanguages, this);
281 break;
282 }
283 case NotificationType::PREF_CHANGED: {
284 DCHECK(*Details<std::string>(details).ptr() == prefs::kAcceptLanguages);
285 PrefService* prefs = Source<PrefService>(source).ptr();
286 InitAcceptLanguages(prefs);
287 break;
288 }
289 default:
290 NOTREACHED();
291 }
292 }
293
OnURLFetchComplete(const URLFetcher * source,const GURL & url,const net::URLRequestStatus & status,int response_code,const ResponseCookies & cookies,const std::string & data)294 void TranslateManager::OnURLFetchComplete(const URLFetcher* source,
295 const GURL& url,
296 const net::URLRequestStatus& status,
297 int response_code,
298 const ResponseCookies& cookies,
299 const std::string& data) {
300 scoped_ptr<const URLFetcher> delete_ptr(source);
301 DCHECK(translate_script_request_pending_);
302 translate_script_request_pending_ = false;
303 bool error =
304 (status.status() != net::URLRequestStatus::SUCCESS ||
305 response_code != 200);
306
307 if (!error) {
308 base::StringPiece str = ResourceBundle::GetSharedInstance().
309 GetRawDataResource(IDR_TRANSLATE_JS);
310 DCHECK(translate_script_.empty());
311 str.CopyToString(&translate_script_);
312 translate_script_ += "\n" + data;
313 // We'll expire the cached script after some time, to make sure long running
314 // browsers still get fixes that might get pushed with newer scripts.
315 MessageLoop::current()->PostDelayedTask(FROM_HERE,
316 method_factory_.NewRunnableMethod(
317 &TranslateManager::ClearTranslateScript),
318 translate_script_expiration_delay_);
319 }
320
321 // Process any pending requests.
322 std::vector<PendingRequest>::const_iterator iter;
323 for (iter = pending_requests_.begin(); iter != pending_requests_.end();
324 ++iter) {
325 const PendingRequest& request = *iter;
326 TabContents* tab = tab_util::GetTabContentsByID(request.render_process_id,
327 request.render_view_id);
328 if (!tab) {
329 // The tab went away while we were retrieving the script.
330 continue;
331 }
332 NavigationEntry* entry = tab->controller().GetActiveEntry();
333 if (!entry || entry->page_id() != request.page_id) {
334 // We navigated away from the page the translation was triggered on.
335 continue;
336 }
337
338 if (error) {
339 ShowInfoBar(tab, TranslateInfoBarDelegate::CreateErrorDelegate(
340 TranslateErrors::NETWORK, tab,
341 request.source_lang, request.target_lang));
342 } else {
343 // Translate the page.
344 DoTranslatePage(tab, translate_script_,
345 request.source_lang, request.target_lang);
346 }
347 }
348 pending_requests_.clear();
349 }
350
351 // static
IsShowingTranslateInfobar(TabContents * tab)352 bool TranslateManager::IsShowingTranslateInfobar(TabContents* tab) {
353 return GetTranslateInfoBarDelegate(tab) != NULL;
354 }
355
TranslateManager()356 TranslateManager::TranslateManager()
357 : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
358 translate_script_expiration_delay_(kTranslateScriptExpirationDelayMS),
359 translate_script_request_pending_(false) {
360 notification_registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED,
361 NotificationService::AllSources());
362 notification_registrar_.Add(this, NotificationType::TAB_LANGUAGE_DETERMINED,
363 NotificationService::AllSources());
364 notification_registrar_.Add(this, NotificationType::PAGE_TRANSLATED,
365 NotificationService::AllSources());
366 }
367
InitiateTranslation(TabContents * tab,const std::string & page_lang)368 void TranslateManager::InitiateTranslation(TabContents* tab,
369 const std::string& page_lang) {
370 PrefService* prefs = tab->profile()->GetOriginalProfile()->GetPrefs();
371 if (!prefs->GetBoolean(prefs::kEnableTranslate))
372 return;
373
374 pref_change_registrar_.Init(prefs);
375
376 // Allow disabling of translate from the command line to assist with
377 // automated browser testing.
378 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableTranslate))
379 return;
380
381 NavigationEntry* entry = tab->controller().GetActiveEntry();
382 if (!entry) {
383 // This can happen for popups created with window.open("").
384 return;
385 }
386
387 // If there is already a translate infobar showing, don't show another one.
388 if (GetTranslateInfoBarDelegate(tab))
389 return;
390
391 std::string target_lang = GetTargetLanguage();
392 // Nothing to do if either the language Chrome is in or the language of the
393 // page is not supported by the translation server.
394 if (target_lang.empty() || !IsSupportedLanguage(page_lang)) {
395 return;
396 }
397
398 // We don't want to translate:
399 // - any Chrome specific page (New Tab Page, Download, History... pages).
400 // - similar languages (ex: en-US to en).
401 // - any user black-listed URLs or user selected language combination.
402 // - any language the user configured as accepted languages.
403 if (!IsTranslatableURL(entry->url()) || page_lang == target_lang ||
404 !TranslatePrefs::CanTranslate(prefs, page_lang, entry->url()) ||
405 IsAcceptLanguage(tab, page_lang)) {
406 return;
407 }
408
409 // If the user has previously selected "always translate" for this language we
410 // automatically translate. Note that in incognito mode we disable that
411 // feature; the user will get an infobar, so they can control whether the
412 // page's text is sent to the translate server.
413 std::string auto_target_lang;
414 if (!tab->profile()->IsOffTheRecord() &&
415 TranslatePrefs::ShouldAutoTranslate(prefs, page_lang,
416 &auto_target_lang)) {
417 TranslatePage(tab, page_lang, auto_target_lang);
418 return;
419 }
420
421 TranslateTabHelper* helper = TabContentsWrapper::GetCurrentWrapperForContents(
422 tab)->translate_tab_helper();
423 std::string auto_translate_to = helper->language_state().AutoTranslateTo();
424 if (!auto_translate_to.empty()) {
425 // This page was navigated through a click from a translated page.
426 TranslatePage(tab, page_lang, auto_translate_to);
427 return;
428 }
429
430 // Prompts the user if he/she wants the page translated.
431 tab->AddInfoBar(TranslateInfoBarDelegate::CreateDelegate(
432 TranslateInfoBarDelegate::BEFORE_TRANSLATE, tab, page_lang, target_lang));
433 }
434
InitiateTranslationPosted(int process_id,int render_id,const std::string & page_lang)435 void TranslateManager::InitiateTranslationPosted(
436 int process_id, int render_id, const std::string& page_lang) {
437 // The tab might have been closed.
438 TabContents* tab = tab_util::GetTabContentsByID(process_id, render_id);
439 if (!tab)
440 return;
441
442 TranslateTabHelper* helper = TabContentsWrapper::GetCurrentWrapperForContents(
443 tab)->translate_tab_helper();
444 if (helper->language_state().translation_pending())
445 return;
446
447 InitiateTranslation(tab, page_lang);
448 }
449
TranslatePage(TabContents * tab_contents,const std::string & source_lang,const std::string & target_lang)450 void TranslateManager::TranslatePage(TabContents* tab_contents,
451 const std::string& source_lang,
452 const std::string& target_lang) {
453 NavigationEntry* entry = tab_contents->controller().GetActiveEntry();
454 if (!entry) {
455 NOTREACHED();
456 return;
457 }
458
459 TranslateInfoBarDelegate* infobar = TranslateInfoBarDelegate::CreateDelegate(
460 TranslateInfoBarDelegate::TRANSLATING, tab_contents,
461 source_lang, target_lang);
462 if (!infobar) {
463 // This means the source or target languages are not supported, which should
464 // not happen as we won't show a translate infobar or have the translate
465 // context menu activated in such cases.
466 NOTREACHED();
467 return;
468 }
469 ShowInfoBar(tab_contents, infobar);
470
471 if (!translate_script_.empty()) {
472 DoTranslatePage(tab_contents, translate_script_, source_lang, target_lang);
473 return;
474 }
475
476 // The script is not available yet. Queue that request and query for the
477 // script. Once it is downloaded we'll do the translate.
478 RenderViewHost* rvh = tab_contents->render_view_host();
479 PendingRequest request;
480 request.render_process_id = rvh->process()->id();
481 request.render_view_id = rvh->routing_id();
482 request.page_id = entry->page_id();
483 request.source_lang = source_lang;
484 request.target_lang = target_lang;
485 pending_requests_.push_back(request);
486 RequestTranslateScript();
487 }
488
RevertTranslation(TabContents * tab_contents)489 void TranslateManager::RevertTranslation(TabContents* tab_contents) {
490 NavigationEntry* entry = tab_contents->controller().GetActiveEntry();
491 if (!entry) {
492 NOTREACHED();
493 return;
494 }
495 tab_contents->render_view_host()->Send(new ViewMsg_RevertTranslation(
496 tab_contents->render_view_host()->routing_id(), entry->page_id()));
497
498 TranslateTabHelper* helper = TabContentsWrapper::GetCurrentWrapperForContents(
499 tab_contents)->translate_tab_helper();
500 helper->language_state().set_current_language(
501 helper->language_state().original_language());
502 }
503
ReportLanguageDetectionError(TabContents * tab_contents)504 void TranslateManager::ReportLanguageDetectionError(TabContents* tab_contents) {
505 UMA_HISTOGRAM_COUNTS("Translate.ReportLanguageDetectionError", 1);
506 GURL page_url = tab_contents->controller().GetActiveEntry()->url();
507 std::string report_error_url(kReportLanguageDetectionErrorURL);
508 report_error_url += "?client=cr&action=langidc&u=";
509 report_error_url += EscapeUrlEncodedData(page_url.spec());
510 report_error_url += "&sl=";
511
512 TranslateTabHelper* helper = TabContentsWrapper::GetCurrentWrapperForContents(
513 tab_contents)->translate_tab_helper();
514 report_error_url += helper->language_state().original_language();
515 report_error_url += "&hl=";
516 report_error_url +=
517 GetLanguageCode(g_browser_process->GetApplicationLocale());
518 // Open that URL in a new tab so that the user can tell us more.
519 Browser* browser = BrowserList::GetLastActive();
520 if (!browser) {
521 NOTREACHED();
522 return;
523 }
524 browser->AddSelectedTabWithURL(GURL(report_error_url),
525 PageTransition::AUTO_BOOKMARK);
526 }
527
DoTranslatePage(TabContents * tab,const std::string & translate_script,const std::string & source_lang,const std::string & target_lang)528 void TranslateManager::DoTranslatePage(TabContents* tab,
529 const std::string& translate_script,
530 const std::string& source_lang,
531 const std::string& target_lang) {
532 NavigationEntry* entry = tab->controller().GetActiveEntry();
533 if (!entry) {
534 NOTREACHED();
535 return;
536 }
537
538 TabContentsWrapper* wrapper =
539 TabContentsWrapper::GetCurrentWrapperForContents(tab);
540
541 wrapper->translate_tab_helper()->language_state().set_translation_pending(
542 true);
543 tab->render_view_host()->Send(new ViewMsg_TranslatePage(
544 tab->render_view_host()->routing_id(), entry->page_id(), translate_script,
545 source_lang, target_lang));
546
547 // Ideally we'd have a better way to uniquely identify form control elements,
548 // but we don't have that yet. So before start translation, we clear the
549 // current form and re-parse it in AutofillManager first to get the new
550 // labels.
551 if (wrapper)
552 wrapper->autofill_manager()->Reset();
553 }
554
PageTranslated(TabContents * tab,PageTranslatedDetails * details)555 void TranslateManager::PageTranslated(TabContents* tab,
556 PageTranslatedDetails* details) {
557 // Create the new infobar to display.
558 TranslateInfoBarDelegate* infobar;
559 if (details->error_type != TranslateErrors::NONE) {
560 infobar = TranslateInfoBarDelegate::CreateErrorDelegate(details->error_type,
561 tab, details->source_language, details->target_language);
562 } else if (!IsSupportedLanguage(details->source_language)) {
563 // TODO(jcivelli): http://crbug.com/9390 We should change the "after
564 // translate" infobar to support unknown as the original
565 // language.
566 UMA_HISTOGRAM_COUNTS("Translate.ServerReportedUnsupportedLanguage", 1);
567 infobar = TranslateInfoBarDelegate::CreateErrorDelegate(
568 TranslateErrors::UNSUPPORTED_LANGUAGE, tab,
569 details->source_language, details->target_language);
570 } else {
571 infobar = TranslateInfoBarDelegate::CreateDelegate(
572 TranslateInfoBarDelegate::AFTER_TRANSLATE, tab,
573 details->source_language, details->target_language);
574 }
575 ShowInfoBar(tab, infobar);
576 }
577
IsAcceptLanguage(TabContents * tab,const std::string & language)578 bool TranslateManager::IsAcceptLanguage(TabContents* tab,
579 const std::string& language) {
580 PrefService* pref_service = tab->profile()->GetOriginalProfile()->GetPrefs();
581 PrefServiceLanguagesMap::const_iterator iter =
582 accept_languages_.find(pref_service);
583 if (iter == accept_languages_.end()) {
584 InitAcceptLanguages(pref_service);
585 // Listen for this profile going away, in which case we would need to clear
586 // the accepted languages for the profile.
587 notification_registrar_.Add(this, NotificationType::PROFILE_DESTROYED,
588 Source<Profile>(tab->profile()));
589 // Also start listening for changes in the accept languages.
590 pref_change_registrar_.Add(prefs::kAcceptLanguages, this);
591
592 iter = accept_languages_.find(pref_service);
593 }
594
595 return iter->second.count(language) != 0;
596 }
597
InitAcceptLanguages(PrefService * prefs)598 void TranslateManager::InitAcceptLanguages(PrefService* prefs) {
599 // We have been asked for this profile, build the languages.
600 std::string accept_langs_str = prefs->GetString(prefs::kAcceptLanguages);
601 std::vector<std::string> accept_langs_list;
602 LanguageSet accept_langs_set;
603 base::SplitString(accept_langs_str, ',', &accept_langs_list);
604 std::vector<std::string>::const_iterator iter;
605 std::string ui_lang =
606 GetLanguageCode(g_browser_process->GetApplicationLocale());
607 bool is_ui_english = StartsWithASCII(ui_lang, "en-", false);
608 for (iter = accept_langs_list.begin();
609 iter != accept_langs_list.end(); ++iter) {
610 // Get rid of the locale extension if any (ex: en-US -> en), but for Chinese
611 // for which the CLD reports zh-CN and zh-TW.
612 std::string accept_lang(*iter);
613 size_t index = iter->find("-");
614 if (index != std::string::npos && *iter != "zh-CN" && *iter != "zh-TW")
615 accept_lang = iter->substr(0, index);
616 // Special-case English until we resolve bug 36182 properly.
617 // Add English only if the UI language is not English. This will annoy
618 // users of non-English Chrome who can comprehend English until English is
619 // black-listed.
620 // TODO(jungshik): Once we determine that it's safe to remove English from
621 // the default Accept-Language values for most locales, remove this
622 // special-casing.
623 if (accept_lang != "en" || is_ui_english)
624 accept_langs_set.insert(accept_lang);
625 }
626 accept_languages_[prefs] = accept_langs_set;
627 }
628
RequestTranslateScript()629 void TranslateManager::RequestTranslateScript() {
630 if (translate_script_request_pending_)
631 return;
632
633 translate_script_request_pending_ = true;
634 URLFetcher* fetcher = URLFetcher::Create(0, GURL(kTranslateScriptURL),
635 URLFetcher::GET, this);
636 fetcher->set_request_context(Profile::GetDefaultRequestContext());
637 fetcher->set_extra_request_headers(kTranslateScriptHeader);
638 fetcher->Start();
639 }
640
ShowInfoBar(TabContents * tab,TranslateInfoBarDelegate * infobar)641 void TranslateManager::ShowInfoBar(TabContents* tab,
642 TranslateInfoBarDelegate* infobar) {
643 TranslateInfoBarDelegate* old_infobar = GetTranslateInfoBarDelegate(tab);
644 infobar->UpdateBackgroundAnimation(old_infobar);
645 if (old_infobar) {
646 // There already is a translate infobar, simply replace it.
647 tab->ReplaceInfoBar(old_infobar, infobar);
648 } else {
649 tab->AddInfoBar(infobar);
650 }
651 }
652
653 // static
GetTargetLanguage()654 std::string TranslateManager::GetTargetLanguage() {
655 std::string target_lang =
656 GetLanguageCode(g_browser_process->GetApplicationLocale());
657 return IsSupportedLanguage(target_lang) ? target_lang : std::string();
658 }
659
660 // static
GetTranslateInfoBarDelegate(TabContents * tab)661 TranslateInfoBarDelegate* TranslateManager::GetTranslateInfoBarDelegate(
662 TabContents* tab) {
663 for (size_t i = 0; i < tab->infobar_count(); ++i) {
664 TranslateInfoBarDelegate* delegate =
665 tab->GetInfoBarDelegateAt(i)->AsTranslateInfoBarDelegate();
666 if (delegate)
667 return delegate;
668 }
669 return NULL;
670 }
671