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.
5 #include "chrome/browser/translate/translate_language_list.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/browser_process.h"
17 #include "chrome/browser/translate/translate_browser_metrics.h"
18 #include "chrome/browser/translate/translate_event_details.h"
19 #include "chrome/browser/translate/translate_manager.h"
20 #include "chrome/browser/translate/translate_url_fetcher.h"
21 #include "chrome/browser/translate/translate_url_util.h"
22 #include "net/base/url_util.h"
23 #include "ui/base/l10n/l10n_util.h"
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:
40 const char* const kDefaultSupportedLanguages[] = {
47 "zh-CN", // Chinese (Simplified)
48 "zh-TW", // Chinese (Traditional)
62 "ht", // Haitian Creole
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";
101 // Used in kTranslateScriptURL to request supporting languages list including
102 // "alpha languages".
103 const char kAlphaLanguageQueryName[] = "alpha";
104 const char kAlphaLanguageQueryValue[] = "1";
106 // Represent if the language list updater is disabled.
107 bool update_is_disabled = false;
109 // Retry parameter for fetching.
110 const int kMaxRetryOn5xx = 5;
112 // Show a message in chrome:://translate-internals Event Logs.
113 void NotifyEvent(int line, const std::string& message) {
114 TranslateManager* manager = TranslateManager::GetInstance();
117 TranslateEventDetails details(__FILE__, line, message);
118 manager->NotifyTranslateEvent(details);
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);
131 // "sl": {"XX": "LanguageName", ...},
132 // "tl": {"XX": "LanguageName", ...},
133 // "al": {"XX": 1, ...}
135 // Where "sl(" is set in kLanguageListCallbackName, "tl" is
136 // kTargetLanguagesKey and "al" kAlphaLanguagesKey.
137 if (!StartsWithASCII(language_list,
138 TranslateLanguageList::kLanguageListCallbackName,
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.
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<Value> json_value(
152 base::JSONReader::Read(languages_json, base::JSON_ALLOW_TRAILING_COMMAS));
153 if (json_value == NULL || !json_value->IsType(Value::TYPE_DICTIONARY)) {
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 DictionaryValue* language_dict =
161 static_cast<DictionaryValue*>(json_value.get());
162 DictionaryValue* target_languages = NULL;
163 if (!language_dict->GetDictionary(TranslateLanguageList::kTargetLanguagesKey,
164 &target_languages) ||
165 target_languages == NULL) {
170 const std::string& locale = g_browser_process->GetApplicationLocale();
172 // Now we can clear language list.
173 target_language_set->clear();
175 // ... and replace it with the values we just fetched from the server.
176 for (DictionaryValue::Iterator iter(*target_languages);
179 const std::string& lang = iter.key();
180 if (!l10n_util::IsLocaleNameTranslated(lang.c_str(), locale)) {
181 TranslateBrowserMetrics::ReportUndisplayableLanguage(lang);
184 target_language_set->insert(lang);
188 message += ", " + lang;
190 NotifyEvent(__LINE__, message);
192 // Get the alpha languages. The "al" parameter could be abandoned.
193 DictionaryValue* alpha_languages = NULL;
194 if (!language_dict->GetDictionary(TranslateLanguageList::kAlphaLanguagesKey,
196 alpha_languages == NULL) {
200 // We assume that the alpha languages are included in the above target
201 // languages, and don't use UMA or NotifyEvent.
202 alpha_language_set->clear();
203 for (DictionaryValue::Iterator iter(*alpha_languages);
204 !iter.IsAtEnd(); iter.Advance()) {
205 const std::string& lang = iter.key();
206 if (!l10n_util::IsLocaleNameTranslated(lang.c_str(), locale))
208 alpha_language_set->insert(lang);
214 // This must be kept in sync with the &cb= value in the kLanguageListFetchURL.
215 const char TranslateLanguageList::kLanguageListCallbackName[] = "sl(";
216 const char TranslateLanguageList::kTargetLanguagesKey[] = "tl";
217 const char TranslateLanguageList::kAlphaLanguagesKey[] = "al";
219 TranslateLanguageList::TranslateLanguageList() {
220 // We default to our hard coded list of languages in
221 // |kDefaultSupportedLanguages|. This list will be overriden by a server
222 // providing supported langauges list.
223 for (size_t i = 0; i < arraysize(kDefaultSupportedLanguages); ++i)
224 all_supported_languages_.insert(kDefaultSupportedLanguages[i]);
226 if (update_is_disabled)
229 language_list_fetcher_.reset(new TranslateURLFetcher(kFetcherId));
230 language_list_fetcher_->set_max_retry_on_5xx(kMaxRetryOn5xx);
233 TranslateLanguageList::~TranslateLanguageList() {
236 void TranslateLanguageList::GetSupportedLanguages(
237 std::vector<std::string>* languages) {
238 DCHECK(languages && languages->empty());
239 std::set<std::string>::const_iterator iter = all_supported_languages_.begin();
240 for (; iter != all_supported_languages_.end(); ++iter)
241 languages->push_back(*iter);
243 // Update language lists if they are not updated after Chrome was launched
244 // for later requests.
245 if (!update_is_disabled && language_list_fetcher_.get())
246 RequestLanguageList();
249 std::string TranslateLanguageList::GetLanguageCode(
250 const std::string& chrome_locale) {
251 // Only remove the country code for country specific languages we don't
252 // support specifically yet.
253 if (IsSupportedLanguage(chrome_locale))
254 return chrome_locale;
256 size_t hypen_index = chrome_locale.find('-');
257 if (hypen_index == std::string::npos)
258 return chrome_locale;
259 return chrome_locale.substr(0, hypen_index);
262 bool TranslateLanguageList::IsSupportedLanguage(const std::string& language) {
263 return all_supported_languages_.count(language) != 0;
266 bool TranslateLanguageList::IsAlphaLanguage(const std::string& language) {
267 return alpha_languages_.count(language) != 0;
270 void TranslateLanguageList::RequestLanguageList() {
271 // If resource requests are not allowed, we'll get a callback when they are.
272 if (resource_request_allowed_notifier_.ResourceRequestsAllowed())
273 OnResourceRequestsAllowed();
276 void TranslateLanguageList::OnResourceRequestsAllowed() {
277 if (language_list_fetcher_.get() &&
278 (language_list_fetcher_->state() == TranslateURLFetcher::IDLE ||
279 language_list_fetcher_->state() == TranslateURLFetcher::FAILED)) {
280 GURL url = GURL(kLanguageListFetchURL);
281 url = TranslateURLUtil::AddHostLocaleToUrl(url);
282 url = TranslateURLUtil::AddApiKeyToUrl(url);
283 url = net::AppendQueryParameter(url,
284 kAlphaLanguageQueryName,
285 kAlphaLanguageQueryValue);
287 std::string message = base::StringPrintf(
288 "Language list includeing alpha languages fetch starts (URL: %s)",
290 NotifyEvent(__LINE__, message);
292 bool result = language_list_fetcher_->Request(
294 base::Bind(&TranslateLanguageList::OnLanguageListFetchComplete,
295 base::Unretained(this)));
297 NotifyEvent(__LINE__, "Request is omitted due to retry limit");
302 void TranslateLanguageList::DisableUpdate() {
303 update_is_disabled = true;
306 void TranslateLanguageList::OnLanguageListFetchComplete(
309 const std::string& data) {
311 // Since it fails just now, omit to schedule resource requests if
312 // ResourceRequestAllowedNotifier think it's ready. Otherwise, a callback
313 // will be invoked later to request resources again.
314 // The TranslateURLFetcher has a limit for retried requests and aborts
315 // re-try not to invoke OnLanguageListFetchComplete anymore if it's asked to
316 // re-try too many times.
317 NotifyEvent(__LINE__, "Failed to fetch languages");
321 NotifyEvent(__LINE__, "Language list is updated");
323 DCHECK_EQ(kFetcherId, id);
325 SetSupportedLanguages(data, &all_supported_languages_, &alpha_languages_);
326 language_list_fetcher_.reset();
328 last_updated_ = base::Time::Now();