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* storage = g_application_locale.Pointer(); |
| 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 |