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