Back to index

webcit  8.12-dfsg
gettext.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 1996-2012 by the citadel.org team
00003  *
00004  * This program is open source software.  You can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License version 3.
00006  *
00007  * This program is distributed in the hope that it will be useful,
00008  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00009  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00010  * GNU General Public License for more details.
00011  */
00012 
00013 #include "webcit.h"
00014 #include "webserver.h"
00015 #define SEARCH_LANG 20             /* how many langs should we parse? */
00016 
00017 #ifdef ENABLE_NLS
00018 /* actual supported locales */
00019 const char *AvailLang[] = {
00020        "C",
00021        "bg_BG",
00022        "cs_CZ",
00023        "en_US",
00024        "da_DK",
00025        "de_DE",
00026        "el_GR",
00027        "en_GB",
00028        "es_ES",
00029        "et_EE",
00030        "fi_FI",
00031        "fr_FR",
00032        "hu_HU",
00033        "it_IT",
00034        "nl_NL",
00035        "pt_BR",
00036        "ru_RU",
00037        "zh_CN",
00038        "he_IL",
00039        "kk_KK",
00040        "ro_RO",
00041        "sl_SL",
00042        "tr_TR",
00043        ""
00044 };
00045 
00046 const char **AvailLangLoaded;
00047 long nLocalesLoaded = 0;
00048 
00049 #ifdef HAVE_USELOCALE
00050 locale_t *wc_locales; /* here we keep the parsed stuff */
00051 #endif
00052 
00053 /* Keep information about one locale */
00054 typedef struct _lang_pref {
00055        char lang[16];          /* the language locale string */
00056        char region[16];        /* the region locale string */
00057        long priority;          /* which priority does it have */
00058        int availability;       /* do we know it? */
00059        int selectedlang;       /* is this the selected language? */
00060 } LangStruct;
00061 
00062 /* parse browser locale header 
00063  *
00064  * seems as most browsers just do a one after comma value even if more than 10 locales are available. Sample strings:
00065  * opera: 
00066  * Accept-Language: sq;q=1.0,de;q=0.9,as;q=0.8,ar;q=0.7,bn;q=0.6,zh-cn;q=0.5,kn;q=0.4,ch;q=0.3,fo;q=0.2,gn;q=0.1,ce;q=0.1,ie;q=0.1 
00067  * Firefox 
00068  * Accept-Language: 'de-de,en-us;q=0.7,en;q=0.3' 
00069  * Accept-Language: de,en-ph;q=0.8,en-us;q=0.5,de-at;q=0.3 
00070  * Accept-Language: de,en-us;q=0.9,it;q=0.9,de-de;q=0.8,en-ph;q=0.7,de-at;q=0.7,zh-cn;q=0.6,cy;q=0.5,ar-om;q=0.5,en-tt;q=0.4,xh;q=0.3,nl-be;q=0.3,cs;q=0.2,sv;q=0.1,tk;q=0.1 
00071  */
00072 
00073 void httplang_to_locale(StrBuf *LocaleString, wcsession *sess)
00074 {
00075        LangStruct wanted_locales[SEARCH_LANG];
00076        LangStruct *ls;
00077 
00078        int i = 0;
00079        int j = 0;
00080        /* size_t len = strlen(LocaleString); */
00081        long prio;
00082        int av;
00083        int nBest;
00084        int nParts;
00085        StrBuf *Buf = NULL;
00086        StrBuf *SBuf = NULL;
00087 
00088        nParts = StrBufNum_tokens(LocaleString, ',');
00089        for (i=0; ((i<nParts) && (i < SEARCH_LANG)); i++)
00090         {
00091               char lbuf[32];
00092               int blen;
00093                      
00094               if (Buf == NULL) {
00095                      Buf = NewStrBuf();
00096                      SBuf = NewStrBuf();
00097               }
00098               else {
00099                      FlushStrBuf(Buf);
00100                      FlushStrBuf(SBuf);
00101               }
00102 
00103               ls = &wanted_locales[i];
00104 
00105               StrBufExtract_token(Buf, LocaleString, i, ',');
00106               /* we are searching, if this list item has something like ;q=n*/
00107               if (StrBufNum_tokens(Buf, '=') > 1) {
00108                      int sbuflen, k;
00109                      StrBufExtract_token(SBuf, Buf, 1, '=');
00110                      sbuflen = StrLength(SBuf);
00111                      for (k = 0; k < sbuflen; k++) {
00112                             if (ChrPtr(SBuf)[k] == '.') {
00113                                    StrBufPeek(SBuf, NULL, k, '0');
00114                             }
00115                      }
00116                      ls->priority = StrTol(SBuf);
00117               }
00118               else {
00119                      ls->priority = 1000;
00120               }
00121 
00122               /* get the locale part */
00123               StrBufExtract_token(SBuf, Buf, 0, ';');
00124 
00125               /* get the lang part, which should be allways there */
00126               extract_token(&ls->lang[0], 
00127                            ChrPtr(SBuf), 
00128                            0, '-', 
00129                            sizeof(ls->lang));
00130 
00131               /* get the area code if any. */
00132               if (StrBufNum_tokens(SBuf, '-') > 1) {
00133                      extract_token(&ls->region[0], 
00134                                   ChrPtr(SBuf), 
00135                                   1, '-', 
00136                                   sizeof(ls->region)
00137                      );
00138               }
00139               else { /* no ara code? use lang code */
00140                      blen=strlen(&ls->lang[0]);
00141                      memcpy(&ls->region[0], ls->lang, blen);
00142                      ls->region[blen] = '\0';
00143               }
00144 
00145               /* area codes are uppercase */
00146               blen = strlen(&ls->region[0]);
00147               for (j = 0; j < blen; j++)
00148               {
00149                      int chars;
00150                      chars = toupper(ls->region[j]);
00151                      ls->region[j] = (char)chars; /* todo ? */
00152               }
00153               snprintf(&lbuf[0], 
00154                       sizeof(lbuf), 
00155                       "%s_%s", 
00156                       &ls->lang[0], 
00157                       &ls->region[0]);
00158                      
00159               /* check if we have this lang */
00160               ls->availability = 1;
00161               ls->selectedlang = -1;
00162               for (j = 0; j < nLocalesLoaded; j++) {
00163                      int result;
00164                      /* match against the LANG part */
00165                      result = strcasecmp(&ls->lang[0], AvailLangLoaded[j]);
00166                      if ((result < 0) && (result < ls->availability)){
00167                             ls->availability = result;
00168                             ls->selectedlang = j;
00169                      }
00170                      /* match against lang and locale */
00171                      if (0 == strcasecmp(&lbuf[0], AvailLangLoaded[j])){
00172                             ls->availability = 0;
00173                             ls->selectedlang = j;
00174                             j = nLocalesLoaded;
00175                      }
00176               }
00177         }
00178        
00179        prio = 0;
00180        av = -1000;
00181        nBest = -1;
00182        for (i = 0; ((i < nParts) && (i<SEARCH_LANG)); i++) {
00183               ls = &wanted_locales[i];
00184               if (   (ls->availability <= 0)
00185                      && (av < ls->availability)
00186                      && (prio < ls->priority)
00187                      && (ls->selectedlang != -1)
00188               ) {
00189                      nBest = ls->selectedlang;
00190                      av = ls->availability;
00191                      prio = ls->priority;
00192               }
00193        }
00194        if (nBest == -1) {
00195               /* fall back to C */
00196               nBest=0;
00197        }
00198        sess->selected_language = nBest;
00199        syslog(9, "language found: %s", AvailLangLoaded[WC->selected_language]);
00200        FreeStrBuf(&Buf);
00201        FreeStrBuf(&SBuf);
00202 }
00203 
00204 
00205 /*
00206  * show the language chooser on the login dialog
00207  * depending on the browser locale change the sequence of the 
00208  * language chooser.
00209  */
00210 void tmplput_offer_languages(StrBuf *Target, WCTemplputParams *TP)
00211 {
00212        int i;
00213 #ifndef HAVE_USELOCALE
00214        char *Lang = getenv("LANG");
00215        
00216        if (Lang == NULL)
00217               Lang = "C";
00218 #endif
00219 
00220        if (nLocalesLoaded == 1) {
00221               wc_printf("<p>%s</p>", AvailLangLoaded[0]);
00222               return;
00223        }
00224 
00225        wc_printf("<select name=\"language\" id=\"lname\" size=\"1\" onChange=\"switch_to_lang($('lname').value);\">\n");
00226 
00227        for (i=0; i < nLocalesLoaded; ++i) {
00228 #ifndef HAVE_USELOCALE
00229               if (strcmp(AvailLangLoaded[i], Lang) == 0)
00230 #endif
00231               wc_printf("<option %s value=%s>%s</option>\n",
00232                      ((WC->selected_language == i) ? "selected" : ""),
00233                      AvailLangLoaded[i],
00234                      AvailLangLoaded[i]
00235               );
00236        }
00237 
00238        wc_printf("</select>\n");
00239 }
00240 
00241 /*
00242  * Set the selected language for this session.
00243  */
00244 void set_selected_language(const char *lang) {
00245 #ifdef HAVE_USELOCALE
00246        int i;
00247        for (i = 0; i<nLocalesLoaded; ++i) {
00248               if (!strcasecmp(lang, AvailLangLoaded[i])) {
00249                      WC->selected_language = i;
00250                      break;
00251               }
00252        }
00253 #endif
00254 }
00255 
00256 /*
00257  * Activate the selected language for this session.
00258  */
00259 void go_selected_language(void) {
00260 #ifdef HAVE_USELOCALE
00261        wcsession *WCC = WC;
00262        if (WCC->selected_language < 0) return;
00263        uselocale(wc_locales[WCC->selected_language]);   /* switch locales */
00264        textdomain(textdomain(NULL));                    /* clear the cache */
00265 #else
00266        char *language;
00267        
00268        language = getenv("LANG");
00269        setlocale(LC_MESSAGES, language);
00270 #endif
00271 }
00272 
00273 /*
00274  * Deactivate the selected language for this session.
00275  */
00276 void stop_selected_language(void) {
00277 #ifdef HAVE_USELOCALE
00278        uselocale(LC_GLOBAL_LOCALE);                     /* switch locales */
00279        textdomain(textdomain(NULL));                    /* clear the cache */
00280 #endif
00281 }
00282 
00283 #ifdef HAVE_USELOCALE
00284        locale_t Empty_Locale;
00285 #endif
00286 
00287 /*
00288  * Create a locale_t for each available language
00289  */
00290 void initialize_locales(void) {
00291        int nLocales;
00292        int i;
00293        char buf[32];
00294        char *language = NULL;
00295 
00296        nLocales = 0; 
00297        while (!IsEmptyStr(AvailLang[nLocales]))
00298               nLocales++;
00299 
00300        language = getenv("WEBCIT_LANG");
00301        if ((language) && (!IsEmptyStr(language)) && (strcmp(language, "UNLIMITED") != 0)) {
00302               syslog(9, "Nailing locale to %s", language);
00303        }
00304        else language = NULL;
00305 
00306        AvailLangLoaded = malloc (sizeof(char*) * nLocales);
00307        memset(AvailLangLoaded, 0, sizeof(char*) * nLocales);
00308 #ifdef HAVE_USELOCALE
00309        wc_locales = malloc (sizeof(locale_t) * nLocales);
00310        memset(wc_locales,0, sizeof(locale_t) * nLocales);
00311        /* create default locale */
00312        Empty_Locale = newlocale(LC_ALL_MASK, NULL, NULL);
00313 #endif
00314 
00315        for (i = 0; i < nLocales; ++i) {
00316               if ((language != NULL) && (strcmp(AvailLang[i], language) != 0))
00317                      continue;
00318               if (i == 0) {
00319                      sprintf(buf, "%s", AvailLang[i]);  /* locale 0 (C) is ascii, not utf-8 */
00320               }
00321               else {
00322                      sprintf(buf, "%s.UTF8", AvailLang[i]);
00323               }
00324 #ifdef HAVE_USELOCALE
00325               wc_locales[nLocalesLoaded] = newlocale(
00326                      (LC_MESSAGES_MASK|LC_TIME_MASK),
00327                      buf,
00328                      (((i > 0) && (wc_locales[0] != NULL)) ? wc_locales[0] : Empty_Locale)
00329               );
00330               if (wc_locales[nLocalesLoaded] == NULL) {
00331                      syslog(1, "locale for %s disabled: %s", buf, strerror(errno));
00332               }
00333               else {
00334                      syslog(3, "Found locale: %s", buf);
00335                      AvailLangLoaded[nLocalesLoaded] = AvailLang[i];
00336                      nLocalesLoaded++;
00337               }
00338 #else
00339               if ((language != NULL) && (strcmp(language, AvailLang[i]) == 0)) {
00340                      setenv("LANG", buf, 1);
00341                      AvailLangLoaded[nLocalesLoaded] = AvailLang[i];
00342                      setlocale(LC_MESSAGES, AvailLang[i]);
00343                      nLocalesLoaded++;
00344               }
00345               else if (nLocalesLoaded == 0) {
00346                      setenv("LANG", buf, 1);
00347                      AvailLangLoaded[nLocalesLoaded] = AvailLang[i];
00348                      nLocalesLoaded++;
00349               }
00350 #endif
00351        }
00352        if ((language != NULL) && (nLocalesLoaded == 0)) {
00353               syslog(1, "Your selected locale [%s] isn't available on your system. falling back to C", language);
00354 #ifdef HAVE_USELOCALE
00355               wc_locales[0] = newlocale(
00356                      (LC_MESSAGES_MASK|LC_TIME_MASK),
00357                      AvailLang[0],
00358                      Empty_Locale
00359               );
00360 #else
00361               setlocale(LC_MESSAGES, AvailLang[0]);
00362               setenv("LANG", AvailLang[0], 1);
00363 #endif
00364               AvailLangLoaded[0] = AvailLang[0];
00365               nLocalesLoaded = 1;
00366        }
00367 
00368 #ifdef ENABLE_NLS
00369        setlocale(LC_ALL, "");
00370        syslog(9, "Text domain: %s", textdomain("webcit"));
00371        syslog(9, "Text domain Charset: %s", bind_textdomain_codeset("webcit", "UTF8"));
00372        syslog(9, "Message catalog directory: %s", bindtextdomain(textdomain(NULL), LOCALEDIR"/locale"));
00373 #endif
00374 }
00375 
00376 
00377 void 
00378 ServerShutdownModule_GETTEXT
00379 (void)
00380 {
00381 #ifdef HAVE_USELOCALE
00382        int i;
00383        for (i = 0; i < nLocalesLoaded; ++i) {
00384               if (Empty_Locale != wc_locales[i]) {
00385                      freelocale(wc_locales[i]);
00386               }
00387        }
00388        free(wc_locales);
00389 #endif
00390        free(AvailLangLoaded);
00391 }
00392 
00393 #else  /* ENABLE_NLS */
00394 const char *AvailLang[] = {
00395        "C",
00396        ""
00397 };
00398 
00399 /* dummy for non NLS enabled systems */
00400 void 
00401 ServerShutdownModule_GETTEXT
00402 (void)
00403 {
00404 }
00405 
00406 
00407 void tmplput_offer_languages(StrBuf *Target, WCTemplputParams *TP)
00408 {
00409        wc_printf("English (US)");
00410 }
00411 
00412 /* dummy for non NLS enabled systems */
00413 void set_selected_language(const char *lang) {
00414 }
00415 
00416 /* dummy for non NLS enabled systems */
00417 void go_selected_language(void) {
00418 }
00419 
00420 /* dummy for non NLS enabled systems */
00421 void stop_selected_language(void) {
00422 }
00423 
00424 void initialize_locales(void) {
00425 }
00426 
00427 #endif /* ENABLE_NLS */
00428 
00429 
00430 void TmplGettext(StrBuf *Target, WCTemplputParams *TP)
00431 {
00432        StrBufAppendBufPlain(Target, _(TP->Tokens->Params[0]->Start), -1, 0);
00433 }
00434 
00435 
00436 /*
00437  * Returns the language currently in use.
00438  * This function returns a static string, so don't do anything stupid please.
00439  */
00440 const char *get_selected_language(void) {
00441 #ifdef ENABLE_NLS
00442 #ifdef HAVE_USELOCALE
00443        return AvailLang[WC->selected_language];
00444 #else
00445        return "en";
00446 #endif
00447 #else
00448        return "en";
00449 #endif
00450 }
00451 
00452 
00453 void Header_HandleAcceptLanguage(StrBuf *Line, ParsedHttpHdrs *hdr)
00454 {
00455        hdr->HR.browser_language = Line;
00456 }
00457 
00458 
00459 void 
00460 InitModule_GETTEXT
00461 (void)
00462 {
00463        initialize_locales();
00464        
00465        RegisterHeaderHandler(HKEY("ACCEPT-LANGUAGE"), 
00466                            Header_HandleAcceptLanguage);
00467                            
00468        RegisterNamespace("LANG:SELECT", 0, 0, 
00469                        tmplput_offer_languages, NULL, CTX_NONE);
00470 }
00471 
00472 
00473 void
00474 SessionNewModule_GETTEXT
00475 (wcsession *sess)
00476 {
00477 #ifdef ENABLE_NLS
00478        if (   (sess != NULL)
00479               && (!sess->Hdr->HR.Static)
00480               && (sess->Hdr->HR.browser_language != NULL)
00481        ) {
00482               httplang_to_locale(sess->Hdr->HR.browser_language, sess);
00483        }
00484 #endif
00485 }
00486 
00487 void
00488 SessionAttachModule_GETTEXT
00489 (wcsession *sess)
00490 {
00491 #ifdef ENABLE_NLS
00492        go_selected_language();                                 /* set locale */
00493 #endif
00494 }
00495 
00496 void 
00497 SessionDestroyModule_GETTEXT
00498 (wcsession *sess)
00499 {
00500 #ifdef ENABLE_NLS
00501        stop_selected_language();                        /* unset locale */
00502 #endif
00503 }