gettext.c: 'search' is now a fixed length string buffer.
[citadel.git] / webcit / gettext.c
1 /*
2  * $Id
3  */
4 /**
5  * \defgroup LocaleHeaderParser Parse the browser http locale headers and set the NLS stuff.
6  * \ingroup WebcitHttpServer 
7  */
8 /*@{*/
9 #include "webcit.h"
10 #include "webserver.h"
11
12 #ifdef ENABLE_NLS
13
14 #define NUM_LANGS 7 /**< how many different locales do we know? */
15 #define SEARCH_LANG 20 /**< how many langs should we parse? */
16
17 /** actual supported locales */
18 char *AvailLang[NUM_LANGS] = {
19         "C",
20         "en_US",
21         "de_DE",
22         "it_IT",
23         "es_ES",
24         "en_GB",
25         "da_DK"
26 };
27
28 locale_t wc_locales[NUM_LANGS]; /**< here we keep the parsed stuff */
29
30 /** Keep information about one locale */
31 typedef struct _lang_pref{
32         char lang[16];          /**< the language locale string */
33         char region[16];        /**< the region locale string */
34         long priority;          /**< which priority does it have */
35         int availability;       /**< do we know it? */
36         int selectedlang;       /**< is this the selected language? */
37 } LangStruct;
38
39 /* \brief parse browser locale header 
40  * seems as most browsers just do a one after coma value even if more than 10 locales are available. Sample strings:
41  * opera: 
42  * 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 
43  * Firefox 
44  * Accept-Language: 'de-de,en-us;q=0.7,en;q=0.3' 
45  * Accept-Language: de,en-ph;q=0.8,en-us;q=0.5,de-at;q=0.3 
46  * 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 
47  * \param LocaleString the string from the browser http headers
48  */
49
50 void httplang_to_locale(char *LocaleString)
51 {
52         LangStruct wanted_locales[SEARCH_LANG];
53         LangStruct *ls;
54
55         int i = 0;
56         int j = 0;
57         /* size_t len = strlen(LocaleString); */
58         long prio;
59         int av;
60         int nBest;
61         int nParts;
62         char search[1024];
63         
64         safestrncpy(search, LocaleString, sizeof search);
65         nParts=num_tokens(search,',');
66         for (i=0; ((i<nParts)&&(i<SEARCH_LANG)); i++)
67         {
68                         char buf[16];
69                         char sbuf[16];
70                         char lbuf[16];
71                         int blen;
72                         
73                         ls=&wanted_locales[i];
74
75                         extract_token(&buf[0],search, i,',',16);
76                         /** we are searching, if this list item has something like ;q=n*/
77                         if (num_tokens(&buf[0],'=')>1) {
78                                 int sbuflen, k;
79                                 extract_token(&sbuf[0],&buf[0], 1,'=',16);
80                                 sbuflen=strlen(&sbuf[0]);
81                                 for (k=0; k<sbuflen; k++) if (sbuf[k]=='.') sbuf[k]='0';
82                                 ls->priority=atol(&sbuf[0]);
83                         }
84                         else {
85                                 ls->priority=1000;
86                         }
87                         /** get the locale part */
88                         extract_token(&sbuf[0],&buf[0],0,';',16);
89                         /** get the lang part, which should be allways there */
90                         extract_token(&ls->lang[0],&sbuf[0],0,'-',16);
91                         /** get the area code if any. */
92                         if (num_tokens(&sbuf[0],'-')>1) {
93                                 extract_token(&ls->region[0],&sbuf[0],1,'-',16);
94                         }
95                         else { /** no ara code? use lang code */
96                                 blen=strlen(&ls->lang[0]);
97                                 memcpy(&ls->region[0], ls->lang,blen);
98                                 ls->region[blen]='\0';
99                         } /** area codes are uppercase */
100                         blen=strlen(&ls->region[0]);
101                         for (j=0; j<blen; j++)
102                                 {
103                                         int chars=toupper(ls->region[j]);
104                                         ls->region[j]=(char)chars;/** \todo ?! */
105                                 }
106                         sprintf(&lbuf[0],"%s_%s",&ls->lang[0],&ls->region[0]);
107                         
108                         /** check if we have this lang */
109                         ls->availability=1;
110                         ls->selectedlang=-1;
111                         for (j=0; j<NUM_LANGS; j++) {
112                                 int result;
113                                 /** match against the LANG part */
114                                 result=strcasecmp(&ls->lang[0], AvailLang[j]);
115                                 if ((result<0)&&(result<ls->availability)){
116                                         ls->availability=result;
117                                         ls->selectedlang=j;
118                                 }
119                                 /** match against lang and locale */
120                                 if (0==strcasecmp(&lbuf[0], AvailLang[j])){
121                                         ls->availability=0;
122                                         ls->selectedlang=j;
123                                         j=NUM_LANGS;
124                                 }
125                         }
126         }
127         
128         prio=0;
129         av=-1000;
130         nBest=-1;
131         for (i=0; ((i<nParts)&&(i<SEARCH_LANG)); i++) {
132                 ls=&wanted_locales[i];
133                 if ((ls->availability<=0)&& 
134                    (av<ls->availability)&&
135                    (prio<ls->priority)&&
136                    (ls->selectedlang!=-1)) {
137                         nBest=ls->selectedlang;
138                         av=ls->availability;
139                         prio=ls->priority;
140                 }
141         }
142         if (nBest == -1) {
143                 /** fall back to C */
144                 nBest=0;
145         }
146         WC->selected_language=nBest;
147         lprintf(9, "language found: %s\n", AvailLang[WC->selected_language]);
148 }
149
150 /* TODO: we skip the language weightening so far. */
151 /* Accept-Language: 'de-de,en-us;q=0.7,en;q=0.3' */
152 /* Accept-Language: de,en-ph;q=0.8,en-us;q=0.5,de-at;q=0.3 */
153 //void httplang_to_locale(char *LocaleString)
154 //{
155 //      char selected_locale[16];
156 //      int i, j;
157 //      char lang[64];
158 //      int num_accept = 0;
159 //
160 //      lprintf(9, "languageAccept: %s\n", LocaleString);
161 //
162 //      strcpy(selected_locale, "C");
163 //      num_accept = num_tokens(LocaleString, ',');
164 //
165 //      for (i=num_accept-1; i>=0; --i) {
166 //              extract_token(lang, LocaleString, i, ',', sizeof lang);
167 //
168 //              /* Strip out the weights; we don't use them.  Also convert
169 //               * hyphens to underscores.
170 //               */
171 //              for (j=0; j<strlen(lang); ++j) {
172 //                      if (lang[j] == '-') lang[j] = '_';
173 //                      if (lang[j] == ';') lang[j] = 0;
174 //              }
175 //
176 //              for (j=0; j<NUM_LANGS; ++j) {
177 //                      if (!strncasecmp(lang, AvailLang[j], strlen(lang))) {
178 //                              strcpy(selected_locale, AvailLang[j]);
179 //                      }
180 //              }
181 //      }
182 //
183 //      lprintf(9, "language found: %s\n", selected_locale);
184 //      set_selected_language(selected_locale);
185 //}
186
187
188 /**
189  * \brief show the language chooser on the login dialog
190  * depending on the browser locale change the sequence of the 
191  * language chooser.
192  */
193 void offer_languages(void) {
194         int i;
195
196         wprintf("<select name=\"language\" size=\"1\">\n");
197
198         for (i=0; i < NUM_LANGS; ++i) {
199                 wprintf("<option %s value=%s>%s</option>\n",
200                         ((WC->selected_language == i) ? "selected" : ""),
201                         AvailLang[i],
202                         AvailLang[i]
203                 );
204         }
205
206         wprintf("</select>\n");
207 }
208
209 /**
210  * \brief Set the selected language for this session.
211  * \param lang the locale to set.
212  */
213 void set_selected_language(char *lang) {
214         int i;
215
216         for (i=0; i<NUM_LANGS; ++i) {
217                 if (!strcasecmp(lang, AvailLang[i])) {
218                         WC->selected_language = i;
219                 }
220         }
221 }
222
223 /**
224  * \brief Activate the selected language for this session.
225  */
226 void go_selected_language(void) {
227         if (WC->selected_language < 0) return;
228         uselocale(wc_locales[WC->selected_language]);   /** switch locales */
229         textdomain(textdomain(NULL));                   /** clear the cache */
230 }
231
232 /**
233  * \brief Deactivate the selected language for this session.
234  */
235 void stop_selected_language(void) {
236         uselocale(LC_GLOBAL_LOCALE);                    /** switch locales */
237         textdomain(textdomain(NULL));                   /** clear the cache */
238 }
239
240
241 /**
242  * \brief Create a locale_t for each available language
243  */
244 void initialize_locales(void) {
245         int i;
246         locale_t Empty_Locale;
247         char buf[32];
248
249         /* create default locale */
250         Empty_Locale = newlocale(LC_ALL_MASK, NULL, NULL);
251
252         for (i = 0; i < NUM_LANGS; ++i) {
253                 if (i == 0) {
254                         sprintf(buf, "%s", AvailLang[i]);       // locale 0 (C) is ascii, not utf-8
255                 }
256                 else {
257                         sprintf(buf, "%s.UTF8", AvailLang[i]);
258                 }
259                 wc_locales[i] = newlocale(
260                         (LC_MESSAGES_MASK|LC_TIME_MASK),
261                         buf,
262                         (((i > 0) && (wc_locales[0] != NULL)) ? wc_locales[0] : Empty_Locale)
263                 );
264                 if (wc_locales[i] == NULL) {
265                         lprintf(1, "Error configuring locale for %s: %s\n",
266                                 buf,
267                                 strerror(errno)
268                         );
269                 }
270                 else {
271                         lprintf(3, "Configured available locale: %s\n", buf);
272                 }
273         }
274 }
275
276
277 #else   /* ENABLE_NLS */
278 /** \brief dummy for non NLS enabled systems */
279 void offer_languages(void) {
280         wprintf("English (US)");
281 }
282
283 /** \brief dummy for non NLS enabled systems */
284 void set_selected_language(char *lang) {
285 }
286
287 /** \brief dummy for non NLS enabled systems */
288 void go_selected_language(void) {
289 }
290
291 /** \brief dummy for non NLS enabled systems */
292 void stop_selected_language(void) {
293 }
294
295 #endif  /* ENABLE_NLS */
296
297
298 /*@}*/