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

Unified 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 side-by-side diff with in-line comments
Download patch
« 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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/installer/util/language_selector.cc
===================================================================
--- chrome/installer/util/language_selector.cc (revision 0)
+++ chrome/installer/util/language_selector.cc (revision 0)
@@ -0,0 +1,301 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This file defines a helper class for selecting a supported language from a
+// set of candidates.
+
+#include "chrome/installer/util/language_selector.h"
+
+#include <algorithm>
+#include <functional>
+
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/win/i18n.h"
+#include "chrome/installer/util/google_update_settings.h"
+
+#include "installer_util_strings.h"
+
+namespace {
+
+struct LangToOffset {
+ const wchar_t* language;
+ int offset;
+};
+
+// The language we fall back upon when all else fails.
+const wchar_t kFallbackLanguage[] = L"en-us";
+const int kFallbackLanguageOffset = IDS_L10N_OFFSET_EN_US;
+
+// http://tools.ietf.org/html/rfc5646 Section 2.3.3
+const std::wstring::size_type kScriptSubtagLength = 4;
+
+// A sorted array of language identifiers (and their offsets) for which
+// translations are available. The contents of the array are generated by
+// create_string_rc.py.
+const LangToOffset kLanguageOffsetPairs[] = {
+#if defined(GOOGLE_CHROME_BUILD)
+#define HANDLE_LANGUAGE(l_, o_) { L#l_, o_ },
+ DO_LANGUAGES
+#undef HANDLE_LANGUAGE
+#else // defined(GOOGLE_CHROME_BUILD)
+ { &kFallbackLanguage[0], kFallbackLanguageOffset }
+#endif // !defined(GOOGLE_CHROME_BUILD)
+};
+
+// A sorted array of language identifiers that are aliases to other languages
+// for which translations are available.
+const LangToOffset kLanguageToOffsetExceptions[] = {
+#if defined(GOOGLE_CHROME_BUILD)
+ // Google web properties use iw for he. Handle both just to be safe.
+ { L"he", IDS_L10N_OFFSET_IW },
+ // Google web properties use no for nb. Handle both just to be safe.
+ { L"nb", IDS_L10N_OFFSET_NO },
+ // Some Google web properties use tl for fil. Handle both just to be safe.
+ // They're not completely identical, but alias it here.
+ { L"tl", IDS_L10N_OFFSET_FIL },
+ // Pre-Vista aliases for Chinese w/ script subtag.
+ { L"zh-chs", IDS_L10N_OFFSET_ZH_CN },
+ { L"zh-cht", IDS_L10N_OFFSET_ZH_TW },
+ // Vista+ aliases for Chinese w/ script subtag.
+ { L"zh-hans", IDS_L10N_OFFSET_ZH_CN },
+ { L"zh-hant", IDS_L10N_OFFSET_ZH_TW },
+ // Alias Macau and Hong Kong to Taiwan.
+ { L"zh-hk", IDS_L10N_OFFSET_ZH_TW },
+ { L"zh-mk", IDS_L10N_OFFSET_ZH_TW },
+ // Windows uses "mo" for Macau.
+ { L"zh-mo", IDS_L10N_OFFSET_ZH_TW },
+ // Although the wildcard entry for zh would result in this, alias zh-sg so
+ // that it will win if it precedes another valid tag in a list of candidates.
+ { L"zh-sg", IDS_L10N_OFFSET_ZH_CN }
+#else // defined(GOOGLE_CHROME_BUILD)
+ // An empty array is no good, so repeat the fallback.
+ { &kFallbackLanguage[0], kFallbackLanguageOffset }
+#endif // !defined(GOOGLE_CHROME_BUILD)
+};
+
+// A sorted array of neutral language identifiers that are wildcard aliases to
+// other languages for which translations are available.
+const LangToOffset kLanguageToOffsetWildcards[] = {
+ // Use the U.S. region for anything English.
+ { L"en", IDS_L10N_OFFSET_EN_US },
+#if defined(GOOGLE_CHROME_BUILD)
+ // Use the Latin American region for anything Spanish.
+ { L"es", IDS_L10N_OFFSET_ES_419 },
+ // Use the Brazil region for anything Portugese.
+ { L"pt", IDS_L10N_OFFSET_PT_BR },
+ // Use the P.R.C. region for anything Chinese.
+ { L"zh", IDS_L10N_OFFSET_ZH_CN }
+#endif // defined(GOOGLE_CHROME_BUILD)
+};
+
+#if !defined(NDEBUG)
+// Returns true if the items in the given range are sorted. If
+// |byNameAndOffset| is true, the items must be sorted by both name and offset.
+bool IsArraySorted(const LangToOffset* first, const LangToOffset* last,
+ bool byNameAndOffset) {
+ if (last - first > 1) {
+ for (--last; first != last; ++first) {
+ if (!(std::wstring(first->language) < (first + 1)->language) ||
+ byNameAndOffset && !(first->offset < (first + 1)->offset)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+// Validates that the static read-only mappings are properly sorted.
+void ValidateMappings() {
+ // Ensure that kLanguageOffsetPairs is sorted.
+ DCHECK(IsArraySorted(&kLanguageOffsetPairs[0],
+ &kLanguageOffsetPairs[arraysize(kLanguageOffsetPairs)],
+ true)) << "kOffsetToLanguageId is not sorted";
+
+ // Ensure that kLanguageToOffsetExceptions is sorted.
+ DCHECK(IsArraySorted(
+ &kLanguageToOffsetExceptions[0],
+ &kLanguageToOffsetExceptions[arraysize(kLanguageToOffsetExceptions)],
+ false)) << "kLanguageToOffsetExceptions is not sorted";
+
+ // Ensure that kLanguageToOffsetWildcards is sorted.
+ DCHECK(IsArraySorted(
+ &kLanguageToOffsetWildcards[0],
+ &kLanguageToOffsetWildcards[arraysize(kLanguageToOffsetWildcards)],
+ false)) << "kLanguageToOffsetWildcards is not sorted";
+}
+#endif // !defined(NDEBUG)
+
+// A less-than overload to do slightly more efficient searches in the
+// sorted arrays.
+bool operator<(const LangToOffset& left, const std::wstring& right) {
+ return left.language < right;
+}
+
+// A less-than overload to do slightly more efficient searches in the
+// sorted arrays.
+bool operator<(const std::wstring& left, const LangToOffset& right) {
+ return left < right.language;
+}
+
+// A not-so-efficient less-than overload for the same uses as above.
+bool operator<(const LangToOffset& left, const LangToOffset& right) {
+ return std::wstring(left.language) < right.language;
+}
+
+// A compare function for searching in a sorted array by offset.
+bool IsOffsetLessThan(const LangToOffset& left, const LangToOffset& right) {
+ return left.offset < right.offset;
+}
+
+// Binary search in one of the sorted arrays to find the offset corresponding to
+// a given language |name|.
+bool TryFindOffset(const LangToOffset* first, const LangToOffset* last,
+ const std::wstring& name, int* offset) {
+ const LangToOffset* search_result = std::lower_bound(first, last, name);
+ if (last != search_result && search_result->language == name) {
+ *offset = search_result->offset;
+ return true;
+ }
+ return false;
+}
+
+// A predicate function for LanguageSelector::SelectIf that searches for the
+// offset of a translated language. The search first tries to find an exact
+// match. Failing that, an exact match with an alias is attempted.
+bool GetLanguageOffset(const std::wstring& language, int* offset) {
+ // Note: always perform the exact match first so that an alias is never
+ // selected in place of a future translation.
+ return
+ TryFindOffset(
+ &kLanguageOffsetPairs[0],
+ &kLanguageOffsetPairs[arraysize(kLanguageOffsetPairs)],
+ language, offset) ||
+ TryFindOffset(
+ &kLanguageToOffsetExceptions[0],
+ &kLanguageToOffsetExceptions[arraysize(kLanguageToOffsetExceptions)],
+ language, offset);
+}
+
+// A predicate function for LanguageSelector::SelectIf that searches for a
+// wildcard match with |language|'s primary language subtag.
+bool MatchLanguageOffset(const std::wstring& language, int* offset) {
+ std::wstring primary_language = language.substr(0, language.find(L'-'));
+
+ // Now check for wildcards.
+ return
+ TryFindOffset(
+ &kLanguageToOffsetWildcards[0],
+ &kLanguageToOffsetWildcards[arraysize(kLanguageToOffsetWildcards)],
+ primary_language, offset);
+}
+
+// Adds to |candidates| the eligible languages on the system. Any language
+// setting specified by Omaha takes precedence over the operating system's
+// configured languages.
+void GetCandidatesFromSystem(std::vector<std::wstring>* candidates) {
+ DCHECK(candidates);
+ std::wstring language;
+
+ // Omaha gets first pick.
+ GoogleUpdateSettings::GetLanguage(&language);
+ if (!language.empty()) {
+ candidates->push_back(language);
+ }
+
+ // Now try the Windows UI languages. Use the thread preferred since that will
+ // kindly return us a list of all kinds of fallbacks.
+ base::win::i18n::GetThreadPreferredUILanguageList(candidates);
+}
+
+} // namespace
+
+namespace installer_util {
+
+LanguageSelector::LanguageSelector()
+ : offset_(arraysize(kLanguageOffsetPairs)) {
+#if !defined(NDEBUG)
+ ValidateMappings();
+#endif // !defined(NDEBUG)
+ std::vector<std::wstring> candidates;
+
+ GetCandidatesFromSystem(&candidates);
+ DoSelect(candidates);
+}
+
+LanguageSelector::LanguageSelector(const std::vector<std::wstring>& candidates)
+ : offset_(arraysize(kLanguageOffsetPairs)) {
+#if !defined(NDEBUG)
+ ValidateMappings();
+#endif // !defined(NDEBUG)
+ DoSelect(candidates);
+}
+
+LanguageSelector::~LanguageSelector() {
+}
+
+// static
+std::wstring LanguageSelector::GetLanguageName(int offset) {
+ DCHECK_GE(offset, 0);
+ DCHECK_LT(static_cast<size_t>(offset), arraysize(kLanguageOffsetPairs));
+
+ LangToOffset value = { NULL, offset };
+ const LangToOffset* search_result =
+ std::lower_bound(&kLanguageOffsetPairs[0],
+ &kLanguageOffsetPairs[arraysize(kLanguageOffsetPairs)],
+ value, IsOffsetLessThan);
+ if (&kLanguageOffsetPairs[arraysize(kLanguageOffsetPairs)] != search_result &&
+ search_result->offset == offset) {
+ return search_result->language;
+ }
+ NOTREACHED() << "Unknown language offset.";
+ return std::wstring(&kFallbackLanguage[0], arraysize(kFallbackLanguage) - 1);
+}
+
+// Runs through the set of candidates, sending their downcased representation
+// through |select_predicate|. Returns true if the predicate selects a
+// candidate, in which case |matched_name| is assigned the value of the
+// candidate and |matched_offset| is assigned the language offset of the
+// selected translation.
+// static
+bool LanguageSelector::SelectIf(const std::vector<std::wstring>& candidates,
+ SelectPred_Fn select_predicate,
+ std::wstring* matched_name,
+ int* matched_offset) {
+ std::wstring candidate;
+ for (std::vector<std::wstring>::const_iterator scan = candidates.begin(),
+ end = candidates.end(); scan != end; ++scan) {
+ candidate.assign(*scan);
+ StringToLowerASCII(&candidate);
+ if (select_predicate(candidate, matched_offset)) {
+ matched_name->assign(*scan);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// Select the best-fit translation from the ordered list |candidates|.
+// At the conclusion, this instance's |matched_candidate_| and |offset_| members
+// are set to the name of the selected candidate and the offset of the matched
+// translation. If no translation is selected, the fallback's name and offset
+// are selected.
+void LanguageSelector::DoSelect(const std::vector<std::wstring>& candidates) {
+ // Make a pass through the candidates looking for an exact or alias match.
+ // Failing that, make another pass looking for a wildcard match.
+ if (!SelectIf(candidates, &GetLanguageOffset, &matched_candidate_,
+ &offset_) &&
+ !SelectIf(candidates, &MatchLanguageOffset, &matched_candidate_,
+ &offset_)) {
+ VLOG(1) << "No suitable language found for any candidates.";
+
+ // Our fallback is "en-us"
+ matched_candidate_.assign(&kFallbackLanguage[0],
+ arraysize(kFallbackLanguage) - 1);
+ offset_ = kFallbackLanguageOffset;
+ }
+}
+
+} // namespace installer_util
Property changes on: chrome\installer\util\language_selector.cc
___________________________________________________________________
Added: svn:eol-style
+ LF
« 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