Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / translate / translate_language_list.cc
blob46299bbe6677c65a43920535cc2fdc0c79bd92b3
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"
7 #include <set>
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/browser_process.h"
17 #include "chrome/browser/translate/chrome_translate_delegate.h"
18 #include "chrome/browser/translate/translate_manager.h"
19 #include "chrome/browser/translate/translate_url_util.h"
20 #include "components/translate/core/browser/translate_browser_metrics.h"
21 #include "components/translate/core/browser/translate_event_details.h"
22 #include "components/translate/core/browser/translate_url_fetcher.h"
23 #include "net/base/url_util.h"
24 #include "ui/base/l10n/l10n_util.h"
25 #include "url/gurl.h"
27 namespace {
29 // The default list of languages the Google translation server supports.
30 // We use this list until we receive the list that the server exposes.
31 // For information, here is the list of languages that Chrome can be run in
32 // but that the translation server does not support:
33 // am Amharic
34 // bn Bengali
35 // gu Gujarati
36 // kn Kannada
37 // ml Malayalam
38 // mr Marathi
39 // ta Tamil
40 // te Telugu
41 const char* const kDefaultSupportedLanguages[] = {
42 "af", // Afrikaans
43 "sq", // Albanian
44 "ar", // Arabic
45 "be", // Belarusian
46 "bg", // Bulgarian
47 "ca", // Catalan
48 "zh-CN", // Chinese (Simplified)
49 "zh-TW", // Chinese (Traditional)
50 "hr", // Croatian
51 "cs", // Czech
52 "da", // Danish
53 "nl", // Dutch
54 "en", // English
55 "eo", // Esperanto
56 "et", // Estonian
57 "tl", // Filipino
58 "fi", // Finnish
59 "fr", // French
60 "gl", // Galician
61 "de", // German
62 "el", // Greek
63 "ht", // Haitian Creole
64 "iw", // Hebrew
65 "hi", // Hindi
66 "hu", // Hungarian
67 "is", // Icelandic
68 "id", // Indonesian
69 "ga", // Irish
70 "it", // Italian
71 "ja", // Japanese
72 "ko", // Korean
73 "lv", // Latvian
74 "lt", // Lithuanian
75 "mk", // Macedonian
76 "ms", // Malay
77 "mt", // Maltese
78 "no", // Norwegian
79 "fa", // Persian
80 "pl", // Polish
81 "pt", // Portuguese
82 "ro", // Romanian
83 "ru", // Russian
84 "sr", // Serbian
85 "sk", // Slovak
86 "sl", // Slovenian
87 "es", // Spanish
88 "sw", // Swahili
89 "sv", // Swedish
90 "th", // Thai
91 "tr", // Turkish
92 "uk", // Ukrainian
93 "vi", // Vietnamese
94 "cy", // Welsh
95 "yi", // Yiddish
98 // Constant URL string to fetch server supporting language list.
99 const char kLanguageListFetchURL[] =
100 "https://translate.googleapis.com/translate_a/l?client=chrome&cb=sl";
102 // Used in kTranslateScriptURL to request supporting languages list including
103 // "alpha languages".
104 const char kAlphaLanguageQueryName[] = "alpha";
105 const char kAlphaLanguageQueryValue[] = "1";
107 // Represent if the language list updater is disabled.
108 bool update_is_disabled = false;
110 // Retry parameter for fetching.
111 const int kMaxRetryOn5xx = 5;
113 // Show a message in chrome:://translate-internals Event Logs.
114 void NotifyEvent(int line, const std::string& message) {
115 TranslateManager* manager = TranslateManager::GetInstance();
116 DCHECK(manager);
118 TranslateEventDetails details(__FILE__, line, message);
119 manager->NotifyTranslateEvent(details);
122 // Parses |language_list| containing the list of languages that the translate
123 // server can translate to and from, and fills |set| with them.
124 void SetSupportedLanguages(const std::string& language_list,
125 std::set<std::string>* target_language_set,
126 std::set<std::string>* alpha_language_set) {
127 DCHECK(target_language_set);
128 DCHECK(alpha_language_set);
130 // The format is:
131 // sl({
132 // "sl": {"XX": "LanguageName", ...},
133 // "tl": {"XX": "LanguageName", ...},
134 // "al": {"XX": 1, ...}
135 // })
136 // Where "sl(" is set in kLanguageListCallbackName, "tl" is
137 // kTargetLanguagesKey and "al" kAlphaLanguagesKey.
138 if (!StartsWithASCII(language_list,
139 TranslateLanguageList::kLanguageListCallbackName,
140 false) ||
141 !EndsWith(language_list, ")", false)) {
142 // We don't have a NOTREACHED here since this can happen in ui_tests, even
143 // though the the BrowserMain function won't call us with parameters.ui_task
144 // is NULL some tests don't set it, so we must bail here.
145 return;
147 static const size_t kLanguageListCallbackNameLength =
148 strlen(TranslateLanguageList::kLanguageListCallbackName);
149 std::string languages_json = language_list.substr(
150 kLanguageListCallbackNameLength,
151 language_list.size() - kLanguageListCallbackNameLength - 1);
152 scoped_ptr<base::Value> json_value(
153 base::JSONReader::Read(languages_json, base::JSON_ALLOW_TRAILING_COMMAS));
154 if (json_value == NULL || !json_value->IsType(base::Value::TYPE_DICTIONARY)) {
155 NOTREACHED();
156 return;
158 // The first level dictionary contains three sub-dict, first for source
159 // languages and second for target languages, we want to use the target
160 // languages. The last is for alpha languages.
161 base::DictionaryValue* language_dict =
162 static_cast<base::DictionaryValue*>(json_value.get());
163 base::DictionaryValue* target_languages = NULL;
164 if (!language_dict->GetDictionary(TranslateLanguageList::kTargetLanguagesKey,
165 &target_languages) ||
166 target_languages == NULL) {
167 NOTREACHED();
168 return;
171 const std::string& locale = g_browser_process->GetApplicationLocale();
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;
185 target_language_set->insert(lang);
186 if (message.empty())
187 message += lang;
188 else
189 message += ", " + lang;
191 NotifyEvent(__LINE__, message);
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;
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);
213 } // namespace
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";
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]);
227 if (update_is_disabled)
228 return;
230 language_list_fetcher_.reset(new TranslateURLFetcher(
231 kFetcherId, ChromeTranslateDelegate::GetInstance()));
232 language_list_fetcher_->set_max_retry_on_5xx(kMaxRetryOn5xx);
235 TranslateLanguageList::~TranslateLanguageList() {
238 void TranslateLanguageList::GetSupportedLanguages(
239 std::vector<std::string>* languages) {
240 DCHECK(languages && languages->empty());
241 std::set<std::string>::const_iterator iter = all_supported_languages_.begin();
242 for (; iter != all_supported_languages_.end(); ++iter)
243 languages->push_back(*iter);
245 // Update language lists if they are not updated after Chrome was launched
246 // for later requests.
247 if (!update_is_disabled && language_list_fetcher_.get())
248 RequestLanguageList();
251 std::string TranslateLanguageList::GetLanguageCode(
252 const std::string& chrome_locale) {
253 // Only remove the country code for country specific languages we don't
254 // support specifically yet.
255 if (IsSupportedLanguage(chrome_locale))
256 return chrome_locale;
258 size_t hypen_index = chrome_locale.find('-');
259 if (hypen_index == std::string::npos)
260 return chrome_locale;
261 return chrome_locale.substr(0, hypen_index);
264 bool TranslateLanguageList::IsSupportedLanguage(const std::string& language) {
265 return all_supported_languages_.count(language) != 0;
268 bool TranslateLanguageList::IsAlphaLanguage(const std::string& language) {
269 return alpha_languages_.count(language) != 0;
272 void TranslateLanguageList::RequestLanguageList() {
273 // If resource requests are not allowed, we'll get a callback when they are.
274 if (resource_request_allowed_notifier_.ResourceRequestsAllowed())
275 OnResourceRequestsAllowed();
278 void TranslateLanguageList::OnResourceRequestsAllowed() {
279 if (language_list_fetcher_.get() &&
280 (language_list_fetcher_->state() == TranslateURLFetcher::IDLE ||
281 language_list_fetcher_->state() == TranslateURLFetcher::FAILED)) {
282 GURL url = GURL(kLanguageListFetchURL);
283 url = TranslateURLUtil::AddHostLocaleToUrl(url);
284 url = TranslateURLUtil::AddApiKeyToUrl(url);
285 url = net::AppendQueryParameter(url,
286 kAlphaLanguageQueryName,
287 kAlphaLanguageQueryValue);
289 std::string message = base::StringPrintf(
290 "Language list including alpha languages fetch starts (URL: %s)",
291 url.spec().c_str());
292 NotifyEvent(__LINE__, message);
294 bool result = language_list_fetcher_->Request(
295 url,
296 base::Bind(&TranslateLanguageList::OnLanguageListFetchComplete,
297 base::Unretained(this)));
298 if (!result)
299 NotifyEvent(__LINE__, "Request is omitted due to retry limit");
303 // static
304 void TranslateLanguageList::DisableUpdate() {
305 update_is_disabled = true;
308 void TranslateLanguageList::OnLanguageListFetchComplete(
309 int id,
310 bool success,
311 const std::string& data) {
312 if (!success) {
313 // Since it fails just now, omit to schedule resource requests if
314 // ResourceRequestAllowedNotifier think it's ready. Otherwise, a callback
315 // will be invoked later to request resources again.
316 // The TranslateURLFetcher has a limit for retried requests and aborts
317 // re-try not to invoke OnLanguageListFetchComplete anymore if it's asked to
318 // re-try too many times.
319 NotifyEvent(__LINE__, "Failed to fetch languages");
320 return;
323 NotifyEvent(__LINE__, "Language list is updated");
325 DCHECK_EQ(kFetcherId, id);
327 SetSupportedLanguages(data, &all_supported_languages_, &alpha_languages_);
328 language_list_fetcher_.reset();
330 last_updated_ = base::Time::Now();