Home | History | Annotate | Download | only in win
      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 
     11 namespace {
     12 
     13 // Keep this enum in sync with kLanguageFunctionNames.
     14 enum LanguageFunction {
     15   SYSTEM_LANGUAGES,
     16   USER_LANGUAGES,
     17   PROCESS_LANGUAGES,
     18   THREAD_LANGUAGES,
     19   NUM_FUNCTIONS
     20 };
     21 
     22 const char kSystemLanguagesFunctionName[] = "GetSystemPreferredUILanguages";
     23 const char kUserLanguagesFunctionName[] = "GetUserPreferredUILanguages";
     24 const char kProcessLanguagesFunctionName[] = "GetProcessPreferredUILanguages";
     25 const char kThreadLanguagesFunctionName[] = "GetThreadPreferredUILanguages";
     26 
     27 // Keep this array in sync with enum LanguageFunction.
     28 const char *const kLanguageFunctionNames[] = {
     29   &kSystemLanguagesFunctionName[0],
     30   &kUserLanguagesFunctionName[0],
     31   &kProcessLanguagesFunctionName[0],
     32   &kThreadLanguagesFunctionName[0]
     33 };
     34 
     35 COMPILE_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|.
     43 bool 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 
     87 bool 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 
    120 bool 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 
    151 namespace base {
    152 namespace win {
    153 namespace i18n {
    154 
    155 bool GetUserPreferredUILanguageList(std::vector<std::wstring>* languages) {
    156   DCHECK(languages);
    157   return GetPreferredUILanguageList(USER_LANGUAGES, 0, languages);
    158 }
    159 
    160 bool 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