• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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