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 string, to account for the very rare cases when the collator fails to | |
Ilya Sherman
2016/01/21 02:07:56
nit: s/string/map
vabr (Chromium)
2016/01/22 17:36:42
Done.
| |
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 | |
137 // If |locale| represents a different locale than "en_US", returns | |
138 // GetLocalizedNames("en_US", collator). Otherwise returns an empty map. | |
139 std::map<std::string, std::string> GetDefaultLocalizedNames( | |
140 const std::string& locale, | |
141 const icu::Collator* collator) { | |
142 icu::Locale locale_object(locale.c_str()); | |
143 icu::Locale default_locale("en_US"); | |
144 | |
145 if (default_locale != locale_object) | |
Ilya Sherman
2016/01/21 02:07:56
If you pass the default_collotor_ into this method
vabr (Chromium)
2016/01/22 17:36:41
You are correct, thanks for catching this!
Removed
| |
146 return GetLocalizedNames("en_US", collator); | |
147 | |
148 return std::map<std::string, std::string>(); | |
149 } | |
150 | |
77 } // namespace | 151 } // namespace |
78 | 152 |
153 void SetLocaleString(std::string locale) { | |
154 static bool initialized = false; | |
155 if (!initialized) { | |
156 initialized = true; | |
157 g_application_locale.Get() = std::move(locale); | |
158 } | |
Ilya Sherman
2016/01/21 02:07:56
I think it would be good to DCHECK that the locale
Ilya Sherman
2016/01/21 02:07:56
Tracking |initialized| here seems not necessary --
vabr (Chromium)
2016/01/22 17:36:42
I'm not sure how to do that. LazyInstance does not
vabr (Chromium)
2016/01/22 17:36:42
So apparently, Chrome might be expected to be able
| |
159 } | |
160 | |
79 // static | 161 // static |
80 CountryNames* CountryNames::GetInstance() { | 162 CountryNames* CountryNames::GetInstance() { |
81 return base::Singleton<CountryNames>::get(); | 163 return base::Singleton<CountryNames>::get(); |
82 } | 164 } |
83 | 165 |
84 CountryNames::CountryNames() : common_names_(GetCommonNames()) {} | 166 CountryNames::CountryNames(const std::string& locale_name) |
167 : locale_(locale_name.c_str()), | |
168 collator_(CreateCollator(locale_)), | |
169 default_collator_(CreateDefaultCollator(locale_)), | |
170 common_names_(GetCommonNames()), | |
171 localized_names_(GetLocalizedNames(locale_name, collator_.get())), | |
172 default_localized_names_( | |
173 GetDefaultLocalizedNames(locale_name, collator_.get())) {} | |
Ilya Sherman
2016/01/21 02:07:56
Why doesn't this use the default_collator_?
vabr (Chromium)
2016/01/22 17:36:41
I got myself confused with all the default stuff,
| |
174 | |
175 CountryNames::CountryNames() : CountryNames(g_application_locale.Get()) {} | |
Ilya Sherman
2016/01/21 02:07:56
Please DCHECK that g_application_locale is non-emp
vabr (Chromium)
2016/01/22 17:36:42
Done.
| |
85 | 176 |
86 CountryNames::~CountryNames() = default; | 177 CountryNames::~CountryNames() = default; |
87 | 178 |
88 const std::string CountryNames::GetCountryCode(const base::string16& country, | 179 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. | 180 // First, check common country names, including 2- and 3-letter country codes. |
91 std::string country_utf8 = base::UTF16ToUTF8(base::ToUpperASCII(country)); | 181 std::string country_utf8 = base::UTF16ToUTF8(base::ToUpperASCII(country)); |
92 const auto result = common_names_.find(country_utf8); | 182 const auto result = common_names_.find(country_utf8); |
93 if (result != common_names_.end()) | 183 if (result != common_names_.end()) |
94 return result->second; | 184 return result->second; |
95 | 185 |
96 // Next, check country names localized to the current locale. | 186 // Next, check country names localized to the current locale. |
97 icu::Locale locale(locale_name.c_str()); | 187 std::string country_code = |
98 std::string country_code = GetCountryCodeForLocalizedName(country, locale); | 188 GetCountryCodeForLocalizedName(country, localized_names_, *collator_); |
99 if (!country_code.empty()) | 189 if (!country_code.empty()) |
100 return country_code; | 190 return country_code; |
101 | 191 |
102 icu::Locale default_locale("en_US"); | 192 icu::Locale default_locale("en_US"); |
103 // Finally, check country names localized to US English, unless done already. | 193 // Finally, check country names localized to US English, unless done already. |
104 if (default_locale != locale) | 194 if (default_locale != locale_) { |
105 return GetCountryCodeForLocalizedName(country, default_locale); | 195 DCHECK(default_collator_); |
Ilya Sherman
2016/01/21 02:07:56
nit: You could just write "if (default_collator_)"
vabr (Chromium)
2016/01/22 17:36:42
Done.
| |
196 return GetCountryCodeForLocalizedName(country, default_localized_names_, | |
197 *default_collator_); | |
198 } | |
106 | 199 |
107 return std::string(); | 200 return std::string(); |
108 } | 201 } |
109 | 202 |
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( | 203 const std::string CountryNames::GetCountryCodeForLocalizedName( |
135 const base::string16& country_name, | 204 const base::string16& country_name, |
136 const icu::Locale& locale) { | 205 const std::map<std::string, std::string>& localized_names, |
137 const icu::Collator* collator = GetCollatorForLocale(locale); | 206 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 | 207 // As recommended[1] by ICU, initialize the buffer size to four times the |
146 // source string length. | 208 // source string length. |
147 // [1] http://userguide.icu-project.org/collation/api#TOC-Examples | 209 // [1] http://userguide.icu-project.org/collation/api#TOC-Examples |
148 int32_t buffer_size = country_name.size() * 4; | 210 int32_t buffer_size = country_name.size() * 4; |
149 scoped_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]); | 211 scoped_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]); |
150 std::string sort_key = | 212 std::string sort_key = |
151 GetSortKey(*collator, country_name, &buffer, &buffer_size); | 213 GetSortKey(collator, country_name, &buffer, &buffer_size); |
152 | 214 |
153 const std::map<std::string, std::string>& localized_names = | 215 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 | 216 |
158 if (result != localized_names.end()) | 217 if (result != localized_names.end()) |
159 return result->second; | 218 return result->second; |
160 | 219 |
161 return std::string(); | 220 return std::string(); |
162 } | 221 } |
163 | 222 |
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 | 223 } // namespace autofill |
OLD | NEW |