Index: chrome/browser/ui/webui/chromeos/login/l10n_util.cc |
diff --git a/chrome/browser/ui/webui/chromeos/login/l10n_util.cc b/chrome/browser/ui/webui/chromeos/login/l10n_util.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..6d1cf7b61a7c1b1cfd5fdb374e9e1909e2b83004 |
--- /dev/null |
+++ b/chrome/browser/ui/webui/chromeos/login/l10n_util.cc |
@@ -0,0 +1,407 @@ |
+// Copyright 2014 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 "chrome/browser/ui/webui/chromeos/login/l10n_util.h" |
+ |
+#include <algorithm> |
+#include <iterator> |
+#include <map> |
+#include <set> |
+#include <utility> |
+ |
+#include "base/basictypes.h" |
+#include "base/i18n/rtl.h" |
+#include "base/logging.h" |
+#include "base/strings/string16.h" |
+#include "base/strings/stringprintf.h" |
+#include "base/strings/utf_string_conversions.h" |
+#include "base/values.h" |
+#include "chrome/browser/browser_process.h" |
+#include "chrome/browser/chromeos/customization_document.h" |
+#include "chrome/browser/chromeos/input_method/input_method_util.h" |
+#include "chromeos/ime/component_extension_ime_manager.h" |
+#include "chromeos/ime/input_method_descriptor.h" |
+#include "chromeos/ime/input_method_manager.h" |
+#include "grit/generated_resources.h" |
+#include "ui/base/l10n/l10n_util.h" |
+ |
+namespace chromeos { |
+ |
+namespace { |
+ |
+base::DictionaryValue* CreateInputMethodsEntry( |
+ const input_method::InputMethodDescriptor& method, |
+ const std::string selected) { |
+ input_method::InputMethodUtil* util = |
+ input_method::InputMethodManager::Get()->GetInputMethodUtil(); |
+ const std::string& ime_id = method.id(); |
+ scoped_ptr<base::DictionaryValue> input_method(new base::DictionaryValue); |
+ input_method->SetString("value", ime_id); |
+ input_method->SetString("title", util->GetInputMethodLongName(method)); |
+ input_method->SetBoolean("selected", ime_id == selected); |
+ return input_method.release(); |
+} |
+ |
+// Returns true if element was inserted. |
+bool InsertString(const std::string& str, std::set<std::string>& to) { |
+ const std::pair<std::set<std::string>::iterator, bool> result = |
+ to.insert(str); |
+ return result.second; |
+} |
+ |
+void AddOptgroupOtherLayouts(base::ListValue* input_methods_list) { |
+ scoped_ptr<base::DictionaryValue> optgroup(new base::DictionaryValue); |
+ optgroup->SetString( |
+ "optionGroupName", |
+ l10n_util::GetStringUTF16(IDS_OOBE_OTHER_KEYBOARD_LAYOUTS)); |
+ input_methods_list->Append(optgroup.release()); |
+} |
+ |
+// TODO(zork): Remove this blacklist when fonts are added to Chrome OS. |
+// see: crbug.com/240586 |
+bool IsBlacklisted(const std::string& language_code) { |
+ return language_code == "si"; // Sinhala |
+} |
+ |
+// Gets the list of languages with |descriptors| based on |base_language_codes|. |
+// The |most_relevant_language_codes| will be first in the list. If |
+// |insert_divider| is true, an entry with its "code" attribute set to |
+// kMostRelevantLanguagesDivider is placed between the most relevant languages |
+// and all others. |
+scoped_ptr<base::ListValue> GetLanguageList( |
+ const input_method::InputMethodDescriptors& descriptors, |
+ const std::vector<std::string>& base_language_codes, |
+ const std::vector<std::string>& most_relevant_language_codes, |
+ bool insert_divider) { |
+ const std::string app_locale = g_browser_process->GetApplicationLocale(); |
+ |
+ std::set<std::string> language_codes; |
+ // Collect the language codes from the supported input methods. |
+ for (size_t i = 0; i < descriptors.size(); ++i) { |
+ const input_method::InputMethodDescriptor& descriptor = descriptors[i]; |
+ const std::vector<std::string>& languages = descriptor.language_codes(); |
+ for (size_t i = 0; i < languages.size(); ++i) |
+ language_codes.insert(languages[i]); |
+ } |
+ |
+ // Language sort order. |
+ std::map<std::string, int /* index */> language_index; |
+ for (size_t i = 0; i < most_relevant_language_codes.size(); ++i) |
+ language_index[most_relevant_language_codes[i]] = i; |
+ |
+ // Map of display name -> {language code, native_display_name}. |
+ // In theory, we should be able to create a map that is sorted by |
+ // display names using ICU comparator, but doing it is hard, thus we'll |
+ // use an auxiliary vector to achieve the same result. |
+ typedef std::pair<std::string, base::string16> LanguagePair; |
+ typedef std::map<base::string16, LanguagePair> LanguageMap; |
+ LanguageMap language_map; |
+ |
+ // The auxiliary vector mentioned above (except the most relevant locales). |
+ std::vector<base::string16> display_names; |
+ |
+ // Separate vector of the most relevant locales. |
+ std::vector<base::string16> most_relevant_locales_display_names( |
+ most_relevant_language_codes.size()); |
+ |
+ size_t most_relevant_locales_count = 0; |
+ |
+ // Build the list of display names, and build the language map. |
+ |
+ // The list of configured locales might have entries not in |
+ // base_language_codes. If there are unsupported language variants, |
+ // but they resolve to backup locale within base_language_codes, also |
+ // add them to the list. |
+ for (std::map<std::string, int>::const_iterator it = language_index.begin(); |
+ it != language_index.end(); ++it) { |
+ const std::string& language_id = it->first; |
+ |
+ const size_t dash_pos = language_id.find_first_of('-'); |
+ |
+ // Ignore non-specific codes. |
+ if (dash_pos == std::string::npos || dash_pos == 0) |
+ continue; |
+ |
+ if (std::find(base_language_codes.begin(), |
+ base_language_codes.end(), |
+ language_id) != base_language_codes.end()) { |
+ // Language is supported. No need to replace |
+ continue; |
+ } |
+ std::string resolved_locale; |
+ if (!l10n_util::CheckAndResolveLocale(language_id, &resolved_locale)) |
+ continue; |
+ |
+ if (std::find(base_language_codes.begin(), |
+ base_language_codes.end(), |
+ resolved_locale) == base_language_codes.end()) { |
+ // Resolved locale is not supported. |
+ continue; |
+ } |
+ |
+ const base::string16 display_name = |
+ l10n_util::GetDisplayNameForLocale(language_id, app_locale, true); |
+ const base::string16 native_display_name = |
+ l10n_util::GetDisplayNameForLocale( |
+ language_id, language_id, true); |
+ |
+ language_map[display_name] = |
+ std::make_pair(language_id, native_display_name); |
+ |
+ most_relevant_locales_display_names[it->second] = display_name; |
+ ++most_relevant_locales_count; |
+ } |
+ |
+ // Translate language codes, generated from input methods. |
+ for (std::set<std::string>::const_iterator it = language_codes.begin(); |
+ it != language_codes.end(); ++it) { |
+ // Exclude the language which is not in |base_langauge_codes| even it has |
+ // input methods. |
+ if (std::find(base_language_codes.begin(), |
+ base_language_codes.end(), |
+ *it) == base_language_codes.end()) { |
+ continue; |
+ } |
+ |
+ const base::string16 display_name = |
+ l10n_util::GetDisplayNameForLocale(*it, app_locale, true); |
+ const base::string16 native_display_name = |
+ l10n_util::GetDisplayNameForLocale(*it, *it, true); |
+ |
+ language_map[display_name] = |
+ std::make_pair(*it, native_display_name); |
+ |
+ const std::map<std::string, int>::const_iterator index_pos = |
+ language_index.find(*it); |
+ if (index_pos != language_index.end()) { |
+ base::string16& stored_display_name = |
+ most_relevant_locales_display_names[index_pos->second]; |
+ if (stored_display_name.empty()) { |
+ stored_display_name = display_name; |
+ ++most_relevant_locales_count; |
+ } |
+ } else { |
+ display_names.push_back(display_name); |
+ } |
+ } |
+ DCHECK_EQ(display_names.size() + most_relevant_locales_count, |
+ language_map.size()); |
+ |
+ // Build the list of display names, and build the language map. |
+ for (size_t i = 0; i < base_language_codes.size(); ++i) { |
+ // Skip this language if it was already added. |
+ if (language_codes.find(base_language_codes[i]) != language_codes.end()) |
+ continue; |
+ |
+ // TODO(zork): Remove this blacklist when fonts are added to Chrome OS. |
+ // see: crbug.com/240586 |
+ if (IsBlacklisted(base_language_codes[i])) |
+ continue; |
+ |
+ base::string16 display_name = |
+ l10n_util::GetDisplayNameForLocale( |
+ base_language_codes[i], app_locale, false); |
+ base::string16 native_display_name = |
+ l10n_util::GetDisplayNameForLocale( |
+ base_language_codes[i], base_language_codes[i], false); |
+ language_map[display_name] = |
+ std::make_pair(base_language_codes[i], native_display_name); |
+ |
+ const std::map<std::string, int>::const_iterator index_pos = |
+ language_index.find(base_language_codes[i]); |
+ if (index_pos != language_index.end()) { |
+ most_relevant_locales_display_names[index_pos->second] = display_name; |
+ ++most_relevant_locales_count; |
+ } else { |
+ display_names.push_back(display_name); |
+ } |
+ } |
+ |
+ // Sort display names using locale specific sorter. |
+ l10n_util::SortStrings16(app_locale, &display_names); |
+ // Concatenate most_relevant_locales_display_names and display_names. |
+ // Insert special divider in between. |
+ std::vector<base::string16> out_display_names; |
+ for (size_t i = 0; i < most_relevant_locales_display_names.size(); ++i) { |
+ if (most_relevant_locales_display_names[i].size() == 0) |
+ continue; |
+ out_display_names.push_back(most_relevant_locales_display_names[i]); |
+ } |
+ |
+ base::string16 divider16; |
+ if (insert_divider) { |
+ divider16 = base::ASCIIToUTF16(kMostRelevantLanguagesDivider); |
+ out_display_names.push_back(divider16); |
+ } |
+ |
+ std::copy(display_names.begin(), |
+ display_names.end(), |
+ std::back_inserter(out_display_names)); |
+ |
+ // Build the language list from the language map. |
+ scoped_ptr<base::ListValue> language_list(new base::ListValue()); |
+ for (size_t i = 0; i < out_display_names.size(); ++i) { |
+ // Sets the directionality of the display language name. |
+ base::string16 display_name(out_display_names[i]); |
+ if (insert_divider && display_name == divider16) { |
+ // Insert divider. |
+ base::DictionaryValue* dictionary = new base::DictionaryValue(); |
+ dictionary->SetString("code", kMostRelevantLanguagesDivider); |
+ language_list->Append(dictionary); |
+ continue; |
+ } |
+ const bool markup_removal = |
+ base::i18n::UnadjustStringForLocaleDirection(&display_name); |
+ DCHECK(markup_removal); |
+ const bool has_rtl_chars = |
+ base::i18n::StringContainsStrongRTLChars(display_name); |
+ const std::string directionality = has_rtl_chars ? "rtl" : "ltr"; |
+ |
+ const LanguagePair& pair = language_map[out_display_names[i]]; |
+ base::DictionaryValue* dictionary = new base::DictionaryValue(); |
+ dictionary->SetString("code", pair.first); |
+ dictionary->SetString("displayName", out_display_names[i]); |
+ dictionary->SetString("textDirection", directionality); |
+ dictionary->SetString("nativeDisplayName", pair.second); |
+ language_list->Append(dictionary); |
+ } |
+ |
+ return language_list.Pass(); |
+} |
+ |
+} // namespace |
+ |
+const char kMostRelevantLanguagesDivider[] = "MOST_RELEVANT_LANGUAGES_DIVIDER"; |
+ |
+scoped_ptr<base::ListValue> GetUILanguageList( |
+ const std::vector<std::string>* most_relevant_language_codes, |
+ const std::string& selected) { |
+ ComponentExtensionIMEManager* manager = |
+ input_method::InputMethodManager::Get()-> |
+ GetComponentExtensionIMEManager(); |
+ input_method::InputMethodDescriptors descriptors; |
+ if (manager->IsInitialized()) |
+ descriptors = manager->GetXkbIMEAsInputMethodDescriptor(); |
+ scoped_ptr<base::ListValue> languages_list(GetLanguageList( |
+ descriptors, |
+ l10n_util::GetAvailableLocales(), |
+ most_relevant_language_codes |
+ ? *most_relevant_language_codes |
+ : StartupCustomizationDocument::GetInstance()->configured_locales(), |
+ true)); |
+ |
+ for (size_t i = 0; i < languages_list->GetSize(); ++i) { |
+ base::DictionaryValue* language_info = NULL; |
+ if (!languages_list->GetDictionary(i, &language_info)) |
+ NOTREACHED(); |
+ |
+ std::string value; |
+ language_info->GetString("code", &value); |
+ std::string display_name; |
+ language_info->GetString("displayName", &display_name); |
+ std::string native_name; |
+ language_info->GetString("nativeDisplayName", &native_name); |
+ |
+ // If it's an option group divider, add field name. |
+ if (value == kMostRelevantLanguagesDivider) { |
+ language_info->SetString( |
+ "optionGroupName", |
+ l10n_util::GetStringUTF16(IDS_OOBE_OTHER_LANGUAGES)); |
+ } |
+ if (display_name != native_name) { |
+ display_name = base::StringPrintf("%s - %s", |
+ display_name.c_str(), |
+ native_name.c_str()); |
+ } |
+ |
+ language_info->SetString("value", value); |
+ language_info->SetString("title", display_name); |
+ if (value == selected) |
+ language_info->SetBoolean("selected", true); |
+ } |
+ return languages_list.Pass(); |
+} |
+ |
+scoped_ptr<base::ListValue> GetAcceptLanguageList() { |
+ // Collect the language codes from the supported accept-languages. |
+ const std::string app_locale = g_browser_process->GetApplicationLocale(); |
+ std::vector<std::string> accept_language_codes; |
+ l10n_util::GetAcceptLanguagesForLocale(app_locale, &accept_language_codes); |
+ return GetLanguageList( |
+ *input_method::InputMethodManager::Get()->GetSupportedInputMethods(), |
+ accept_language_codes, |
+ StartupCustomizationDocument::GetInstance()->configured_locales(), |
+ false); |
+} |
+ |
+scoped_ptr<base::ListValue> GetLoginKeyboardLayouts( |
+ const std::string& locale, |
+ const std::string& selected) { |
+ scoped_ptr<base::ListValue> input_methods_list(new base::ListValue); |
+ input_method::InputMethodManager* manager = |
+ input_method::InputMethodManager::Get(); |
+ input_method::InputMethodUtil* util = manager->GetInputMethodUtil(); |
+ if (!manager->GetComponentExtensionIMEManager()->IsInitialized()) { |
+ input_method::InputMethodDescriptor fallback = |
+ util->GetFallbackInputMethodDescriptor(); |
+ input_methods_list->Append( |
+ CreateInputMethodsEntry(fallback, fallback.id())); |
+ return input_methods_list.Pass(); |
+ } |
+ |
+ const std::vector<std::string>& hardware_login_input_methods = |
+ util->GetHardwareLoginInputMethodIds(); |
+ manager->EnableLoginLayouts(locale, hardware_login_input_methods); |
+ |
+ scoped_ptr<input_method::InputMethodDescriptors> input_methods( |
+ manager->GetActiveInputMethods()); |
+ std::set<std::string> input_methods_added; |
+ |
+ for (std::vector<std::string>::const_iterator i = |
+ hardware_login_input_methods.begin(); |
+ i != hardware_login_input_methods.end(); |
+ ++i) { |
+ const input_method::InputMethodDescriptor* ime = |
+ util->GetInputMethodDescriptorFromId(*i); |
+ // Do not crash in case of misconfiguration. |
+ if (ime) { |
+ input_methods_added.insert(*i); |
+ input_methods_list->Append(CreateInputMethodsEntry(*ime, selected)); |
+ } else { |
+ NOTREACHED(); |
+ } |
+ } |
+ |
+ bool optgroup_added = false; |
+ for (size_t i = 0; i < input_methods->size(); ++i) { |
+ // Makes sure the id is in legacy xkb id format. |
+ const std::string& ime_id = (*input_methods)[i].id(); |
+ if (!InsertString(ime_id, input_methods_added)) |
+ continue; |
+ if (!optgroup_added) { |
+ optgroup_added = true; |
+ AddOptgroupOtherLayouts(input_methods_list.get()); |
+ } |
+ input_methods_list->Append(CreateInputMethodsEntry((*input_methods)[i], |
+ selected)); |
+ } |
+ |
+ // "xkb:us::eng" should always be in the list of available layouts. |
+ const std::string us_keyboard_id = |
+ util->GetFallbackInputMethodDescriptor().id(); |
+ if (input_methods_added.find(us_keyboard_id) == input_methods_added.end()) { |
+ const input_method::InputMethodDescriptor* us_eng_descriptor = |
+ util->GetInputMethodDescriptorFromId(us_keyboard_id); |
+ DCHECK(us_eng_descriptor); |
+ if (!optgroup_added) { |
+ optgroup_added = true; |
+ AddOptgroupOtherLayouts(input_methods_list.get()); |
+ } |
+ input_methods_list->Append(CreateInputMethodsEntry(*us_eng_descriptor, |
+ selected)); |
+ } |
+ return input_methods_list.Pass(); |
+} |
+ |
+} // namespace chromeos |