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 // For SetHardwareKeyboardLayoutForTesting.
21 #include "chromeos/ime/fake_input_method_delegate.h"
22 #include "chromeos/ime/input_method_delegate.h"
23 #include "chromeos/ime/input_method_whitelist.h"
24 // TODO(nona): move this header from this file.
25 #include "grit/generated_resources.h"
29 // A mapping from an input method id to a string for the language indicator. The
30 // mapping is necessary since some input methods belong to the same language.
31 // For example, both "xkb:us::eng" and "xkb:us:dvorak:eng" are for US English.
33 const char* input_method_id
;
34 const char* indicator_text
;
35 } kMappingFromIdToIndicatorText
[] = {
36 // To distinguish from "xkb:jp::jpn"
37 // TODO(nona): Make following variables configurable. http://crbug.com/232260.
38 { "_comp_ime_fpfbhcjppmaeaijcidgiibchfbnhbeljnacl_mozc_us", "\xe3\x81\x82" },
39 { "_comp_ime_fpfbhcjppmaeaijcidgiibchfbnhbeljnacl_mozc_jp", "\xe3\x81\x82" },
40 { "_comp_ime_bbaiamgfapehflhememkfglaehiobjnknacl_mozc_us", "\xe3\x81\x82" },
41 { "_comp_ime_bbaiamgfapehflhememkfglaehiobjnknacl_mozc_jp", "\xe3\x81\x82" },
42 // For simplified Chinese input methods
43 { "pinyin", "\xe6\x8b\xbc" }, // U+62FC
44 { "_comp_ime_cpgalbafkoofkjmaeonnfijgpfennjjnzh-t-i0-pinyin",
46 { "_comp_ime_gjaehgfemfahhmlgpdfknkhdnemmolopzh-t-i0-pinyin",
48 { "_comp_ime_gjaehgfemfahhmlgpdfknkhdnemmolopzh-t-i0-wubi-1986",
49 "\xe4\xba\x94" }, // U+4E94
50 { "pinyin-dv", "\xe6\x8b\xbc" },
51 // For traditional Chinese input methods
52 { "mozc-chewing", "\xe9\x85\xb7" }, // U+9177
53 { "_comp_ime_ekbifjdfhkmdeeajnolmgdlmkllopefizh-hant-t-i0-und",
54 "\xE6\xB3\xA8" }, // U+6CE8
55 { "_comp_ime_gjaehgfemfahhmlgpdfknkhdnemmolopzh-hant-t-i0-und",
56 "\xE6\xB3\xA8" }, // U+6CE8
57 { "m17n:zh:cangjie", "\xe5\x80\x89" }, // U+5009
58 { "_comp_ime_aeebooiibjahgpgmhkeocbeekccfknbjzh-hant-t-i0-cangjie-1987",
59 "\xe5\x80\x89" }, // U+5009
60 { "_comp_ime_gjaehgfemfahhmlgpdfknkhdnemmolopzh-hant-t-i0-cangjie-1987",
61 "\xe5\x80\x89" }, // U+5009
62 { "m17n:zh:quick", "\xe9\x80\x9f" }, // U+901F
63 // For Hangul input method.
64 { "mozc-hangul", "\xed\x95\x9c" }, // U+D55C
65 { "_comp_ime_bdgdidmhaijohebebipajioienkglgfohangul_2set", "\xed\x95\x9c" },
66 { "_comp_ime_bdgdidmhaijohebebipajioienkglgfohangul_3set390",
68 { "_comp_ime_bdgdidmhaijohebebipajioienkglgfohangul_3setfinal",
70 { "_comp_ime_bdgdidmhaijohebebipajioienkglgfohangul_3setnoshift",
72 { "_comp_ime_bdgdidmhaijohebebipajioienkglgfohangul_romaja", "\xed\x95\x9c" },
75 const size_t kMappingFromIdToIndicatorTextLen
=
76 ARRAYSIZE_UNSAFE(kMappingFromIdToIndicatorText
);
78 // A mapping from an input method id to a resource id for a
79 // medium length language indicator.
80 // For those languages that want to display a slightly longer text in the
81 // "Your input method has changed to..." bubble than in the status tray.
82 // If an entry is not found in this table the short name is used.
84 const char* input_method_id
;
85 const int resource_id
;
86 } kMappingImeIdToMediumLenNameResourceId
[] = {
87 { "m17n:zh:cangjie", IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_TRADITIONAL
},
88 { "m17n:zh:quick", IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_TRADITIONAL
},
89 { "mozc-chewing", IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_TRADITIONAL
},
90 { "mozc-hangul", IDS_LANGUAGES_MEDIUM_LEN_NAME_KOREAN
},
91 { "pinyin", IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_SIMPLIFIED
},
92 { "pinyin-dv", IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_SIMPLIFIED
},
93 { "_comp_ime_cpgalbafkoofkjmaeonnfijgpfennjjnzh-t-i0-pinyin",
94 IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_SIMPLIFIED
},
95 { "_comp_ime_gjaehgfemfahhmlgpdfknkhdnemmolopzh-t-i0-pinyin",
96 IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_SIMPLIFIED
},
97 { "_comp_ime_gjaehgfemfahhmlgpdfknkhdnemmolopzh-t-i0-wubi-1986",
98 IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_SIMPLIFIED
},
99 { "_comp_ime_ekbifjdfhkmdeeajnolmgdlmkllopefizh-hant-t-i0-und",
100 IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_TRADITIONAL
},
101 { "_comp_ime_gjaehgfemfahhmlgpdfknkhdnemmolopzh-hant-t-i0-und",
102 IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_TRADITIONAL
},
103 { "_comp_ime_aeebooiibjahgpgmhkeocbeekccfknbjzh-hant-t-i0-cangjie-1987",
104 IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_TRADITIONAL
},
105 { "_comp_ime_gjaehgfemfahhmlgpdfknkhdnemmolopzh-hant-t-i0-cangjie-1987",
106 IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_TRADITIONAL
},
108 const size_t kMappingImeIdToMediumLenNameResourceIdLen
=
109 ARRAYSIZE_UNSAFE(kMappingImeIdToMediumLenNameResourceId
);
111 // Due to asynchronous initialization of component extension manager,
112 // GetFirstLogingInputMethodIds may miss component extension IMEs. To enable
113 // component extension IME as the first loging input method, we have to prepare
114 // component extension IME IDs.
118 const char* input_method_id
;
119 } kDefaultInputMethodRecommendation
[] = {
120 { "ja", "us", "_comp_ime_fpfbhcjppmaeaijcidgiibchfbnhbeljnacl_mozc_us" },
121 { "ja", "jp", "_comp_ime_fpfbhcjppmaeaijcidgiibchfbnhbeljnacl_mozc_jp" },
122 { "zh-CN", "us", "_comp_ime_gjaehgfemfahhmlgpdfknkhdnemmolopzh-t-i0-pinyin" },
124 "_comp_ime_gjaehgfemfahhmlgpdfknkhdnemmolopzh-hant-t-i0-und" },
125 #if defined(OFFICIAL_BUILD)
126 { "th", "us", "_comp_ime_habcdindjejkmepknlhkkloncjcpcnbfvkd_th" },
127 { "vi", "us", "_comp_ime_habcdindjejkmepknlhkkloncjcpcnbfvkd_vi_tcvn" },
128 { "vi", "us", "_comp_ime_habcdindjejkmepknlhkkloncjcpcnbfvkd_vi_tcvn" },
130 { "th", "us", "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_th" },
131 { "vi", "us", "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_vi_tcvn" },
132 { "vi", "us", "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_vi_tcvn" },
136 // The map from xkb layout to the indicator text.
137 // Refer to crbug.com/349829.
138 const char* const kXkbIndicators
[][2] = {{"am", "AM"},
141 {"bg(phonetic)", "BG"},
146 {"ca(multix)", "CA"},
150 {"cz(qwerty)", "CS"},
159 {"gb(dvorak)", "DV"},
171 {"lv(apostrophe)", "LV"},
179 {"ru(phonetic)", "RU"},
186 {"us(altgr-intl)", "EXTD"},
187 {"us(colemak)", "CO"},
188 {"us(dvorak)", "DV"},
189 {"us(intl)", "INTL"}, };
191 // The old chinese input method ids for migration.
192 // See crbug.com/357384.
193 const char* kOldChineseExtensionIds
[] = {
194 "goedamlknlnjaengojinmfgpmdjmkooo",
195 "nmblnjkfdkabgdofidlkienfnnbjhnab",
196 "gjhclobljhjhgoebiipblnmdodbmpdgd"
199 // The new chinese input method id for migration.
200 // See crbug.com/357384.
201 const char* kNewChineseExtensionId
= "gjaehgfemfahhmlgpdfknkhdnemmolop";
203 const size_t kExtensionIdLen
= 32;
209 extern const char* kExtensionImePrefix
;
211 namespace input_method
{
215 const struct EnglishToResouceId
{
216 const char* english_string_from_ibus
;
218 } kEnglishToResourceIdArray
[] = {
219 // For ibus-mozc-hangul
220 { "Hanja mode", IDS_STATUSBAR_IME_KOREAN_HANJA_INPUT_MODE
},
221 { "Hangul mode", IDS_STATUSBAR_IME_KOREAN_HANGUL_INPUT_MODE
},
223 // For ibus-mozc-pinyin.
225 IDS_STATUSBAR_IME_CHINESE_PINYIN_TOGGLE_FULL_HALF
},
226 { "Full/Half width punctuation",
227 IDS_STATUSBAR_IME_CHINESE_PINYIN_TOGGLE_FULL_HALF_PUNCTUATION
},
228 // TODO(hsumita): Fixes a typo
229 { "Simplfied/Traditional Chinese",
230 IDS_STATUSBAR_IME_CHINESE_PINYIN_TOGGLE_S_T_CHINESE
},
232 IDS_STATUSBAR_IME_CHINESE_PINYIN_TOGGLE_CHINESE_ENGLISH
},
234 // For ibus-mozc-chewing.
236 IDS_STATUSBAR_IME_CHINESE_MOZC_CHEWING_ENGLISH_MODE
},
238 IDS_STATUSBAR_IME_CHINESE_MOZC_CHEWING_CHINESE_MODE
},
239 { "Full-width English",
240 IDS_STATUSBAR_IME_CHINESE_MOZC_CHEWING_FULL_WIDTH_ENGLISH_MODE
},
242 // For the "Languages and Input" dialog.
243 { "m17n:ar:kbd", IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD
},
244 { "m17n:hi:itrans", // also uses the "STANDARD_INPUT_METHOD" id.
245 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD
},
247 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_CHINESE_CANGJIE_INPUT_METHOD
},
249 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_CHINESE_QUICK_INPUT_METHOD
},
251 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_PERSIAN_ISIRI_2901_INPUT_METHOD
},
252 { "m17n:th:kesmanee",
253 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_THAI_KESMANEE_INPUT_METHOD
},
255 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_THAI_TIS820_INPUT_METHOD
},
256 { "m17n:th:pattachote",
257 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_THAI_PATTACHOTE_INPUT_METHOD
},
259 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_TCVN_INPUT_METHOD
},
261 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_TELEX_INPUT_METHOD
},
263 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_VIQR_INPUT_METHOD
},
265 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_VNI_INPUT_METHOD
},
267 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD
},
269 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD
},
271 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD
},
273 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD
},
274 { "m17n:ta:phonetic",
275 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_TAMIL_PHONETIC
},
276 { "m17n:ta:inscript",
277 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_TAMIL_INSCRIPT
},
279 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_TAMIL_TAMIL99
},
281 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_TAMIL_ITRANS
},
282 { "m17n:ta:typewriter",
283 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_TAMIL_TYPEWRITER
},
285 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD
},
287 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD
},
289 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD
},
292 IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_INPUT_METHOD
},
293 { "pinyin", IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_INPUT_METHOD
},
295 IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_DV_INPUT_METHOD
},
297 IDS_OPTIONS_SETTINGS_LANGUAGES_JAPANESE_HANDWRITING_INPUT_METHOD
},
298 { "mozc-hangul", IDS_OPTIONS_SETTINGS_LANGUAGES_KOREAN_INPUT_METHOD
},
300 // For ibus-xkb-layouts engine: third_party/ibus-xkb-layouts/files
301 { "xkb:jp::jpn", IDS_STATUSBAR_LAYOUT_JAPAN
},
302 { "xkb:si::slv", IDS_STATUSBAR_LAYOUT_SLOVENIA
},
303 { "xkb:de::ger", IDS_STATUSBAR_LAYOUT_GERMANY
},
304 { "xkb:de:neo:ger", IDS_STATUSBAR_LAYOUT_GERMANY_NEO2
},
305 { "xkb:it::ita", IDS_STATUSBAR_LAYOUT_ITALY
},
306 { "xkb:ee::est", IDS_STATUSBAR_LAYOUT_ESTONIA
},
307 { "xkb:hu::hun", IDS_STATUSBAR_LAYOUT_HUNGARY
},
308 { "xkb:pl::pol", IDS_STATUSBAR_LAYOUT_POLAND
},
309 { "xkb:dk::dan", IDS_STATUSBAR_LAYOUT_DENMARK
},
310 { "xkb:hr::scr", IDS_STATUSBAR_LAYOUT_CROATIA
},
311 { "xkb:br::por", IDS_STATUSBAR_LAYOUT_BRAZIL
},
312 { "xkb:rs::srp", IDS_STATUSBAR_LAYOUT_SERBIA
},
313 { "xkb:cz::cze", IDS_STATUSBAR_LAYOUT_CZECHIA
},
314 { "xkb:cz:qwerty:cze", IDS_STATUSBAR_LAYOUT_CZECHIA_QWERTY
},
315 { "xkb:us:dvorak:eng", IDS_STATUSBAR_LAYOUT_USA_DVORAK
},
316 { "xkb:us:colemak:eng", IDS_STATUSBAR_LAYOUT_USA_COLEMAK
},
317 { "xkb:ro::rum", IDS_STATUSBAR_LAYOUT_ROMANIA
},
318 { "xkb:us::eng", IDS_STATUSBAR_LAYOUT_USA
},
319 { "xkb:us:altgr-intl:eng", IDS_STATUSBAR_LAYOUT_USA_EXTENDED
},
320 { "xkb:us:intl:eng", IDS_STATUSBAR_LAYOUT_USA_INTERNATIONAL
},
321 { "xkb:lt::lit", IDS_STATUSBAR_LAYOUT_LITHUANIA
},
322 { "xkb:gb:extd:eng", IDS_STATUSBAR_LAYOUT_UNITED_KINGDOM
},
323 { "xkb:gb:dvorak:eng", IDS_STATUSBAR_LAYOUT_UNITED_KINGDOM_DVORAK
},
324 { "xkb:sk::slo", IDS_STATUSBAR_LAYOUT_SLOVAKIA
},
325 { "xkb:ru::rus", IDS_STATUSBAR_LAYOUT_RUSSIA
},
326 { "xkb:ru:phonetic:rus", IDS_STATUSBAR_LAYOUT_RUSSIA_PHONETIC
},
327 { "xkb:gr::gre", IDS_STATUSBAR_LAYOUT_GREECE
},
328 { "xkb:be::fra", IDS_STATUSBAR_LAYOUT_BELGIUM
},
329 { "xkb:be::ger", IDS_STATUSBAR_LAYOUT_BELGIUM
},
330 { "xkb:be::nld", IDS_STATUSBAR_LAYOUT_BELGIUM
},
331 { "xkb:bg::bul", IDS_STATUSBAR_LAYOUT_BULGARIA
},
332 { "xkb:bg:phonetic:bul", IDS_STATUSBAR_LAYOUT_BULGARIA_PHONETIC
},
333 { "xkb:ch::ger", IDS_STATUSBAR_LAYOUT_SWITZERLAND
},
334 { "xkb:ch:fr:fra", IDS_STATUSBAR_LAYOUT_SWITZERLAND_FRENCH
},
335 { "xkb:tr::tur", IDS_STATUSBAR_LAYOUT_TURKEY
},
336 { "xkb:pt::por", IDS_STATUSBAR_LAYOUT_PORTUGAL
},
337 { "xkb:es::spa", IDS_STATUSBAR_LAYOUT_SPAIN
},
338 { "xkb:fi::fin", IDS_STATUSBAR_LAYOUT_FINLAND
},
339 { "xkb:ua::ukr", IDS_STATUSBAR_LAYOUT_UKRAINE
},
340 { "xkb:es:cat:cat", IDS_STATUSBAR_LAYOUT_SPAIN_CATALAN
},
341 { "xkb:fr::fra", IDS_STATUSBAR_LAYOUT_FRANCE
},
342 { "xkb:no::nob", IDS_STATUSBAR_LAYOUT_NORWAY
},
343 { "xkb:se::swe", IDS_STATUSBAR_LAYOUT_SWEDEN
},
344 { "xkb:nl::nld", IDS_STATUSBAR_LAYOUT_NETHERLANDS
},
345 { "xkb:latam::spa", IDS_STATUSBAR_LAYOUT_LATIN_AMERICAN
},
346 { "xkb:lv:apostrophe:lav", IDS_STATUSBAR_LAYOUT_LATVIA
},
347 { "xkb:ca::fra", IDS_STATUSBAR_LAYOUT_CANADA
},
348 { "xkb:ca:eng:eng", IDS_STATUSBAR_LAYOUT_CANADA_ENGLISH
},
349 { "xkb:il::heb", IDS_STATUSBAR_LAYOUT_ISRAEL
},
350 { "xkb:kr:kr104:kor", IDS_STATUSBAR_LAYOUT_KOREA_104
},
351 { "xkb:is::ice", IDS_STATUSBAR_LAYOUT_ICELANDIC
},
352 { "xkb:ca:multix:fra", IDS_STATUSBAR_LAYOUT_CANADIAN_MULTILINGUAL
},
353 { "xkb:by::bel", IDS_STATUSBAR_LAYOUT_BELARUSIAN
},
354 { "xkb:am:phonetic:arm", IDS_STATUSBAR_LAYOUT_ARMENIAN_PHONETIC
},
355 { "xkb:ge::geo", IDS_STATUSBAR_LAYOUT_GEORGIAN
},
356 { "xkb:mn::mon", IDS_STATUSBAR_LAYOUT_MONGOLIAN
},
358 { "english-m", IDS_STATUSBAR_LAYOUT_USA_MYSTERY
},
360 const size_t kEnglishToResourceIdArraySize
=
361 arraysize(kEnglishToResourceIdArray
);
365 InputMethodUtil::InputMethodUtil(
366 InputMethodDelegate
* delegate
,
367 scoped_ptr
<InputMethodDescriptors
> supported_input_methods
)
368 : supported_input_methods_(supported_input_methods
.Pass()),
369 delegate_(delegate
) {
370 // Makes sure the supported input methods at least have the fallback ime.
371 // So that it won't cause massive test failures.
372 if (supported_input_methods_
->empty())
373 supported_input_methods_
->push_back(GetFallbackInputMethodDescriptor());
375 ReloadInternalMaps();
377 // Initialize a map from English string to Chrome string resource ID as well.
378 for (size_t i
= 0; i
< kEnglishToResourceIdArraySize
; ++i
) {
379 const EnglishToResouceId
& map_entry
= kEnglishToResourceIdArray
[i
];
380 const bool result
= english_to_resource_id_
.insert(std::make_pair(
381 map_entry
.english_string_from_ibus
, map_entry
.resource_id
)).second
;
382 DCHECK(result
) << "Duplicated string is found: "
383 << map_entry
.english_string_from_ibus
;
386 // Initialize the map from xkb layout to indicator text.
387 for (size_t i
= 0; i
< arraysize(kXkbIndicators
); ++i
) {
388 xkb_layout_to_indicator_
[kXkbIndicators
[i
][0]] = kXkbIndicators
[i
][1];
392 InputMethodUtil::~InputMethodUtil() {
395 bool InputMethodUtil::TranslateStringInternal(
396 const std::string
& english_string
, base::string16
*out_string
) const {
398 HashType::const_iterator iter
= english_to_resource_id_
.find(english_string
);
399 if (iter
== english_to_resource_id_
.end()) {
400 // TODO(yusukes): Write Autotest which checks if all display names and all
401 // property names for supported input methods are listed in the resource
402 // ID array (crosbug.com/4572).
403 LOG(ERROR
) << "Resource ID is not found for: " << english_string
;
407 *out_string
= delegate_
->GetLocalizedString(iter
->second
);
411 base::string16
InputMethodUtil::TranslateString(
412 const std::string
& english_string
) const {
413 base::string16 localized_string
;
414 if (TranslateStringInternal(english_string
, &localized_string
)) {
415 return localized_string
;
417 return base::UTF8ToUTF16(english_string
);
420 bool InputMethodUtil::IsValidInputMethodId(
421 const std::string
& input_method_id
) const {
422 // We can't check the component extension is whilelisted or not here because
423 // it might not be initialized.
424 return GetInputMethodDescriptorFromId(input_method_id
) != NULL
||
425 extension_ime_util::IsComponentExtensionIME(input_method_id
);
429 bool InputMethodUtil::IsKeyboardLayout(const std::string
& input_method_id
) {
430 return StartsWithASCII(input_method_id
, "xkb:", false) ||
431 extension_ime_util::IsKeyboardLayoutExtension(input_method_id
);
434 std::string
InputMethodUtil::GetLanguageCodeFromInputMethodId(
435 const std::string
& input_method_id
) const {
436 // The code should be compatible with one of codes used for UI languages,
437 // defined in app/l10_util.cc.
438 const char kDefaultLanguageCode
[] = "en-US";
439 std::map
<std::string
, std::string
>::const_iterator iter
440 = id_to_language_code_
.find(input_method_id
);
441 return (iter
== id_to_language_code_
.end()) ?
442 // Returning |kDefaultLanguageCode| here is not for Chrome OS but for
443 // Ubuntu where the ibus-xkb-layouts engine could be missing.
444 kDefaultLanguageCode
: iter
->second
;
447 std::string
InputMethodUtil::GetKeyboardLayoutName(
448 const std::string
& input_method_id
) const {
449 InputMethodIdToDescriptorMap::const_iterator iter
450 = id_to_descriptor_
.find(input_method_id
);
451 return (iter
== id_to_descriptor_
.end()) ?
452 "" : iter
->second
.GetPreferredKeyboardLayout();
455 std::string
InputMethodUtil::GetInputMethodDisplayNameFromId(
456 const std::string
& input_method_id
) const {
457 base::string16 display_name
;
458 if (!extension_ime_util::IsExtensionIME(input_method_id
) &&
459 TranslateStringInternal(input_method_id
, &display_name
)) {
460 return base::UTF16ToUTF8(display_name
);
462 // Return an empty string if the display name is not found.
466 base::string16
InputMethodUtil::GetInputMethodShortName(
467 const InputMethodDescriptor
& input_method
) const {
468 // For the status area, we use two-letter, upper-case language code like
471 // Use the indicator string if set.
472 if (!input_method
.indicator().empty()) {
473 return base::UTF8ToUTF16(input_method
.indicator());
477 // Check special cases first.
478 for (size_t i
= 0; i
< kMappingFromIdToIndicatorTextLen
; ++i
) {
479 if (kMappingFromIdToIndicatorText
[i
].input_method_id
==
481 text
= base::UTF8ToUTF16(kMappingFromIdToIndicatorText
[i
].indicator_text
);
486 // Display the keyboard layout name when using a keyboard layout.
487 if (text
.empty() && IsKeyboardLayout(input_method
.id())) {
488 std::map
<std::string
, std::string
>::const_iterator it
=
489 xkb_layout_to_indicator_
.find(GetKeyboardLayoutName(input_method
.id()));
490 if (it
!= xkb_layout_to_indicator_
.end())
491 text
= base::UTF8ToUTF16(it
->second
);
494 // TODO(yusukes): Some languages have two or more input methods. For example,
495 // Thai has 3, Vietnamese has 4. If these input methods could be activated at
496 // the same time, we should do either of the following:
497 // (1) Add mappings to |kMappingFromIdToIndicatorText|
498 // (2) Add suffix (1, 2, ...) to |text| when ambiguous.
501 const size_t kMaxLanguageNameLen
= 2;
502 DCHECK(!input_method
.language_codes().empty());
503 const std::string language_code
= input_method
.language_codes().at(0);
504 text
= StringToUpperASCII(base::UTF8ToUTF16(language_code
)).substr(
505 0, kMaxLanguageNameLen
);
507 DCHECK(!text
.empty()) << input_method
.id();
511 base::string16
InputMethodUtil::GetInputMethodMediumName(
512 const InputMethodDescriptor
& input_method
) const {
513 // For the "Your input method has changed to..." bubble. In most cases
514 // it uses the same name as the short name, unless found in a table
515 // for medium length names.
516 for (size_t i
= 0; i
< kMappingImeIdToMediumLenNameResourceIdLen
; ++i
) {
517 if (kMappingImeIdToMediumLenNameResourceId
[i
].input_method_id
==
519 return delegate_
->GetLocalizedString(
520 kMappingImeIdToMediumLenNameResourceId
[i
].resource_id
);
523 return GetInputMethodShortName(input_method
);
526 base::string16
InputMethodUtil::GetInputMethodLongName(
527 const InputMethodDescriptor
& input_method
) const {
528 if (!input_method
.name().empty() && !IsKeyboardLayout(input_method
.id())) {
529 // If the descriptor has a name, use it.
530 return base::UTF8ToUTF16(input_method
.name());
533 // We don't show language here. Name of keyboard layout or input method
534 // usually imply (or explicitly include) its language.
536 // Special case for German, French and Dutch: these languages have multiple
537 // keyboard layouts and share the same layout of keyboard (Belgian). We need
538 // to show explicitly the language for the layout. For Arabic, Amharic, and
539 // Indic languages: they share "Standard Input Method".
540 const base::string16 standard_input_method_text
=
541 delegate_
->GetLocalizedString(
542 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD
);
543 DCHECK(!input_method
.language_codes().empty());
544 const std::string language_code
= input_method
.language_codes().at(0);
546 // Before translate the string, convert the input method id to legacy xkb id
548 // TODO(shuchen): the GetInputMethodLongName() method should be removed when
549 // finish the wrapping of xkb to extension.
550 base::string16 text
= TranslateString(
551 extension_ime_util::MaybeGetLegacyXkbId(input_method
.id()));
552 if (text
== standard_input_method_text
||
553 language_code
== "de" ||
554 language_code
== "fr" ||
555 language_code
== "nl") {
556 const base::string16 language_name
= delegate_
->GetDisplayLanguageName(
559 text
= language_name
+ base::UTF8ToUTF16(" - ") + text
;
562 DCHECK(!text
.empty());
566 const InputMethodDescriptor
* InputMethodUtil::GetInputMethodDescriptorFromId(
567 const std::string
& input_method_id
) const {
568 InputMethodIdToDescriptorMap::const_iterator iter
=
569 id_to_descriptor_
.find(input_method_id
);
570 if (iter
== id_to_descriptor_
.end())
572 return &(iter
->second
);
575 bool InputMethodUtil::GetInputMethodIdsFromLanguageCode(
576 const std::string
& normalized_language_code
,
577 InputMethodType type
,
578 std::vector
<std::string
>* out_input_method_ids
) const {
579 return GetInputMethodIdsFromLanguageCodeInternal(
580 language_code_to_ids_
,
581 normalized_language_code
, type
, out_input_method_ids
);
584 bool InputMethodUtil::GetInputMethodIdsFromLanguageCodeInternal(
585 const std::multimap
<std::string
, std::string
>& language_code_to_ids
,
586 const std::string
& normalized_language_code
,
587 InputMethodType type
,
588 std::vector
<std::string
>* out_input_method_ids
) const {
589 DCHECK(out_input_method_ids
);
590 out_input_method_ids
->clear();
593 std::pair
<LanguageCodeToIdsMap::const_iterator
,
594 LanguageCodeToIdsMap::const_iterator
> range
=
595 language_code_to_ids
.equal_range(normalized_language_code
);
596 for (LanguageCodeToIdsMap::const_iterator iter
= range
.first
;
597 iter
!= range
.second
; ++iter
) {
598 const std::string
& input_method_id
= iter
->second
;
599 if ((type
== kAllInputMethods
) || IsKeyboardLayout(input_method_id
)) {
600 out_input_method_ids
->push_back(input_method_id
);
604 if ((type
== kAllInputMethods
) && !result
) {
605 DVLOG(1) << "Unknown language code: " << normalized_language_code
;
610 void InputMethodUtil::GetFirstLoginInputMethodIds(
611 const std::string
& language_code
,
612 const InputMethodDescriptor
& current_input_method
,
613 std::vector
<std::string
>* out_input_method_ids
) const {
614 out_input_method_ids
->clear();
616 // First, add the current keyboard layout (one used on the login screen).
617 out_input_method_ids
->push_back(current_input_method
.id());
619 const std::string current_layout
620 = current_input_method
.GetPreferredKeyboardLayout();
621 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(kDefaultInputMethodRecommendation
);
623 if (kDefaultInputMethodRecommendation
[i
].locale
== language_code
&&
624 kDefaultInputMethodRecommendation
[i
].layout
== current_layout
) {
625 out_input_method_ids
->push_back(
626 kDefaultInputMethodRecommendation
[i
].input_method_id
);
631 // Second, find the most popular input method associated with the
632 // current UI language. The input method IDs returned from
633 // GetInputMethodIdsFromLanguageCode() are sorted by popularity, hence
634 // our basic strategy is to pick the first one, but it's a bit more
635 // complicated as shown below.
636 std::string most_popular_id
;
637 std::vector
<std::string
> input_method_ids
;
638 // This returns the input methods sorted by popularity.
639 GetInputMethodIdsFromLanguageCode(
640 language_code
, kAllInputMethods
, &input_method_ids
);
641 for (size_t i
= 0; i
< input_method_ids
.size(); ++i
) {
642 const std::string
& input_method_id
= input_method_ids
[i
];
643 // Pick the first one.
644 if (most_popular_id
.empty())
645 most_popular_id
= input_method_id
;
647 // Check if there is one that matches the current keyboard layout, but
648 // not the current keyboard itself. This is useful if there are
649 // multiple keyboard layout choices for one input method. For
650 // instance, Mozc provides three choices: mozc (US keyboard), mozc-jp
651 // (JP keyboard), mozc-dv (Dvorak).
652 const InputMethodDescriptor
* descriptor
=
653 GetInputMethodDescriptorFromId(input_method_id
);
655 descriptor
->id() != current_input_method
.id() &&
656 descriptor
->GetPreferredKeyboardLayout() ==
657 current_input_method
.GetPreferredKeyboardLayout()) {
658 most_popular_id
= input_method_id
;
662 // Add the most popular input method ID, if it's different from the
663 // current input method.
664 if (most_popular_id
!= current_input_method
.id() &&
665 // TODO(yusukes): Remove this hack when we remove the "english-m" IME.
666 most_popular_id
!= "english-m") {
667 out_input_method_ids
->push_back(most_popular_id
);
671 void InputMethodUtil::GetLanguageCodesFromInputMethodIds(
672 const std::vector
<std::string
>& input_method_ids
,
673 std::vector
<std::string
>* out_language_codes
) const {
674 out_language_codes
->clear();
676 for (size_t i
= 0; i
< input_method_ids
.size(); ++i
) {
677 const std::string
& input_method_id
= input_method_ids
[i
];
678 const InputMethodDescriptor
* input_method
=
679 GetInputMethodDescriptorFromId(input_method_id
);
681 DVLOG(1) << "Unknown input method ID: " << input_method_ids
[i
];
684 DCHECK(!input_method
->language_codes().empty());
685 const std::string language_code
= input_method
->language_codes().at(0);
686 // Add it if it's not already present.
687 if (std::count(out_language_codes
->begin(), out_language_codes
->end(),
688 language_code
) == 0) {
689 out_language_codes
->push_back(language_code
);
694 std::string
InputMethodUtil::GetLanguageDefaultInputMethodId(
695 const std::string
& language_code
) {
696 std::vector
<std::string
> candidates
;
697 GetInputMethodIdsFromLanguageCode(
698 language_code
, input_method::kKeyboardLayoutsOnly
, &candidates
);
699 if (candidates
.size())
700 return candidates
.front();
702 return std::string();
705 bool InputMethodUtil::MigrateXkbInputMethods(
706 std::vector
<std::string
>* input_method_ids
) {
707 bool rewritten
= false;
708 std::vector
<std::string
>& ids
= *input_method_ids
;
709 for (size_t i
= 0; i
< ids
.size(); ++i
) {
711 extension_ime_util::GetInputMethodIDByKeyboardLayout(ids
[i
]);
712 // Migrates the old chinese ime id to new ones.
713 // TODO(shuchen): Change the function name to MigrateInputMethods,
714 // and create an abstract layer to map a comprehensive input method id to
715 // the real extension based input method id.
716 // e.g. "zh-t-i0-pinyin" maps to
717 // "_comp_id_gjaehgfemfahhmlgpdfknkhdnemmolopzh-t-i0-pinyin".
718 // See crbug.com/358083.
719 for (size_t j
= 0; j
< arraysize(kOldChineseExtensionIds
); ++j
) {
720 size_t pos
= id
.find(kOldChineseExtensionIds
[j
]);
721 if (pos
!= std::string::npos
) {
722 id
.replace(pos
, kExtensionIdLen
, kNewChineseExtensionId
);
732 // Removes the duplicates.
733 std::vector
<std::string
> new_ids
;
734 for (size_t i
= 0; i
< ids
.size(); ++i
) {
735 if (std::find(new_ids
.begin(), new_ids
.end(), ids
[i
]) == new_ids
.end())
736 new_ids
.push_back(ids
[i
]);
743 void InputMethodUtil::UpdateHardwareLayoutCache() {
744 DCHECK(thread_checker_
.CalledOnValidThread());
745 hardware_layouts_
.clear();
746 hardware_login_layouts_
.clear();
747 if (cached_hardware_layouts_
.empty())
748 Tokenize(delegate_
->GetHardwareKeyboardLayouts(), ",",
749 &cached_hardware_layouts_
);
750 hardware_layouts_
= cached_hardware_layouts_
;
751 MigrateXkbInputMethods(&hardware_layouts_
);
753 for (size_t i
= 0; i
< hardware_layouts_
.size(); ++i
) {
754 if (IsLoginKeyboard(hardware_layouts_
[i
]))
755 hardware_login_layouts_
.push_back(hardware_layouts_
[i
]);
757 if (hardware_layouts_
.empty()) {
758 // This is totally fine if it's empty. The hardware keyboard layout is
759 // not stored if startup_manifest.json (OEM customization data) is not
760 // present (ex. Cr48 doen't have that file).
761 hardware_layouts_
.push_back(GetFallbackInputMethodDescriptor().id());
764 if (hardware_login_layouts_
.empty())
765 hardware_login_layouts_
.push_back(GetFallbackInputMethodDescriptor().id());
768 void InputMethodUtil::SetHardwareKeyboardLayoutForTesting(
769 const std::string
& layout
) {
770 delegate_
->SetHardwareKeyboardLayoutForTesting(layout
);
771 cached_hardware_layouts_
.clear();
772 UpdateHardwareLayoutCache();
775 const std::vector
<std::string
>&
776 InputMethodUtil::GetHardwareInputMethodIds() {
777 DCHECK(thread_checker_
.CalledOnValidThread());
778 UpdateHardwareLayoutCache();
779 return hardware_layouts_
;
782 const std::vector
<std::string
>&
783 InputMethodUtil::GetHardwareLoginInputMethodIds() {
784 DCHECK(thread_checker_
.CalledOnValidThread());
785 UpdateHardwareLayoutCache();
786 return hardware_login_layouts_
;
789 bool InputMethodUtil::IsLoginKeyboard(const std::string
& input_method_id
)
791 const InputMethodDescriptor
* ime
=
792 GetInputMethodDescriptorFromId(input_method_id
);
793 return ime
? ime
->is_login_keyboard() : false;
796 void InputMethodUtil::SetComponentExtensions(
797 const InputMethodDescriptors
& imes
) {
798 for (size_t i
= 0; i
< imes
.size(); ++i
) {
799 const InputMethodDescriptor
& input_method
= imes
[i
];
800 DCHECK(!input_method
.language_codes().empty());
801 const std::vector
<std::string
>& language_codes
=
802 input_method
.language_codes();
803 id_to_language_code_
[input_method
.id()] = language_codes
[0];
804 id_to_descriptor_
[input_method
.id()] = input_method
;
806 typedef LanguageCodeToIdsMap::const_iterator It
;
807 for (size_t j
= 0; j
< language_codes
.size(); ++j
) {
808 std::pair
<It
, It
> range
=
809 language_code_to_ids_
.equal_range(language_codes
[j
]);
811 for (; it
!= range
.second
; ++it
) {
812 if (it
->second
== input_method
.id())
815 if (it
== range
.second
)
816 language_code_to_ids_
.insert(
817 std::make_pair(language_codes
[j
], input_method
.id()));
822 void InputMethodUtil::InitXkbInputMethodsForTesting() {
823 cached_hardware_layouts_
.clear();
824 if (!extension_ime_util::UseWrappedExtensionKeyboardLayouts())
826 scoped_ptr
<InputMethodDescriptors
> original_imes
=
827 InputMethodWhitelist().GetSupportedInputMethods();
828 InputMethodDescriptors whitelist_imes
;
829 for (size_t i
= 0; i
< original_imes
->size(); ++i
) {
830 const InputMethodDescriptor
& ime
= (*original_imes
)[i
];
831 whitelist_imes
.push_back(InputMethodDescriptor(
832 extension_ime_util::GetInputMethodIDByKeyboardLayout(ime
.id()),
835 ime
.keyboard_layouts(),
836 ime
.language_codes(),
837 ime
.is_login_keyboard(),
838 ime
.options_page_url(),
839 ime
.input_view_url()));
841 SetComponentExtensions(whitelist_imes
);
844 InputMethodDescriptor
InputMethodUtil::GetFallbackInputMethodDescriptor() {
845 std::vector
<std::string
> layouts
;
846 layouts
.push_back("us");
847 std::vector
<std::string
> languages
;
848 languages
.push_back("en-US");
849 return InputMethodDescriptor(
850 extension_ime_util::GetInputMethodIDByKeyboardLayout("xkb:us::eng"),
855 true, // login keyboard.
856 GURL(), // options page, not available.
857 GURL()); // input view page, not available.
860 void InputMethodUtil::ReloadInternalMaps() {
861 if (supported_input_methods_
->size() <= 1) {
862 DVLOG(1) << "GetSupportedInputMethods returned a fallback ID";
863 // TODO(yusukes): Handle this error in nicer way.
866 // Clear the existing maps.
867 language_code_to_ids_
.clear();
868 id_to_language_code_
.clear();
869 id_to_descriptor_
.clear();
870 xkb_id_to_descriptor_
.clear();
872 for (size_t i
= 0; i
< supported_input_methods_
->size(); ++i
) {
873 const InputMethodDescriptor
& input_method
=
874 supported_input_methods_
->at(i
);
875 const std::vector
<std::string
>& language_codes
=
876 input_method
.language_codes();
877 for (size_t i
= 0; i
< language_codes
.size(); ++i
) {
878 language_code_to_ids_
.insert(
879 std::make_pair(language_codes
[i
], input_method
.id()));
880 // Remember the pairs.
881 id_to_language_code_
.insert(
882 std::make_pair(input_method
.id(), language_codes
[i
]));
884 id_to_descriptor_
.insert(
885 std::make_pair(input_method
.id(), input_method
));
886 if (IsKeyboardLayout(input_method
.id())) {
887 xkb_id_to_descriptor_
.insert(
888 std::make_pair(input_method
.GetPreferredKeyboardLayout(),
894 } // namespace input_method
895 } // namespace chromeos