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