Home | History | Annotate | Download | only in input_method
      1 // Copyright (c) 2012 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 "base/basictypes.h"
     13 #include "base/memory/scoped_ptr.h"
     14 #include "base/prefs/pref_service.h"
     15 #include "base/strings/string_split.h"
     16 #include "base/strings/string_util.h"
     17 #include "base/strings/utf_string_conversions.h"
     18 #include "chromeos/ime/component_extension_ime_manager.h"
     19 #include "chromeos/ime/extension_ime_util.h"
     20 #include "chromeos/ime/input_method_delegate.h"
     21 // TODO(nona): move this header from this file.
     22 #include "grit/generated_resources.h"
     23 
     24 namespace {
     25 
     26 // A mapping from an input method id to a string for the language indicator. The
     27 // mapping is necessary since some input methods belong to the same language.
     28 // For example, both "xkb:us::eng" and "xkb:us:dvorak:eng" are for US English.
     29 const struct {
     30   const char* input_method_id;
     31   const char* indicator_text;
     32 } kMappingFromIdToIndicatorText[] = {
     33   // To distinguish from "xkb:us::eng"
     34   { "xkb:us:altgr-intl:eng", "EXTD" },
     35   { "xkb:us:dvorak:eng", "DV" },
     36   { "xkb:us:intl:eng", "INTL" },
     37   { "xkb:us:colemak:eng", "CO" },
     38   { "english-m", "??" },
     39   { "xkb:de:neo:ger", "NEO" },
     40   // To distinguish from "xkb:es::spa"
     41   { "xkb:es:cat:cat", "CAS" },
     42   // To distinguish from "xkb:gb::eng"
     43   { "xkb:gb:dvorak:eng", "DV" },
     44   // To distinguish from "xkb:jp::jpn"
     45   // TODO(nona): Make following variables configurable. http://crbug.com/232260.
     46   { "_comp_ime_fpfbhcjppmaeaijcidgiibchfbnhbeljnacl_mozc_us", "\xe3\x81\x82" },
     47   { "_comp_ime_fpfbhcjppmaeaijcidgiibchfbnhbeljnacl_mozc_jp", "\xe3\x81\x82" },
     48   { "_comp_ime_bbaiamgfapehflhememkfglaehiobjnknacl_mozc_us", "\xe3\x81\x82" },
     49   { "_comp_ime_bbaiamgfapehflhememkfglaehiobjnknacl_mozc_jp", "\xe3\x81\x82" },
     50   // For simplified Chinese input methods
     51   { "pinyin", "\xe6\x8b\xbc" },  // U+62FC
     52   { "_comp_ime_cpgalbafkoofkjmaeonnfijgpfennjjnzh-t-i0-pinyin",
     53     "\xe6\x8b\xbc" },
     54   { "_comp_ime_nmblnjkfdkabgdofidlkienfnnbjhnabzh-t-i0-pinyin",
     55     "\xe6\x8b\xbc" },
     56   { "_comp_ime_gjhclobljhjhgoebiipblnmdodbmpdgdzh-t-i0-wubi-1986",
     57     "\xe4\xba\x94" }, // U+4E94
     58   { "pinyin-dv", "\xe6\x8b\xbc" },
     59   // For traditional Chinese input methods
     60   { "mozc-chewing", "\xe9\x85\xb7" },  // U+9177
     61   { "_comp_ime_ekbifjdfhkmdeeajnolmgdlmkllopefizh-hant-t-i0-und",
     62     "\xe9\x85\xb7" },  // U+9177
     63   { "_comp_ime_goedamlknlnjaengojinmfgpmdjmkooozh-hant-t-i0-und",
     64     "\xe9\x85\xb7" },  // U+9177
     65   { "m17n:zh:cangjie", "\xe5\x80\x89" },  // U+5009
     66   { "_comp_ime_aeebooiibjahgpgmhkeocbeekccfknbjzh-hant-t-i0-cangjie-1987",
     67     "\xe5\x80\x89" },  // U+5009
     68   { "_comp_ime_gjhclobljhjhgoebiipblnmdodbmpdgdzh-hant-t-i0-cangjie-1987",
     69     "\xe5\x80\x89" },  // U+5009
     70   { "m17n:zh:quick", "\xe9\x80\x9f" },  // U+901F
     71   // For Hangul input method.
     72   { "mozc-hangul", "\xed\x95\x9c" },  // U+D55C
     73   { "_comp_ime_bdgdidmhaijohebebipajioienkglgfohangul_2set", "\xed\x95\x9c" },
     74   { "_comp_ime_bdgdidmhaijohebebipajioienkglgfohangul_3set390",
     75     "\xed\x95\x9c" },
     76   { "_comp_ime_bdgdidmhaijohebebipajioienkglgfohangul_3setfinal",
     77     "\xed\x95\x9c" },
     78   { "_comp_ime_bdgdidmhaijohebebipajioienkglgfohangul_3setnoshift",
     79     "\xed\x95\x9c" },
     80   { "_comp_ime_bdgdidmhaijohebebipajioienkglgfohangul_romaja", "\xed\x95\x9c" },
     81 };
     82 
     83 const size_t kMappingFromIdToIndicatorTextLen =
     84     ARRAYSIZE_UNSAFE(kMappingFromIdToIndicatorText);
     85 
     86 // A mapping from an input method id to a resource id for a
     87 // medium length language indicator.
     88 // For those languages that want to display a slightly longer text in the
     89 // "Your input method has changed to..." bubble than in the status tray.
     90 // If an entry is not found in this table the short name is used.
     91 const struct {
     92   const char* input_method_id;
     93   const int resource_id;
     94 } kMappingImeIdToMediumLenNameResourceId[] = {
     95   { "m17n:zh:cangjie", IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_TRADITIONAL },
     96   { "m17n:zh:quick", IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_TRADITIONAL },
     97   { "mozc-chewing", IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_TRADITIONAL },
     98   { "mozc-hangul", IDS_LANGUAGES_MEDIUM_LEN_NAME_KOREAN },
     99   { "pinyin", IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_SIMPLIFIED },
    100   { "pinyin-dv", IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_SIMPLIFIED },
    101   { "_comp_ime_cpgalbafkoofkjmaeonnfijgpfennjjnzh-t-i0-pinyin",
    102     IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_SIMPLIFIED},
    103   { "_comp_ime_nmblnjkfdkabgdofidlkienfnnbjhnabzh-t-i0-pinyin",
    104     IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_SIMPLIFIED },
    105   { "_comp_ime_gjhclobljhjhgoebiipblnmdodbmpdgdzh-t-i0-wubi-1986",
    106     IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_SIMPLIFIED },
    107   { "_comp_ime_ekbifjdfhkmdeeajnolmgdlmkllopefizh-hant-t-i0-und",
    108     IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_TRADITIONAL },
    109   { "_comp_ime_goedamlknlnjaengojinmfgpmdjmkooozh-hant-t-i0-und",
    110     IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_TRADITIONAL },
    111   { "_comp_ime_aeebooiibjahgpgmhkeocbeekccfknbjzh-hant-t-i0-cangjie-1987",
    112     IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_TRADITIONAL },
    113   { "_comp_ime_gjhclobljhjhgoebiipblnmdodbmpdgdzh-hant-t-i0-cangjie-1987",
    114     IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_TRADITIONAL },
    115 };
    116 const size_t kMappingImeIdToMediumLenNameResourceIdLen =
    117     ARRAYSIZE_UNSAFE(kMappingImeIdToMediumLenNameResourceId);
    118 
    119 // Due to asynchronous initialization of component extension manager,
    120 // GetFirstLogingInputMethodIds may miss component extension IMEs. To enable
    121 // component extension IME as the first loging input method, we have to prepare
    122 // component extension IME IDs.
    123 const struct {
    124   const char* locale;
    125   const char* layout;
    126   const char* input_method_id;
    127 } kDefaultInputMethodRecommendation[] = {
    128   { "ja", "us", "_comp_ime_fpfbhcjppmaeaijcidgiibchfbnhbeljnacl_mozc_us" },
    129   { "ja", "jp", "_comp_ime_fpfbhcjppmaeaijcidgiibchfbnhbeljnacl_mozc_jp" },
    130   { "zh-CN", "us", "_comp_ime_nmblnjkfdkabgdofidlkienfnnbjhnabzh-t-i0-pinyin" },
    131   { "zh-TW", "us",
    132     "_comp_ime_goedamlknlnjaengojinmfgpmdjmkooozh-hant-t-i0-und" },
    133   { "th", "us", "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_th" },
    134   { "vi", "us", "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_vi_tcvn" },
    135   { "vi", "us", "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_vi_tcvn" },
    136 };
    137 
    138 }  // namespace
    139 
    140 namespace chromeos {
    141 
    142 extern const char* kExtensionImePrefix;
    143 
    144 namespace input_method {
    145 
    146 namespace {
    147 
    148 const struct EnglishToResouceId {
    149   const char* english_string_from_ibus;
    150   int resource_id;
    151 } kEnglishToResourceIdArray[] = {
    152   // For ibus-mozc-hangul
    153   { "Hanja mode", IDS_STATUSBAR_IME_KOREAN_HANJA_INPUT_MODE },
    154   { "Hangul mode", IDS_STATUSBAR_IME_KOREAN_HANGUL_INPUT_MODE },
    155 
    156   // For ibus-mozc-pinyin.
    157   { "Full/Half width",
    158     IDS_STATUSBAR_IME_CHINESE_PINYIN_TOGGLE_FULL_HALF },
    159   { "Full/Half width punctuation",
    160     IDS_STATUSBAR_IME_CHINESE_PINYIN_TOGGLE_FULL_HALF_PUNCTUATION },
    161   // TODO(hsumita): Fixes a typo
    162   { "Simplfied/Traditional Chinese",
    163     IDS_STATUSBAR_IME_CHINESE_PINYIN_TOGGLE_S_T_CHINESE },
    164   { "Chinese",
    165     IDS_STATUSBAR_IME_CHINESE_PINYIN_TOGGLE_CHINESE_ENGLISH },
    166 
    167   // For ibus-mozc-chewing.
    168   { "English",
    169     IDS_STATUSBAR_IME_CHINESE_MOZC_CHEWING_ENGLISH_MODE },
    170   { "_Chinese",
    171     IDS_STATUSBAR_IME_CHINESE_MOZC_CHEWING_CHINESE_MODE },
    172   { "Full-width English",
    173     IDS_STATUSBAR_IME_CHINESE_MOZC_CHEWING_FULL_WIDTH_ENGLISH_MODE },
    174 
    175   // For the "Languages and Input" dialog.
    176   { "m17n:ar:kbd", IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD },
    177   { "m17n:hi:itrans",  // also uses the "STANDARD_INPUT_METHOD" id.
    178     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD },
    179   { "m17n:zh:cangjie",
    180     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_CHINESE_CANGJIE_INPUT_METHOD },
    181   { "m17n:zh:quick",
    182     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_CHINESE_QUICK_INPUT_METHOD },
    183   { "m17n:fa:isiri",
    184     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_PERSIAN_ISIRI_2901_INPUT_METHOD },
    185   { "m17n:th:kesmanee",
    186     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_THAI_KESMANEE_INPUT_METHOD },
    187   { "m17n:th:tis820",
    188     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_THAI_TIS820_INPUT_METHOD },
    189   { "m17n:th:pattachote",
    190     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_THAI_PATTACHOTE_INPUT_METHOD },
    191   { "m17n:vi:tcvn",
    192     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_TCVN_INPUT_METHOD },
    193   { "m17n:vi:telex",
    194     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_TELEX_INPUT_METHOD },
    195   { "m17n:vi:viqr",
    196     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_VIQR_INPUT_METHOD },
    197   { "m17n:vi:vni",
    198     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_VNI_INPUT_METHOD },
    199   { "m17n:bn:itrans",
    200     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD },
    201   { "m17n:gu:itrans",
    202     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD },
    203   { "m17n:ml:itrans",
    204     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD },
    205   { "m17n:mr:itrans",
    206     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD },
    207   { "m17n:ta:phonetic",
    208     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_TAMIL_PHONETIC },
    209   { "m17n:ta:inscript",
    210     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_TAMIL_INSCRIPT },
    211   { "m17n:ta:tamil99",
    212     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_TAMIL_TAMIL99 },
    213   { "m17n:ta:itrans",
    214     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_TAMIL_ITRANS },
    215   { "m17n:ta:typewriter",
    216     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_TAMIL_TYPEWRITER },
    217   { "m17n:am:sera",
    218     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD },
    219   { "m17n:te:itrans",
    220     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD },
    221   { "m17n:kn:itrans",
    222     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD },
    223 
    224   { "mozc-chewing",
    225     IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_INPUT_METHOD },
    226   { "pinyin", IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_INPUT_METHOD },
    227   { "pinyin-dv",
    228     IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_DV_INPUT_METHOD },
    229   { "zinnia-japanese",
    230     IDS_OPTIONS_SETTINGS_LANGUAGES_JAPANESE_HANDWRITING_INPUT_METHOD },
    231   { "mozc-hangul", IDS_OPTIONS_SETTINGS_LANGUAGES_KOREAN_INPUT_METHOD },
    232 
    233   // For ibus-xkb-layouts engine: third_party/ibus-xkb-layouts/files
    234   { "xkb:jp::jpn", IDS_STATUSBAR_LAYOUT_JAPAN },
    235   { "xkb:si::slv", IDS_STATUSBAR_LAYOUT_SLOVENIA },
    236   { "xkb:de::ger", IDS_STATUSBAR_LAYOUT_GERMANY },
    237   { "xkb:de:neo:ger", IDS_STATUSBAR_LAYOUT_GERMANY_NEO2 },
    238   { "xkb:it::ita", IDS_STATUSBAR_LAYOUT_ITALY },
    239   { "xkb:ee::est", IDS_STATUSBAR_LAYOUT_ESTONIA },
    240   { "xkb:hu::hun", IDS_STATUSBAR_LAYOUT_HUNGARY },
    241   { "xkb:pl::pol", IDS_STATUSBAR_LAYOUT_POLAND },
    242   { "xkb:dk::dan", IDS_STATUSBAR_LAYOUT_DENMARK },
    243   { "xkb:hr::scr", IDS_STATUSBAR_LAYOUT_CROATIA },
    244   { "xkb:br::por", IDS_STATUSBAR_LAYOUT_BRAZIL },
    245   { "xkb:rs::srp", IDS_STATUSBAR_LAYOUT_SERBIA },
    246   { "xkb:cz::cze", IDS_STATUSBAR_LAYOUT_CZECHIA },
    247   { "xkb:cz:qwerty:cze", IDS_STATUSBAR_LAYOUT_CZECHIA_QWERTY },
    248   { "xkb:us:dvorak:eng", IDS_STATUSBAR_LAYOUT_USA_DVORAK },
    249   { "xkb:us:colemak:eng", IDS_STATUSBAR_LAYOUT_USA_COLEMAK },
    250   { "xkb:ro::rum", IDS_STATUSBAR_LAYOUT_ROMANIA },
    251   { "xkb:us::eng", IDS_STATUSBAR_LAYOUT_USA },
    252   { "xkb:us:altgr-intl:eng", IDS_STATUSBAR_LAYOUT_USA_EXTENDED },
    253   { "xkb:us:intl:eng", IDS_STATUSBAR_LAYOUT_USA_INTERNATIONAL },
    254   { "xkb:lt::lit", IDS_STATUSBAR_LAYOUT_LITHUANIA },
    255   { "xkb:gb:extd:eng", IDS_STATUSBAR_LAYOUT_UNITED_KINGDOM },
    256   { "xkb:gb:dvorak:eng", IDS_STATUSBAR_LAYOUT_UNITED_KINGDOM_DVORAK },
    257   { "xkb:sk::slo", IDS_STATUSBAR_LAYOUT_SLOVAKIA },
    258   { "xkb:ru::rus", IDS_STATUSBAR_LAYOUT_RUSSIA },
    259   { "xkb:ru:phonetic:rus", IDS_STATUSBAR_LAYOUT_RUSSIA_PHONETIC },
    260   { "xkb:gr::gre", IDS_STATUSBAR_LAYOUT_GREECE },
    261   { "xkb:be::fra", IDS_STATUSBAR_LAYOUT_BELGIUM },
    262   { "xkb:be::ger", IDS_STATUSBAR_LAYOUT_BELGIUM },
    263   { "xkb:be::nld", IDS_STATUSBAR_LAYOUT_BELGIUM },
    264   { "xkb:bg::bul", IDS_STATUSBAR_LAYOUT_BULGARIA },
    265   { "xkb:bg:phonetic:bul", IDS_STATUSBAR_LAYOUT_BULGARIA_PHONETIC },
    266   { "xkb:ch::ger", IDS_STATUSBAR_LAYOUT_SWITZERLAND },
    267   { "xkb:ch:fr:fra", IDS_STATUSBAR_LAYOUT_SWITZERLAND_FRENCH },
    268   { "xkb:tr::tur", IDS_STATUSBAR_LAYOUT_TURKEY },
    269   { "xkb:pt::por", IDS_STATUSBAR_LAYOUT_PORTUGAL },
    270   { "xkb:es::spa", IDS_STATUSBAR_LAYOUT_SPAIN },
    271   { "xkb:fi::fin", IDS_STATUSBAR_LAYOUT_FINLAND },
    272   { "xkb:ua::ukr", IDS_STATUSBAR_LAYOUT_UKRAINE },
    273   { "xkb:es:cat:cat", IDS_STATUSBAR_LAYOUT_SPAIN_CATALAN },
    274   { "xkb:fr::fra", IDS_STATUSBAR_LAYOUT_FRANCE },
    275   { "xkb:no::nob", IDS_STATUSBAR_LAYOUT_NORWAY },
    276   { "xkb:se::swe", IDS_STATUSBAR_LAYOUT_SWEDEN },
    277   { "xkb:nl::nld", IDS_STATUSBAR_LAYOUT_NETHERLANDS },
    278   { "xkb:latam::spa", IDS_STATUSBAR_LAYOUT_LATIN_AMERICAN },
    279   { "xkb:lv:apostrophe:lav", IDS_STATUSBAR_LAYOUT_LATVIA },
    280   { "xkb:ca::fra", IDS_STATUSBAR_LAYOUT_CANADA },
    281   { "xkb:ca:eng:eng", IDS_STATUSBAR_LAYOUT_CANADA_ENGLISH },
    282   { "xkb:il::heb", IDS_STATUSBAR_LAYOUT_ISRAEL },
    283   { "xkb:kr:kr104:kor", IDS_STATUSBAR_LAYOUT_KOREA_104 },
    284   { "xkb:is::ice", IDS_STATUSBAR_LAYOUT_ICELANDIC },
    285   { "xkb:ca:multix:fra", IDS_STATUSBAR_LAYOUT_CANADIAN_MULTILINGUAL },
    286   { "xkb:by::bel", IDS_STATUSBAR_LAYOUT_BELARUSIAN },
    287   { "xkb:am:phonetic:arm", IDS_STATUSBAR_LAYOUT_ARMENIAN_PHONETIC },
    288   { "xkb:ge::geo", IDS_STATUSBAR_LAYOUT_GEORGIAN },
    289   { "xkb:mn::mon", IDS_STATUSBAR_LAYOUT_MONGOLIAN },
    290 
    291   { "english-m", IDS_STATUSBAR_LAYOUT_USA_MYSTERY },
    292 };
    293 const size_t kEnglishToResourceIdArraySize =
    294     arraysize(kEnglishToResourceIdArray);
    295 
    296 // The list of language that do not have associated input methods in IBus.
    297 // For these languages, we associate input methods here.
    298 const struct ExtraLanguage {
    299   const char* language_code;
    300   const char* input_method_id;
    301 } kExtraLanguages[] = {
    302   // Language Code  Input Method ID
    303   { "en-AU", "xkb:us::eng" },  // For Austrailia, use US keyboard layout.
    304   { "id", "xkb:us::eng" },  // For Indonesian, use US keyboard layout.
    305   // The code "fil" comes from l10_util.cc.
    306   { "fil", "xkb:us::eng" },  // For Filipino, use US keyboard layout.
    307   // For Netherlands, use US international keyboard layout.
    308   { "nl", "xkb:us:intl:eng" },
    309   // The code "es-419" comes from l10_util.cc.
    310   // For Spanish in Latin America, use Latin American keyboard layout.
    311   { "es-419", "xkb:latam::spa" },
    312   // For Malay, use US keyboard layout. crosbug.com/p/8288
    313   { "ms", "xkb:us::eng" },
    314   // For Brazil, it is common to use US-international keyboard layout.
    315   { "pt-BR", "xkb:us:intl:eng" },
    316 
    317   // TODO(yusukes): Add {"sw", "xkb:us::eng"} once Swahili is removed from the
    318   // blacklist in src/ui/base/l10n/l10n_util_posix.cc.
    319 };
    320 const size_t kExtraLanguagesLength = arraysize(kExtraLanguages);
    321 
    322 }  // namespace
    323 
    324 InputMethodUtil::InputMethodUtil(
    325     InputMethodDelegate* delegate,
    326     scoped_ptr<InputMethodDescriptors> supported_input_methods)
    327     : supported_input_methods_(supported_input_methods.Pass()),
    328       delegate_(delegate) {
    329   ReloadInternalMaps();
    330 
    331   // Initialize a map from English string to Chrome string resource ID as well.
    332   for (size_t i = 0; i < kEnglishToResourceIdArraySize; ++i) {
    333     const EnglishToResouceId& map_entry = kEnglishToResourceIdArray[i];
    334     const bool result = english_to_resource_id_.insert(std::make_pair(
    335         map_entry.english_string_from_ibus, map_entry.resource_id)).second;
    336     DCHECK(result) << "Duplicated string is found: "
    337                    << map_entry.english_string_from_ibus;
    338   }
    339 }
    340 
    341 InputMethodUtil::~InputMethodUtil() {
    342 }
    343 
    344 bool InputMethodUtil::TranslateStringInternal(
    345     const std::string& english_string, string16 *out_string) const {
    346   DCHECK(out_string);
    347   HashType::const_iterator iter = english_to_resource_id_.find(english_string);
    348   if (iter == english_to_resource_id_.end()) {
    349     // TODO(yusukes): Write Autotest which checks if all display names and all
    350     // property names for supported input methods are listed in the resource
    351     // ID array (crosbug.com/4572).
    352     LOG(ERROR) << "Resource ID is not found for: " << english_string;
    353     return false;
    354   }
    355 
    356   *out_string = delegate_->GetLocalizedString(iter->second);
    357   return true;
    358 }
    359 
    360 string16 InputMethodUtil::TranslateString(
    361     const std::string& english_string) const {
    362   string16 localized_string;
    363   if (TranslateStringInternal(english_string, &localized_string)) {
    364     return localized_string;
    365   }
    366   return UTF8ToUTF16(english_string);
    367 }
    368 
    369 bool InputMethodUtil::IsValidInputMethodId(
    370     const std::string& input_method_id) const {
    371   // We can't check the component extension is whilelisted or not here because
    372   // it might not be initialized.
    373   return GetInputMethodDescriptorFromId(input_method_id) != NULL ||
    374       ComponentExtensionIMEManager::IsComponentExtensionIMEId(input_method_id);
    375 }
    376 
    377 // static
    378 bool InputMethodUtil::IsKeyboardLayout(const std::string& input_method_id) {
    379   const bool kCaseInsensitive = false;
    380   return StartsWithASCII(input_method_id, "xkb:", kCaseInsensitive);
    381 }
    382 
    383 std::string InputMethodUtil::GetLanguageCodeFromInputMethodId(
    384     const std::string& input_method_id) const {
    385   // The code should be compatible with one of codes used for UI languages,
    386   // defined in app/l10_util.cc.
    387   const char kDefaultLanguageCode[] = "en-US";
    388   std::map<std::string, std::string>::const_iterator iter
    389       = id_to_language_code_.find(input_method_id);
    390   return (iter == id_to_language_code_.end()) ?
    391       // Returning |kDefaultLanguageCode| here is not for Chrome OS but for
    392       // Ubuntu where the ibus-xkb-layouts engine could be missing.
    393       kDefaultLanguageCode : iter->second;
    394 }
    395 
    396 std::string InputMethodUtil::GetKeyboardLayoutName(
    397     const std::string& input_method_id) const {
    398   InputMethodIdToDescriptorMap::const_iterator iter
    399       = id_to_descriptor_.find(input_method_id);
    400   return (iter == id_to_descriptor_.end()) ?
    401       "" : iter->second.GetPreferredKeyboardLayout();
    402 }
    403 
    404 std::string InputMethodUtil::GetInputMethodDisplayNameFromId(
    405     const std::string& input_method_id) const {
    406   string16 display_name;
    407   if (!extension_ime_util::IsExtensionIME(input_method_id) &&
    408       TranslateStringInternal(input_method_id, &display_name)) {
    409     return UTF16ToUTF8(display_name);
    410   }
    411   // Return an empty string if the display name is not found.
    412   return "";
    413 }
    414 
    415 string16 InputMethodUtil::GetInputMethodShortName(
    416     const InputMethodDescriptor& input_method) const {
    417   // For the status area, we use two-letter, upper-case language code like
    418   // "US" and "JP".
    419   string16 text;
    420 
    421   // Check special cases first.
    422   for (size_t i = 0; i < kMappingFromIdToIndicatorTextLen; ++i) {
    423     if (kMappingFromIdToIndicatorText[i].input_method_id == input_method.id()) {
    424       text = UTF8ToUTF16(kMappingFromIdToIndicatorText[i].indicator_text);
    425       break;
    426     }
    427   }
    428 
    429   // Display the keyboard layout name when using a keyboard layout.
    430   if (text.empty() &&
    431       IsKeyboardLayout(input_method.id())) {
    432     const size_t kMaxKeyboardLayoutNameLen = 2;
    433     const string16 keyboard_layout =
    434         UTF8ToUTF16(GetKeyboardLayoutName(input_method.id()));
    435     text = StringToUpperASCII(keyboard_layout).substr(
    436         0, kMaxKeyboardLayoutNameLen);
    437   }
    438 
    439   // TODO(yusukes): Some languages have two or more input methods. For example,
    440   // Thai has 3, Vietnamese has 4. If these input methods could be activated at
    441   // the same time, we should do either of the following:
    442   //   (1) Add mappings to |kMappingFromIdToIndicatorText|
    443   //   (2) Add suffix (1, 2, ...) to |text| when ambiguous.
    444 
    445   if (text.empty()) {
    446     const size_t kMaxLanguageNameLen = 2;
    447     DCHECK(!input_method.language_codes().empty());
    448     const std::string language_code = input_method.language_codes().at(0);
    449     text = StringToUpperASCII(UTF8ToUTF16(language_code)).substr(
    450         0, kMaxLanguageNameLen);
    451   }
    452   DCHECK(!text.empty());
    453   return text;
    454 }
    455 
    456 string16 InputMethodUtil::GetInputMethodMediumName(
    457     const InputMethodDescriptor& input_method) const {
    458   // For the "Your input method has changed to..." bubble. In most cases
    459   // it uses the same name as the short name, unless found in a table
    460   // for medium length names.
    461   for (size_t i = 0; i < kMappingImeIdToMediumLenNameResourceIdLen; ++i) {
    462     if (kMappingImeIdToMediumLenNameResourceId[i].input_method_id ==
    463         input_method.id()) {
    464       return delegate_->GetLocalizedString(
    465           kMappingImeIdToMediumLenNameResourceId[i].resource_id);
    466     }
    467   }
    468   return GetInputMethodShortName(input_method);
    469 }
    470 
    471 string16 InputMethodUtil::GetInputMethodLongName(
    472     const InputMethodDescriptor& input_method) const {
    473   if (!input_method.name().empty()) {
    474     // If the descriptor has a name, use it.
    475     return UTF8ToUTF16(input_method.name());
    476   }
    477 
    478   // We don't show language here.  Name of keyboard layout or input method
    479   // usually imply (or explicitly include) its language.
    480 
    481   // Special case for German, French and Dutch: these languages have multiple
    482   // keyboard layouts and share the same layout of keyboard (Belgian). We need
    483   // to show explicitly the language for the layout. For Arabic, Amharic, and
    484   // Indic languages: they share "Standard Input Method".
    485   const string16 standard_input_method_text = delegate_->GetLocalizedString(
    486       IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD);
    487   DCHECK(!input_method.language_codes().empty());
    488   const std::string language_code = input_method.language_codes().at(0);
    489 
    490   string16 text = TranslateString(input_method.id());
    491   if (text == standard_input_method_text ||
    492              language_code == "de" ||
    493              language_code == "fr" ||
    494              language_code == "nl") {
    495     const string16 language_name = delegate_->GetDisplayLanguageName(
    496         language_code);
    497 
    498     text = language_name + UTF8ToUTF16(" - ") + text;
    499   }
    500 
    501   DCHECK(!text.empty());
    502   return text;
    503 }
    504 
    505 const InputMethodDescriptor* InputMethodUtil::GetInputMethodDescriptorFromId(
    506     const std::string& input_method_id) const {
    507   InputMethodIdToDescriptorMap::const_iterator iter
    508       = id_to_descriptor_.find(input_method_id);
    509   return (iter == id_to_descriptor_.end()) ? NULL : &(iter->second);
    510 }
    511 
    512 std::vector<std::string> InputMethodUtil::GetExtraLanguageCodesFromId(
    513     const std::string& input_method_id) const {
    514   std::vector<std::string> result;
    515   for (size_t i = 0; i < kExtraLanguagesLength; ++i) {
    516     if (input_method_id == kExtraLanguages[i].input_method_id)
    517       result.push_back(kExtraLanguages[i].language_code);
    518   }
    519   return result;
    520 }
    521 
    522 std::vector<std::string> InputMethodUtil::GetExtraLanguageCodeList() const {
    523   std::vector<std::string> result;
    524   for (size_t i = 0; i < kExtraLanguagesLength; ++i)
    525     result.push_back(kExtraLanguages[i].language_code);
    526   return result;
    527 }
    528 
    529 bool InputMethodUtil::GetInputMethodIdsFromLanguageCode(
    530     const std::string& normalized_language_code,
    531     InputMethodType type,
    532     std::vector<std::string>* out_input_method_ids) const {
    533   return GetInputMethodIdsFromLanguageCodeInternal(
    534       language_code_to_ids_,
    535       normalized_language_code, type, out_input_method_ids);
    536 }
    537 
    538 bool InputMethodUtil::GetInputMethodIdsFromLanguageCodeInternal(
    539     const std::multimap<std::string, std::string>& language_code_to_ids,
    540     const std::string& normalized_language_code,
    541     InputMethodType type,
    542     std::vector<std::string>* out_input_method_ids) const {
    543   DCHECK(out_input_method_ids);
    544   out_input_method_ids->clear();
    545 
    546   bool result = false;
    547   std::pair<LanguageCodeToIdsMap::const_iterator,
    548       LanguageCodeToIdsMap::const_iterator> range =
    549       language_code_to_ids.equal_range(normalized_language_code);
    550   for (LanguageCodeToIdsMap::const_iterator iter = range.first;
    551        iter != range.second; ++iter) {
    552     const std::string& input_method_id = iter->second;
    553     if ((type == kAllInputMethods) || IsKeyboardLayout(input_method_id)) {
    554       out_input_method_ids->push_back(input_method_id);
    555       result = true;
    556     }
    557   }
    558   if ((type == kAllInputMethods) && !result) {
    559     DVLOG(1) << "Unknown language code: " << normalized_language_code;
    560   }
    561   return result;
    562 }
    563 
    564 void InputMethodUtil::GetFirstLoginInputMethodIds(
    565     const std::string& language_code,
    566     const InputMethodDescriptor& current_input_method,
    567     std::vector<std::string>* out_input_method_ids) const {
    568   out_input_method_ids->clear();
    569 
    570   // First, add the current keyboard layout (one used on the login screen).
    571   out_input_method_ids->push_back(current_input_method.id());
    572 
    573   const std::string current_layout
    574       = current_input_method.GetPreferredKeyboardLayout();
    575   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kDefaultInputMethodRecommendation);
    576        ++i) {
    577     if (kDefaultInputMethodRecommendation[i].locale == language_code &&
    578         kDefaultInputMethodRecommendation[i].layout == current_layout) {
    579       out_input_method_ids->push_back(
    580           kDefaultInputMethodRecommendation[i].input_method_id);
    581       return;
    582     }
    583   }
    584 
    585   // Second, find the most popular input method associated with the
    586   // current UI language. The input method IDs returned from
    587   // GetInputMethodIdsFromLanguageCode() are sorted by popularity, hence
    588   // our basic strategy is to pick the first one, but it's a bit more
    589   // complicated as shown below.
    590   std::string most_popular_id;
    591   std::vector<std::string> input_method_ids;
    592   // This returns the input methods sorted by popularity.
    593   GetInputMethodIdsFromLanguageCode(
    594       language_code, kAllInputMethods, &input_method_ids);
    595   for (size_t i = 0; i < input_method_ids.size(); ++i) {
    596     const std::string& input_method_id = input_method_ids[i];
    597     // Pick the first one.
    598     if (most_popular_id.empty())
    599       most_popular_id = input_method_id;
    600 
    601     // Check if there is one that matches the current keyboard layout, but
    602     // not the current keyboard itself. This is useful if there are
    603     // multiple keyboard layout choices for one input method. For
    604     // instance, Mozc provides three choices: mozc (US keyboard), mozc-jp
    605     // (JP keyboard), mozc-dv (Dvorak).
    606     const InputMethodDescriptor* descriptor =
    607         GetInputMethodDescriptorFromId(input_method_id);
    608     if (descriptor &&
    609         descriptor->id() != current_input_method.id() &&
    610         descriptor->GetPreferredKeyboardLayout() ==
    611         current_input_method.GetPreferredKeyboardLayout()) {
    612       most_popular_id = input_method_id;
    613       break;
    614     }
    615   }
    616   // Add the most popular input method ID, if it's different from the
    617   // current input method.
    618   if (most_popular_id != current_input_method.id() &&
    619       // TODO(yusukes): Remove this hack when we remove the "english-m" IME.
    620       most_popular_id != "english-m") {
    621     out_input_method_ids->push_back(most_popular_id);
    622   }
    623 }
    624 
    625 void InputMethodUtil::GetLanguageCodesFromInputMethodIds(
    626     const std::vector<std::string>& input_method_ids,
    627     std::vector<std::string>* out_language_codes) const {
    628   out_language_codes->clear();
    629 
    630   for (size_t i = 0; i < input_method_ids.size(); ++i) {
    631     const std::string& input_method_id = input_method_ids[i];
    632     const InputMethodDescriptor* input_method =
    633         GetInputMethodDescriptorFromId(input_method_id);
    634     if (!input_method) {
    635       DVLOG(1) << "Unknown input method ID: " << input_method_ids[i];
    636       continue;
    637     }
    638     DCHECK(!input_method->language_codes().empty());
    639     const std::string language_code = input_method->language_codes().at(0);
    640     // Add it if it's not already present.
    641     if (std::count(out_language_codes->begin(), out_language_codes->end(),
    642                    language_code) == 0) {
    643       out_language_codes->push_back(language_code);
    644     }
    645   }
    646 }
    647 
    648 std::string InputMethodUtil::GetHardwareInputMethodId() const {
    649   const std::string input_method_id = delegate_->GetHardwareKeyboardLayout();
    650 
    651   if (input_method_id.empty()) {
    652     // This is totally fine if it's empty. The hardware keyboard layout is
    653     // not stored if startup_manifest.json (OEM customization data) is not
    654     // present (ex. Cr48 doen't have that file).
    655     return GetFallbackInputMethodDescriptor().id();
    656   }
    657   return input_method_id;
    658 }
    659 
    660 void InputMethodUtil::SetComponentExtensions(
    661     const InputMethodDescriptors& imes) {
    662   component_extension_ime_id_to_descriptor_.clear();
    663   for (size_t i = 0; i < imes.size(); ++i) {
    664     const InputMethodDescriptor& input_method = imes.at(i);
    665     DCHECK(!input_method.language_codes().empty());
    666     const std::string language_code = input_method.language_codes().at(0);
    667     id_to_language_code_.insert(
    668         std::make_pair(input_method.id(), language_code));
    669     id_to_descriptor_.insert(
    670         std::make_pair(input_method.id(), input_method));
    671   }
    672 }
    673 
    674 InputMethodDescriptor InputMethodUtil::GetFallbackInputMethodDescriptor() {
    675   std::vector<std::string> layouts;
    676   layouts.push_back("us");
    677   std::vector<std::string> languages;
    678   languages.push_back("en-US");
    679   return InputMethodDescriptor("xkb:us::eng",
    680                                "",
    681                                layouts,
    682                                languages,
    683                                GURL());  // options page, not available.
    684 }
    685 
    686 void InputMethodUtil::ReloadInternalMaps() {
    687   if (supported_input_methods_->size() <= 1) {
    688     DVLOG(1) << "GetSupportedInputMethods returned a fallback ID";
    689     // TODO(yusukes): Handle this error in nicer way.
    690   }
    691 
    692   // Clear the existing maps.
    693   language_code_to_ids_.clear();
    694   id_to_language_code_.clear();
    695   id_to_descriptor_.clear();
    696   xkb_id_to_descriptor_.clear();
    697 
    698   for (size_t i = 0; i < supported_input_methods_->size(); ++i) {
    699     const InputMethodDescriptor& input_method =
    700         supported_input_methods_->at(i);
    701     const std::vector<std::string>& language_codes =
    702         input_method.language_codes();
    703     for (size_t i = 0; i < language_codes.size(); ++i) {
    704       language_code_to_ids_.insert(
    705           std::make_pair(language_codes[i], input_method.id()));
    706       // Remember the pairs.
    707       id_to_language_code_.insert(
    708           std::make_pair(input_method.id(), language_codes[i]));
    709     }
    710     id_to_descriptor_.insert(
    711         std::make_pair(input_method.id(), input_method));
    712     if (IsKeyboardLayout(input_method.id())) {
    713       xkb_id_to_descriptor_.insert(
    714           std::make_pair(input_method.GetPreferredKeyboardLayout(),
    715                          input_method));
    716     }
    717   }
    718 
    719   // Go through the languages listed in kExtraLanguages.
    720   for (size_t i = 0; i < kExtraLanguagesLength; ++i) {
    721     const char* language_code = kExtraLanguages[i].language_code;
    722     const char* input_method_id = kExtraLanguages[i].input_method_id;
    723     InputMethodIdToDescriptorMap::const_iterator iter =
    724         id_to_descriptor_.find(input_method_id);
    725     // If the associated input method descriptor is found, add the language
    726     // code and the input method.
    727     if (iter != id_to_descriptor_.end()) {
    728       const InputMethodDescriptor& input_method = iter->second;
    729       language_code_to_ids_.insert(
    730           std::make_pair(language_code, input_method.id()));
    731     }
    732   }
    733 }
    734 
    735 }  // namespace input_method
    736 }  // namespace chromeos
    737