PageRenderTime 57ms CodeModel.GetById 2ms app.highlight 49ms RepoModel.GetById 2ms app.codeStats 0ms

/indra/llcommon/llfindlocale.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 518 lines | 421 code | 28 blank | 69 comment | 56 complexity | 1d1c0174b27169988cfb0a685c1fb1c0 MD5 | raw file
  1/** 
  2 * @file llfindlocale.cpp
  3 * @brief Detect system language setting
  4 *
  5 * $LicenseInfo:firstyear=2008&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26
 27/* Yes, this was originally C code. */
 28
 29#include "linden_common.h"
 30
 31#include <stdlib.h>
 32#include <string.h>
 33#include <ctype.h>
 34
 35#ifdef WIN32
 36#include <windows.h>
 37#include <winnt.h>
 38#endif
 39
 40#include "llfindlocale.h"
 41
 42static int
 43is_lcchar(const int c) {
 44  return isalnum(c);
 45}
 46
 47static void
 48lang_country_variant_from_envstring(const char *str,
 49                                    char **lang,
 50                                    char **country,
 51                                    char **variant) {
 52  int end = 0;
 53  int start;
 54
 55  /* get lang, if any */
 56  start = end;
 57  while (is_lcchar(str[end])) {
 58    ++end;
 59  }
 60  if (start != end) {
 61    int i;
 62    int len = end - start;
 63    char *s = (char*)malloc(len + 1);
 64    for (i=0; i<len; ++i) {
 65      s[i] = tolower(str[start + i]);
 66    }
 67    s[i] = '\0';
 68    *lang = s;
 69  } else {
 70    *lang = NULL;
 71  }
 72
 73  if (str[end] && str[end]!=':') { /* not at end of str */
 74    ++end;
 75  }
 76
 77  /* get country, if any */
 78  start = end;
 79  while (is_lcchar(str[end])) {
 80    ++end;
 81  }
 82  if (start != end) {
 83    int i;
 84    int len = end - start;
 85    char *s = (char*)malloc(len + 1);
 86    for (i=0; i<len; ++i) {
 87      s[i] = toupper(str[start + i]);
 88    }
 89    s[i] = '\0';
 90    *country = s;
 91  } else {
 92    *country = NULL;
 93  }
 94
 95  if (str[end] && str[end]!=':') { /* not at end of str */
 96    ++end;
 97  }
 98
 99  /* get variant, if any */
100  start = end;
101  while (str[end] && str[end]!=':') {
102    ++end;
103  }
104  if (start != end) {
105    int i;
106    int len = end - start;
107    char *s = (char*)malloc(len + 1);
108    for (i=0; i<len; ++i) {
109      s[i] = str[start + i];
110    }
111    s[i] = '\0';
112    *variant = s;
113  } else {
114    *variant = NULL;
115  }
116}
117
118
119static int
120accumulate_locstring(const char *str, FL_Locale *l) {
121  char *lang = NULL;
122  char *country = NULL;
123  char *variant = NULL;
124  if (str) {
125    lang_country_variant_from_envstring(str, &lang, &country, &variant);
126    if (lang) {
127      l->lang = lang;
128      l->country = country;
129      l->variant = variant;
130      return 1;
131    }
132  }
133  free(lang); free(country); free(variant);
134  return 0;
135}
136
137
138static int
139accumulate_env(const char *name, FL_Locale *l) {
140  char *env;
141  char *lang = NULL;
142  char *country = NULL;
143  char *variant = NULL;
144  env = getenv(name);
145  if (env) {
146    return accumulate_locstring(env, l);
147  }
148  free(lang); free(country); free(variant);
149  return 0;
150}
151
152
153static void
154canonise_fl(FL_Locale *l) {
155  /* this function fixes some common locale-specifying mistakes */
156  /* en_UK -> en_GB */
157  if (l->lang && 0 == strcmp(l->lang, "en")) {
158    if (l->country && 0 == strcmp(l->country, "UK")) {
159      free((void*)l->country);
160      l->country = strdup("GB");
161    }
162  }
163  /* ja_JA -> ja_JP */
164  if (l->lang && 0 == strcmp(l->lang, "ja")) {
165    if (l->country && 0 == strcmp(l->country, "JA")) {
166      free((void*)l->country);
167      l->country = strdup("JP");
168    }
169  }
170}
171
172
173#ifdef WIN32
174#include <stdio.h>
175#define ML(pn,sn) MAKELANGID(LANG_##pn, SUBLANG_##pn##_##sn)
176#define MLN(pn) MAKELANGID(LANG_##pn, SUBLANG_DEFAULT)
177#define RML(pn,sn) MAKELANGID(LANG_##pn, SUBLANG_##sn)
178struct IDToCode {
179  LANGID id;
180  char*  code;
181};
182static const IDToCode both_to_code[] = {
183  {ML(ENGLISH,US),           "en_US.ISO_8859-1"},
184  {ML(ENGLISH,CAN),          "en_CA"}, /* english / canadian */
185  {ML(ENGLISH,UK),           "en_GB"},
186  {ML(ENGLISH,EIRE),         "en_IE"},
187  {ML(ENGLISH,AUS),          "en_AU"},
188  {MLN(GERMAN),              "de_DE"},
189  {MLN(SPANISH),             "es_ES"},
190  {ML(SPANISH,MEXICAN),      "es_MX"},
191  {MLN(FRENCH),              "fr_FR"},
192  {ML(FRENCH,CANADIAN),      "fr_CA"},
193  {ML(FRENCH,BELGIAN),       "fr_BE"}, /* ? */
194  {ML(DUTCH,BELGIAN),        "nl_BE"}, /* ? */
195  {ML(PORTUGUESE,BRAZILIAN), "pt_BR"},
196  {MLN(PORTUGUESE),          "pt_PT"},
197  {MLN(SWEDISH),             "sv_SE"},
198  {ML(CHINESE,HONGKONG),     "zh_HK"},
199  /* these are machine-generated and not yet verified */
200  {RML(AFRIKAANS,DEFAULT), "af_ZA"},
201  {RML(ALBANIAN,DEFAULT), "sq_AL"},
202  {RML(ARABIC,ARABIC_ALGERIA), "ar_DZ"},
203  {RML(ARABIC,ARABIC_BAHRAIN), "ar_BH"},
204  {RML(ARABIC,ARABIC_EGYPT), "ar_EG"},
205  {RML(ARABIC,ARABIC_IRAQ), "ar_IQ"},
206  {RML(ARABIC,ARABIC_JORDAN), "ar_JO"},
207  {RML(ARABIC,ARABIC_KUWAIT), "ar_KW"},
208  {RML(ARABIC,ARABIC_LEBANON), "ar_LB"},
209  {RML(ARABIC,ARABIC_LIBYA), "ar_LY"},
210  {RML(ARABIC,ARABIC_MOROCCO), "ar_MA"},
211  {RML(ARABIC,ARABIC_OMAN), "ar_OM"},
212  {RML(ARABIC,ARABIC_QATAR), "ar_QA"},
213  {RML(ARABIC,ARABIC_SAUDI_ARABIA), "ar_SA"},
214  {RML(ARABIC,ARABIC_SYRIA), "ar_SY"},
215  {RML(ARABIC,ARABIC_TUNISIA), "ar_TN"},
216  {RML(ARABIC,ARABIC_UAE), "ar_AE"},
217  {RML(ARABIC,ARABIC_YEMEN), "ar_YE"},
218  {RML(ARMENIAN,DEFAULT), "hy_AM"},
219  {RML(AZERI,AZERI_CYRILLIC), "az_AZ"},
220  {RML(AZERI,AZERI_LATIN), "az_AZ"},
221  {RML(BASQUE,DEFAULT), "eu_ES"},
222  {RML(BELARUSIAN,DEFAULT), "be_BY"},
223/*{RML(BRETON,DEFAULT), "br_FR"},*/
224  {RML(BULGARIAN,DEFAULT), "bg_BG"},
225  {RML(CATALAN,DEFAULT), "ca_ES"},
226  {RML(CHINESE,CHINESE_HONGKONG), "zh_HK"},
227  {RML(CHINESE,CHINESE_MACAU), "zh_MO"},
228  {RML(CHINESE,CHINESE_SIMPLIFIED), "zh_CN"},
229  {RML(CHINESE,CHINESE_SINGAPORE), "zh_SG"},
230  {RML(CHINESE,CHINESE_TRADITIONAL), "zh_TW"},
231/*{RML(CORNISH,DEFAULT), "kw_GB"},*/
232  {RML(CZECH,DEFAULT), "cs_CZ"},
233  {RML(DANISH,DEFAULT), "da_DK"},
234  {RML(DUTCH,DUTCH), "nl_NL"},
235  {RML(DUTCH,DUTCH_BELGIAN), "nl_BE"},
236/*{RML(DUTCH,DUTCH_SURINAM), "nl_SR"},*/
237  {RML(ENGLISH,ENGLISH_AUS), "en_AU"},
238  {RML(ENGLISH,ENGLISH_BELIZE), "en_BZ"},
239  {RML(ENGLISH,ENGLISH_CAN), "en_CA"},
240  {RML(ENGLISH,ENGLISH_CARIBBEAN), "en_CB"},
241  {RML(ENGLISH,ENGLISH_EIRE), "en_IE"},
242  {RML(ENGLISH,ENGLISH_JAMAICA), "en_JM"},
243  {RML(ENGLISH,ENGLISH_NZ), "en_NZ"},
244  {RML(ENGLISH,ENGLISH_PHILIPPINES), "en_PH"},
245  {RML(ENGLISH,ENGLISH_SOUTH_AFRICA), "en_ZA"},
246  {RML(ENGLISH,ENGLISH_TRINIDAD), "en_TT"},
247  {RML(ENGLISH,ENGLISH_UK), "en_GB"},
248  {RML(ENGLISH,ENGLISH_US), "en_US"},
249  {RML(ENGLISH,ENGLISH_ZIMBABWE), "en_ZW"},
250/*{RML(ESPERANTO,DEFAULT), "eo_"},*/
251  {RML(ESTONIAN,DEFAULT), "et_EE"},
252  {RML(FAEROESE,DEFAULT), "fo_FO"},
253  {RML(FARSI,DEFAULT), "fa_IR"},
254  {RML(FINNISH,DEFAULT), "fi_FI"},
255  {RML(FRENCH,FRENCH), "fr_FR"},
256  {RML(FRENCH,FRENCH_BELGIAN), "fr_BE"},
257  {RML(FRENCH,FRENCH_CANADIAN), "fr_CA"},
258  {RML(FRENCH,FRENCH_LUXEMBOURG), "fr_LU"},
259  {RML(FRENCH,FRENCH_MONACO), "fr_MC"},
260  {RML(FRENCH,FRENCH_SWISS), "fr_CH"},
261/*{RML(GAELIC,GAELIC), "ga_IE"},*/
262/*{RML(GAELIC,GAELIC_MANX), "gv_GB"},*/
263/*{RML(GAELIC,GAELIC_SCOTTISH), "gd_GB"},*/
264/*{RML(GALICIAN,DEFAULT), "gl_ES"},*/
265  {RML(GEORGIAN,DEFAULT), "ka_GE"},
266  {RML(GERMAN,GERMAN), "de_DE"},
267  {RML(GERMAN,GERMAN_AUSTRIAN), "de_AT"},
268  {RML(GERMAN,GERMAN_LIECHTENSTEIN), "de_LI"},
269  {RML(GERMAN,GERMAN_LUXEMBOURG), "de_LU"},
270  {RML(GERMAN,GERMAN_SWISS), "de_CH"},
271  {RML(GREEK,DEFAULT), "el_GR"},
272  {RML(GUJARATI,DEFAULT), "gu_IN"},
273  {RML(HEBREW,DEFAULT), "he_IL"},
274  {RML(HINDI,DEFAULT), "hi_IN"},
275  {RML(HUNGARIAN,DEFAULT), "hu_HU"},
276  {RML(ICELANDIC,DEFAULT), "is_IS"},
277  {RML(INDONESIAN,DEFAULT), "id_ID"},
278  {RML(ITALIAN,ITALIAN), "it_IT"},
279  {RML(ITALIAN,ITALIAN_SWISS), "it_CH"},
280  {RML(JAPANESE,DEFAULT), "ja_JP"},
281  {RML(KANNADA,DEFAULT), "kn_IN"},
282  {RML(KAZAK,DEFAULT), "kk_KZ"},
283  {RML(KONKANI,DEFAULT), "kok_IN"},
284  {RML(KOREAN,KOREAN), "ko_KR"},
285/*{RML(KYRGYZ,DEFAULT), "ky_KG"},*/
286  {RML(LATVIAN,DEFAULT), "lv_LV"},
287  {RML(LITHUANIAN,LITHUANIAN), "lt_LT"},
288  {RML(MACEDONIAN,DEFAULT), "mk_MK"},
289  {RML(MALAY,MALAY_BRUNEI_DARUSSALAM), "ms_BN"},
290  {RML(MALAY,MALAY_MALAYSIA), "ms_MY"},
291  {RML(MARATHI,DEFAULT), "mr_IN"},
292/*{RML(MONGOLIAN,DEFAULT), "mn_MN"},*/
293  {RML(NORWEGIAN,NORWEGIAN_BOKMAL), "nb_NO"},
294  {RML(NORWEGIAN,NORWEGIAN_NYNORSK), "nn_NO"},
295  {RML(POLISH,DEFAULT), "pl_PL"},
296  {RML(PORTUGUESE,PORTUGUESE), "pt_PT"},
297  {RML(PORTUGUESE,PORTUGUESE_BRAZILIAN), "pt_BR"},
298  {RML(PUNJABI,DEFAULT), "pa_IN"},
299  {RML(ROMANIAN,DEFAULT), "ro_RO"},
300  {RML(RUSSIAN,DEFAULT), "ru_RU"},
301  {RML(SANSKRIT,DEFAULT), "sa_IN"},
302  {RML(SERBIAN,DEFAULT), "hr_HR"},
303  {RML(SERBIAN,SERBIAN_CYRILLIC), "sr_SP"},
304  {RML(SERBIAN,SERBIAN_LATIN), "sr_SP"},
305  {RML(SLOVAK,DEFAULT), "sk_SK"},
306  {RML(SLOVENIAN,DEFAULT), "sl_SI"},
307  {RML(SPANISH,SPANISH), "es_ES"},
308  {RML(SPANISH,SPANISH_ARGENTINA), "es_AR"},
309  {RML(SPANISH,SPANISH_BOLIVIA), "es_BO"},
310  {RML(SPANISH,SPANISH_CHILE), "es_CL"},
311  {RML(SPANISH,SPANISH_COLOMBIA), "es_CO"},
312  {RML(SPANISH,SPANISH_COSTA_RICA), "es_CR"},
313  {RML(SPANISH,SPANISH_DOMINICAN_REPUBLIC), "es_DO"},
314  {RML(SPANISH,SPANISH_ECUADOR), "es_EC"},
315  {RML(SPANISH,SPANISH_EL_SALVADOR), "es_SV"},
316  {RML(SPANISH,SPANISH_GUATEMALA), "es_GT"},
317  {RML(SPANISH,SPANISH_HONDURAS), "es_HN"},
318  {RML(SPANISH,SPANISH_MEXICAN), "es_MX"},
319  {RML(SPANISH,SPANISH_MODERN), "es_ES"},
320  {RML(SPANISH,SPANISH_NICARAGUA), "es_NI"},
321  {RML(SPANISH,SPANISH_PANAMA), "es_PA"},
322  {RML(SPANISH,SPANISH_PARAGUAY), "es_PY"},
323  {RML(SPANISH,SPANISH_PERU), "es_PE"},
324  {RML(SPANISH,SPANISH_PUERTO_RICO), "es_PR"},
325  {RML(SPANISH,SPANISH_URUGUAY), "es_UY"},
326  {RML(SPANISH,SPANISH_VENEZUELA), "es_VE"},
327  {RML(SWAHILI,DEFAULT), "sw_KE"},
328  {RML(SWEDISH,SWEDISH), "sv_SE"},
329  {RML(SWEDISH,SWEDISH_FINLAND), "sv_FI"},
330/*{RML(SYRIAC,DEFAULT), "syr_SY"},*/
331  {RML(TAMIL,DEFAULT), "ta_IN"},
332  {RML(TATAR,DEFAULT), "tt_TA"},
333  {RML(TELUGU,DEFAULT), "te_IN"},
334  {RML(THAI,DEFAULT), "th_TH"},
335  {RML(TURKISH,DEFAULT), "tr_TR"},
336  {RML(UKRAINIAN,DEFAULT), "uk_UA"},
337  {RML(URDU,URDU_PAKISTAN), "ur_PK"},
338  {RML(UZBEK,UZBEK_CYRILLIC), "uz_UZ"},
339  {RML(UZBEK,UZBEK_LATIN), "uz_UZ"},
340  {RML(VIETNAMESE,DEFAULT), "vi_VN"},
341/*{RML(WALON,DEFAULT), "wa_BE"},*/
342/*{RML(WELSH,DEFAULT), "cy_GB"},*/
343};
344static const IDToCode primary_to_code[] = {
345  {LANG_AFRIKAANS,  "af"},
346  {LANG_ARABIC,     "ar"},
347  {LANG_AZERI,      "az"},
348  {LANG_BULGARIAN,  "bg"},
349/*{LANG_BRETON,     "br"},*/
350  {LANG_BELARUSIAN, "by"},
351  {LANG_CATALAN,    "ca"},
352  {LANG_CZECH,      "cs"},
353/*{LANG_WELSH,      "cy"},*/
354  {LANG_DANISH,     "da"},
355  {LANG_GERMAN,     "de"},
356  {LANG_GREEK,      "el"},
357  {LANG_ENGLISH,    "en"},
358/*{LANG_ESPERANTO,  "eo"},*/
359  {LANG_SPANISH,    "es"},
360  {LANG_ESTONIAN,   "et"},
361  {LANG_BASQUE,     "eu"},
362  {LANG_FARSI,      "fa"},
363  {LANG_FINNISH,    "fi"},
364  {LANG_FAEROESE,   "fo"},
365  {LANG_FRENCH,     "fr"},
366/*{LANG_GAELIC,     "ga"},*/
367/*{LANG_GALICIAN,   "gl"},*/
368  {LANG_GUJARATI,   "gu"},
369  {LANG_HEBREW,     "he"},
370  {LANG_HINDI,      "hi"},
371  {LANG_SERBIAN,    "hr"},
372  {LANG_HUNGARIAN,  "hu"},
373  {LANG_ARMENIAN,   "hy"},
374  {LANG_INDONESIAN, "id"},
375  {LANG_ITALIAN,    "it"},
376  {LANG_JAPANESE,   "ja"},
377  {LANG_GEORGIAN,   "ka"},
378  {LANG_KAZAK,      "kk"},
379  {LANG_KANNADA,    "kn"},
380  {LANG_KOREAN,     "ko"},
381/*{LANG_KYRGYZ,     "ky"},*/
382  {LANG_LITHUANIAN, "lt"},
383  {LANG_LATVIAN,    "lv"},
384  {LANG_MACEDONIAN, "mk"},
385/*{LANG_MONGOLIAN,  "mn"},*/
386  {LANG_MARATHI,    "mr"},
387  {LANG_MALAY,      "ms"},
388  {LANG_NORWEGIAN,  "nb"},
389  {LANG_DUTCH,      "nl"},
390  {LANG_NORWEGIAN,  "nn"},
391  {LANG_NORWEGIAN,  "no"},/* unofficial? */
392  {LANG_PUNJABI,    "pa"},
393  {LANG_POLISH,     "pl"},
394  {LANG_PORTUGUESE, "pt"},
395  {LANG_ROMANIAN,   "ro"},
396  {LANG_RUSSIAN,    "ru"},
397  {LANG_SLOVAK,     "sk"},
398  {LANG_SLOVENIAN,  "sl"},
399  {LANG_ALBANIAN,   "sq"},
400  {LANG_SERBIAN,    "sr"},
401  {LANG_SWEDISH,    "sv"},
402  {LANG_SWAHILI,    "sw"},
403  {LANG_TAMIL,      "ta"},
404  {LANG_THAI,       "th"},
405  {LANG_TURKISH,    "tr"},
406  {LANG_TATAR,      "tt"},
407  {LANG_UKRAINIAN,  "uk"},
408  {LANG_URDU,       "ur"},
409  {LANG_UZBEK,      "uz"},
410  {LANG_VIETNAMESE, "vi"},
411/*{LANG_WALON,      "wa"},*/
412  {LANG_CHINESE,    "zh"},
413};
414static int num_primary_to_code = LL_ARRAY_SIZE(primary_to_code);
415static int num_both_to_code = LL_ARRAY_SIZE(both_to_code);
416
417static const int
418lcid_to_fl(LCID lcid,
419           FL_Locale *rtn) {
420  LANGID langid       = LANGIDFROMLCID(lcid);
421  LANGID primary_lang = PRIMARYLANGID(langid);
422  /*LANGID sub_lang     = SUBLANGID(langid);*/
423  int i;
424  /* try to find an exact primary/sublanguage combo that we know about */
425  for (i=0; i<num_both_to_code; ++i) {
426    if (both_to_code[i].id == langid) {
427      accumulate_locstring(both_to_code[i].code, rtn);
428      return 1;
429    }
430  }
431  /* fallback to just checking the primary language id */
432  for (i=0; i<num_primary_to_code; ++i) {
433    if (primary_to_code[i].id == primary_lang) {
434      accumulate_locstring(primary_to_code[i].code, rtn);
435      return 1;
436    }
437  }
438  return 0;
439}
440#endif
441
442
443FL_Success
444FL_FindLocale(FL_Locale **locale, FL_Domain domain) {
445  FL_Success success = FL_FAILED;
446  FL_Locale *rtn = (FL_Locale*)malloc(sizeof(FL_Locale));
447  rtn->lang = NULL;
448  rtn->country = NULL;
449  rtn->variant = NULL;
450
451#ifdef WIN32
452  /* win32 >= mswindows95 */
453  {
454    LCID lcid = GetThreadLocale();
455    if (lcid_to_fl(lcid, rtn)) {
456      success = FL_CONFIDENT;
457    }
458    if (success == FL_FAILED) {
459      /* assume US English on mswindows systems unless we know otherwise */
460      if (accumulate_locstring("en_US.ISO_8859-1", rtn)) {
461        success = FL_DEFAULT_GUESS;
462      }
463    }
464  }
465#else
466  /* assume unixoid */
467  {
468    /* examples: */
469    /* sv_SE.ISO_8859-1 */
470    /* fr_FR.ISO8859-1 */
471    /* no_NO_NB */
472    /* no_NO_NY */
473    /* no_NO */
474    /* de_DE */
475    /* try the various vars in decreasing order of authority */
476    if (accumulate_env("LC_ALL", rtn) ||
477        accumulate_env("LC_MESSAGES", rtn) ||
478        accumulate_env("LANG", rtn) ||
479        accumulate_env("LANGUAGE", rtn)) {
480      success = FL_CONFIDENT;
481    }
482    if (success == FL_FAILED) {
483      /* assume US English on unixoid systems unless we know otherwise */
484      if (accumulate_locstring("en_US.ISO_8859-1", rtn)) {
485        success = FL_DEFAULT_GUESS;
486      }
487    }
488  }
489#endif
490
491  if (success != FL_FAILED) {
492    canonise_fl(rtn);
493  }
494
495  *locale = rtn;
496  return success;
497}
498
499
500void
501FL_FreeLocale(FL_Locale **locale) {
502  if (locale) {
503    FL_Locale *l = *locale;
504    if (l) {
505      if (l->lang) {
506        free((void*)l->lang);
507      }
508      if (l->country) {
509        free((void*)l->country);
510      }
511      if (l->variant) {
512        free((void*)l->variant);
513      }
514      free(l);
515      *locale = NULL;
516    }
517  }
518}