| OLD | NEW |
| (Empty) |
| 1 // Copyright 2013 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_language_list.h" | |
| 6 | |
| 7 #include <set> | |
| 8 | |
| 9 #include "base/bind.h" | |
| 10 #include "base/json/json_reader.h" | |
| 11 #include "base/lazy_instance.h" | |
| 12 #include "base/logging.h" | |
| 13 #include "base/strings/string_util.h" | |
| 14 #include "base/strings/stringprintf.h" | |
| 15 #include "base/values.h" | |
| 16 #include "chrome/browser/translate/translate_manager.h" | |
| 17 #include "chrome/browser/translate/translate_url_util.h" | |
| 18 #include "components/translate/core/browser/translate_browser_metrics.h" | |
| 19 #include "components/translate/core/browser/translate_download_manager.h" | |
| 20 #include "components/translate/core/browser/translate_event_details.h" | |
| 21 #include "components/translate/core/browser/translate_url_fetcher.h" | |
| 22 #include "net/base/url_util.h" | |
| 23 #include "ui/base/l10n/l10n_util.h" | |
| 24 #include "url/gurl.h" | |
| 25 | |
| 26 namespace { | |
| 27 | |
| 28 // The default list of languages the Google translation server supports. | |
| 29 // We use this list until we receive the list that the server exposes. | |
| 30 // For information, here is the list of languages that Chrome can be run in | |
| 31 // but that the translation server does not support: | |
| 32 // am Amharic | |
| 33 // bn Bengali | |
| 34 // gu Gujarati | |
| 35 // kn Kannada | |
| 36 // ml Malayalam | |
| 37 // mr Marathi | |
| 38 // ta Tamil | |
| 39 // te Telugu | |
| 40 const char* const kDefaultSupportedLanguages[] = { | |
| 41 "af", // Afrikaans | |
| 42 "sq", // Albanian | |
| 43 "ar", // Arabic | |
| 44 "be", // Belarusian | |
| 45 "bg", // Bulgarian | |
| 46 "ca", // Catalan | |
| 47 "zh-CN", // Chinese (Simplified) | |
| 48 "zh-TW", // Chinese (Traditional) | |
| 49 "hr", // Croatian | |
| 50 "cs", // Czech | |
| 51 "da", // Danish | |
| 52 "nl", // Dutch | |
| 53 "en", // English | |
| 54 "eo", // Esperanto | |
| 55 "et", // Estonian | |
| 56 "tl", // Filipino | |
| 57 "fi", // Finnish | |
| 58 "fr", // French | |
| 59 "gl", // Galician | |
| 60 "de", // German | |
| 61 "el", // Greek | |
| 62 "ht", // Haitian Creole | |
| 63 "iw", // Hebrew | |
| 64 "hi", // Hindi | |
| 65 "hu", // Hungarian | |
| 66 "is", // Icelandic | |
| 67 "id", // Indonesian | |
| 68 "ga", // Irish | |
| 69 "it", // Italian | |
| 70 "ja", // Japanese | |
| 71 "ko", // Korean | |
| 72 "lv", // Latvian | |
| 73 "lt", // Lithuanian | |
| 74 "mk", // Macedonian | |
| 75 "ms", // Malay | |
| 76 "mt", // Maltese | |
| 77 "no", // Norwegian | |
| 78 "fa", // Persian | |
| 79 "pl", // Polish | |
| 80 "pt", // Portuguese | |
| 81 "ro", // Romanian | |
| 82 "ru", // Russian | |
| 83 "sr", // Serbian | |
| 84 "sk", // Slovak | |
| 85 "sl", // Slovenian | |
| 86 "es", // Spanish | |
| 87 "sw", // Swahili | |
| 88 "sv", // Swedish | |
| 89 "th", // Thai | |
| 90 "tr", // Turkish | |
| 91 "uk", // Ukrainian | |
| 92 "vi", // Vietnamese | |
| 93 "cy", // Welsh | |
| 94 "yi", // Yiddish | |
| 95 }; | |
| 96 | |
| 97 // Constant URL string to fetch server supporting language list. | |
| 98 const char kLanguageListFetchURL[] = | |
| 99 "https://translate.googleapis.com/translate_a/l?client=chrome&cb=sl"; | |
| 100 | |
| 101 // Used in kTranslateScriptURL to request supporting languages list including | |
| 102 // "alpha languages". | |
| 103 const char kAlphaLanguageQueryName[] = "alpha"; | |
| 104 const char kAlphaLanguageQueryValue[] = "1"; | |
| 105 | |
| 106 // Represent if the language list updater is disabled. | |
| 107 bool update_is_disabled = false; | |
| 108 | |
| 109 // Retry parameter for fetching. | |
| 110 const int kMaxRetryOn5xx = 5; | |
| 111 | |
| 112 // Show a message in chrome:://translate-internals Event Logs. | |
| 113 void NotifyEvent(int line, const std::string& message) { | |
| 114 TranslateManager* manager = TranslateManager::GetInstance(); | |
| 115 DCHECK(manager); | |
| 116 | |
| 117 TranslateEventDetails details(__FILE__, line, message); | |
| 118 manager->NotifyTranslateEvent(details); | |
| 119 } | |
| 120 | |
| 121 // Parses |language_list| containing the list of languages that the translate | |
| 122 // server can translate to and from, and fills |set| with them. | |
| 123 void SetSupportedLanguages(const std::string& language_list, | |
| 124 std::set<std::string>* target_language_set, | |
| 125 std::set<std::string>* alpha_language_set) { | |
| 126 DCHECK(target_language_set); | |
| 127 DCHECK(alpha_language_set); | |
| 128 | |
| 129 // The format is: | |
| 130 // sl({ | |
| 131 // "sl": {"XX": "LanguageName", ...}, | |
| 132 // "tl": {"XX": "LanguageName", ...}, | |
| 133 // "al": {"XX": 1, ...} | |
| 134 // }) | |
| 135 // Where "sl(" is set in kLanguageListCallbackName, "tl" is | |
| 136 // kTargetLanguagesKey and "al" kAlphaLanguagesKey. | |
| 137 if (!StartsWithASCII(language_list, | |
| 138 TranslateLanguageList::kLanguageListCallbackName, | |
| 139 false) || | |
| 140 !EndsWith(language_list, ")", false)) { | |
| 141 // We don't have a NOTREACHED here since this can happen in ui_tests, even | |
| 142 // though the the BrowserMain function won't call us with parameters.ui_task | |
| 143 // is NULL some tests don't set it, so we must bail here. | |
| 144 return; | |
| 145 } | |
| 146 static const size_t kLanguageListCallbackNameLength = | |
| 147 strlen(TranslateLanguageList::kLanguageListCallbackName); | |
| 148 std::string languages_json = language_list.substr( | |
| 149 kLanguageListCallbackNameLength, | |
| 150 language_list.size() - kLanguageListCallbackNameLength - 1); | |
| 151 scoped_ptr<base::Value> json_value( | |
| 152 base::JSONReader::Read(languages_json, base::JSON_ALLOW_TRAILING_COMMAS)); | |
| 153 if (json_value == NULL || !json_value->IsType(base::Value::TYPE_DICTIONARY)) { | |
| 154 NOTREACHED(); | |
| 155 return; | |
| 156 } | |
| 157 // The first level dictionary contains three sub-dict, first for source | |
| 158 // languages and second for target languages, we want to use the target | |
| 159 // languages. The last is for alpha languages. | |
| 160 base::DictionaryValue* language_dict = | |
| 161 static_cast<base::DictionaryValue*>(json_value.get()); | |
| 162 base::DictionaryValue* target_languages = NULL; | |
| 163 if (!language_dict->GetDictionary(TranslateLanguageList::kTargetLanguagesKey, | |
| 164 &target_languages) || | |
| 165 target_languages == NULL) { | |
| 166 NOTREACHED(); | |
| 167 return; | |
| 168 } | |
| 169 | |
| 170 const std::string& locale = | |
| 171 TranslateDownloadManager::GetInstance()->application_locale(); | |
| 172 | |
| 173 // Now we can clear language list. | |
| 174 target_language_set->clear(); | |
| 175 std::string message; | |
| 176 // ... and replace it with the values we just fetched from the server. | |
| 177 for (base::DictionaryValue::Iterator iter(*target_languages); | |
| 178 !iter.IsAtEnd(); | |
| 179 iter.Advance()) { | |
| 180 const std::string& lang = iter.key(); | |
| 181 if (!l10n_util::IsLocaleNameTranslated(lang.c_str(), locale)) { | |
| 182 TranslateBrowserMetrics::ReportUndisplayableLanguage(lang); | |
| 183 continue; | |
| 184 } | |
| 185 target_language_set->insert(lang); | |
| 186 if (message.empty()) | |
| 187 message += lang; | |
| 188 else | |
| 189 message += ", " + lang; | |
| 190 } | |
| 191 NotifyEvent(__LINE__, message); | |
| 192 | |
| 193 // Get the alpha languages. The "al" parameter could be abandoned. | |
| 194 base::DictionaryValue* alpha_languages = NULL; | |
| 195 if (!language_dict->GetDictionary(TranslateLanguageList::kAlphaLanguagesKey, | |
| 196 &alpha_languages) || | |
| 197 alpha_languages == NULL) { | |
| 198 return; | |
| 199 } | |
| 200 | |
| 201 // We assume that the alpha languages are included in the above target | |
| 202 // languages, and don't use UMA or NotifyEvent. | |
| 203 alpha_language_set->clear(); | |
| 204 for (base::DictionaryValue::Iterator iter(*alpha_languages); | |
| 205 !iter.IsAtEnd(); iter.Advance()) { | |
| 206 const std::string& lang = iter.key(); | |
| 207 if (!l10n_util::IsLocaleNameTranslated(lang.c_str(), locale)) | |
| 208 continue; | |
| 209 alpha_language_set->insert(lang); | |
| 210 } | |
| 211 } | |
| 212 | |
| 213 } // namespace | |
| 214 | |
| 215 // This must be kept in sync with the &cb= value in the kLanguageListFetchURL. | |
| 216 const char TranslateLanguageList::kLanguageListCallbackName[] = "sl("; | |
| 217 const char TranslateLanguageList::kTargetLanguagesKey[] = "tl"; | |
| 218 const char TranslateLanguageList::kAlphaLanguagesKey[] = "al"; | |
| 219 | |
| 220 TranslateLanguageList::TranslateLanguageList() { | |
| 221 // We default to our hard coded list of languages in | |
| 222 // |kDefaultSupportedLanguages|. This list will be overriden by a server | |
| 223 // providing supported langauges list. | |
| 224 for (size_t i = 0; i < arraysize(kDefaultSupportedLanguages); ++i) | |
| 225 all_supported_languages_.insert(kDefaultSupportedLanguages[i]); | |
| 226 | |
| 227 if (update_is_disabled) | |
| 228 return; | |
| 229 | |
| 230 language_list_fetcher_.reset(new TranslateURLFetcher(kFetcherId)); | |
| 231 language_list_fetcher_->set_max_retry_on_5xx(kMaxRetryOn5xx); | |
| 232 } | |
| 233 | |
| 234 TranslateLanguageList::~TranslateLanguageList() { | |
| 235 } | |
| 236 | |
| 237 void TranslateLanguageList::GetSupportedLanguages( | |
| 238 std::vector<std::string>* languages) { | |
| 239 DCHECK(languages && languages->empty()); | |
| 240 std::set<std::string>::const_iterator iter = all_supported_languages_.begin(); | |
| 241 for (; iter != all_supported_languages_.end(); ++iter) | |
| 242 languages->push_back(*iter); | |
| 243 | |
| 244 // Update language lists if they are not updated after Chrome was launched | |
| 245 // for later requests. | |
| 246 if (!update_is_disabled && language_list_fetcher_.get()) | |
| 247 RequestLanguageList(); | |
| 248 } | |
| 249 | |
| 250 std::string TranslateLanguageList::GetLanguageCode( | |
| 251 const std::string& chrome_locale) { | |
| 252 // Only remove the country code for country specific languages we don't | |
| 253 // support specifically yet. | |
| 254 if (IsSupportedLanguage(chrome_locale)) | |
| 255 return chrome_locale; | |
| 256 | |
| 257 size_t hypen_index = chrome_locale.find('-'); | |
| 258 if (hypen_index == std::string::npos) | |
| 259 return chrome_locale; | |
| 260 return chrome_locale.substr(0, hypen_index); | |
| 261 } | |
| 262 | |
| 263 bool TranslateLanguageList::IsSupportedLanguage(const std::string& language) { | |
| 264 return all_supported_languages_.count(language) != 0; | |
| 265 } | |
| 266 | |
| 267 bool TranslateLanguageList::IsAlphaLanguage(const std::string& language) { | |
| 268 return alpha_languages_.count(language) != 0; | |
| 269 } | |
| 270 | |
| 271 void TranslateLanguageList::RequestLanguageList() { | |
| 272 // If resource requests are not allowed, we'll get a callback when they are. | |
| 273 if (resource_request_allowed_notifier_.ResourceRequestsAllowed()) | |
| 274 OnResourceRequestsAllowed(); | |
| 275 } | |
| 276 | |
| 277 void TranslateLanguageList::OnResourceRequestsAllowed() { | |
| 278 if (language_list_fetcher_.get() && | |
| 279 (language_list_fetcher_->state() == TranslateURLFetcher::IDLE || | |
| 280 language_list_fetcher_->state() == TranslateURLFetcher::FAILED)) { | |
| 281 GURL url = GURL(kLanguageListFetchURL); | |
| 282 url = TranslateURLUtil::AddHostLocaleToUrl(url); | |
| 283 url = TranslateURLUtil::AddApiKeyToUrl(url); | |
| 284 url = net::AppendQueryParameter(url, | |
| 285 kAlphaLanguageQueryName, | |
| 286 kAlphaLanguageQueryValue); | |
| 287 | |
| 288 std::string message = base::StringPrintf( | |
| 289 "Language list including alpha languages fetch starts (URL: %s)", | |
| 290 url.spec().c_str()); | |
| 291 NotifyEvent(__LINE__, message); | |
| 292 | |
| 293 bool result = language_list_fetcher_->Request( | |
| 294 url, | |
| 295 base::Bind(&TranslateLanguageList::OnLanguageListFetchComplete, | |
| 296 base::Unretained(this))); | |
| 297 if (!result) | |
| 298 NotifyEvent(__LINE__, "Request is omitted due to retry limit"); | |
| 299 } | |
| 300 } | |
| 301 | |
| 302 // static | |
| 303 void TranslateLanguageList::DisableUpdate() { | |
| 304 update_is_disabled = true; | |
| 305 } | |
| 306 | |
| 307 void TranslateLanguageList::OnLanguageListFetchComplete( | |
| 308 int id, | |
| 309 bool success, | |
| 310 const std::string& data) { | |
| 311 if (!success) { | |
| 312 // Since it fails just now, omit to schedule resource requests if | |
| 313 // ResourceRequestAllowedNotifier think it's ready. Otherwise, a callback | |
| 314 // will be invoked later to request resources again. | |
| 315 // The TranslateURLFetcher has a limit for retried requests and aborts | |
| 316 // re-try not to invoke OnLanguageListFetchComplete anymore if it's asked to | |
| 317 // re-try too many times. | |
| 318 NotifyEvent(__LINE__, "Failed to fetch languages"); | |
| 319 return; | |
| 320 } | |
| 321 | |
| 322 NotifyEvent(__LINE__, "Language list is updated"); | |
| 323 | |
| 324 DCHECK_EQ(kFetcherId, id); | |
| 325 | |
| 326 SetSupportedLanguages(data, &all_supported_languages_, &alpha_languages_); | |
| 327 language_list_fetcher_.reset(); | |
| 328 | |
| 329 last_updated_ = base::Time::Now(); | |
| 330 } | |
| OLD | NEW |