summaryrefslogtreecommitdiffstats
path: root/chrome/browser/chromeos/input_method
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/chromeos/input_method')
-rw-r--r--chrome/browser/chromeos/input_method/input_method_util.cc503
-rw-r--r--chrome/browser/chromeos/input_method/input_method_util.h119
-rw-r--r--chrome/browser/chromeos/input_method/input_method_util_unittest.cc204
3 files changed, 826 insertions, 0 deletions
diff --git a/chrome/browser/chromeos/input_method/input_method_util.cc b/chrome/browser/chromeos/input_method/input_method_util.cc
new file mode 100644
index 0000000..9d7cf1e
--- /dev/null
+++ b/chrome/browser/chromeos/input_method/input_method_util.cc
@@ -0,0 +1,503 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/input_method/input_method_util.h"
+
+#include <map>
+#include <utility>
+
+#include "app/l10n_util.h"
+#include "app/l10n_util_collator.h"
+#include "base/basictypes.h"
+#include "base/hash_tables.h"
+#include "base/scoped_ptr.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/browser/chromeos/cros/cros_library.h"
+#include "chrome/browser/chromeos/cros/keyboard_library.h"
+#include "grit/generated_resources.h"
+#include "third_party/icu/public/common/unicode/uloc.h"
+
+namespace {
+
+// Map from language code to associated input method IDs.
+typedef std::multimap<std::string, std::string> LanguageCodeToIdsMap;
+LanguageCodeToIdsMap* g_language_code_to_ids_map = NULL;
+
+const struct EnglishToResouceId {
+ const char* english_string_from_ibus;
+ int resource_id;
+} kEnglishToResourceIdArray[] = {
+ // For ibus-mozc: third_party/ibus-mozc/files/src/unix/ibus/.
+ { "Direct input", IDS_STATUSBAR_IME_JAPANESE_IME_STATUS_DIRECT_INPUT },
+ { "Hiragana", IDS_STATUSBAR_IME_JAPANESE_IME_STATUS_HIRAGANA },
+ { "Katakana", IDS_STATUSBAR_IME_JAPANESE_IME_STATUS_KATAKANA },
+ { "Half width katakana", // small k is not a typo.
+ IDS_STATUSBAR_IME_JAPANESE_IME_STATUS_HALF_WIDTH_KATAKANA },
+ { "Latin", IDS_STATUSBAR_IME_JAPANESE_IME_STATUS_LATIN },
+ { "Wide Latin", IDS_STATUSBAR_IME_JAPANESE_IME_STATUS_WIDE_LATIN },
+
+ // For ibus-hangul: third_party/ibus-hangul/files/po/.
+ { "Enable/Disable Hanja mode", IDS_STATUSBAR_IME_KOREAN_HANJA_MODE },
+
+ // For ibus-pinyin: third_party/ibus-pinyin/files/po/.
+ { "Chinese", IDS_STATUSBAR_IME_CHINESE_PINYIN_TOGGLE_CHINESE_ENGLISH },
+ { "Full/Half width",
+ IDS_STATUSBAR_IME_CHINESE_PINYIN_TOGGLE_FULL_HALF },
+ { "Full/Half width punctuation",
+ IDS_STATUSBAR_IME_CHINESE_PINYIN_TOGGLE_FULL_HALF_PUNCTUATION },
+ { "Simplfied/Traditional Chinese",
+ IDS_STATUSBAR_IME_CHINESE_PINYIN_TOGGLE_S_T_CHINESE },
+
+ // TODO(yusukes): Support ibus-chewing and ibus-table-* if needed.
+
+ // For the "Languages and Input" dialog.
+ { "kbd (m17n)", IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD },
+ { "itrans (m17n)", // also uses the "STANDARD_INPUT_METHOD" id.
+ IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD },
+ { "cangjie (m17n)",
+ IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_CHINESE_CANGJIE_INPUT_METHOD },
+ { "quick (m17n)",
+ IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_CHINESE_QUICK_INPUT_METHOD },
+ { "isiri (m17n)",
+ IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_PERSIAN_ISIRI_2901_INPUT_METHOD },
+ { "kesmanee (m17n)",
+ IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_THAI_KESMANEE_INPUT_METHOD },
+ { "tis820 (m17n)",
+ IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_THAI_TIS820_INPUT_METHOD },
+ { "pattachote (m17n)",
+ IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_THAI_PATTACHOTE_INPUT_METHOD },
+ { "tcvn (m17n)",
+ IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_TCVN_INPUT_METHOD },
+ { "telex (m17n)",
+ IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_TELEX_INPUT_METHOD },
+ { "viqr (m17n)",
+ IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_VIQR_INPUT_METHOD },
+ { "vni (m17n)",
+ IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_VNI_INPUT_METHOD },
+ { "latn-post (m17n)",
+ IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_LATIN_POST_INPUT_METHOD },
+ { "latn-pre (m17n)",
+ IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_LATIN_PRE_INPUT_METHOD },
+ { "Bopomofo", IDS_OPTIONS_SETTINGS_LANGUAGES_BOPOMOFO_INPUT_METHOD },
+ { "Chewing", IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_INPUT_METHOD },
+ { "Pinyin", IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_INPUT_METHOD },
+ { "Mozc (US keyboard layout)",
+ IDS_OPTIONS_SETTINGS_LANGUAGES_JAPANESE_MOZC_US_INPUT_METHOD },
+ { "Mozc (Japanese keyboard layout)",
+ IDS_OPTIONS_SETTINGS_LANGUAGES_JAPANESE_MOZC_JP_INPUT_METHOD },
+ { "Google Japanese Input (US keyboard layout)",
+ IDS_OPTIONS_SETTINGS_LANGUAGES_JAPANESE_GOOGLE_US_INPUT_METHOD },
+ { "Google Japanese Input (Japanese keyboard layout)",
+ IDS_OPTIONS_SETTINGS_LANGUAGES_JAPANESE_GOOGLE_JP_INPUT_METHOD },
+ { "Korean", IDS_OPTIONS_SETTINGS_LANGUAGES_KOREAN_INPUT_METHOD },
+
+ // For ibus-xkb-layouts engine: third_party/ibus-xkb-layouts/files
+ { "Japan", IDS_STATUSBAR_LAYOUT_JAPAN },
+ { "Slovenia", IDS_STATUSBAR_LAYOUT_SLOVENIA },
+ { "Germany", IDS_STATUSBAR_LAYOUT_GERMANY },
+ { "Italy", IDS_STATUSBAR_LAYOUT_ITALY },
+ { "Estonia", IDS_STATUSBAR_LAYOUT_ESTONIA },
+ { "Hungary", IDS_STATUSBAR_LAYOUT_HUNGARY },
+ { "Poland", IDS_STATUSBAR_LAYOUT_POLAND },
+ { "Denmark", IDS_STATUSBAR_LAYOUT_DENMARK },
+ { "Croatia", IDS_STATUSBAR_LAYOUT_CROATIA },
+ { "Brazil", IDS_STATUSBAR_LAYOUT_BRAZIL },
+ { "Serbia", IDS_STATUSBAR_LAYOUT_SERBIA },
+ { "Czechia", IDS_STATUSBAR_LAYOUT_CZECHIA },
+ { "USA - Dvorak", IDS_STATUSBAR_LAYOUT_USA_DVORAK },
+ { "Romania", IDS_STATUSBAR_LAYOUT_ROMANIA },
+ { "USA", IDS_STATUSBAR_LAYOUT_USA },
+ { "Lithuania", IDS_STATUSBAR_LAYOUT_LITHUANIA },
+ { "United Kingdom", IDS_STATUSBAR_LAYOUT_UNITED_KINGDOM },
+ { "Slovakia", IDS_STATUSBAR_LAYOUT_SLOVAKIA },
+ { "Russia", IDS_STATUSBAR_LAYOUT_RUSSIA },
+ { "Greece", IDS_STATUSBAR_LAYOUT_GREECE },
+ { "Belgium", IDS_STATUSBAR_LAYOUT_BELGIUM },
+ { "Bulgaria", IDS_STATUSBAR_LAYOUT_BULGARIA },
+ { "Switzerland", IDS_STATUSBAR_LAYOUT_SWITZERLAND },
+ { "Turkey", IDS_STATUSBAR_LAYOUT_TURKEY },
+ { "Portugal", IDS_STATUSBAR_LAYOUT_PORTUGAL },
+ { "Spain", IDS_STATUSBAR_LAYOUT_SPAIN },
+ { "Finland", IDS_STATUSBAR_LAYOUT_FINLAND },
+ { "Ukraine", IDS_STATUSBAR_LAYOUT_UKRAINE },
+ { "Spain - Catalan variant with middle-dot L",
+ IDS_STATUSBAR_LAYOUT_SPAIN_CATALAN },
+ { "France", IDS_STATUSBAR_LAYOUT_FRANCE },
+ { "Norway", IDS_STATUSBAR_LAYOUT_NORWAY },
+ { "Sweden", IDS_STATUSBAR_LAYOUT_SWEDEN },
+ { "Netherlands", IDS_STATUSBAR_LAYOUT_NETHERLANDS },
+ { "Latvia", IDS_STATUSBAR_LAYOUT_LATVIA },
+};
+const size_t kNumEntries = arraysize(kEnglishToResourceIdArray);
+
+// There are some differences between ISO 639-2 (T) and ISO 639-2 B, and
+// some language codes are not recognized by ICU (i.e. ICU cannot convert
+// these codes to two-letter language codes and display names). Hence we
+// convert these codes to ones that ICU recognize.
+//
+// See http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes for details.
+const char* kIso639VariantMapping[][2] = {
+ {"cze", "ces"},
+ {"ger", "deu"},
+ {"gre", "ell"},
+ // "scr" is not a ISO 639 code. For some reason, evdev.xml uses "scr" as
+ // the language code for Croatian.
+ {"scr", "hrv"},
+ {"rum", "ron"},
+ {"slo", "slk"},
+};
+
+// The list defines pairs of language code and the default input method
+// id. The list is used for reordering input method ids.
+//
+// TODO(satorux): We may need to handle secondary, and ternary input
+// methods, rather than handling the default input method only.
+const struct LanguageDefaultInputMethodId {
+ const char* language_code;
+ const char* input_method_id;
+} kLanguageDefaultInputMethodIds[] = {
+ { "en-US", "xkb:us::eng", }, // US - English
+ { "fr", "xkb:fr::fra", }, // France - French
+ { "de", "xkb:de::ger", }, // Germany - German
+};
+
+// The comparator is used for sorting language codes by their
+// corresponding language names, using the ICU collator.
+struct CompareLanguageCodesByLanguageName
+ : std::binary_function<const std::string&, const std::string&, bool> {
+ explicit CompareLanguageCodesByLanguageName(icu::Collator* collator)
+ : collator_(collator) {
+ }
+
+ // Calling GetLanguageDisplayNameFromCode() in the comparator is not
+ // efficient, but acceptable as the function is cheap, and the language
+ // list is short (about 40 at most).
+ bool operator()(const std::string& s1, const std::string& s2) const {
+ const std::wstring key1 =
+ chromeos::input_method::GetLanguageDisplayNameFromCode(s1);
+ const std::wstring key2 =
+ chromeos::input_method::GetLanguageDisplayNameFromCode(s2);
+ return l10n_util::StringComparator<std::wstring>(collator_)(key1, key2);
+ }
+
+ icu::Collator* collator_;
+};
+
+// The comparator is used for sorting input method ids by their
+// corresponding language names, using the ICU collator.
+struct CompareInputMethodIdsByLanguageName
+ : std::binary_function<const std::string&, const std::string&, bool> {
+ CompareInputMethodIdsByLanguageName(
+ icu::Collator* collator,
+ const std::map<std::string, std::string>& id_to_language_code_map)
+ : comparator_(collator),
+ id_to_language_code_map_(id_to_language_code_map) {
+ }
+
+ bool operator()(const std::string& s1, const std::string& s2) const {
+ std::string language_code_1;
+ std::map<std::string, std::string>::const_iterator iter =
+ id_to_language_code_map_.find(s1);
+ if (iter != id_to_language_code_map_.end()) {
+ language_code_1 = iter->second;
+ }
+ std::string language_code_2;
+ iter = id_to_language_code_map_.find(s2);
+ if (iter != id_to_language_code_map_.end()) {
+ language_code_2 = iter->second;
+ }
+ return comparator_(language_code_1, language_code_2);
+ }
+
+ const CompareLanguageCodesByLanguageName comparator_;
+ const std::map<std::string, std::string>& id_to_language_code_map_;
+};
+
+bool GetLocalizedString(
+ const std::string& english_string, string16 *out_string) {
+ DCHECK(out_string);
+ typedef base::hash_map<std::string, int> HashType;
+ static HashType* english_to_resource_id = NULL;
+
+ // Initialize the map if needed.
+ if (!english_to_resource_id) {
+ // We don't free this map.
+ english_to_resource_id = new HashType(kNumEntries);
+ for (size_t i = 0; i < kNumEntries; ++i) {
+ const bool result = english_to_resource_id->insert(
+ std::make_pair(kEnglishToResourceIdArray[i].english_string_from_ibus,
+ kEnglishToResourceIdArray[i].resource_id)).second;
+ DCHECK(result) << "Duplicated string is found: "
+ << kEnglishToResourceIdArray[i].english_string_from_ibus;
+ }
+ }
+
+ HashType::const_iterator iter = english_to_resource_id->find(english_string);
+ if (iter == english_to_resource_id->end()) {
+ LOG(ERROR) << "Resouce ID is not found for: " << english_string;
+ return false;
+ }
+
+ *out_string = l10n_util::GetStringUTF16(iter->second);
+ return true;
+};
+
+// Initializes |g_language_code_to_ids_map| if necessary.
+// Returns true on success. If this function returns true, it is guaranteed
+// |g_language_code_to_ids_map| is non-NULL. The function might return false
+// when ibus-daemon is not ready.
+bool InitializeLanguageCodeToIdsMap() {
+ if (g_language_code_to_ids_map) {
+ return true;
+ }
+
+ chromeos::InputMethodLibrary* library =
+ chromeos::CrosLibrary::Get()->GetInputMethodLibrary();
+ scoped_ptr<chromeos::InputMethodDescriptors> supported_input_methods(
+ library->GetSupportedInputMethods());
+ if (supported_input_methods->size() <= 1) {
+ // TODO(yusukes): Handle this error in nicer way.
+ LOG(ERROR) << "GetSupportedInputMethods returned a fallback ID";
+ return false;
+ }
+
+ g_language_code_to_ids_map = new LanguageCodeToIdsMap;
+ for (size_t i = 0; i < supported_input_methods->size(); ++i) {
+ const std::string language_code =
+ chromeos::input_method::GetLanguageCodeFromDescriptor(
+ supported_input_methods->at(i));
+ const std::string& input_method_id = supported_input_methods->at(i).id;
+ g_language_code_to_ids_map->insert(
+ std::make_pair(language_code, input_method_id));
+ }
+ // Go through the languages listed in kExtraLanguages.
+ using chromeos::input_method::kExtraLanguages;
+ for (size_t i = 0; i < arraysize(kExtraLanguages); ++i) {
+ const char* language_code = kExtraLanguages[i].language_code;
+ const char* input_method_id = kExtraLanguages[i].input_method_id;
+ g_language_code_to_ids_map->insert(
+ std::make_pair(language_code, input_method_id));
+ }
+ return true;
+}
+
+} // namespace
+
+namespace chromeos {
+namespace input_method {
+
+std::wstring GetString(const std::string& english_string) {
+ string16 localized_string;
+ if (GetLocalizedString(english_string, &localized_string)) {
+ return UTF16ToWide(localized_string);
+ }
+ return UTF8ToWide(english_string);
+}
+
+std::string GetStringUTF8(const std::string& english_string) {
+ string16 localized_string;
+ if (GetLocalizedString(english_string, &localized_string)) {
+ return UTF16ToUTF8(localized_string);
+ }
+ return english_string;
+}
+
+string16 GetStringUTF16(const std::string& english_string) {
+ string16 localized_string;
+ if (GetLocalizedString(english_string, &localized_string)) {
+ return localized_string;
+ }
+ return UTF8ToUTF16(english_string);
+}
+
+bool StringIsSupported(const std::string& english_string) {
+ string16 localized_string;
+ return GetLocalizedString(english_string, &localized_string);
+}
+
+std::string NormalizeLanguageCode(
+ const std::string& language_code) {
+ // Some ibus engines return locale codes like "zh_CN" as language codes.
+ // Normalize these to like "zh-CN".
+ if (language_code.size() >= 5 && language_code[2] == '_') {
+ std::string copied_language_code = language_code;
+ copied_language_code[2] = '-';
+ // Downcase the language code part.
+ for (size_t i = 0; i < 2; ++i) {
+ copied_language_code[i] = ToLowerASCII(copied_language_code[i]);
+ }
+ // Upcase the country code part.
+ for (size_t i = 3; i < copied_language_code.size(); ++i) {
+ copied_language_code[i] = ToUpperASCII(copied_language_code[i]);
+ }
+ return copied_language_code;
+ }
+ // We only handle three-letter codes from here.
+ if (language_code.size() != 3) {
+ return language_code;
+ }
+
+ // Convert special language codes. See comments at kIso639VariantMapping.
+ std::string copied_language_code = language_code;
+ for (size_t i = 0; i < arraysize(kIso639VariantMapping); ++i) {
+ if (language_code == kIso639VariantMapping[i][0]) {
+ copied_language_code = kIso639VariantMapping[i][1];
+ }
+ }
+ // Convert the three-letter code to two letter-code.
+ UErrorCode error = U_ZERO_ERROR;
+ char two_letter_code[ULOC_LANG_CAPACITY];
+ uloc_getLanguage(copied_language_code.c_str(),
+ two_letter_code, sizeof(two_letter_code), &error);
+ if (U_FAILURE(error)) {
+ return language_code;
+ }
+ return two_letter_code;
+}
+
+bool IsKeyboardLayout(const std::string& input_method_id) {
+ const bool kCaseInsensitive = false;
+ return StartsWithASCII(input_method_id, "xkb:", kCaseInsensitive);
+}
+
+std::string GetLanguageCodeFromDescriptor(
+ const InputMethodDescriptor& descriptor) {
+ // Handle some Chinese input methods as zh-CN/zh-TW, rather than zh.
+ // TODO: we should fix this issue in engines rather than here.
+ if (descriptor.language_code == "zh") {
+ if (descriptor.id == "pinyin") {
+ return "zh-CN";
+ } else if (descriptor.id == "bopomofo" ||
+ descriptor.id == "chewing" ||
+ descriptor.id == "m17n:zh:cangjie" ||
+ descriptor.id == "m17n:zh:quick") {
+ return "zh-TW";
+ }
+ }
+
+ std::string language_code = NormalizeLanguageCode(descriptor.language_code);
+
+ // Add country codes to language codes of some XKB input methods to make
+ // these compatible with Chrome's application locale codes like "en-US".
+ // TODO(satorux): Maybe we need to handle "es" for "es-419".
+ // TODO: We should not rely on the format of the engine name. Should we add
+ // |country_code| in InputMethodDescriptor?
+ if (IsKeyboardLayout(descriptor.id) &&
+ (language_code == "en" ||
+ language_code == "zh" ||
+ language_code == "pt")) {
+ std::vector<std::string> portions;
+ SplitString(descriptor.id, ':', &portions);
+ if (portions.size() >= 2 && !portions[1].empty()) {
+ language_code.append("-");
+ language_code.append(StringToUpperASCII(portions[1]));
+ }
+ }
+ return language_code;
+}
+
+std::wstring MaybeRewriteLanguageName(const std::wstring& language_name) {
+ // "t" is used as the language code for input methods that don't fall
+ // under any other languages.
+ if (language_name == L"t") {
+ return l10n_util::GetString(
+ IDS_OPTIONS_SETTINGS_LANGUAGES_OTHERS);
+ }
+ return language_name;
+}
+
+std::wstring GetLanguageDisplayNameFromCode(const std::string& language_code) {
+ if (!g_browser_process) {
+ return L"";
+ }
+ return MaybeRewriteLanguageName(UTF16ToWide(
+ l10n_util::GetDisplayNameForLocale(
+ language_code, g_browser_process->GetApplicationLocale(),
+ true)));
+}
+
+void SortLanguageCodesByNames(std::vector<std::string>* language_codes) {
+ if (!g_browser_process) {
+ return;
+ }
+ // We should build collator outside of the comparator. We cannot have
+ // scoped_ptr<> in the comparator for a subtle STL reason.
+ UErrorCode error = U_ZERO_ERROR;
+ icu::Locale locale(g_browser_process->GetApplicationLocale().c_str());
+ scoped_ptr<icu::Collator> collator(
+ icu::Collator::createInstance(locale, error));
+ if (U_FAILURE(error)) {
+ collator.reset();
+ }
+ std::sort(language_codes->begin(), language_codes->end(),
+ CompareLanguageCodesByLanguageName(collator.get()));
+}
+
+void SortInputMethodIdsByNames(
+ const std::map<std::string, std::string>& id_to_language_code_map,
+ std::vector<std::string>* input_method_ids) {
+ if (!g_browser_process) {
+ return;
+ }
+ UErrorCode error = U_ZERO_ERROR;
+ icu::Locale locale(g_browser_process->GetApplicationLocale().c_str());
+ scoped_ptr<icu::Collator> collator(
+ icu::Collator::createInstance(locale, error));
+ if (U_FAILURE(error)) {
+ collator.reset();
+ }
+ std::stable_sort(input_method_ids->begin(), input_method_ids->end(),
+ CompareInputMethodIdsByLanguageName(
+ collator.get(), id_to_language_code_map));
+}
+
+void ReorderInputMethodIdsForLanguageCode(
+ const std::string& language_code,
+ std::vector<std::string>* input_method_ids) {
+ for (size_t i = 0; i < arraysize(kLanguageDefaultInputMethodIds); ++i) {
+ if (language_code == kLanguageDefaultInputMethodIds[i].language_code) {
+ std::vector<std::string>::iterator iter =
+ std::find(input_method_ids->begin(), input_method_ids->end(),
+ kLanguageDefaultInputMethodIds[i].input_method_id);
+ // If it's not on the top of |input_method_id|, swap it with the top one.
+ if (iter != input_method_ids->end() &&
+ iter != input_method_ids->begin()) {
+ std::swap(*input_method_ids->begin(), *iter);
+ }
+ break; // Don't have to check other language codes.
+ }
+ }
+}
+
+bool GetInputMethodIdsByLanguageCode(
+ const std::string& normalized_language_code,
+ bool keyboard_layout_only,
+ std::vector<std::string>* out_input_method_ids) {
+ DCHECK(out_input_method_ids);
+ out_input_method_ids->clear();
+
+ bool result = false;
+ if (InitializeLanguageCodeToIdsMap()) {
+ std::pair<LanguageCodeToIdsMap::const_iterator,
+ LanguageCodeToIdsMap::const_iterator> range =
+ g_language_code_to_ids_map->equal_range(normalized_language_code);
+ for (LanguageCodeToIdsMap::const_iterator iter = range.first;
+ iter != range.second; ++iter) {
+ const std::string& input_method_id = iter->second;
+ if ((!keyboard_layout_only) || IsKeyboardLayout(input_method_id)) {
+ out_input_method_ids->push_back(input_method_id);
+ result = true;
+ }
+ }
+ if (!result) {
+ LOG(ERROR) << "Unknown language code: " << normalized_language_code;
+ }
+ }
+ return result;
+}
+
+} // namespace input_method
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/input_method/input_method_util.h b/chrome/browser/chromeos/input_method/input_method_util.h
new file mode 100644
index 0000000..77ebbd3
--- /dev/null
+++ b/chrome/browser/chromeos/input_method/input_method_util.h
@@ -0,0 +1,119 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_INPUT_METHOD_INPUT_METHOD_UTIL_H_
+#define CHROME_BROWSER_CHROMEOS_INPUT_METHOD_INPUT_METHOD_UTIL_H_
+
+#include <string>
+#include <vector>
+
+#include "base/string16.h"
+#include "chrome/browser/chromeos/cros/language_library.h"
+
+namespace chromeos {
+namespace input_method {
+
+// The list of language that do not have associated input methods. For
+// these languages, we associate input methods here.
+const struct ExtraLanguage {
+ const char* language_code;
+ const char* input_method_id;
+} kExtraLanguages[] = {
+ { "id", "xkb:us::eng" }, // For Indonesian, use US keyboard layout.
+ // The code "fil" comes from app/l10_util.cc.
+ { "fil", "xkb:us::eng" }, // For Filipino, use US keyboard layout.
+ // The code "es-419" comes from app/l10_util.cc.
+ // For Spanish in Latin America, use Spanish keyboard layout.
+ { "es-419", "xkb:es::spa" },
+};
+// TODO(yusukes): Move |kExtraLanguages| to input_method_util.cc.
+
+// Converts a string sent from IBus IME engines, which is written in English,
+// into Chrome's string ID, then pulls internationalized resource string from
+// the resource bundle and returns it. These functions are not thread-safe.
+// Non-UI threads are not allowed to call them.
+std::wstring GetString(const std::string& english_string);
+std::string GetStringUTF8(const std::string& english_string);
+string16 GetStringUTF16(const std::string& english_string);
+
+// This method is ONLY for unit testing. Returns true if the given string is
+// supported (i.e. the string is associated with a resource ID).
+bool StringIsSupported(const std::string& english_string);
+
+// Normalizes the language code and returns the normalized version. The
+// function normalizes the given language code to be compatible with the
+// one used in Chrome's application locales. Otherwise, returns the
+// given language code as-is.
+//
+// Examples:
+//
+// - "zh_CN" => "zh-CN" (Use - instead of _)
+// - "jpn" => "ja" (Use two-letter code)
+// - "t" => "t" (Return as-is if unknown)
+std::string NormalizeLanguageCode(const std::string& language_code);
+
+// Returns true if the given input method id is for a keyboard layout.
+bool IsKeyboardLayout(const std::string& input_method_id);
+
+// Gets the language code from the given input method descriptor. This
+// encapsulates differences between the language codes used in
+// InputMethodDescriptor and Chrome's application locale codes.
+std::string GetLanguageCodeFromDescriptor(
+ const InputMethodDescriptor& descriptor);
+
+// Gets the keyboard layout name from the given input method ID.
+// If the ID is invalid, the default layout name will be returned.
+//
+// Examples:
+//
+// "xkb:us::eng" => "us"
+// "xkb:us:dvorak:eng" => "us(dvorak)"
+std::string GetKeyboardLayoutName(const std::string& input_method_id);
+
+// Rewrites the language name and returns the modified version if
+// necessary. Otherwise, returns the given language name as is.
+// In particular, this rewrites the special language name used for input
+// methods that don't fall under any other languages.
+std::wstring MaybeRewriteLanguageName(const std::wstring& language_name);
+
+// Converts a language code to a language display name, using the
+// current application locale. MaybeRewriteLanguageName() is called
+// internally.
+// Examples: "fr" => "French"
+// "en-US" => "English (United States)"
+std::wstring GetLanguageDisplayNameFromCode(const std::string& language_code);
+
+// Sorts the given language codes by their corresponding language names,
+// using the unicode string comparator. Uses unstable sorting.
+void SortLanguageCodesByNames(std::vector<std::string>* language_codes);
+
+// Sorts the given input method ids by their corresponding language names,
+// using the unicode string comparator. Uses stable sorting.
+void SortInputMethodIdsByNames(
+ const std::map<std::string, std::string>& id_to_language_code_map,
+ std::vector<std::string>* input_method_ids);
+
+// Reorders the given input method ids for the language code. For
+// example, if |language_codes| is "fr" and |input_method_ids| contains
+// ["xkb:be::fra", and "xkb:fr::fra"], the list is reordered to
+// ["xkb:fr::fra", and "xkb:be::fra"], so that French keyboard layout
+// comes before Belgian keyboard layout.
+void ReorderInputMethodIdsForLanguageCode(
+ const std::string& language_code,
+ std::vector<std::string>* input_method_ids);
+
+// Gets input method ids that belong to |language_code|.
+// If |keyboard_layout_only| is true, the function does not return input methods
+// that are not for keybord layout switching. Returns true on success. Note that
+// the function might return false if ibus-daemon is not running, or
+// |language_code| is unknown.
+bool GetInputMethodIdsByLanguageCode(
+ const std::string& language_code,
+ bool keyboard_layout_only,
+ std::vector<std::string>* out_input_method_ids);
+
+} // namespace input_method
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_INPUT_METHOD_INPUT_METHOD_UTIL_H_
diff --git a/chrome/browser/chromeos/input_method/input_method_util_unittest.cc b/chrome/browser/chromeos/input_method/input_method_util_unittest.cc
new file mode 100644
index 0000000..3c59b6a
--- /dev/null
+++ b/chrome/browser/chromeos/input_method/input_method_util_unittest.cc
@@ -0,0 +1,204 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/input_method/input_method_util.h"
+
+#include <string>
+
+#include "app/l10n_util.h"
+#include "base/utf_string_conversions.h"
+#include "grit/generated_resources.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+namespace input_method {
+
+TEST(InputMethodUtilTest, FindLocalizedStringTest) {
+ EXPECT_TRUE(StringIsSupported("Hiragana"));
+ EXPECT_TRUE(StringIsSupported("Latin"));
+ EXPECT_TRUE(StringIsSupported("Direct input"));
+ EXPECT_FALSE(StringIsSupported("####THIS_STRING_IS_NOT_SUPPORTED####"));
+}
+
+TEST(InputMethodUtilTest, NormalizeLanguageCode) {
+ // TODO(yusukes): test all language codes that IBus provides.
+ EXPECT_EQ("ja", NormalizeLanguageCode("ja"));
+ EXPECT_EQ("ja", NormalizeLanguageCode("jpn"));
+ EXPECT_EQ("t", NormalizeLanguageCode("t"));
+ EXPECT_EQ("zh-CN", NormalizeLanguageCode("zh-CN"));
+ EXPECT_EQ("zh-CN", NormalizeLanguageCode("zh_CN"));
+ EXPECT_EQ("en-US", NormalizeLanguageCode("EN_us"));
+ // See app/l10n_util.cc for es-419.
+ EXPECT_EQ("es-419", NormalizeLanguageCode("es_419"));
+
+ // Special three-letter language codes.
+ EXPECT_EQ("cs", NormalizeLanguageCode("cze"));
+ EXPECT_EQ("de", NormalizeLanguageCode("ger"));
+ EXPECT_EQ("el", NormalizeLanguageCode("gre"));
+ EXPECT_EQ("hr", NormalizeLanguageCode("scr"));
+ EXPECT_EQ("ro", NormalizeLanguageCode("rum"));
+ EXPECT_EQ("sk", NormalizeLanguageCode("slo"));
+}
+
+TEST(InputMethodUtilTest, IsKeyboardLayout) {
+ EXPECT_TRUE(IsKeyboardLayout("xkb:us::eng"));
+ EXPECT_FALSE(IsKeyboardLayout("anthy"));
+}
+
+TEST(InputMethodUtilTest, GetLanguageCodeFromDescriptor) {
+ EXPECT_EQ("ja", GetLanguageCodeFromDescriptor(
+ InputMethodDescriptor("anthy", "Anthy", "us", "ja")));
+ EXPECT_EQ("zh-TW", GetLanguageCodeFromDescriptor(
+ InputMethodDescriptor("chewing", "Chewing", "us", "zh")));
+ EXPECT_EQ("zh-TW", GetLanguageCodeFromDescriptor(
+ InputMethodDescriptor("bopomofo", "Bopomofo(Zhuyin)", "us", "zh")));
+ EXPECT_EQ("zh-TW", GetLanguageCodeFromDescriptor(
+ InputMethodDescriptor("m17n:zh:cangjie", "Cangjie", "us", "zh")));
+ EXPECT_EQ("zh-TW", GetLanguageCodeFromDescriptor(
+ InputMethodDescriptor("m17n:zh:quick", "Quick", "us", "zh")));
+ EXPECT_EQ("zh-CN", GetLanguageCodeFromDescriptor(
+ InputMethodDescriptor("pinyin", "Pinyin", "us", "zh")));
+ EXPECT_EQ("en-US", GetLanguageCodeFromDescriptor(
+ InputMethodDescriptor("xkb:us::eng", "USA", "us", "eng")));
+ EXPECT_EQ("en-UK", GetLanguageCodeFromDescriptor(
+ InputMethodDescriptor("xkb:uk::eng", "United Kingdom", "us", "eng")));
+}
+
+TEST(LanguageConfigModelTest, MaybeRewriteLanguageName) {
+ EXPECT_EQ(L"English", MaybeRewriteLanguageName(L"English"));
+ EXPECT_EQ(l10n_util::GetString(IDS_OPTIONS_SETTINGS_LANGUAGES_OTHERS),
+ MaybeRewriteLanguageName(L"t"));
+}
+
+TEST(LanguageConfigModelTest, GetLanguageDisplayNameFromCode) {
+ EXPECT_EQ(L"French", GetLanguageDisplayNameFromCode("fr"));
+ // MaybeRewriteLanguageName() should be applied.
+ EXPECT_EQ(l10n_util::GetString(IDS_OPTIONS_SETTINGS_LANGUAGES_OTHERS),
+ GetLanguageDisplayNameFromCode("t"));
+}
+
+TEST(LanguageConfigModelTest, SortLanguageCodesByNames) {
+ std::vector<std::string> language_codes;
+ // Check if this function can handle an empty list.
+ SortLanguageCodesByNames(&language_codes);
+
+ language_codes.push_back("ja");
+ language_codes.push_back("fr");
+ language_codes.push_back("t");
+ SortLanguageCodesByNames(&language_codes);
+ ASSERT_EQ(3U, language_codes.size());
+ ASSERT_EQ("fr", language_codes[0]); // French
+ ASSERT_EQ("ja", language_codes[1]); // Japanese
+ ASSERT_EQ("t", language_codes[2]); // Others
+
+ // Add a duplicate entry and see if it works.
+ language_codes.push_back("ja");
+ SortLanguageCodesByNames(&language_codes);
+ ASSERT_EQ(4U, language_codes.size());
+ ASSERT_EQ("fr", language_codes[0]); // French
+ ASSERT_EQ("ja", language_codes[1]); // Japanese
+ ASSERT_EQ("ja", language_codes[2]); // Japanese
+ ASSERT_EQ("t", language_codes[3]); // Others
+}
+
+TEST(LanguageConfigModelTest, SortInputMethodIdsByNames) {
+ std::map<std::string, std::string> id_to_language_code_map;
+ id_to_language_code_map.insert(std::make_pair("mozc", "ja"));
+ id_to_language_code_map.insert(std::make_pair("mozc-jp", "ja"));
+ id_to_language_code_map.insert(std::make_pair("xkb:jp::jpn", "ja"));
+ id_to_language_code_map.insert(std::make_pair("xkb:fr::fra", "fr"));
+ id_to_language_code_map.insert(std::make_pair("m17n:latn-pre", "t"));
+
+ std::vector<std::string> input_method_ids;
+ // Check if this function can handle an empty list.
+ SortInputMethodIdsByNames(id_to_language_code_map,
+ &input_method_ids);
+
+ input_method_ids.push_back("mozc"); // Japanese
+ input_method_ids.push_back("xkb:fr::fra"); // French
+ input_method_ids.push_back("m17n:latn-pre"); // Others
+ SortInputMethodIdsByNames(id_to_language_code_map,
+ &input_method_ids);
+ ASSERT_EQ(3U, input_method_ids.size());
+ ASSERT_EQ("xkb:fr::fra", input_method_ids[0]); // French
+ ASSERT_EQ("mozc", input_method_ids[1]); // Japanese
+ ASSERT_EQ("m17n:latn-pre", input_method_ids[2]); // Others
+
+ // Add a duplicate entry and see if it works.
+ // Note that SortInputMethodIdsByNames uses std::stable_sort.
+ input_method_ids.push_back("xkb:jp::jpn"); // also Japanese
+ SortInputMethodIdsByNames(id_to_language_code_map,
+ &input_method_ids);
+ ASSERT_EQ(4U, input_method_ids.size());
+ ASSERT_EQ("xkb:fr::fra", input_method_ids[0]); // French
+ ASSERT_EQ("mozc", input_method_ids[1]); // Japanese
+ ASSERT_EQ("xkb:jp::jpn", input_method_ids[2]); // Japanese
+ ASSERT_EQ("m17n:latn-pre", input_method_ids[3]); // Others
+
+ input_method_ids.push_back("mozc-jp"); // also Japanese
+ SortInputMethodIdsByNames(id_to_language_code_map,
+ &input_method_ids);
+ ASSERT_EQ(5U, input_method_ids.size());
+ ASSERT_EQ("xkb:fr::fra", input_method_ids[0]); // French
+ ASSERT_EQ("mozc", input_method_ids[1]); // Japanese
+ ASSERT_EQ("xkb:jp::jpn", input_method_ids[2]); // Japanese
+ ASSERT_EQ("mozc-jp", input_method_ids[3]); // Japanese
+ ASSERT_EQ("m17n:latn-pre", input_method_ids[4]); // Others
+}
+
+TEST(LanguageConfigModelTest, ReorderInputMethodIdsForLanguageCode_DE) {
+ std::vector<std::string> input_method_ids;
+ input_method_ids.push_back("xkb:ch::ger"); // Switzerland - German
+ input_method_ids.push_back("xkb:de::ger"); // Germany - German
+ ReorderInputMethodIdsForLanguageCode("de", &input_method_ids);
+ // The list should be reordered.
+ ASSERT_EQ(2U, input_method_ids.size());
+ EXPECT_EQ("xkb:de::ger", input_method_ids[0]);
+ EXPECT_EQ("xkb:ch::ger", input_method_ids[1]);
+}
+
+TEST(LanguageConfigModelTest, ReorderInputMethodIdsForLanguageCode_FR) {
+ std::vector<std::string> input_method_ids;
+ input_method_ids.push_back("xkb:be::fra"); // Belgium - French
+ input_method_ids.push_back("xkb:fr::fra"); // France - French
+ ReorderInputMethodIdsForLanguageCode("fr", &input_method_ids);
+ // The list should be reordered.
+ ASSERT_EQ(2U, input_method_ids.size());
+ EXPECT_EQ("xkb:fr::fra", input_method_ids[0]);
+ EXPECT_EQ("xkb:be::fra", input_method_ids[1]);
+}
+
+TEST(LanguageConfigModelTest, ReorderInputMethodIdsForLanguageCode_EN_US) {
+ std::vector<std::string> input_method_ids;
+ input_method_ids.push_back("xkb:us:dvorak:eng"); // US - Dvorak - English
+ input_method_ids.push_back("xkb:us::eng"); // US - English
+ ReorderInputMethodIdsForLanguageCode("en-US", &input_method_ids);
+ // The list should be reordered.
+ ASSERT_EQ(2U, input_method_ids.size());
+ EXPECT_EQ("xkb:us::eng", input_method_ids[0]);
+ EXPECT_EQ("xkb:us:dvorak:eng", input_method_ids[1]);
+}
+
+TEST(LanguageConfigModelTest, ReorderInputMethodIdsForLanguageCode_FI) {
+ std::vector<std::string> input_method_ids;
+ input_method_ids.push_back("xkb:fi::fin"); // Finland - Finnish
+ ReorderInputMethodIdsForLanguageCode("fi", &input_method_ids);
+ // There is no rule for reordering for Finnish.
+ ASSERT_EQ(1U, input_method_ids.size());
+ EXPECT_EQ("xkb:fi::fin", input_method_ids[0]);
+}
+
+TEST(LanguageConfigModelTest, ReorderInputMethodIdsForLanguageCode_Noop) {
+ std::vector<std::string> input_method_ids;
+ input_method_ids.push_back("xkb:fr::fra"); // France - French
+ input_method_ids.push_back("xkb:be::fra"); // Belgium - French
+ // If the list is already sorted, nothing should happen.
+ ReorderInputMethodIdsForLanguageCode("fr", &input_method_ids);
+ ASSERT_EQ(2U, input_method_ids.size());
+ EXPECT_EQ("xkb:fr::fra", input_method_ids[0]);
+ EXPECT_EQ("xkb:be::fra", input_method_ids[1]);
+}
+
+} // namespace input_method
+} // namespace chromeos