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