• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
InputMethodUtil(InputMethodDelegate * delegate,scoped_ptr<InputMethodDescriptors> supported_input_methods)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 
~InputMethodUtil()315 InputMethodUtil::~InputMethodUtil() {
316 }
317 
TranslateStringInternal(const std::string & english_string,base::string16 * out_string) const318 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 
TranslateString(const std::string & english_string) const334 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 
IsValidInputMethodId(const std::string & input_method_id) const343 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
IsKeyboardLayout(const std::string & 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);
355 }
356 
GetLanguageCodeFromInputMethodId(const std::string & input_method_id) const357 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 
GetKeyboardLayoutName(const std::string & input_method_id) const370 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 
GetInputMethodDisplayNameFromId(const std::string & input_method_id) const378 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 
GetInputMethodShortName(const InputMethodDescriptor & input_method) const389 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 
GetInputMethodMediumName(const InputMethodDescriptor & input_method) const430 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 
GetInputMethodLongName(const InputMethodDescriptor & input_method) const445 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 
GetInputMethodDescriptorFromId(const std::string & input_method_id) const480 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 
GetInputMethodIdsFromLanguageCode(const std::string & normalized_language_code,InputMethodType type,std::vector<std::string> * out_input_method_ids) const487 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 
GetInputMethodIdsFromLanguageCodeInternal(const std::multimap<std::string,std::string> & language_code_to_ids,const std::string & normalized_language_code,InputMethodType type,std::vector<std::string> * out_input_method_ids) const496 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 
GetFirstLoginInputMethodIds(const std::string & language_code,const InputMethodDescriptor & current_input_method,std::vector<std::string> * out_input_method_ids) const522 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 
GetLanguageCodesFromInputMethodIds(const std::vector<std::string> & input_method_ids,std::vector<std::string> * out_language_codes) const583 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 
GetHardwareInputMethodId() const606 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 
SetComponentExtensions(const InputMethodDescriptors & imes)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 
GetFallbackInputMethodDescriptor()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 
ReloadInternalMaps()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