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