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