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