summaryrefslogtreecommitdiffstats
path: root/ui/base
diff options
context:
space:
mode:
authorben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-01-21 23:30:17 +0000
committerben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-01-21 23:30:17 +0000
commitc051a1b531d77e7d2eb3afd1dec67e13aab39154 (patch)
treea6dcc2760d1cdc216a329298052723cb0e25e242 /ui/base
parent28df88370f358468bffa1e1f6bc43aa25c11e388 (diff)
downloadchromium_src-c051a1b531d77e7d2eb3afd1dec67e13aab39154.zip
chromium_src-c051a1b531d77e7d2eb3afd1dec67e13aab39154.tar.gz
chromium_src-c051a1b531d77e7d2eb3afd1dec67e13aab39154.tar.bz2
Move l10n_util to ui/base
BUG=none TEST=none TBR=brettw git-svn-id: svn://svn.chromium.org/chrome/trunk/src@72227 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui/base')
-rw-r--r--ui/base/dragdrop/os_exchange_data_provider_win.cc2
-rw-r--r--ui/base/l10n/l10n_font_util.cc2
-rw-r--r--ui/base/l10n/l10n_util.cc876
-rw-r--r--ui/base/l10n/l10n_util.h161
-rw-r--r--ui/base/l10n/l10n_util_collator.h154
-rw-r--r--ui/base/l10n/l10n_util_mac.h82
-rw-r--r--ui/base/l10n/l10n_util_mac.mm167
-rw-r--r--ui/base/l10n/l10n_util_mac_unittest.mm46
-rw-r--r--ui/base/l10n/l10n_util_posix.cc14
-rw-r--r--ui/base/l10n/l10n_util_unittest.cc399
-rw-r--r--ui/base/l10n/l10n_util_win.cc185
-rw-r--r--ui/base/l10n/l10n_util_win.h63
-rw-r--r--ui/base/models/button_menu_item_model.cc2
-rw-r--r--ui/base/models/simple_menu_model.cc2
-rw-r--r--ui/base/models/table_model.cc4
-rw-r--r--ui/base/resource/data_pack_unittest.cc2
-rw-r--r--ui/base/resource/resource_bundle_posix.cc2
-rw-r--r--ui/base/resource/resource_bundle_win.cc4
18 files changed, 2157 insertions, 10 deletions
diff --git a/ui/base/dragdrop/os_exchange_data_provider_win.cc b/ui/base/dragdrop/os_exchange_data_provider_win.cc
index f8488a3..657a917 100644
--- a/ui/base/dragdrop/os_exchange_data_provider_win.cc
+++ b/ui/base/dragdrop/os_exchange_data_provider_win.cc
@@ -4,7 +4,6 @@
#include "ui/base/dragdrop/os_exchange_data_provider_win.h"
-#include "app/l10n_util.h"
#include "base/file_path.h"
#include "base/i18n/file_util_icu.h"
#include "base/logging.h"
@@ -17,6 +16,7 @@
#include "grit/app_strings.h"
#include "net/base/net_util.h"
#include "ui/base/clipboard/clipboard_util_win.h"
+#include "ui/base/l10n/l10n_util.h"
namespace ui {
diff --git a/ui/base/l10n/l10n_font_util.cc b/ui/base/l10n/l10n_font_util.cc
index 19f058f..9228de9 100644
--- a/ui/base/l10n/l10n_font_util.cc
+++ b/ui/base/l10n/l10n_font_util.cc
@@ -4,7 +4,7 @@
#include "ui/base/l10n/l10n_font_util.h"
-#include "app/l10n_util.h"
+#include "ui/base/l10n/l10n_util.h"
#include "base/logging.h"
#include "base/string_number_conversions.h"
#include "base/utf_string_conversions.h"
diff --git a/ui/base/l10n/l10n_util.cc b/ui/base/l10n/l10n_util.cc
new file mode 100644
index 0000000..26e12c85
--- /dev/null
+++ b/ui/base/l10n/l10n_util.cc
@@ -0,0 +1,876 @@
+// Copyright (c) 2011 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 "ui/base/l10n/l10n_util.h"
+
+#if defined(TOOLKIT_USES_GTK)
+#include <glib/gutils.h>
+#endif
+
+#include <algorithm>
+#include <cstdlib>
+#include <iterator>
+
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/i18n/file_util_icu.h"
+#include "base/i18n/rtl.h"
+#include "base/path_service.h"
+#include "base/scoped_ptr.h"
+#include "base/string16.h"
+#include "base/string_number_conversions.h"
+#include "base/string_split.h"
+#include "base/sys_string_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "gfx/canvas.h"
+#include "ui/base/l10n/l10n_util_collator.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/ui_base_paths.h"
+#include "unicode/rbbi.h"
+#include "unicode/uloc.h"
+
+#if defined(OS_MACOSX)
+#include "ui/base/l10n/l10n_util_mac.h"
+#elif defined(OS_WIN)
+#include "ui/base/l10n/l10n_util_win.h"
+#endif
+
+namespace {
+
+#if defined(OS_WIN)
+static const FilePath::CharType kLocaleFileExtension[] = L".dll";
+#elif defined(OS_POSIX)
+static const FilePath::CharType kLocaleFileExtension[] = ".pak";
+#endif
+
+static const char* const kAcceptLanguageList[] = {
+ "af", // Afrikaans
+ "am", // Amharic
+ "ar", // Arabic
+ "az", // Azerbaijani
+ "be", // Belarusian
+ "bg", // Bulgarian
+ "bh", // Bihari
+ "bn", // Bengali
+ "br", // Breton
+ "bs", // Bosnian
+ "ca", // Catalan
+ "co", // Corsican
+ "cs", // Czech
+ "cy", // Welsh
+ "da", // Danish
+ "de", // German
+ "de-AT", // German (Austria)
+ "de-CH", // German (Switzerland)
+ "de-DE", // German (Germany)
+ "el", // Greek
+ "en", // English
+ "en-AU", // English (Austrailia)
+ "en-CA", // English (Canada)
+ "en-GB", // English (UK)
+ "en-NZ", // English (New Zealand)
+ "en-US", // English (US)
+ "en-ZA", // English (South Africa)
+ "eo", // Esperanto
+ // TODO(jungshik) : Do we want to list all es-Foo for Latin-American
+ // Spanish speaking countries?
+ "es", // Spanish
+ "et", // Estonian
+ "eu", // Basque
+ "fa", // Persian
+ "fi", // Finnish
+ "fil", // Filipino
+ "fo", // Faroese
+ "fr", // French
+ "fr-CA", // French (Canada)
+ "fr-CH", // French (Switzerland)
+ "fr-FR", // French (France)
+ "fy", // Frisian
+ "ga", // Irish
+ "gd", // Scots Gaelic
+ "gl", // Galician
+ "gn", // Guarani
+ "gu", // Gujarati
+ "ha", // Hausa
+ "haw", // Hawaiian
+ "he", // Hebrew
+ "hi", // Hindi
+ "hr", // Croatian
+ "hu", // Hungarian
+ "hy", // Armenian
+ "ia", // Interlingua
+ "id", // Indonesian
+ "is", // Icelandic
+ "it", // Italian
+ "it-CH", // Italian (Switzerland)
+ "it-IT", // Italian (Italy)
+ "ja", // Japanese
+ "jw", // Javanese
+ "ka", // Georgian
+ "kk", // Kazakh
+ "km", // Cambodian
+ "kn", // Kannada
+ "ko", // Korean
+ "ku", // Kurdish
+ "ky", // Kyrgyz
+ "la", // Latin
+ "ln", // Lingala
+ "lo", // Laothian
+ "lt", // Lithuanian
+ "lv", // Latvian
+ "mk", // Macedonian
+ "ml", // Malayalam
+ "mn", // Mongolian
+ "mo", // Moldavian
+ "mr", // Marathi
+ "ms", // Malay
+ "mt", // Maltese
+ "nb", // Norwegian (Bokmal)
+ "ne", // Nepali
+ "nl", // Dutch
+ "nn", // Norwegian (Nynorsk)
+ "no", // Norwegian
+ "oc", // Occitan
+ "om", // Oromo
+ "or", // Oriya
+ "pa", // Punjabi
+ "pl", // Polish
+ "ps", // Pashto
+ "pt", // Portuguese
+ "pt-BR", // Portuguese (Brazil)
+ "pt-PT", // Portuguese (Portugal)
+ "qu", // Quechua
+ "rm", // Romansh
+ "ro", // Romanian
+ "ru", // Russian
+ "sd", // Sindhi
+ "sh", // Serbo-Croatian
+ "si", // Sinhalese
+ "sk", // Slovak
+ "sl", // Slovenian
+ "sn", // Shona
+ "so", // Somali
+ "sq", // Albanian
+ "sr", // Serbian
+ "st", // Sesotho
+ "su", // Sundanese
+ "sv", // Swedish
+ "sw", // Swahili
+ "ta", // Tamil
+ "te", // Telugu
+ "tg", // Tajik
+ "th", // Thai
+ "ti", // Tigrinya
+ "tk", // Turkmen
+ "to", // Tonga
+ "tr", // Turkish
+ "tt", // Tatar
+ "tw", // Twi
+ "ug", // Uighur
+ "uk", // Ukrainian
+ "ur", // Urdu
+ "uz", // Uzbek
+ "vi", // Vietnamese
+ "xh", // Xhosa
+ "yi", // Yiddish
+ "yo", // Yoruba
+ "zh", // Chinese
+ "zh-CN", // Chinese (Simplified)
+ "zh-TW", // Chinese (Traditional)
+ "zu", // Zulu
+};
+
+// Returns true if |locale_name| has an alias in the ICU data file.
+bool IsDuplicateName(const std::string& locale_name) {
+ static const char* const kDuplicateNames[] = {
+ "en",
+ "pt",
+ "zh",
+ "zh_hans_cn",
+ "zh_hant_tw"
+ };
+
+ // Skip all 'es_RR'. Currently, we use 'es' for es-ES (Spanish in Spain).
+ // 'es-419' (Spanish in Latin America) is not available in ICU so that it
+ // has to be added manually in GetAvailableLocales().
+ if (LowerCaseEqualsASCII(locale_name.substr(0, 3), "es_"))
+ return true;
+ for (size_t i = 0; i < arraysize(kDuplicateNames); ++i) {
+ if (base::strcasecmp(kDuplicateNames[i], locale_name.c_str()) == 0)
+ return true;
+ }
+ return false;
+}
+
+bool IsLocaleNameTranslated(const char* locale,
+ const std::string& display_locale) {
+ string16 display_name =
+ l10n_util::GetDisplayNameForLocale(locale, display_locale, false);
+ // Because ICU sets the error code to U_USING_DEFAULT_WARNING whether or not
+ // uloc_getDisplayName returns the actual translation or the default
+ // value (locale code), we have to rely on this hack to tell whether
+ // the translation is available or not. If ICU doesn't have a translated
+ // name for this locale, GetDisplayNameForLocale will just return the
+ // locale code.
+ return !IsStringASCII(display_name) || UTF16ToASCII(display_name) != locale;
+}
+
+// We added 30+ minimally populated locales with only a few entries
+// (exemplar character set, script, writing direction and its own
+// lanaguage name). These locales have to be distinguished from the
+// fully populated locales to which Chrome is localized.
+bool IsLocalePartiallyPopulated(const std::string& locale_name) {
+ // For partially populated locales, even the translation for "English"
+ // is not available. A more robust/elegant way to check is to add a special
+ // field (say, 'isPartial' to our version of ICU locale files) and
+ // check its value, but this hack seems to work well.
+ return !IsLocaleNameTranslated("en", locale_name);
+}
+
+#if !defined(OS_MACOSX)
+bool IsLocaleAvailable(const std::string& locale,
+ const FilePath& locale_path) {
+ // If locale has any illegal characters in it, we don't want to try to
+ // load it because it may be pointing outside the locale data file directory.
+ if (!file_util::IsFilenameLegal(ASCIIToUTF16(locale)))
+ return false;
+
+ // IsLocalePartiallyPopulated() can be called here for an early return w/o
+ // checking the resource availability below. It'd help when Chrome is run
+ // under a system locale Chrome is not localized to (e.g.Farsi on Linux),
+ // but it'd slow down the start up time a little bit for locales Chrome is
+ // localized to. So, we don't call it here.
+ if (!l10n_util::IsLocaleSupportedByOS(locale))
+ return false;
+
+ FilePath test_path = locale_path;
+ test_path =
+ test_path.AppendASCII(locale).ReplaceExtension(kLocaleFileExtension);
+ return file_util::PathExists(test_path);
+}
+
+bool CheckAndResolveLocale(const std::string& locale,
+ const FilePath& locale_path,
+ std::string* resolved_locale) {
+ if (IsLocaleAvailable(locale, locale_path)) {
+ *resolved_locale = locale;
+ return true;
+ }
+ // If the locale matches language but not country, use that instead.
+ // TODO(jungshik) : Nothing is done about languages that Chrome
+ // does not support but available on Windows. We fall
+ // back to en-US in GetApplicationLocale so that it's a not critical,
+ // but we can do better.
+ std::string::size_type hyphen_pos = locale.find('-');
+ if (hyphen_pos != std::string::npos && hyphen_pos > 0) {
+ std::string lang(locale, 0, hyphen_pos);
+ std::string region(locale, hyphen_pos + 1);
+ std::string tmp_locale(lang);
+ // Map es-RR other than es-ES to es-419 (Chrome's Latin American
+ // Spanish locale).
+ if (LowerCaseEqualsASCII(lang, "es") && !LowerCaseEqualsASCII(region, "es"))
+ tmp_locale.append("-419");
+ else if (LowerCaseEqualsASCII(lang, "zh")) {
+ // Map zh-HK and zh-MK to zh-TW. Otherwise, zh-FOO is mapped to zh-CN.
+ if (LowerCaseEqualsASCII(region, "hk") ||
+ LowerCaseEqualsASCII(region, "mk")) {
+ tmp_locale.append("-TW");
+ } else {
+ tmp_locale.append("-CN");
+ }
+ }
+ if (IsLocaleAvailable(tmp_locale, locale_path)) {
+ resolved_locale->swap(tmp_locale);
+ return true;
+ }
+ }
+
+ // Google updater uses no, iw and en for our nb, he, and en-US.
+ // We need to map them to our codes.
+ struct {
+ const char* source;
+ const char* dest;
+ } alias_map[] = {
+ {"no", "nb"},
+ {"tl", "fil"},
+ {"iw", "he"},
+ {"en", "en-US"},
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(alias_map); ++i) {
+ if (LowerCaseEqualsASCII(locale, alias_map[i].source)) {
+ std::string tmp_locale(alias_map[i].dest);
+ if (IsLocaleAvailable(tmp_locale, locale_path)) {
+ resolved_locale->swap(tmp_locale);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+#endif
+
+// On Linux, the text layout engine Pango determines paragraph directionality
+// by looking at the first strongly-directional character in the text. This
+// means text such as "Google Chrome foo bar..." will be layed out LTR even
+// if "foo bar" is RTL. So this function prepends the necessary RLM in such
+// cases.
+void AdjustParagraphDirectionality(string16* paragraph) {
+#if defined(OS_LINUX)
+ if (base::i18n::IsRTL() &&
+ base::i18n::StringContainsStrongRTLChars(*paragraph)) {
+ paragraph->insert(0, 1, static_cast<char16>(base::i18n::kRightToLeftMark));
+ }
+#endif
+}
+
+#if defined(OS_WIN)
+std::string GetCanonicalLocale(const std::string& locale) {
+ return base::i18n::GetCanonicalLocale(locale.c_str());
+}
+#endif
+
+} // namespace
+
+namespace l10n_util {
+
+std::string GetApplicationLocale(const std::string& pref_locale) {
+#if defined(OS_MACOSX)
+
+ // Use any override (Cocoa for the browser), otherwise use the preference
+ // passed to the function.
+ std::string app_locale = l10n_util::GetLocaleOverride();
+ if (app_locale.empty())
+ app_locale = pref_locale;
+
+ // The above should handle all of the cases Chrome normally hits, but for some
+ // unit tests, we need something to fall back too.
+ if (app_locale.empty())
+ app_locale = "en-US";
+
+ // Windows/Linux call SetICUDefaultLocale after determining the actual locale
+ // with CheckAndResolveLocal to make ICU APIs work in that locale.
+ // Mac doesn't use a locale directory tree of resources (it uses Mac style
+ // resources), so mirror the Windows/Linux behavior of calling
+ // SetICUDefaultLocale.
+ base::i18n::SetICUDefaultLocale(app_locale);
+ return app_locale;
+
+#else
+
+ FilePath locale_path;
+ PathService::Get(ui::DIR_LOCALES, &locale_path);
+ std::string resolved_locale;
+ std::vector<std::string> candidates;
+
+ // We only use --lang and the app pref on Windows. On Linux, we only
+ // look at the LC_*/LANG environment variables. We do, however, pass --lang
+ // to renderer and plugin processes so they know what language the parent
+ // process decided to use.
+
+#if defined(OS_WIN)
+
+ // First, try the preference value.
+ if (!pref_locale.empty())
+ candidates.push_back(pref_locale);
+
+ // 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)
+
+ // On ChromeOS, use the application locale preference.
+ if (!pref_locale.empty())
+ candidates.push_back(pref_locale);
+
+#elif defined(OS_POSIX) && defined(TOOLKIT_USES_GTK)
+
+ // GLib implements correct environment variable parsing with
+ // the precedence order: LANGUAGE, LC_ALL, LC_MESSAGES and LANG.
+ // We used to use our custom parsing code along with ICU for this purpose.
+ // If we have a port that does not depend on GTK, we have to
+ // restore our custom code for that port.
+ const char* const* languages = g_get_language_names();
+ DCHECK(languages); // A valid pointer is guaranteed.
+ DCHECK(*languages); // At least one entry, "C", is guaranteed.
+
+ for (; *languages != NULL; ++languages) {
+ candidates.push_back(base::i18n::GetCanonicalLocale(*languages));
+ }
+
+#else
+#error Unsupported platform, see build/build_config.h
+#endif
+
+ std::vector<std::string>::const_iterator i = candidates.begin();
+ for (; i != candidates.end(); ++i) {
+ if (CheckAndResolveLocale(*i, locale_path, &resolved_locale)) {
+ base::i18n::SetICUDefaultLocale(resolved_locale);
+ return resolved_locale;
+ }
+ }
+
+ // Fallback on en-US.
+ const std::string fallback_locale("en-US");
+ if (IsLocaleAvailable(fallback_locale, locale_path)) {
+ base::i18n::SetICUDefaultLocale(fallback_locale);
+ return fallback_locale;
+ }
+
+ // No locale data file was found; we shouldn't get here.
+ NOTREACHED();
+
+ return std::string();
+
+#endif
+}
+
+string16 GetDisplayNameForLocale(const std::string& locale,
+ const std::string& display_locale,
+ bool is_for_ui) {
+ std::string locale_code = locale;
+ // Internally, we use the language code of zh-CN and zh-TW, but we want the
+ // display names to be Chinese (Simplified) and Chinese (Traditional) instead
+ // of Chinese (China) and Chinese (Taiwan). To do that, we pass zh-Hans
+ // and zh-Hant to ICU. Even with this mapping, we'd get
+ // 'Chinese (Simplified Han)' and 'Chinese (Traditional Han)' in English and
+ // even longer results in other languages. Arguably, they're better than
+ // the current results : Chinese (China) / Chinese (Taiwan).
+ // TODO(jungshik): Do one of the following:
+ // 1. Special-case Chinese by getting the custom-translation for them
+ // 2. Recycle IDS_ENCODING_{SIMP,TRAD}_CHINESE.
+ // 3. Get translations for two directly from the ICU resouce bundle
+ // because they're not accessible with other any API.
+ // 4. Patch ICU to special-case zh-Hans/zh-Hant for us.
+ // #1 and #2 wouldn't work if display_locale != current UI locale although
+ // we can think of additional hack to work around the problem.
+ // #3 can be potentially expensive.
+ if (locale_code == "zh-CN")
+ locale_code = "zh-Hans";
+ else if (locale_code == "zh-TW")
+ locale_code = "zh-Hant";
+
+ UErrorCode error = U_ZERO_ERROR;
+ const int buffer_size = 1024;
+
+ string16 display_name;
+ int actual_size = uloc_getDisplayName(locale_code.c_str(),
+ display_locale.c_str(),
+ WriteInto(&display_name, buffer_size + 1), buffer_size, &error);
+ DCHECK(U_SUCCESS(error));
+ display_name.resize(actual_size);
+ // Add an RTL mark so parentheses are properly placed.
+ if (is_for_ui && base::i18n::IsRTL())
+ display_name.push_back(static_cast<char16>(base::i18n::kRightToLeftMark));
+ return display_name;
+}
+
+std::string NormalizeLocale(const std::string& locale) {
+ std::string normalized_locale(locale);
+ std::replace(normalized_locale.begin(), normalized_locale.end(), '-', '_');
+
+ return normalized_locale;
+}
+
+void GetParentLocales(const std::string& current_locale,
+ std::vector<std::string>* parent_locales) {
+ std::string locale(NormalizeLocale(current_locale));
+
+ const int kNameCapacity = 256;
+ char parent[kNameCapacity];
+ base::strlcpy(parent, locale.c_str(), kNameCapacity);
+ parent_locales->push_back(parent);
+ UErrorCode err = U_ZERO_ERROR;
+ while (uloc_getParent(parent, parent, kNameCapacity, &err) > 0) {
+ if (U_FAILURE(err))
+ break;
+ parent_locales->push_back(parent);
+ }
+}
+
+bool IsValidLocaleSyntax(const std::string& locale) {
+ // Check that the length is plausible.
+ if (locale.size() < 2 || locale.size() >= ULOC_FULLNAME_CAPACITY)
+ return false;
+
+ // Strip off the part after an '@' sign, which might contain keywords,
+ // as in en_IE@currency=IEP or fr@collation=phonebook;calendar=islamic-civil.
+ // We don't validate that part much, just check that there's at least one
+ // equals sign in a plausible place. Normalize the prefix so that hyphens
+ // are changed to underscores.
+ std::string prefix = NormalizeLocale(locale);
+ size_t split_point = locale.find("@");
+ if (split_point != std::string::npos) {
+ std::string keywords = locale.substr(split_point + 1);
+ prefix = locale.substr(0, split_point);
+
+ size_t equals_loc = keywords.find("=");
+ if (equals_loc == std::string::npos ||
+ equals_loc < 1 || equals_loc > keywords.size() - 2)
+ return false;
+ }
+
+ // Check that all characters before the at-sign are alphanumeric or
+ // underscore.
+ for (size_t i = 0; i < prefix.size(); i++) {
+ char ch = prefix[i];
+ if (!IsAsciiAlpha(ch) && !IsAsciiDigit(ch) && ch != '_')
+ return false;
+ }
+
+ // Check that the initial token (before the first hyphen/underscore)
+ // is 1 - 3 alphabetical characters (a language tag).
+ for (size_t i = 0; i < prefix.size(); i++) {
+ char ch = prefix[i];
+ if (ch == '_') {
+ if (i < 1 || i > 3)
+ return false;
+ break;
+ }
+ if (!IsAsciiAlpha(ch))
+ return false;
+ }
+
+ // Check that the all tokens after the initial token are 1 - 8 characters.
+ // (Tokenize/StringTokenizer don't work here, they collapse multiple
+ // delimiters into one.)
+ int token_len = 0;
+ int token_index = 0;
+ for (size_t i = 0; i < prefix.size(); i++) {
+ if (prefix[i] != '_') {
+ token_len++;
+ continue;
+ }
+
+ if (token_index > 0 && (token_len < 1 || token_len > 8)) {
+ return false;
+ }
+ token_index++;
+ token_len = 0;
+ }
+ if (token_index == 0 && (token_len < 1 || token_len > 3)) {
+ return false;
+ } else if (token_len < 1 || token_len > 8) {
+ return false;
+ }
+
+ return true;
+}
+
+std::string GetStringUTF8(int message_id) {
+ return UTF16ToUTF8(GetStringUTF16(message_id));
+}
+
+string16 GetStringUTF16(int message_id) {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ string16 str = rb.GetLocalizedString(message_id);
+ AdjustParagraphDirectionality(&str);
+
+ return str;
+}
+
+static string16 GetStringF(int message_id,
+ const std::vector<string16>& replacements,
+ std::vector<size_t>* offsets) {
+ // TODO(tc): We could save a string copy if we got the raw string as
+ // a StringPiece and were able to call ReplaceStringPlaceholders with
+ // a StringPiece format string and string16 substitution strings. In
+ // practice, the strings should be relatively short.
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ const string16& format_string = rb.GetLocalizedString(message_id);
+
+#ifndef NDEBUG
+ // Make sure every replacement string is being used, so we don't just
+ // silently fail to insert one. If |offsets| is non-NULL, then don't do this
+ // check as the code may simply want to find the placeholders rather than
+ // actually replacing them.
+ if (!offsets) {
+ std::string utf8_string = UTF16ToUTF8(format_string);
+
+ // $9 is the highest allowed placeholder.
+ for (size_t i = 0; i < 9; ++i) {
+ bool placeholder_should_exist = replacements.size() > i;
+
+ std::string placeholder = StringPrintf("$%d", static_cast<int>(i + 1));
+ size_t pos = utf8_string.find(placeholder.c_str());
+ if (placeholder_should_exist) {
+ DCHECK_NE(std::string::npos, pos) <<
+ " Didn't find a " << placeholder << " placeholder in " <<
+ utf8_string;
+ } else {
+ DCHECK_EQ(std::string::npos, pos) <<
+ " Unexpectedly found a " << placeholder << " placeholder in " <<
+ utf8_string;
+ }
+ }
+ }
+#endif
+
+ string16 formatted = ReplaceStringPlaceholders(format_string, replacements,
+ offsets);
+ AdjustParagraphDirectionality(&formatted);
+
+ return formatted;
+}
+
+std::string GetStringFUTF8(int message_id,
+ const string16& a) {
+ return UTF16ToUTF8(GetStringFUTF16(message_id, a));
+}
+
+std::string GetStringFUTF8(int message_id,
+ const string16& a,
+ const string16& b) {
+ return UTF16ToUTF8(GetStringFUTF16(message_id, a, b));
+}
+
+std::string GetStringFUTF8(int message_id,
+ const string16& a,
+ const string16& b,
+ const string16& c) {
+ return UTF16ToUTF8(GetStringFUTF16(message_id, a, b, c));
+}
+
+std::string GetStringFUTF8(int message_id,
+ const string16& a,
+ const string16& b,
+ const string16& c,
+ const string16& d) {
+ return UTF16ToUTF8(GetStringFUTF16(message_id, a, b, c, d));
+}
+
+string16 GetStringFUTF16(int message_id,
+ const string16& a) {
+ std::vector<string16> replacements;
+ replacements.push_back(a);
+ return GetStringF(message_id, replacements, NULL);
+}
+
+string16 GetStringFUTF16(int message_id,
+ const string16& a,
+ const string16& b) {
+ return GetStringFUTF16(message_id, a, b, NULL);
+}
+
+string16 GetStringFUTF16(int message_id,
+ const string16& a,
+ const string16& b,
+ const string16& c) {
+ std::vector<string16> replacements;
+ replacements.push_back(a);
+ replacements.push_back(b);
+ replacements.push_back(c);
+ return GetStringF(message_id, replacements, NULL);
+}
+
+string16 GetStringFUTF16(int message_id,
+ const string16& a,
+ const string16& b,
+ const string16& c,
+ const string16& d) {
+ std::vector<string16> replacements;
+ replacements.push_back(a);
+ replacements.push_back(b);
+ replacements.push_back(c);
+ replacements.push_back(d);
+ return GetStringF(message_id, replacements, NULL);
+}
+
+string16 GetStringFUTF16(int message_id, const string16& a, size_t* offset) {
+ DCHECK(offset);
+ std::vector<size_t> offsets;
+ std::vector<string16> replacements;
+ replacements.push_back(a);
+ string16 result = GetStringF(message_id, replacements, &offsets);
+ DCHECK(offsets.size() == 1);
+ *offset = offsets[0];
+ return result;
+}
+
+string16 GetStringFUTF16(int message_id,
+ const string16& a,
+ const string16& b,
+ std::vector<size_t>* offsets) {
+ std::vector<string16> replacements;
+ replacements.push_back(a);
+ replacements.push_back(b);
+ return GetStringF(message_id, replacements, offsets);
+}
+
+string16 GetStringFUTF16Int(int message_id, int a) {
+ return GetStringFUTF16(message_id, UTF8ToUTF16(base::IntToString(a)));
+}
+
+string16 GetStringFUTF16Int(int message_id, int64 a) {
+ return GetStringFUTF16(message_id, UTF8ToUTF16(base::Int64ToString(a)));
+}
+
+string16 TruncateString(const string16& string, size_t length) {
+ if (string.size() <= length)
+ // String fits, return it.
+ return string;
+
+ if (length == 0) {
+ // No room for the elide string, return an empty string.
+ return string16();
+ }
+ size_t max = length - 1;
+
+ // Added to the end of strings that are too big.
+ static const char16 kElideString[] = { 0x2026, 0 };
+
+ if (max == 0) {
+ // Just enough room for the elide string.
+ return kElideString;
+ }
+
+ // Use a line iterator to find the first boundary.
+ UErrorCode status = U_ZERO_ERROR;
+ scoped_ptr<icu::RuleBasedBreakIterator> bi(
+ static_cast<icu::RuleBasedBreakIterator*>(
+ icu::RuleBasedBreakIterator::createLineInstance(
+ icu::Locale::getDefault(), status)));
+ if (U_FAILURE(status))
+ return string.substr(0, max) + kElideString;
+ bi->setText(string.c_str());
+ int32_t index = bi->preceding(static_cast<int32_t>(max));
+ if (index == icu::BreakIterator::DONE) {
+ index = static_cast<int32_t>(max);
+ } else {
+ // Found a valid break (may be the beginning of the string). Now use
+ // a character iterator to find the previous non-whitespace character.
+ icu::StringCharacterIterator char_iterator(string.c_str());
+ if (index == 0) {
+ // No valid line breaks. Start at the end again. This ensures we break
+ // on a valid character boundary.
+ index = static_cast<int32_t>(max);
+ }
+ char_iterator.setIndex(index);
+ while (char_iterator.hasPrevious()) {
+ char_iterator.previous();
+ if (!(u_isspace(char_iterator.current()) ||
+ u_charType(char_iterator.current()) == U_CONTROL_CHAR ||
+ u_charType(char_iterator.current()) == U_NON_SPACING_MARK)) {
+ // Not a whitespace character. Advance the iterator so that we
+ // include the current character in the truncated string.
+ char_iterator.next();
+ break;
+ }
+ }
+ if (char_iterator.hasPrevious()) {
+ // Found a valid break point.
+ index = char_iterator.getIndex();
+ } else {
+ // String has leading whitespace, return the elide string.
+ return kElideString;
+ }
+ }
+ return string.substr(0, index) + kElideString;
+}
+
+string16 ToLower(const string16& string) {
+ icu::UnicodeString lower_u_str(
+ icu::UnicodeString(string.c_str()).toLower(icu::Locale::getDefault()));
+ string16 result;
+ lower_u_str.extract(0, lower_u_str.length(),
+ WriteInto(&result, lower_u_str.length() + 1));
+ return result;
+}
+
+string16 ToUpper(const string16& string) {
+ icu::UnicodeString upper_u_str(
+ icu::UnicodeString(string.c_str()).toUpper(icu::Locale::getDefault()));
+ string16 result;
+ upper_u_str.extract(0, upper_u_str.length(),
+ WriteInto(&result, upper_u_str.length() + 1));
+ return result;
+}
+
+// Compares the character data stored in two different string16 strings by
+// specified Collator instance.
+UCollationResult CompareString16WithCollator(const icu::Collator* collator,
+ const string16& lhs,
+ const string16& rhs) {
+ DCHECK(collator);
+ UErrorCode error = U_ZERO_ERROR;
+ UCollationResult result = collator->compare(
+ static_cast<const UChar*>(lhs.c_str()), static_cast<int>(lhs.length()),
+ static_cast<const UChar*>(rhs.c_str()), static_cast<int>(rhs.length()),
+ error);
+ DCHECK(U_SUCCESS(error));
+ return result;
+}
+
+// Specialization of operator() method for string16 version.
+template <>
+bool StringComparator<string16>::operator()(const string16& lhs,
+ const string16& rhs) {
+ // If we can not get collator instance for specified locale, just do simple
+ // string compare.
+ if (!collator_)
+ return lhs < rhs;
+ return CompareString16WithCollator(collator_, lhs, rhs) == UCOL_LESS;
+};
+
+void SortStrings16(const std::string& locale,
+ std::vector<string16>* strings) {
+ SortVectorWithStringKey(locale, strings, false);
+}
+
+const std::vector<std::string>& GetAvailableLocales() {
+ static std::vector<std::string> locales;
+ if (locales.empty()) {
+ int num_locales = uloc_countAvailable();
+ for (int i = 0; i < num_locales; ++i) {
+ std::string locale_name = uloc_getAvailable(i);
+ // Filter out the names that have aliases.
+ if (IsDuplicateName(locale_name))
+ continue;
+ // Filter out locales for which we have only partially populated data
+ // and to which Chrome is not localized.
+ if (IsLocalePartiallyPopulated(locale_name))
+ continue;
+ if (!IsLocaleSupportedByOS(locale_name))
+ continue;
+ // Normalize underscores to hyphens because that's what our locale files
+ // use.
+ std::replace(locale_name.begin(), locale_name.end(), '_', '-');
+
+ // Map the Chinese locale names over to zh-CN and zh-TW.
+ if (LowerCaseEqualsASCII(locale_name, "zh-hans")) {
+ locale_name = "zh-CN";
+ } else if (LowerCaseEqualsASCII(locale_name, "zh-hant")) {
+ locale_name = "zh-TW";
+ }
+ locales.push_back(locale_name);
+ }
+
+ // Manually add 'es-419' to the list. See the comment in IsDuplicateName().
+ locales.push_back("es-419");
+ }
+ return locales;
+}
+
+void GetAcceptLanguagesForLocale(const std::string& display_locale,
+ std::vector<std::string>* locale_codes) {
+ for (size_t i = 0; i < arraysize(kAcceptLanguageList); ++i) {
+ if (!IsLocaleNameTranslated(kAcceptLanguageList[i], display_locale))
+ // TODO(jungshik) : Put them at the of the list with language codes
+ // enclosed by brackets instead of skipping.
+ continue;
+ locale_codes->push_back(kAcceptLanguageList[i]);
+ }
+}
+
+} // namespace l10n_util
diff --git a/ui/base/l10n/l10n_util.h b/ui/base/l10n/l10n_util.h
new file mode 100644
index 0000000..e0c6150
--- /dev/null
+++ b/ui/base/l10n/l10n_util.h
@@ -0,0 +1,161 @@
+// Copyright (c) 2011 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.
+
+// This file contains utility functions for dealing with localized
+// content.
+
+#ifndef UI_BASE_L10N_L10N_UTIL_H_
+#define UI_BASE_L10N_L10N_UTIL_H_
+#pragma once
+
+#include <algorithm>
+#include <functional>
+#include <string>
+#include <vector>
+
+#include "build/build_config.h"
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "base/string16.h"
+#include "base/string_util.h"
+
+#if defined(OS_MACOSX)
+#include "ui/base/l10n/l10n_util_mac.h"
+#endif // OS_MACOSX
+
+namespace l10n_util {
+
+// This method is responsible for determining the locale as defined below. In
+// nearly all cases you shouldn't call this, rather use GetApplicationLocale
+// defined on browser_process.
+//
+// Returns the locale used by the Application. First we use the value from the
+// command line (--lang), second we try the value in the prefs file (passed in
+// as |pref_locale|), finally, we fall back on the system locale. We only return
+// a value if there's a corresponding resource DLL for the locale. Otherwise,
+// we fall back to en-us.
+std::string GetApplicationLocale(const std::string& pref_locale);
+
+// Given a locale code, return true if the OS is capable of supporting it.
+// For instance, Oriya is not well supported on Windows XP and we return
+// false for "or".
+bool IsLocaleSupportedByOS(const std::string& locale);
+
+// This method returns the display name of the locale code in |display_locale|.
+
+// For example, for |locale| = "fr" and |display_locale| = "en",
+// it returns "French". To get the display name of
+// |locale| in the UI language of Chrome, |display_locale| can be
+// set to the return value of g_browser_process->GetApplicationLocale()
+// in the UI thread.
+// If |is_for_ui| is true, U+200F is appended so that it can be
+// rendered properly in a RTL Chrome.
+string16 GetDisplayNameForLocale(const std::string& locale,
+ const std::string& display_locale,
+ bool is_for_ui);
+
+// Converts all - into _, to be consistent with ICU and file system names.
+std::string NormalizeLocale(const std::string& locale);
+
+// Produce a vector of parent locales for given locale.
+// It includes the current locale in the result.
+// sr_Cyrl_RS generates sr_Cyrl_RS, sr_Cyrl and sr.
+void GetParentLocales(const std::string& current_locale,
+ std::vector<std::string>* parent_locales);
+
+// Checks if a string is plausibly a syntactically-valid locale string,
+// for cases where we want the valid input to be a locale string such as
+// 'en', 'pt-BR', 'fil', 'es-419', 'zh-Hans-CN', 'i-klingon' or
+// 'de_DE@collation=phonebook', but we don't want to limit it to
+// locales that Chrome actually knows about, so 'xx-YY' should be
+// accepted, but 'z', 'German', 'en-$1', or 'abcd-1234' should not.
+// Case-insensitive. Based on BCP 47, see:
+// http://unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers
+bool IsValidLocaleSyntax(const std::string& locale);
+
+//
+// Mac Note: See l10n_util_mac.h for some NSString versions and other support.
+//
+
+// Pulls resource string from the string bundle and returns it.
+std::string GetStringUTF8(int message_id);
+string16 GetStringUTF16(int message_id);
+
+// Get a resource string and replace $1-$2-$3 with |a| and |b|
+// respectively. Additionally, $$ is replaced by $.
+string16 GetStringFUTF16(int message_id,
+ const string16& a);
+string16 GetStringFUTF16(int message_id,
+ const string16& a,
+ const string16& b);
+string16 GetStringFUTF16(int message_id,
+ const string16& a,
+ const string16& b,
+ const string16& c);
+string16 GetStringFUTF16(int message_id,
+ const string16& a,
+ const string16& b,
+ const string16& c,
+ const string16& d);
+std::string GetStringFUTF8(int message_id,
+ const string16& a);
+std::string GetStringFUTF8(int message_id,
+ const string16& a,
+ const string16& b);
+std::string GetStringFUTF8(int message_id,
+ const string16& a,
+ const string16& b,
+ const string16& c);
+std::string GetStringFUTF8(int message_id,
+ const string16& a,
+ const string16& b,
+ const string16& c,
+ const string16& d);
+
+// Variants that return the offset(s) of the replaced parameters. The
+// vector based version returns offsets ordered by parameter. For example if
+// invoked with a and b offsets[0] gives the offset for a and offsets[1] the
+// offset of b regardless of where the parameters end up in the string.
+string16 GetStringFUTF16(int message_id,
+ const string16& a,
+ size_t* offset);
+string16 GetStringFUTF16(int message_id,
+ const string16& a,
+ const string16& b,
+ std::vector<size_t>* offsets);
+
+// Convenience functions to get a string with a single number as a parameter.
+string16 GetStringFUTF16Int(int message_id, int a);
+string16 GetStringFUTF16Int(int message_id, int64 a);
+
+// Truncates the string to length characters. This breaks the string at
+// the first word break before length, adding the horizontal ellipsis
+// character (unicode character 0x2026) to render ...
+// The supplied string is returned if the string has length characters or
+// less.
+string16 TruncateString(const string16& string, size_t length);
+
+// Returns the lower case equivalent of string.
+string16 ToLower(const string16& string);
+
+// Returns the upper case equivalent of string.
+string16 ToUpper(const string16& string);
+
+// In place sorting of string16 strings using collation rules for |locale|.
+void SortStrings16(const std::string& locale,
+ std::vector<string16>* strings);
+
+// Returns a vector of available locale codes. E.g., a vector containing
+// en-US, es, fr, fi, pt-PT, pt-BR, etc.
+const std::vector<std::string>& GetAvailableLocales();
+
+// Returns a vector of locale codes usable for accept-languages.
+void GetAcceptLanguagesForLocale(const std::string& display_locale,
+ std::vector<std::string>* locale_codes);
+
+
+} // namespace l10n_util
+
+#endif // UI_BASE_L10N_L10N_UTIL_H_
diff --git a/ui/base/l10n/l10n_util_collator.h b/ui/base/l10n/l10n_util_collator.h
new file mode 100644
index 0000000..b2c3b9b
--- /dev/null
+++ b/ui/base/l10n/l10n_util_collator.h
@@ -0,0 +1,154 @@
+// Copyright (c) 2011 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.
+
+#ifndef UI_BASE_L10N_L10N_UTIL_COLLATOR_H_
+#define UI_BASE_L10N_L10N_UTIL_COLLATOR_H_
+#pragma once
+
+#include <algorithm>
+#include <functional>
+#include <string>
+#include <vector>
+
+#include "base/utf_string_conversions.h"
+#include "unicode/coll.h"
+
+namespace l10n_util {
+
+// Compares the two strings using the specified collator.
+UCollationResult CompareString16WithCollator(const icu::Collator* collator,
+ const string16& lhs,
+ const string16& rhs);
+
+// Used by SortStringsUsingMethod. Invokes a method on the objects passed to
+// operator (), comparing the string results using a collator.
+template <class T, class Method>
+class StringMethodComparatorWithCollator
+ : public std::binary_function<const string16&,
+ const string16&,
+ bool> {
+ public:
+ StringMethodComparatorWithCollator(icu::Collator* collator, Method method)
+ : collator_(collator),
+ method_(method) { }
+
+ // Returns true if lhs preceeds rhs.
+ bool operator() (T* lhs_t, T* rhs_t) {
+ return CompareString16WithCollator(collator_, (lhs_t->*method_)(),
+ (rhs_t->*method_)()) == UCOL_LESS;
+ }
+
+ private:
+ icu::Collator* collator_;
+ Method method_;
+};
+
+// Used by SortStringsUsingMethod. Invokes a method on the objects passed to
+// operator (), comparing the string results using <.
+template <class T, class Method>
+class StringMethodComparator : public std::binary_function<const string16&,
+ const string16&,
+ bool> {
+ public:
+ explicit StringMethodComparator(Method method) : method_(method) { }
+
+ // Returns true if lhs preceeds rhs.
+ bool operator() (T* lhs_t, T* rhs_t) {
+ return (lhs_t->*method_)() < (rhs_t->*method_)();
+ }
+
+ private:
+ Method method_;
+};
+
+// Sorts the objects in |elements| using the method |method|, which must return
+// a string. Sorting is done using a collator, unless a collator can not be
+// found in which case the strings are sorted using the operator <.
+template <class T, class Method>
+void SortStringsUsingMethod(const std::string& locale,
+ std::vector<T*>* elements,
+ Method method) {
+ UErrorCode error = U_ZERO_ERROR;
+ icu::Locale loc(locale.c_str());
+ scoped_ptr<icu::Collator> collator(icu::Collator::createInstance(loc, error));
+ if (U_FAILURE(error)) {
+ sort(elements->begin(), elements->end(),
+ StringMethodComparator<T, Method>(method));
+ return;
+ }
+
+ std::sort(elements->begin(), elements->end(),
+ StringMethodComparatorWithCollator<T, Method>(collator.get(), method));
+}
+
+// Compares two elements' string keys and returns true if the first element's
+// string key is less than the second element's string key. The Element must
+// have a method like the follow format to return the string key.
+// const string16& GetStringKey() const;
+// This uses the locale specified in the constructor.
+template <class Element>
+class StringComparator : public std::binary_function<const Element&,
+ const Element&,
+ bool> {
+ public:
+ explicit StringComparator(icu::Collator* collator)
+ : collator_(collator) { }
+
+ // Returns true if lhs precedes rhs.
+ bool operator()(const Element& lhs, const Element& rhs) {
+ const string16& lhs_string_key = lhs.GetStringKey();
+ const string16& rhs_string_key = rhs.GetStringKey();
+
+ return StringComparator<string16>(collator_)(lhs_string_key,
+ rhs_string_key);
+ }
+
+ private:
+ icu::Collator* collator_;
+};
+
+// Specialization of operator() method for string16 version.
+template <>
+bool StringComparator<string16>::operator()(const string16& lhs,
+ const string16& rhs);
+
+// In place sorting of |elements| of a vector according to the string key of
+// each element in the vector by using collation rules for |locale|.
+// |begin_index| points to the start position of elements in the vector which
+// want to be sorted. |end_index| points to the end position of elements in the
+// vector which want to be sorted
+template <class Element>
+void SortVectorWithStringKey(const std::string& locale,
+ std::vector<Element>* elements,
+ unsigned int begin_index,
+ unsigned int end_index,
+ bool needs_stable_sort) {
+ DCHECK(begin_index < end_index &&
+ end_index <= static_cast<unsigned int>(elements->size()));
+ UErrorCode error = U_ZERO_ERROR;
+ icu::Locale loc(locale.c_str());
+ scoped_ptr<icu::Collator> collator(icu::Collator::createInstance(loc, error));
+ if (U_FAILURE(error))
+ collator.reset();
+ StringComparator<Element> c(collator.get());
+ if (needs_stable_sort) {
+ stable_sort(elements->begin() + begin_index,
+ elements->begin() + end_index,
+ c);
+ } else {
+ sort(elements->begin() + begin_index, elements->begin() + end_index, c);
+ }
+}
+
+template <class Element>
+void SortVectorWithStringKey(const std::string& locale,
+ std::vector<Element>* elements,
+ bool needs_stable_sort) {
+ SortVectorWithStringKey<Element>(locale, elements, 0, elements->size(),
+ needs_stable_sort);
+}
+
+} // namespace l10n_util
+
+#endif // UI_BASE_L10N_L10N_UTIL_COLLATOR_H_
diff --git a/ui/base/l10n/l10n_util_mac.h b/ui/base/l10n/l10n_util_mac.h
new file mode 100644
index 0000000..73eaee6
--- /dev/null
+++ b/ui/base/l10n/l10n_util_mac.h
@@ -0,0 +1,82 @@
+// Copyright (c) 2011 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.
+
+#ifndef UI_BASE_L10N_L10N_UTIL_MAC_H_
+#define UI_BASE_L10N_L10N_UTIL_MAC_H_
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/string16.h"
+
+#ifdef __OBJC__
+@class NSString;
+#else
+class NSString;
+#endif
+
+namespace l10n_util {
+
+// Remove the Windows-style accelerator marker (for labels, menuitems, etc.)
+// and change "..." into an ellipsis.
+// Returns the result in an autoreleased NSString.
+NSString* FixUpWindowsStyleLabel(const string16& label);
+
+// Pulls resource string from the string bundle and returns it.
+NSString* GetNSString(int message_id);
+
+// Get a resource string and replace $1-$2-$3 with |a| and |b|
+// respectively. Additionally, $$ is replaced by $.
+NSString* GetNSStringF(int message_id,
+ const string16& a);
+NSString* GetNSStringF(int message_id,
+ const string16& a,
+ const string16& b);
+NSString* GetNSStringF(int message_id,
+ const string16& a,
+ const string16& b,
+ const string16& c);
+NSString* GetNSStringF(int message_id,
+ const string16& a,
+ const string16& b,
+ const string16& c,
+ const string16& d);
+
+// Variants that return the offset(s) of the replaced parameters. (See
+// app/l10n_util.h for more details.)
+NSString* GetNSStringF(int message_id,
+ const string16& a,
+ const string16& b,
+ std::vector<size_t>* offsets);
+
+// Same as GetNSString, but runs the result through FixUpWindowsStyleLabel
+// before returning it.
+NSString* GetNSStringWithFixup(int message_id);
+
+// Same as GetNSStringF, but runs the result through FixUpWindowsStyleLabel
+// before returning it.
+NSString* GetNSStringFWithFixup(int message_id,
+ const string16& a);
+NSString* GetNSStringFWithFixup(int message_id,
+ const string16& a,
+ const string16& b);
+NSString* GetNSStringFWithFixup(int message_id,
+ const string16& a,
+ const string16& b,
+ const string16& c);
+NSString* GetNSStringFWithFixup(int message_id,
+ const string16& a,
+ const string16& b,
+ const string16& c,
+ const string16& d);
+
+// Support the override of the locale with the value from Cocoa.
+void OverrideLocaleWithCocoaLocale();
+const std::string& GetLocaleOverride();
+
+} // namespace l10n_util
+
+#endif // UI_BASE_L10N_L10N_UTIL_MAC_H_
diff --git a/ui/base/l10n/l10n_util_mac.mm b/ui/base/l10n/l10n_util_mac.mm
new file mode 100644
index 0000000..528c486
--- /dev/null
+++ b/ui/base/l10n/l10n_util_mac.mm
@@ -0,0 +1,167 @@
+// Copyright (c) 2011 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.
+
+#import <Foundation/Foundation.h>
+
+#include "base/sys_string_conversions.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/l10n/l10n_util_mac.h"
+
+namespace {
+
+class OverrideLocaleHolder {
+ public:
+ OverrideLocaleHolder() {}
+ const std::string& value() const { return value_; }
+ void set_value(const std::string override_value) { value_ = override_value; }
+ private:
+ DISALLOW_COPY_AND_ASSIGN(OverrideLocaleHolder);
+ std::string value_;
+};
+
+base::LazyInstance<OverrideLocaleHolder>
+ override_locale_holder(base::LINKER_INITIALIZED);
+
+} // namespace
+
+namespace l10n_util {
+
+const std::string& GetLocaleOverride() {
+ return override_locale_holder.Get().value();
+}
+
+void OverrideLocaleWithCocoaLocale() {
+ // NSBundle really should only be called on the main thread.
+ DCHECK([NSThread isMainThread]);
+
+ // Chrome really only has one concept of locale, but Mac OS X has locale and
+ // language that can be set independently. After talking with Chrome UX folks
+ // (Cole), the best path from an experience point of view is to map the Mac OS
+ // X language into the Chrome locale. This way strings like "Yesterday" and
+ // "Today" are in the same language as raw dates like "March 20, 1999" (Chrome
+ // strings resources vs ICU generated strings). This also makes the Mac acts
+ // like other Chrome platforms.
+ NSArray* languageList = [[NSBundle mainBundle] preferredLocalizations];
+ NSString* firstLocale = [languageList objectAtIndex:0];
+ // Mac OS X uses "_" instead of "-", so swap to get a real locale value.
+ std::string locale_value =
+ [[firstLocale stringByReplacingOccurrencesOfString:@"_"
+ withString:@"-"] UTF8String];
+
+ // On disk the "en-US" resources are just "en" (http://crbug.com/25578), so
+ // the reverse mapping is done here to continue to feed Chrome the same values
+ // in all cases on all platforms. (l10n_util maps en to en-US if it gets
+ // passed this on the command line)
+ if (locale_value == "en")
+ locale_value = "en-US";
+
+ override_locale_holder.Get().set_value(locale_value);
+}
+
+// Remove the Windows-style accelerator marker and change "..." into an
+// ellipsis. Returns the result in an autoreleased NSString.
+NSString* FixUpWindowsStyleLabel(const string16& label) {
+ const char16 kEllipsisUTF16 = 0x2026;
+ string16 ret;
+ size_t label_len = label.length();
+ ret.reserve(label_len);
+ for (size_t i = 0; i < label_len; ++i) {
+ char16 c = label[i];
+ if (c == '&') {
+ if (i + 1 < label_len && label[i + 1] == '&') {
+ ret.push_back(c);
+ ++i;
+ }
+ } else if (c == '.' && i + 2 < label_len && label[i + 1] == '.'
+ && label[i + 2] == '.') {
+ ret.push_back(kEllipsisUTF16);
+ i += 2;
+ } else {
+ ret.push_back(c);
+ }
+ }
+
+ return base::SysUTF16ToNSString(ret);
+}
+
+NSString* GetNSString(int message_id) {
+ return base::SysUTF16ToNSString(l10n_util::GetStringUTF16(message_id));
+}
+
+NSString* GetNSStringF(int message_id,
+ const string16& a) {
+ return base::SysUTF16ToNSString(l10n_util::GetStringFUTF16(message_id,
+ a));
+}
+
+NSString* GetNSStringF(int message_id,
+ const string16& a,
+ const string16& b) {
+ return base::SysUTF16ToNSString(l10n_util::GetStringFUTF16(message_id,
+ a, b));
+}
+
+NSString* GetNSStringF(int message_id,
+ const string16& a,
+ const string16& b,
+ const string16& c) {
+ return base::SysUTF16ToNSString(l10n_util::GetStringFUTF16(message_id,
+ a, b, c));
+}
+
+NSString* GetNSStringF(int message_id,
+ const string16& a,
+ const string16& b,
+ const string16& c,
+ const string16& d) {
+ return base::SysUTF16ToNSString(l10n_util::GetStringFUTF16(message_id,
+ a, b, c, d));
+}
+
+NSString* GetNSStringF(int message_id,
+ const string16& a,
+ const string16& b,
+ std::vector<size_t>* offsets) {
+ return base::SysUTF16ToNSString(l10n_util::GetStringFUTF16(message_id,
+ a, b, offsets));
+}
+
+NSString* GetNSStringWithFixup(int message_id) {
+ return FixUpWindowsStyleLabel(l10n_util::GetStringUTF16(message_id));
+}
+
+NSString* GetNSStringFWithFixup(int message_id,
+ const string16& a) {
+ return FixUpWindowsStyleLabel(l10n_util::GetStringFUTF16(message_id,
+ a));
+}
+
+NSString* GetNSStringFWithFixup(int message_id,
+ const string16& a,
+ const string16& b) {
+ return FixUpWindowsStyleLabel(l10n_util::GetStringFUTF16(message_id,
+ a, b));
+}
+
+NSString* GetNSStringFWithFixup(int message_id,
+ const string16& a,
+ const string16& b,
+ const string16& c) {
+ return FixUpWindowsStyleLabel(l10n_util::GetStringFUTF16(message_id,
+ a, b, c));
+}
+
+NSString* GetNSStringFWithFixup(int message_id,
+ const string16& a,
+ const string16& b,
+ const string16& c,
+ const string16& d) {
+ return FixUpWindowsStyleLabel(l10n_util::GetStringFUTF16(message_id,
+ a, b, c, d));
+}
+
+
+} // namespace l10n_util
diff --git a/ui/base/l10n/l10n_util_mac_unittest.mm b/ui/base/l10n/l10n_util_mac_unittest.mm
new file mode 100644
index 0000000..8084027
--- /dev/null
+++ b/ui/base/l10n/l10n_util_mac_unittest.mm
@@ -0,0 +1,46 @@
+// Copyright (c) 2011 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.
+
+#import <Foundation/Foundation.h>
+
+#include "base/sys_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+#include "ui/base/l10n/l10n_util_mac.h"
+
+typedef PlatformTest L10nUtilMacTest;
+
+TEST_F(L10nUtilMacTest, FixUpWindowsStyleLabel) {
+ struct TestData {
+ NSString* input;
+ NSString* output;
+ };
+
+ TestData data[] = {
+ { @"", @"" },
+ { @"nothing", @"nothing" },
+ { @"foo &bar", @"foo bar" },
+ { @"foo &&bar", @"foo &bar" },
+ { @"foo &&&bar", @"foo &bar" },
+ { @"&foo &&bar", @"foo &bar" },
+ { @"&foo &bar", @"foo bar" },
+ { @"foo bar.", @"foo bar." },
+ { @"foo bar..", @"foo bar.." },
+ { @"foo bar...", @"foo bar\u2026" },
+ { @"foo.bar", @"foo.bar" },
+ { @"foo..bar", @"foo..bar" },
+ { @"foo...bar", @"foo\u2026bar" },
+ { @"foo...bar...", @"foo\u2026bar\u2026" },
+ };
+ for (size_t idx = 0; idx < ARRAYSIZE_UNSAFE(data); ++idx) {
+ string16 input16(base::SysNSStringToUTF16(data[idx].input));
+
+ NSString* result = l10n_util::FixUpWindowsStyleLabel(input16);
+ EXPECT_TRUE(result != nil) << "Fixup Failed, idx = " << idx;
+
+ EXPECT_TRUE([data[idx].output isEqualTo:result])
+ << "For idx " << idx << ", expected '" << [data[idx].output UTF8String]
+ << "', got '" << [result UTF8String] << "'";
+ }
+}
diff --git a/ui/base/l10n/l10n_util_posix.cc b/ui/base/l10n/l10n_util_posix.cc
new file mode 100644
index 0000000..3b4604d
--- /dev/null
+++ b/ui/base/l10n/l10n_util_posix.cc
@@ -0,0 +1,14 @@
+// Copyright (c) 2011 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 <string>
+
+namespace l10n_util {
+
+// Return true blindly for now.
+bool IsLocaleSupportedByOS(const std::string& locale) {
+ return true;
+}
+
+} // namespace l10n_util
diff --git a/ui/base/l10n/l10n_util_unittest.cc b/ui/base/l10n/l10n_util_unittest.cc
new file mode 100644
index 0000000..857c87e1
--- /dev/null
+++ b/ui/base/l10n/l10n_util_unittest.cc
@@ -0,0 +1,399 @@
+// Copyright (c) 2011 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 "build/build_config.h"
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+#include <cstdlib>
+#endif
+
+#include "base/basictypes.h"
+#include "base/environment.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/stl_util-inl.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/l10n/l10n_util_collator.h"
+#include "ui/base/ui_base_paths.h"
+#include "unicode/locid.h"
+
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#endif
+
+#if !defined(OS_MACOSX)
+#include "ui/base/test/data/resource.h"
+#endif
+
+namespace {
+
+class StringWrapper {
+ public:
+ explicit StringWrapper(const string16& string) : string_(string) {}
+ const string16& string() const { return string_; }
+
+ private:
+ string16 string_;
+
+ DISALLOW_COPY_AND_ASSIGN(StringWrapper);
+};
+
+} // namespace
+
+class L10nUtilTest : public PlatformTest {
+};
+
+#if defined(OS_WIN)
+// TODO(beng): disabled until app strings move to app.
+TEST_F(L10nUtilTest, DISABLED_GetString) {
+ std::string s = l10n_util::GetStringUTF8(IDS_SIMPLE);
+ EXPECT_EQ(std::string("Hello World!"), s);
+
+ s = l10n_util::GetStringFUTF8(IDS_PLACEHOLDERS,
+ UTF8ToUTF16("chrome"),
+ UTF8ToUTF16("10"));
+ EXPECT_EQ(std::string("Hello, chrome. Your number is 10."), s);
+
+ string16 s16 = l10n_util::GetStringFUTF16Int(IDS_PLACEHOLDERS_2, 20);
+ EXPECT_EQ(UTF8ToUTF16("You owe me $20."), s16);
+}
+#endif // defined(OS_WIN)
+
+TEST_F(L10nUtilTest, TruncateString) {
+ string16 string = ASCIIToUTF16("foooooey bxxxar baz");
+
+ // Make sure it doesn't modify the string if length > string length.
+ EXPECT_EQ(string, l10n_util::TruncateString(string, 100));
+
+ // Test no characters.
+ EXPECT_EQ(L"", UTF16ToWide(l10n_util::TruncateString(string, 0)));
+
+ // Test 1 character.
+ EXPECT_EQ(L"\x2026", UTF16ToWide(l10n_util::TruncateString(string, 1)));
+
+ // Test adds ... at right spot when there is enough room to break at a
+ // word boundary.
+ EXPECT_EQ(L"foooooey\x2026",
+ UTF16ToWide(l10n_util::TruncateString(string, 14)));
+
+ // Test adds ... at right spot when there is not enough space in first word.
+ EXPECT_EQ(L"f\x2026", UTF16ToWide(l10n_util::TruncateString(string, 2)));
+
+ // Test adds ... at right spot when there is not enough room to break at a
+ // word boundary.
+ EXPECT_EQ(L"foooooey\x2026",
+ UTF16ToWide(l10n_util::TruncateString(string, 11)));
+
+ // Test completely truncates string if break is on initial whitespace.
+ EXPECT_EQ(L"\x2026",
+ UTF16ToWide(l10n_util::TruncateString(ASCIIToUTF16(" "), 2)));
+}
+
+void SetICUDefaultLocale(const std::string& locale_string) {
+ icu::Locale locale(locale_string.c_str());
+ UErrorCode error_code = U_ZERO_ERROR;
+ icu::Locale::setDefault(locale, error_code);
+ EXPECT_TRUE(U_SUCCESS(error_code));
+}
+
+#if !defined(OS_MACOSX)
+// We are disabling this test on MacOS because GetApplicationLocale() as an
+// API isn't something that we'll easily be able to unit test in this manner.
+// The meaning of that API, on the Mac, is "the locale used by Cocoa's main
+// nib file", which clearly can't be stubbed by a test app that doesn't use
+// Cocoa.
+
+void SetDefaultLocaleForTest(const std::string& tag, base::Environment* env) {
+#if defined(OS_POSIX) && !defined(OS_CHROMEOS)
+ env->SetVar("LANGUAGE", tag);
+#else
+ SetICUDefaultLocale(tag);
+#endif
+}
+
+TEST_F(L10nUtilTest, GetAppLocale) {
+ scoped_ptr<base::Environment> env;
+ // Use a temporary locale dir so we don't have to actually build the locale
+ // dlls for this test.
+ FilePath orig_locale_dir;
+ PathService::Get(ui::DIR_LOCALES, &orig_locale_dir);
+ FilePath new_locale_dir;
+ EXPECT_TRUE(file_util::CreateNewTempDirectory(
+ FILE_PATH_LITERAL("l10n_util_test"),
+ &new_locale_dir));
+ PathService::Override(ui::DIR_LOCALES, new_locale_dir);
+ // Make fake locale files.
+ std::string filenames[] = {
+ "en-US",
+ "en-GB",
+ "fr",
+ "es-419",
+ "es",
+ "zh-TW",
+ "zh-CN",
+ "he",
+ "fil",
+ "nb",
+ "am",
+ };
+
+#if defined(OS_WIN)
+ static const char kLocaleFileExtension[] = ".dll";
+#elif defined(OS_POSIX)
+ static const char kLocaleFileExtension[] = ".pak";
+#endif
+ for (size_t i = 0; i < arraysize(filenames); ++i) {
+ FilePath filename = new_locale_dir.AppendASCII(
+ filenames[i] + kLocaleFileExtension);
+ file_util::WriteFile(filename, "", 0);
+ }
+
+ // Keep a copy of ICU's default locale before we overwrite it.
+ icu::Locale locale = icu::Locale::getDefault();
+
+#if defined(OS_POSIX) && !defined(OS_CHROMEOS)
+ env.reset(base::Environment::Create());
+
+ // Test the support of LANGUAGE environment variable.
+ SetICUDefaultLocale("en-US");
+ env->SetVar("LANGUAGE", "xx:fr_CA");
+ EXPECT_EQ("fr", l10n_util::GetApplicationLocale(""));
+
+ env->SetVar("LANGUAGE", "xx:yy:en_gb.utf-8@quot");
+ EXPECT_EQ("en-GB", l10n_util::GetApplicationLocale(""));
+
+ env->SetVar("LANGUAGE", "xx:zh-hk");
+ EXPECT_EQ("zh-TW", l10n_util::GetApplicationLocale(""));
+
+ // 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");
+ env->SetVar("LANGUAGE", "xx:yy");
+ EXPECT_EQ("en-US", l10n_util::GetApplicationLocale(""));
+
+ env->SetVar("LANGUAGE", "/fr:zh_CN");
+ EXPECT_EQ("zh-CN", l10n_util::GetApplicationLocale(""));
+
+ // Test prioritization of the different environment variables.
+ env->SetVar("LANGUAGE", "fr");
+ env->SetVar("LC_ALL", "es");
+ env->SetVar("LC_MESSAGES", "he");
+ env->SetVar("LANG", "nb");
+ EXPECT_EQ("fr", l10n_util::GetApplicationLocale(""));
+ env->UnSetVar("LANGUAGE");
+ EXPECT_EQ("es", l10n_util::GetApplicationLocale(""));
+ env->UnSetVar("LC_ALL");
+ EXPECT_EQ("he", l10n_util::GetApplicationLocale(""));
+ env->UnSetVar("LC_MESSAGES");
+ EXPECT_EQ("nb", l10n_util::GetApplicationLocale(""));
+ env->UnSetVar("LANG");
+#endif // defined(OS_POSIX) && !defined(OS_CHROMEOS)
+
+ SetDefaultLocaleForTest("en-US", env.get());
+ EXPECT_EQ("en-US", l10n_util::GetApplicationLocale(""));
+
+ SetDefaultLocaleForTest("xx", env.get());
+ EXPECT_EQ("en-US", l10n_util::GetApplicationLocale(""));
+
+#if defined(OS_CHROMEOS)
+ // ChromeOS honors preferred locale first in GetApplicationLocale(),
+ // defaulting to en-US, while other targets first honor other signals.
+ SetICUDefaultLocale("en-GB");
+ EXPECT_EQ("en-US", l10n_util::GetApplicationLocale(""));
+
+ SetICUDefaultLocale("en-US");
+ EXPECT_EQ("en-GB", l10n_util::GetApplicationLocale("en-GB"));
+
+#else // defined(OS_CHROMEOS)
+ SetDefaultLocaleForTest("en-GB", env.get());
+ EXPECT_EQ("en-GB", l10n_util::GetApplicationLocale(""));
+
+ SetDefaultLocaleForTest("fr-CA", env.get());
+ EXPECT_EQ("fr", l10n_util::GetApplicationLocale(""));
+
+ SetDefaultLocaleForTest("es-MX", env.get());
+ EXPECT_EQ("es-419", l10n_util::GetApplicationLocale(""));
+
+ SetDefaultLocaleForTest("es-AR", env.get());
+ EXPECT_EQ("es-419", l10n_util::GetApplicationLocale(""));
+
+ SetDefaultLocaleForTest("es-ES", env.get());
+ EXPECT_EQ("es", l10n_util::GetApplicationLocale(""));
+
+ SetDefaultLocaleForTest("es", env.get());
+ EXPECT_EQ("es", l10n_util::GetApplicationLocale(""));
+
+ SetDefaultLocaleForTest("zh-HK", env.get());
+ EXPECT_EQ("zh-TW", l10n_util::GetApplicationLocale(""));
+
+ SetDefaultLocaleForTest("zh-MK", env.get());
+ EXPECT_EQ("zh-TW", l10n_util::GetApplicationLocale(""));
+
+ SetDefaultLocaleForTest("zh-SG", env.get());
+ EXPECT_EQ("zh-CN", l10n_util::GetApplicationLocale(""));
+#endif // defined (OS_CHROMEOS)
+
+#if defined(OS_WIN)
+ // We don't allow user prefs for locale on linux/mac.
+ SetICUDefaultLocale("en-US");
+ EXPECT_EQ("fr", l10n_util::GetApplicationLocale("fr"));
+ EXPECT_EQ("fr", l10n_util::GetApplicationLocale("fr-CA"));
+
+ SetICUDefaultLocale("en-US");
+ // Aliases iw, no, tl to he, nb, fil.
+ EXPECT_EQ("he", l10n_util::GetApplicationLocale("iw"));
+ EXPECT_EQ("nb", l10n_util::GetApplicationLocale("no"));
+ EXPECT_EQ("fil", l10n_util::GetApplicationLocale("tl"));
+ // es-419 and es-XX (where XX is not Spain) should be
+ // mapped to es-419 (Latin American Spanish).
+ EXPECT_EQ("es-419", l10n_util::GetApplicationLocale("es-419"));
+ EXPECT_EQ("es", l10n_util::GetApplicationLocale("es-ES"));
+ EXPECT_EQ("es-419", l10n_util::GetApplicationLocale("es-AR"));
+
+ SetICUDefaultLocale("es-AR");
+ EXPECT_EQ("es", l10n_util::GetApplicationLocale("es"));
+
+ SetICUDefaultLocale("zh-HK");
+ EXPECT_EQ("zh-CN", l10n_util::GetApplicationLocale("zh-CN"));
+
+ SetICUDefaultLocale("he");
+ EXPECT_EQ("en-US", l10n_util::GetApplicationLocale("en"));
+
+ // Amharic should be blocked unless OS is Vista or newer.
+ if (base::win::GetVersion() < base::win::VERSION_VISTA) {
+ SetICUDefaultLocale("am");
+ EXPECT_EQ("en-US", l10n_util::GetApplicationLocale(""));
+ SetICUDefaultLocale("en-GB");
+ EXPECT_EQ("en-GB", l10n_util::GetApplicationLocale("am"));
+ } else {
+ SetICUDefaultLocale("am");
+ EXPECT_EQ("am", l10n_util::GetApplicationLocale(""));
+ SetICUDefaultLocale("en-GB");
+ EXPECT_EQ("am", l10n_util::GetApplicationLocale("am"));
+ }
+#endif // defined(OS_WIN)
+
+ // Clean up.
+ PathService::Override(ui::DIR_LOCALES, orig_locale_dir);
+ file_util::Delete(new_locale_dir, true);
+ UErrorCode error_code = U_ZERO_ERROR;
+ icu::Locale::setDefault(locale, error_code);
+}
+#endif // !defined(OS_MACOSX)
+
+TEST_F(L10nUtilTest, SortStringsUsingFunction) {
+ std::vector<StringWrapper*> strings;
+ strings.push_back(new StringWrapper(UTF8ToUTF16("C")));
+ strings.push_back(new StringWrapper(UTF8ToUTF16("d")));
+ strings.push_back(new StringWrapper(UTF8ToUTF16("b")));
+ strings.push_back(new StringWrapper(UTF8ToUTF16("a")));
+ l10n_util::SortStringsUsingMethod("en-US",
+ &strings,
+ &StringWrapper::string);
+ ASSERT_TRUE(UTF8ToUTF16("a") == strings[0]->string());
+ ASSERT_TRUE(UTF8ToUTF16("b") == strings[1]->string());
+ ASSERT_TRUE(UTF8ToUTF16("C") == strings[2]->string());
+ ASSERT_TRUE(UTF8ToUTF16("d") == strings[3]->string());
+ STLDeleteElements(&strings);
+}
+
+// Test upper and lower case string conversion.
+TEST_F(L10nUtilTest, UpperLower) {
+ string16 mixed(ASCIIToUTF16("Text with UPPer & lowER casE."));
+ const string16 expected_lower(ASCIIToUTF16("text with upper & lower case."));
+ const string16 expected_upper(ASCIIToUTF16("TEXT WITH UPPER & LOWER CASE."));
+
+ string16 result = l10n_util::ToLower(mixed);
+ EXPECT_EQ(result, expected_lower);
+
+ result = l10n_util::ToUpper(mixed);
+ EXPECT_EQ(result, expected_upper);
+}
+
+TEST_F(L10nUtilTest, LocaleDisplayName) {
+ // TODO(jungshik): Make this test more extensive.
+ // Test zh-CN and zh-TW are treated as zh-Hans and zh-Hant.
+ string16 result = l10n_util::GetDisplayNameForLocale("zh-CN", "en", false);
+ EXPECT_EQ(result, ASCIIToUTF16("Chinese (Simplified Han)"));
+
+ result = l10n_util::GetDisplayNameForLocale("zh-TW", "en", false);
+ EXPECT_EQ(result, ASCIIToUTF16("Chinese (Traditional Han)"));
+
+ result = l10n_util::GetDisplayNameForLocale("pt-BR", "en", false);
+ EXPECT_EQ(result, ASCIIToUTF16("Portuguese (Brazil)"));
+
+ result = l10n_util::GetDisplayNameForLocale("es-419", "en", false);
+ EXPECT_EQ(result, ASCIIToUTF16("Spanish (Latin America and the Caribbean)"));
+}
+
+TEST_F(L10nUtilTest, GetParentLocales) {
+ std::vector<std::string> locales;
+ const std::string top_locale("sr_Cyrl_RS");
+ l10n_util::GetParentLocales(top_locale, &locales);
+
+ ASSERT_EQ(3U, locales.size());
+ EXPECT_EQ("sr_Cyrl_RS", locales[0]);
+ EXPECT_EQ("sr_Cyrl", locales[1]);
+ EXPECT_EQ("sr", locales[2]);
+}
+
+TEST_F(L10nUtilTest, IsValidLocaleSyntax) {
+ // Test valid locales.
+ EXPECT_TRUE(l10n_util::IsValidLocaleSyntax("en"));
+ EXPECT_TRUE(l10n_util::IsValidLocaleSyntax("fr"));
+ EXPECT_TRUE(l10n_util::IsValidLocaleSyntax("de"));
+ EXPECT_TRUE(l10n_util::IsValidLocaleSyntax("pt"));
+ EXPECT_TRUE(l10n_util::IsValidLocaleSyntax("zh"));
+ EXPECT_TRUE(l10n_util::IsValidLocaleSyntax("fil"));
+ EXPECT_TRUE(l10n_util::IsValidLocaleSyntax("haw"));
+ EXPECT_TRUE(l10n_util::IsValidLocaleSyntax("en-US"));
+ EXPECT_TRUE(l10n_util::IsValidLocaleSyntax("en_US"));
+ EXPECT_TRUE(l10n_util::IsValidLocaleSyntax("en_GB"));
+ EXPECT_TRUE(l10n_util::IsValidLocaleSyntax("pt-BR"));
+ EXPECT_TRUE(l10n_util::IsValidLocaleSyntax("zh_CN"));
+ EXPECT_TRUE(l10n_util::IsValidLocaleSyntax("zh_Hans"));
+ EXPECT_TRUE(l10n_util::IsValidLocaleSyntax("zh_Hans_CN"));
+ EXPECT_TRUE(l10n_util::IsValidLocaleSyntax("zh_Hant"));
+ EXPECT_TRUE(l10n_util::IsValidLocaleSyntax("zh_Hant_TW"));
+ EXPECT_TRUE(l10n_util::IsValidLocaleSyntax("fr_CA"));
+ EXPECT_TRUE(l10n_util::IsValidLocaleSyntax("i-klingon"));
+ EXPECT_TRUE(l10n_util::IsValidLocaleSyntax("es-419"));
+ EXPECT_TRUE(l10n_util::IsValidLocaleSyntax("en_IE_PREEURO"));
+ EXPECT_TRUE(l10n_util::IsValidLocaleSyntax("en_IE_u_cu_IEP"));
+ EXPECT_TRUE(l10n_util::IsValidLocaleSyntax("en_IE@currency=IEP"));
+ EXPECT_TRUE(l10n_util::IsValidLocaleSyntax("fr@x=y"));
+ EXPECT_TRUE(l10n_util::IsValidLocaleSyntax("zn_CN@foo=bar"));
+ EXPECT_TRUE(l10n_util::IsValidLocaleSyntax(
+ "fr@collation=phonebook;calendar=islamic-civil"));
+ EXPECT_TRUE(l10n_util::IsValidLocaleSyntax(
+ "sr_Latn_RS_REVISED@currency=USD"));
+
+ // Test invalid locales.
+ EXPECT_FALSE(l10n_util::IsValidLocaleSyntax(""));
+ EXPECT_FALSE(l10n_util::IsValidLocaleSyntax("x"));
+ EXPECT_FALSE(l10n_util::IsValidLocaleSyntax("12"));
+ EXPECT_FALSE(l10n_util::IsValidLocaleSyntax("456"));
+ EXPECT_FALSE(l10n_util::IsValidLocaleSyntax("a1"));
+ EXPECT_FALSE(l10n_util::IsValidLocaleSyntax("enUS"));
+ EXPECT_FALSE(l10n_util::IsValidLocaleSyntax("zhcn"));
+ EXPECT_FALSE(l10n_util::IsValidLocaleSyntax("en.US"));
+ EXPECT_FALSE(l10n_util::IsValidLocaleSyntax("en#US"));
+ EXPECT_FALSE(l10n_util::IsValidLocaleSyntax("-en-US"));
+ EXPECT_FALSE(l10n_util::IsValidLocaleSyntax("en-US-"));
+ EXPECT_FALSE(l10n_util::IsValidLocaleSyntax("123-en-US"));
+ EXPECT_FALSE(l10n_util::IsValidLocaleSyntax("Latin"));
+ EXPECT_FALSE(l10n_util::IsValidLocaleSyntax("German"));
+ EXPECT_FALSE(l10n_util::IsValidLocaleSyntax("pt--BR"));
+ EXPECT_FALSE(l10n_util::IsValidLocaleSyntax("sl-macedonia"));
+ EXPECT_FALSE(l10n_util::IsValidLocaleSyntax("@"));
+ EXPECT_FALSE(l10n_util::IsValidLocaleSyntax("en-US@"));
+ EXPECT_FALSE(l10n_util::IsValidLocaleSyntax("en-US@x"));
+ EXPECT_FALSE(l10n_util::IsValidLocaleSyntax("en-US@x="));
+ EXPECT_FALSE(l10n_util::IsValidLocaleSyntax("en-US@=y"));
+}
diff --git a/ui/base/l10n/l10n_util_win.cc b/ui/base/l10n/l10n_util_win.cc
new file mode 100644
index 0000000..65f5d3e
--- /dev/null
+++ b/ui/base/l10n/l10n_util_win.cc
@@ -0,0 +1,185 @@
+// Copyright (c) 2011 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 "ui/base/l10n/l10n_util_win.h"
+
+#include <windowsx.h>
+#include <algorithm>
+#include <iterator>
+
+#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"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace {
+
+void AdjustLogFont(const std::wstring& font_family,
+ double font_size_scaler,
+ LOGFONT* logfont) {
+ DCHECK(font_size_scaler > 0);
+ font_size_scaler = std::max(std::min(font_size_scaler, 2.0), 0.7);
+ logfont->lfHeight = static_cast<long>(font_size_scaler *
+ static_cast<double>(abs(logfont->lfHeight)) + 0.5) *
+ (logfont->lfHeight > 0 ? 1 : -1);
+
+ // TODO(jungshik): We may want to check the existence of the font.
+ // If it's not installed, we shouldn't adjust the font.
+ if (font_family != L"default") {
+ int name_len = std::min(static_cast<int>(font_family.size()),
+ LF_FACESIZE -1);
+ memcpy(logfont->lfFaceName, font_family.data(), name_len * sizeof(WORD));
+ logfont->lfFaceName[name_len] = 0;
+ }
+}
+
+bool IsFontPresent(const wchar_t* font_name) {
+ HFONT hfont = CreateFont(12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ font_name);
+ if (hfont == NULL)
+ return false;
+ HDC dc = GetDC(0);
+ HGDIOBJ oldFont = static_cast<HFONT>(SelectObject(dc, hfont));
+ WCHAR actual_font_name[LF_FACESIZE];
+ DWORD size_ret = GetTextFace(dc, LF_FACESIZE, actual_font_name);
+ actual_font_name[LF_FACESIZE - 1] = 0;
+ SelectObject(dc, oldFont);
+ DeleteObject(hfont);
+ ReleaseDC(0, dc);
+ // We don't have to worry about East Asian fonts with locale-dependent
+ // names here.
+ 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 {
+
+int GetExtendedStyles() {
+ return !base::i18n::IsRTL() ? 0 : WS_EX_LAYOUTRTL | WS_EX_RTLREADING;
+}
+
+int GetExtendedTooltipStyles() {
+ return !base::i18n::IsRTL() ? 0 : WS_EX_LAYOUTRTL;
+}
+
+void HWNDSetRTLLayout(HWND hwnd) {
+ DWORD ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE);
+
+ // We don't have to do anything if the style is already set for the HWND.
+ if (!(ex_style & WS_EX_LAYOUTRTL)) {
+ ex_style |= WS_EX_LAYOUTRTL;
+ ::SetWindowLong(hwnd, GWL_EXSTYLE, ex_style);
+
+ // Right-to-left layout changes are not applied to the window immediately
+ // so we should make sure a WM_PAINT is sent to the window by invalidating
+ // the entire window rect.
+ ::InvalidateRect(hwnd, NULL, true);
+ }
+}
+
+bool IsLocaleSupportedByOS(const std::string& locale) {
+ // Block Amharic on Windows XP unless 'Abyssinica SIL' font is present.
+ // On Win XP, no Ethiopic/Amahric font is availabel out of box. We hard-coded
+ // 'Abyssinica SIL' in the resource bundle to use in the UI. Check
+ // for its presence to determine whether or not to support Amharic UI on XP.
+ return (base::win::GetVersion() >= base::win::VERSION_VISTA ||
+ !LowerCaseEqualsASCII(locale, "am") || IsFontPresent(L"Abyssinica SIL"));
+}
+
+bool NeedOverrideDefaultUIFont(std::wstring* override_font_family,
+ double* font_size_scaler) {
+ // This is rather simple-minded to deal with the UI font size
+ // issue for some Indian locales (ml, bn, hi) for which
+ // the default Windows fonts are too small to be legible. For those
+ // locales, IDS_UI_FONT_FAMILY is set to an actual font family to
+ // use while for other locales, it's set to 'default'.
+
+ // XP and Vista or later have different font size issues and
+ // we need separate ui font specifications.
+ int ui_font_family_id = IDS_UI_FONT_FAMILY;
+ int ui_font_size_scaler_id = IDS_UI_FONT_SIZE_SCALER;
+ if (base::win::GetVersion() < base::win::VERSION_VISTA) {
+ ui_font_family_id = IDS_UI_FONT_FAMILY_XP;
+ ui_font_size_scaler_id = IDS_UI_FONT_SIZE_SCALER_XP;
+ }
+
+ std::wstring ui_font_family = GetStringUTF16(ui_font_family_id);
+ int scaler100;
+ if (!base::StringToInt(l10n_util::GetStringUTF16(ui_font_size_scaler_id),
+ &scaler100))
+ return false;
+
+ // We use the OS default in two cases:
+ // 1) The resource bundle has 'default' and '100' for font family and
+ // font scaler.
+ // 2) The resource bundle is not available for some reason and
+ // ui_font_family is empty.
+ if (ui_font_family == L"default" && scaler100 == 100 ||
+ ui_font_family.empty())
+ return false;
+ if (override_font_family && font_size_scaler) {
+ override_font_family->swap(ui_font_family);
+ *font_size_scaler = scaler100 / 100.0;
+ }
+ return true;
+}
+
+void AdjustUIFont(LOGFONT* logfont) {
+ std::wstring ui_font_family;
+ double ui_font_size_scaler;
+ if (NeedOverrideDefaultUIFont(&ui_font_family, &ui_font_size_scaler))
+ AdjustLogFont(ui_font_family, ui_font_size_scaler, logfont);
+}
+
+void AdjustUIFontForWindow(HWND hwnd) {
+ std::wstring ui_font_family;
+ double ui_font_size_scaler;
+ if (NeedOverrideDefaultUIFont(&ui_font_family, &ui_font_size_scaler)) {
+ LOGFONT logfont;
+ if (GetObject(GetWindowFont(hwnd), sizeof(logfont), &logfont)) {
+ AdjustLogFont(ui_font_family, ui_font_size_scaler, &logfont);
+ HFONT hfont = CreateFontIndirect(&logfont);
+ if (hfont)
+ SetWindowFont(hwnd, hfont, FALSE);
+ }
+ }
+}
+
+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
diff --git a/ui/base/l10n/l10n_util_win.h b/ui/base/l10n/l10n_util_win.h
new file mode 100644
index 0000000..52a4ecc
--- /dev/null
+++ b/ui/base/l10n/l10n_util_win.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2011 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.
+
+#ifndef UI_BASE_L10N_L10N_UTIL_WIN_H_
+#define UI_BASE_L10N_L10N_UTIL_WIN_H_
+#pragma once
+
+#include <windows.h>
+#include <string>
+#include <vector>
+
+namespace l10n_util {
+
+// Returns the locale-dependent extended window styles.
+// This function is used for adding locale-dependent extended window styles
+// (e.g. WS_EX_LAYOUTRTL, WS_EX_RTLREADING, etc.) when creating a window.
+// Callers should OR this value into their extended style value when creating
+// a window.
+int GetExtendedStyles();
+
+// TODO(xji):
+// This is a temporary name, it will eventually replace GetExtendedStyles
+int GetExtendedTooltipStyles();
+
+// Give an HWND, this function sets the WS_EX_LAYOUTRTL extended style for the
+// underlying window. When this style is set, the UI for the window is going to
+// be mirrored. This is generally done for the UI of right-to-left languages
+// such as Hebrew.
+void HWNDSetRTLLayout(HWND hwnd);
+
+// See http://blogs.msdn.com/oldnewthing/archive/2005/09/15/467598.aspx
+// and http://blogs.msdn.com/oldnewthing/archive/2006/06/26/647365.aspx
+// as to why we need these three functions.
+
+// Return true if the default font (we get from Windows) is not suitable
+// to use in the UI of the current UI (e.g. Malayalam, Bengali). If
+// override_font_family and font_size_scaler are not null, they'll be
+// filled with the font family name and the size scaler.
+bool NeedOverrideDefaultUIFont(std::wstring* override_font_family,
+ double* font_size_scaler);
+
+// If the default UI font stored in |logfont| is not suitable, its family
+// and size are replaced with those stored in the per-locale resource.
+void AdjustUIFont(LOGFONT* logfont);
+
+// If the font for a given window (pointed to by HWND) is not suitable for the
+// UI in the current UI langauge, its family and size are replaced with those
+// 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 // UI_BASE_L10N_L10N_UTIL_WIN_H_
diff --git a/ui/base/models/button_menu_item_model.cc b/ui/base/models/button_menu_item_model.cc
index 0ebb11f..9490b9d 100644
--- a/ui/base/models/button_menu_item_model.cc
+++ b/ui/base/models/button_menu_item_model.cc
@@ -4,7 +4,7 @@
#include "ui/base/models/button_menu_item_model.h"
-#include "app/l10n_util.h"
+#include "ui/base/l10n/l10n_util.h"
namespace ui {
diff --git a/ui/base/models/simple_menu_model.cc b/ui/base/models/simple_menu_model.cc
index 7094a70..ba7555b 100644
--- a/ui/base/models/simple_menu_model.cc
+++ b/ui/base/models/simple_menu_model.cc
@@ -4,8 +4,8 @@
#include "ui/base/models/simple_menu_model.h"
-#include "app/l10n_util.h"
#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/base/l10n/l10n_util.h"
namespace ui {
diff --git a/ui/base/models/table_model.cc b/ui/base/models/table_model.cc
index f9d9877..921ca89 100644
--- a/ui/base/models/table_model.cc
+++ b/ui/base/models/table_model.cc
@@ -4,10 +4,10 @@
#include "ui/base/models/table_model.h"
-#include "app/l10n_util.h"
-#include "app/l10n_util_collator.h"
#include "base/logging.h"
#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/l10n/l10n_util_collator.h"
namespace ui {
diff --git a/ui/base/resource/data_pack_unittest.cc b/ui/base/resource/data_pack_unittest.cc
index fb15a07..9205fdf 100644
--- a/ui/base/resource/data_pack_unittest.cc
+++ b/ui/base/resource/data_pack_unittest.cc
@@ -16,7 +16,7 @@ TEST(DataPackTest, Load) {
FilePath data_path;
PathService::Get(base::DIR_SOURCE_ROOT, &data_path);
data_path = data_path.Append(
- FILE_PATH_LITERAL("app/test/data/data_pack_unittest/sample.pak"));
+ FILE_PATH_LITERAL("ui/base/test/data/data_pack_unittest/sample.pak"));
DataPack pack;
ASSERT_TRUE(pack.Load(data_path));
diff --git a/ui/base/resource/resource_bundle_posix.cc b/ui/base/resource/resource_bundle_posix.cc
index 277974d..cce0a9e 100644
--- a/ui/base/resource/resource_bundle_posix.cc
+++ b/ui/base/resource/resource_bundle_posix.cc
@@ -4,13 +4,13 @@
#include "ui/base/resource/resource_bundle.h"
-#include "app/l10n_util.h"
#include "base/logging.h"
#include "base/stl_util-inl.h"
#include "base/string16.h"
#include "base/string_piece.h"
#include "base/synchronization/lock.h"
#include "gfx/font.h"
+#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/data_pack.h"
namespace ui {
diff --git a/ui/base/resource/resource_bundle_win.cc b/ui/base/resource/resource_bundle_win.cc
index 629265a..7c02db1 100644
--- a/ui/base/resource/resource_bundle_win.cc
+++ b/ui/base/resource/resource_bundle_win.cc
@@ -6,7 +6,6 @@
#include <atlbase.h>
-#include "app/l10n_util.h"
#include "base/debug/stack_trace.h"
#include "base/file_util.h"
#include "base/logging.h"
@@ -17,8 +16,9 @@
#include "base/synchronization/lock.h"
#include "base/win/windows_version.h"
#include "gfx/font.h"
-#include "ui/base/ui_base_paths.h"
+#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/data_pack.h"
+#include "ui/base/ui_base_paths.h"
namespace ui {