20 #include <freerdp/config.h>
26 #include <winpr/crt.h>
27 #include <winpr/path.h>
28 #include <winpr/json.h>
30 #include "liblocale.h"
32 #include <freerdp/types.h>
33 #include <freerdp/scancode.h>
34 #include <freerdp/locale/keyboard.h>
36 #include <freerdp/log.h>
38 #if defined(WITH_KEYBOARD_LAYOUT_FROM_FILE)
39 #define TAG FREERDP_TAG("locale.keyboard.layouts")
44 struct LanguageIdentifier
52 UINT16 LanguageIdentifier;
53 const char* PrimaryLanguage;
54 UINT8 PrimaryLanguageIdentifier;
55 const char* PrimaryLanguageSymbol;
56 const char* Sublanguage;
57 UINT8 SublangaugeIdentifier;
58 const char* SublanguageSymbol;
66 } RDP_KEYBOARD_LAYOUT_VARIANT;
75 static const struct LanguageIdentifier language_identifiers[] = {
78 {
"", 0xc00,
"Default custom locale language", 0x0,
"LANG_NEUTRAL",
79 "Default custom sublanguage", 0x3,
"SUBLANG_CUSTOM_DEFAULT" },
80 {
"", 0x1400,
"Default custom MUI locale language", 0x0,
"LANG_NEUTRAL",
81 "Default custom MUI sublanguage", 0x5,
"SUBLANG_UI_CUSTOM_DEFAULT" },
82 {
"", 0x7f,
"Invariant locale language", 0x7f,
"LANG_INVARIANT",
"Invariant sublanguage", 0x0,
84 {
"", 0x0,
"Neutral locale language", 0x0,
"LANG_NEUTRAL",
"Neutral sublanguage", 0x0,
86 {
"", 0x800,
"System default locale language", 0x2,
"LANG_SYSTEM_DEFAULT",
87 "System default sublanguage", 0x2,
"SUBLANG_SYS_DEFAULT" },
88 {
"", 0x1000,
"Unspecified custom locale language", 0x0,
"LANG_NEUTRAL",
89 "Unspecified custom sublanguage", 0x4,
"SUBLANG_CUSTOM_UNSPECIFIED" },
90 {
"", 0x400,
"User default locale language", 0x0,
"LANG_USER_DEFAULT",
91 "User default sublanguage", 0x1,
"SUBLANG_DEFAULT" },
92 {
"af_ZA", 0x436,
"Afrikaans (af)", 0x36,
"LANG_AFRIKAANS",
"South Africa (ZA)", 0x1,
93 "SUBLANG_AFRIKAANS_SOUTH_AFRICA" },
94 {
"sq_AL", 0x41c,
"Albanian (sq)", 0x1c,
"LANG_ALBANIAN",
"Albania (AL)", 0x1,
95 "SUBLANG_ALBANIAN_ALBANIA" },
96 {
"gsw_FR", 0x484,
"Alsatian (gsw)", 0x84,
"LANG_ALSATIAN",
"France (FR)", 0x1,
97 "SUBLANG_ALSATIAN_FRANCE" },
98 {
"am_ET", 0x45e,
"Amharic (am)", 0x5e,
"LANG_AMHARIC",
"Ethiopia (ET)", 0x1,
99 "SUBLANG_AMHARIC_ETHIOPIA" },
100 {
"ar_DZ", 0x1401,
"Arabic (ar)", 0x1,
"LANG_ARABIC",
"Algeria (DZ)", 0x5,
101 "SUBLANG_ARABIC_ALGERIA" },
102 {
"ar_BH", 0x3c01,
"Arabic (ar)", 0x01,
"LANG_ARABIC",
"Bahrain (BH)", 0xf,
103 "SUBLANG_ARABIC_BAHRAIN" },
104 {
"ar_EG", 0xc01,
"Arabic (ar)", 0x01,
"LANG_ARABIC",
"Egypt (EG)", 0x3,
105 "SUBLANG_ARABIC_EGYPT" },
106 {
"ar_IQ", 0x801,
"Arabic (ar)", 0x01,
"LANG_ARABIC",
"Iraq (IQ)", 0x2,
"SUBLANG_ARABIC_IRAQ" },
107 {
"ar_JO", 0x2c01,
"Arabic (ar)", 0x01,
"LANG_ARABIC",
"Jordan (JO)", 0xb,
108 "SUBLANG_ARABIC_JORDAN" },
109 {
"ar_KW", 0x3401,
"Arabic (ar)", 0x01,
"LANG_ARABIC",
"Kuwait (KW)", 0xd,
110 "SUBLANG_ARABIC_KUWAIT" },
111 {
"ar_LB", 0x3001,
"Arabic (ar)", 0x01,
"LANG_ARABIC",
"Lebanon (LB)", 0xc,
112 "SUBLANG_ARABIC_LEBANON" },
113 {
"ar_LY", 0x1001,
"Arabic (ar)", 0x01,
"LANG_ARABIC",
"Libya (LY)", 0x4,
114 "SUBLANG_ARABIC_LIBYA" },
115 {
"ar_MA", 0x1801,
"Arabic (ar)", 0x01,
"LANG_ARABIC",
"Morocco (MA)", 0x6,
116 "SUBLANG_ARABIC_MOROCCO" },
117 {
"ar_OM", 0x2001,
"Arabic (ar)", 0x01,
"LANG_ARABIC",
"Oman (OM)", 0x8,
118 "SUBLANG_ARABIC_OMAN" },
119 {
"ar_QA", 0x4001,
"Arabic (ar)", 0x01,
"LANG_ARABIC",
"Qatar (QA)", 0x10,
120 "SUBLANG_ARABIC_QATAR" },
121 {
"ar_SA", 0x401,
"Arabic (ar)", 0x01,
"LANG_ARABIC",
"Saudi Arabia (SA)", 0x1,
122 "SUBLANG_ARABIC_SAUDI_ARABIA" },
123 {
"ar_SY", 0x2801,
"Arabic (ar)", 0x01,
"LANG_ARABIC",
"Syria (SY)", 0xa,
124 "SUBLANG_ARABIC_SYRIA" },
125 {
"ar_TN", 0x1c01,
"Arabic (ar)", 0x01,
"LANG_ARABIC",
"Tunisia (TN)", 0x7,
126 "SUBLANG_ARABIC_TUNISIA" },
127 {
"ar_AE", 0x3801,
"Arabic (ar)", 0x01,
"LANG_ARABIC",
"U.A.E. (AE)", 0xe,
128 "SUBLANG_ARABIC_UAE" },
129 {
"ar_YE", 0x2401,
"Arabic (ar)", 0x01,
"LANG_ARABIC",
"Yemen (YE)", 0x9,
130 "SUBLANG_ARABIC_YEMEN" },
131 {
"hy_AM", 0x42b,
"Armenian (hy)", 0x2b,
"LANG_ARMENIAN",
"Armenia (AM)", 0x1,
132 "SUBLANG_ARMENIAN_ARMENIA" },
133 {
"as_IN", 0x44d,
"Assamese (as)", 0x4d,
"LANG_ASSAMESE",
"India (IN)", 0x1,
134 "SUBLANG_ASSAMESE_INDIA" },
135 {
"az_AZ", 0x82c,
"Azerbaijani (az)", 0x2c,
"LANG_AZERI",
"Azerbaijan, Cyrillic (AZ)", 0x2,
136 "SUBLANG_AZERI_CYRILLIC" },
137 {
"az_AZ", 0x42c,
"Azerbaijani (az)", 0x2c,
"LANG_AZERI",
"Azerbaijan, Latin (AZ)", 0x1,
138 "SUBLANG_AZERI_LATIN" },
139 {
"bn_BD", 0x445,
"Bangla (bn)", 0x45,
"LANG_BANGLA",
"Bangladesh (BD)", 0x2,
140 "SUBLANG_BANGLA_BANGLADESH" },
141 {
"bn_IN", 0x445,
"Bangla (bn)", 0x45,
"LANG_BANGLA",
"India (IN)", 0x1,
142 "SUBLANG_BANGLA_INDIA" },
143 {
"ba_RU", 0x46d,
"Bashkir (ba)", 0x6d,
"LANG_BASHKIR",
"Russia (RU)", 0x1,
144 "SUBLANG_BASHKIR_RUSSIA" },
145 {
"", 0x42d,
"Basque (Basque)", 0x2d,
"LANG_BASQUE",
"Basque (Basque)", 0x1,
146 "SUBLANG_BASQUE_BASQUE" },
147 {
"be_BY", 0x423,
"Belarusian (be)", 0x23,
"LANG_BELARUSIAN",
"Belarus (BY)", 0x1,
148 "SUBLANG_BELARUSIAN_BELARUS" },
149 {
"bs", 0x781a,
"Bosnian (bs)", 0x1a,
"LANG_BOSNIAN_NEUTRAL",
"Neutral", 0x1E,
"" },
150 {
"bs_BA", 0x201a,
"Bosnian (bs)", 0x1a,
"LANG_BOSNIAN",
151 "Bosnia and Herzegovina, Cyrillic (BA)", 0x8,
"SUBLANG_BOSNIAN_BOSNIA_HERZEGOVINA_CYRILLIC" },
152 {
"bs_BA", 0x141a,
"Bosnian (bs)", 0x1a,
"LANG_BOSNIAN",
"Bosnia and Herzegovina, Latin (BA)",
153 0x5,
"SUBLANG_BOSNIAN_BOSNIA_HERZEGOVINA_LATIN" },
154 {
"br_FR", 0x47e,
"Breton (br)", 0x7e,
"LANG_BRETON",
"France (FR)", 0x1,
155 "SUBLANG_BRETON_FRANCE" },
156 {
"bg_BG", 0x402,
"Bulgarian (bg)", 0x2,
"LANG_BULGARIAN",
"Bulgaria (BG)", 0x1,
157 "SUBLANG_BULGARIAN_BULGARIA" },
158 {
"ku_IQ", 0x492,
"Central Kurdish (ku)", 0x92,
"LANG_CENTRAL_KURDISH",
"Iraq (IQ)", 0x1,
159 "SUBLANG_CENTRAL_KURDISH_IRAQ" },
160 {
"chr_US", 0x45c,
"Cherokee (chr)", 0x5c,
"LANG_CHEROKEE",
"Cherokee (Cher)", 0x1,
161 "SUBLANG_CHEROKEE_CHEROKEE" },
162 {
"ca_ES", 0x403,
"Catalan (ca)", 0x3,
"LANG_CATALAN",
"Spain (ES)", 0x1,
163 "SUBLANG_CATALAN_CATALAN" },
164 {
"zh_HK", 0xc04,
"Chinese (zh)", 0x04,
"LANG_CHINESE",
"Hong Kong SAR, PRC (HK)", 0x3,
165 "SUBLANG_CHINESE_HONGKONG" },
166 {
"zh_MO", 0x1404,
"Chinese (zh)", 0x04,
"LANG_CHINESE",
"Macao SAR (MO)", 0x5,
167 "SUBLANG_CHINESE_MACAU" },
168 {
"zh_SG", 0x1004,
"Chinese (zh)", 0x04,
"LANG_CHINESE",
"Singapore (SG)", 0x4,
169 "SUBLANG_CHINESE_SINGAPORE" },
170 {
"zh_CN", 0x4,
"Chinese (zh)", 0x4,
"LANG_CHINESE_SIMPLIFIED",
"Simplified (Hans)", 0x2,
171 "SUBLANG_CHINESE_SIMPLIFIED" },
172 {
"zh_CN", 0x7c04,
"Chinese (zh)", 0x04,
"LANG_CHINESE_TRADITIONAL",
"Traditional (Hant)", 0x1,
173 "SUBLANG_CHINESE_TRADITIONAL" },
174 {
"co_FR", 0x483,
"Corsican (co)", 0x83,
"LANG_CORSICAN",
"France (FR)", 0x1,
175 "SUBLANG_CORSICAN_FRANCE" },
176 {
"hr", 0x1a,
"Croatian (hr)", 0x1a,
"LANG_CROATIAN",
"Neutral", 0x00,
"" },
177 {
"hr_BA", 0x101a,
"Croatian (hr)", 0x1a,
"LANG_CROATIAN",
"Bosnia and Herzegovina, Latin (BA)",
178 0x4,
"SUBLANG_CROATIAN_BOSNIA_HERZEGOVINA_LATIN" },
179 {
"hr_HR", 0x41a,
"Croatian (hr)", 0x1a,
"LANG_CROATIAN",
"Croatia (HR)", 0x1,
180 "SUBLANG_CROATIAN_CROATIA" },
181 {
"cs_CZ", 0x405,
"Czech (cs)", 0x5,
"LANG_CZECH",
"Czech Republic (CZ)", 0x1,
182 "SUBLANG_CZECH_CZECH_REPUBLIC" },
183 {
"da_DK", 0x406,
"Danish (da)", 0x6,
"LANG_DANISH",
"Denmark (DK)", 0x1,
184 "SUBLANG_DANISH_DENMARK" },
185 {
"prs_AF", 0x48c,
"Dari (prs)", 0x8c,
"LANG_DARI",
"Afghanistan (AF)", 0x1,
186 "SUBLANG_DARI_AFGHANISTAN" },
187 {
"dv_MV", 0x465,
"Divehi (dv)", 0x65,
"LANG_DIVEHI",
"Maldives (MV)", 0x1,
188 "SUBLANG_DIVEHI_MALDIVES" },
189 {
"nl_BE", 0x813,
"Dutch (nl)", 0x13,
"LANG_DUTCH",
"Belgium (BE)", 0x2,
190 "SUBLANG_DUTCH_BELGIAN" },
191 {
"nl_NL", 0x413,
"Dutch (nl)", 0x13,
"LANG_DUTCH",
"Netherlands (NL)", 0x1,
"SUBLANG_DUTCH" },
192 {
"en_AU", 0xc09,
"English (en)", 0x9,
"LANG_ENGLISH",
"Australia (AU)", 0x3,
193 "SUBLANG_ENGLISH_AUS" },
194 {
"en_BZ", 0x2809,
"English (en)", 0x09,
"LANG_ENGLISH",
"Belize (BZ)", 0xa,
195 "SUBLANG_ENGLISH_BELIZE" },
196 {
"en_CA", 0x1009,
"English (en)", 0x09,
"LANG_ENGLISH",
"Canada (CA)", 0x4,
197 "SUBLANG_ENGLISH_CAN" },
198 {
"en_CB", 0x2409,
"English (en)", 0x09,
"LANG_ENGLISH",
"Caribbean (029)", 0x9,
199 "SUBLANG_ENGLISH_CARIBBEAN" },
200 {
"en_IN", 0x4009,
"English (en)", 0x09,
"LANG_ENGLISH",
"India (IN)", 0x10,
201 "SUBLANG_ENGLISH_INDIA" },
202 {
"en_IE", 0x1809,
"English (en)", 0x09,
"LANG_ENGLISH",
"Ireland (IE)", 0x6,
203 "SUBLANG_ENGLISH_EIRE" },
204 {
"en_IE", 0x1809,
"English (en)", 0x09,
"LANG_ENGLISH",
"Ireland (IE)", 0x6,
205 "SUBLANG_ENGLISH_IRELAND" },
206 {
"en_JM", 0x2009,
"English (en)", 0x09,
"LANG_ENGLISH",
"Jamaica (JM)", 0x8,
207 "SUBLANG_ENGLISH_JAMAICA" },
208 {
"en_MY", 0x4409,
"English (en)", 0x09,
"LANG_ENGLISH",
"Malaysia (MY)", 0x11,
209 "SUBLANG_ENGLISH_MALAYSIA" },
210 {
"en_NZ", 0x1409,
"English (en)", 0x09,
"LANG_ENGLISH",
"New Zealand (NZ)", 0x5,
211 "SUBLANG_ENGLISH_NZ" },
212 {
"en_PH", 0x3409,
"English (en)", 0x09,
"LANG_ENGLISH",
"Philippines (PH)", 0xd,
213 "SUBLANG_ENGLISH_PHILIPPINES" },
214 {
"en_SG", 0x4809,
"English (en)", 0x09,
"LANG_ENGLISH",
"Singapore (SG)", 0x12,
215 "SUBLANG_ENGLISH_SINGAPORE" },
216 {
"en_ZA", 0x1c09,
"English (en)", 0x09,
"LANG_ENGLISH",
"South Africa (ZA)", 0x7,
217 "SUBLANG_ENGLISH_SOUTH_AFRICA" },
218 {
"en_TT", 0x2c09,
"English (en)", 0x09,
"LANG_ENGLISH",
"Trinidad and Tobago (TT)", 0xb,
219 "SUBLANG_ENGLISH_TRINIDAD" },
220 {
"en_GB", 0x809,
"English (en)", 0x09,
"LANG_ENGLISH",
"United Kingdom (GB)", 0x2,
221 "SUBLANG_ENGLISH_UK" },
222 {
"en_US", 0x409,
"English (en)", 0x09,
"LANG_ENGLISH",
"United States (US)", 0x1,
223 "SUBLANG_ENGLISH_US" },
224 {
"en_ZW", 0x3009,
"English (en)", 0x09,
"LANG_ENGLISH",
"Zimbabwe (ZW)", 0xc,
225 "SUBLANG_ENGLISH_ZIMBABWE" },
226 {
"et_EE", 0x425,
"Estonian (et)", 0x25,
"LANG_ESTONIAN",
"Estonia (EE)", 0x1,
227 "SUBLANG_ESTONIAN_ESTONIA" },
228 {
"fo_FO", 0x438,
"Faroese (fo)", 0x38,
"LANG_FAEROESE",
"Faroe Islands (FO)", 0x1,
229 "SUBLANG_FAEROESE_FAROE_ISLANDS" },
230 {
"fil_PH", 0x464,
"Filipino (fil)", 0x64,
"LANG_FILIPINO",
"Philippines (PH)", 0x1,
231 "SUBLANG_FILIPINO_PHILIPPINES" },
232 {
"fi_FI", 0x40b,
"Finnish (fi)", 0xb,
"LANG_FINNISH",
"Finland (FI)", 0x1,
233 "SUBLANG_FINNISH_FINLAND" },
234 {
"fr_BE", 0x80c,
"French (fr)", 0xc,
"LANG_FRENCH",
"Belgium (BE)", 0x2,
235 "SUBLANG_FRENCH_BELGIAN" },
236 {
"fr_CA", 0xc0c,
"French (fr)", 0x0c,
"LANG_FRENCH",
"Canada (CA)", 0x3,
237 "SUBLANG_FRENCH_CANADIAN" },
238 {
"fr_FR", 0x40c,
"French (fr)", 0x0c,
"LANG_FRENCH",
"France (FR)", 0x1,
"SUBLANG_FRENCH" },
239 {
"fr_LU", 0x140c,
"French (fr)", 0x0c,
"LANG_FRENCH",
"Luxembourg (LU)", 0x5,
240 "SUBLANG_FRENCH_LUXEMBOURG" },
241 {
"fr_MC", 0x180c,
"French (fr)", 0x0c,
"LANG_FRENCH",
"Monaco (MC)", 0x6,
242 "SUBLANG_FRENCH_MONACO" },
243 {
"fr_CH", 0x100c,
"French (fr)", 0x0c,
"LANG_FRENCH",
"Switzerland (CH)", 0x4,
244 "SUBLANG_FRENCH_SWISS" },
245 {
"fy_NL", 0x462,
"Frisian (fy)", 0x62,
"LANG_FRISIAN",
"Netherlands (NL)", 0x1,
246 "SUBLANG_FRISIAN_NETHERLANDS" },
247 {
"gl_ES", 0x456,
"Galician (gl)", 0x56,
"LANG_GALICIAN",
"Spain (ES)", 0x1,
248 "SUBLANG_GALICIAN_GALICIAN" },
249 {
"ka_GE", 0x437,
"Georgian (ka)", 0x37,
"LANG_GEORGIAN",
"Georgia (GE)", 0x1,
250 "SUBLANG_GEORGIAN_GEORGIA" },
251 {
"de_AT", 0xc07,
"German (de)", 0x7,
"LANG_GERMAN",
"Austria (AT)", 0x3,
252 "SUBLANG_GERMAN_AUSTRIAN" },
253 {
"de_DE", 0x407,
"German (de)", 0x07,
"LANG_GERMAN",
"Germany (DE)", 0x1,
"SUBLANG_GERMAN" },
254 {
"de_LI", 0x1407,
"German (de)", 0x07,
"LANG_GERMAN",
"Liechtenstein (LI)", 0x5,
255 "SUBLANG_GERMAN_LIECHTENSTEIN" },
256 {
"de_LU", 0x1007,
"German (de)", 0x07,
"LANG_GERMAN",
"Luxembourg (LU)", 0x4,
257 "SUBLANG_GERMAN_LUXEMBOURG" },
258 {
"de_CH", 0x807,
"German (de)", 0x07,
"LANG_GERMAN",
"Switzerland (CH)", 0x2,
259 "SUBLANG_GERMAN_SWISS" },
260 {
"el_GR", 0x408,
"Greek (el)", 0x8,
"LANG_GREEK",
"Greece (GR)", 0x1,
"SUBLANG_GREEK_GREECE" },
261 {
"kl_GL", 0x46f,
"Greenlandic (kl)", 0x6f,
"LANG_GREENLANDIC",
"Greenland (GL)", 0x1,
262 "SUBLANG_GREENLANDIC_GREENLAND" },
263 {
"gu_IN", 0x447,
"Gujarati (gu)", 0x47,
"LANG_GUJARATI",
"India (IN)", 0x1,
264 "SUBLANG_GUJARATI_INDIA" },
265 {
"ha_NG", 0x468,
"Hausa (ha)", 0x68,
"LANG_HAUSA",
"Nigeria (NG)", 0x1,
266 "SUBLANG_HAUSA_NIGERIA_LATIN" },
267 {
"haw_US", 0x475,
"Hawiian (haw)", 0x75,
"LANG_HAWAIIAN",
"United States (US)", 0x1,
268 "SUBLANG_HAWAIIAN_US" },
269 {
"he_IL", 0x40d,
"Hebrew (he)", 0xd,
"LANG_HEBREW",
"Israel (IL)", 0x1,
270 "SUBLANG_HEBREW_ISRAEL" },
271 {
"hi_IN", 0x439,
"Hindi (hi)", 0x39,
"LANG_HINDI",
"India (IN)", 0x1,
"SUBLANG_HINDI_INDIA" },
272 {
"hu_HU", 0x40e,
"Hungarian (hu)", 0xe,
"LANG_HUNGARIAN",
"Hungary (HU)", 0x1,
273 "SUBLANG_HUNGARIAN_HUNGARY" },
274 {
"is_IS", 0x40f,
"Icelandic (is)", 0xf,
"LANG_ICELANDIC",
"Iceland (IS)", 0x1,
275 "SUBLANG_ICELANDIC_ICELAND" },
276 {
"ig_NG", 0x470,
"Igbo (ig)", 0x70,
"LANG_IGBO",
"Nigeria (NG)", 0x1,
"SUBLANG_IGBO_NIGERIA" },
277 {
"id_ID", 0x421,
"Indonesian (id)", 0x21,
"LANG_INDONESIAN",
"Indonesia (ID)", 0x1,
278 "SUBLANG_INDONESIAN_INDONESIA" },
279 {
"iu_CA", 0x85d,
"Inuktitut (iu)", 0x5d,
"LANG_INUKTITUT",
"Canada (CA), Latin", 0x2,
280 "SUBLANG_INUKTITUT_CANADA_LATIN" },
281 {
"iu_CA", 0x45d,
"Inuktitut (iu)", 0x5d,
"LANG_INUKTITUT",
"Canada (CA), Canadian Syllabics",
282 0x1,
"SUBLANG_INUKTITUT_CANADA" },
283 {
"ga_IE", 0x83c,
"Irish (ga)", 0x3c,
"LANG_IRISH",
"Ireland (IE)", 0x2,
284 "SUBLANG_IRISH_IRELAND" },
285 {
"xh_ZA", 0x434,
"isiXhosa (xh)", 0x34,
"LANG_XHOSA",
"South Africa (ZA)", 0x1,
286 "SUBLANG_XHOSA_SOUTH_AFRICA" },
287 {
"zu_ZA", 0x435,
"isiZulu (zu)", 0x35,
"LANG_ZULU",
"South Africa (ZA)", 0x1,
288 "SUBLANG_ZULU_SOUTH_AFRICA" },
289 {
"it_IT", 0x410,
"Italian (it)", 0x10,
"LANG_ITALIAN",
"Italy (IT)", 0x1,
"SUBLANG_ITALIAN" },
290 {
"it_CH", 0x810,
"Italian (it)", 0x10,
"LANG_ITALIAN",
"Switzerland (CH)", 0x2,
291 "SUBLANG_ITALIAN_SWISS" },
292 {
"ja_JP", 0x411,
"Japanese (ja)", 0x11,
"LANG_JAPANESE",
"Japan (JP)", 0x1,
293 "SUBLANG_JAPANESE_JAPAN" },
294 {
"kn_IN", 0x44b,
"Kannada (kn)", 0x4b,
"LANG_KANNADA",
"India (IN)", 0x1,
295 "SUBLANG_KANNADA_INDIA" },
296 {
"kk_KZ", 0x43f,
"Kazakh (kk)", 0x3f,
"LANG_KAZAK",
"Kazakhstan (KZ)", 0x1,
297 "SUBLANG_KAZAK_KAZAKHSTAN" },
298 {
"kh_KH", 0x453,
"Khmer (kh)", 0x53,
"LANG_KHMER",
"Cambodia (KH)", 0x1,
299 "SUBLANG_KHMER_CAMBODIA" },
300 {
"qut_GT", 0x486,
"K'iche (qut)", 0x86,
"LANG_KICHE",
"Guatemala (GT)", 0x1,
301 "SUBLANG_KICHE_GUATEMALA" },
302 {
"rw_RW", 0x487,
"Kinyarwanda (rw)", 0x87,
"LANG_KINYARWANDA",
"Rwanda (RW)", 0x1,
303 "SUBLANG_KINYARWANDA_RWANDA" },
304 {
"kok_IN", 0x457,
"Konkani (kok)", 0x57,
"LANG_KONKANI",
"India (IN)", 0x1,
305 "SUBLANG_KONKANI_INDIA" },
306 {
"ko_KR", 0x412,
"Korean (ko)", 0x12,
"LANG_KOREAN",
"Korea (KR)", 0x1,
"SUBLANG_KOREAN" },
307 {
"ky_KG", 0x440,
"Kyrgyz (ky)", 0x40,
"LANG_KYRGYZ",
"Kyrgyzstan (KG)", 0x1,
308 "SUBLANG_KYRGYZ_KYRGYZSTAN" },
309 {
"lo_LA", 0x454,
"Lao (lo)", 0x54,
"LANG_LAO",
"Lao PDR (LA)", 0x1,
"SUBLANG_LAO_LAO" },
310 {
"lv_LV", 0x426,
"Latvian (lv)", 0x26,
"LANG_LATVIAN",
"Latvia (LV)", 0x1,
311 "SUBLANG_LATVIAN_LATVIA" },
312 {
"lt_LT", 0x427,
"Lithuanian (lt)", 0x27,
"LANG_LITHUANIAN",
"Lithuanian (LT)", 0x1,
313 "SUBLANG_LITHUANIAN_LITHUANIA" },
314 {
"dsb_DE", 0x82e,
"Lower Sorbian (dsb)", 0x2e,
"LANG_LOWER_SORBIAN",
"Germany (DE)", 0x2,
315 "SUBLANG_LOWER_SORBIAN_GERMANY" },
316 {
"lb_LU", 0x46e,
"Luxembourgish (lb)", 0x6e,
"LANG_LUXEMBOURGISH",
"Luxembourg (LU)", 0x1,
317 "SUBLANG_LUXEMBOURGISH_LUXEMBOURG" },
318 {
"mk_MK", 0x42f,
"Macedonian (mk)", 0x2f,
"LANG_MACEDONIAN",
"Macedonia (FYROM) (MK)", 0x1,
319 "SUBLANG_MACEDONIAN_MACEDONIA" },
320 {
"ms_BN", 0x83e,
"Malay (ms)", 0x3e,
"LANG_MALAY",
"Brunei Darassalam (BN)", 0x2,
321 "SUBLANG_MALAY_BRUNEI_DARUSSALAM" },
322 {
"ms_MY", 0x43e,
"Malay (ms)", 0x3e,
"LANG_MALAY",
"Malaysia (MY)", 0x1,
323 "SUBLANG_MALAY_MALAYSIA" },
324 {
"ml_IN", 0x44c,
"Malayalam (ml)", 0x4c,
"LANG_MALAYALAM",
"India (IN)", 0x1,
325 "SUBLANG_MALAYALAM_INDIA" },
326 {
"mt_MT", 0x43a,
"Maltese (mt)", 0x3a,
"LANG_MALTESE",
"Malta (MT)", 0x1,
327 "SUBLANG_MALTESE_MALTA" },
328 {
"mi_NZ", 0x481,
"Maori (mi)", 0x81,
"LANG_MAORI",
"New Zealand (NZ)", 0x1,
329 "SUBLANG_MAORI_NEW_ZEALAND" },
330 {
"arn_CL", 0x47a,
"Mapudungun (arn)", 0x7a,
"LANG_MAPUDUNGUN",
"Chile (CL)", 0x1,
331 "SUBLANG_MAPUDUNGUN_CHILE" },
332 {
"mr_IN", 0x44e,
"Marathi (mr)", 0x4e,
"LANG_MARATHI",
"India (IN)", 0x1,
333 "SUBLANG_MARATHI_INDIA" },
334 {
"moh_CA", 0x47c,
"Mohawk (moh)", 0x7c,
"LANG_MOHAWK",
"Canada (CA)", 0x1,
335 "SUBLANG_MOHAWK_MOHAWK" },
336 {
"mn_MN", 0x450,
"Mongolian (mn)", 0x50,
"LANG_MONGOLIAN",
"Mongolia, Cyrillic (MN)", 0x1,
337 "SUBLANG_MONGOLIAN_CYRILLIC_MONGOLIA" },
338 {
"mn_MN", 0x850,
"Mongolian (mn)", 0x50,
"LANG_MONGOLIAN",
"Mongolia, Mong (MN)", 0x2,
339 "SUBLANG_MONGOLIAN_PRC" },
340 {
"ne_NP", 0x461,
"Nepali (ne)", 0x61,
"LANG_NEPALI",
"Nepal (NP)", 0x1,
341 "SUBLANG_NEPALI_NEPAL" },
342 {
"ne_IN", 0x861,
"Nepali (ne)", 0x61,
"LANG_NEPALI",
"India (IN)", 0x2,
343 "SUBLANG_NEPALI_INDIA" },
344 {
"no_NO", 0x414,
"Norwegian (no)", 0x14,
"LANG_NORWEGIAN",
"Bokmål, Norway (NO)", 0x1,
345 "SUBLANG_NORWEGIAN_BOKMAL" },
346 {
"no_NO", 0x814,
"Norwegian (no)", 0x14,
"LANG_NORWEGIAN",
"Nynorsk, Norway (NO)", 0x2,
347 "SUBLANG_NORWEGIAN_NYNORSK" },
348 {
"oc_FR", 0x482,
"Occitan (oc)", 0x82,
"LANG_OCCITAN",
"France (FR)", 0x1,
349 "SUBLANG_OCCITAN_FRANCE" },
350 {
"or_IN", 0x448,
"Odia (or)", 0x48,
"LANG_ORIYA",
"India (IN)", 0x1,
"SUBLANG_ORIYA_INDIA" },
351 {
"ps_AF", 0x463,
"Pashto (ps)", 0x63,
"LANG_PASHTO",
"Afghanistan (AF)", 0x1,
352 "SUBLANG_PASHTO_AFGHANISTAN" },
353 {
"fa_IR", 0x429,
"Persian (fa)", 0x29,
"LANG_PERSIAN",
"Iran (IR)", 0x1,
354 "SUBLANG_PERSIAN_IRAN" },
355 {
"pl_PL", 0x415,
"Polish (pl)", 0x15,
"LANG_POLISH",
"Poland (PL)", 0x1,
356 "SUBLANG_POLISH_POLAND" },
357 {
"pt_BR", 0x416,
"Portuguese (pt)", 0x16,
"LANG_PORTUGUESE",
"Brazil (BR)", 0x1,
358 "SUBLANG_PORTUGUESE_BRAZILIAN" },
359 {
"pt_PT", 0x816,
"Portuguese (pt)", 0x16,
"LANG_PORTUGUESE",
"Portugal (PT)", 0x2,
360 "SUBLANG_PORTUGUESE" },
361 {
"ff_SN", 0x867,
"Pular (ff)", 0x67,
"LANG_PULAR",
"Senegal (SN)", 0x2,
362 "SUBLANG_PULAR_SENEGAL" },
363 {
"pa_IN", 0x446,
"Punjabi (pa)", 0x46,
"LANG_PUNJABI",
"India, Gurmukhi script (IN)", 0x1,
364 "SUBLANG_PUNJABI_INDIA" },
365 {
"pa_PK", 0x846,
"Punjabi (pa)", 0x46,
"LANG_PUNJABI",
"Pakistan, Arabic script(PK)", 0x2,
366 "SUBLANG_PUNJABI_PAKISTAN" },
367 {
"quz_BO", 0x46b,
"Quechua (quz)", 0x6b,
"LANG_QUECHUA",
"Bolivia (BO)", 0x1,
368 "SUBLANG_QUECHUA_BOLIVIA" },
369 {
"quz_EC", 0x86b,
"Quechua (quz)", 0x6b,
"LANG_QUECHUA",
"Ecuador (EC)", 0x2,
370 "SUBLANG_QUECHUA_ECUADOR" },
371 {
"quz_PE", 0xc6b,
"Quechua (quz)", 0x6b,
"LANG_QUECHUA",
"Peru (PE)", 0x3,
372 "SUBLANG_QUECHUA_PERU" },
373 {
"ro_RO", 0x418,
"Romanian (ro)", 0x18,
"LANG_ROMANIAN",
"Romania (RO)", 0x1,
374 "SUBLANG_ROMANIAN_ROMANIA" },
375 {
"rm_CH", 0x417,
"Romansh (rm)", 0x17,
"LANG_ROMANSH",
"Switzerland (CH)", 0x1,
376 "SUBLANG_ROMANSH_SWITZERLAND" },
377 {
"ru_RU", 0x419,
"Russian (ru)", 0x19,
"LANG_RUSSIAN",
"Russia (RU)", 0x1,
378 "SUBLANG_RUSSIAN_RUSSIA" },
379 {
"sah_RU", 0x485,
"Sakha (sah)", 0x85,
"LANG_SAKHA",
"Russia (RU)", 0x1,
380 "SUBLANG_SAKHA_RUSSIA" },
381 {
"smn_FI", 0x243b,
"Sami (smn)", 0x3b,
"LANG_SAMI",
"Inari, Finland (FI)", 0x9,
382 "SUBLANG_SAMI_INARI_FINLAND" },
383 {
"smj_NO", 0x103b,
"Sami (smj)", 0x3b,
"LANG_SAMI",
"Lule, Norway (NO)", 0x4,
384 "SUBLANG_SAMI_LULE_NORWAY" },
385 {
"smj_SE", 0x143b,
"Sami (smj)", 0x3b,
"LANG_SAMI",
"Lule, Sweden (SE)", 0x5,
386 "SUBLANG_SAMI_LULE_SWEDEN" },
387 {
"se_FI", 0xc3b,
"Sami (se)", 0x3b,
"LANG_SAMI",
"Northern, Finland (FI)", 0x3,
388 "SUBLANG_SAMI_NORTHERN_FINLAND" },
389 {
"se_NO", 0x43b,
"Sami (se)", 0x3b,
"LANG_SAMI",
"Northern, Norway (NO)", 0x1,
390 "SUBLANG_SAMI_NORTHERN_NORWAY" },
391 {
"se_SE", 0x83b,
"Sami (se)", 0x3b,
"LANG_SAMI",
"Northern, Sweden (SE)", 0x2,
392 "SUBLANG_SAMI_NORTHERN_SWEDEN" },
393 {
"sms_FI", 0x203b,
"Sami (sms)", 0x3b,
"LANG_SAMI",
"Skolt, Finland (FI)", 0x8,
394 "SUBLANG_SAMI_SKOLT_FINLAND" },
395 {
"sma_NO", 0x183b,
"Sami (sma)", 0x3b,
"LANG_SAMI",
"Southern, Norway (NO)", 0x6,
396 "SUBLANG_SAMI_SOUTHERN_NORWAY" },
397 {
"sma_SE", 0x1c3b,
"Sami (sma)", 0x3b,
"LANG_SAMI",
"Southern, Sweden (SE)", 0x7,
398 "SUBLANG_SAMI_SOUTHERN_SWEDEN" },
399 {
"sa_IN", 0x44f,
"Sanskrit (sa)", 0x4f,
"LANG_SANSKRIT",
"India (IN)", 0x1,
400 "SUBLANG_SANSKRIT_INDIA" },
401 {
"sr", 0x7c1a,
"Serbian (sr)", 0x1a,
"LANG_SERBIAN_NEUTRAL",
"Neutral", 0x00,
"" },
402 {
"sr_BA", 0x1c1a,
"Serbian (sr)", 0x1a,
"LANG_SERBIAN",
403 "Bosnia and Herzegovina, Cyrillic (BA)", 0x7,
"SUBLANG_SERBIAN_BOSNIA_HERZEGOVINA_CYRILLIC" },
404 {
"sr_BA", 0x181a,
"Serbian (sr)", 0x1a,
"LANG_SERBIAN",
"Bosnia and Herzegovina, Latin (BA)",
405 0x6,
"SUBLANG_SERBIAN_BOSNIA_HERZEGOVINA_LATIN" },
406 {
"sr_HR", 0x41a,
"Serbian (sr)", 0x1a,
"LANG_SERBIAN",
"Croatia (HR)", 0x1,
407 "SUBLANG_SERBIAN_CROATIA" },
408 {
"sr_CS", 0xc1a,
"Serbian (sr)", 0x1a,
"LANG_SERBIAN",
409 "Serbia and Montenegro (former), Cyrillic (CS)", 0x3,
"SUBLANG_SERBIAN_CYRILLIC" },
410 {
"sr_CS", 0x81a,
"Serbian (sr)", 0x1a,
"LANG_SERBIAN",
411 "Serbia and Montenegro (former), Latin (CS)", 0x2,
"SUBLANG_SERBIAN_LATIN" },
412 {
"nso_ZA", 0x46c,
"Sesotho sa Leboa (nso)", 0x6c,
"LANG_SOTHO",
"South Africa (ZA)", 0x1,
413 "SUBLANG_SOTHO_NORTHERN_SOUTH_AFRICA" },
414 {
"tn_BW", 0x832,
"Setswana / Tswana (tn)", 0x32,
"LANG_TSWANA",
"Botswana (BW)", 0x2,
415 "SUBLANG_TSWANA_BOTSWANA" },
416 {
"tn_ZA", 0x432,
"Setswana / Tswana (tn)", 0x32,
"LANG_TSWANA",
"South Africa (ZA)", 0x1,
417 "SUBLANG_TSWANA_SOUTH_AFRICA" },
418 {
"", 0x859,
"(reserved)", 0x59,
"LANG_SINDHI",
"(reserved)", 0x2,
419 "SUBLANG_SINDHI_AFGHANISTAN" },
420 {
"", 0x459,
"(reserved)", 0x59,
"LANG_SINDHI",
"(reserved)", 0x1,
"SUBLANG_SINDHI_INDIA" },
421 {
"sd_PK", 0x859,
"Sindhi (sd)", 0x59,
"LANG_SINDHI",
"Pakistan (PK)", 0x2,
422 "SUBLANG_SINDHI_PAKISTAN" },
423 {
"si_LK", 0x45b,
"Sinhala (si)", 0x5b,
"LANG_SINHALESE",
"Sri Lanka (LK)", 0x1,
424 "SUBLANG_SINHALESE_SRI_LANKA" },
425 {
"sk_SK", 0x41b,
"Slovak (sk)", 0x1b,
"LANG_SLOVAK",
"Slovakia (SK)", 0x1,
426 "SUBLANG_SLOVAK_SLOVAKIA" },
427 {
"sl_SI", 0x424,
"Slovenian (sl)", 0x24,
"LANG_SLOVENIAN",
"Slovenia (SI)", 0x1,
428 "SUBLANG_SLOVENIAN_SLOVENIA" },
429 {
"es_AR", 0x2c0a,
"Spanish (es)", 0xa,
"LANG_SPANISH",
"Argentina (AR)", 0xb,
430 "SUBLANG_SPANISH_ARGENTINA" },
431 {
"es_BO", 0x400a,
"Spanish (es)", 0x0a,
"LANG_SPANISH",
"Bolivia (BO)", 0x10,
432 "SUBLANG_SPANISH_BOLIVIA" },
433 {
"es_CL", 0x340a,
"Spanish (es)", 0x0a,
"LANG_SPANISH",
"Chile (CL)", 0xd,
434 "SUBLANG_SPANISH_CHILE" },
435 {
"es_CO", 0x240a,
"Spanish (es)", 0x0a,
"LANG_SPANISH",
"Colombia (CO)", 0x9,
436 "SUBLANG_SPANISH_COLOMBIA" },
437 {
"es_CR", 0x140a,
"Spanish (es)", 0x0a,
"LANG_SPANISH",
"Costa Rica (CR)", 0x5,
438 "SUBLANG_SPANISH_COSTA_RICA" },
439 {
"es_DO", 0x1c0a,
"Spanish (es)", 0x0a,
"LANG_SPANISH",
"Dominican Republic (DO)", 0x7,
440 "SUBLANG_SPANISH_DOMINICAN_REPUBLIC" },
441 {
"es_EC", 0x300a,
"Spanish (es)", 0x0a,
"LANG_SPANISH",
"Ecuador (EC)", 0xc,
442 "SUBLANG_SPANISH_ECUADOR" },
443 {
"es_SV", 0x440a,
"Spanish (es)", 0x0a,
"LANG_SPANISH",
"El Salvador (SV)", 0x11,
444 "SUBLANG_SPANISH_EL_SALVADOR" },
445 {
"es_GT", 0x100a,
"Spanish (es)", 0x0a,
"LANG_SPANISH",
"Guatemala (GT)", 0x4,
446 "SUBLANG_SPANISH_GUATEMALA" },
447 {
"es_HN", 0x480a,
"Spanish (es)", 0x0a,
"LANG_SPANISH",
"Honduras (HN)", 0x12,
448 "SUBLANG_SPANISH_HONDURAS" },
449 {
"es_MX", 0x80a,
"Spanish (es)", 0x0a,
"LANG_SPANISH",
"Mexico (MX)", 0x2,
450 "SUBLANG_SPANISH_MEXICAN" },
451 {
"es_NI", 0x4c0a,
"Spanish (es)", 0x0a,
"LANG_SPANISH",
"Nicaragua (NI)", 0x13,
452 "SUBLANG_SPANISH_NICARAGUA" },
453 {
"es_PA", 0x180a,
"Spanish (es)", 0x0a,
"LANG_SPANISH",
"Panama (PA)", 0x6,
454 "SUBLANG_SPANISH_PANAMA" },
455 {
"es_PY", 0x3c0a,
"Spanish (es)", 0x0a,
"LANG_SPANISH",
"Paraguay (PY)", 0xf,
456 "SUBLANG_SPANISH_PARAGUAY" },
457 {
"es_PE", 0x280a,
"Spanish (es)", 0x0a,
"LANG_SPANISH",
"Peru (PE)", 0xa,
458 "SUBLANG_SPANISH_PERU" },
459 {
"es_PR", 0x500a,
"Spanish (es)", 0x0a,
"LANG_SPANISH",
"Puerto Rico (PR)", 0x14,
460 "SUBLANG_SPANISH_PUERTO_RICO" },
461 {
"es_ES", 0xc0a,
"Spanish (es)", 0x0a,
"LANG_SPANISH",
"Spain, Modern Sort (ES)", 0x3,
462 "SUBLANG_SPANISH_MODERN" },
463 {
"es_ES", 0x40a,
"Spanish (es)", 0x0a,
"LANG_SPANISH",
"Spain, Traditional Sort (ES)", 0x1,
465 {
"es_US", 0x540a,
"Spanish (es)", 0x0a,
"LANG_SPANISH",
"United States (US)", 0x15,
466 "SUBLANG_SPANISH_US" },
467 {
"es_UY", 0x380a,
"Spanish (es)", 0x0a,
"LANG_SPANISH",
"Uruguay (UY)", 0xe,
468 "SUBLANG_SPANISH_URUGUAY" },
469 {
"es_VE", 0x200a,
"Spanish (es)", 0x0a,
"LANG_SPANISH",
"Venezuela (VE)", 0x8,
470 "SUBLANG_SPANISH_VENEZUELA" },
471 {
"sw_KE", 0x441,
"Swahili (sw)", 0x41,
"LANG_SWAHILI",
"Kenya (KE)", 0x1,
"SUBLANG_SWAHILI" },
472 {
"sv_FI", 0x81d,
"Swedish (sv)", 0x1d,
"LANG_SWEDISH",
"Finland (FI)", 0x2,
473 "SUBLANG_SWEDISH_FINLAND" },
474 {
"sv_SE", 0x41d,
"Swedish (sv)", 0x1d,
"LANG_SWEDISH",
"Sweden (SE);", 0x1,
476 {
"sv_SE", 0x41d,
"Swedish (sv)", 0x1d,
"LANG_SWEDISH",
"Sweden (SE);", 0x1,
477 "SUBLANG_SWEDISH_SWEDEN" },
478 {
"syr_SY", 0x45a,
"Syriac (syr)", 0x5a,
"LANG_SYRIAC",
"Syria (SY)", 0x1,
"SUBLANG_SYRIAC" },
479 {
"tg_TJ", 0x428,
"Tajik (tg)", 0x28,
"LANG_TAJIK",
"Tajikistan, Cyrillic (TJ)", 0x1,
480 "SUBLANG_TAJIK_TAJIKISTAN" },
481 {
"tzm_DZ", 0x85f,
"Tamazight (tzm)", 0x5f,
"LANG_TAMAZIGHT",
"Algeria, Latin (DZ)", 0x2,
482 "SUBLANG_TAMAZIGHT_ALGERIA_LATIN" },
483 {
"ta_IN", 0x449,
"Tamil (ta)", 0x49,
"LANG_TAMIL",
"India (IN)", 0x1,
"SUBLANG_TAMIL_INDIA" },
484 {
"ta_LK", 0x849,
"Tamil (ta)", 0x49,
"LANG_TAMIL",
"Sri Lanka (LK)", 0x2,
485 "SUBLANG_TAMIL_SRI_LANKA" },
486 {
"tt_RU", 0x444,
"Tatar (tt)", 0x44,
"LANG_TATAR",
"Russia (RU)", 0x1,
487 "SUBLANG_TATAR_RUSSIA" },
488 {
"te_IN", 0x44a,
"Telugu (te)", 0x4a,
"LANG_TELUGU",
"India (IN)", 0x1,
489 "SUBLANG_TELUGU_INDIA" },
490 {
"th_TH", 0x41e,
"Thai (th)", 0x1e,
"LANG_THAI",
"Thailand (TH)", 0x1,
491 "SUBLANG_THAI_THAILAND" },
492 {
"bo_CN", 0x451,
"Tibetan (bo)", 0x51,
"LANG_TIBETAN",
"PRC (CN)", 0x1,
493 "SUBLANG_TIBETAN_PRC" },
494 {
"ti_ER", 0x873,
"Tigrinya (ti)", 0x73,
"LANG_TIGRINYA",
"Eritrea (ER)", 0x2,
495 "SUBLANG_TIGRINYA_ERITREA" },
496 {
"ti_ET", 0x473,
"Tigrinya (ti)", 0x73,
"LANG_TIGRIGNA",
"Ethiopia (ET)", 0x1,
497 "SUBLANG_TIGRINYA_ETHIOPIA" },
498 {
"tr_TR", 0x41f,
"Turkish (tr)", 0x1f,
"LANG_TURKISH",
"Turkey (TR)", 0x1,
499 "SUBLANG_TURKISH_TURKEY" },
500 {
"tk_KM", 0x442,
"Turkmen (tk)", 0x42,
"LANG_TURKMEN",
"Turkmenistan (TM)", 0x1,
501 "SUBLANG_TURKMEN_TURKMENISTAN" },
502 {
"uk_UA", 0x422,
"Ukrainian (uk)", 0x22,
"LANG_UKRAINIAN",
"Ukraine (UA)", 0x1,
503 "SUBLANG_UKRAINIAN_UKRAINE" },
504 {
"hsb_DE", 0x42e,
"Upper Sorbian (hsb)", 0x2e,
"LANG_UPPER_SORBIAN",
"Germany (DE)", 0x1,
505 "SUBLANG_UPPER_SORBIAN_GERMANY" },
506 {
"ur", 0x820,
"Urdu (ur)", 0x20,
"LANG_URDU",
"(reserved)", 0x2,
"SUBLANG_URDU_INDIA" },
507 {
"ur_PK", 0x420,
"Urdu (ur)", 0x20,
"LANG_URDU",
"Pakistan (PK)", 0x1,
508 "SUBLANG_URDU_PAKISTAN" },
509 {
"ug_CN", 0x480,
"Uyghur (ug)", 0x80,
"LANG_UIGHUR",
"PRC (CN)", 0x1,
"SUBLANG_UIGHUR_PRC" },
510 {
"uz_UZ", 0x843,
"Uzbek (uz)", 0x43,
"LANG_UZBEK",
"Uzbekistan, Cyrillic (UZ)", 0x2,
511 "SUBLANG_UZBEK_CYRILLIC" },
512 {
"uz_UZ", 0x443,
"Uzbek (uz)", 0x43,
"LANG_UZBEK",
"Uzbekistan, Latin (UZ)", 0x1,
513 "SUBLANG_UZBEK_LATIN" },
514 {
"ca", 0x803,
"Valencian (ca)", 0x3,
"LANG_VALENCIAN",
"Valencia (ES-Valencia)", 0x2,
515 "SUBLANG_VALENCIAN_VALENCIA" },
516 {
"vi_VN", 0x42a,
"Vietnamese (vi)", 0x2a,
"LANG_VIETNAMESE",
"Vietnam (VN)", 0x1,
517 "SUBLANG_VIETNAMESE_VIETNAM" },
518 {
"cy_GB", 0x452,
"Welsh (cy)", 0x52,
"LANG_WELSH",
"United Kingdom (GB)", 0x1,
519 "SUBLANG_WELSH_UNITED_KINGDOM" },
520 {
"wo_SN", 0x488,
"Wolof (wo)", 0x88,
"LANG_WOLOF",
"Senegal (SN)", 0x1,
521 "SUBLANG_WOLOF_SENEGAL" },
522 {
"ii_CN", 0x478,
"Yi (ii)", 0x78,
"LANG_YI",
"PRC (CN)", 0x1,
"SUBLANG_YI_PRC" },
523 {
"yu_NG", 0x46a,
"Yoruba (yo)", 0x6a,
"LANG_YORUBA",
"Nigeria (NG)", 0x1,
524 "SUBLANG_YORUBA_NIGERIA" },
530 WINPR_ASSERT(layouts);
531 WINPR_ASSERT(pcount);
545 #if !defined(WITH_KEYBOARD_LAYOUT_FROM_FILE)
552 { 0x0000041c,
"Albanian" },
553 { 0x00000401,
"Arabic (101)" },
554 { 0x00010401,
"Arabic (102)" },
555 { 0x00020401,
"Arabic (102) AZERTY" },
556 { 0x0000042b,
"Armenian Eastern" },
557 { 0x0002042b,
"Armenian Phonetic" },
558 { 0x0003042b,
"Armenian Typewriter" },
559 { 0x0001042b,
"Armenian Western" },
560 { 0x0000044d,
"Assamese - Inscript" },
561 { 0x0001042c,
"Azerbaijani (Standard)" },
562 { 0x0000082c,
"Azerbaijani Cyrillic" },
563 { 0x0000042c,
"Azerbaijani Latin" },
564 { 0x0000046d,
"Bashkir" },
565 { 0x00000423,
"Belarusian" },
566 { 0x0001080c,
"Belgian (Comma)" },
567 { 0x00000813,
"Belgian (Period)" },
568 { 0x0000080c,
"Belgian French" },
569 { 0x00000445,
"Bangla (Bangladesh)" },
570 { 0x00020445,
"Bangla (India)" },
571 { 0x00010445,
"Bangla (India - Legacy)" },
572 { 0x0000201a,
"Bosnian (Cyrillic)" },
573 { 0x000b0c00,
"Buginese" },
574 { 0x00030402,
"Bulgarian" },
575 { 0x00010402,
"Bulgarian (Latin)" },
576 { 0x00020402,
"Bulgarian (phonetic layout)" },
577 { 0x00040402,
"Bulgarian (phonetic traditional)" },
578 { 0x00000402,
"Bulgarian (Typewriter)" },
579 { 0x00001009,
"Canadian French" },
580 { 0x00000c0c,
"Canadian French (Legacy)" },
581 { 0x00011009,
"Canadian Multilingual Standard" },
582 { 0x0000085f,
"Central Atlas Tamazight" },
583 { 0x00000429,
"Central Kurdish" },
584 { 0x0000045c,
"Cherokee Nation" },
585 { 0x0001045c,
"Cherokee Nation Phonetic" },
586 { 0x00000804,
"Chinese (Simplified) - US Keyboard" },
587 { 0x00000404,
"Chinese (Traditional) - US Keyboard" },
588 { 0x00000c04,
"Chinese (Traditional, Hong Kong S.A.R.)" },
589 { 0x00001404,
"Chinese (Traditional Macao S.A.R.) US Keyboard" },
590 { 0x00001004,
"Chinese (Simplified, Singapore) - US keyboard" },
591 { 0x0000041a,
"Croatian" },
592 { 0x00000405,
"Czech" },
593 { 0x00010405,
"Czech (QWERTY)" },
594 { 0x00020405,
"Czech Programmers" },
595 { 0x00000406,
"Danish" },
596 { 0x00000439,
"Devanagari-INSCRIPT" },
597 { 0x00000465,
"Divehi Phonetic" },
598 { 0x00010465,
"Divehi Typewriter" },
599 { 0x00000413,
"Dutch" },
600 { 0x00000c51,
"Dzongkha" },
601 { 0x00000425,
"Estonian" },
602 { 0x00000438,
"Faeroese" },
603 { 0x0000040b,
"Finnish" },
604 { 0x0001083b,
"Finnish with Sami" },
605 { 0x0000040c,
"French" },
606 { 0x00120c00,
"Futhark" },
607 { 0x00000437,
"Georgian" },
608 { 0x00020437,
"Georgian (Ergonomic)" },
609 { 0x00010437,
"Georgian (QWERTY)" },
610 { 0x00030437,
"Georgian Ministry of Education and Science Schools" },
611 { 0x00040437,
"Georgian (Old Alphabets)" },
612 { 0x00000407,
"German" },
613 { 0x00010407,
"German (IBM)" },
614 { 0x000c0c00,
"Gothic" },
615 { 0x00000408,
"Greek" },
616 { 0x00010408,
"Greek (220)" },
617 { 0x00030408,
"Greek (220) Latin" },
618 { 0x00020408,
"Greek (319)" },
619 { 0x00040408,
"Greek (319) Latin" },
620 { 0x00050408,
"Greek Latin" },
621 { 0x00060408,
"Greek Polytonic" },
622 { 0x0000046f,
"Greenlandic" },
623 { 0x00000474,
"Guarani" },
624 { 0x00000447,
"Gujarati" },
625 { 0x00000468,
"Hausa" },
626 { 0x0000040d,
"Hebrew" },
627 { 0x00010439,
"Hindi Traditional" },
628 { 0x0000040e,
"Hungarian" },
629 { 0x0001040e,
"Hungarian 101-key" },
630 { 0x0000040f,
"Icelandic" },
631 { 0x00000470,
"Igbo" },
632 { 0x00004009,
"India" },
633 { 0x0000085d,
"Inuktitut - Latin" },
634 { 0x0001045d,
"Inuktitut - Naqittaut" },
635 { 0x00001809,
"Irish" },
636 { 0x00000410,
"Italian" },
637 { 0x00010410,
"Italian (142)" },
638 { 0x00000411,
"Japanese" },
639 { 0x00110c00,
"Javanese" },
640 { 0x0000044b,
"Kannada" },
641 { 0x0000043f,
"Kazakh" },
642 { 0x00000453,
"Khmer" },
643 { 0x00010453,
"Khmer (NIDA)" },
644 { 0x00000412,
"Korean" },
645 { 0x00000440,
"Kyrgyz Cyrillic" },
646 { 0x00000454,
"Lao" },
647 { 0x0000080a,
"Latin American" },
648 { 0x00020426,
"Latvian (Standard)" },
649 { 0x00010426,
"Latvian (Legacy)" },
650 { 0x00070c00,
"Lisu (Basic)" },
651 { 0x00080c00,
"Lisu (Standard)" },
652 { 0x00010427,
"Lithuanian" },
653 { 0x00000427,
"Lithuanian IBM" },
654 { 0x00020427,
"Lithuanian Standard" },
655 { 0x0000046e,
"Luxembourgish" },
656 { 0x0000042f,
"Macedonia (FYROM)" },
657 { 0x0001042f,
"Macedonia (FYROM) - Standard" },
658 { 0x0000044c,
"Malayalam" },
659 { 0x0000043a,
"Maltese 47-Key" },
660 { 0x0001043a,
"Maltese 48-key" },
661 { 0x00000481,
"Maori" },
662 { 0x0000044e,
"Marathi" },
663 { 0x00000850,
"Mongolian (Mongolian Script - Legacy)" },
664 { 0x00020850,
"Mongolian (Mongolian Script - Standard)" },
665 { 0x00000450,
"Mongolian Cyrillic" },
666 { 0x00010c00,
"Myanmar" },
667 { 0x00090c00,
"N'ko" },
668 { 0x00000461,
"Nepali" },
669 { 0x00020c00,
"New Tai Lue" },
670 { 0x00000414,
"Norwegian" },
671 { 0x0000043b,
"Norwegian with Sami" },
672 { 0x00000448,
"Odia" },
673 { 0x000d0c00,
"Ol Chiki" },
674 { 0x000f0c00,
"Old Italic" },
675 { 0x000e0c00,
"Osmanya" },
676 { 0x00000463,
"Pashto (Afghanistan)" },
677 { 0x00000429,
"Persian" },
678 { 0x00050429,
"Persian (Standard)" },
679 { 0x000a0c00,
"Phags-pa" },
680 { 0x00010415,
"Polish (214)" },
681 { 0x00000415,
"Polish (Programmers)" },
682 { 0x00000816,
"Portuguese" },
683 { 0x00000416,
"Portuguese (Brazilian ABNT)" },
684 { 0x00010416,
"Portuguese (Brazilian ABNT2)" },
685 { 0x00000446,
"Punjabi" },
686 { 0x00000418,
"Romanian (Legacy)" },
687 { 0x00020418,
"Romanian (Programmers)" },
688 { 0x00010418,
"Romanian (Standard)" },
689 { 0x00000419,
"Russian" },
690 { 0x00020419,
"Russian - Mnemonic" },
691 { 0x00010419,
"Russian (Typewriter)" },
692 { 0x00000485,
"Sakha" },
693 { 0x0002083b,
"Sami Extended Finland-Sweden" },
694 { 0x0001043b,
"Sami Extended Norway" },
695 { 0x00011809,
"Scottish Gaelic" },
696 { 0x00000c1a,
"Serbian (Cyrillic)" },
697 { 0x0000081a,
"Serbian (Latin)" },
698 { 0x0000046c,
"Sesotho sa Leboa" },
699 { 0x00000432,
"Setswana" },
700 { 0x0000045b,
"Sinhala" },
701 { 0x0001045b,
"Sinhala - wij 9" },
702 { 0x0000041b,
"Slovak" },
703 { 0x0001041b,
"Slovak (QWERTY)" },
704 { 0x00000424,
"Slovenian" },
705 { 0x00100c00,
"Sora" },
706 { 0x0001042e,
"Sorbian Extended" },
707 { 0x0002042e,
"Sorbian Standard" },
708 { 0x0000042e,
"Sorbian Standard (Legacy)" },
709 { 0x0000040a,
"Spanish" },
710 { 0x0001040a,
"Spanish Variation" },
711 { 0x0000041d,
"Swedish" },
712 { 0x0000083b,
"Swedish with Sami" },
713 { 0x0000100c,
"Swiss French" },
714 { 0x00000807,
"Swiss German" },
715 { 0x0000045a,
"Syriac" },
716 { 0x0001045a,
"Syriac Phonetic" },
717 { 0x00030c00,
"Tai Le" },
718 { 0x00000428,
"Tajik" },
719 { 0x00000449,
"Tamil" },
720 { 0x00010444,
"Tatar" },
721 { 0x00000444,
"Tatar (Legacy)" },
722 { 0x0000044a,
"Telugu" },
723 { 0x0000041e,
"Thai Kedmanee" },
724 { 0x0002041e,
"Thai Kedmanee (non-ShiftLock)" },
725 { 0x0001041e,
"Thai Pattachote" },
726 { 0x0003041e,
"Thai Pattachote (non-ShiftLock)" },
727 { 0x00010451,
"Tibetan (PRC - Standard)" },
728 { 0x00000451,
"Tibetan (PRC - Legacy)" },
729 { 0x00050c00,
"Tifinagh (Basic)" },
730 { 0x00060c00,
"Tifinagh (Full)" },
731 { 0x0001041f,
"Turkish F" },
732 { 0x0000041f,
"Turkish Q" },
733 { 0x00000442,
"Turkmen" },
734 { 0x00010408,
"Uyghur" },
735 { 0x00000480,
"Uyghur (Legacy)" },
736 { 0x00000422,
"Ukrainian" },
737 { 0x00020422,
"Ukrainian (Enhanced)" },
738 { 0x00000809,
"United Kingdom" },
739 { 0x00000452,
"United Kingdom Extended" },
740 { 0x00010409,
"United States - Dvorak" },
741 { 0x00020409,
"United States - International" },
742 { 0x00030409,
"United States-Dvorak for left hand" },
743 { 0x00040409,
"United States-Dvorak for right hand" },
744 { 0x00000409,
"United States - English" },
745 { 0x00000420,
"Urdu" },
746 { 0x00010480,
"Uyghur" },
747 { 0x00000843,
"Uzbek Cyrillic" },
748 { 0x0000042a,
"Vietnamese" },
749 { 0x00000488,
"Wolof" },
750 { 0x00000485,
"Yakut" },
751 { 0x0000046a,
"Yoruba" },
753 static const size_t sRDP_KEYBOARD_LAYOUT_TABLE_len = ARRAYSIZE(sRDP_KEYBOARD_LAYOUT_TABLE);
755 static const RDP_KEYBOARD_LAYOUT_VARIANT sRDP_KEYBOARD_LAYOUT_VARIANT_TABLE[] = {
756 { KBD_ARABIC_102, 0x0028,
"Arabic (102)" },
757 { KBD_BULGARIAN_LATIN, 0x0004,
"Bulgarian (Latin)" },
758 { KBD_CZECH_QWERTY, 0x0005,
"Czech (QWERTY)" },
759 { KBD_GERMAN_IBM, 0x0012,
"German (IBM)" },
760 { KBD_GREEK_220, 0x0016,
"Greek (220)" },
761 { KBD_UNITED_STATES_DVORAK, 0x0002,
"United States-Dvorak" },
762 { KBD_SPANISH_VARIATION, 0x0086,
"Spanish Variation" },
763 { KBD_HUNGARIAN_101_KEY, 0x0006,
"Hungarian 101-key" },
764 { KBD_ITALIAN_142, 0x0003,
"Italian (142)" },
765 { KBD_POLISH_214, 0x0007,
"Polish (214)" },
766 { KBD_PORTUGUESE_BRAZILIAN_ABNT2, 0x001D,
"Portuguese (Brazilian ABNT2)" },
767 { KBD_RUSSIAN_TYPEWRITER, 0x0008,
"Russian (Typewriter)" },
768 { KBD_SLOVAK_QWERTY, 0x0013,
"Slovak (QWERTY)" },
769 { KBD_THAI_PATTACHOTE, 0x0021,
"Thai Pattachote" },
770 { KBD_TURKISH_F, 0x0014,
"Turkish F" },
771 { KBD_LATVIAN_QWERTY, 0x0015,
"Latvian (QWERTY)" },
772 { KBD_LITHUANIAN, 0x0027,
"Lithuanian" },
773 { KBD_ARMENIAN_WESTERN, 0x0025,
"Armenian Western" },
774 { KBD_HINDI_TRADITIONAL, 0x000C,
"Hindi Traditional" },
775 { KBD_MALTESE_48_KEY, 0x002B,
"Maltese 48-key" },
776 { KBD_SAMI_EXTENDED_NORWAY, 0x002C,
"Sami Extended Norway" },
777 { KBD_BENGALI_INSCRIPT, 0x002A,
"Bengali (Inscript)" },
778 { KBD_SYRIAC_PHONETIC, 0x000E,
"Syriac Phonetic" },
779 { KBD_DIVEHI_TYPEWRITER, 0x000D,
"Divehi Typewriter" },
780 { KBD_BELGIAN_COMMA, 0x001E,
"Belgian (Comma)" },
781 { KBD_FINNISH_WITH_SAMI, 0x002D,
"Finnish with Sami" },
782 { KBD_CANADIAN_MULTILINGUAL_STANDARD, 0x0020,
"Canadian Multilingual Standard" },
783 { KBD_GAELIC, 0x0026,
"Gaelic" },
784 { KBD_ARABIC_102_AZERTY, 0x0029,
"Arabic (102) AZERTY" },
785 { KBD_CZECH_PROGRAMMERS, 0x000A,
"Czech Programmers" },
786 { KBD_GREEK_319, 0x0018,
"Greek (319)" },
787 { KBD_UNITED_STATES_INTERNATIONAL, 0x0001,
"United States-International" },
788 { KBD_THAI_KEDMANEE_NON_SHIFTLOCK, 0x0022,
"Thai Kedmanee (non-ShiftLock)" },
789 { KBD_SAMI_EXTENDED_FINLAND_SWEDEN, 0x002E,
"Sami Extended Finland-Sweden" },
790 { KBD_GREEK_220_LATIN, 0x0017,
"Greek (220) Latin" },
791 { KBD_UNITED_STATES_DVORAK_FOR_LEFT_HAND, 0x001A,
"United States-Dvorak for left hand" },
792 { KBD_THAI_PATTACHOTE_NON_SHIFTLOCK, 0x0023,
"Thai Pattachote (non-ShiftLock)" },
793 { KBD_GREEK_319_LATIN, 0x0011,
"Greek (319) Latin" },
794 { KBD_UNITED_STATES_DVORAK_FOR_RIGHT_HAND, 0x001B,
"United States-Dvorak for right hand" },
795 { KBD_UNITED_STATES_DVORAK_PROGRAMMER, 0x001C,
"United States-Programmer Dvorak" },
796 { KBD_GREEK_LATIN, 0x0019,
"Greek Latin" },
797 { KBD_US_ENGLISH_TABLE_FOR_IBM_ARABIC_238_L, 0x000B,
"US English Table for IBM Arabic 238_L" },
798 { KBD_GREEK_POLYTONIC, 0x001F,
"Greek Polytonic" },
799 { KBD_FRENCH_BEPO, 0x00C0,
"French Bépo" },
800 { KBD_GERMAN_NEO, 0x00C0,
"German Neo" }
802 static const size_t sRDP_KEYBOARD_LAYOUT_VARIANT_TABLE_len =
803 ARRAYSIZE(sRDP_KEYBOARD_LAYOUT_VARIANT_TABLE);
809 static const RDP_KEYBOARD_IME sRDP_KEYBOARD_IME_TABLE[] = {
810 { KBD_CHINESE_TRADITIONAL_PHONETIC,
"phon.ime",
"Chinese (Traditional) - Phonetic" },
811 { KBD_JAPANESE_INPUT_SYSTEM_MS_IME2002,
"imjp81.ime",
"Japanese Input System (MS-IME2002)" },
812 { KBD_KOREAN_INPUT_SYSTEM_IME_2000,
"imekr61.ime",
"Korean Input System (IME 2000)" },
813 { KBD_CHINESE_SIMPLIFIED_QUANPIN,
"winpy.ime",
"Chinese (Simplified) - QuanPin" },
814 { KBD_CHINESE_TRADITIONAL_CHANGJIE,
"chajei.ime",
"Chinese (Traditional) - ChangJie" },
815 { KBD_CHINESE_SIMPLIFIED_SHUANGPIN,
"winsp.ime",
"Chinese (Simplified) - ShuangPin" },
816 { KBD_CHINESE_TRADITIONAL_QUICK,
"quick.ime",
"Chinese (Traditional) - Quick" },
817 { KBD_CHINESE_SIMPLIFIED_ZHENGMA,
"winzm.ime",
"Chinese (Simplified) - ZhengMa" },
818 { KBD_CHINESE_TRADITIONAL_BIG5_CODE,
"winime.ime",
"Chinese (Traditional) - Big5 Code" },
819 { KBD_CHINESE_TRADITIONAL_ARRAY,
"winar30.ime",
"Chinese (Traditional) - Array" },
820 { KBD_CHINESE_SIMPLIFIED_NEIMA,
"wingb.ime",
"Chinese (Simplified) - NeiMa" },
821 { KBD_CHINESE_TRADITIONAL_DAYI,
"dayi.ime",
"Chinese (Traditional) - DaYi" },
822 { KBD_CHINESE_TRADITIONAL_UNICODE,
"unicdime.ime",
"Chinese (Traditional) - Unicode" },
823 { KBD_CHINESE_TRADITIONAL_NEW_PHONETIC,
"TINTLGNT.IME",
824 "Chinese (Traditional) - New Phonetic" },
825 { KBD_CHINESE_TRADITIONAL_NEW_CHANGJIE,
"CINTLGNT.IME",
826 "Chinese (Traditional) - New ChangJie" },
827 { KBD_CHINESE_TRADITIONAL_MICROSOFT_PINYIN_IME_3,
"pintlgnt.ime",
828 "Chinese (Traditional) - Microsoft Pinyin IME 3.0" },
829 { KBD_CHINESE_TRADITIONAL_ALPHANUMERIC,
"romanime.ime",
"Chinese (Traditional) - Alphanumeric" }
831 static const size_t sRDP_KEYBOARD_IME_TABLE_len = ARRAYSIZE(sRDP_KEYBOARD_IME_TABLE);
833 #if defined(DUMP_LAYOUTS_TO_JSON)
834 static BOOL append_layout(WINPR_JSON* json)
840 for (
size_t x = 0; x < sRDP_KEYBOARD_LAYOUT_TABLE_len; x++)
856 static BOOL append_variant(WINPR_JSON* json)
862 for (
size_t x = 0; x < sRDP_KEYBOARD_LAYOUT_VARIANT_TABLE_len; x++)
864 const RDP_KEYBOARD_LAYOUT_VARIANT*
const ime = &sRDP_KEYBOARD_LAYOUT_VARIANT_TABLE[x];
879 static BOOL append_ime(WINPR_JSON* json)
885 for (
size_t x = 0; x < sRDP_KEYBOARD_IME_TABLE_len; x++)
887 const RDP_KEYBOARD_IME*
const ime = &sRDP_KEYBOARD_IME_TABLE[x];
903 static BOOL load_layout_file(
void)
905 #if defined(DUMP_LAYOUTS_TO_JSON)
912 if (!append_layout(json))
914 if (!append_variant(json))
916 if (!append_ime(json))
923 FILE* fp = winpr_fopen(
"/tmp/kbd.json",
"w");
926 (void)fprintf(fp,
"%s", str);
937 static size_t sRDP_KEYBOARD_LAYOUT_TABLE_len = 0;
939 static RDP_KEYBOARD_LAYOUT_VARIANT* sRDP_KEYBOARD_LAYOUT_VARIANT_TABLE = NULL;
940 static size_t sRDP_KEYBOARD_LAYOUT_VARIANT_TABLE_len = 0;
942 static RDP_KEYBOARD_IME* sRDP_KEYBOARD_IME_TABLE = NULL;
943 static size_t sRDP_KEYBOARD_IME_TABLE_len = 0;
955 static void clear_keyboard_variant(RDP_KEYBOARD_LAYOUT_VARIANT* layout)
961 const RDP_KEYBOARD_LAYOUT_VARIANT empty = { 0 };
965 static void clear_keyboard_ime(RDP_KEYBOARD_IME* layout)
972 const RDP_KEYBOARD_IME empty = { 0 };
976 static void clear_layout_tables(
void)
978 for (
size_t x = 0; x < sRDP_KEYBOARD_LAYOUT_TABLE_len; x++)
981 clear_keyboard_layout(layout);
984 free(sRDP_KEYBOARD_LAYOUT_TABLE);
985 sRDP_KEYBOARD_LAYOUT_TABLE = NULL;
986 sRDP_KEYBOARD_LAYOUT_TABLE_len = 0;
988 for (
size_t x = 0; x < sRDP_KEYBOARD_LAYOUT_VARIANT_TABLE_len; x++)
990 RDP_KEYBOARD_LAYOUT_VARIANT* variant = &sRDP_KEYBOARD_LAYOUT_VARIANT_TABLE[x];
991 clear_keyboard_variant(variant);
993 free(sRDP_KEYBOARD_LAYOUT_VARIANT_TABLE);
994 sRDP_KEYBOARD_LAYOUT_VARIANT_TABLE = NULL;
995 sRDP_KEYBOARD_LAYOUT_VARIANT_TABLE_len = 0;
997 for (
size_t x = 0; x < sRDP_KEYBOARD_IME_TABLE_len; x++)
999 RDP_KEYBOARD_IME* ime = &sRDP_KEYBOARD_IME_TABLE[x];
1000 clear_keyboard_ime(ime);
1002 free(sRDP_KEYBOARD_IME_TABLE);
1003 sRDP_KEYBOARD_IME_TABLE = NULL;
1004 sRDP_KEYBOARD_IME_TABLE_len = 0;
1007 static WINPR_JSON* load_layouts_from_file(
const char* filename)
1011 WINPR_JSON* json = NULL;
1012 FILE* fp = winpr_fopen(filename,
"r");
1015 WLog_WARN(TAG,
"resource file '%s' does not exist or is not readable", filename);
1019 if (_fseeki64(fp, 0, SEEK_END) < 0)
1021 WLog_WARN(TAG,
"resource file '%s' seek failed", filename);
1024 jstrlen = _ftelli64(fp);
1027 WLog_WARN(TAG,
"resource file '%s' invalid length %" PRId64, filename, jstrlen);
1030 if (_fseeki64(fp, 0, SEEK_SET) < 0)
1032 WLog_WARN(TAG,
"resource file '%s' seek failed", filename);
1036 jstr = calloc(jstrlen + 1,
sizeof(
char));
1039 WLog_WARN(TAG,
"resource file '%s' failed to allocate buffer of size %" PRId64, filename,
1044 if (fread(jstr, jstrlen,
sizeof(
char), fp) != 1)
1046 WLog_WARN(TAG,
"resource file '%s' failed to read buffer of size %" PRId64, filename,
1053 WLog_WARN(TAG,
"resource file '%s' is not a valid JSON file", filename);
1060 static char* get_object_str(WINPR_JSON* json,
size_t pos,
const char* name)
1065 WLog_WARN(TAG,
"Invalid JSON entry at entry %" PRIuz
", missing an Object named '%s'", pos,
1074 "Invalid JSON entry at entry %" PRIuz
", Object named '%s': Not of type string",
1082 WLog_WARN(TAG,
"Invalid JSON entry at entry %" PRIuz
", Object named '%s': NULL string",
1087 return _strdup(str);
1090 static UINT32 get_object_integer(WINPR_JSON* json,
size_t pos,
const char* name)
1095 WLog_WARN(TAG,
"Invalid JSON entry at entry %" PRIuz
", missing an Object named '%s'", pos,
1104 "Invalid JSON entry at entry %" PRIuz
", Object named '%s': Not of type string",
1112 static BOOL parse_json_layout_entry(WINPR_JSON* json,
size_t pos,
RDP_KEYBOARD_LAYOUT* entry)
1114 WINPR_ASSERT(entry);
1117 WLog_WARN(TAG,
"Invalid JSON entry at entry %" PRIuz
", expected an array", pos);
1121 entry->code = get_object_integer(json, pos,
"code");
1122 entry->name = get_object_str(json, pos,
"name");
1125 clear_keyboard_layout(entry);
1131 static BOOL parse_layout_entries(WINPR_JSON* json,
const char* filename)
1135 WLog_WARN(TAG,
"Invalid top level JSON type in file %s, expected an array", filename);
1140 sRDP_KEYBOARD_LAYOUT_TABLE =
1142 if (!sRDP_KEYBOARD_LAYOUT_TABLE)
1144 clear_layout_tables();
1148 for (
size_t x = 0; x < sRDP_KEYBOARD_LAYOUT_TABLE_len; x++)
1152 if (!obj || !parse_json_layout_entry(obj, x, &sRDP_KEYBOARD_LAYOUT_TABLE[x]))
1154 clear_layout_tables();
1162 static BOOL parse_json_variant_entry(WINPR_JSON* json,
size_t pos,
1163 RDP_KEYBOARD_LAYOUT_VARIANT* entry)
1165 WINPR_ASSERT(entry);
1168 WLog_WARN(TAG,
"Invalid JSON entry at entry %" PRIuz
", expected an array", pos);
1172 entry->code = get_object_integer(json, pos,
"code");
1173 entry->id = get_object_integer(json, pos,
"id");
1174 entry->name = get_object_str(json, pos,
"name");
1177 clear_keyboard_variant(entry);
1183 static BOOL parse_variant_entries(WINPR_JSON* json,
const char* filename)
1187 WLog_WARN(TAG,
"Invalid top level JSON type in file %s, expected an array", filename);
1190 WINPR_ASSERT(!sRDP_KEYBOARD_LAYOUT_VARIANT_TABLE);
1191 WINPR_ASSERT(sRDP_KEYBOARD_LAYOUT_VARIANT_TABLE_len == 0);
1194 sRDP_KEYBOARD_LAYOUT_VARIANT_TABLE = calloc(count,
sizeof(RDP_KEYBOARD_LAYOUT_VARIANT));
1195 if (!sRDP_KEYBOARD_LAYOUT_VARIANT_TABLE)
1197 sRDP_KEYBOARD_LAYOUT_VARIANT_TABLE_len = count;
1199 for (
size_t x = 0; x < count; x++)
1202 if (!parse_json_variant_entry(entry, x, &sRDP_KEYBOARD_LAYOUT_VARIANT_TABLE[x]))
1208 static BOOL parse_json_ime_entry(WINPR_JSON* json,
size_t pos, RDP_KEYBOARD_IME* entry)
1210 WINPR_ASSERT(entry);
1213 WLog_WARN(TAG,
"Invalid JSON entry at entry %" PRIuz
", expected an array", pos);
1217 entry->code = get_object_integer(json, pos,
"code");
1218 entry->file = get_object_str(json, pos,
"file");
1219 entry->name = get_object_str(json, pos,
"name");
1220 if (!entry->file || !entry->name)
1222 clear_keyboard_ime(entry);
1228 static BOOL parse_ime_entries(WINPR_JSON* json,
const char* filename)
1232 WLog_WARN(TAG,
"Invalid top level JSON type in file %s, expected an array", filename);
1235 WINPR_ASSERT(!sRDP_KEYBOARD_IME_TABLE);
1236 WINPR_ASSERT(sRDP_KEYBOARD_IME_TABLE_len == 0);
1239 sRDP_KEYBOARD_IME_TABLE = calloc(count,
sizeof(RDP_KEYBOARD_IME));
1240 if (!sRDP_KEYBOARD_IME_TABLE)
1242 sRDP_KEYBOARD_IME_TABLE_len = count;
1244 for (
size_t x = 0; x < count; x++)
1247 if (!parse_json_ime_entry(entry, x, &sRDP_KEYBOARD_IME_TABLE[x]))
1253 static BOOL CALLBACK load_layouts(
PINIT_ONCE once, PVOID param, PVOID* context)
1256 WINPR_UNUSED(param);
1257 WINPR_UNUSED(context);
1259 WINPR_JSON* json = NULL;
1260 char* filename = GetCombinedPath(FREERDP_RESOURCE_ROOT,
"KeyboardLayoutMap.json");
1263 WLog_WARN(TAG,
"Could not create WinPR timezone resource filename");
1267 json = load_layouts_from_file(filename);
1273 WLog_WARN(TAG,
"Invalid top level JSON type in file %s, expected an array", filename);
1277 clear_layout_tables();
1280 if (!parse_layout_entries(obj, filename))
1285 if (!parse_variant_entries(obj, filename))
1290 if (!parse_ime_entries(obj, filename))
1297 (void)atexit(clear_layout_tables);
1301 static BOOL load_layout_file(
void)
1303 static INIT_ONCE once = INIT_ONCE_STATIC_INIT;
1304 InitOnceExecuteOnce(&once, load_layouts, NULL, NULL);
1310 static UINT32 rdp_keyboard_layout_by_name(
const char* name)
1315 for (
size_t i = 0; i < sRDP_KEYBOARD_LAYOUT_TABLE_len; i++)
1318 if (strcmp(layout->name, name) == 0)
1319 return layout->code;
1324 static UINT32 rdp_keyboard_variant_by_name(
const char* name)
1329 for (
size_t i = 0; i < sRDP_KEYBOARD_LAYOUT_VARIANT_TABLE_len; i++)
1331 const RDP_KEYBOARD_LAYOUT_VARIANT*
const variant = &sRDP_KEYBOARD_LAYOUT_VARIANT_TABLE[i];
1332 if (strcmp(variant->name, name) == 0)
1333 return variant->code;
1338 static UINT32 rdp_keyboard_ime_by_name(
const char* name)
1343 for (
size_t i = 0; i < sRDP_KEYBOARD_IME_TABLE_len; i++)
1345 const RDP_KEYBOARD_IME*
const ime = &sRDP_KEYBOARD_IME_TABLE[i];
1346 if (strcmp(ime->name, name) == 0)
1352 static const char* rdp_keyboard_layout_by_id(UINT32
id)
1356 for (
size_t i = 0; i < sRDP_KEYBOARD_LAYOUT_TABLE_len; i++)
1359 if (layout->code ==
id)
1360 return layout->name;
1366 static const char* rdp_keyboard_variant_by_id(UINT32
id)
1370 for (
size_t i = 0; i < sRDP_KEYBOARD_LAYOUT_VARIANT_TABLE_len; i++)
1372 const RDP_KEYBOARD_LAYOUT_VARIANT*
const variant = &sRDP_KEYBOARD_LAYOUT_VARIANT_TABLE[i];
1373 if (variant->code ==
id)
1374 return variant->name;
1379 static const char* rdp_keyboard_ime_by_id(UINT32
id)
1383 for (
size_t i = 0; i < sRDP_KEYBOARD_IME_TABLE_len; i++)
1385 const RDP_KEYBOARD_IME*
const ime = &sRDP_KEYBOARD_IME_TABLE[i];
1386 if (ime->code ==
id)
1392 static BOOL rdp_keyboard_layout_clone_append(
RDP_KEYBOARD_LAYOUT** layouts,
size_t* pcount)
1394 WINPR_ASSERT(layouts);
1395 WINPR_ASSERT(pcount);
1399 const size_t length = sRDP_KEYBOARD_LAYOUT_TABLE_len;
1400 const size_t offset = *pcount;
1401 if (!reallocate_keyboard_layouts(layouts, pcount, length))
1404 for (
size_t i = 0; i < length; i++)
1408 layout->code = ime->code;
1410 layout->name = _strdup(ime->name);
1418 static BOOL rdp_keyboard_variant_clone_append(
RDP_KEYBOARD_LAYOUT** layouts,
size_t* pcount)
1420 WINPR_ASSERT(layouts);
1421 WINPR_ASSERT(pcount);
1425 const size_t length = sRDP_KEYBOARD_LAYOUT_VARIANT_TABLE_len;
1426 const size_t offset = *pcount;
1427 if (!reallocate_keyboard_layouts(layouts, pcount, length))
1430 for (
size_t i = 0; i < length; i++)
1432 const RDP_KEYBOARD_LAYOUT_VARIANT*
const ime = &sRDP_KEYBOARD_LAYOUT_VARIANT_TABLE[i];
1434 layout->code = ime->code;
1436 layout->name = _strdup(ime->name);
1446 WINPR_ASSERT(layouts);
1447 WINPR_ASSERT(pcount);
1451 const size_t length = sRDP_KEYBOARD_IME_TABLE_len;
1452 const size_t offset = *pcount;
1453 if (!reallocate_keyboard_layouts(layouts, pcount, length))
1456 for (
size_t i = 0; i < length; i++)
1458 const RDP_KEYBOARD_IME*
const ime = &sRDP_KEYBOARD_IME_TABLE[i];
1460 layout->code = ime->code;
1462 layout->name = _strdup(ime->name);
1475 for (
size_t x = 0; x < count; x++)
1478 free(current->name);
1494 WINPR_ASSERT(count);
1497 if ((types & RDP_KEYBOARD_LAYOUT_TYPE_STANDARD) != 0)
1499 if (!rdp_keyboard_layout_clone_append(&layouts, &num))
1503 if ((types & RDP_KEYBOARD_LAYOUT_TYPE_VARIANT) != 0)
1505 if (!rdp_keyboard_variant_clone_append(&layouts, &num))
1509 if ((types & RDP_KEYBOARD_LAYOUT_TYPE_IME) != 0)
1511 if (!rdp_keyboard_ime_clone_append(&layouts, &num))
1518 freerdp_keyboard_layouts_free(layouts, num);
1522 const char* freerdp_keyboard_get_layout_name_from_id(DWORD keyboardLayoutID)
1524 const char* name = rdp_keyboard_layout_by_id(keyboardLayoutID);
1528 name = rdp_keyboard_variant_by_id(keyboardLayoutID);
1532 name = rdp_keyboard_ime_by_id(keyboardLayoutID);
1539 DWORD freerdp_keyboard_get_layout_id_from_name(
const char* name)
1541 DWORD rc = rdp_keyboard_layout_by_name(name);
1545 rc = rdp_keyboard_variant_by_name(name);
1549 return rdp_keyboard_ime_by_name(name);
1552 static void copy(
const struct LanguageIdentifier*
id,
RDP_CODEPAGE* cp)
1554 cp->id =
id->LanguageIdentifier;
1555 cp->subId =
id->SublangaugeIdentifier;
1556 cp->primaryId =
id->PrimaryLanguageIdentifier;
1558 strncpy(cp->locale, id->locale, ARRAYSIZE(cp->locale) - 1);
1559 if (id->PrimaryLanguage)
1560 strncpy(cp->primaryLanguage, id->PrimaryLanguage, ARRAYSIZE(cp->primaryLanguage) - 1);
1561 if (id->PrimaryLanguageSymbol)
1562 strncpy(cp->primaryLanguageSymbol, id->PrimaryLanguageSymbol,
1563 ARRAYSIZE(cp->primaryLanguageSymbol) - 1);
1564 if (id->Sublanguage)
1565 strncpy(cp->subLanguage, id->Sublanguage, ARRAYSIZE(cp->subLanguage) - 1);
1566 if (id->SublanguageSymbol)
1567 strncpy(cp->subLanguageSymbol, id->SublanguageSymbol, ARRAYSIZE(cp->subLanguageSymbol) - 1);
1570 static BOOL copyOnMatch(DWORD column,
const char* filter,
const struct LanguageIdentifier* cur,
1573 const char* what = NULL;
1580 what = cur->PrimaryLanguage;
1583 what = cur->PrimaryLanguageSymbol;
1586 what = cur->Sublanguage;
1589 what = cur->SublanguageSymbol;
1597 if (!strstr(what, filter))
1604 RDP_CODEPAGE* freerdp_keyboard_get_matching_codepages(DWORD column,
const char* filter,
1608 const size_t c = ARRAYSIZE(language_identifiers);
1620 for (
size_t x = 0; x < c; x++)
1622 const struct LanguageIdentifier* cur = &language_identifiers[x];
1623 if (copyOnMatch(column, filter, cur, &pages[cnt]))
1635 freerdp_codepages_free(pages);
WINPR_API BOOL WINPR_JSON_HasObjectItem(const WINPR_JSON *object, const char *string)
Check if JSON has an object matching the name.
WINPR_API WINPR_JSON * WINPR_JSON_ParseWithLength(const char *value, size_t buffer_length)
Parse a JSON string.
WINPR_API WINPR_JSON * WINPR_JSON_AddStringToObject(WINPR_JSON *object, const char *name, const char *string)
WINPR_JSON_AddStringToObject.
WINPR_API BOOL WINPR_JSON_IsString(const WINPR_JSON *item)
Check if JSON item is of type String.
WINPR_API BOOL WINPR_JSON_AddItemToArray(WINPR_JSON *array, WINPR_JSON *item)
Add an item to an existing array.
WINPR_API WINPR_JSON * WINPR_JSON_AddNumberToObject(WINPR_JSON *object, const char *name, double number)
WINPR_JSON_AddNumberToObject.
WINPR_API double WINPR_JSON_GetNumberValue(const WINPR_JSON *item)
Return the Number value of a JSON item.
WINPR_API char * WINPR_JSON_Print(WINPR_JSON *item)
Serialize a JSON instance to string for minimal size without formatting see WINPR_JSON_PrintUnformatt...
WINPR_API BOOL WINPR_JSON_IsNumber(const WINPR_JSON *item)
Check if JSON item is of type Number.
WINPR_API WINPR_JSON * WINPR_JSON_AddArrayToObject(WINPR_JSON *object, const char *name)
WINPR_JSON_AddArrayToObject.
WINPR_API BOOL WINPR_JSON_IsObject(const WINPR_JSON *item)
Check if JSON item is of type Object.
WINPR_API WINPR_JSON * WINPR_JSON_GetArrayItem(const WINPR_JSON *array, size_t index)
Return a pointer to an item in the array.
WINPR_API void WINPR_JSON_Delete(WINPR_JSON *item)
Delete a WinPR JSON wrapper object.
WINPR_API WINPR_JSON * WINPR_JSON_GetObjectItem(const WINPR_JSON *object, const char *string)
Return a pointer to an JSON object item.
WINPR_API WINPR_JSON * WINPR_JSON_CreateObject(void)
WINPR_JSON_CreateObject.
WINPR_API size_t WINPR_JSON_GetArraySize(const WINPR_JSON *array)
Get the number of arrayitems from an array.
WINPR_API BOOL WINPR_JSON_IsArray(const WINPR_JSON *item)
Check if JSON item is of type Array.
WINPR_API const char * WINPR_JSON_GetStringValue(WINPR_JSON *item)
Return the String value of a JSON item.