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. 47 static IdMaps* GetInstance() { 48 return Singleton<IdMaps>::get(); 49 } 50 51 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: 107 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> { 282 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). 289 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 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 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 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 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 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 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 441 bool IsKeyboardLayout(const std::string& input_method_id) { 442 const bool kCaseInsensitive = false; 443 return StartsWithASCII(input_method_id, "xkb:", kCaseInsensitive); 444 } 445 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 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 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 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 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 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 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 532 string16 GetLanguageNativeDisplayNameFromCode( 533 const std::string& language_code) { 534 return l10n_util::GetDisplayNameForLocale(language_code, language_code, true); 535 } 536 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 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 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 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 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 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 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 728 InputMethodDescriptor GetFallbackInputMethodDescriptor() { 729 return InputMethodDescriptor("xkb:us::eng", "USA", "us", "eng"); 730 } 731 732 void ReloadInternalMaps() { 733 IdMaps::GetInstance()->ReloadMaps(); 734 } 735 736 void OnLocaleChanged() { 737 ReloadInternalMaps(); 738 } 739 740 } // namespace input_method 741 } // namespace chromeos 742