• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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 "unicode/uloc.h"
13 
14 #include "base/basictypes.h"
15 #include "base/hash_tables.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/memory/singleton.h"
18 #include "base/string_split.h"
19 #include "base/string_util.h"
20 #include "base/utf_string_conversions.h"
21 #include "chrome/browser/browser_process.h"
22 #include "chrome/browser/chromeos/cros/cros_library.h"
23 #include "chrome/browser/chromeos/language_preferences.h"
24 #include "chrome/browser/prefs/pref_service.h"
25 #include "chrome/common/pref_names.h"
26 #include "grit/generated_resources.h"
27 #include "ui/base/l10n/l10n_util.h"
28 #include "ui/base/l10n/l10n_util_collator.h"
29 
30 namespace {
31 
32 // Map from language code to associated input method IDs, etc.
33 typedef std::multimap<std::string, std::string> LanguageCodeToIdsMap;
34 // Map from input method ID to associated input method descriptor.
35 typedef std::map<std::string, chromeos::InputMethodDescriptor>
36     InputMethodIdToDescriptorMap;
37 // Map from layout name to associated overlay ID
38 typedef std::map<std::string, std::string> InputMethodNameToOverlayIdMap;
39 
40 struct IdMaps {
41   scoped_ptr<LanguageCodeToIdsMap> language_code_to_ids;
42   scoped_ptr<std::map<std::string, std::string> > id_to_language_code;
43   scoped_ptr<InputMethodIdToDescriptorMap> id_to_descriptor;
44   scoped_ptr<std::map<std::string, std::string> > name_to_overlay_id;
45 
46   // Returns the singleton instance.
GetInstance__anon171a85370111::IdMaps47   static IdMaps* GetInstance() {
48     return Singleton<IdMaps>::get();
49   }
50 
ReloadMaps__anon171a85370111::IdMaps51   void ReloadMaps() {
52     chromeos::InputMethodLibrary* library =
53         chromeos::CrosLibrary::Get()->GetInputMethodLibrary();
54     scoped_ptr<chromeos::InputMethodDescriptors> supported_input_methods(
55         library->GetSupportedInputMethods());
56     if (supported_input_methods->size() <= 1) {
57       LOG(ERROR) << "GetSupportedInputMethods returned a fallback ID";
58       // TODO(yusukes): Handle this error in nicer way.
59     }
60 
61     // Clear the existing maps.
62     language_code_to_ids->clear();
63     id_to_language_code->clear();
64     id_to_descriptor->clear();
65     name_to_overlay_id->clear();
66 
67     for (size_t i = 0; i < supported_input_methods->size(); ++i) {
68       const chromeos::InputMethodDescriptor& input_method =
69           supported_input_methods->at(i);
70       const std::string language_code =
71           chromeos::input_method::GetLanguageCodeFromDescriptor(input_method);
72       const std::string keyboard_overlay_id =
73           library->GetKeyboardOverlayId(input_method.id);
74       language_code_to_ids->insert(
75           std::make_pair(language_code, input_method.id));
76       // Remember the pairs.
77       id_to_language_code->insert(
78           std::make_pair(input_method.id, language_code));
79       id_to_descriptor->insert(
80           std::make_pair(input_method.id, input_method));
81       name_to_overlay_id->insert(
82           std::make_pair(input_method.keyboard_layout, keyboard_overlay_id));
83     }
84 
85     // Go through the languages listed in kExtraLanguages.
86     using chromeos::input_method::kExtraLanguages;
87     for (size_t i = 0; i < arraysize(kExtraLanguages); ++i) {
88       const char* language_code = kExtraLanguages[i].language_code;
89       const char* input_method_id = kExtraLanguages[i].input_method_id;
90       InputMethodIdToDescriptorMap::const_iterator iter =
91           id_to_descriptor->find(input_method_id);
92       // If the associated input method descriptor is found, add the
93       // language code and the input method.
94       if (iter != id_to_descriptor->end()) {
95         const chromeos::InputMethodDescriptor& input_method = iter->second;
96         const std::string keyboard_overlay_id =
97             library->GetKeyboardOverlayId(input_method.id);
98         language_code_to_ids->insert(
99             std::make_pair(language_code, input_method.id));
100         name_to_overlay_id->insert(
101             std::make_pair(input_method.keyboard_layout, keyboard_overlay_id));
102       }
103     }
104   }
105 
106  private:
IdMaps__anon171a85370111::IdMaps107   IdMaps() : language_code_to_ids(new LanguageCodeToIdsMap),
108              id_to_language_code(new std::map<std::string, std::string>),
109              id_to_descriptor(new InputMethodIdToDescriptorMap),
110              name_to_overlay_id(new std::map<std::string, std::string>) {
111     ReloadMaps();
112   }
113 
114   friend struct DefaultSingletonTraits<IdMaps>;
115 
116   DISALLOW_COPY_AND_ASSIGN(IdMaps);
117 };
118 
119 const struct EnglishToResouceId {
120   const char* english_string_from_ibus;
121   int resource_id;
122 } kEnglishToResourceIdArray[] = {
123   // For ibus-mozc.
124   { "Direct input", IDS_STATUSBAR_IME_JAPANESE_IME_STATUS_DIRECT_INPUT },
125   { "Hiragana", IDS_STATUSBAR_IME_JAPANESE_IME_STATUS_HIRAGANA },
126   { "Katakana", IDS_STATUSBAR_IME_JAPANESE_IME_STATUS_KATAKANA },
127   { "Half width katakana",  // small k is not a typo.
128     IDS_STATUSBAR_IME_JAPANESE_IME_STATUS_HALF_WIDTH_KATAKANA },
129   { "Latin", IDS_STATUSBAR_IME_JAPANESE_IME_STATUS_LATIN },
130   { "Wide Latin", IDS_STATUSBAR_IME_JAPANESE_IME_STATUS_WIDE_LATIN },
131 
132   // For ibus-hangul: third_party/ibus-hangul/files/po/.
133   { "Enable/Disable Hanja mode", IDS_STATUSBAR_IME_KOREAN_HANJA_MODE },
134 
135   // For ibus-pinyin.
136   { "Full/Half width",
137     IDS_STATUSBAR_IME_CHINESE_PINYIN_TOGGLE_FULL_HALF },
138   { "Full/Half width punctuation",
139     IDS_STATUSBAR_IME_CHINESE_PINYIN_TOGGLE_FULL_HALF_PUNCTUATION },
140   { "Simplfied/Traditional Chinese",
141     IDS_STATUSBAR_IME_CHINESE_PINYIN_TOGGLE_S_T_CHINESE },
142 
143   // For ibus-mozc-chewing.
144   { "English",
145     IDS_STATUSBAR_IME_CHINESE_MOZC_CHEWING_ENGLISH_MODE },
146   { "Full-width English",
147     IDS_STATUSBAR_IME_CHINESE_MOZC_CHEWING_FULL_WIDTH_ENGLISH_MODE },
148 
149   // For the "Languages and Input" dialog.
150   { "kbd (m17n)", IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD },
151   { "itrans (m17n)",  // also uses the "STANDARD_INPUT_METHOD" id.
152     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD },
153   { "cangjie (m17n)",
154     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_CHINESE_CANGJIE_INPUT_METHOD },
155   { "quick (m17n)",
156     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_CHINESE_QUICK_INPUT_METHOD },
157   { "isiri (m17n)",
158     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_PERSIAN_ISIRI_2901_INPUT_METHOD },
159   { "kesmanee (m17n)",
160     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_THAI_KESMANEE_INPUT_METHOD },
161   { "tis820 (m17n)",
162     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_THAI_TIS820_INPUT_METHOD },
163   { "pattachote (m17n)",
164     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_THAI_PATTACHOTE_INPUT_METHOD },
165   { "tcvn (m17n)",
166     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_TCVN_INPUT_METHOD },
167   { "telex (m17n)",
168     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_TELEX_INPUT_METHOD },
169   { "viqr (m17n)",
170     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_VIQR_INPUT_METHOD },
171   { "vni (m17n)",
172     IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_VNI_INPUT_METHOD },
173   { "Mozc Chewing (Chewing)",
174     IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_INPUT_METHOD },
175   { "Pinyin", IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_INPUT_METHOD },
176   { "Mozc (US keyboard layout)",
177     IDS_OPTIONS_SETTINGS_LANGUAGES_JAPANESE_MOZC_US_INPUT_METHOD },
178   { "Mozc (US Dvorak keyboard layout)",
179     IDS_OPTIONS_SETTINGS_LANGUAGES_JAPANESE_MOZC_US_DV_INPUT_METHOD },
180   { "Mozc (Japanese keyboard layout)",
181     IDS_OPTIONS_SETTINGS_LANGUAGES_JAPANESE_MOZC_JP_INPUT_METHOD },
182   { "Google Japanese Input (US keyboard layout)",
183     IDS_OPTIONS_SETTINGS_LANGUAGES_JAPANESE_GOOGLE_US_INPUT_METHOD },
184   { "Google Japanese Input (US Dvorak keyboard layout)",
185     IDS_OPTIONS_SETTINGS_LANGUAGES_JAPANESE_GOOGLE_US_DV_INPUT_METHOD },
186   { "Google Japanese Input (Japanese keyboard layout)",
187     IDS_OPTIONS_SETTINGS_LANGUAGES_JAPANESE_GOOGLE_JP_INPUT_METHOD },
188   { "Korean", IDS_OPTIONS_SETTINGS_LANGUAGES_KOREAN_INPUT_METHOD },
189 
190   // For ibus-xkb-layouts engine: third_party/ibus-xkb-layouts/files
191   { "Japan", IDS_STATUSBAR_LAYOUT_JAPAN },
192   { "Slovenia", IDS_STATUSBAR_LAYOUT_SLOVENIA },
193   { "Germany", IDS_STATUSBAR_LAYOUT_GERMANY },
194   { "Germany - Neo 2", IDS_STATUSBAR_LAYOUT_GERMANY_NEO2 },
195   { "Italy", IDS_STATUSBAR_LAYOUT_ITALY },
196   { "Estonia", IDS_STATUSBAR_LAYOUT_ESTONIA },
197   { "Hungary", IDS_STATUSBAR_LAYOUT_HUNGARY },
198   { "Poland", IDS_STATUSBAR_LAYOUT_POLAND },
199   { "Denmark", IDS_STATUSBAR_LAYOUT_DENMARK },
200   { "Croatia", IDS_STATUSBAR_LAYOUT_CROATIA },
201   { "Brazil", IDS_STATUSBAR_LAYOUT_BRAZIL },
202   { "Serbia", IDS_STATUSBAR_LAYOUT_SERBIA },
203   { "Czechia", IDS_STATUSBAR_LAYOUT_CZECHIA },
204   { "USA - Dvorak", IDS_STATUSBAR_LAYOUT_USA_DVORAK },
205   { "USA - Colemak", IDS_STATUSBAR_LAYOUT_USA_COLEMAK },
206   { "Romania", IDS_STATUSBAR_LAYOUT_ROMANIA },
207   { "USA", IDS_STATUSBAR_LAYOUT_USA },
208   { "USA - International (AltGr dead keys)",
209     IDS_STATUSBAR_LAYOUT_USA_EXTENDED },
210   { "USA - International (with dead keys)",
211     IDS_STATUSBAR_LAYOUT_USA_INTERNATIONAL },
212   { "Lithuania", IDS_STATUSBAR_LAYOUT_LITHUANIA },
213   { "United Kingdom - Extended - Winkeys",
214     IDS_STATUSBAR_LAYOUT_UNITED_KINGDOM },
215   { "United Kingdom - Dvorak",
216     IDS_STATUSBAR_LAYOUT_UNITED_KINGDOM_DVORAK },
217   { "Slovakia", IDS_STATUSBAR_LAYOUT_SLOVAKIA },
218   { "Russia", IDS_STATUSBAR_LAYOUT_RUSSIA },
219   { "Russia - Phonetic", IDS_STATUSBAR_LAYOUT_RUSSIA_PHONETIC },
220   { "Greece", IDS_STATUSBAR_LAYOUT_GREECE },
221   { "Belgium", IDS_STATUSBAR_LAYOUT_BELGIUM },
222   { "Bulgaria", IDS_STATUSBAR_LAYOUT_BULGARIA },
223   { "Bulgaria - Traditional phonetic", IDS_STATUSBAR_LAYOUT_BULGARIA_PHONETIC },
224   { "Switzerland", IDS_STATUSBAR_LAYOUT_SWITZERLAND },
225   { "Switzerland - French", IDS_STATUSBAR_LAYOUT_SWITZERLAND_FRENCH },
226   { "Turkey", IDS_STATUSBAR_LAYOUT_TURKEY },
227   { "Portugal", IDS_STATUSBAR_LAYOUT_PORTUGAL },
228   { "Spain", IDS_STATUSBAR_LAYOUT_SPAIN },
229   { "Finland", IDS_STATUSBAR_LAYOUT_FINLAND },
230   { "Ukraine", IDS_STATUSBAR_LAYOUT_UKRAINE },
231   { "Spain - Catalan variant with middle-dot L",
232     IDS_STATUSBAR_LAYOUT_SPAIN_CATALAN },
233   { "France", IDS_STATUSBAR_LAYOUT_FRANCE },
234   { "Norway", IDS_STATUSBAR_LAYOUT_NORWAY },
235   { "Sweden", IDS_STATUSBAR_LAYOUT_SWEDEN },
236   { "Netherlands", IDS_STATUSBAR_LAYOUT_NETHERLANDS },
237   { "Latin American", IDS_STATUSBAR_LAYOUT_LATIN_AMERICAN },
238   { "Latvia - Apostrophe (') variant", IDS_STATUSBAR_LAYOUT_LATVIA },
239   { "Canada", IDS_STATUSBAR_LAYOUT_CANADA },
240   { "Canada - English", IDS_STATUSBAR_LAYOUT_CANADA_ENGLISH },
241   { "Israel", IDS_STATUSBAR_LAYOUT_ISRAEL },
242   { "Korea, Republic of - 101/104 key Compatible",
243     IDS_STATUSBAR_LAYOUT_KOREA_104 },
244 };
245 const size_t kEnglishToResourceIdArraySize =
246     arraysize(kEnglishToResourceIdArray);
247 
248 const struct EnglishAndInputMethodIdToResouceId {
249   const char* english_string_from_ibus;
250   const char* input_method_id;
251   int resource_id;
252 } kEnglishAndInputMethodIdToResourceIdArray[] = {
253   { "Chinese", "pinyin",
254     IDS_STATUSBAR_IME_CHINESE_PINYIN_TOGGLE_CHINESE_ENGLISH },
255   { "Chinese", "mozc-chewing",
256     IDS_STATUSBAR_IME_CHINESE_MOZC_CHEWING_CHINESE_MODE },
257 };
258 const size_t kEnglishAndInputMethodIdToResourceIdArraySize =
259     arraysize(kEnglishAndInputMethodIdToResourceIdArray);
260 
261 // There are some differences between ISO 639-2 (T) and ISO 639-2 B, and
262 // some language codes are not recognized by ICU (i.e. ICU cannot convert
263 // these codes to two-letter language codes and display names). Hence we
264 // convert these codes to ones that ICU recognize.
265 //
266 // See http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes for details.
267 const char* kIso639VariantMapping[][2] = {
268   { "cze", "ces" },
269   { "ger", "deu" },
270   { "gre", "ell" },
271   // "scr" is not a ISO 639 code. For some reason, evdev.xml uses "scr" as
272   // the language code for Croatian.
273   { "scr", "hrv" },
274   { "rum", "ron" },
275   { "slo", "slk" },
276 };
277 
278 // The comparator is used for sorting language codes by their
279 // corresponding language names, using the ICU collator.
280 struct CompareLanguageCodesByLanguageName
281     : std::binary_function<const std::string&, const std::string&, bool> {
CompareLanguageCodesByLanguageName__anon171a85370111::CompareLanguageCodesByLanguageName282   explicit CompareLanguageCodesByLanguageName(icu::Collator* collator)
283       : collator_(collator) {
284   }
285 
286   // Calling GetLanguageDisplayNameFromCode() in the comparator is not
287   // efficient, but acceptable as the function is cheap, and the language
288   // list is short (about 40 at most).
operator ()__anon171a85370111::CompareLanguageCodesByLanguageName289   bool operator()(const std::string& s1, const std::string& s2) const {
290     const string16 key1 =
291         chromeos::input_method::GetLanguageDisplayNameFromCode(s1);
292     const string16 key2 =
293         chromeos::input_method::GetLanguageDisplayNameFromCode(s2);
294     return l10n_util::StringComparator<string16>(collator_)(key1, key2);
295   }
296 
297  private:
298   icu::Collator* collator_;
299 };
300 
GetLocalizedString(const std::string & english_string,const std::string & input_method_id,string16 * out_string)301 bool GetLocalizedString(const std::string& english_string,
302                         const std::string& input_method_id,
303                         string16 *out_string) {
304   DCHECK(out_string);
305 
306   // Initialize the primary map if needed.
307   typedef base::hash_map<std::string, int> HashType;
308   static HashType* english_to_resource_id = NULL;
309   if (!english_to_resource_id) {
310     // We don't free this map.
311     english_to_resource_id = new HashType(kEnglishToResourceIdArraySize);
312     for (size_t i = 0; i < kEnglishToResourceIdArraySize; ++i) {
313       const EnglishToResouceId& map_entry = kEnglishToResourceIdArray[i];
314       const bool result = english_to_resource_id->insert(std::make_pair(
315           map_entry.english_string_from_ibus, map_entry.resource_id)).second;
316       DCHECK(result) << "Duplicated string is found: "
317                      << map_entry.english_string_from_ibus;
318     }
319   }
320 
321   // Initialize the secondary map if needed.
322   typedef std::map<std::pair<std::string, std::string>, int> MapType;
323   static MapType* english_and_input_method_id_to_resource_id = NULL;
324   if (!english_and_input_method_id_to_resource_id) {
325     // We don't free this map.
326     english_and_input_method_id_to_resource_id = new MapType;
327     for (size_t i = 0; i < kEnglishAndInputMethodIdToResourceIdArraySize; ++i) {
328       const EnglishAndInputMethodIdToResouceId& map_entry =
329           kEnglishAndInputMethodIdToResourceIdArray[i];
330       const std::pair<std::string, std::string> key = std::make_pair(
331           map_entry.english_string_from_ibus, map_entry.input_method_id);
332       const bool result = english_and_input_method_id_to_resource_id->insert(
333           std::make_pair(key, map_entry.resource_id)).second;
334       DCHECK(result) << "Duplicated key is found: pair of "
335                      << map_entry.english_string_from_ibus
336                      << " and "
337                      << map_entry.input_method_id;
338     }
339   }
340 
341   HashType::const_iterator iter = english_to_resource_id->find(english_string);
342   if (iter == english_to_resource_id->end()) {
343     // The string is not found in the primary map. Try the secondary map with
344     // |input_method_id|.
345     const std::pair<std::string, std::string> key =
346         std::make_pair(english_string, input_method_id);
347     MapType::const_iterator iter2 =
348         english_and_input_method_id_to_resource_id->find(key);
349     if (iter2 == english_and_input_method_id_to_resource_id->end()) {
350       // TODO(yusukes): Write Autotest which checks if all display names and all
351       // property names for supported input methods are listed in the resource
352       // ID array (crosbug.com/4572).
353       LOG(ERROR) << "Resource ID is not found for: " << english_string;
354       return false;
355     }
356     *out_string = l10n_util::GetStringUTF16(iter2->second);
357   } else {
358     *out_string = l10n_util::GetStringUTF16(iter->second);
359   }
360   return true;
361 };
362 
363 }  // namespace
364 
365 namespace chromeos {
366 namespace input_method {
367 
GetString(const std::string & english_string,const std::string & input_method_id)368 std::wstring GetString(const std::string& english_string,
369                        const std::string& input_method_id) {
370   string16 localized_string;
371   if (GetLocalizedString(english_string, input_method_id, &localized_string)) {
372     return UTF16ToWide(localized_string);
373   }
374   return UTF8ToWide(english_string);
375 }
376 
GetStringUTF8(const std::string & english_string,const std::string & input_method_id)377 std::string GetStringUTF8(const std::string& english_string,
378                           const std::string& input_method_id) {
379   string16 localized_string;
380   if (GetLocalizedString(english_string, input_method_id, &localized_string)) {
381     return UTF16ToUTF8(localized_string);
382   }
383   return english_string;
384 }
385 
GetStringUTF16(const std::string & english_string,const std::string & input_method_id)386 string16 GetStringUTF16(const std::string& english_string,
387                         const std::string& input_method_id) {
388   string16 localized_string;
389   if (GetLocalizedString(english_string, input_method_id, &localized_string)) {
390     return localized_string;
391   }
392   return UTF8ToUTF16(english_string);
393 }
394 
StringIsSupported(const std::string & english_string,const std::string & input_method_id)395 bool StringIsSupported(const std::string& english_string,
396                        const std::string& input_method_id) {
397   string16 localized_string;
398   return GetLocalizedString(english_string, input_method_id, &localized_string);
399 }
400 
NormalizeLanguageCode(const std::string & language_code)401 std::string NormalizeLanguageCode(
402     const std::string& language_code) {
403   // Some ibus engines return locale codes like "zh_CN" as language codes.
404   // Normalize these to like "zh-CN".
405   if (language_code.size() >= 5 && language_code[2] == '_') {
406     std::string copied_language_code = language_code;
407     copied_language_code[2] = '-';
408     // Downcase the language code part.
409     for (size_t i = 0; i < 2; ++i) {
410       copied_language_code[i] = base::ToLowerASCII(copied_language_code[i]);
411     }
412     // Upcase the country code part.
413     for (size_t i = 3; i < copied_language_code.size(); ++i) {
414       copied_language_code[i] = base::ToUpperASCII(copied_language_code[i]);
415     }
416     return copied_language_code;
417   }
418   // We only handle three-letter codes from here.
419   if (language_code.size() != 3) {
420     return language_code;
421   }
422 
423   // Convert special language codes. See comments at kIso639VariantMapping.
424   std::string copied_language_code = language_code;
425   for (size_t i = 0; i < arraysize(kIso639VariantMapping); ++i) {
426     if (language_code == kIso639VariantMapping[i][0]) {
427       copied_language_code = kIso639VariantMapping[i][1];
428     }
429   }
430   // Convert the three-letter code to two letter-code.
431   UErrorCode error = U_ZERO_ERROR;
432   char two_letter_code[ULOC_LANG_CAPACITY];
433   uloc_getLanguage(copied_language_code.c_str(),
434                    two_letter_code, sizeof(two_letter_code), &error);
435   if (U_FAILURE(error)) {
436     return language_code;
437   }
438   return two_letter_code;
439 }
440 
IsKeyboardLayout(const std::string & input_method_id)441 bool IsKeyboardLayout(const std::string& input_method_id) {
442   const bool kCaseInsensitive = false;
443   return StartsWithASCII(input_method_id, "xkb:", kCaseInsensitive);
444 }
445 
GetLanguageCodeFromDescriptor(const InputMethodDescriptor & descriptor)446 std::string GetLanguageCodeFromDescriptor(
447     const InputMethodDescriptor& descriptor) {
448   // Handle some Chinese input methods as zh-CN/zh-TW, rather than zh.
449   // TODO: we should fix this issue in engines rather than here.
450   if (descriptor.language_code == "zh") {
451     if (descriptor.id == "pinyin") {
452       return "zh-CN";
453     } else if (descriptor.id == "mozc-chewing" ||
454                descriptor.id == "m17n:zh:cangjie" ||
455                descriptor.id == "m17n:zh:quick") {
456       return "zh-TW";
457     }
458   }
459 
460   std::string language_code = NormalizeLanguageCode(descriptor.language_code);
461 
462   // Add country codes to language codes of some XKB input methods to make
463   // these compatible with Chrome's application locale codes like "en-US".
464   // TODO(satorux): Maybe we need to handle "es" for "es-419".
465   // TODO: We should not rely on the format of the engine name. Should we add
466   //       |country_code| in InputMethodDescriptor?
467   if (IsKeyboardLayout(descriptor.id) &&
468       (language_code == "en" ||
469        language_code == "zh" ||
470        language_code == "pt")) {
471     std::vector<std::string> portions;
472     base::SplitString(descriptor.id, ':', &portions);
473     if (portions.size() >= 2 && !portions[1].empty()) {
474       language_code.append("-");
475       language_code.append(StringToUpperASCII(portions[1]));
476     }
477   }
478   return language_code;
479 }
480 
GetLanguageCodeFromInputMethodId(const std::string & input_method_id)481 std::string GetLanguageCodeFromInputMethodId(
482     const std::string& input_method_id) {
483   // The code should be compatible with one of codes used for UI languages,
484   // defined in app/l10_util.cc.
485   const char kDefaultLanguageCode[] = "en-US";
486   std::map<std::string, std::string>::const_iterator iter
487       = IdMaps::GetInstance()->id_to_language_code->find(input_method_id);
488   return (iter == IdMaps::GetInstance()->id_to_language_code->end()) ?
489       // Returning |kDefaultLanguageCode| here is not for Chrome OS but for
490       // Ubuntu where the ibus-xkb-layouts engine could be missing.
491       kDefaultLanguageCode : iter->second;
492 }
493 
GetKeyboardLayoutName(const std::string & input_method_id)494 std::string GetKeyboardLayoutName(const std::string& input_method_id) {
495   InputMethodIdToDescriptorMap::const_iterator iter
496       = IdMaps::GetInstance()->id_to_descriptor->find(input_method_id);
497   return (iter == IdMaps::GetInstance()->id_to_descriptor->end()) ?
498       "" : iter->second.keyboard_layout;
499 }
500 
GetKeyboardOverlayId(const std::string & input_method_name)501 std::string GetKeyboardOverlayId(const std::string& input_method_name) {
502   std::map<std::string, std::string>::const_iterator iter
503       = IdMaps::GetInstance()->name_to_overlay_id->find(input_method_name);
504   return (iter == IdMaps::GetInstance()->name_to_overlay_id->end()) ?
505       "" : iter->second;
506 }
507 
GetInputMethodDisplayNameFromId(const std::string & input_method_id)508 std::string GetInputMethodDisplayNameFromId(
509     const std::string& input_method_id) {
510   InputMethodIdToDescriptorMap::const_iterator iter
511       = IdMaps::GetInstance()->id_to_descriptor->find(input_method_id);
512   return (iter == IdMaps::GetInstance()->id_to_descriptor->end()) ?
513       "" : GetStringUTF8(iter->second.display_name, input_method_id);
514 }
515 
GetInputMethodDescriptorFromId(const std::string & input_method_id)516 const chromeos::InputMethodDescriptor* GetInputMethodDescriptorFromId(
517     const std::string& input_method_id) {
518   InputMethodIdToDescriptorMap::const_iterator iter
519       = IdMaps::GetInstance()->id_to_descriptor->find(input_method_id);
520   return (iter == IdMaps::GetInstance()->id_to_descriptor->end()) ?
521       NULL : &(iter->second);
522 }
523 
GetLanguageDisplayNameFromCode(const std::string & language_code)524 string16 GetLanguageDisplayNameFromCode(const std::string& language_code) {
525   if (!g_browser_process) {
526     return string16();
527   }
528   return l10n_util::GetDisplayNameForLocale(
529       language_code, g_browser_process->GetApplicationLocale(), true);
530 }
531 
GetLanguageNativeDisplayNameFromCode(const std::string & language_code)532 string16 GetLanguageNativeDisplayNameFromCode(
533     const std::string& language_code) {
534   return l10n_util::GetDisplayNameForLocale(language_code, language_code, true);
535 }
536 
SortLanguageCodesByNames(std::vector<std::string> * language_codes)537 void SortLanguageCodesByNames(std::vector<std::string>* language_codes) {
538   if (!g_browser_process) {
539     return;
540   }
541   // We should build collator outside of the comparator. We cannot have
542   // scoped_ptr<> in the comparator for a subtle STL reason.
543   UErrorCode error = U_ZERO_ERROR;
544   icu::Locale locale(g_browser_process->GetApplicationLocale().c_str());
545   scoped_ptr<icu::Collator> collator(
546       icu::Collator::createInstance(locale, error));
547   if (U_FAILURE(error)) {
548     collator.reset();
549   }
550   std::sort(language_codes->begin(), language_codes->end(),
551             CompareLanguageCodesByLanguageName(collator.get()));
552 }
553 
GetInputMethodIdsFromLanguageCode(const std::string & normalized_language_code,InputMethodType type,std::vector<std::string> * out_input_method_ids)554 bool GetInputMethodIdsFromLanguageCode(
555     const std::string& normalized_language_code,
556     InputMethodType type,
557     std::vector<std::string>* out_input_method_ids) {
558   return GetInputMethodIdsFromLanguageCodeInternal(
559       *IdMaps::GetInstance()->language_code_to_ids,
560       normalized_language_code, type, out_input_method_ids);
561 }
562 
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)563 bool GetInputMethodIdsFromLanguageCodeInternal(
564     const std::multimap<std::string, std::string>& language_code_to_ids,
565     const std::string& normalized_language_code,
566     InputMethodType type,
567     std::vector<std::string>* out_input_method_ids) {
568   DCHECK(out_input_method_ids);
569   out_input_method_ids->clear();
570 
571   bool result = false;
572   std::pair<LanguageCodeToIdsMap::const_iterator,
573       LanguageCodeToIdsMap::const_iterator> range =
574       language_code_to_ids.equal_range(normalized_language_code);
575   for (LanguageCodeToIdsMap::const_iterator iter = range.first;
576        iter != range.second; ++iter) {
577     const std::string& input_method_id = iter->second;
578     if ((type == kAllInputMethods) || IsKeyboardLayout(input_method_id)) {
579       out_input_method_ids->push_back(input_method_id);
580       result = true;
581     }
582   }
583   if ((type == kAllInputMethods) && !result) {
584     LOG(ERROR) << "Unknown language code: " << normalized_language_code;
585   }
586   return result;
587 }
588 
GetFirstLoginInputMethodIds(const std::string & language_code,const InputMethodDescriptor & current_input_method,std::vector<std::string> * out_input_method_ids)589 void GetFirstLoginInputMethodIds(
590     const std::string& language_code,
591     const InputMethodDescriptor& current_input_method,
592     std::vector<std::string>* out_input_method_ids) {
593   out_input_method_ids->clear();
594 
595   // First, add the current keyboard layout (one used on the login screen).
596   out_input_method_ids->push_back(current_input_method.id);
597 
598   // Second, find the most popular input method associated with the
599   // current UI language. The input method IDs returned from
600   // GetInputMethodIdsFromLanguageCode() are sorted by popularity, hence
601   // our basic strategy is to pick the first one, but it's a bit more
602   // complicated as shown below.
603   std::string most_popular_id;
604   std::vector<std::string> input_method_ids;
605   // This returns the input methods sorted by popularity.
606   input_method::GetInputMethodIdsFromLanguageCode(
607       language_code, input_method::kAllInputMethods, &input_method_ids);
608   for (size_t i = 0; i < input_method_ids.size(); ++i) {
609     const std::string& input_method_id = input_method_ids[i];
610     // Pick the first one.
611     if (most_popular_id.empty())
612       most_popular_id = input_method_id;
613 
614     // Check if there is one that matches the current keyboard layout, but
615     // not the current keyboard itself. This is useful if there are
616     // multiple keyboard layout choices for one input method. For
617     // instance, Mozc provides three choices: mozc (US keyboard), mozc-jp
618     // (JP keyboard), mozc-dv (Dvorak).
619     const InputMethodDescriptor* descriptor =
620         GetInputMethodDescriptorFromId(input_method_id);
621     if (descriptor &&
622         descriptor->id != current_input_method.id &&
623         descriptor->keyboard_layout == current_input_method.keyboard_layout) {
624       most_popular_id = input_method_id;
625       break;
626     }
627   }
628   // Add the most popular input method ID, if it's different from the
629   // current input method.
630   if (most_popular_id != current_input_method.id) {
631     out_input_method_ids->push_back(most_popular_id);
632   }
633 }
634 
GetLanguageCodesFromInputMethodIds(const std::vector<std::string> & input_method_ids,std::vector<std::string> * out_language_codes)635 void GetLanguageCodesFromInputMethodIds(
636     const std::vector<std::string>& input_method_ids,
637     std::vector<std::string>* out_language_codes) {
638   out_language_codes->clear();
639 
640   for (size_t i = 0; i < input_method_ids.size(); ++i) {
641     const std::string& input_method_id = input_method_ids[i];
642     const InputMethodDescriptor* input_method =
643         GetInputMethodDescriptorFromId(input_method_id);
644     if (!input_method) {
645       LOG(ERROR) << "Unknown input method ID: " << input_method_ids[i];
646       continue;
647     }
648     const std::string language_code =
649         GetLanguageCodeFromDescriptor(*input_method);
650     // Add it if it's not already present.
651     if (std::count(out_language_codes->begin(), out_language_codes->end(),
652                    language_code) == 0) {
653       out_language_codes->push_back(language_code);
654     }
655   }
656 }
657 
EnableInputMethods(const std::string & language_code,InputMethodType type,const std::string & initial_input_method_id)658 void EnableInputMethods(const std::string& language_code, InputMethodType type,
659                         const std::string& initial_input_method_id) {
660   std::vector<std::string> candidates;
661   // Add input methods associated with the language.
662   GetInputMethodIdsFromLanguageCode(language_code, type, &candidates);
663   // Add the hardware keyboard as well. We should always add this so users
664   // can use the hardware keyboard on the login screen and the screen locker.
665   candidates.push_back(GetHardwareInputMethodId());
666 
667   std::vector<std::string> input_method_ids;
668   // First, add the initial input method ID, if it's requested, to
669   // input_method_ids, so it appears first on the list of active input
670   // methods at the input language status menu.
671   if (!initial_input_method_id.empty()) {
672     input_method_ids.push_back(initial_input_method_id);
673   }
674 
675   // Add candidates to input_method_ids, while skipping duplicates.
676   for (size_t i = 0; i < candidates.size(); ++i) {
677     const std::string& candidate = candidates[i];
678     // Not efficient, but should be fine, as the two vectors are very
679     // short (2-5 items).
680     if (std::count(input_method_ids.begin(), input_method_ids.end(),
681                    candidate) == 0) {
682       input_method_ids.push_back(candidate);
683     }
684   }
685 
686   // Update ibus-daemon setting. Here, we don't save the input method list
687   // in the user's preferences.
688   ImeConfigValue value;
689   value.type = ImeConfigValue::kValueTypeStringList;
690   value.string_list_value = input_method_ids;
691   InputMethodLibrary* library = CrosLibrary::Get()->GetInputMethodLibrary();
692   library->SetImeConfig(language_prefs::kGeneralSectionName,
693                         language_prefs::kPreloadEnginesConfigName, value);
694 
695   // Finaly, change to the initial input method, as needed.
696   if (!initial_input_method_id.empty()) {
697     library->ChangeInputMethod(initial_input_method_id);
698   }
699 }
700 
GetHardwareInputMethodId()701 std::string GetHardwareInputMethodId() {
702   if (!(g_browser_process && g_browser_process->local_state())) {
703     // This shouldn't happen but just in case.
704     LOG(ERROR) << "Local state is not yet ready";
705     return GetFallbackInputMethodDescriptor().id;
706   }
707 
708   PrefService* local_state = g_browser_process->local_state();
709   if (!local_state->FindPreference(prefs::kHardwareKeyboardLayout)) {
710     // This could happen in unittests. We register the preference in
711     // BrowserMain::InitializeLocalState and that method is not called during
712     // unittests.
713     LOG(ERROR) << prefs::kHardwareKeyboardLayout << " is not registered";
714     return GetFallbackInputMethodDescriptor().id;
715   }
716 
717   const std::string input_method_id =
718       local_state->GetString(prefs::kHardwareKeyboardLayout);
719   if (input_method_id.empty()) {
720     // This is totally fine if it's empty. The hardware keyboard layout is
721     // not stored if startup_manifest.json (OEM customization data) is not
722     // present (ex. Cr48 doen't have that file).
723     return GetFallbackInputMethodDescriptor().id;
724   }
725   return input_method_id;
726 }
727 
GetFallbackInputMethodDescriptor()728 InputMethodDescriptor GetFallbackInputMethodDescriptor() {
729   return InputMethodDescriptor("xkb:us::eng", "USA", "us", "eng");
730 }
731 
ReloadInternalMaps()732 void ReloadInternalMaps() {
733   IdMaps::GetInstance()->ReloadMaps();
734 }
735 
OnLocaleChanged()736 void OnLocaleChanged() {
737   ReloadInternalMaps();
738 }
739 
740 }  // namespace input_method
741 }  // namespace chromeos
742