|  | // 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. | 
|  |  | 
|  | #include "base/win/i18n.h" | 
|  |  | 
|  | #include <windows.h> | 
|  |  | 
|  | #include "base/logging.h" | 
|  | #include "base/macros.h" | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Keep this enum in sync with kLanguageFunctionNames. | 
|  | enum LanguageFunction { | 
|  | SYSTEM_LANGUAGES, | 
|  | USER_LANGUAGES, | 
|  | PROCESS_LANGUAGES, | 
|  | THREAD_LANGUAGES, | 
|  | NUM_FUNCTIONS | 
|  | }; | 
|  |  | 
|  | const char kSystemLanguagesFunctionName[] = "GetSystemPreferredUILanguages"; | 
|  | const char kUserLanguagesFunctionName[] = "GetUserPreferredUILanguages"; | 
|  | const char kProcessLanguagesFunctionName[] = "GetProcessPreferredUILanguages"; | 
|  | const char kThreadLanguagesFunctionName[] = "GetThreadPreferredUILanguages"; | 
|  |  | 
|  | // Keep this array in sync with enum LanguageFunction. | 
|  | const char *const kLanguageFunctionNames[] = { | 
|  | &kSystemLanguagesFunctionName[0], | 
|  | &kUserLanguagesFunctionName[0], | 
|  | &kProcessLanguagesFunctionName[0], | 
|  | &kThreadLanguagesFunctionName[0] | 
|  | }; | 
|  |  | 
|  | static_assert(NUM_FUNCTIONS == arraysize(kLanguageFunctionNames), | 
|  | "LanguageFunction enum and kLanguageFunctionNames array must be " | 
|  | "kept in sync"); | 
|  |  | 
|  | // Calls one of the MUI Get*PreferredUILanguages functions, placing the result | 
|  | // in |languages|.  |function| identifies the function to call and |flags| is | 
|  | // the function-specific flags (callers must not specify MUI_LANGUAGE_ID or | 
|  | // MUI_LANGUAGE_NAME).  Returns true if at least one language is placed in | 
|  | // |languages|. | 
|  | bool GetMUIPreferredUILanguageList(LanguageFunction function, ULONG flags, | 
|  | std::vector<wchar_t>* languages) { | 
|  | DCHECK(0 <= function && NUM_FUNCTIONS > function); | 
|  | DCHECK_EQ(0U, (flags & (MUI_LANGUAGE_ID | MUI_LANGUAGE_NAME))); | 
|  | DCHECK(languages); | 
|  |  | 
|  | HMODULE kernel32 = GetModuleHandle(L"kernel32.dll"); | 
|  | if (NULL != kernel32) { | 
|  | typedef BOOL (WINAPI* GetPreferredUILanguages_Fn)( | 
|  | DWORD, PULONG, PZZWSTR, PULONG); | 
|  | GetPreferredUILanguages_Fn get_preferred_ui_languages = | 
|  | reinterpret_cast<GetPreferredUILanguages_Fn>( | 
|  | GetProcAddress(kernel32, kLanguageFunctionNames[function])); | 
|  | if (NULL != get_preferred_ui_languages) { | 
|  | const ULONG call_flags = flags | MUI_LANGUAGE_NAME; | 
|  | ULONG language_count = 0; | 
|  | ULONG buffer_length = 0; | 
|  | if (get_preferred_ui_languages(call_flags, &language_count, NULL, | 
|  | &buffer_length) && | 
|  | 0 != buffer_length) { | 
|  | languages->resize(buffer_length); | 
|  | if (get_preferred_ui_languages(call_flags, &language_count, | 
|  | &(*languages)[0], &buffer_length) && | 
|  | 0 != language_count) { | 
|  | DCHECK(languages->size() == buffer_length); | 
|  | return true; | 
|  | } else { | 
|  | DPCHECK(0 == language_count) | 
|  | << "Failed getting preferred UI languages."; | 
|  | } | 
|  | } else { | 
|  | DPCHECK(0 == buffer_length) | 
|  | << "Failed getting size of preferred UI languages."; | 
|  | } | 
|  | } else { | 
|  | DVLOG(2) << "MUI not available."; | 
|  | } | 
|  | } else { | 
|  | NOTREACHED() << "kernel32.dll not found."; | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool GetUserDefaultUILanguage(std::wstring* language, std::wstring* region) { | 
|  | DCHECK(language); | 
|  |  | 
|  | LANGID lang_id = ::GetUserDefaultUILanguage(); | 
|  | if (LOCALE_CUSTOM_UI_DEFAULT != lang_id) { | 
|  | const LCID locale_id = MAKELCID(lang_id, SORT_DEFAULT); | 
|  | // max size for LOCALE_SISO639LANGNAME and LOCALE_SISO3166CTRYNAME is 9 | 
|  | wchar_t result_buffer[9]; | 
|  | int result_length = | 
|  | GetLocaleInfo(locale_id, LOCALE_SISO639LANGNAME, &result_buffer[0], | 
|  | arraysize(result_buffer)); | 
|  | DPCHECK(0 != result_length) << "Failed getting language id"; | 
|  | if (1 < result_length) { | 
|  | language->assign(&result_buffer[0], result_length - 1); | 
|  | region->clear(); | 
|  | if (SUBLANG_NEUTRAL != SUBLANGID(lang_id)) { | 
|  | result_length = | 
|  | GetLocaleInfo(locale_id, LOCALE_SISO3166CTRYNAME, &result_buffer[0], | 
|  | arraysize(result_buffer)); | 
|  | DPCHECK(0 != result_length) << "Failed getting region id"; | 
|  | if (1 < result_length) | 
|  | region->assign(&result_buffer[0], result_length - 1); | 
|  | } | 
|  | return true; | 
|  | } | 
|  | } else { | 
|  | // This is entirely unexpected on pre-Vista, which is the only time we | 
|  | // should try GetUserDefaultUILanguage anyway. | 
|  | NOTREACHED() << "Cannot determine language for a supplemental locale."; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool GetPreferredUILanguageList(LanguageFunction function, ULONG flags, | 
|  | std::vector<std::wstring>* languages) { | 
|  | std::vector<wchar_t> buffer; | 
|  | std::wstring language; | 
|  | std::wstring region; | 
|  |  | 
|  | if (GetMUIPreferredUILanguageList(function, flags, &buffer)) { | 
|  | std::vector<wchar_t>::const_iterator scan = buffer.begin(); | 
|  | language.assign(&*scan); | 
|  | while (!language.empty()) { | 
|  | languages->push_back(language); | 
|  | scan += language.size() + 1; | 
|  | language.assign(&*scan); | 
|  | } | 
|  | } else if (GetUserDefaultUILanguage(&language, ®ion)) { | 
|  | // Mimic the MUI behavior of putting the neutral version of the lang after | 
|  | // the regional one (e.g., "fr-CA, fr"). | 
|  | if (!region.empty()) | 
|  | languages->push_back(std::wstring(language) | 
|  | .append(1, L'-') | 
|  | .append(region)); | 
|  | languages->push_back(language); | 
|  | } else { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | namespace base { | 
|  | namespace win { | 
|  | namespace i18n { | 
|  |  | 
|  | bool GetUserPreferredUILanguageList(std::vector<std::wstring>* languages) { | 
|  | DCHECK(languages); | 
|  | return GetPreferredUILanguageList(USER_LANGUAGES, 0, languages); | 
|  | } | 
|  |  | 
|  | bool GetThreadPreferredUILanguageList(std::vector<std::wstring>* languages) { | 
|  | DCHECK(languages); | 
|  | return GetPreferredUILanguageList( | 
|  | THREAD_LANGUAGES, MUI_MERGE_SYSTEM_FALLBACK | MUI_MERGE_USER_FALLBACK, | 
|  | languages); | 
|  | } | 
|  |  | 
|  | }  // namespace i18n | 
|  | }  // namespace win | 
|  | }  // namespace base |