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#include "base/win/i18n.h" 6 7#include <windows.h> 8 9#include "base/logging.h" 10 11namespace { 12 13// Keep this enum in sync with kLanguageFunctionNames. 14enum LanguageFunction { 15 SYSTEM_LANGUAGES, 16 USER_LANGUAGES, 17 PROCESS_LANGUAGES, 18 THREAD_LANGUAGES, 19 NUM_FUNCTIONS 20}; 21 22const char kSystemLanguagesFunctionName[] = "GetSystemPreferredUILanguages"; 23const char kUserLanguagesFunctionName[] = "GetUserPreferredUILanguages"; 24const char kProcessLanguagesFunctionName[] = "GetProcessPreferredUILanguages"; 25const char kThreadLanguagesFunctionName[] = "GetThreadPreferredUILanguages"; 26 27// Keep this array in sync with enum LanguageFunction. 28const char *const kLanguageFunctionNames[] = { 29 &kSystemLanguagesFunctionName[0], 30 &kUserLanguagesFunctionName[0], 31 &kProcessLanguagesFunctionName[0], 32 &kThreadLanguagesFunctionName[0] 33}; 34 35COMPILE_ASSERT(NUM_FUNCTIONS == arraysize(kLanguageFunctionNames), 36 language_function_enum_and_names_out_of_sync); 37 38// Calls one of the MUI Get*PreferredUILanguages functions, placing the result 39// in |languages|. |function| identifies the function to call and |flags| is 40// the function-specific flags (callers must not specify MUI_LANGUAGE_ID or 41// MUI_LANGUAGE_NAME). Returns true if at least one language is placed in 42// |languages|. 43bool GetMUIPreferredUILanguageList(LanguageFunction function, ULONG flags, 44 std::vector<wchar_t>* languages) { 45 DCHECK(0 <= function && NUM_FUNCTIONS > function); 46 DCHECK_EQ(0U, (flags & (MUI_LANGUAGE_ID | MUI_LANGUAGE_NAME))); 47 DCHECK(languages); 48 49 HMODULE kernel32 = GetModuleHandle(L"kernel32.dll"); 50 if (NULL != kernel32) { 51 typedef BOOL (WINAPI* GetPreferredUILanguages_Fn)( 52 DWORD, PULONG, PZZWSTR, PULONG); 53 GetPreferredUILanguages_Fn get_preferred_ui_languages = 54 reinterpret_cast<GetPreferredUILanguages_Fn>( 55 GetProcAddress(kernel32, kLanguageFunctionNames[function])); 56 if (NULL != get_preferred_ui_languages) { 57 const ULONG call_flags = flags | MUI_LANGUAGE_NAME; 58 ULONG language_count = 0; 59 ULONG buffer_length = 0; 60 if (get_preferred_ui_languages(call_flags, &language_count, NULL, 61 &buffer_length) && 62 0 != buffer_length) { 63 languages->resize(buffer_length); 64 if (get_preferred_ui_languages(call_flags, &language_count, 65 &(*languages)[0], &buffer_length) && 66 0 != language_count) { 67 DCHECK(languages->size() == buffer_length); 68 return true; 69 } else { 70 DPCHECK(0 == language_count) 71 << "Failed getting preferred UI languages."; 72 } 73 } else { 74 DPCHECK(0 == buffer_length) 75 << "Failed getting size of preferred UI languages."; 76 } 77 } else { 78 VLOG(2) << "MUI not available."; 79 } 80 } else { 81 NOTREACHED() << "kernel32.dll not found."; 82 } 83 84 return false; 85} 86 87bool GetUserDefaultUILanguage(std::wstring* language, std::wstring* region) { 88 DCHECK(language); 89 90 LANGID lang_id = ::GetUserDefaultUILanguage(); 91 if (LOCALE_CUSTOM_UI_DEFAULT != lang_id) { 92 const LCID locale_id = MAKELCID(lang_id, SORT_DEFAULT); 93 // max size for LOCALE_SISO639LANGNAME and LOCALE_SISO3166CTRYNAME is 9 94 wchar_t result_buffer[9]; 95 int result_length = 96 GetLocaleInfo(locale_id, LOCALE_SISO639LANGNAME, &result_buffer[0], 97 arraysize(result_buffer)); 98 DPCHECK(0 != result_length) << "Failed getting language id"; 99 if (1 < result_length) { 100 language->assign(&result_buffer[0], result_length - 1); 101 region->clear(); 102 if (SUBLANG_NEUTRAL != SUBLANGID(lang_id)) { 103 result_length = 104 GetLocaleInfo(locale_id, LOCALE_SISO3166CTRYNAME, &result_buffer[0], 105 arraysize(result_buffer)); 106 DPCHECK(0 != result_length) << "Failed getting region id"; 107 if (1 < result_length) 108 region->assign(&result_buffer[0], result_length - 1); 109 } 110 return true; 111 } 112 } else { 113 // This is entirely unexpected on pre-Vista, which is the only time we 114 // should try GetUserDefaultUILanguage anyway. 115 NOTREACHED() << "Cannot determine language for a supplemental locale."; 116 } 117 return false; 118} 119 120bool GetPreferredUILanguageList(LanguageFunction function, ULONG flags, 121 std::vector<std::wstring>* languages) { 122 std::vector<wchar_t> buffer; 123 std::wstring language; 124 std::wstring region; 125 126 if (GetMUIPreferredUILanguageList(function, flags, &buffer)) { 127 std::vector<wchar_t>::const_iterator scan = buffer.begin(); 128 language.assign(&*scan); 129 while (!language.empty()) { 130 languages->push_back(language); 131 scan += language.size() + 1; 132 language.assign(&*scan); 133 } 134 } else if (GetUserDefaultUILanguage(&language, ®ion)) { 135 // Mimic the MUI behavior of putting the neutral version of the lang after 136 // the regional one (e.g., "fr-CA, fr"). 137 if (!region.empty()) 138 languages->push_back(std::wstring(language) 139 .append(1, L'-') 140 .append(region)); 141 languages->push_back(language); 142 } else { 143 return false; 144 } 145 146 return true; 147} 148 149} // namespace 150 151namespace base { 152namespace win { 153namespace i18n { 154 155bool GetUserPreferredUILanguageList(std::vector<std::wstring>* languages) { 156 DCHECK(languages); 157 return GetPreferredUILanguageList(USER_LANGUAGES, 0, languages); 158} 159 160bool GetThreadPreferredUILanguageList(std::vector<std::wstring>* languages) { 161 DCHECK(languages); 162 return GetPreferredUILanguageList( 163 THREAD_LANGUAGES, MUI_MERGE_SYSTEM_FALLBACK | MUI_MERGE_USER_FALLBACK, 164 languages); 165} 166 167} // namespace i18n 168} // namespace win 169} // namespace base 170