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.
5 #include "chrome/browser/translate/translate_prefs.h"
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"
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";
36 void GetBlacklistedLanguages(const PrefService
* prefs
,
37 std::vector
<std::string
>* languages
) {
39 DCHECK(languages
->empty());
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
) {
46 (*it
)->GetAsString(&lang
);
47 languages
->push_back(lang
);
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());
59 // used to eliminate duplicates.
60 std::set
<std::string
> seen
;
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
);
70 std::vector
<std::string
> tokens
;
71 base::SplitString(language
, '-', &tokens
);
72 if (tokens
.size() == 0)
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
);
84 TranslatePrefs::TranslatePrefs(PrefService
* user_prefs
)
85 : prefs_(user_prefs
) {
88 void TranslatePrefs::ResetToDefaults() {
89 ClearBlockedLanguages();
90 ClearBlacklistedSites();
91 ClearWhitelistedLanguagePairs();
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
);
103 bool TranslatePrefs::IsBlockedLanguage(
104 const std::string
& original_language
) const {
105 return IsValueBlacklisted(kPrefTranslateBlockedLanguages
,
109 void TranslatePrefs::BlockLanguage(
110 const std::string
& original_language
) {
111 BlacklistValue(kPrefTranslateBlockedLanguages
, original_language
);
113 // Add the language to the language list at chrome://settings/languages.
114 std::string language
= original_language
;
115 translate::ToChromeLanguageSynonym(&language
);
117 std::vector
<std::string
> languages
;
118 GetLanguageList(&languages
);
120 if (std::find(languages
.begin(), languages
.end(), language
) ==
122 languages
.push_back(language
);
123 UpdateLanguageList(languages
);
127 void TranslatePrefs::UnblockLanguage(
128 const std::string
& original_language
) {
129 RemoveValueFromBlacklist(kPrefTranslateBlockedLanguages
,
133 void TranslatePrefs::RemoveLanguageFromLegacyBlacklist(
134 const std::string
& original_language
) {
135 RemoveValueFromBlacklist(kPrefTranslateLanguageBlacklist
,
139 bool TranslatePrefs::IsSiteBlacklisted(const std::string
& site
) const {
140 return IsValueBlacklisted(kPrefTranslateSiteBlacklist
, site
);
143 void TranslatePrefs::BlacklistSite(const std::string
& site
) {
144 BlacklistValue(kPrefTranslateSiteBlacklist
, site
);
147 void TranslatePrefs::RemoveSiteFromBlacklist(const std::string
& site
) {
148 RemoveValueFromBlacklist(kPrefTranslateSiteBlacklist
, site
);
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
)
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();
171 NOTREACHED() << "Unregistered translate whitelist pref";
174 dict
->SetString(original_language
, 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();
183 NOTREACHED() << "Unregistered translate whitelist pref";
186 dict
->Remove(original_language
, NULL
);
189 bool TranslatePrefs::HasBlockedLanguages() const {
190 return !IsListEmpty(kPrefTranslateBlockedLanguages
);
193 void TranslatePrefs::ClearBlockedLanguages() {
194 prefs_
->ClearPref(kPrefTranslateBlockedLanguages
);
197 bool TranslatePrefs::HasBlacklistedSites() const {
198 return !IsListEmpty(kPrefTranslateSiteBlacklist
);
201 void TranslatePrefs::ClearBlacklistedSites() {
202 prefs_
->ClearPref(kPrefTranslateSiteBlacklist
);
205 bool TranslatePrefs::HasWhitelistedLanguagePairs() const {
206 return !IsDictionaryEmpty(kPrefTranslateWhitelists
);
209 void TranslatePrefs::ClearWhitelistedLanguagePairs() {
210 prefs_
->ClearPref(kPrefTranslateWhitelists
);
213 int TranslatePrefs::GetTranslationDeniedCount(
214 const std::string
& language
) const {
215 const base::DictionaryValue
* dict
=
216 prefs_
->GetDictionary(kPrefTranslateDeniedCount
);
218 return dict
->GetInteger(language
, &count
) ? count
: 0;
221 void TranslatePrefs::IncrementTranslationDeniedCount(
222 const std::string
& language
) {
223 DictionaryPrefUpdate
update(prefs_
, kPrefTranslateDeniedCount
);
224 base::DictionaryValue
* dict
= update
.Get();
227 dict
->GetInteger(language
, &count
);
228 dict
->SetInteger(language
, count
+ 1);
231 void TranslatePrefs::ResetTranslationDeniedCount(const std::string
& language
) {
232 DictionaryPrefUpdate
update(prefs_
, kPrefTranslateDeniedCount
);
233 update
.Get()->SetInteger(language
, 0);
236 int TranslatePrefs::GetTranslationAcceptedCount(const std::string
& language
) {
237 const base::DictionaryValue
* dict
=
238 prefs_
->GetDictionary(kPrefTranslateAcceptedCount
);
240 return dict
->GetInteger(language
, &count
) ? count
: 0;
243 void TranslatePrefs::IncrementTranslationAcceptedCount(
244 const std::string
& language
) {
245 DictionaryPrefUpdate
update(prefs_
, kPrefTranslateAcceptedCount
);
246 base::DictionaryValue
* dict
= update
.Get();
248 dict
->GetInteger(language
, &count
);
249 dict
->SetInteger(language
, count
+ 1);
252 void TranslatePrefs::ResetTranslationAcceptedCount(
253 const std::string
& language
) {
254 DictionaryPrefUpdate
update(prefs_
, kPrefTranslateAcceptedCount
);
255 update
.Get()->SetInteger(language
, 0);
258 void TranslatePrefs::GetLanguageList(std::vector
<std::string
>* languages
) {
260 DCHECK(languages
->empty());
262 #if defined(OS_CHROMEOS)
263 const char* key
= prefs::kLanguagePreferredLanguages
;
265 const char* key
= prefs::kAcceptLanguages
;
268 std::string languages_str
= prefs_
->GetString(key
);
269 base::SplitString(languages_str
, ',', 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(prefs::kLanguagePreferredLanguages
, languages_str
);
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
);
289 bool TranslatePrefs::CanTranslateLanguage(Profile
* profile
,
290 const std::string
& language
) {
291 TranslatePrefs
translate_prefs(profile
->GetPrefs());
292 bool blocked
= translate_prefs
.IsBlockedLanguage(language
);
294 bool is_accept_language
=
295 TranslateManager::IsAcceptLanguage(profile
, language
);
296 bool can_be_accept_language
=
297 TranslateAcceptLanguages::CanBeAcceptLanguage(language
);
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
306 if (blocked
&& (is_accept_language
|| !can_be_accept_language
))
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
);
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
);
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
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
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.
368 std::string target_lang
;
370 !list
->GetString(list
->GetSize() - 1, &target_lang
) ||
371 target_lang
.empty()) {
372 dict
->Remove(key
, NULL
);
374 dict
->SetString(key
, target_lang
);
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
);
389 std::vector
<std::string
> blacklisted_languages
;
390 GetBlacklistedLanguages(user_prefs
, &blacklisted_languages
);
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
);
397 std::vector
<std::string
> blocked_languages
;
398 CreateBlockedLanguages(&blocked_languages
,
399 blacklisted_languages
,
402 // Create the new preference kPrefTranslateBlockedLanguages.
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
));
410 ListPrefUpdate
update(user_prefs
, kPrefTranslateBlockedLanguages
);
411 base::ListValue
* list
= update
.Get();
412 DCHECK(list
!= NULL
);
413 list
->Swap(&blocked_languages_list
);
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
);
423 std::find(accept_languages
.begin(), accept_languages
.end(), lang
) ==
424 accept_languages
.end();
426 accept_languages
.push_back(lang
);
429 std::string new_accept_languages_str
= JoinString(accept_languages
, ",");
430 user_prefs
->SetString(prefs::kAcceptLanguages
, new_accept_languages_str
);
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());
442 std::set
<std::string
> result
;
444 for (std::vector
<std::string
>::const_iterator it
=
445 blacklisted_languages
.begin();
446 it
!= blacklisted_languages
.end(); ++it
) {
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);
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
);
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
)
464 result
.insert(converted_lang
);
467 blocked_languages
->insert(blocked_languages
->begin(),
468 result
.begin(), result
.end());
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)
479 std::string main_part
= tokens
[0];
481 // Translate doesn't support General Chinese and the sub code is necessary.
482 if (main_part
== "zh")
485 translate::ToTranslateLanguageSynonym(&main_part
);
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
) {
493 if (list
->GetString(i
, &value
) && value
== in_value
)
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
));
505 void TranslatePrefs::BlacklistValue(const char* pref_id
,
506 const std::string
& value
) {
508 ListPrefUpdate
update(prefs_
, pref_id
);
509 base::ListValue
* blacklist
= update
.Get();
511 NOTREACHED() << "Unregistered translate blacklist pref";
514 blacklist
->Append(new base::StringValue(value
));
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();
523 NOTREACHED() << "Unregistered translate blacklist pref";
526 base::StringValue
string_value(value
);
527 blacklist
->Remove(string_value
, NULL
);
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();
541 bool TranslatePrefs::IsListEmpty(const char* pref_id
) const {
542 const base::ListValue
* blacklist
= prefs_
->GetList(pref_id
);
543 return (blacklist
== NULL
|| blacklist
->empty());
546 bool TranslatePrefs::IsDictionaryEmpty(const char* pref_id
) const {
547 const base::DictionaryValue
* dict
= prefs_
->GetDictionary(pref_id
);
548 return (dict
== NULL
|| dict
->empty());