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