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