input_method_util.cc revision 4a5e2dc747d50c653511c68ccb2cfbfb740bd5a7
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 <map>
9#include <utility>
10
11#include "unicode/uloc.h"
12
13#include "app/l10n_util.h"
14#include "app/l10n_util_collator.h"
15#include "base/basictypes.h"
16#include "base/hash_tables.h"
17#include "base/scoped_ptr.h"
18#include "base/singleton.h"
19#include "base/string_split.h"
20#include "base/string_util.h"
21#include "base/utf_string_conversions.h"
22#include "chrome/browser/browser_process.h"
23#include "chrome/browser/browser_thread.h"
24#include "chrome/browser/chromeos/cros/cros_library.h"
25#include "chrome/browser/chromeos/cros/keyboard_library.h"
26#include "chrome/browser/chromeos/language_preferences.h"
27#include "grit/generated_resources.h"
28
29namespace {
30
31// Map from language code to associated input method IDs, etc.
32typedef std::multimap<std::string, std::string> LanguageCodeToIdsMap;
33struct IdMaps {
34  scoped_ptr<LanguageCodeToIdsMap> language_code_to_ids;
35  scoped_ptr<std::map<std::string, std::string> > id_to_language_code;
36  scoped_ptr<std::map<std::string, std::string> > id_to_display_name;
37
38  void ReloadMaps() {
39    chromeos::InputMethodLibrary* library =
40        chromeos::CrosLibrary::Get()->GetInputMethodLibrary();
41    scoped_ptr<chromeos::InputMethodDescriptors> supported_input_methods(
42        library->GetSupportedInputMethods());
43    if (supported_input_methods->size() <= 1) {
44      LOG(ERROR) << "GetSupportedInputMethods returned a fallback ID";
45      // TODO(yusukes): Handle this error in nicer way.
46    }
47
48    language_code_to_ids->clear();
49    id_to_language_code->clear();
50    id_to_display_name->clear();
51
52    // Build the id to descriptor map for handling kExtraLanguages later.
53    typedef std::map<std::string,
54        const chromeos::InputMethodDescriptor*> DescMap;
55    DescMap id_to_descriptor_map;
56
57    for (size_t i = 0; i < supported_input_methods->size(); ++i) {
58      const chromeos::InputMethodDescriptor& input_method =
59          supported_input_methods->at(i);
60      const std::string language_code =
61          chromeos::input_method::GetLanguageCodeFromDescriptor(input_method);
62      AddInputMethodToMaps(language_code, input_method);
63      // Remember the pair.
64      id_to_descriptor_map.insert(
65          std::make_pair(input_method.id, &input_method));
66    }
67
68    // Go through the languages listed in kExtraLanguages.
69    using chromeos::input_method::kExtraLanguages;
70    for (size_t i = 0; i < arraysize(kExtraLanguages); ++i) {
71      const char* language_code = kExtraLanguages[i].language_code;
72      const char* input_method_id = kExtraLanguages[i].input_method_id;
73      DescMap::const_iterator iter = id_to_descriptor_map.find(input_method_id);
74      // If the associated input method descriptor is found, add the
75      // language code and the input method.
76      if (iter != id_to_descriptor_map.end()) {
77        const chromeos::InputMethodDescriptor& input_method = *(iter->second);
78        AddInputMethodToMaps(language_code, input_method);
79      }
80    }
81  }
82
83 private:
84  IdMaps() : language_code_to_ids(new LanguageCodeToIdsMap),
85             id_to_language_code(new std::map<std::string, std::string>),
86             id_to_display_name(new std::map<std::string, std::string>) {
87    ReloadMaps();
88  }
89
90  void AddInputMethodToMaps(
91      const std::string& language_code,
92      const chromeos::InputMethodDescriptor& input_method) {
93    language_code_to_ids->insert(
94        std::make_pair(language_code, input_method.id));
95    id_to_language_code->insert(
96        std::make_pair(input_method.id, language_code));
97    id_to_display_name->insert(std::make_pair(
98        input_method.id,
99        chromeos::input_method::GetStringUTF8(input_method.display_name)));
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  { "Italy", IDS_STATUSBAR_LAYOUT_ITALY },
184  { "Estonia", IDS_STATUSBAR_LAYOUT_ESTONIA },
185  { "Hungary", IDS_STATUSBAR_LAYOUT_HUNGARY },
186  { "Poland", IDS_STATUSBAR_LAYOUT_POLAND },
187  { "Denmark", IDS_STATUSBAR_LAYOUT_DENMARK },
188  { "Croatia", IDS_STATUSBAR_LAYOUT_CROATIA },
189  { "Brazil", IDS_STATUSBAR_LAYOUT_BRAZIL },
190  { "Serbia", IDS_STATUSBAR_LAYOUT_SERBIA },
191  { "Czechia", IDS_STATUSBAR_LAYOUT_CZECHIA },
192  { "USA - Dvorak", IDS_STATUSBAR_LAYOUT_USA_DVORAK },
193  { "Romania", IDS_STATUSBAR_LAYOUT_ROMANIA },
194  { "USA", IDS_STATUSBAR_LAYOUT_USA },
195  { "USA - International (AltGr dead keys)",
196    IDS_STATUSBAR_LAYOUT_USA_INTERNATIONAL },
197  { "Lithuania", IDS_STATUSBAR_LAYOUT_LITHUANIA },
198  { "United Kingdom - Extended - Winkeys",
199    IDS_STATUSBAR_LAYOUT_UNITED_KINGDOM },
200  { "Slovakia", IDS_STATUSBAR_LAYOUT_SLOVAKIA },
201  { "Russia", IDS_STATUSBAR_LAYOUT_RUSSIA },
202  { "Russia - Phonetic", IDS_STATUSBAR_LAYOUT_RUSSIA_PHONETIC },
203  { "Greece", IDS_STATUSBAR_LAYOUT_GREECE },
204  { "Belgium", IDS_STATUSBAR_LAYOUT_BELGIUM },
205  { "Bulgaria", IDS_STATUSBAR_LAYOUT_BULGARIA },
206  { "Bulgaria - Traditional phonetic", IDS_STATUSBAR_LAYOUT_BULGARIA_PHONETIC },
207  { "Switzerland", IDS_STATUSBAR_LAYOUT_SWITZERLAND },
208  { "Switzerland - French", IDS_STATUSBAR_LAYOUT_SWITZERLAND_FRENCH },
209  { "Turkey", IDS_STATUSBAR_LAYOUT_TURKEY },
210  { "Portugal", IDS_STATUSBAR_LAYOUT_PORTUGAL },
211  { "Spain", IDS_STATUSBAR_LAYOUT_SPAIN },
212  { "Finland", IDS_STATUSBAR_LAYOUT_FINLAND },
213  { "Ukraine", IDS_STATUSBAR_LAYOUT_UKRAINE },
214  { "Spain - Catalan variant with middle-dot L",
215    IDS_STATUSBAR_LAYOUT_SPAIN_CATALAN },
216  { "France", IDS_STATUSBAR_LAYOUT_FRANCE },
217  { "Norway", IDS_STATUSBAR_LAYOUT_NORWAY },
218  { "Sweden", IDS_STATUSBAR_LAYOUT_SWEDEN },
219  { "Netherlands", IDS_STATUSBAR_LAYOUT_NETHERLANDS },
220  { "Latvia", IDS_STATUSBAR_LAYOUT_LATVIA },
221  { "Canada", IDS_STATUSBAR_LAYOUT_CANADA },
222  { "Canada - English", IDS_STATUSBAR_LAYOUT_CANADA_ENGLISH },
223  { "Israel", IDS_STATUSBAR_LAYOUT_ISRAEL },
224  { "Korea, Republic of - 101/104 key Compatible",
225    IDS_STATUSBAR_LAYOUT_KOREA_104 },
226};
227const size_t kNumEntries = arraysize(kEnglishToResourceIdArray);
228
229// There are some differences between ISO 639-2 (T) and ISO 639-2 B, and
230// some language codes are not recognized by ICU (i.e. ICU cannot convert
231// these codes to two-letter language codes and display names). Hence we
232// convert these codes to ones that ICU recognize.
233//
234// See http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes for details.
235const char* kIso639VariantMapping[][2] = {
236  {"cze", "ces"},
237  {"ger", "deu"},
238  {"gre", "ell"},
239  // "scr" is not a ISO 639 code. For some reason, evdev.xml uses "scr" as
240  // the language code for Croatian.
241  {"scr", "hrv"},
242  {"rum", "ron"},
243  {"slo", "slk"},
244};
245
246// The comparator is used for sorting language codes by their
247// corresponding language names, using the ICU collator.
248struct CompareLanguageCodesByLanguageName
249    : std::binary_function<const std::string&, const std::string&, bool> {
250  explicit CompareLanguageCodesByLanguageName(icu::Collator* collator)
251      : collator_(collator) {
252  }
253
254  // Calling GetLanguageDisplayNameFromCode() in the comparator is not
255  // efficient, but acceptable as the function is cheap, and the language
256  // list is short (about 40 at most).
257  bool operator()(const std::string& s1, const std::string& s2) const {
258    const std::wstring key1 =
259        chromeos::input_method::GetLanguageDisplayNameFromCode(s1);
260    const std::wstring key2 =
261        chromeos::input_method::GetLanguageDisplayNameFromCode(s2);
262    return l10n_util::StringComparator<std::wstring>(collator_)(key1, key2);
263  }
264
265 private:
266  icu::Collator* collator_;
267};
268
269// The comparator is used for sorting input method ids by their
270// corresponding language names, using the ICU collator.
271struct CompareInputMethodIdsByLanguageName
272    : std::binary_function<const std::string&, const std::string&, bool> {
273  CompareInputMethodIdsByLanguageName(
274      icu::Collator* collator,
275      const std::map<std::string, std::string>& id_to_language_code_map)
276      : comparator_(collator),
277        id_to_language_code_map_(id_to_language_code_map) {
278  }
279
280  bool operator()(const std::string& s1, const std::string& s2) const {
281    std::string language_code_1;
282    std::map<std::string, std::string>::const_iterator iter =
283        id_to_language_code_map_.find(s1);
284    if (iter != id_to_language_code_map_.end()) {
285      language_code_1 = iter->second;
286    }
287    std::string language_code_2;
288    iter = id_to_language_code_map_.find(s2);
289    if (iter != id_to_language_code_map_.end()) {
290      language_code_2 = iter->second;
291    }
292    return comparator_(language_code_1, language_code_2);
293  }
294
295 private:
296  const CompareLanguageCodesByLanguageName comparator_;
297  const std::map<std::string, std::string>& id_to_language_code_map_;
298};
299
300bool GetLocalizedString(
301    const std::string& english_string, string16 *out_string) {
302  DCHECK(out_string);
303  typedef base::hash_map<std::string, int> HashType;
304  static HashType* english_to_resource_id = NULL;
305
306  // Initialize the map if needed.
307  if (!english_to_resource_id) {
308    // We don't free this map.
309    english_to_resource_id = new HashType(kNumEntries);
310    for (size_t i = 0; i < kNumEntries; ++i) {
311      const bool result = english_to_resource_id->insert(
312          std::make_pair(kEnglishToResourceIdArray[i].english_string_from_ibus,
313                         kEnglishToResourceIdArray[i].resource_id)).second;
314      DCHECK(result) << "Duplicated string is found: "
315                     << kEnglishToResourceIdArray[i].english_string_from_ibus;
316    }
317  }
318
319  HashType::const_iterator iter = english_to_resource_id->find(english_string);
320  if (iter == english_to_resource_id->end()) {
321    // TODO(yusukes): Write Autotest which checks if all display names and all
322    // property names for supported input methods are listed in the resource ID
323    // array (crosbug.com/4572).
324    LOG(ERROR) << "Resource ID is not found for: " << english_string;
325    return false;
326  }
327
328  *out_string = l10n_util::GetStringUTF16(iter->second);
329  return true;
330};
331
332}  // namespace
333
334namespace chromeos {
335namespace input_method {
336
337std::wstring GetString(const std::string& english_string) {
338  string16 localized_string;
339  if (GetLocalizedString(english_string, &localized_string)) {
340    return UTF16ToWide(localized_string);
341  }
342  return UTF8ToWide(english_string);
343}
344
345std::string GetStringUTF8(const std::string& english_string) {
346  string16 localized_string;
347  if (GetLocalizedString(english_string, &localized_string)) {
348    return UTF16ToUTF8(localized_string);
349  }
350  return english_string;
351}
352
353string16 GetStringUTF16(const std::string& english_string) {
354  string16 localized_string;
355  if (GetLocalizedString(english_string, &localized_string)) {
356    return localized_string;
357  }
358  return UTF8ToUTF16(english_string);
359}
360
361bool StringIsSupported(const std::string& english_string) {
362  string16 localized_string;
363  return GetLocalizedString(english_string, &localized_string);
364}
365
366std::string NormalizeLanguageCode(
367    const std::string& language_code) {
368  // Some ibus engines return locale codes like "zh_CN" as language codes.
369  // Normalize these to like "zh-CN".
370  if (language_code.size() >= 5 && language_code[2] == '_') {
371    std::string copied_language_code = language_code;
372    copied_language_code[2] = '-';
373    // Downcase the language code part.
374    for (size_t i = 0; i < 2; ++i) {
375      copied_language_code[i] = base::ToLowerASCII(copied_language_code[i]);
376    }
377    // Upcase the country code part.
378    for (size_t i = 3; i < copied_language_code.size(); ++i) {
379      copied_language_code[i] = base::ToUpperASCII(copied_language_code[i]);
380    }
381    return copied_language_code;
382  }
383  // We only handle three-letter codes from here.
384  if (language_code.size() != 3) {
385    return language_code;
386  }
387
388  // Convert special language codes. See comments at kIso639VariantMapping.
389  std::string copied_language_code = language_code;
390  for (size_t i = 0; i < arraysize(kIso639VariantMapping); ++i) {
391    if (language_code == kIso639VariantMapping[i][0]) {
392      copied_language_code = kIso639VariantMapping[i][1];
393    }
394  }
395  // Convert the three-letter code to two letter-code.
396  UErrorCode error = U_ZERO_ERROR;
397  char two_letter_code[ULOC_LANG_CAPACITY];
398  uloc_getLanguage(copied_language_code.c_str(),
399                   two_letter_code, sizeof(two_letter_code), &error);
400  if (U_FAILURE(error)) {
401    return language_code;
402  }
403  return two_letter_code;
404}
405
406bool IsKeyboardLayout(const std::string& input_method_id) {
407  const bool kCaseInsensitive = false;
408  return StartsWithASCII(input_method_id, "xkb:", kCaseInsensitive);
409}
410
411std::string GetLanguageCodeFromDescriptor(
412    const InputMethodDescriptor& descriptor) {
413  // Handle some Chinese input methods as zh-CN/zh-TW, rather than zh.
414  // TODO: we should fix this issue in engines rather than here.
415  if (descriptor.language_code == "zh") {
416    if (descriptor.id == "pinyin") {
417      return "zh-CN";
418    } else if (descriptor.id == "bopomofo" ||
419               descriptor.id == "chewing" ||
420               descriptor.id == "m17n:zh:cangjie" ||
421               descriptor.id == "m17n:zh:quick") {
422      return "zh-TW";
423    }
424  }
425
426  std::string language_code = NormalizeLanguageCode(descriptor.language_code);
427
428  // Add country codes to language codes of some XKB input methods to make
429  // these compatible with Chrome's application locale codes like "en-US".
430  // TODO(satorux): Maybe we need to handle "es" for "es-419".
431  // TODO: We should not rely on the format of the engine name. Should we add
432  //       |country_code| in InputMethodDescriptor?
433  if (IsKeyboardLayout(descriptor.id) &&
434      (language_code == "en" ||
435       language_code == "zh" ||
436       language_code == "pt")) {
437    std::vector<std::string> portions;
438    base::SplitString(descriptor.id, ':', &portions);
439    if (portions.size() >= 2 && !portions[1].empty()) {
440      language_code.append("-");
441      language_code.append(StringToUpperASCII(portions[1]));
442    }
443  }
444  return language_code;
445}
446
447std::string GetLanguageCodeFromInputMethodId(
448    const std::string& input_method_id) {
449  // The code should be compatible with one of codes used for UI languages,
450  // defined in app/l10_util.cc.
451  const char kDefaultLanguageCode[] = "en-US";
452  std::map<std::string, std::string>::const_iterator iter
453      = Singleton<IdMaps>::get()->id_to_language_code->find(input_method_id);
454  return (iter == Singleton<IdMaps>::get()->id_to_language_code->end()) ?
455      // Returning |kDefaultLanguageCode| here is not for Chrome OS but for
456      // Ubuntu where the ibus-xkb-layouts engine could be missing.
457      kDefaultLanguageCode : iter->second;
458}
459
460std::string GetKeyboardLayoutName(const std::string& input_method_id) {
461  if (!StartsWithASCII(input_method_id, "xkb:", true)) {
462    return "";
463  }
464
465  std::vector<std::string> splitted_id;
466  base::SplitString(input_method_id, ':', &splitted_id);
467  return (splitted_id.size() > 1) ? splitted_id[1] : "";
468}
469
470std::string GetInputMethodDisplayNameFromId(
471    const std::string& input_method_id) {
472  static const char kDefaultDisplayName[] = "USA";
473  std::map<std::string, std::string>::const_iterator iter
474      = Singleton<IdMaps>::get()->id_to_display_name->find(input_method_id);
475  return (iter == Singleton<IdMaps>::get()->id_to_display_name->end()) ?
476      kDefaultDisplayName : iter->second;
477}
478
479std::wstring GetLanguageDisplayNameFromCode(const std::string& language_code) {
480  if (!g_browser_process) {
481    return L"";
482  }
483  return UTF16ToWide(l10n_util::GetDisplayNameForLocale(
484      language_code, g_browser_process->GetApplicationLocale(), true));
485}
486
487std::wstring GetLanguageNativeDisplayNameFromCode(
488    const std::string& language_code) {
489  return UTF16ToWide(l10n_util::GetDisplayNameForLocale(
490      language_code, language_code, true));
491}
492
493void SortLanguageCodesByNames(std::vector<std::string>* language_codes) {
494  if (!g_browser_process) {
495    return;
496  }
497  // We should build collator outside of the comparator. We cannot have
498  // scoped_ptr<> in the comparator for a subtle STL reason.
499  UErrorCode error = U_ZERO_ERROR;
500  icu::Locale locale(g_browser_process->GetApplicationLocale().c_str());
501  scoped_ptr<icu::Collator> collator(
502      icu::Collator::createInstance(locale, error));
503  if (U_FAILURE(error)) {
504    collator.reset();
505  }
506  std::sort(language_codes->begin(), language_codes->end(),
507            CompareLanguageCodesByLanguageName(collator.get()));
508}
509
510void SortInputMethodIdsByNames(std::vector<std::string>* input_method_ids) {
511  SortInputMethodIdsByNamesInternal(
512      *(Singleton<IdMaps>::get()->id_to_language_code), input_method_ids);
513}
514
515void SortInputMethodIdsByNamesInternal(
516    const std::map<std::string, std::string>& id_to_language_code_map,
517    std::vector<std::string>* input_method_ids) {
518  if (!g_browser_process) {
519    return;
520  }
521  UErrorCode error = U_ZERO_ERROR;
522  icu::Locale locale(g_browser_process->GetApplicationLocale().c_str());
523  scoped_ptr<icu::Collator> collator(
524      icu::Collator::createInstance(locale, error));
525  if (U_FAILURE(error)) {
526    collator.reset();
527  }
528  std::stable_sort(input_method_ids->begin(), input_method_ids->end(),
529                   CompareInputMethodIdsByLanguageName(
530                       collator.get(), id_to_language_code_map));
531}
532
533bool GetInputMethodIdsFromLanguageCode(
534    const std::string& normalized_language_code,
535    InputMethodType type,
536    std::vector<std::string>* out_input_method_ids) {
537  return GetInputMethodIdsFromLanguageCodeInternal(
538      *Singleton<IdMaps>::get()->language_code_to_ids,
539      normalized_language_code, type, out_input_method_ids);
540}
541
542bool GetInputMethodIdsFromLanguageCodeInternal(
543    const std::multimap<std::string, std::string>& language_code_to_ids,
544    const std::string& normalized_language_code,
545    InputMethodType type,
546    std::vector<std::string>* out_input_method_ids) {
547  DCHECK(out_input_method_ids);
548  out_input_method_ids->clear();
549
550  bool result = false;
551  std::pair<LanguageCodeToIdsMap::const_iterator,
552      LanguageCodeToIdsMap::const_iterator> range =
553      language_code_to_ids.equal_range(normalized_language_code);
554  for (LanguageCodeToIdsMap::const_iterator iter = range.first;
555       iter != range.second; ++iter) {
556    const std::string& input_method_id = iter->second;
557    if ((type == kAllInputMethods) || IsKeyboardLayout(input_method_id)) {
558      out_input_method_ids->push_back(input_method_id);
559      result = true;
560    }
561  }
562  if ((type == kAllInputMethods) && !result) {
563    LOG(ERROR) << "Unknown language code: " << normalized_language_code;
564  }
565  return result;
566}
567
568void EnableInputMethods(const std::string& language_code, InputMethodType type,
569                        const std::string& initial_input_method_id) {
570  std::vector<std::string> input_method_ids;
571  GetInputMethodIdsFromLanguageCode(language_code, type, &input_method_ids);
572
573  std::string keyboard = CrosLibrary::Get()->GetKeyboardLibrary()->
574      GetHardwareKeyboardLayoutName();
575  if (std::count(input_method_ids.begin(), input_method_ids.end(),
576                 keyboard) == 0) {
577    input_method_ids.push_back(keyboard);
578  }
579  // First, sort the vector by input method id, then by its display name.
580  std::sort(input_method_ids.begin(), input_method_ids.end());
581  SortInputMethodIdsByNames(&input_method_ids);
582
583  // Update ibus-daemon setting.
584  ImeConfigValue value;
585  value.type = ImeConfigValue::kValueTypeStringList;
586  value.string_list_value = input_method_ids;
587  InputMethodLibrary* library = CrosLibrary::Get()->GetInputMethodLibrary();
588  library->SetImeConfig(language_prefs::kGeneralSectionName,
589                        language_prefs::kPreloadEnginesConfigName, value);
590  if (!initial_input_method_id.empty()) {
591    library->ChangeInputMethod(initial_input_method_id);
592  }
593}
594
595void OnLocaleChanged() {
596  Singleton<IdMaps>::get()->ReloadMaps();
597}
598
599}  // namespace input_method
600}  // namespace chromeos
601