Home | History | Annotate | Download | only in input_method
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/chromeos/input_method/input_method_util.h"
      6 
      7 #include <algorithm>
      8 #include <functional>
      9 #include <map>
     10 #include <utility>
     11 
     12 #include "unicode/uloc.h"
     13 
     14 #include "base/basictypes.h"
     15 #include "base/hash_tables.h"
     16 #include "base/memory/scoped_ptr.h"
     17 #include "base/memory/singleton.h"
     18 #include "base/string_split.h"
     19 #include "base/string_util.h"
     20 #include "base/utf_string_conversions.h"
     21 #include "chrome/browser/browser_process.h"
     22 #include "chrome/browser/chromeos/cros/cros_library.h"
     23 #include "chrome/browser/chromeos/language_preferences.h"
     24 #include "chrome/browser/prefs/pref_service.h"
     25 #include "chrome/common/pref_names.h"
     26 #include "grit/generated_resources.h"
     27 #include "ui/base/l10n/l10n_util.h"
     28 #include "ui/base/l10n/l10n_util_collator.h"
     29 
     30 namespace {
     31 
     32 // Map from language code to associated input method IDs, etc.
     33 typedef std::multimap<std::string, std::string> LanguageCodeToIdsMap;
     34 // Map from input method ID to associated input method descriptor.
     35 typedef std::map<std::string, chromeos::InputMethodDescriptor>
     36     InputMethodIdToDescriptorMap;
     37 // Map from layout name to associated overlay ID
     38 typedef std::map<std::string, std::string> InputMethodNameToOverlayIdMap;
     39 
     40 struct IdMaps {
     41   scoped_ptr<LanguageCodeToIdsMap> language_code_to_ids;
     42   scoped_ptr<std::map<std::string, std::string> > id_to_language_code;
     43   scoped_ptr<InputMethodIdToDescriptorMap> id_to_descriptor;
     44   scoped_ptr<std::map<std::string, std::string> > name_to_overlay_id;
     45 
     46   // Returns the singleton instance.
     47   static IdMaps* GetInstance() {
     48     return Singleton<IdMaps>::get();
     49   }
     50 
     51   void ReloadMaps() {
     52     chromeos::InputMethodLibrary* library =
     53         chromeos::CrosLibrary::Get()->GetInputMethodLibrary();
     54     scoped_ptr<chromeos::InputMethodDescriptors> supported_input_methods(
     55         library->GetSupportedInputMethods());
     56     if (supported_input_methods->size() <= 1) {
     57       LOG(ERROR) << "GetSupportedInputMethods returned a fallback ID";
     58       // TODO(yusukes): Handle this error in nicer way.
     59     }
     60 
     61     // Clear the existing maps.
     62     language_code_to_ids->clear();
     63     id_to_language_code->clear();
     64     id_to_descriptor->clear();
     65     name_to_overlay_id->clear();
     66 
     67     for (size_t i = 0; i < supported_input_methods->size(); ++i) {
     68       const chromeos::InputMethodDescriptor& input_method =
     69           supported_input_methods->at(i);
     70       const std::string language_code =
     71           chromeos::input_method::GetLanguageCodeFromDescriptor(input_method);
     72       const std::string keyboard_overlay_id =
     73           library->GetKeyboardOverlayId(input_method.id);
     74       language_code_to_ids->insert(
     75           std::make_pair(language_code, input_method.id));
     76       // Remember the pairs.
     77       id_to_language_code->insert(
     78           std::make_pair(input_method.id, language_code));
     79       id_to_descriptor->insert(
     80           std::make_pair(input_method.id, input_method));
     81       name_to_overlay_id->insert(
     82           std::make_pair(input_method.keyboard_layout, keyboard_overlay_id));
     83     }
     84 
     85     // Go through the languages listed in kExtraLanguages.
     86     using chromeos::input_method::kExtraLanguages;
     87     for (size_t i = 0; i < arraysize(kExtraLanguages); ++i) {
     88       const char* language_code = kExtraLanguages[i].language_code;
     89       const char* input_method_id = kExtraLanguages[i].input_method_id;
     90       InputMethodIdToDescriptorMap::const_iterator iter =
     91           id_to_descriptor->find(input_method_id);
     92       // If the associated input method descriptor is found, add the
     93       // language code and the input method.
     94       if (iter != id_to_descriptor->end()) {
     95         const chromeos::InputMethodDescriptor& input_method = iter->second;
     96         const std::string keyboard_overlay_id =
     97             library->GetKeyboardOverlayId(input_method.id);
     98         language_code_to_ids->insert(
     99             std::make_pair(language_code, input_method.id));
    100         name_to_overlay_id->insert(
    101             std::make_pair(input_method.keyboard_layout, keyboard_overlay_id));
    102       }
    103     }
    104   }
    105 
    106  private:
    107   IdMaps() : language_code_to_ids(new LanguageCodeToIdsMap),
    108              id_to_language_code(new std::map<std::string, std::string>),
    109              id_to_descriptor(new InputMethodIdToDescriptorMap),
    110              name_to_overlay_id(new std::map<std::string, std::string>) {
    111     ReloadMaps();
    112   }
    113 
    114   friend struct DefaultSingletonTraits<IdMaps>;
    115 
    116   DISALLOW_COPY_AND_ASSIGN(IdMaps);
    117 };
    118 
    119 const struct EnglishToResouceId {
    120   const char* english_string_from_ibus;
    121   int resource_id;
    122 } kEnglishToResourceIdArray[] = {
    123   // For ibus-mozc.
    124   { "Direct input", IDS_STATUSBAR_IME_JAPANESE_IME_STATUS_DIRECT_INPUT },
    125   { "Hiragana", IDS_STATUSBAR_IME_JAPANESE_IME_STATUS_HIRAGANA },
    126   { "Katakana", IDS_STATUSBAR_IME_JAPANESE_IME_STATUS_KATAKANA },
    127   { "Half width katakana",  // small k is not a typo.
    128     IDS_STATUSBAR_IME_JAPANESE_IME_STATUS_HALF_WIDTH_KATAKANA },
    129   { "Latin", IDS_STATUSBAR_IME_JAPANESE_IME_STATUS_LATIN },
    130   { "Wide Latin", IDS_STATUSBAR_IME_JAPANESE_IME_STATUS_WIDE_LATIN },
    131 
    132   // For ibus-hangul: third_party/ibus-hangul/files/po/.
    133   { "Enable/Disable Hanja mode", IDS_STATUSBAR_IME_KOREAN_HANJA_MODE },
    134 
    135   // For ibus-pinyin.
    136   { "Full/Half width",
    137     IDS_STATUSBAR_IME_CHINESE_PINYIN_TOGGLE_FULL_HALF },
    138   { "Full/Half width punctuation",
    139     IDS_STATUSBAR_IME_CHINESE_PINYIN_TOGGLE_FULL_HALF_PUNCTUATION },
    140   { "Simplfied/Traditional Chinese",
    141     IDS_STATUSBAR_IME_CHINESE_PINYIN_TOGGLE_S_T_CHINESE },
    142 
    143   // For ibus-mozc-chewing.
    144   { "English",
    145     IDS_STATUSBAR_IME_CHINESE_MOZC_CHEWING_ENGLISH_MODE },
    146   { "Full-width English",
    147     IDS_STATUSBAR_IME_CHINESE_MOZC_CHEWING_FULL_WIDTH_ENGLISH_MODE },
    148 
    149   // For the "Languages and Input" dialog.
    150   { "kbd (m17n)", IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD },
    151   { "itrans (m17n)",  // also uses the "STANDARD_INPUT_METHOD" id.
    152     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD },
    153   { "cangjie (m17n)",
    154     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_CHINESE_CANGJIE_INPUT_METHOD },
    155   { "quick (m17n)",
    156     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_CHINESE_QUICK_INPUT_METHOD },
    157   { "isiri (m17n)",
    158     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_PERSIAN_ISIRI_2901_INPUT_METHOD },
    159   { "kesmanee (m17n)",
    160     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_THAI_KESMANEE_INPUT_METHOD },
    161   { "tis820 (m17n)",
    162     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_THAI_TIS820_INPUT_METHOD },
    163   { "pattachote (m17n)",
    164     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_THAI_PATTACHOTE_INPUT_METHOD },
    165   { "tcvn (m17n)",
    166     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_TCVN_INPUT_METHOD },
    167   { "telex (m17n)",
    168     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_TELEX_INPUT_METHOD },
    169   { "viqr (m17n)",
    170     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_VIQR_INPUT_METHOD },
    171   { "vni (m17n)",
    172     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_VNI_INPUT_METHOD },
    173   { "Mozc Chewing (Chewing)",
    174     IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_INPUT_METHOD },
    175   { "Pinyin", IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_INPUT_METHOD },
    176   { "Mozc (US keyboard layout)",
    177     IDS_OPTIONS_SETTINGS_LANGUAGES_JAPANESE_MOZC_US_INPUT_METHOD },
    178   { "Mozc (US Dvorak keyboard layout)",
    179     IDS_OPTIONS_SETTINGS_LANGUAGES_JAPANESE_MOZC_US_DV_INPUT_METHOD },
    180   { "Mozc (Japanese keyboard layout)",
    181     IDS_OPTIONS_SETTINGS_LANGUAGES_JAPANESE_MOZC_JP_INPUT_METHOD },
    182   { "Google Japanese Input (US keyboard layout)",
    183     IDS_OPTIONS_SETTINGS_LANGUAGES_JAPANESE_GOOGLE_US_INPUT_METHOD },
    184   { "Google Japanese Input (US Dvorak keyboard layout)",
    185     IDS_OPTIONS_SETTINGS_LANGUAGES_JAPANESE_GOOGLE_US_DV_INPUT_METHOD },
    186   { "Google Japanese Input (Japanese keyboard layout)",
    187     IDS_OPTIONS_SETTINGS_LANGUAGES_JAPANESE_GOOGLE_JP_INPUT_METHOD },
    188   { "Korean", IDS_OPTIONS_SETTINGS_LANGUAGES_KOREAN_INPUT_METHOD },
    189 
    190   // For ibus-xkb-layouts engine: third_party/ibus-xkb-layouts/files
    191   { "Japan", IDS_STATUSBAR_LAYOUT_JAPAN },
    192   { "Slovenia", IDS_STATUSBAR_LAYOUT_SLOVENIA },
    193   { "Germany", IDS_STATUSBAR_LAYOUT_GERMANY },
    194   { "Germany - Neo 2", IDS_STATUSBAR_LAYOUT_GERMANY_NEO2 },
    195   { "Italy", IDS_STATUSBAR_LAYOUT_ITALY },
    196   { "Estonia", IDS_STATUSBAR_LAYOUT_ESTONIA },
    197   { "Hungary", IDS_STATUSBAR_LAYOUT_HUNGARY },
    198   { "Poland", IDS_STATUSBAR_LAYOUT_POLAND },
    199   { "Denmark", IDS_STATUSBAR_LAYOUT_DENMARK },
    200   { "Croatia", IDS_STATUSBAR_LAYOUT_CROATIA },
    201   { "Brazil", IDS_STATUSBAR_LAYOUT_BRAZIL },
    202   { "Serbia", IDS_STATUSBAR_LAYOUT_SERBIA },
    203   { "Czechia", IDS_STATUSBAR_LAYOUT_CZECHIA },
    204   { "USA - Dvorak", IDS_STATUSBAR_LAYOUT_USA_DVORAK },
    205   { "USA - Colemak", IDS_STATUSBAR_LAYOUT_USA_COLEMAK },
    206   { "Romania", IDS_STATUSBAR_LAYOUT_ROMANIA },
    207   { "USA", IDS_STATUSBAR_LAYOUT_USA },
    208   { "USA - International (AltGr dead keys)",
    209     IDS_STATUSBAR_LAYOUT_USA_EXTENDED },
    210   { "USA - International (with dead keys)",
    211     IDS_STATUSBAR_LAYOUT_USA_INTERNATIONAL },
    212   { "Lithuania", IDS_STATUSBAR_LAYOUT_LITHUANIA },
    213   { "United Kingdom - Extended - Winkeys",
    214     IDS_STATUSBAR_LAYOUT_UNITED_KINGDOM },
    215   { "United Kingdom - Dvorak",
    216     IDS_STATUSBAR_LAYOUT_UNITED_KINGDOM_DVORAK },
    217   { "Slovakia", IDS_STATUSBAR_LAYOUT_SLOVAKIA },
    218   { "Russia", IDS_STATUSBAR_LAYOUT_RUSSIA },
    219   { "Russia - Phonetic", IDS_STATUSBAR_LAYOUT_RUSSIA_PHONETIC },
    220   { "Greece", IDS_STATUSBAR_LAYOUT_GREECE },
    221   { "Belgium", IDS_STATUSBAR_LAYOUT_BELGIUM },
    222   { "Bulgaria", IDS_STATUSBAR_LAYOUT_BULGARIA },
    223   { "Bulgaria - Traditional phonetic", IDS_STATUSBAR_LAYOUT_BULGARIA_PHONETIC },
    224   { "Switzerland", IDS_STATUSBAR_LAYOUT_SWITZERLAND },
    225   { "Switzerland - French", IDS_STATUSBAR_LAYOUT_SWITZERLAND_FRENCH },
    226   { "Turkey", IDS_STATUSBAR_LAYOUT_TURKEY },
    227   { "Portugal", IDS_STATUSBAR_LAYOUT_PORTUGAL },
    228   { "Spain", IDS_STATUSBAR_LAYOUT_SPAIN },
    229   { "Finland", IDS_STATUSBAR_LAYOUT_FINLAND },
    230   { "Ukraine", IDS_STATUSBAR_LAYOUT_UKRAINE },
    231   { "Spain - Catalan variant with middle-dot L",
    232     IDS_STATUSBAR_LAYOUT_SPAIN_CATALAN },
    233   { "France", IDS_STATUSBAR_LAYOUT_FRANCE },
    234   { "Norway", IDS_STATUSBAR_LAYOUT_NORWAY },
    235   { "Sweden", IDS_STATUSBAR_LAYOUT_SWEDEN },
    236   { "Netherlands", IDS_STATUSBAR_LAYOUT_NETHERLANDS },
    237   { "Latin American", IDS_STATUSBAR_LAYOUT_LATIN_AMERICAN },
    238   { "Latvia - Apostrophe (') variant", IDS_STATUSBAR_LAYOUT_LATVIA },
    239   { "Canada", IDS_STATUSBAR_LAYOUT_CANADA },
    240   { "Canada - English", IDS_STATUSBAR_LAYOUT_CANADA_ENGLISH },
    241   { "Israel", IDS_STATUSBAR_LAYOUT_ISRAEL },
    242   { "Korea, Republic of - 101/104 key Compatible",
    243     IDS_STATUSBAR_LAYOUT_KOREA_104 },
    244 };
    245 const size_t kEnglishToResourceIdArraySize =
    246     arraysize(kEnglishToResourceIdArray);
    247 
    248 const struct EnglishAndInputMethodIdToResouceId {
    249   const char* english_string_from_ibus;
    250   const char* input_method_id;
    251   int resource_id;
    252 } kEnglishAndInputMethodIdToResourceIdArray[] = {
    253   { "Chinese", "pinyin",
    254     IDS_STATUSBAR_IME_CHINESE_PINYIN_TOGGLE_CHINESE_ENGLISH },
    255   { "Chinese", "mozc-chewing",
    256     IDS_STATUSBAR_IME_CHINESE_MOZC_CHEWING_CHINESE_MODE },
    257 };
    258 const size_t kEnglishAndInputMethodIdToResourceIdArraySize =
    259     arraysize(kEnglishAndInputMethodIdToResourceIdArray);
    260 
    261 // There are some differences between ISO 639-2 (T) and ISO 639-2 B, and
    262 // some language codes are not recognized by ICU (i.e. ICU cannot convert
    263 // these codes to two-letter language codes and display names). Hence we
    264 // convert these codes to ones that ICU recognize.
    265 //
    266 // See http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes for details.
    267 const char* kIso639VariantMapping[][2] = {
    268   { "cze", "ces" },
    269   { "ger", "deu" },
    270   { "gre", "ell" },
    271   // "scr" is not a ISO 639 code. For some reason, evdev.xml uses "scr" as
    272   // the language code for Croatian.
    273   { "scr", "hrv" },
    274   { "rum", "ron" },
    275   { "slo", "slk" },
    276 };
    277 
    278 // The comparator is used for sorting language codes by their
    279 // corresponding language names, using the ICU collator.
    280 struct CompareLanguageCodesByLanguageName
    281     : std::binary_function<const std::string&, const std::string&, bool> {
    282   explicit CompareLanguageCodesByLanguageName(icu::Collator* collator)
    283       : collator_(collator) {
    284   }
    285 
    286   // Calling GetLanguageDisplayNameFromCode() in the comparator is not
    287   // efficient, but acceptable as the function is cheap, and the language
    288   // list is short (about 40 at most).
    289   bool operator()(const std::string& s1, const std::string& s2) const {
    290     const string16 key1 =
    291         chromeos::input_method::GetLanguageDisplayNameFromCode(s1);
    292     const string16 key2 =
    293         chromeos::input_method::GetLanguageDisplayNameFromCode(s2);
    294     return l10n_util::StringComparator<string16>(collator_)(key1, key2);
    295   }
    296 
    297  private:
    298   icu::Collator* collator_;
    299 };
    300 
    301 bool GetLocalizedString(const std::string& english_string,
    302                         const std::string& input_method_id,
    303                         string16 *out_string) {
    304   DCHECK(out_string);
    305 
    306   // Initialize the primary map if needed.
    307   typedef base::hash_map<std::string, int> HashType;
    308   static HashType* english_to_resource_id = NULL;
    309   if (!english_to_resource_id) {
    310     // We don't free this map.
    311     english_to_resource_id = new HashType(kEnglishToResourceIdArraySize);
    312     for (size_t i = 0; i < kEnglishToResourceIdArraySize; ++i) {
    313       const EnglishToResouceId& map_entry = kEnglishToResourceIdArray[i];
    314       const bool result = english_to_resource_id->insert(std::make_pair(
    315           map_entry.english_string_from_ibus, map_entry.resource_id)).second;
    316       DCHECK(result) << "Duplicated string is found: "
    317                      << map_entry.english_string_from_ibus;
    318     }
    319   }
    320 
    321   // Initialize the secondary map if needed.
    322   typedef std::map<std::pair<std::string, std::string>, int> MapType;
    323   static MapType* english_and_input_method_id_to_resource_id = NULL;
    324   if (!english_and_input_method_id_to_resource_id) {
    325     // We don't free this map.
    326     english_and_input_method_id_to_resource_id = new MapType;
    327     for (size_t i = 0; i < kEnglishAndInputMethodIdToResourceIdArraySize; ++i) {
    328       const EnglishAndInputMethodIdToResouceId& map_entry =
    329           kEnglishAndInputMethodIdToResourceIdArray[i];
    330       const std::pair<std::string, std::string> key = std::make_pair(
    331           map_entry.english_string_from_ibus, map_entry.input_method_id);
    332       const bool result = english_and_input_method_id_to_resource_id->insert(
    333           std::make_pair(key, map_entry.resource_id)).second;
    334       DCHECK(result) << "Duplicated key is found: pair of "
    335                      << map_entry.english_string_from_ibus
    336                      << " and "
    337                      << map_entry.input_method_id;
    338     }
    339   }
    340 
    341   HashType::const_iterator iter = english_to_resource_id->find(english_string);
    342   if (iter == english_to_resource_id->end()) {
    343     // The string is not found in the primary map. Try the secondary map with
    344     // |input_method_id|.
    345     const std::pair<std::string, std::string> key =
    346         std::make_pair(english_string, input_method_id);
    347     MapType::const_iterator iter2 =
    348         english_and_input_method_id_to_resource_id->find(key);
    349     if (iter2 == english_and_input_method_id_to_resource_id->end()) {
    350       // TODO(yusukes): Write Autotest which checks if all display names and all
    351       // property names for supported input methods are listed in the resource
    352       // ID array (crosbug.com/4572).
    353       LOG(ERROR) << "Resource ID is not found for: " << english_string;
    354       return false;
    355     }
    356     *out_string = l10n_util::GetStringUTF16(iter2->second);
    357   } else {
    358     *out_string = l10n_util::GetStringUTF16(iter->second);
    359   }
    360   return true;
    361 };
    362 
    363 }  // namespace
    364 
    365 namespace chromeos {
    366 namespace input_method {
    367 
    368 std::wstring GetString(const std::string& english_string,
    369                        const std::string& input_method_id) {
    370   string16 localized_string;
    371   if (GetLocalizedString(english_string, input_method_id, &localized_string)) {
    372     return UTF16ToWide(localized_string);
    373   }
    374   return UTF8ToWide(english_string);
    375 }
    376 
    377 std::string GetStringUTF8(const std::string& english_string,
    378                           const std::string& input_method_id) {
    379   string16 localized_string;
    380   if (GetLocalizedString(english_string, input_method_id, &localized_string)) {
    381     return UTF16ToUTF8(localized_string);
    382   }
    383   return english_string;
    384 }
    385 
    386 string16 GetStringUTF16(const std::string& english_string,
    387                         const std::string& input_method_id) {
    388   string16 localized_string;
    389   if (GetLocalizedString(english_string, input_method_id, &localized_string)) {
    390     return localized_string;
    391   }
    392   return UTF8ToUTF16(english_string);
    393 }
    394 
    395 bool StringIsSupported(const std::string& english_string,
    396                        const std::string& input_method_id) {
    397   string16 localized_string;
    398   return GetLocalizedString(english_string, input_method_id, &localized_string);
    399 }
    400 
    401 std::string NormalizeLanguageCode(
    402     const std::string& language_code) {
    403   // Some ibus engines return locale codes like "zh_CN" as language codes.
    404   // Normalize these to like "zh-CN".
    405   if (language_code.size() >= 5 && language_code[2] == '_') {
    406     std::string copied_language_code = language_code;
    407     copied_language_code[2] = '-';
    408     // Downcase the language code part.
    409     for (size_t i = 0; i < 2; ++i) {
    410       copied_language_code[i] = base::ToLowerASCII(copied_language_code[i]);
    411     }
    412     // Upcase the country code part.
    413     for (size_t i = 3; i < copied_language_code.size(); ++i) {
    414       copied_language_code[i] = base::ToUpperASCII(copied_language_code[i]);
    415     }
    416     return copied_language_code;
    417   }
    418   // We only handle three-letter codes from here.
    419   if (language_code.size() != 3) {
    420     return language_code;
    421   }
    422 
    423   // Convert special language codes. See comments at kIso639VariantMapping.
    424   std::string copied_language_code = language_code;
    425   for (size_t i = 0; i < arraysize(kIso639VariantMapping); ++i) {
    426     if (language_code == kIso639VariantMapping[i][0]) {
    427       copied_language_code = kIso639VariantMapping[i][1];
    428     }
    429   }
    430   // Convert the three-letter code to two letter-code.
    431   UErrorCode error = U_ZERO_ERROR;
    432   char two_letter_code[ULOC_LANG_CAPACITY];
    433   uloc_getLanguage(copied_language_code.c_str(),
    434                    two_letter_code, sizeof(two_letter_code), &error);
    435   if (U_FAILURE(error)) {
    436     return language_code;
    437   }
    438   return two_letter_code;
    439 }
    440 
    441 bool IsKeyboardLayout(const std::string& input_method_id) {
    442   const bool kCaseInsensitive = false;
    443   return StartsWithASCII(input_method_id, "xkb:", kCaseInsensitive);
    444 }
    445 
    446 std::string GetLanguageCodeFromDescriptor(
    447     const InputMethodDescriptor& descriptor) {
    448   // Handle some Chinese input methods as zh-CN/zh-TW, rather than zh.
    449   // TODO: we should fix this issue in engines rather than here.
    450   if (descriptor.language_code == "zh") {
    451     if (descriptor.id == "pinyin") {
    452       return "zh-CN";
    453     } else if (descriptor.id == "mozc-chewing" ||
    454                descriptor.id == "m17n:zh:cangjie" ||
    455                descriptor.id == "m17n:zh:quick") {
    456       return "zh-TW";
    457     }
    458   }
    459 
    460   std::string language_code = NormalizeLanguageCode(descriptor.language_code);
    461 
    462   // Add country codes to language codes of some XKB input methods to make
    463   // these compatible with Chrome's application locale codes like "en-US".
    464   // TODO(satorux): Maybe we need to handle "es" for "es-419".
    465   // TODO: We should not rely on the format of the engine name. Should we add
    466   //       |country_code| in InputMethodDescriptor?
    467   if (IsKeyboardLayout(descriptor.id) &&
    468       (language_code == "en" ||
    469        language_code == "zh" ||
    470        language_code == "pt")) {
    471     std::vector<std::string> portions;
    472     base::SplitString(descriptor.id, ':', &portions);
    473     if (portions.size() >= 2 && !portions[1].empty()) {
    474       language_code.append("-");
    475       language_code.append(StringToUpperASCII(portions[1]));
    476     }
    477   }
    478   return language_code;
    479 }
    480 
    481 std::string GetLanguageCodeFromInputMethodId(
    482     const std::string& input_method_id) {
    483   // The code should be compatible with one of codes used for UI languages,
    484   // defined in app/l10_util.cc.
    485   const char kDefaultLanguageCode[] = "en-US";
    486   std::map<std::string, std::string>::const_iterator iter
    487       = IdMaps::GetInstance()->id_to_language_code->find(input_method_id);
    488   return (iter == IdMaps::GetInstance()->id_to_language_code->end()) ?
    489       // Returning |kDefaultLanguageCode| here is not for Chrome OS but for
    490       // Ubuntu where the ibus-xkb-layouts engine could be missing.
    491       kDefaultLanguageCode : iter->second;
    492 }
    493 
    494 std::string GetKeyboardLayoutName(const std::string& input_method_id) {
    495   InputMethodIdToDescriptorMap::const_iterator iter
    496       = IdMaps::GetInstance()->id_to_descriptor->find(input_method_id);
    497   return (iter == IdMaps::GetInstance()->id_to_descriptor->end()) ?
    498       "" : iter->second.keyboard_layout;
    499 }
    500 
    501 std::string GetKeyboardOverlayId(const std::string& input_method_name) {
    502   std::map<std::string, std::string>::const_iterator iter
    503       = IdMaps::GetInstance()->name_to_overlay_id->find(input_method_name);
    504   return (iter == IdMaps::GetInstance()->name_to_overlay_id->end()) ?
    505       "" : iter->second;
    506 }
    507 
    508 std::string GetInputMethodDisplayNameFromId(
    509     const std::string& input_method_id) {
    510   InputMethodIdToDescriptorMap::const_iterator iter
    511       = IdMaps::GetInstance()->id_to_descriptor->find(input_method_id);
    512   return (iter == IdMaps::GetInstance()->id_to_descriptor->end()) ?
    513       "" : GetStringUTF8(iter->second.display_name, input_method_id);
    514 }
    515 
    516 const chromeos::InputMethodDescriptor* GetInputMethodDescriptorFromId(
    517     const std::string& input_method_id) {
    518   InputMethodIdToDescriptorMap::const_iterator iter
    519       = IdMaps::GetInstance()->id_to_descriptor->find(input_method_id);
    520   return (iter == IdMaps::GetInstance()->id_to_descriptor->end()) ?
    521       NULL : &(iter->second);
    522 }
    523 
    524 string16 GetLanguageDisplayNameFromCode(const std::string& language_code) {
    525   if (!g_browser_process) {
    526     return string16();
    527   }
    528   return l10n_util::GetDisplayNameForLocale(
    529       language_code, g_browser_process->GetApplicationLocale(), true);
    530 }
    531 
    532 string16 GetLanguageNativeDisplayNameFromCode(
    533     const std::string& language_code) {
    534   return l10n_util::GetDisplayNameForLocale(language_code, language_code, true);
    535 }
    536 
    537 void SortLanguageCodesByNames(std::vector<std::string>* language_codes) {
    538   if (!g_browser_process) {
    539     return;
    540   }
    541   // We should build collator outside of the comparator. We cannot have
    542   // scoped_ptr<> in the comparator for a subtle STL reason.
    543   UErrorCode error = U_ZERO_ERROR;
    544   icu::Locale locale(g_browser_process->GetApplicationLocale().c_str());
    545   scoped_ptr<icu::Collator> collator(
    546       icu::Collator::createInstance(locale, error));
    547   if (U_FAILURE(error)) {
    548     collator.reset();
    549   }
    550   std::sort(language_codes->begin(), language_codes->end(),
    551             CompareLanguageCodesByLanguageName(collator.get()));
    552 }
    553 
    554 bool GetInputMethodIdsFromLanguageCode(
    555     const std::string& normalized_language_code,
    556     InputMethodType type,
    557     std::vector<std::string>* out_input_method_ids) {
    558   return GetInputMethodIdsFromLanguageCodeInternal(
    559       *IdMaps::GetInstance()->language_code_to_ids,
    560       normalized_language_code, type, out_input_method_ids);
    561 }
    562 
    563 bool GetInputMethodIdsFromLanguageCodeInternal(
    564     const std::multimap<std::string, std::string>& language_code_to_ids,
    565     const std::string& normalized_language_code,
    566     InputMethodType type,
    567     std::vector<std::string>* out_input_method_ids) {
    568   DCHECK(out_input_method_ids);
    569   out_input_method_ids->clear();
    570 
    571   bool result = false;
    572   std::pair<LanguageCodeToIdsMap::const_iterator,
    573       LanguageCodeToIdsMap::const_iterator> range =
    574       language_code_to_ids.equal_range(normalized_language_code);
    575   for (LanguageCodeToIdsMap::const_iterator iter = range.first;
    576        iter != range.second; ++iter) {
    577     const std::string& input_method_id = iter->second;
    578     if ((type == kAllInputMethods) || IsKeyboardLayout(input_method_id)) {
    579       out_input_method_ids->push_back(input_method_id);
    580       result = true;
    581     }
    582   }
    583   if ((type == kAllInputMethods) && !result) {
    584     LOG(ERROR) << "Unknown language code: " << normalized_language_code;
    585   }
    586   return result;
    587 }
    588 
    589 void GetFirstLoginInputMethodIds(
    590     const std::string& language_code,
    591     const InputMethodDescriptor& current_input_method,
    592     std::vector<std::string>* out_input_method_ids) {
    593   out_input_method_ids->clear();
    594 
    595   // First, add the current keyboard layout (one used on the login screen).
    596   out_input_method_ids->push_back(current_input_method.id);
    597 
    598   // Second, find the most popular input method associated with the
    599   // current UI language. The input method IDs returned from
    600   // GetInputMethodIdsFromLanguageCode() are sorted by popularity, hence
    601   // our basic strategy is to pick the first one, but it's a bit more
    602   // complicated as shown below.
    603   std::string most_popular_id;
    604   std::vector<std::string> input_method_ids;
    605   // This returns the input methods sorted by popularity.
    606   input_method::GetInputMethodIdsFromLanguageCode(
    607       language_code, input_method::kAllInputMethods, &input_method_ids);
    608   for (size_t i = 0; i < input_method_ids.size(); ++i) {
    609     const std::string& input_method_id = input_method_ids[i];
    610     // Pick the first one.
    611     if (most_popular_id.empty())
    612       most_popular_id = input_method_id;
    613 
    614     // Check if there is one that matches the current keyboard layout, but
    615     // not the current keyboard itself. This is useful if there are
    616     // multiple keyboard layout choices for one input method. For
    617     // instance, Mozc provides three choices: mozc (US keyboard), mozc-jp
    618     // (JP keyboard), mozc-dv (Dvorak).
    619     const InputMethodDescriptor* descriptor =
    620         GetInputMethodDescriptorFromId(input_method_id);
    621     if (descriptor &&
    622         descriptor->id != current_input_method.id &&
    623         descriptor->keyboard_layout == current_input_method.keyboard_layout) {
    624       most_popular_id = input_method_id;
    625       break;
    626     }
    627   }
    628   // Add the most popular input method ID, if it's different from the
    629   // current input method.
    630   if (most_popular_id != current_input_method.id) {
    631     out_input_method_ids->push_back(most_popular_id);
    632   }
    633 }
    634 
    635 void GetLanguageCodesFromInputMethodIds(
    636     const std::vector<std::string>& input_method_ids,
    637     std::vector<std::string>* out_language_codes) {
    638   out_language_codes->clear();
    639 
    640   for (size_t i = 0; i < input_method_ids.size(); ++i) {
    641     const std::string& input_method_id = input_method_ids[i];
    642     const InputMethodDescriptor* input_method =
    643         GetInputMethodDescriptorFromId(input_method_id);
    644     if (!input_method) {
    645       LOG(ERROR) << "Unknown input method ID: " << input_method_ids[i];
    646       continue;
    647     }
    648     const std::string language_code =
    649         GetLanguageCodeFromDescriptor(*input_method);
    650     // Add it if it's not already present.
    651     if (std::count(out_language_codes->begin(), out_language_codes->end(),
    652                    language_code) == 0) {
    653       out_language_codes->push_back(language_code);
    654     }
    655   }
    656 }
    657 
    658 void EnableInputMethods(const std::string& language_code, InputMethodType type,
    659                         const std::string& initial_input_method_id) {
    660   std::vector<std::string> candidates;
    661   // Add input methods associated with the language.
    662   GetInputMethodIdsFromLanguageCode(language_code, type, &candidates);
    663   // Add the hardware keyboard as well. We should always add this so users
    664   // can use the hardware keyboard on the login screen and the screen locker.
    665   candidates.push_back(GetHardwareInputMethodId());
    666 
    667   std::vector<std::string> input_method_ids;
    668   // First, add the initial input method ID, if it's requested, to
    669   // input_method_ids, so it appears first on the list of active input
    670   // methods at the input language status menu.
    671   if (!initial_input_method_id.empty()) {
    672     input_method_ids.push_back(initial_input_method_id);
    673   }
    674 
    675   // Add candidates to input_method_ids, while skipping duplicates.
    676   for (size_t i = 0; i < candidates.size(); ++i) {
    677     const std::string& candidate = candidates[i];
    678     // Not efficient, but should be fine, as the two vectors are very
    679     // short (2-5 items).
    680     if (std::count(input_method_ids.begin(), input_method_ids.end(),
    681                    candidate) == 0) {
    682       input_method_ids.push_back(candidate);
    683     }
    684   }
    685 
    686   // Update ibus-daemon setting. Here, we don't save the input method list
    687   // in the user's preferences.
    688   ImeConfigValue value;
    689   value.type = ImeConfigValue::kValueTypeStringList;
    690   value.string_list_value = input_method_ids;
    691   InputMethodLibrary* library = CrosLibrary::Get()->GetInputMethodLibrary();
    692   library->SetImeConfig(language_prefs::kGeneralSectionName,
    693                         language_prefs::kPreloadEnginesConfigName, value);
    694 
    695   // Finaly, change to the initial input method, as needed.
    696   if (!initial_input_method_id.empty()) {
    697     library->ChangeInputMethod(initial_input_method_id);
    698   }
    699 }
    700 
    701 std::string GetHardwareInputMethodId() {
    702   if (!(g_browser_process && g_browser_process->local_state())) {
    703     // This shouldn't happen but just in case.
    704     LOG(ERROR) << "Local state is not yet ready";
    705     return GetFallbackInputMethodDescriptor().id;
    706   }
    707 
    708   PrefService* local_state = g_browser_process->local_state();
    709   if (!local_state->FindPreference(prefs::kHardwareKeyboardLayout)) {
    710     // This could happen in unittests. We register the preference in
    711     // BrowserMain::InitializeLocalState and that method is not called during
    712     // unittests.
    713     LOG(ERROR) << prefs::kHardwareKeyboardLayout << " is not registered";
    714     return GetFallbackInputMethodDescriptor().id;
    715   }
    716 
    717   const std::string input_method_id =
    718       local_state->GetString(prefs::kHardwareKeyboardLayout);
    719   if (input_method_id.empty()) {
    720     // This is totally fine if it's empty. The hardware keyboard layout is
    721     // not stored if startup_manifest.json (OEM customization data) is not
    722     // present (ex. Cr48 doen't have that file).
    723     return GetFallbackInputMethodDescriptor().id;
    724   }
    725   return input_method_id;
    726 }
    727 
    728 InputMethodDescriptor GetFallbackInputMethodDescriptor() {
    729   return InputMethodDescriptor("xkb:us::eng", "USA", "us", "eng");
    730 }
    731 
    732 void ReloadInternalMaps() {
    733   IdMaps::GetInstance()->ReloadMaps();
    734 }
    735 
    736 void OnLocaleChanged() {
    737   ReloadInternalMaps();
    738 }
    739 
    740 }  // namespace input_method
    741 }  // namespace chromeos
    742