Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/autofill/core/browser/country_names.h" | 5 #include "components/autofill/core/browser/country_names.h" |
| 6 | 6 |
| 7 #include <stdint.h> | 7 #include <stdint.h> |
| 8 #include <utility> | 8 #include <utility> |
| 9 | 9 |
| 10 #include "base/lazy_instance.h" | |
| 10 #include "base/logging.h" | 11 #include "base/logging.h" |
| 11 #include "base/memory/scoped_ptr.h" | 12 #include "base/memory/scoped_ptr.h" |
| 12 #include "base/memory/singleton.h" | 13 #include "base/memory/singleton.h" |
| 13 #include "base/stl_util.h" | 14 #include "base/stl_util.h" |
| 14 #include "base/strings/string_util.h" | 15 #include "base/strings/string_util.h" |
| 15 #include "base/strings/utf_string_conversions.h" | 16 #include "base/strings/utf_string_conversions.h" |
| 16 #include "components/autofill/core/browser/country_data.h" | 17 #include "components/autofill/core/browser/country_data.h" |
| 17 #include "components/autofill/core/common/autofill_l10n_util.h" | 18 #include "components/autofill/core/common/autofill_l10n_util.h" |
| 18 #include "third_party/icu/source/common/unicode/unistr.h" | 19 #include "third_party/icu/source/common/unicode/unistr.h" |
| 19 #include "ui/base/l10n/l10n_util.h" | 20 #include "ui/base/l10n/l10n_util.h" |
| 20 | 21 |
| 21 namespace autofill { | 22 namespace autofill { |
| 22 namespace { | 23 namespace { |
| 23 | 24 |
| 25 // A copy of the application locale string, which should be ready for | |
| 26 // CountryName's construction. | |
| 27 static base::LazyInstance<std::string> g_application_locale = | |
| 28 LAZY_INSTANCE_INITIALIZER; | |
| 29 | |
| 24 // Returns the ICU sort key corresponding to |str| for the given |collator|. | 30 // Returns the ICU sort key corresponding to |str| for the given |collator|. |
| 25 // Uses |buffer| as temporary storage, and might resize |buffer| as a side- | 31 // Uses |buffer| as temporary storage, and might resize |buffer| as a side- |
| 26 // effect. |buffer_size| should specify the |buffer|'s size, and is updated if | 32 // effect. |buffer_size| should specify the |buffer|'s size, and is updated if |
| 27 // the |buffer| is resized. | 33 // the |buffer| is resized. |
| 28 const std::string GetSortKey(const icu::Collator& collator, | 34 const std::string GetSortKey(const icu::Collator& collator, |
| 29 const base::string16& str, | 35 const base::string16& str, |
| 30 scoped_ptr<uint8_t[]>* buffer, | 36 scoped_ptr<uint8_t[]>* buffer, |
| 31 int32_t* buffer_size) { | 37 int32_t* buffer_size) { |
| 32 DCHECK(buffer); | 38 DCHECK(buffer); |
| 33 DCHECK(buffer_size); | 39 DCHECK(buffer_size); |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 67 // Add a few other common synonyms. | 73 // Add a few other common synonyms. |
| 68 common_names.insert(std::make_pair("UNITED STATES OF AMERICA", "US")); | 74 common_names.insert(std::make_pair("UNITED STATES OF AMERICA", "US")); |
| 69 common_names.insert(std::make_pair("U.S.A.", "US")); | 75 common_names.insert(std::make_pair("U.S.A.", "US")); |
| 70 common_names.insert(std::make_pair("GREAT BRITAIN", "GB")); | 76 common_names.insert(std::make_pair("GREAT BRITAIN", "GB")); |
| 71 common_names.insert(std::make_pair("UK", "GB")); | 77 common_names.insert(std::make_pair("UK", "GB")); |
| 72 common_names.insert(std::make_pair("BRASIL", "BR")); | 78 common_names.insert(std::make_pair("BRASIL", "BR")); |
| 73 common_names.insert(std::make_pair("DEUTSCHLAND", "DE")); | 79 common_names.insert(std::make_pair("DEUTSCHLAND", "DE")); |
| 74 return common_names; | 80 return common_names; |
| 75 } | 81 } |
| 76 | 82 |
| 83 // Creates collator for |locale| and sets its attributes as needed. | |
| 84 scoped_ptr<icu::Collator> CreateCollator(const icu::Locale& locale) { | |
| 85 scoped_ptr<icu::Collator> collator( | |
| 86 autofill::l10n::GetCollatorForLocale(locale)); | |
| 87 if (!collator) | |
| 88 return nullptr; | |
| 89 | |
| 90 // Compare case-insensitively and ignoring punctuation. | |
| 91 UErrorCode ignored = U_ZERO_ERROR; | |
| 92 collator->setAttribute(UCOL_STRENGTH, UCOL_SECONDARY, ignored); | |
| 93 ignored = U_ZERO_ERROR; | |
| 94 collator->setAttribute(UCOL_ALTERNATE_HANDLING, UCOL_SHIFTED, ignored); | |
| 95 | |
| 96 return collator; | |
| 97 } | |
| 98 | |
| 99 // If |locale| is different from "en_US", returns a collator for "en_US" and | |
| 100 // sets its attributes as appropriate. Otherwise returns null. | |
| 101 scoped_ptr<icu::Collator> CreateDefaultCollator(const icu::Locale& locale) { | |
| 102 icu::Locale default_locale("en_US"); | |
| 103 | |
| 104 if (default_locale != locale) | |
| 105 return CreateCollator(default_locale); | |
| 106 | |
| 107 return nullptr; | |
| 108 } | |
| 109 | |
| 110 // Returns the mapping of country names localized to |locale| to their | |
| 111 // corresponding country codes. The provided |collator| should be suitable for | |
| 112 // the locale. The collator being null is handled gracefully by returning an | |
| 113 // empty map, to account for the very rare cases when the collator fails to | |
| 114 // initialize. | |
| 115 std::map<std::string, std::string> GetLocalizedNames( | |
| 116 const std::string& locale, | |
| 117 const icu::Collator* collator) { | |
| 118 if (!collator) | |
| 119 return std::map<std::string, std::string>(); | |
| 120 | |
| 121 std::map<std::string, std::string> localized_names; | |
| 122 int32_t buffer_size = 1000; | |
| 123 scoped_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]); | |
| 124 | |
| 125 for (const std::string& country_code : | |
| 126 CountryDataMap::GetInstance()->country_codes()) { | |
| 127 base::string16 country_name = | |
| 128 l10n_util::GetDisplayNameForCountry(country_code, locale); | |
| 129 std::string sort_key = | |
| 130 GetSortKey(*collator, country_name, &buffer, &buffer_size); | |
| 131 | |
| 132 localized_names.insert(std::make_pair(sort_key, country_code)); | |
| 133 } | |
| 134 return localized_names; | |
| 135 } | |
| 136 | |
| 77 } // namespace | 137 } // namespace |
| 78 | 138 |
| 79 // static | 139 // static |
| 80 CountryNames* CountryNames::GetInstance() { | 140 CountryNames* CountryNames::GetInstance() { |
| 81 return base::Singleton<CountryNames>::get(); | 141 return base::Singleton<CountryNames>::get(); |
| 82 } | 142 } |
| 83 | 143 |
| 84 CountryNames::CountryNames() : common_names_(GetCommonNames()) {} | 144 // static |
| 145 void CountryNames::SetLocaleString(std::string locale) { | |
| 146 DCHECK(!locale.empty()); | |
| 147 // Application locale should never be empty. The empty value of | |
| 148 // |g_application_locale| means that it has not been initialized yet. | |
| 149 std::string* const storage = g_application_locale.Pointer(); | |
|
Ilya Sherman
2016/01/23 01:41:04
nit: The const here seems not very necessary.
vabr (Chromium)
2016/01/25 10:36:44
A matter of taste, I guess :), but removed.
| |
| 150 if (storage->empty()) { | |
| 151 *storage = std::move(locale); | |
| 152 } | |
| 153 // TODO(crbug.com/579971) CountryNames currently cannot adapt to changed | |
| 154 // locale without Chrome's restart. | |
| 155 } | |
| 156 | |
| 157 CountryNames::CountryNames(const std::string& locale_name) | |
| 158 : locale_(locale_name.c_str()), | |
| 159 collator_(CreateCollator(locale_)), | |
| 160 default_collator_(CreateDefaultCollator(locale_)), | |
| 161 common_names_(GetCommonNames()), | |
| 162 localized_names_(GetLocalizedNames(locale_name, collator_.get())), | |
| 163 default_localized_names_( | |
| 164 GetLocalizedNames("en_US", default_collator_.get())) {} | |
| 165 | |
| 166 CountryNames::CountryNames() : CountryNames(g_application_locale.Get()) { | |
| 167 DCHECK(!g_application_locale.Get().empty()); | |
| 168 } | |
| 85 | 169 |
| 86 CountryNames::~CountryNames() = default; | 170 CountryNames::~CountryNames() = default; |
| 87 | 171 |
| 88 const std::string CountryNames::GetCountryCode(const base::string16& country, | 172 const std::string CountryNames::GetCountryCode(const base::string16& country) { |
| 89 const std::string& locale_name) { | |
| 90 // First, check common country names, including 2- and 3-letter country codes. | 173 // First, check common country names, including 2- and 3-letter country codes. |
| 91 std::string country_utf8 = base::UTF16ToUTF8(base::ToUpperASCII(country)); | 174 std::string country_utf8 = base::UTF16ToUTF8(base::ToUpperASCII(country)); |
| 92 const auto result = common_names_.find(country_utf8); | 175 const auto result = common_names_.find(country_utf8); |
| 93 if (result != common_names_.end()) | 176 if (result != common_names_.end()) |
| 94 return result->second; | 177 return result->second; |
| 95 | 178 |
| 96 // Next, check country names localized to the current locale. | 179 // Next, check country names localized to the current locale. |
| 97 icu::Locale locale(locale_name.c_str()); | 180 std::string country_code = |
| 98 std::string country_code = GetCountryCodeForLocalizedName(country, locale); | 181 GetCountryCodeForLocalizedName(country, localized_names_, *collator_); |
| 99 if (!country_code.empty()) | 182 if (!country_code.empty()) |
| 100 return country_code; | 183 return country_code; |
| 101 | 184 |
| 102 icu::Locale default_locale("en_US"); | |
| 103 // Finally, check country names localized to US English, unless done already. | 185 // Finally, check country names localized to US English, unless done already. |
| 104 if (default_locale != locale) | 186 if (default_collator_) { |
| 105 return GetCountryCodeForLocalizedName(country, default_locale); | 187 return GetCountryCodeForLocalizedName(country, default_localized_names_, |
| 188 *default_collator_); | |
| 189 } | |
| 106 | 190 |
| 107 return std::string(); | 191 return std::string(); |
| 108 } | 192 } |
| 109 | 193 |
| 110 void CountryNames::AddLocalizedNamesForLocale(const std::string& locale, | |
| 111 const icu::Collator& collator) { | |
| 112 // Nothing to do if we've previously added the localized names for the given | |
| 113 // |locale|. | |
| 114 if (locales_to_localized_names_.count(locale)) | |
| 115 return; | |
| 116 | |
| 117 std::map<std::string, std::string> localized_names; | |
| 118 int32_t buffer_size = 1000; | |
| 119 scoped_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]); | |
| 120 | |
| 121 for (const std::string& country_code : | |
| 122 CountryDataMap::GetInstance()->country_codes()) { | |
| 123 base::string16 country_name = | |
| 124 l10n_util::GetDisplayNameForCountry(country_code, locale); | |
| 125 std::string sort_key = | |
| 126 GetSortKey(collator, country_name, &buffer, &buffer_size); | |
| 127 | |
| 128 localized_names.insert(std::make_pair(sort_key, country_code)); | |
| 129 } | |
| 130 | |
| 131 locales_to_localized_names_.insert(std::make_pair(locale, localized_names)); | |
| 132 } | |
| 133 | |
| 134 const std::string CountryNames::GetCountryCodeForLocalizedName( | 194 const std::string CountryNames::GetCountryCodeForLocalizedName( |
| 135 const base::string16& country_name, | 195 const base::string16& country_name, |
| 136 const icu::Locale& locale) { | 196 const std::map<std::string, std::string>& localized_names, |
| 137 const icu::Collator* collator = GetCollatorForLocale(locale); | 197 const icu::Collator& collator) { |
| 138 // In very rare cases, the collator fails to initialize. | |
| 139 if (!collator) | |
| 140 return std::string(); | |
| 141 | |
| 142 std::string locale_name = locale.getName(); | |
| 143 AddLocalizedNamesForLocale(locale_name, *collator); | |
| 144 | |
| 145 // As recommended[1] by ICU, initialize the buffer size to four times the | 198 // As recommended[1] by ICU, initialize the buffer size to four times the |
| 146 // source string length. | 199 // source string length. |
| 147 // [1] http://userguide.icu-project.org/collation/api#TOC-Examples | 200 // [1] http://userguide.icu-project.org/collation/api#TOC-Examples |
| 148 int32_t buffer_size = country_name.size() * 4; | 201 int32_t buffer_size = country_name.size() * 4; |
| 149 scoped_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]); | 202 scoped_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]); |
| 150 std::string sort_key = | 203 std::string sort_key = |
| 151 GetSortKey(*collator, country_name, &buffer, &buffer_size); | 204 GetSortKey(collator, country_name, &buffer, &buffer_size); |
| 152 | 205 |
| 153 const std::map<std::string, std::string>& localized_names = | 206 auto result = localized_names.find(sort_key); |
| 154 locales_to_localized_names_[locale_name]; | |
| 155 std::map<std::string, std::string>::const_iterator result = | |
| 156 localized_names.find(sort_key); | |
| 157 | 207 |
| 158 if (result != localized_names.end()) | 208 if (result != localized_names.end()) |
| 159 return result->second; | 209 return result->second; |
| 160 | 210 |
| 161 return std::string(); | 211 return std::string(); |
| 162 } | 212 } |
| 163 | 213 |
| 164 const icu::Collator* CountryNames::GetCollatorForLocale( | |
| 165 const icu::Locale& locale) { | |
| 166 std::string locale_name = locale.getName(); | |
| 167 if (!ContainsKey(collators_, locale_name)) { | |
| 168 scoped_ptr<icu::Collator> collator( | |
| 169 autofill::l10n::GetCollatorForLocale(locale)); | |
| 170 if (!collator) | |
| 171 return nullptr; | |
| 172 | |
| 173 // Compare case-insensitively and ignoring punctuation. | |
| 174 UErrorCode ignored = U_ZERO_ERROR; | |
| 175 collator->setAttribute(UCOL_STRENGTH, UCOL_SECONDARY, ignored); | |
| 176 ignored = U_ZERO_ERROR; | |
| 177 collator->setAttribute(UCOL_ALTERNATE_HANDLING, UCOL_SHIFTED, ignored); | |
| 178 | |
| 179 collators_[locale_name] = std::move(collator); | |
| 180 } | |
| 181 | |
| 182 return collators_[locale_name].get(); | |
| 183 } | |
| 184 | |
| 185 } // namespace autofill | 214 } // namespace autofill |
| OLD | NEW |