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