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