2 * Copyright (c) 1996-2012 by the citadel.org team
4 * This program is open source software. You can redistribute it and/or
5 * modify it under the terms of the GNU General Public License version 3.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
14 #include "webserver.h"
15 #define SEARCH_LANG 20 /* how many langs should we parse? */
18 /* actual supported locales */
19 const char *AvailLang[] = {
46 const char **AvailLangLoaded;
47 long nLocalesLoaded = 0;
49 locale_t *wc_locales; /* here we keep the parsed stuff */
51 /* Keep information about one locale */
52 typedef struct _lang_pref{
53 char lang[16]; /* the language locale string */
54 char region[16]; /* the region locale string */
55 long priority; /* which priority does it have */
56 int availability; /* do we know it? */
57 int selectedlang; /* is this the selected language? */
60 /* parse browser locale header
62 * seems as most browsers just do a one after comma value even if more than 10 locales are available. Sample strings:
64 * 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
66 * Accept-Language: 'de-de,en-us;q=0.7,en;q=0.3'
67 * Accept-Language: de,en-ph;q=0.8,en-us;q=0.5,de-at;q=0.3
68 * 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
71 void httplang_to_locale(StrBuf *LocaleString, wcsession *sess)
73 LangStruct wanted_locales[SEARCH_LANG];
78 /* size_t len = strlen(LocaleString); */
86 nParts = StrBufNum_tokens(LocaleString, ',');
87 for (i=0; ((i<nParts) && (i < SEARCH_LANG)); i++)
101 ls = &wanted_locales[i];
103 StrBufExtract_token(Buf, LocaleString, i, ',');
104 /* we are searching, if this list item has something like ;q=n*/
105 if (StrBufNum_tokens(Buf, '=') > 1) {
107 StrBufExtract_token(SBuf, Buf, 1, '=');
108 sbuflen = StrLength(SBuf);
109 for (k = 0; k < sbuflen; k++) {
110 if (ChrPtr(SBuf)[k] == '.') {
111 StrBufPeek(SBuf, NULL, k, '0');
114 ls->priority = StrTol(SBuf);
120 /* get the locale part */
121 StrBufExtract_token(SBuf, Buf, 0, ';');
123 /* get the lang part, which should be allways there */
124 extract_token(&ls->lang[0],
129 /* get the area code if any. */
130 if (StrBufNum_tokens(SBuf, '-') > 1) {
131 extract_token(&ls->region[0],
137 else { /* no ara code? use lang code */
138 blen=strlen(&ls->lang[0]);
139 memcpy(&ls->region[0], ls->lang, blen);
140 ls->region[blen] = '\0';
143 /* area codes are uppercase */
144 blen = strlen(&ls->region[0]);
145 for (j = 0; j < blen; j++)
148 chars = toupper(ls->region[j]);
149 ls->region[j] = (char)chars; /* todo ? */
157 /* check if we have this lang */
158 ls->availability = 1;
159 ls->selectedlang = -1;
160 for (j = 0; j < nLocalesLoaded; j++) {
162 /* match against the LANG part */
163 result = strcasecmp(&ls->lang[0], AvailLangLoaded[j]);
164 if ((result < 0) && (result < ls->availability)){
165 ls->availability = result;
166 ls->selectedlang = j;
168 /* match against lang and locale */
169 if (0 == strcasecmp(&lbuf[0], AvailLangLoaded[j])){
170 ls->availability = 0;
171 ls->selectedlang = j;
180 for (i = 0; ((i < nParts) && (i<SEARCH_LANG)); i++) {
181 ls = &wanted_locales[i];
182 if ( (ls->availability <= 0)
183 && (av < ls->availability)
184 && (prio < ls->priority)
185 && (ls->selectedlang != -1)
187 nBest = ls->selectedlang;
188 av = ls->availability;
196 sess->selected_language = nBest;
197 syslog(9, "language found: %s", AvailLangLoaded[WC->selected_language]);
204 * show the language chooser on the login dialog
205 * depending on the browser locale change the sequence of the
208 void tmplput_offer_languages(StrBuf *Target, WCTemplputParams *TP)
212 wc_printf("<select name=\"language\" id=\"lname\" size=\"1\" onChange=\"switch_to_lang($('lname').value);\">\n");
214 for (i=0; i < nLocalesLoaded; ++i) {
215 wc_printf("<option %s value=%s>%s</option>\n",
216 ((WC->selected_language == i) ? "selected" : ""),
222 wc_printf("</select>\n");
226 * Set the selected language for this session.
228 void set_selected_language(const char *lang) {
230 for (i = 0; i<nLocalesLoaded; ++i) {
231 if (!strcasecmp(lang, AvailLangLoaded[i])) {
232 WC->selected_language = i;
239 * Activate the selected language for this session.
241 void go_selected_language(void) {
243 if (WCC->selected_language < 0) return;
244 uselocale(wc_locales[WCC->selected_language]); /* switch locales */
245 textdomain(textdomain(NULL)); /* clear the cache */
249 * Deactivate the selected language for this session.
251 void stop_selected_language(void) {
252 uselocale(LC_GLOBAL_LOCALE); /* switch locales */
253 textdomain(textdomain(NULL)); /* clear the cache */
258 * Create a locale_t for each available language
260 void initialize_locales(void) {
263 char *language = NULL;
265 setlocale(LC_ALL, "");
266 syslog(9, "Text domain: %s", textdomain("webcit"));
267 syslog(9, "Message catalog directory: %s", bindtextdomain(textdomain(NULL), LOCALEDIR"/locale"));
268 syslog(9, "Text domain Charset: %s", bind_textdomain_codeset("webcit","UTF8"));
271 while (!IsEmptyStr(AvailLang[nLocales]))
274 language = getenv("WEBCIT_LANG");
275 if ((language) && (!IsEmptyStr(language)) && (strcmp(language, "UNLIMITED") != 0)) {
276 syslog(9, "Nailing locale to %s", language);
278 else language = NULL;
280 AvailLangLoaded = malloc (sizeof(char*) * nLocales);
281 memset(AvailLangLoaded, 0, sizeof(char*) * nLocales);
282 wc_locales = malloc (sizeof(locale_t) * nLocales);
283 memset(wc_locales,0, sizeof(locale_t) * nLocales);
284 wc_locales[0] = newlocale(LC_ALL_MASK, NULL, NULL);
286 for (i = 1; i < nLocales; ++i) {
287 wc_locales[nLocalesLoaded] = newlocale(
288 (LC_MESSAGES_MASK|LC_TIME_MASK),
292 if (wc_locales[nLocalesLoaded] == NULL) {
293 syslog(1, "locale for %s disabled: %s (domain: %s, path: %s)",
297 bindtextdomain(textdomain(NULL), NULL)
301 syslog(3, "Found locale: %s", AvailLang[i]);
302 AvailLangLoaded[nLocalesLoaded] = AvailLang[i];
306 if ((language != NULL) && (nLocalesLoaded == 0)) {
307 syslog(1, "Your selected locale [%s] isn't available on your system. falling back to C", language);
308 setlocale(LC_MESSAGES, AvailLang[0]);
309 setenv("LANG", AvailLang[0], 1);
310 AvailLangLoaded[0] = AvailLang[0];
317 ServerShutdownModule_GETTEXT
321 for (i = 0; i < nLocalesLoaded; ++i) {
322 freelocale(wc_locales[i]);
325 free(AvailLangLoaded);
328 #else /* HAVE_USELOCALE */
329 const char *AvailLang[] = {
334 /* dummy for non NLS enabled systems */
335 void tmplput_offer_languages(StrBuf *Target, WCTemplputParams *TP)
337 wc_printf("English (US)");
340 /* dummy for non NLS enabled systems */
341 void set_selected_language(const char *lang) {
344 /* dummy for non NLS enabled systems */
345 void go_selected_language(void) {
348 /* dummy for non NLS enabled systems */
349 void stop_selected_language(void) {
352 /* dummy for non NLS enabled systems */
353 void initialize_locales(void) {
356 /* dummy for non NLS enabled systems */
358 ServerShutdownModule_GETTEXT
364 #endif /* HAVE_USELOCALE */
372 void TmplGettext(StrBuf *Target, WCTemplputParams *TP)
374 StrBufAppendBufPlain(Target, _(TP->Tokens->Params[0]->Start), -1, 0);
379 * Returns the language currently in use.
380 * This function returns a static string, so don't do anything stupid please.
382 const char *get_selected_language(void) {
383 #ifdef HAVE_USELOCALE
384 return AvailLang[WC->selected_language];
391 void Header_HandleAcceptLanguage(StrBuf *Line, ParsedHttpHdrs *hdr)
393 hdr->HR.browser_language = Line;
401 initialize_locales();
403 RegisterHeaderHandler(HKEY("ACCEPT-LANGUAGE"),
404 Header_HandleAcceptLanguage);
406 RegisterNamespace("LANG:SELECT", 0, 0,
407 tmplput_offer_languages, NULL, CTX_NONE);
412 SessionNewModule_GETTEXT
415 #ifdef HAVE_USELOCALE
417 && (!sess->Hdr->HR.Static)
418 && (sess->Hdr->HR.browser_language != NULL)
420 httplang_to_locale(sess->Hdr->HR.browser_language, sess);
426 SessionAttachModule_GETTEXT
429 #ifdef HAVE_USELOCALE
430 go_selected_language(); /* set locale */
435 SessionDestroyModule_GETTEXT
438 #ifdef HAVE_USELOCALE
439 stop_selected_language(); /* unset locale */