diff options
author | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-01-21 23:30:17 +0000 |
---|---|---|
committer | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-01-21 23:30:17 +0000 |
commit | c051a1b531d77e7d2eb3afd1dec67e13aab39154 (patch) | |
tree | a6dcc2760d1cdc216a329298052723cb0e25e242 /ui/base | |
parent | 28df88370f358468bffa1e1f6bc43aa25c11e388 (diff) | |
download | chromium_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.cc | 2 | ||||
-rw-r--r-- | ui/base/l10n/l10n_font_util.cc | 2 | ||||
-rw-r--r-- | ui/base/l10n/l10n_util.cc | 876 | ||||
-rw-r--r-- | ui/base/l10n/l10n_util.h | 161 | ||||
-rw-r--r-- | ui/base/l10n/l10n_util_collator.h | 154 | ||||
-rw-r--r-- | ui/base/l10n/l10n_util_mac.h | 82 | ||||
-rw-r--r-- | ui/base/l10n/l10n_util_mac.mm | 167 | ||||
-rw-r--r-- | ui/base/l10n/l10n_util_mac_unittest.mm | 46 | ||||
-rw-r--r-- | ui/base/l10n/l10n_util_posix.cc | 14 | ||||
-rw-r--r-- | ui/base/l10n/l10n_util_unittest.cc | 399 | ||||
-rw-r--r-- | ui/base/l10n/l10n_util_win.cc | 185 | ||||
-rw-r--r-- | ui/base/l10n/l10n_util_win.h | 63 | ||||
-rw-r--r-- | ui/base/models/button_menu_item_model.cc | 2 | ||||
-rw-r--r-- | ui/base/models/simple_menu_model.cc | 2 | ||||
-rw-r--r-- | ui/base/models/table_model.cc | 4 | ||||
-rw-r--r-- | ui/base/resource/data_pack_unittest.cc | 2 | ||||
-rw-r--r-- | ui/base/resource/resource_bundle_posix.cc | 2 | ||||
-rw-r--r-- | ui/base/resource/resource_bundle_win.cc | 4 |
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 { |