| // 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 |