Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(222)

Side by Side Diff: chrome/installer/util/language_selector.cc

Issue 4139010: The UI language rather than the locale is now used to pick Chrome's language ... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 10 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2010 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 // This file defines a helper class for selecting a supported language from a
6 // set of candidates.
7
8 #include "chrome/installer/util/language_selector.h"
9
10 #include <algorithm>
11 #include <functional>
12
13 #include "base/logging.h"
14 #include "base/string_util.h"
15 #include "base/win/i18n.h"
16 #include "chrome/installer/util/google_update_settings.h"
17
18 #include "installer_util_strings.h"
19
20 namespace {
21
22 struct LangToOffset {
23 const wchar_t* language;
24 int offset;
25 };
26
27 // The language we fall back upon when all else fails.
28 const wchar_t kFallbackLanguage[] = L"en-us";
29 const int kFallbackLanguageOffset = IDS_L10N_OFFSET_EN_US;
30
31 // http://tools.ietf.org/html/rfc5646 Section 2.3.3
32 const std::wstring::size_type kScriptSubtagLength = 4;
33
34 // A sorted array of language identifiers (and their offsets) for which
35 // translations are available. The contents of the array are generated by
36 // create_string_rc.py.
37 const LangToOffset kLanguageOffsetPairs[] = {
38 #if defined(GOOGLE_CHROME_BUILD)
39 #define HANDLE_LANGUAGE(l_, o_) { L#l_, o_ },
40 DO_LANGUAGES
41 #undef HANDLE_LANGUAGE
42 #else // defined(GOOGLE_CHROME_BUILD)
43 { &kFallbackLanguage[0], kFallbackLanguageOffset }
44 #endif // !defined(GOOGLE_CHROME_BUILD)
45 };
46
47 // A sorted array of language identifiers that are aliases to other languages
48 // for which translations are available.
49 const LangToOffset kLanguageToOffsetExceptions[] = {
50 #if defined(GOOGLE_CHROME_BUILD)
51 // Google web properties use iw for he. Handle both just to be safe.
52 { L"he", IDS_L10N_OFFSET_IW },
53 // Google web properties use no for nb. Handle both just to be safe.
54 { L"nb", IDS_L10N_OFFSET_NO },
55 // Some Google web properties use tl for fil. Handle both just to be safe.
56 // They're not completely identical, but alias it here.
57 { L"tl", IDS_L10N_OFFSET_FIL },
58 // Pre-Vista aliases for Chinese w/ script subtag.
59 { L"zh-chs", IDS_L10N_OFFSET_ZH_CN },
60 { L"zh-cht", IDS_L10N_OFFSET_ZH_TW },
61 // Vista+ aliases for Chinese w/ script subtag.
62 { L"zh-hans", IDS_L10N_OFFSET_ZH_CN },
63 { L"zh-hant", IDS_L10N_OFFSET_ZH_TW },
64 // Alias Macau and Hong Kong to Taiwan.
65 { L"zh-hk", IDS_L10N_OFFSET_ZH_TW },
66 { L"zh-mk", IDS_L10N_OFFSET_ZH_TW },
67 // Windows uses "mo" for Macau.
68 { L"zh-mo", IDS_L10N_OFFSET_ZH_TW },
69 // Although the wildcard entry for zh would result in this, alias zh-sg so
70 // that it will win if it precedes another valid tag in a list of candidates.
71 { L"zh-sg", IDS_L10N_OFFSET_ZH_CN }
72 #else // defined(GOOGLE_CHROME_BUILD)
73 // An empty array is no good, so repeat the fallback.
74 { &kFallbackLanguage[0], kFallbackLanguageOffset }
75 #endif // !defined(GOOGLE_CHROME_BUILD)
76 };
77
78 // A sorted array of neutral language identifiers that are wildcard aliases to
79 // other languages for which translations are available.
80 const LangToOffset kLanguageToOffsetWildcards[] = {
81 // Use the U.S. region for anything English.
82 { L"en", IDS_L10N_OFFSET_EN_US },
83 #if defined(GOOGLE_CHROME_BUILD)
84 // Use the Latin American region for anything Spanish.
85 { L"es", IDS_L10N_OFFSET_ES_419 },
86 // Use the Brazil region for anything Portugese.
87 { L"pt", IDS_L10N_OFFSET_PT_BR },
88 // Use the P.R.C. region for anything Chinese.
89 { L"zh", IDS_L10N_OFFSET_ZH_CN }
90 #endif // defined(GOOGLE_CHROME_BUILD)
91 };
92
93 #if !defined(NDEBUG)
94 // Returns true if the items in the given range are sorted. If
95 // |byNameAndOffset| is true, the items must be sorted by both name and offset.
96 bool IsArraySorted(const LangToOffset* first, const LangToOffset* last,
97 bool byNameAndOffset) {
98 if (last - first > 1) {
99 for (--last; first != last; ++first) {
100 if (!(std::wstring(first->language) < (first + 1)->language) ||
101 byNameAndOffset && !(first->offset < (first + 1)->offset)) {
102 return false;
103 }
104 }
105 }
106 return true;
107 }
108
109 // Validates that the static read-only mappings are properly sorted.
110 void ValidateMappings() {
111 // Ensure that kLanguageOffsetPairs is sorted.
112 DCHECK(IsArraySorted(&kLanguageOffsetPairs[0],
113 &kLanguageOffsetPairs[arraysize(kLanguageOffsetPairs)],
114 true)) << "kOffsetToLanguageId is not sorted";
115
116 // Ensure that kLanguageToOffsetExceptions is sorted.
117 DCHECK(IsArraySorted(
118 &kLanguageToOffsetExceptions[0],
119 &kLanguageToOffsetExceptions[arraysize(kLanguageToOffsetExceptions)],
120 false)) << "kLanguageToOffsetExceptions is not sorted";
121
122 // Ensure that kLanguageToOffsetWildcards is sorted.
123 DCHECK(IsArraySorted(
124 &kLanguageToOffsetWildcards[0],
125 &kLanguageToOffsetWildcards[arraysize(kLanguageToOffsetWildcards)],
126 false)) << "kLanguageToOffsetWildcards is not sorted";
127 }
128 #endif // !defined(NDEBUG)
129
130 // A less-than overload to do slightly more efficient searches in the
131 // sorted arrays.
132 bool operator<(const LangToOffset& left, const std::wstring& right) {
133 return left.language < right;
134 }
135
136 // A less-than overload to do slightly more efficient searches in the
137 // sorted arrays.
138 bool operator<(const std::wstring& left, const LangToOffset& right) {
139 return left < right.language;
140 }
141
142 // A not-so-efficient less-than overload for the same uses as above.
143 bool operator<(const LangToOffset& left, const LangToOffset& right) {
144 return std::wstring(left.language) < right.language;
145 }
146
147 // A compare function for searching in a sorted array by offset.
148 bool IsOffsetLessThan(const LangToOffset& left, const LangToOffset& right) {
149 return left.offset < right.offset;
150 }
151
152 // Binary search in one of the sorted arrays to find the offset corresponding to
153 // a given language |name|.
154 bool TryFindOffset(const LangToOffset* first, const LangToOffset* last,
155 const std::wstring& name, int* offset) {
156 const LangToOffset* search_result = std::lower_bound(first, last, name);
157 if (last != search_result && search_result->language == name) {
158 *offset = search_result->offset;
159 return true;
160 }
161 return false;
162 }
163
164 // A predicate function for LanguageSelector::SelectIf that searches for the
165 // offset of a translated language. The search first tries to find an exact
166 // match. Failing that, an exact match with an alias is attempted.
167 bool GetLanguageOffset(const std::wstring& language, int* offset) {
168 // Note: always perform the exact match first so that an alias is never
169 // selected in place of a future translation.
170 return
171 TryFindOffset(
172 &kLanguageOffsetPairs[0],
173 &kLanguageOffsetPairs[arraysize(kLanguageOffsetPairs)],
174 language, offset) ||
175 TryFindOffset(
176 &kLanguageToOffsetExceptions[0],
177 &kLanguageToOffsetExceptions[arraysize(kLanguageToOffsetExceptions)],
178 language, offset);
179 }
180
181 // A predicate function for LanguageSelector::SelectIf that searches for a
182 // wildcard match with |language|'s primary language subtag.
183 bool MatchLanguageOffset(const std::wstring& language, int* offset) {
184 std::wstring primary_language = language.substr(0, language.find(L'-'));
185
186 // Now check for wildcards.
187 return
188 TryFindOffset(
189 &kLanguageToOffsetWildcards[0],
190 &kLanguageToOffsetWildcards[arraysize(kLanguageToOffsetWildcards)],
191 primary_language, offset);
192 }
193
194 // Adds to |candidates| the eligible languages on the system. Any language
195 // setting specified by Omaha takes precedence over the operating system's
196 // configured languages.
197 void GetCandidatesFromSystem(std::vector<std::wstring>* candidates) {
198 DCHECK(candidates);
199 std::wstring language;
200
201 // Omaha gets first pick.
202 GoogleUpdateSettings::GetLanguage(&language);
203 if (!language.empty()) {
204 candidates->push_back(language);
205 }
206
207 // Now try the Windows UI languages. Use the thread preferred since that will
208 // kindly return us a list of all kinds of fallbacks.
209 base::win::i18n::GetThreadPreferredUILanguageList(candidates);
210 }
211
212 } // namespace
213
214 namespace installer_util {
215
216 LanguageSelector::LanguageSelector()
217 : offset_(arraysize(kLanguageOffsetPairs)) {
218 #if !defined(NDEBUG)
219 ValidateMappings();
220 #endif // !defined(NDEBUG)
221 std::vector<std::wstring> candidates;
222
223 GetCandidatesFromSystem(&candidates);
224 DoSelect(candidates);
225 }
226
227 LanguageSelector::LanguageSelector(const std::vector<std::wstring>& candidates)
228 : offset_(arraysize(kLanguageOffsetPairs)) {
229 #if !defined(NDEBUG)
230 ValidateMappings();
231 #endif // !defined(NDEBUG)
232 DoSelect(candidates);
233 }
234
235 LanguageSelector::~LanguageSelector() {
236 }
237
238 // static
239 std::wstring LanguageSelector::GetLanguageName(int offset) {
240 DCHECK_GE(offset, 0);
241 DCHECK_LT(static_cast<size_t>(offset), arraysize(kLanguageOffsetPairs));
242
243 LangToOffset value = { NULL, offset };
244 const LangToOffset* search_result =
245 std::lower_bound(&kLanguageOffsetPairs[0],
246 &kLanguageOffsetPairs[arraysize(kLanguageOffsetPairs)],
247 value, IsOffsetLessThan);
248 if (&kLanguageOffsetPairs[arraysize(kLanguageOffsetPairs)] != search_result &&
249 search_result->offset == offset) {
250 return search_result->language;
251 }
252 NOTREACHED() << "Unknown language offset.";
253 return std::wstring(&kFallbackLanguage[0], arraysize(kFallbackLanguage) - 1);
254 }
255
256 // Runs through the set of candidates, sending their downcased representation
257 // through |select_predicate|. Returns true if the predicate selects a
258 // candidate, in which case |matched_name| is assigned the value of the
259 // candidate and |matched_offset| is assigned the language offset of the
260 // selected translation.
261 // static
262 bool LanguageSelector::SelectIf(const std::vector<std::wstring>& candidates,
263 SelectPred_Fn select_predicate,
264 std::wstring* matched_name,
265 int* matched_offset) {
266 std::wstring candidate;
267 for (std::vector<std::wstring>::const_iterator scan = candidates.begin(),
268 end = candidates.end(); scan != end; ++scan) {
269 candidate.assign(*scan);
270 StringToLowerASCII(&candidate);
271 if (select_predicate(candidate, matched_offset)) {
272 matched_name->assign(*scan);
273 return true;
274 }
275 }
276
277 return false;
278 }
279
280 // Select the best-fit translation from the ordered list |candidates|.
281 // At the conclusion, this instance's |matched_candidate_| and |offset_| members
282 // are set to the name of the selected candidate and the offset of the matched
283 // translation. If no translation is selected, the fallback's name and offset
284 // are selected.
285 void LanguageSelector::DoSelect(const std::vector<std::wstring>& candidates) {
286 // Make a pass through the candidates looking for an exact or alias match.
287 // Failing that, make another pass looking for a wildcard match.
288 if (!SelectIf(candidates, &GetLanguageOffset, &matched_candidate_,
289 &offset_) &&
290 !SelectIf(candidates, &MatchLanguageOffset, &matched_candidate_,
291 &offset_)) {
292 VLOG(1) << "No suitable language found for any candidates.";
293
294 // Our fallback is "en-us"
295 matched_candidate_.assign(&kFallbackLanguage[0],
296 arraysize(kFallbackLanguage) - 1);
297 offset_ = kFallbackLanguageOffset;
298 }
299 }
300
301 } // namespace installer_util
OLDNEW
« no previous file with comments | « chrome/installer/util/language_selector.h ('k') | chrome/installer/util/language_selector_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698