Skip to content

Commit

Permalink
The UI language rather than the locale is now used to pick Chrome's l…
Browse files Browse the repository at this point in the history
…anguage on Windows. (Also fixed some unrelated lint errors.) With this change, l10n_util::GetApplicationLocale first checks for an override of the "configured locale" (in base::i18n) containing the list of preferred Windows UI languages. The browser triggers an override early in startup before the ApplicationLocale is determined. In effect, the browser no longer uses ICU on Windows for language detection. (This locale override mechanism is borrowed from the OS X port.)

Changes in Chrome Frame are largely a refactor, as some Win32 code in there has been moved into base/win.

Also cleaned up language selection in installer_util so that the proper language is chosen for the EULA, installer messages, and shortcuts.  In so doing, replaced hand-crafted lists of supported languages with either auto-generated lists (static consts) or logic so that the addition of translations in the future doesn't require code motion (that being said, there may be reason to update the alias and/or wildcard tables in language_selector.cc).  In so doing, this change unlocks Amharic, Farsi, and Swahili translations for installer messages and shortcuts.

BUG=39986,40496,26470
TEST=New MUI/Win32 calls are tested in base/win/i18n_unittest.cc.  To test the overall functionality, uninstall Chrome, remove intl.app_locale user pref, switch to a supported display language (via the "Keyboards and Languages" tab of Win7's "Regional and Language" control panel, and install with { "distribution": { "require_eula": true } } in master_preferences (via -installerdata arg to setup.exe).  If all goes well, both EULA and outer frame are in the same language as Windows.  Also, from gwilson: "Install system-level Chrome in audit mode on a new machine, then go through the out-of-box-experience, select a language, and the in -product EULA (triggered by "require_eula" : true) and Chrome's UI should be in the language that the user selected."

Review URL: http://codereview.chromium.org/4139010

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@66275 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
grt@chromium.org committed Nov 16, 2010
1 parent 4051959 commit 337ca07
Show file tree
Hide file tree
Showing 21 changed files with 879 additions and 366 deletions.
20 changes: 18 additions & 2 deletions app/l10n_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
#include <glib/gutils.h>
#endif

#include <algorithm>
#include <cstdlib>
#include <iterator>

#include "app/app_paths.h"
#include "app/l10n_util_collator.h"
Expand All @@ -30,6 +32,8 @@

#if defined(OS_MACOSX)
#include "app/l10n_util_mac.h"
#elif defined(OS_WIN)
#include "app/l10n_util_win.h"
#endif

namespace {
Expand Down Expand Up @@ -325,6 +329,10 @@ void AdjustParagraphDirectionality(string16* paragraph) {
#endif
}

std::string GetCanonicalLocale(const std::string& locale) {
return base::i18n::GetCanonicalLocale(locale.c_str());
}

} // namespace

namespace l10n_util {
Expand Down Expand Up @@ -369,8 +377,16 @@ std::string GetApplicationLocale(const std::string& pref_locale) {
if (!pref_locale.empty())
candidates.push_back(pref_locale);

// Next, try the system locale.
candidates.push_back(base::i18n::GetConfiguredLocale());
// Next, try the overridden locale.
const std::vector<std::string>& languages = l10n_util::GetLocaleOverrides();
if (!languages.empty()) {
candidates.reserve(candidates.size() + languages.size());
std::transform(languages.begin(), languages.end(),
std::back_inserter(candidates), &GetCanonicalLocale);
} else {
// If no override was set, defer to ICU
candidates.push_back(base::i18n::GetConfiguredLocale());
}

#elif defined(OS_CHROMEOS)

Expand Down
41 changes: 38 additions & 3 deletions app/l10n_util_win.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "app/l10n_util.h"
#include "app/l10n_util_win.h"

#include <algorithm>
#include <windowsx.h>
#include <algorithm>
#include <iterator>

#include "app/l10n_util_win.h"
#include "app/l10n_util.h"
#include "base/i18n/rtl.h"
#include "base/lazy_instance.h"
#include "base/string_number_conversions.h"
#include "base/win/i18n.h"
#include "base/win/windows_version.h"
#include "grit/app_locale_settings.h"

Expand Down Expand Up @@ -52,6 +55,21 @@ bool IsFontPresent(const wchar_t* font_name) {
return wcscmp(font_name, actual_font_name) == 0;
}

class OverrideLocaleHolder {
public:
OverrideLocaleHolder() {}
const std::vector<std::string>& value() const { return value_; }
void swap_value(std::vector<std::string>* override_value) {
value_.swap(*override_value);
}
private:
std::vector<std::string> value_;
DISALLOW_COPY_AND_ASSIGN(OverrideLocaleHolder);
};

base::LazyInstance<OverrideLocaleHolder>
override_locale_holder(base::LINKER_INITIALIZED);

} // namespace

namespace l10n_util {
Expand Down Expand Up @@ -147,4 +165,21 @@ void AdjustUIFontForWindow(HWND hwnd) {
}
}

void OverrideLocaleWithUILanguageList() {
std::vector<std::wstring> ui_languages;
if (base::win::i18n::GetThreadPreferredUILanguageList(&ui_languages)) {
std::vector<std::string> ascii_languages;
ascii_languages.reserve(ui_languages.size());
std::transform(ui_languages.begin(), ui_languages.end(),
std::back_inserter(ascii_languages), &WideToASCII);
override_locale_holder.Get().swap_value(&ascii_languages);
} else {
NOTREACHED() << "Failed to determine the UI language for locale override.";
}
}

const std::vector<std::string>& GetLocaleOverrides() {
return override_locale_holder.Get().value();
}

} // namespace l10n_util
11 changes: 11 additions & 0 deletions app/l10n_util_win.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#pragma once

#include <windows.h>
#include <string>
#include <vector>

namespace l10n_util {

Expand Down Expand Up @@ -47,6 +49,15 @@ void AdjustUIFont(LOGFONT* logfont);
// stored in the per-locale resource.
void AdjustUIFontForWindow(HWND hwnd);

// Allow processes to override the configured locale with the user's Windows UI
// languages. This function should generally be called once early in
// Application startup.
void OverrideLocaleWithUILanguageList();

// Retrieve the locale override, or an empty vector if the locale has not been
// or failed to be overridden.
const std::vector<std::string>& GetLocaleOverrides();

} // namespace l10n_util

#endif // APP_L10N_UTIL_WIN_H_
1 change: 1 addition & 0 deletions base/base.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@
'win/event_trace_consumer_unittest.cc',
'win/event_trace_controller_unittest.cc',
'win/event_trace_provider_unittest.cc',
'win/i18n_unittest.cc',
'win/pe_image_unittest.cc',
'win/registry_unittest.cc',
'win/scoped_bstr_unittest.cc',
Expand Down
2 changes: 2 additions & 0 deletions base/base.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,8 @@
'watchdog.h',
'weak_ptr.cc',
'weak_ptr.h',
'win/i18n.cc',
'win/i18n.h',
'win/pe_image.cc',
'win/event_trace_consumer.h',
'win/event_trace_controller.cc',
Expand Down
16 changes: 0 additions & 16 deletions base/i18n/rtl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,22 +44,6 @@ namespace i18n {
// Represents the locale-specific ICU text direction.
static TextDirection g_icu_text_direction = UNKNOWN_DIRECTION;

#if defined(OS_WIN)
void GetLanguageAndRegionFromOS(std::string* lang, std::string* region) {
// Later we may have to change this to be OS-dependent so that
// it's not affected by ICU's default locale. It's all right
// to do this way because SetICUDefaultLocale is internal
// to this file and we know that it's not yet called when this function
// is called.
const icu::Locale& locale = icu::Locale::getDefault();
const char* language = locale.getLanguage();
const char* country = locale.getCountry();
DCHECK(language);
*lang = language;
*region = country;
}
#endif

// Convert the ICU default locale to a string.
std::string GetConfiguredLocale() {
return GetLocaleString(icu::Locale::getDefault());
Expand Down
7 changes: 2 additions & 5 deletions base/i18n/rtl.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#define BASE_I18N_RTL_H_
#pragma once

#include <string>

#include "base/compiler_specific.h"
#include "base/string16.h"
#include "build/build_config.h"
Expand All @@ -29,11 +31,6 @@ enum TextDirection {
LEFT_TO_RIGHT,
};

#if defined(OS_WIN)
// Get language and region from the OS. Used by Chrome Frame.
void GetLanguageAndRegionFromOS(std::string* lang, std::string* region);
#endif

// Get the locale that the currently running process has been configured to use.
// The return value is of the form language[-country] (e.g., en-US) where the
// language is the 2 or 3 letter code from ISO-639.
Expand Down
169 changes: 169 additions & 0 deletions base/win/i18n.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
// 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"

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]
};

COMPILE_ASSERT(NUM_FUNCTIONS == arraysize(kLanguageFunctionNames),
language_function_enum_and_names_out_of_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 {
VLOG(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, &region)) {
// 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
Loading

0 comments on commit 337ca07

Please sign in to comment.