This source file includes following definitions.
- IsArraySorted
- ValidateMappings
- IsOffsetLessThan
- TryFindOffset
- GetLanguageOffset
- MatchLanguageOffset
- GetCandidatesFromSystem
- GetLanguageName
- SelectIf
- DoSelect
#include "chrome/installer/util/language_selector.h"
#include <algorithm>
#include <functional>
#include "base/logging.h"
#include "base/strings/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;
};
const wchar_t kFallbackLanguage[] = L"en-us";
const int kFallbackLanguageOffset = IDS_L10N_OFFSET_EN_US;
const std::wstring::size_type kScriptSubtagLength = 4;
const LangToOffset kLanguageOffsetPairs[] = {
#define HANDLE_LANGUAGE(l_, o_) { L ## #l_, o_ },
DO_LANGUAGES
#undef HANDLE_LANGUAGE
};
const LangToOffset kLanguageToOffsetExceptions[] = {
{ L"en-au", IDS_L10N_OFFSET_EN_GB },
{ L"en-ca", IDS_L10N_OFFSET_EN_GB },
{ L"en-nz", IDS_L10N_OFFSET_EN_GB },
{ L"en-za", IDS_L10N_OFFSET_EN_GB },
{ L"es-es", IDS_L10N_OFFSET_ES },
{ L"he", IDS_L10N_OFFSET_IW },
{ L"nb", IDS_L10N_OFFSET_NO },
{ L"tl", IDS_L10N_OFFSET_FIL },
{ L"zh-chs", IDS_L10N_OFFSET_ZH_CN },
{ L"zh-cht", IDS_L10N_OFFSET_ZH_TW },
{ L"zh-hans", IDS_L10N_OFFSET_ZH_CN },
{ L"zh-hant", IDS_L10N_OFFSET_ZH_TW },
{ L"zh-hk", IDS_L10N_OFFSET_ZH_TW },
{ L"zh-mo", IDS_L10N_OFFSET_ZH_TW },
{ L"zh-sg", IDS_L10N_OFFSET_ZH_CN }
};
const LangToOffset kLanguageToOffsetWildcards[] = {
{ L"en", IDS_L10N_OFFSET_EN_US },
{ L"es", IDS_L10N_OFFSET_ES_419 },
{ L"pt", IDS_L10N_OFFSET_PT_BR },
{ L"zh", IDS_L10N_OFFSET_ZH_CN }
};
#if !defined(NDEBUG)
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;
}
void ValidateMappings() {
DCHECK(IsArraySorted(&kLanguageOffsetPairs[0],
&kLanguageOffsetPairs[arraysize(kLanguageOffsetPairs)],
true)) << "kOffsetToLanguageId is not sorted";
DCHECK(IsArraySorted(
&kLanguageToOffsetExceptions[0],
&kLanguageToOffsetExceptions[arraysize(kLanguageToOffsetExceptions)],
false)) << "kLanguageToOffsetExceptions is not sorted";
DCHECK(IsArraySorted(
&kLanguageToOffsetWildcards[0],
&kLanguageToOffsetWildcards[arraysize(kLanguageToOffsetWildcards)],
false)) << "kLanguageToOffsetWildcards is not sorted";
}
#endif
bool operator<(const LangToOffset& left, const std::wstring& right) {
return left.language < right;
}
bool operator<(const std::wstring& left, const LangToOffset& right) {
return left < right.language;
}
bool operator<(const LangToOffset& left, const LangToOffset& right) {
return std::wstring(left.language) < right.language;
}
bool IsOffsetLessThan(const LangToOffset& left, const LangToOffset& right) {
return left.offset < right.offset;
}
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;
}
bool GetLanguageOffset(const std::wstring& language, int* offset) {
return
TryFindOffset(
&kLanguageOffsetPairs[0],
&kLanguageOffsetPairs[arraysize(kLanguageOffsetPairs)],
language, offset) ||
TryFindOffset(
&kLanguageToOffsetExceptions[0],
&kLanguageToOffsetExceptions[arraysize(kLanguageToOffsetExceptions)],
language, offset);
}
bool MatchLanguageOffset(const std::wstring& language, int* offset) {
std::wstring primary_language = language.substr(0, language.find(L'-'));
return
TryFindOffset(
&kLanguageToOffsetWildcards[0],
&kLanguageToOffsetWildcards[arraysize(kLanguageToOffsetWildcards)],
primary_language, offset);
}
void GetCandidatesFromSystem(std::vector<std::wstring>* candidates) {
DCHECK(candidates);
std::wstring language;
GoogleUpdateSettings::GetLanguage(&language);
if (!language.empty()) {
candidates->push_back(language);
}
base::win::i18n::GetThreadPreferredUILanguageList(candidates);
}
}
namespace installer {
LanguageSelector::LanguageSelector()
: offset_(arraysize(kLanguageOffsetPairs)) {
#if !defined(NDEBUG)
ValidateMappings();
#endif
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
DoSelect(candidates);
}
LanguageSelector::~LanguageSelector() {
}
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);
}
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;
}
void LanguageSelector::DoSelect(const std::vector<std::wstring>& candidates) {
if (!SelectIf(candidates, &GetLanguageOffset, &matched_candidate_,
&offset_) &&
!SelectIf(candidates, &MatchLanguageOffset, &matched_candidate_,
&offset_)) {
VLOG(1) << "No suitable language found for any candidates.";
matched_candidate_.assign(&kFallbackLanguage[0],
arraysize(kFallbackLanguage) - 1);
offset_ = kFallbackLanguageOffset;
}
}
}