Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / translate / translate_prefs.cc
blob33927b4656f8979dad345813a339b96d8fc14484
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"
7 #include <set>
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";
34 namespace {
36 void GetBlacklistedLanguages(const PrefService* prefs,
37 std::vector<std::string>* languages) {
38 DCHECK(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) {
45 std::string lang;
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)
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);
82 } // namespace
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,
106 original_language);
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) ==
121 languages.end()) {
122 languages.push_back(language);
123 UpdateLanguageList(languages);
127 void TranslatePrefs::UnblockLanguage(
128 const std::string& original_language) {
129 RemoveValueFromBlacklist(kPrefTranslateBlockedLanguages,
130 original_language);
133 void TranslatePrefs::RemoveLanguageFromLegacyBlacklist(
134 const std::string& original_language) {
135 RemoveValueFromBlacklist(kPrefTranslateLanguageBlacklist,
136 original_language);
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)
160 return true;
162 return false;
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;
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();
182 if (!dict) {
183 NOTREACHED() << "Unregistered translate whitelist pref";
184 return;
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);
217 int count = 0;
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();
226 int count = 0;
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);
239 int count = 0;
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();
247 int count = 0;
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) {
259 DCHECK(languages);
260 DCHECK(languages->empty());
262 #if defined(OS_CHROMEOS)
263 const char* key = prefs::kLanguagePreferredLanguages;
264 #else
265 const char* key = prefs::kAcceptLanguages;
266 #endif
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);
277 #endif
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);
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);
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
305 // server.
306 if (blocked && (is_accept_language || !can_be_accept_language))
307 return false;
309 return true;
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);
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);
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);
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);
388 if (!merged) {
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,
400 accept_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);
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);
429 std::string new_accept_languages_str = JoinString(accept_languages, ",");
430 user_prefs->SetString(prefs::kAcceptLanguages, new_accept_languages_str);
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());
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) {
447 result.insert(*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)
462 continue;
464 result.insert(converted_lang);
467 blocked_languages->insert(blocked_languages->begin(),
468 result.begin(), result.end());
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;
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")
483 return lang;
485 translate::ToTranslateLanguageSynonym(&main_part);
486 return 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) {
492 std::string value;
493 if (list->GetString(i, &value) && value == in_value)
494 return true;
496 return false;
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();
510 if (!blacklist) {
511 NOTREACHED() << "Unregistered translate blacklist pref";
512 return;
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();
522 if (!blacklist) {
523 NOTREACHED() << "Unregistered translate blacklist pref";
524 return;
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();
538 return false;
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());