Home | History | Annotate | Download | only in translate
      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 
    150 TranslateManager::~TranslateManager() {
    151 }
    152 
    153 // static
    154 TranslateManager* TranslateManager::GetInstance() {
    155   return Singleton<TranslateManager>::get();
    156 }
    157 
    158 // static
    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
    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
    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
    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 
    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 
    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
    352 bool TranslateManager::IsShowingTranslateInfobar(TabContents* tab) {
    353   return GetTranslateInfoBarDelegate(tab) != NULL;
    354 }
    355 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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
    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
    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