2 findlocale-0.46.tar.gz from http://icculus.org/~aspirin/findlocale/
4 Copyright (C) 2004 Adam D. Moss (the "Author"). All Rights Reserved.
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is fur-
11 nished to do so, subject to the following conditions:
13 The above copyright notice and this permission notice shall be included in
14 all copies or substantial portions of the Software.
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT-
18 NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CON-
21 NECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 Except as contained in this notice, the name of the Author of the
24 Software shall not be used in advertising or otherwise to promote the sale,
25 use or other dealings in this Software without prior written authorization
41 #include <Carbon/Carbon.h>
44 #include "findlocale.hpp"
47 is_lcchar(const int c) {
52 lang_country_variant_from_envstring(const char *str,
59 /* get lang, if any */
61 while (is_lcchar(str[end])) {
66 int len = end - start;
67 char *s = (char*) malloc(len + 1);
68 for (i=0; i<len; ++i) {
69 s[i] = tolower(str[start + i]);
77 if (str[end] && str[end]!=':') { /* not at end of str */
81 /* get country, if any */
83 while (is_lcchar(str[end])) {
88 int len = end - start;
89 char *s = (char*) malloc(len + 1);
90 for (i=0; i<len; ++i) {
91 s[i] = toupper(str[start + i]);
99 if (str[end] && str[end]!=':') { /* not at end of str */
103 /* get variant, if any */
105 while (str[end] && str[end]!=':') {
110 int len = end - start;
111 char *s = (char*) malloc(len + 1);
112 for (i=0; i<len; ++i) {
113 s[i] = str[start + i];
124 accumulate_locstring(const char *str, FL_Locale *l) {
126 char *country = NULL;
127 char *variant = NULL;
129 lang_country_variant_from_envstring(str, &lang, &country, &variant);
132 l->country = country;
133 l->variant = variant;
137 free(lang); free(country); free(variant);
144 accumulate_env(const char *name, FL_Locale *l) {
147 char *country = NULL;
148 char *variant = NULL;
151 return accumulate_locstring(env, l);
153 free(lang); free(country); free(variant);
159 canonise_fl(FL_Locale *l) {
160 /* this function fixes some common locale-specifying mistakes */
162 if (l->lang && 0 == strcmp(l->lang, "en")) {
163 if (l->country && 0 == strcmp(l->country, "UK")) {
164 free((void*)l->country);
165 l->country = strdup("GB");
169 if (l->lang && 0 == strcmp(l->lang, "ja")) {
170 if (l->country && 0 == strcmp(l->country, "JA")) {
171 free((void*)l->country);
172 l->country = strdup("JP");
180 #define ML(pn,sn) MAKELANGID(LANG_##pn, SUBLANG_##pn##_##sn)
181 #define MLN(pn) MAKELANGID(LANG_##pn, SUBLANG_DEFAULT)
182 #define RML(pn,sn) MAKELANGID(LANG_##pn, SUBLANG_##sn)
187 static const IDToCode both_to_code[] = {
188 {ML(ENGLISH,US), "en_US.ISO_8859-1"},
189 {ML(ENGLISH,CAN), "en_CA"}, /* english / canadian */
190 {ML(ENGLISH,UK), "en_GB"},
191 {ML(ENGLISH,EIRE), "en_IE"},
192 {ML(ENGLISH,AUS), "en_AU"},
193 {MLN(GERMAN), "de_DE"},
194 {MLN(SPANISH), "es_ES"},
195 {ML(SPANISH,MEXICAN), "es_MX"},
196 {MLN(FRENCH), "fr_FR"},
197 {ML(FRENCH,CANADIAN), "fr_CA"},
198 {ML(FRENCH,BELGIAN), "fr_BE"}, /* ? */
199 {ML(DUTCH,BELGIAN), "nl_BE"}, /* ? */
200 {ML(PORTUGUESE,BRAZILIAN), "pt_BR"},
201 {MLN(PORTUGUESE), "pt_PT"},
202 {MLN(SWEDISH), "sv_SE"},
203 {ML(CHINESE,HONGKONG), "zh_HK"},
204 /* these are machine-generated and not yet verified */
205 {RML(AFRIKAANS,DEFAULT), "af_ZA"},
206 {RML(ALBANIAN,DEFAULT), "sq_AL"},
207 {RML(ARABIC,ARABIC_ALGERIA), "ar_DZ"},
208 {RML(ARABIC,ARABIC_BAHRAIN), "ar_BH"},
209 {RML(ARABIC,ARABIC_EGYPT), "ar_EG"},
210 {RML(ARABIC,ARABIC_IRAQ), "ar_IQ"},
211 {RML(ARABIC,ARABIC_JORDAN), "ar_JO"},
212 {RML(ARABIC,ARABIC_KUWAIT), "ar_KW"},
213 {RML(ARABIC,ARABIC_LEBANON), "ar_LB"},
214 {RML(ARABIC,ARABIC_LIBYA), "ar_LY"},
215 {RML(ARABIC,ARABIC_MOROCCO), "ar_MA"},
216 {RML(ARABIC,ARABIC_OMAN), "ar_OM"},
217 {RML(ARABIC,ARABIC_QATAR), "ar_QA"},
218 {RML(ARABIC,ARABIC_SAUDI_ARABIA), "ar_SA"},
219 {RML(ARABIC,ARABIC_SYRIA), "ar_SY"},
220 {RML(ARABIC,ARABIC_TUNISIA), "ar_TN"},
221 {RML(ARABIC,ARABIC_UAE), "ar_AE"},
222 {RML(ARABIC,ARABIC_YEMEN), "ar_YE"},
223 {RML(ARMENIAN,DEFAULT), "hy_AM"},
224 {RML(AZERI,AZERI_CYRILLIC), "az_AZ"},
225 {RML(AZERI,AZERI_LATIN), "az_AZ"},
226 {RML(BASQUE,DEFAULT), "eu_ES"},
227 {RML(BELARUSIAN,DEFAULT), "be_BY"},
228 /*{RML(BRETON,DEFAULT), "br_FR"},*/
229 {RML(BULGARIAN,DEFAULT), "bg_BG"},
230 {RML(CATALAN,DEFAULT), "ca_ES"},
231 {RML(CHINESE,CHINESE_HONGKONG), "zh_HK"},
232 {RML(CHINESE,CHINESE_MACAU), "zh_MO"},
233 {RML(CHINESE,CHINESE_SIMPLIFIED), "zh_CN"},
234 {RML(CHINESE,CHINESE_SINGAPORE), "zh_SG"},
235 {RML(CHINESE,CHINESE_TRADITIONAL), "zh_TW"},
236 /*{RML(CORNISH,DEFAULT), "kw_GB"},*/
237 {RML(CZECH,DEFAULT), "cs_CZ"},
238 {RML(DANISH,DEFAULT), "da_DK"},
239 {RML(DUTCH,DUTCH), "nl_NL"},
240 {RML(DUTCH,DUTCH_BELGIAN), "nl_BE"},
241 /*{RML(DUTCH,DUTCH_SURINAM), "nl_SR"},*/
242 {RML(ENGLISH,ENGLISH_AUS), "en_AU"},
243 {RML(ENGLISH,ENGLISH_BELIZE), "en_BZ"},
244 {RML(ENGLISH,ENGLISH_CAN), "en_CA"},
245 {RML(ENGLISH,ENGLISH_CARIBBEAN), "en_CB"},
246 {RML(ENGLISH,ENGLISH_EIRE), "en_IE"},
247 {RML(ENGLISH,ENGLISH_JAMAICA), "en_JM"},
248 {RML(ENGLISH,ENGLISH_NZ), "en_NZ"},
249 {RML(ENGLISH,ENGLISH_PHILIPPINES), "en_PH"},
250 {RML(ENGLISH,ENGLISH_SOUTH_AFRICA), "en_ZA"},
251 {RML(ENGLISH,ENGLISH_TRINIDAD), "en_TT"},
252 {RML(ENGLISH,ENGLISH_UK), "en_GB"},
253 {RML(ENGLISH,ENGLISH_US), "en_US"},
254 {RML(ENGLISH,ENGLISH_ZIMBABWE), "en_ZW"},
255 /*{RML(ESPERANTO,DEFAULT), "eo_"},*/
256 {RML(ESTONIAN,DEFAULT), "et_EE"},
257 {RML(FAEROESE,DEFAULT), "fo_FO"},
258 {RML(FARSI,DEFAULT), "fa_IR"},
259 {RML(FINNISH,DEFAULT), "fi_FI"},
260 {RML(FRENCH,FRENCH), "fr_FR"},
261 {RML(FRENCH,FRENCH_BELGIAN), "fr_BE"},
262 {RML(FRENCH,FRENCH_CANADIAN), "fr_CA"},
263 {RML(FRENCH,FRENCH_LUXEMBOURG), "fr_LU"},
264 {RML(FRENCH,FRENCH_MONACO), "fr_MC"},
265 {RML(FRENCH,FRENCH_SWISS), "fr_CH"},
266 /*{RML(GAELIC,GAELIC), "ga_IE"},*/
267 /*{RML(GAELIC,GAELIC_MANX), "gv_GB"},*/
268 /*{RML(GAELIC,GAELIC_SCOTTISH), "gd_GB"},*/
269 /*{RML(GALICIAN,DEFAULT), "gl_ES"},*/
270 {RML(GEORGIAN,DEFAULT), "ka_GE"},
271 {RML(GERMAN,GERMAN), "de_DE"},
272 {RML(GERMAN,GERMAN_AUSTRIAN), "de_AT"},
273 {RML(GERMAN,GERMAN_LIECHTENSTEIN), "de_LI"},
274 {RML(GERMAN,GERMAN_LUXEMBOURG), "de_LU"},
275 {RML(GERMAN,GERMAN_SWISS), "de_CH"},
276 {RML(GREEK,DEFAULT), "el_GR"},
277 {RML(GUJARATI,DEFAULT), "gu_IN"},
278 {RML(HEBREW,DEFAULT), "he_IL"},
279 {RML(HINDI,DEFAULT), "hi_IN"},
280 {RML(HUNGARIAN,DEFAULT), "hu_HU"},
281 {RML(ICELANDIC,DEFAULT), "is_IS"},
282 {RML(INDONESIAN,DEFAULT), "id_ID"},
283 {RML(ITALIAN,ITALIAN), "it_IT"},
284 {RML(ITALIAN,ITALIAN_SWISS), "it_CH"},
285 {RML(JAPANESE,DEFAULT), "ja_JP"},
286 {RML(KANNADA,DEFAULT), "kn_IN"},
287 {RML(KAZAK,DEFAULT), "kk_KZ"},
288 {RML(KONKANI,DEFAULT), "kok_IN"},
289 {RML(KOREAN,KOREAN), "ko_KR"},
290 /*{RML(KYRGYZ,DEFAULT), "ky_KG"},*/
291 {RML(LATVIAN,DEFAULT), "lv_LV"},
292 {RML(LITHUANIAN,LITHUANIAN), "lt_LT"},
293 {RML(MACEDONIAN,DEFAULT), "mk_MK"},
294 {RML(MALAY,MALAY_BRUNEI_DARUSSALAM), "ms_BN"},
295 {RML(MALAY,MALAY_MALAYSIA), "ms_MY"},
296 {RML(MARATHI,DEFAULT), "mr_IN"},
297 /*{RML(MONGOLIAN,DEFAULT), "mn_MN"},*/
298 {RML(NORWEGIAN,NORWEGIAN_BOKMAL), "nb_NO"},
299 {RML(NORWEGIAN,NORWEGIAN_NYNORSK), "nn_NO"},
300 {RML(POLISH,DEFAULT), "pl_PL"},
301 {RML(PORTUGUESE,PORTUGUESE), "pt_PT"},
302 {RML(PORTUGUESE,PORTUGUESE_BRAZILIAN), "pt_BR"},
303 {RML(PUNJABI,DEFAULT), "pa_IN"},
304 {RML(ROMANIAN,DEFAULT), "ro_RO"},
305 {RML(RUSSIAN,DEFAULT), "ru_RU"},
306 {RML(SANSKRIT,DEFAULT), "sa_IN"},
307 {RML(SERBIAN,DEFAULT), "hr_HR"},
308 {RML(SERBIAN,SERBIAN_CYRILLIC), "sr_SP"},
309 {RML(SERBIAN,SERBIAN_LATIN), "sr_SP"},
310 {RML(SLOVAK,DEFAULT), "sk_SK"},
311 {RML(SLOVENIAN,DEFAULT), "sl_SI"},
312 {RML(SPANISH,SPANISH), "es_ES"},
313 {RML(SPANISH,SPANISH_ARGENTINA), "es_AR"},
314 {RML(SPANISH,SPANISH_BOLIVIA), "es_BO"},
315 {RML(SPANISH,SPANISH_CHILE), "es_CL"},
316 {RML(SPANISH,SPANISH_COLOMBIA), "es_CO"},
317 {RML(SPANISH,SPANISH_COSTA_RICA), "es_CR"},
318 {RML(SPANISH,SPANISH_DOMINICAN_REPUBLIC), "es_DO"},
319 {RML(SPANISH,SPANISH_ECUADOR), "es_EC"},
320 {RML(SPANISH,SPANISH_EL_SALVADOR), "es_SV"},
321 {RML(SPANISH,SPANISH_GUATEMALA), "es_GT"},
322 {RML(SPANISH,SPANISH_HONDURAS), "es_HN"},
323 {RML(SPANISH,SPANISH_MEXICAN), "es_MX"},
324 {RML(SPANISH,SPANISH_MODERN), "es_ES"},
325 {RML(SPANISH,SPANISH_NICARAGUA), "es_NI"},
326 {RML(SPANISH,SPANISH_PANAMA), "es_PA"},
327 {RML(SPANISH,SPANISH_PARAGUAY), "es_PY"},
328 {RML(SPANISH,SPANISH_PERU), "es_PE"},
329 {RML(SPANISH,SPANISH_PUERTO_RICO), "es_PR"},
330 {RML(SPANISH,SPANISH_URUGUAY), "es_UY"},
331 {RML(SPANISH,SPANISH_VENEZUELA), "es_VE"},
332 {RML(SWAHILI,DEFAULT), "sw_KE"},
333 {RML(SWEDISH,SWEDISH), "sv_SE"},
334 {RML(SWEDISH,SWEDISH_FINLAND), "sv_FI"},
335 /*{RML(SYRIAC,DEFAULT), "syr_SY"},*/
336 {RML(TAMIL,DEFAULT), "ta_IN"},
337 {RML(TATAR,DEFAULT), "tt_TA"},
338 {RML(TELUGU,DEFAULT), "te_IN"},
339 {RML(THAI,DEFAULT), "th_TH"},
340 {RML(TURKISH,DEFAULT), "tr_TR"},
341 {RML(UKRAINIAN,DEFAULT), "uk_UA"},
342 {RML(URDU,URDU_PAKISTAN), "ur_PK"},
343 {RML(UZBEK,UZBEK_CYRILLIC), "uz_UZ"},
344 {RML(UZBEK,UZBEK_LATIN), "uz_UZ"},
345 {RML(VIETNAMESE,DEFAULT), "vi_VN"},
346 /*{RML(WALON,DEFAULT), "wa_BE"},*/
347 /*{RML(WELSH,DEFAULT), "cy_GB"},*/
349 static const IDToCode primary_to_code[] = {
350 {LANG_AFRIKAANS, "af"},
353 {LANG_BULGARIAN, "bg"},
354 /*{LANG_BRETON, "br"},*/
355 {LANG_BELARUSIAN, "by"},
356 {LANG_CATALAN, "ca"},
358 /*{LANG_WELSH, "cy"},*/
362 {LANG_ENGLISH, "en"},
363 /*{LANG_ESPERANTO, "eo"},*/
364 {LANG_SPANISH, "es"},
365 {LANG_ESTONIAN, "et"},
368 {LANG_FINNISH, "fi"},
369 {LANG_FAEROESE, "fo"},
371 /*{LANG_GAELIC, "ga"},*/
372 /*{LANG_GALICIAN, "gl"},*/
373 {LANG_GUJARATI, "gu"},
376 {LANG_SERBIAN, "hr"},
377 {LANG_HUNGARIAN, "hu"},
378 {LANG_ARMENIAN, "hy"},
379 {LANG_INDONESIAN, "id"},
380 {LANG_ITALIAN, "it"},
381 {LANG_JAPANESE, "ja"},
382 {LANG_GEORGIAN, "ka"},
384 {LANG_KANNADA, "kn"},
386 /*{LANG_KYRGYZ, "ky"},*/
387 {LANG_LITHUANIAN, "lt"},
388 {LANG_LATVIAN, "lv"},
389 {LANG_MACEDONIAN, "mk"},
390 /*{LANG_MONGOLIAN, "mn"},*/
391 {LANG_MARATHI, "mr"},
393 {LANG_NORWEGIAN, "nb"},
395 {LANG_NORWEGIAN, "nn"},
396 {LANG_NORWEGIAN, "no"},/* unofficial? */
397 {LANG_PUNJABI, "pa"},
399 {LANG_PORTUGUESE, "pt"},
400 {LANG_ROMANIAN, "ro"},
401 {LANG_RUSSIAN, "ru"},
403 {LANG_SLOVENIAN, "sl"},
404 {LANG_ALBANIAN, "sq"},
405 {LANG_SERBIAN, "sr"},
406 {LANG_SWEDISH, "sv"},
407 {LANG_SWAHILI, "sw"},
410 {LANG_TURKISH, "tr"},
412 {LANG_UKRAINIAN, "uk"},
415 {LANG_VIETNAMESE, "vi"},
416 /*{LANG_WALON, "wa"},*/
417 {LANG_CHINESE, "zh"},
419 static int num_primary_to_code =
420 sizeof(primary_to_code) / sizeof(*primary_to_code);
421 static int num_both_to_code =
422 sizeof(both_to_code) / sizeof(*both_to_code);
425 lcid_to_fl(LCID lcid,
427 LANGID langid = LANGIDFROMLCID(lcid);
428 LANGID primary_lang = PRIMARYLANGID(langid);
430 LANGID sub_lang = SUBLANGID(langid);
433 /* try to find an exact primary/sublanguage combo that we know about */
434 for (i=0; i<num_both_to_code; ++i) {
435 if (both_to_code[i].id == langid) {
436 accumulate_locstring(both_to_code[i].code, rtn);
440 /* fallback to just checking the primary language id */
441 for (i=0; i<num_primary_to_code; ++i) {
442 if (primary_to_code[i].id == primary_lang) {
443 accumulate_locstring(primary_to_code[i].code, rtn);
453 FL_FindLocale(FL_Locale **locale, FL_Domain /*domain*/) {
454 FL_Success success = FL_FAILED;
455 FL_Locale *rtn = (FL_Locale*) malloc(sizeof(FL_Locale));
461 /* win32 >= mswindows95 */
463 LCID lcid = GetThreadLocale();
464 if (lcid_to_fl(lcid, rtn)) {
465 success = FL_CONFIDENT;
467 if (success == FL_FAILED) {
468 /* assume US English on mswindows systems unless we know otherwise */
469 if (accumulate_locstring("en_US.ISO_8859-1", rtn)) {
470 success = FL_DEFAULT_GUESS;
479 CFArrayRef languages;
480 CFStringRef uxstylelangs;
483 /* get the languages from the user's presets */
484 languages = (CFArrayRef)CFPreferencesCopyValue(CFSTR("AppleLanguages"),
485 kCFPreferencesAnyApplication, kCFPreferencesCurrentUser,
486 kCFPreferencesAnyHost);
488 /* join the returned string array into a string separated by colons */
489 uxstylelangs = CFStringCreateByCombiningStrings(kCFAllocatorDefault,
490 languages, CFSTR(":"));
492 /* convert this string into a C string */
493 sz = CFStringGetLength(uxstylelangs) + 1;
494 uxsl = (char*)malloc(sz);
495 CFStringGetCString(uxstylelangs, uxsl, sz, kCFStringEncodingISOLatin1);
497 /* add it to the list */
498 if (accumulate_locstring(uxsl, rtn)) {
499 success = FL_CONFIDENT;
501 /* continue the UNIX method */
504 /* sv_SE.ISO_8859-1 */
505 /* fr_FR.ISO8859-1 */
510 /* try the various vars in decreasing order of authority */
511 if (accumulate_env("LC_ALL", rtn) ||
512 accumulate_env("LC_MESSAGES", rtn) ||
513 accumulate_env("LANG", rtn) ||
514 accumulate_env("LANGUAGE", rtn)) {
515 success = FL_CONFIDENT;
517 if (success == FL_FAILED) {
518 /* assume US English on unixoid systems unless we know otherwise */
519 if (accumulate_locstring("en_US.ISO_8859-1", rtn)) {
520 success = FL_DEFAULT_GUESS;
526 if (success != FL_FAILED) {
536 FL_FreeLocale(FL_Locale **locale) {
538 FL_Locale *l = *locale;
541 free((void*)l->lang);
544 free((void*)l->country);
547 free((void*)l->variant);