summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/l10n_util.cc78
-rw-r--r--app/l10n_util_unittest.cc32
2 files changed, 96 insertions, 14 deletions
diff --git a/app/l10n_util.cc b/app/l10n_util.cc
index f23de27..2afdf2b 100644
--- a/app/l10n_util.cc
+++ b/app/l10n_util.cc
@@ -4,6 +4,7 @@
#include "app/l10n_util.h"
+#include <cstdlib>
#include "app/app_paths.h"
#include "app/app_switches.h"
#include "app/gfx/canvas.h"
@@ -376,6 +377,43 @@ std::string GetSystemLocale() {
return ret;
}
+#if defined(OS_LINUX)
+// Split and normalize the language list specified by LANGUAGE environment.
+// LANGUAGE environment specifies a priority list of user prefered locales for
+// application UI messages. Locales are separated by ':' character. The format
+// of a locale is: language[_territory[.codeset]][@modifier]
+//
+// This function splits the language list and normalizes each locale into
+// language[-territory] format, eg. fr, zh-CN, etc.
+void SplitAndNormalizeLanguageList(const std::string& env_language,
+ std::vector<std::string>* result) {
+ std::vector<std::string> langs;
+ SplitString(env_language, ':', &langs);
+ std::vector<std::string>::iterator i = langs.begin();
+ for (; i != langs.end(); ++i) {
+ size_t end_pos = i->find_first_of(".@");
+ // Erase encoding and modifier part.
+ if (end_pos != std::string::npos)
+ i->erase(end_pos);
+
+ if (!i->empty()) {
+ std::string locale;
+ size_t sep = i->find_first_of("_-");
+ if (sep != std::string::npos) {
+ // language part is always in lower case.
+ locale = StringToLowerASCII(i->substr(0, sep));
+ locale.append("-");
+ // territory part is always in upper case.
+ locale.append(StringToUpperASCII(i->substr(sep + 1)));
+ } else {
+ locale = StringToLowerASCII(*i);
+ }
+ result->push_back(locale);
+ }
+ }
+}
+#endif
+
} // namespace
namespace l10n_util {
@@ -392,6 +430,8 @@ std::string GetApplicationLocale(const std::wstring& pref_locale) {
FilePath locale_path;
PathService::Get(app::DIR_LOCALES, &locale_path);
std::string resolved_locale;
+ std::vector<std::string> candidates;
+ const std::string system_locale = GetSystemLocale();
// We only use --lang and the app pref on Windows. On Linux/Mac, we only
// look at the LC_*/LANG environment variables. We do, however, pass --lang
@@ -402,23 +442,35 @@ std::string GetApplicationLocale(const std::wstring& pref_locale) {
const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
const std::string& lang_arg = WideToASCII(
parsed_command_line.GetSwitchValue(switches::kLang));
- if (!lang_arg.empty()) {
- if (CheckAndResolveLocale(lang_arg, locale_path, &resolved_locale))
- return resolved_locale;
- }
+ if (!lang_arg.empty())
+ candidates.push_back(lang_arg);
// Second, try user prefs.
- if (!pref_locale.empty()) {
- if (CheckAndResolveLocale(WideToASCII(pref_locale),
- locale_path, &resolved_locale))
- return resolved_locale;
- }
-#endif
+ if (!pref_locale.empty())
+ candidates.push_back(WideToASCII(pref_locale));
// Next, try the system locale.
- const std::string system_locale = GetSystemLocale();
- if (CheckAndResolveLocale(system_locale, locale_path, &resolved_locale))
- return resolved_locale;
+ candidates.push_back(system_locale);
+#elif defined(OS_LINUX)
+ // On Linux, we also check LANGUAGE environment variable, which is supported
+ // by gettext to specify a priority list of prefered languages.
+ const char* env_language = ::getenv("LANGUAGE");
+ if (env_language)
+ SplitAndNormalizeLanguageList(env_language, &candidates);
+
+ // Only fallback to the system locale if LANGUAGE is not specified.
+ // We emulate gettext's behavior here, which ignores LANG/LC_MESSAGES/LC_ALL
+ // when LANGUAGE is specified. If no language specified in LANGUAGE is valid,
+ // then just fallback to the default language, which is en-US for us.
+ if (candidates.empty())
+ candidates.push_back(system_locale);
+#endif
+
+ std::vector<std::string>::const_iterator i = candidates.begin();
+ for (; i != candidates.end(); ++i) {
+ if (CheckAndResolveLocale(*i, locale_path, &resolved_locale))
+ return resolved_locale;
+ }
// Fallback on en-US.
const std::string fallback_locale("en-US");
diff --git a/app/l10n_util_unittest.cc b/app/l10n_util_unittest.cc
index c9409fe..0298988 100644
--- a/app/l10n_util_unittest.cc
+++ b/app/l10n_util_unittest.cc
@@ -4,6 +4,10 @@
#include "build/build_config.h"
+#if defined(OS_LINUX)
+#include <cstdlib>
+#endif
+
#include "app/app_paths.h"
#include "app/l10n_util.h"
#if !defined(OS_MACOSX)
@@ -136,6 +140,33 @@ TEST_F(L10nUtilTest, GetAppLocale) {
// Keep a copy of ICU's default locale before we overwrite it.
icu::Locale locale = icu::Locale::getDefault();
+#if defined(OS_LINUX)
+ // Test the support of LANGUAGE environment variable.
+ SetICUDefaultLocale("en-US");
+ ::setenv("LANGUAGE", "xx:fr_CA", 1);
+ EXPECT_EQ("fr", l10n_util::GetApplicationLocale(L""));
+
+ ::setenv("LANGUAGE", "xx:yy:en_gb.utf-8@quot", 1);
+ EXPECT_EQ("en-GB", l10n_util::GetApplicationLocale(L""));
+
+ ::setenv("LANGUAGE", "xx:zh-hk", 1);
+ EXPECT_EQ("zh-TW", l10n_util::GetApplicationLocale(L""));
+
+ // We emulate gettext's behavior here, which ignores LANG/LC_MESSAGES/LC_ALL
+ // when LANGUAGE is specified. If no language specified in LANGUAGE is valid,
+ // then just fallback to the default language, which is en-US for us.
+ SetICUDefaultLocale("fr-FR");
+ ::setenv("LANGUAGE", "xx:yy", 1);
+ EXPECT_EQ("en-US", l10n_util::GetApplicationLocale(L""));
+
+ ::setenv("LANGUAGE", "/fr:zh_CN", 1);
+ EXPECT_EQ("zh-CN", l10n_util::GetApplicationLocale(L""));
+
+ // Make sure the follow tests won't be affected by LANGUAGE environment
+ // variable.
+ ::unsetenv("LANGUAGE");
+#endif
+
SetICUDefaultLocale("en-US");
EXPECT_EQ("en-US", l10n_util::GetApplicationLocale(L""));
@@ -479,4 +510,3 @@ TEST_F(L10nUtilTest, UpperLower) {
result = l10n_util::ToUpper(mixed);
EXPECT_EQ(result, expected_upper);
}
-