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