Upstream version 5.34.98.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / translate / translate_prefs.cc
1 // Copyright (c) 2011 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_prefs.h"
6
7 #include <set>
8
9 #include "base/prefs/pref_service.h"
10 #include "base/prefs/scoped_user_pref_update.h"
11 #include "base/strings/string_split.h"
12 #include "base/strings/string_util.h"
13 #include "chrome/browser/browser_process.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/translate/translate_accept_languages.h"
16 #include "chrome/browser/translate/translate_manager.h"
17 #include "chrome/common/pref_names.h"
18 #include "components/translate/core/common/translate_util.h"
19 #include "components/user_prefs/pref_registry_syncable.h"
20
21 const char TranslatePrefs::kPrefTranslateLanguageBlacklist[] =
22     "translate_language_blacklist";
23 const char TranslatePrefs::kPrefTranslateSiteBlacklist[] =
24     "translate_site_blacklist";
25 const char TranslatePrefs::kPrefTranslateWhitelists[] =
26     "translate_whitelists";
27 const char TranslatePrefs::kPrefTranslateDeniedCount[] =
28     "translate_denied_count";
29 const char TranslatePrefs::kPrefTranslateAcceptedCount[] =
30     "translate_accepted_count";
31 const char TranslatePrefs::kPrefTranslateBlockedLanguages[] =
32     "translate_blocked_languages";
33
34 namespace {
35
36 void GetBlacklistedLanguages(const PrefService* prefs,
37                              std::vector<std::string>* languages) {
38   DCHECK(languages);
39   DCHECK(languages->empty());
40
41   const char* key = TranslatePrefs::kPrefTranslateLanguageBlacklist;
42   const base::ListValue* list = prefs->GetList(key);
43   for (base::ListValue::const_iterator it = list->begin();
44        it != list->end(); ++it) {
45     std::string lang;
46     (*it)->GetAsString(&lang);
47     languages->push_back(lang);
48   }
49 }
50
51 // Expands language codes to make these more suitable for Accept-Language.
52 // Example: ['en-US', 'ja', 'en-CA'] => ['en-US', 'en', 'ja', 'en-CA'].
53 // 'en' won't appear twice as this function eliminates duplicates.
54 void ExpandLanguageCodes(const std::vector<std::string>& languages,
55                          std::vector<std::string>* expanded_languages) {
56   DCHECK(expanded_languages);
57   DCHECK(expanded_languages->empty());
58
59   // used to eliminate duplicates.
60   std::set<std::string> seen;
61
62   for (std::vector<std::string>::const_iterator it = languages.begin();
63        it != languages.end(); ++it) {
64     const std::string& language = *it;
65     if (seen.find(language) == seen.end()) {
66       expanded_languages->push_back(language);
67       seen.insert(language);
68     }
69
70     std::vector<std::string> tokens;
71     base::SplitString(language, '-', &tokens);
72     if (tokens.size() == 0)
73       continue;
74     const std::string& main_part = tokens[0];
75     if (seen.find(main_part) == seen.end()) {
76       expanded_languages->push_back(main_part);
77       seen.insert(main_part);
78     }
79   }
80 }
81
82 }  // namespace
83
84 TranslatePrefs::TranslatePrefs(PrefService* user_prefs)
85     : prefs_(user_prefs) {
86 }
87
88 void TranslatePrefs::ResetToDefaults() {
89   ClearBlockedLanguages();
90   ClearBlacklistedSites();
91   ClearWhitelistedLanguagePairs();
92
93   std::vector<std::string> languages;
94   GetLanguageList(&languages);
95   for (std::vector<std::string>::const_iterator it = languages.begin();
96       it != languages.end(); ++it) {
97     const std::string& language = *it;
98     ResetTranslationAcceptedCount(language);
99     ResetTranslationDeniedCount(language);
100   }
101 }
102
103 bool TranslatePrefs::IsBlockedLanguage(
104     const std::string& original_language) const {
105   return IsValueBlacklisted(kPrefTranslateBlockedLanguages,
106                             original_language);
107 }
108
109 void TranslatePrefs::BlockLanguage(
110     const std::string& original_language) {
111   BlacklistValue(kPrefTranslateBlockedLanguages, original_language);
112
113   // Add the language to the language list at chrome://settings/languages.
114   std::string language = original_language;
115   translate::ToChromeLanguageSynonym(&language);
116
117   std::vector<std::string> languages;
118   GetLanguageList(&languages);
119
120   if (std::find(languages.begin(), languages.end(), language) ==
121       languages.end()) {
122     languages.push_back(language);
123     UpdateLanguageList(languages);
124   }
125 }
126
127 void TranslatePrefs::UnblockLanguage(
128     const std::string& original_language) {
129   RemoveValueFromBlacklist(kPrefTranslateBlockedLanguages,
130                            original_language);
131 }
132
133 void TranslatePrefs::RemoveLanguageFromLegacyBlacklist(
134     const std::string& original_language) {
135   RemoveValueFromBlacklist(kPrefTranslateLanguageBlacklist,
136                            original_language);
137 }
138
139 bool TranslatePrefs::IsSiteBlacklisted(const std::string& site) const {
140   return IsValueBlacklisted(kPrefTranslateSiteBlacklist, site);
141 }
142
143 void TranslatePrefs::BlacklistSite(const std::string& site) {
144   BlacklistValue(kPrefTranslateSiteBlacklist, site);
145 }
146
147 void TranslatePrefs::RemoveSiteFromBlacklist(const std::string& site) {
148   RemoveValueFromBlacklist(kPrefTranslateSiteBlacklist, site);
149 }
150
151 bool TranslatePrefs::IsLanguagePairWhitelisted(
152     const std::string& original_language,
153     const std::string& target_language) {
154   const base::DictionaryValue* dict =
155       prefs_->GetDictionary(kPrefTranslateWhitelists);
156   if (dict && !dict->empty()) {
157     std::string auto_target_lang;
158     if (dict->GetString(original_language, &auto_target_lang) &&
159         auto_target_lang == target_language)
160       return true;
161   }
162   return false;
163 }
164
165 void TranslatePrefs::WhitelistLanguagePair(
166     const std::string& original_language,
167     const std::string& target_language) {
168   DictionaryPrefUpdate update(prefs_, kPrefTranslateWhitelists);
169   base::DictionaryValue* dict = update.Get();
170   if (!dict) {
171     NOTREACHED() << "Unregistered translate whitelist pref";
172     return;
173   }
174   dict->SetString(original_language, target_language);
175 }
176
177 void TranslatePrefs::RemoveLanguagePairFromWhitelist(
178     const std::string& original_language,
179     const std::string& target_language) {
180   DictionaryPrefUpdate update(prefs_, kPrefTranslateWhitelists);
181   base::DictionaryValue* dict = update.Get();
182   if (!dict) {
183     NOTREACHED() << "Unregistered translate whitelist pref";
184     return;
185   }
186   dict->Remove(original_language, NULL);
187 }
188
189 bool TranslatePrefs::HasBlockedLanguages() const {
190   return !IsListEmpty(kPrefTranslateBlockedLanguages);
191 }
192
193 void TranslatePrefs::ClearBlockedLanguages() {
194   prefs_->ClearPref(kPrefTranslateBlockedLanguages);
195 }
196
197 bool TranslatePrefs::HasBlacklistedSites() const {
198   return !IsListEmpty(kPrefTranslateSiteBlacklist);
199 }
200
201 void TranslatePrefs::ClearBlacklistedSites() {
202   prefs_->ClearPref(kPrefTranslateSiteBlacklist);
203 }
204
205 bool TranslatePrefs::HasWhitelistedLanguagePairs() const {
206   return !IsDictionaryEmpty(kPrefTranslateWhitelists);
207 }
208
209 void TranslatePrefs::ClearWhitelistedLanguagePairs() {
210   prefs_->ClearPref(kPrefTranslateWhitelists);
211 }
212
213 int TranslatePrefs::GetTranslationDeniedCount(
214     const std::string& language) const {
215   const base::DictionaryValue* dict =
216       prefs_->GetDictionary(kPrefTranslateDeniedCount);
217   int count = 0;
218   return dict->GetInteger(language, &count) ? count : 0;
219 }
220
221 void TranslatePrefs::IncrementTranslationDeniedCount(
222     const std::string& language) {
223   DictionaryPrefUpdate update(prefs_, kPrefTranslateDeniedCount);
224   base::DictionaryValue* dict = update.Get();
225
226   int count = 0;
227   dict->GetInteger(language, &count);
228   dict->SetInteger(language, count + 1);
229 }
230
231 void TranslatePrefs::ResetTranslationDeniedCount(const std::string& language) {
232   DictionaryPrefUpdate update(prefs_, kPrefTranslateDeniedCount);
233   update.Get()->SetInteger(language, 0);
234 }
235
236 int TranslatePrefs::GetTranslationAcceptedCount(const std::string& language) {
237   const base::DictionaryValue* dict =
238       prefs_->GetDictionary(kPrefTranslateAcceptedCount);
239   int count = 0;
240   return dict->GetInteger(language, &count) ? count : 0;
241 }
242
243 void TranslatePrefs::IncrementTranslationAcceptedCount(
244     const std::string& language) {
245   DictionaryPrefUpdate update(prefs_, kPrefTranslateAcceptedCount);
246   base::DictionaryValue* dict = update.Get();
247   int count = 0;
248   dict->GetInteger(language, &count);
249   dict->SetInteger(language, count + 1);
250 }
251
252 void TranslatePrefs::ResetTranslationAcceptedCount(
253     const std::string& language) {
254   DictionaryPrefUpdate update(prefs_, kPrefTranslateAcceptedCount);
255   update.Get()->SetInteger(language, 0);
256 }
257
258 void TranslatePrefs::GetLanguageList(std::vector<std::string>* languages) {
259   DCHECK(languages);
260   DCHECK(languages->empty());
261
262 #if defined(OS_CHROMEOS)
263   const char* key = prefs::kLanguagePreferredLanguages;
264 #else
265   const char* key = prefs::kAcceptLanguages;
266 #endif
267
268   std::string languages_str = prefs_->GetString(key);
269   base::SplitString(languages_str, ',', languages);
270 }
271
272 void TranslatePrefs::UpdateLanguageList(
273     const std::vector<std::string>& languages) {
274 #if defined(OS_CHROMEOS)
275   std::string languages_str = JoinString(languages, ',');
276   prefs_->SetString(prefs::kLanguagePreferredLanguages, languages_str);
277 #endif
278
279   // Save the same language list as accept languages preference as well, but we
280   // need to expand the language list, to make it more acceptable. For instance,
281   // some web sites don't understand 'en-US' but 'en'. See crosbug.com/9884.
282   std::vector<std::string> accept_languages;
283   ExpandLanguageCodes(languages, &accept_languages);
284   std::string accept_languages_str = JoinString(accept_languages, ',');
285   prefs_->SetString(prefs::kAcceptLanguages, accept_languages_str);
286 }
287
288 // static
289 bool TranslatePrefs::CanTranslateLanguage(Profile* profile,
290                                           const std::string& language) {
291   TranslatePrefs translate_prefs(profile->GetPrefs());
292   bool blocked = translate_prefs.IsBlockedLanguage(language);
293
294   bool is_accept_language =
295       TranslateManager::IsAcceptLanguage(profile, language);
296   bool can_be_accept_language =
297       TranslateAcceptLanguages::CanBeAcceptLanguage(language);
298
299   // Don't translate any user black-listed languages. Checking
300   // |is_accept_language| is necessary because if the user eliminates the
301   // language from the preference, it is natural to forget whether or not
302   // the language should be translated. Checking |cannot_be_accept_language|
303   // is also necessary because some minor languages can't be selected in the
304   // language preference even though the language is available in Translate
305   // server.
306   if (blocked && (is_accept_language || !can_be_accept_language))
307     return false;
308
309   return true;
310 }
311
312 // static
313 bool TranslatePrefs::ShouldAutoTranslate(PrefService* user_prefs,
314     const std::string& original_language, std::string* target_language) {
315   TranslatePrefs prefs(user_prefs);
316   return prefs.IsLanguageWhitelisted(original_language, target_language);
317 }
318
319 // static
320 void TranslatePrefs::RegisterProfilePrefs(
321     user_prefs::PrefRegistrySyncable* registry) {
322   registry->RegisterListPref(kPrefTranslateLanguageBlacklist,
323                              user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
324   registry->RegisterListPref(kPrefTranslateSiteBlacklist,
325                              user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
326   registry->RegisterDictionaryPref(
327       kPrefTranslateWhitelists,
328       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
329   registry->RegisterDictionaryPref(
330       kPrefTranslateDeniedCount,
331       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
332   registry->RegisterDictionaryPref(
333       kPrefTranslateAcceptedCount,
334       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
335   registry->RegisterListPref(kPrefTranslateBlockedLanguages,
336                              user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
337 }
338
339 // static
340 void TranslatePrefs::MigrateUserPrefs(PrefService* user_prefs) {
341   // Old format of kPrefTranslateWhitelists
342   // - original language -> list of target langs to auto-translate
343   // - list of langs is in order of being enabled i.e. last in list is the
344   //   most recent language that user enabled via
345   //   Always translate |source_lang| to |target_lang|"
346   // - this results in a one-to-n relationship between source lang and target
347   //   langs.
348   // New format:
349   // - original language -> one target language to auto-translate
350   // - each time that the user enables the "Always translate..." option, that
351   //   target lang overwrites the previous one.
352   // - this results in a one-to-one relationship between source lang and target
353   //   lang
354   // - we replace old list of target langs with the last target lang in list,
355   //   assuming the last (i.e. most recent) target lang is what user wants to
356   //   keep auto-translated.
357   DictionaryPrefUpdate update(user_prefs, kPrefTranslateWhitelists);
358   base::DictionaryValue* dict = update.Get();
359   if (dict && !dict->empty()) {
360     base::DictionaryValue::Iterator iter(*dict);
361     while (!iter.IsAtEnd()) {
362       const base::ListValue* list = NULL;
363       if (!iter.value().GetAsList(&list) || !list)
364         break;  // Dictionary has either been migrated or new format.
365       std::string key = iter.key();
366       // Advance the iterator before removing the current element.
367       iter.Advance();
368       std::string target_lang;
369       if (list->empty() ||
370           !list->GetString(list->GetSize() - 1, &target_lang) ||
371           target_lang.empty()) {
372         dict->Remove(key, NULL);
373       } else {
374         dict->SetString(key, target_lang);
375       }
376     }
377   }
378
379   // Get the union of the blacklist and the Accept languages, and set this to
380   // the new language set 'translate_blocked_languages'. This is used for the
381   // settings UI for Translate and configration to determine which langauage
382   // should be translated instead of the blacklist. The blacklist is no longer
383   // used after launching the settings UI.
384   // After that, Set 'translate_languages_not_translate' to Accept languages to
385   // enable settings for users.
386   bool merged = user_prefs->HasPrefPath(kPrefTranslateBlockedLanguages);
387
388   if (!merged) {
389     std::vector<std::string> blacklisted_languages;
390     GetBlacklistedLanguages(user_prefs, &blacklisted_languages);
391
392     std::string accept_languages_str =
393         user_prefs->GetString(prefs::kAcceptLanguages);
394     std::vector<std::string> accept_languages;
395     base::SplitString(accept_languages_str, ',', &accept_languages);
396
397     std::vector<std::string> blocked_languages;
398     CreateBlockedLanguages(&blocked_languages,
399                            blacklisted_languages,
400                            accept_languages);
401
402     // Create the new preference kPrefTranslateBlockedLanguages.
403     {
404       base::ListValue blocked_languages_list;
405       for (std::vector<std::string>::const_iterator it =
406                blocked_languages.begin();
407            it != blocked_languages.end(); ++it) {
408         blocked_languages_list.Append(new base::StringValue(*it));
409       }
410       ListPrefUpdate update(user_prefs, kPrefTranslateBlockedLanguages);
411       base::ListValue* list = update.Get();
412       DCHECK(list != NULL);
413       list->Swap(&blocked_languages_list);
414     }
415
416     // Update kAcceptLanguages
417     for (std::vector<std::string>::const_iterator it =
418              blocked_languages.begin();
419          it != blocked_languages.end(); ++it) {
420       std::string lang = *it;
421       translate::ToChromeLanguageSynonym(&lang);
422       bool not_found =
423           std::find(accept_languages.begin(), accept_languages.end(), lang) ==
424           accept_languages.end();
425       if (not_found)
426         accept_languages.push_back(lang);
427     }
428
429     std::string new_accept_languages_str = JoinString(accept_languages, ",");
430     user_prefs->SetString(prefs::kAcceptLanguages, new_accept_languages_str);
431   }
432 }
433
434 // static
435 void TranslatePrefs::CreateBlockedLanguages(
436     std::vector<std::string>* blocked_languages,
437     const std::vector<std::string>& blacklisted_languages,
438     const std::vector<std::string>& accept_languages) {
439   DCHECK(blocked_languages);
440   DCHECK(blocked_languages->empty());
441
442   std::set<std::string> result;
443
444   for (std::vector<std::string>::const_iterator it =
445            blacklisted_languages.begin();
446        it != blacklisted_languages.end(); ++it) {
447     result.insert(*it);
448   }
449
450   const std::string& app_locale = g_browser_process->GetApplicationLocale();
451   std::string ui_lang = TranslateManager::GetLanguageCode(app_locale);
452   bool is_ui_english = ui_lang == "en" ||
453       StartsWithASCII(ui_lang, "en-", false);
454
455   for (std::vector<std::string>::const_iterator it = accept_languages.begin();
456        it != accept_languages.end(); ++it) {
457     std::string converted_lang = ConvertLangCodeForTranslation(*it);
458
459     // Regarding http://crbug.com/36182, even though English exists in Accept
460     // language list, English could be translated on non-English locale.
461     if (converted_lang == "en" && !is_ui_english)
462       continue;
463
464     result.insert(converted_lang);
465   }
466
467   blocked_languages->insert(blocked_languages->begin(),
468                             result.begin(), result.end());
469 }
470
471 // static
472 std::string TranslatePrefs::ConvertLangCodeForTranslation(
473     const std::string &lang) {
474   std::vector<std::string> tokens;
475   base::SplitString(lang, '-', &tokens);
476   if (tokens.size() < 1)
477     return lang;
478
479   std::string main_part = tokens[0];
480
481   // Translate doesn't support General Chinese and the sub code is necessary.
482   if (main_part == "zh")
483     return lang;
484
485   translate::ToTranslateLanguageSynonym(&main_part);
486   return main_part;
487 }
488
489 bool TranslatePrefs::IsValueInList(const base::ListValue* list,
490     const std::string& in_value) const {
491   for (size_t i = 0; i < list->GetSize(); ++i) {
492     std::string value;
493     if (list->GetString(i, &value) && value == in_value)
494       return true;
495   }
496   return false;
497 }
498
499 bool TranslatePrefs::IsValueBlacklisted(const char* pref_id,
500     const std::string& value) const {
501   const base::ListValue* blacklist = prefs_->GetList(pref_id);
502   return (blacklist && !blacklist->empty() && IsValueInList(blacklist, value));
503 }
504
505 void TranslatePrefs::BlacklistValue(const char* pref_id,
506     const std::string& value) {
507   {
508     ListPrefUpdate update(prefs_, pref_id);
509     base::ListValue* blacklist = update.Get();
510     if (!blacklist) {
511       NOTREACHED() << "Unregistered translate blacklist pref";
512       return;
513     }
514     blacklist->Append(new base::StringValue(value));
515   }
516 }
517
518 void TranslatePrefs::RemoveValueFromBlacklist(const char* pref_id,
519     const std::string& value) {
520   ListPrefUpdate update(prefs_, pref_id);
521   base::ListValue* blacklist = update.Get();
522   if (!blacklist) {
523     NOTREACHED() << "Unregistered translate blacklist pref";
524     return;
525   }
526   base::StringValue string_value(value);
527   blacklist->Remove(string_value, NULL);
528 }
529
530 bool TranslatePrefs::IsLanguageWhitelisted(
531     const std::string& original_language, std::string* target_language) const {
532   const base::DictionaryValue* dict =
533       prefs_->GetDictionary(kPrefTranslateWhitelists);
534   if (dict && dict->GetString(original_language, target_language)) {
535     DCHECK(!target_language->empty());
536     return !target_language->empty();
537   }
538   return false;
539 }
540
541 bool TranslatePrefs::IsListEmpty(const char* pref_id) const {
542   const base::ListValue* blacklist = prefs_->GetList(pref_id);
543   return (blacklist == NULL || blacklist->empty());
544 }
545
546 bool TranslatePrefs::IsDictionaryEmpty(const char* pref_id) const {
547   const base::DictionaryValue* dict = prefs_->GetDictionary(pref_id);
548   return (dict == NULL || dict->empty());
549 }