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, &region)) {
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