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.
5 #include "chrome/browser/chromeos/input_method/input_method_util.h"
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"
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.
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",
54 { "_comp_ime_nmblnjkfdkabgdofidlkienfnnbjhnabzh-t-i0-pinyin",
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",
76 { "_comp_ime_bdgdidmhaijohebebipajioienkglgfohangul_3setfinal",
78 { "_comp_ime_bdgdidmhaijohebebipajioienkglgfohangul_3setnoshift",
80 { "_comp_ime_bdgdidmhaijohebebipajioienkglgfohangul_romaja", "\xed\x95\x9c" },
83 const size_t kMappingFromIdToIndicatorTextLen
=
84 ARRAYSIZE_UNSAFE(kMappingFromIdToIndicatorText
);
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.
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
},
116 const size_t kMappingImeIdToMediumLenNameResourceIdLen
=
117 ARRAYSIZE_UNSAFE(kMappingImeIdToMediumLenNameResourceId
);
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.
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" },
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" },
142 extern const char* kExtensionImePrefix
;
144 namespace input_method
{
148 const struct EnglishToResouceId
{
149 const char* english_string_from_ibus
;
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
},
156 // For ibus-mozc-pinyin.
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
},
165 IDS_STATUSBAR_IME_CHINESE_PINYIN_TOGGLE_CHINESE_ENGLISH
},
167 // For ibus-mozc-chewing.
169 IDS_STATUSBAR_IME_CHINESE_MOZC_CHEWING_ENGLISH_MODE
},
171 IDS_STATUSBAR_IME_CHINESE_MOZC_CHEWING_CHINESE_MODE
},
172 { "Full-width English",
173 IDS_STATUSBAR_IME_CHINESE_MOZC_CHEWING_FULL_WIDTH_ENGLISH_MODE
},
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
},
180 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_CHINESE_CANGJIE_INPUT_METHOD
},
182 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_CHINESE_QUICK_INPUT_METHOD
},
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
},
188 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_THAI_TIS820_INPUT_METHOD
},
189 { "m17n:th:pattachote",
190 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_THAI_PATTACHOTE_INPUT_METHOD
},
192 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_TCVN_INPUT_METHOD
},
194 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_TELEX_INPUT_METHOD
},
196 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_VIQR_INPUT_METHOD
},
198 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_VNI_INPUT_METHOD
},
200 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD
},
202 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD
},
204 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD
},
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
},
212 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_TAMIL_TAMIL99
},
214 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_TAMIL_ITRANS
},
215 { "m17n:ta:typewriter",
216 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_TAMIL_TYPEWRITER
},
218 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD
},
220 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD
},
222 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD
},
225 IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_INPUT_METHOD
},
226 { "pinyin", IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_INPUT_METHOD
},
228 IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_DV_INPUT_METHOD
},
230 IDS_OPTIONS_SETTINGS_LANGUAGES_JAPANESE_HANDWRITING_INPUT_METHOD
},
231 { "mozc-hangul", IDS_OPTIONS_SETTINGS_LANGUAGES_KOREAN_INPUT_METHOD
},
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
},
291 { "english-m", IDS_STATUSBAR_LAYOUT_USA_MYSTERY
},
293 const size_t kEnglishToResourceIdArraySize
=
294 arraysize(kEnglishToResourceIdArray
);
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();
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
;
315 InputMethodUtil::~InputMethodUtil() {
318 bool InputMethodUtil::TranslateStringInternal(
319 const std::string
& english_string
, base::string16
*out_string
) const {
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
;
330 *out_string
= delegate_
->GetLocalizedString(iter
->second
);
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
;
340 return base::UTF8ToUTF16(english_string
);
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
);
352 bool InputMethodUtil::IsKeyboardLayout(const std::string
& input_method_id
) {
353 const bool kCaseInsensitive
= false;
354 return StartsWithASCII(input_method_id
, "xkb:", kCaseInsensitive
);
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
;
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();
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 base::UTF16ToUTF8(display_name
);
385 // Return an empty string if the display name is not found.
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
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
= base::UTF8ToUTF16(kMappingFromIdToIndicatorText
[i
].indicator_text
);
403 // Display the keyboard layout name when using a keyboard layout.
405 IsKeyboardLayout(input_method
.id())) {
406 const size_t kMaxKeyboardLayoutNameLen
= 2;
407 const base::string16 keyboard_layout
=
408 base::UTF8ToUTF16(GetKeyboardLayoutName(input_method
.id()));
409 text
= StringToUpperASCII(keyboard_layout
).substr(
410 0, kMaxKeyboardLayoutNameLen
);
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.
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(base::UTF8ToUTF16(language_code
)).substr(
424 0, kMaxLanguageNameLen
);
426 DCHECK(!text
.empty());
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
==
438 return delegate_
->GetLocalizedString(
439 kMappingImeIdToMediumLenNameResourceId
[i
].resource_id
);
442 return GetInputMethodShortName(input_method
);
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 base::UTF8ToUTF16(input_method
.name());
452 // We don't show language here. Name of keyboard layout or input method
453 // usually imply (or explicitly include) its language.
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);
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(
473 text
= language_name
+ base::UTF8ToUTF16(" - ") + text
;
476 DCHECK(!text
.empty());
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
);
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
);
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();
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
);
516 if ((type
== kAllInputMethods
) && !result
) {
517 DVLOG(1) << "Unknown language code: " << normalized_language_code
;
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();
528 // First, add the current keyboard layout (one used on the login screen).
529 out_input_method_ids
->push_back(current_input_method
.id());
531 const std::string current_layout
532 = current_input_method
.GetPreferredKeyboardLayout();
533 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(kDefaultInputMethodRecommendation
);
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
);
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
;
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
);
567 descriptor
->id() != current_input_method
.id() &&
568 descriptor
->GetPreferredKeyboardLayout() ==
569 current_input_method
.GetPreferredKeyboardLayout()) {
570 most_popular_id
= input_method_id
;
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
);
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();
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
);
593 DVLOG(1) << "Unknown input method ID: " << input_method_ids
[i
];
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
);
606 std::string
InputMethodUtil::GetHardwareInputMethodId() const {
607 const std::string input_method_id
= delegate_
->GetHardwareKeyboardLayout();
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();
615 return input_method_id
;
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
));
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",
641 true, // login keyboard.
642 GURL(), // options page, not available.
643 GURL()); // input view page, not available.
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.
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();
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
]));
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(),
680 } // namespace input_method
681 } // namespace chromeos