14a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch// Copyright (c) 2010 The Chromium Authors. All rights reserved.
24a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch// Use of this source code is governed by a BSD-style license that can be
34a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch// found in the LICENSE file.
44a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
54a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "base/win/i18n.h"
64a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
74a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include <windows.h>
84a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
94a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "base/logging.h"
104a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
114a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochnamespace {
124a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
134a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch// Keep this enum in sync with kLanguageFunctionNames.
144a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochenum LanguageFunction {
154a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  SYSTEM_LANGUAGES,
164a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  USER_LANGUAGES,
174a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  PROCESS_LANGUAGES,
184a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  THREAD_LANGUAGES,
194a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  NUM_FUNCTIONS
204a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch};
214a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
224a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochconst char kSystemLanguagesFunctionName[] = "GetSystemPreferredUILanguages";
234a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochconst char kUserLanguagesFunctionName[] = "GetUserPreferredUILanguages";
244a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochconst char kProcessLanguagesFunctionName[] = "GetProcessPreferredUILanguages";
254a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochconst char kThreadLanguagesFunctionName[] = "GetThreadPreferredUILanguages";
264a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
274a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch// Keep this array in sync with enum LanguageFunction.
284a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochconst char *const kLanguageFunctionNames[] = {
294a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  &kSystemLanguagesFunctionName[0],
304a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  &kUserLanguagesFunctionName[0],
314a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  &kProcessLanguagesFunctionName[0],
324a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  &kThreadLanguagesFunctionName[0]
334a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch};
344a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
354a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben MurdochCOMPILE_ASSERT(NUM_FUNCTIONS == arraysize(kLanguageFunctionNames),
364a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch               language_function_enum_and_names_out_of_sync);
374a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
384a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch// Calls one of the MUI Get*PreferredUILanguages functions, placing the result
394a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch// in |languages|.  |function| identifies the function to call and |flags| is
404a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch// the function-specific flags (callers must not specify MUI_LANGUAGE_ID or
414a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch// MUI_LANGUAGE_NAME).  Returns true if at least one language is placed in
424a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch// |languages|.
434a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochbool GetMUIPreferredUILanguageList(LanguageFunction function, ULONG flags,
444a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                                   std::vector<wchar_t>* languages) {
454a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK(0 <= function && NUM_FUNCTIONS > function);
464a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK_EQ(0U, (flags & (MUI_LANGUAGE_ID | MUI_LANGUAGE_NAME)));
474a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK(languages);
484a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
494a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  HMODULE kernel32 = GetModuleHandle(L"kernel32.dll");
504a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  if (NULL != kernel32) {
514a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    typedef BOOL (WINAPI* GetPreferredUILanguages_Fn)(
524a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        DWORD, PULONG, PZZWSTR, PULONG);
534a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    GetPreferredUILanguages_Fn get_preferred_ui_languages =
544a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        reinterpret_cast<GetPreferredUILanguages_Fn>(
554a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch            GetProcAddress(kernel32, kLanguageFunctionNames[function]));
564a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    if (NULL != get_preferred_ui_languages) {
574a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      const ULONG call_flags = flags | MUI_LANGUAGE_NAME;
584a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      ULONG language_count = 0;
594a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      ULONG buffer_length = 0;
604a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      if (get_preferred_ui_languages(call_flags, &language_count, NULL,
614a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                                     &buffer_length) &&
624a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch          0 != buffer_length) {
634a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        languages->resize(buffer_length);
644a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        if (get_preferred_ui_languages(call_flags, &language_count,
654a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                                       &(*languages)[0], &buffer_length) &&
664a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch            0 != language_count) {
674a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch          DCHECK(languages->size() == buffer_length);
684a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch          return true;
694a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        } else {
704a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch          DPCHECK(0 == language_count)
714a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch              << "Failed getting preferred UI languages.";
724a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        }
734a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      } else {
744a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        DPCHECK(0 == buffer_length)
754a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch            << "Failed getting size of preferred UI languages.";
764a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      }
774a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    } else {
784a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      VLOG(2) << "MUI not available.";
794a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    }
804a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  } else {
814a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    NOTREACHED() << "kernel32.dll not found.";
824a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  }
834a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
844a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  return false;
854a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
864a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
874a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochbool GetUserDefaultUILanguage(std::wstring* language, std::wstring* region) {
884a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK(language);
894a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
904a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  LANGID lang_id = ::GetUserDefaultUILanguage();
914a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  if (LOCALE_CUSTOM_UI_DEFAULT != lang_id) {
924a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    const LCID locale_id = MAKELCID(lang_id, SORT_DEFAULT);
934a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    // max size for LOCALE_SISO639LANGNAME and LOCALE_SISO3166CTRYNAME is 9
944a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    wchar_t result_buffer[9];
954a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    int result_length =
964a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        GetLocaleInfo(locale_id, LOCALE_SISO639LANGNAME, &result_buffer[0],
974a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                      arraysize(result_buffer));
984a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    DPCHECK(0 != result_length) << "Failed getting language id";
994a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    if (1 < result_length) {
1004a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      language->assign(&result_buffer[0], result_length - 1);
1014a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      region->clear();
1024a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      if (SUBLANG_NEUTRAL != SUBLANGID(lang_id)) {
1034a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        result_length =
1044a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch            GetLocaleInfo(locale_id, LOCALE_SISO3166CTRYNAME, &result_buffer[0],
1054a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                          arraysize(result_buffer));
1064a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        DPCHECK(0 != result_length) << "Failed getting region id";
1074a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        if (1 < result_length)
1084a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch          region->assign(&result_buffer[0], result_length - 1);
1094a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      }
1104a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      return true;
1114a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    }
1124a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  } else {
1134a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    // This is entirely unexpected on pre-Vista, which is the only time we
1144a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    // should try GetUserDefaultUILanguage anyway.
1154a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    NOTREACHED() << "Cannot determine language for a supplemental locale.";
1164a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  }
1174a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  return false;
1184a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
1194a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1204a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochbool GetPreferredUILanguageList(LanguageFunction function, ULONG flags,
1214a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                                std::vector<std::wstring>* languages) {
1224a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  std::vector<wchar_t> buffer;
1234a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  std::wstring language;
1244a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  std::wstring region;
1254a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1264a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  if (GetMUIPreferredUILanguageList(function, flags, &buffer)) {
1274a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    std::vector<wchar_t>::const_iterator scan = buffer.begin();
1284a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    language.assign(&*scan);
1294a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    while (!language.empty()) {
1304a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      languages->push_back(language);
1314a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      scan += language.size() + 1;
1324a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      language.assign(&*scan);
1334a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    }
1344a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  } else if (GetUserDefaultUILanguage(&language, &region)) {
1354a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    // Mimic the MUI behavior of putting the neutral version of the lang after
1364a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    // the regional one (e.g., "fr-CA, fr").
1374a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    if (!region.empty())
1384a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      languages->push_back(std::wstring(language)
1394a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                               .append(1, L'-')
1404a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                               .append(region));
1414a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    languages->push_back(language);
1424a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  } else {
1434a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    return false;
1444a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  }
1454a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1464a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  return true;
1474a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
1484a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1494a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}  // namespace
1504a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1514a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochnamespace base {
1524a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochnamespace win {
1534a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochnamespace i18n {
1544a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1554a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochbool GetUserPreferredUILanguageList(std::vector<std::wstring>* languages) {
1564a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK(languages);
1574a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  return GetPreferredUILanguageList(USER_LANGUAGES, 0, languages);
1584a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
1594a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1604a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochbool GetThreadPreferredUILanguageList(std::vector<std::wstring>* languages) {
1614a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK(languages);
1624a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  return GetPreferredUILanguageList(
1634a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      THREAD_LANGUAGES, MUI_MERGE_SYSTEM_FALLBACK | MUI_MERGE_USER_FALLBACK,
1644a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      languages);
1654a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
1664a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1674a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}  // namespace i18n
1684a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}  // namespace win
1694a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}  // namespace base
170