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     "\xE6\xB3\xA8" },  // U+6CE8
     63   { "_comp_ime_goedamlknlnjaengojinmfgpmdjmkooozh-hant-t-i0-und",
     64     "\xE6\xB3\xA8" },  // U+6CE8
     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 }  // namespace
    297 
    298 InputMethodUtil::InputMethodUtil(
    299     InputMethodDelegate* delegate,
    300     scoped_ptr<InputMethodDescriptors> supported_input_methods)
    301     : supported_input_methods_(supported_input_methods.Pass()),
    302       delegate_(delegate) {
    303   ReloadInternalMaps();
    304 
    305   // Initialize a map from English string to Chrome string resource ID as well.
    306   for (size_t i = 0; i < kEnglishToResourceIdArraySize; ++i) {
    307     const EnglishToResouceId& map_entry = kEnglishToResourceIdArray[i];
    308     const bool result = english_to_resource_id_.insert(std::make_pair(
    309         map_entry.english_string_from_ibus, map_entry.resource_id)).second;
    310     DCHECK(result) << "Duplicated string is found: "
    311                    << map_entry.english_string_from_ibus;
    312   }
    313 }
    314 
    315 InputMethodUtil::~InputMethodUtil() {
    316 }
    317 
    318 bool InputMethodUtil::TranslateStringInternal(
    319     const std::string& english_string, base::string16 *out_string) const {
    320   DCHECK(out_string);
    321   HashType::const_iterator iter = english_to_resource_id_.find(english_string);
    322   if (iter == english_to_resource_id_.end()) {
    323     // TODO(yusukes): Write Autotest which checks if all display names and all
    324     // property names for supported input methods are listed in the resource
    325     // ID array (crosbug.com/4572).
    326     LOG(ERROR) << "Resource ID is not found for: " << english_string;
    327     return false;
    328   }
    329 
    330   *out_string = delegate_->GetLocalizedString(iter->second);
    331   return true;
    332 }
    333 
    334 base::string16 InputMethodUtil::TranslateString(
    335     const std::string& english_string) const {
    336   base::string16 localized_string;
    337   if (TranslateStringInternal(english_string, &localized_string)) {
    338     return localized_string;
    339   }
    340   return UTF8ToUTF16(english_string);
    341 }
    342 
    343 bool InputMethodUtil::IsValidInputMethodId(
    344     const std::string& input_method_id) const {
    345   // We can't check the component extension is whilelisted or not here because
    346   // it might not be initialized.
    347   return GetInputMethodDescriptorFromId(input_method_id) != NULL ||
    348       extension_ime_util::IsComponentExtensionIME(input_method_id);
    349 }
    350 
    351 // static
    352 bool InputMethodUtil::IsKeyboardLayout(const std::string& input_method_id) {
    353   const bool kCaseInsensitive = false;
    354   return StartsWithASCII(input_method_id, "xkb:", kCaseInsensitive);
    355 }
    356 
    357 std::string InputMethodUtil::GetLanguageCodeFromInputMethodId(
    358     const std::string& input_method_id) const {
    359   // The code should be compatible with one of codes used for UI languages,
    360   // defined in app/l10_util.cc.
    361   const char kDefaultLanguageCode[] = "en-US";
    362   std::map<std::string, std::string>::const_iterator iter
    363       = id_to_language_code_.find(input_method_id);
    364   return (iter == id_to_language_code_.end()) ?
    365       // Returning |kDefaultLanguageCode| here is not for Chrome OS but for
    366       // Ubuntu where the ibus-xkb-layouts engine could be missing.
    367       kDefaultLanguageCode : iter->second;
    368 }
    369 
    370 std::string InputMethodUtil::GetKeyboardLayoutName(
    371     const std::string& input_method_id) const {
    372   InputMethodIdToDescriptorMap::const_iterator iter
    373       = id_to_descriptor_.find(input_method_id);
    374   return (iter == id_to_descriptor_.end()) ?
    375       "" : iter->second.GetPreferredKeyboardLayout();
    376 }
    377 
    378 std::string InputMethodUtil::GetInputMethodDisplayNameFromId(
    379     const std::string& input_method_id) const {
    380   base::string16 display_name;
    381   if (!extension_ime_util::IsExtensionIME(input_method_id) &&
    382       TranslateStringInternal(input_method_id, &display_name)) {
    383     return UTF16ToUTF8(display_name);
    384   }
    385   // Return an empty string if the display name is not found.
    386   return "";
    387 }
    388 
    389 base::string16 InputMethodUtil::GetInputMethodShortName(
    390     const InputMethodDescriptor& input_method) const {
    391   // For the status area, we use two-letter, upper-case language code like
    392   // "US" and "JP".
    393   base::string16 text;
    394 
    395   // Check special cases first.
    396   for (size_t i = 0; i < kMappingFromIdToIndicatorTextLen; ++i) {
    397     if (kMappingFromIdToIndicatorText[i].input_method_id == input_method.id()) {
    398       text = UTF8ToUTF16(kMappingFromIdToIndicatorText[i].indicator_text);
    399       break;
    400     }
    401   }
    402 
    403   // Display the keyboard layout name when using a keyboard layout.
    404   if (text.empty() &&
    405       IsKeyboardLayout(input_method.id())) {
    406     const size_t kMaxKeyboardLayoutNameLen = 2;
    407     const base::string16 keyboard_layout =
    408         UTF8ToUTF16(GetKeyboardLayoutName(input_method.id()));
    409     text = StringToUpperASCII(keyboard_layout).substr(
    410         0, kMaxKeyboardLayoutNameLen);
    411   }
    412 
    413   // TODO(yusukes): Some languages have two or more input methods. For example,
    414   // Thai has 3, Vietnamese has 4. If these input methods could be activated at
    415   // the same time, we should do either of the following:
    416   //   (1) Add mappings to |kMappingFromIdToIndicatorText|
    417   //   (2) Add suffix (1, 2, ...) to |text| when ambiguous.
    418 
    419   if (text.empty()) {
    420     const size_t kMaxLanguageNameLen = 2;
    421     DCHECK(!input_method.language_codes().empty());
    422     const std::string language_code = input_method.language_codes().at(0);
    423     text = StringToUpperASCII(UTF8ToUTF16(language_code)).substr(
    424         0, kMaxLanguageNameLen);
    425   }
    426   DCHECK(!text.empty());
    427   return text;
    428 }
    429 
    430 base::string16 InputMethodUtil::GetInputMethodMediumName(
    431     const InputMethodDescriptor& input_method) const {
    432   // For the "Your input method has changed to..." bubble. In most cases
    433   // it uses the same name as the short name, unless found in a table
    434   // for medium length names.
    435   for (size_t i = 0; i < kMappingImeIdToMediumLenNameResourceIdLen; ++i) {
    436     if (kMappingImeIdToMediumLenNameResourceId[i].input_method_id ==
    437         input_method.id()) {
    438       return delegate_->GetLocalizedString(
    439           kMappingImeIdToMediumLenNameResourceId[i].resource_id);
    440     }
    441   }
    442   return GetInputMethodShortName(input_method);
    443 }
    444 
    445 base::string16 InputMethodUtil::GetInputMethodLongName(
    446     const InputMethodDescriptor& input_method) const {
    447   if (!input_method.name().empty()) {
    448     // If the descriptor has a name, use it.
    449     return UTF8ToUTF16(input_method.name());
    450   }
    451 
    452   // We don't show language here.  Name of keyboard layout or input method
    453   // usually imply (or explicitly include) its language.
    454 
    455   // Special case for German, French and Dutch: these languages have multiple
    456   // keyboard layouts and share the same layout of keyboard (Belgian). We need
    457   // to show explicitly the language for the layout. For Arabic, Amharic, and
    458   // Indic languages: they share "Standard Input Method".
    459   const base::string16 standard_input_method_text =
    460       delegate_->GetLocalizedString(
    461           IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD);
    462   DCHECK(!input_method.language_codes().empty());
    463   const std::string language_code = input_method.language_codes().at(0);
    464 
    465   base::string16 text = TranslateString(input_method.id());
    466   if (text == standard_input_method_text ||
    467              language_code == "de" ||
    468              language_code == "fr" ||
    469              language_code == "nl") {
    470     const base::string16 language_name = delegate_->GetDisplayLanguageName(
    471         language_code);
    472 
    473     text = language_name + UTF8ToUTF16(" - ") + text;
    474   }
    475 
    476   DCHECK(!text.empty());
    477   return text;
    478 }
    479 
    480 const InputMethodDescriptor* InputMethodUtil::GetInputMethodDescriptorFromId(
    481     const std::string& input_method_id) const {
    482   InputMethodIdToDescriptorMap::const_iterator iter
    483       = id_to_descriptor_.find(input_method_id);
    484   return (iter == id_to_descriptor_.end()) ? NULL : &(iter->second);
    485 }
    486 
    487 bool InputMethodUtil::GetInputMethodIdsFromLanguageCode(
    488     const std::string& normalized_language_code,
    489     InputMethodType type,
    490     std::vector<std::string>* out_input_method_ids) const {
    491   return GetInputMethodIdsFromLanguageCodeInternal(
    492       language_code_to_ids_,
    493       normalized_language_code, type, out_input_method_ids);
    494 }
    495 
    496 bool InputMethodUtil::GetInputMethodIdsFromLanguageCodeInternal(
    497     const std::multimap<std::string, std::string>& language_code_to_ids,
    498     const std::string& normalized_language_code,
    499     InputMethodType type,
    500     std::vector<std::string>* out_input_method_ids) const {
    501   DCHECK(out_input_method_ids);
    502   out_input_method_ids->clear();
    503 
    504   bool result = false;
    505   std::pair<LanguageCodeToIdsMap::const_iterator,
    506       LanguageCodeToIdsMap::const_iterator> range =
    507       language_code_to_ids.equal_range(normalized_language_code);
    508   for (LanguageCodeToIdsMap::const_iterator iter = range.first;
    509        iter != range.second; ++iter) {
    510     const std::string& input_method_id = iter->second;
    511     if ((type == kAllInputMethods) || IsKeyboardLayout(input_method_id)) {
    512       out_input_method_ids->push_back(input_method_id);
    513       result = true;
    514     }
    515   }
    516   if ((type == kAllInputMethods) && !result) {
    517     DVLOG(1) << "Unknown language code: " << normalized_language_code;
    518   }
    519   return result;
    520 }
    521 
    522 void InputMethodUtil::GetFirstLoginInputMethodIds(
    523     const std::string& language_code,
    524     const InputMethodDescriptor& current_input_method,
    525     std::vector<std::string>* out_input_method_ids) const {
    526   out_input_method_ids->clear();
    527 
    528   // First, add the current keyboard layout (one used on the login screen).
    529   out_input_method_ids->push_back(current_input_method.id());
    530 
    531   const std::string current_layout
    532       = current_input_method.GetPreferredKeyboardLayout();
    533   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kDefaultInputMethodRecommendation);
    534        ++i) {
    535     if (kDefaultInputMethodRecommendation[i].locale == language_code &&
    536         kDefaultInputMethodRecommendation[i].layout == current_layout) {
    537       out_input_method_ids->push_back(
    538           kDefaultInputMethodRecommendation[i].input_method_id);
    539       return;
    540     }
    541   }
    542 
    543   // Second, find the most popular input method associated with the
    544   // current UI language. The input method IDs returned from
    545   // GetInputMethodIdsFromLanguageCode() are sorted by popularity, hence
    546   // our basic strategy is to pick the first one, but it's a bit more
    547   // complicated as shown below.
    548   std::string most_popular_id;
    549   std::vector<std::string> input_method_ids;
    550   // This returns the input methods sorted by popularity.
    551   GetInputMethodIdsFromLanguageCode(
    552       language_code, kAllInputMethods, &input_method_ids);
    553   for (size_t i = 0; i < input_method_ids.size(); ++i) {
    554     const std::string& input_method_id = input_method_ids[i];
    555     // Pick the first one.
    556     if (most_popular_id.empty())
    557       most_popular_id = input_method_id;
    558 
    559     // Check if there is one that matches the current keyboard layout, but
    560     // not the current keyboard itself. This is useful if there are
    561     // multiple keyboard layout choices for one input method. For
    562     // instance, Mozc provides three choices: mozc (US keyboard), mozc-jp
    563     // (JP keyboard), mozc-dv (Dvorak).
    564     const InputMethodDescriptor* descriptor =
    565         GetInputMethodDescriptorFromId(input_method_id);
    566     if (descriptor &&
    567         descriptor->id() != current_input_method.id() &&
    568         descriptor->GetPreferredKeyboardLayout() ==
    569         current_input_method.GetPreferredKeyboardLayout()) {
    570       most_popular_id = input_method_id;
    571       break;
    572     }
    573   }
    574   // Add the most popular input method ID, if it's different from the
    575   // current input method.
    576   if (most_popular_id != current_input_method.id() &&
    577       // TODO(yusukes): Remove this hack when we remove the "english-m" IME.
    578       most_popular_id != "english-m") {
    579     out_input_method_ids->push_back(most_popular_id);
    580   }
    581 }
    582 
    583 void InputMethodUtil::GetLanguageCodesFromInputMethodIds(
    584     const std::vector<std::string>& input_method_ids,
    585     std::vector<std::string>* out_language_codes) const {
    586   out_language_codes->clear();
    587 
    588   for (size_t i = 0; i < input_method_ids.size(); ++i) {
    589     const std::string& input_method_id = input_method_ids[i];
    590     const InputMethodDescriptor* input_method =
    591         GetInputMethodDescriptorFromId(input_method_id);
    592     if (!input_method) {
    593       DVLOG(1) << "Unknown input method ID: " << input_method_ids[i];
    594       continue;
    595     }
    596     DCHECK(!input_method->language_codes().empty());
    597     const std::string language_code = input_method->language_codes().at(0);
    598     // Add it if it's not already present.
    599     if (std::count(out_language_codes->begin(), out_language_codes->end(),
    600                    language_code) == 0) {
    601       out_language_codes->push_back(language_code);
    602     }
    603   }
    604 }
    605 
    606 std::string InputMethodUtil::GetHardwareInputMethodId() const {
    607   const std::string input_method_id = delegate_->GetHardwareKeyboardLayout();
    608 
    609   if (input_method_id.empty()) {
    610     // This is totally fine if it's empty. The hardware keyboard layout is
    611     // not stored if startup_manifest.json (OEM customization data) is not
    612     // present (ex. Cr48 doen't have that file).
    613     return GetFallbackInputMethodDescriptor().id();
    614   }
    615   return input_method_id;
    616 }
    617 
    618 void InputMethodUtil::SetComponentExtensions(
    619     const InputMethodDescriptors& imes) {
    620   component_extension_ime_id_to_descriptor_.clear();
    621   for (size_t i = 0; i < imes.size(); ++i) {
    622     const InputMethodDescriptor& input_method = imes.at(i);
    623     DCHECK(!input_method.language_codes().empty());
    624     const std::string language_code = input_method.language_codes().at(0);
    625     id_to_language_code_.insert(
    626         std::make_pair(input_method.id(), language_code));
    627     id_to_descriptor_.insert(
    628         std::make_pair(input_method.id(), input_method));
    629   }
    630 }
    631 
    632 InputMethodDescriptor InputMethodUtil::GetFallbackInputMethodDescriptor() {
    633   std::vector<std::string> layouts;
    634   layouts.push_back("us");
    635   std::vector<std::string> languages;
    636   languages.push_back("en-US");
    637   return InputMethodDescriptor("xkb:us::eng",
    638                                "",
    639                                layouts,
    640                                languages,
    641                                true,  // login keyboard.
    642                                GURL(),  // options page, not available.
    643                                GURL()); // input view page, not available.
    644 }
    645 
    646 void InputMethodUtil::ReloadInternalMaps() {
    647   if (supported_input_methods_->size() <= 1) {
    648     DVLOG(1) << "GetSupportedInputMethods returned a fallback ID";
    649     // TODO(yusukes): Handle this error in nicer way.
    650   }
    651 
    652   // Clear the existing maps.
    653   language_code_to_ids_.clear();
    654   id_to_language_code_.clear();
    655   id_to_descriptor_.clear();
    656   xkb_id_to_descriptor_.clear();
    657 
    658   for (size_t i = 0; i < supported_input_methods_->size(); ++i) {
    659     const InputMethodDescriptor& input_method =
    660         supported_input_methods_->at(i);
    661     const std::vector<std::string>& language_codes =
    662         input_method.language_codes();
    663     for (size_t i = 0; i < language_codes.size(); ++i) {
    664       language_code_to_ids_.insert(
    665           std::make_pair(language_codes[i], input_method.id()));
    666       // Remember the pairs.
    667       id_to_language_code_.insert(
    668           std::make_pair(input_method.id(), language_codes[i]));
    669     }
    670     id_to_descriptor_.insert(
    671         std::make_pair(input_method.id(), input_method));
    672     if (IsKeyboardLayout(input_method.id())) {
    673       xkb_id_to_descriptor_.insert(
    674           std::make_pair(input_method.GetPreferredKeyboardLayout(),
    675                          input_method));
    676     }
    677   }
    678 }
    679 
    680 }  // namespace input_method
    681 }  // namespace chromeos
    682