1 /*
2 Copyright (c) 2001-2002 Perry Rapp
3 "The MIT license"
4 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
7 */
8 /*=============================================================
9 * locales.c -- functions dealing with locales
10 * TODO: this is a mess of ifdefs -- please clean up at some point
11 *==============================================================*/
12
13 #include "llstdlib.h"
14 #ifdef HAVE_LOCALE_H
15 # include <locale.h>
16 #if defined(_WIN32) && !defined(__CYGWIN__)
17 # include "isolangs.h"
18 #endif
19 #endif
20 #ifdef HAVE_LANGINFO_CODESET
21 # include <langinfo.h>
22 #else
23 # include "langinfz.h"
24 #endif
25 #include "translat.h"
26 #include "liflines.h"
27 #include "feedback.h"
28 #include "icvt.h"
29 #include "lloptions.h"
30 #include "date.h"
31 #include "gedcomi.h"
32
33 /*********************************************
34 * local function prototypes
35 *********************************************/
36
37 /* alphabetical */
38 static void customlocale(STRING prefix);
39 static STRING get_current_locale(INT category);
40 #ifdef ENABLE_NLS
41 static BOOLEAN is_msgcategory(int category);
42 #if ! ( defined(HAVE_SETLOCALE) && defined(HAVE_LC_MESSAGES) )
43 static STRING llsetenv(STRING name, STRING value);
44 #endif /* ! (defined(HAVE_SETLOCALE) && defined(HAVE_LC_MESSAGES) ) */
45 #endif /* ENABLE_NLS */
46 static void notify_gettext_language_changed(void);
47 static void send_uilang_callbacks(void);
48 #ifdef ENABLE_NLS
49 static STRING setmsgs(STRING localename);
50 #endif /* ENABLE_NLS */
51 static char * win32_setlocale(int category, char * locale);
52
53 /*********************************************
54 * local variables
55 *********************************************/
56
57 static STRING deflocale_coll = NULL;
58 static STRING deflocale_msgs = NULL;
59 static BOOLEAN customized_loc = FALSE;
60 #ifdef ENABLE_NLS
61 static BOOLEAN customized_msgs = FALSE;
62 #endif /* ENABLE_NLS */
63 static STRING current_coll = NULL; /* most recent */
64 static STRING current_msgs = NULL; /* most recent */
65 static STRING rptlocalestr = NULL; /* if set by report program */
66 static LIST f_uicodeset_callbacks = NULL; /* collection of callbacks for UI codeset changes */
67 static LIST f_uilang_callbacks = NULL; /* collection of callbacks for UI language changes */
68
69
70
71 /*********************************************
72 * local & exported function definitions
73 * body of module
74 *********************************************/
75
76 /*==========================================
77 * get_current_locale -- return current locale
78 * returns "C" in case setlocale(x, 0) returns 0
79 * Created: 2002/02/24 (Perry Rapp)
80 *========================================*/
81 #ifdef HAVE_SETLOCALE
82 static STRING
get_current_locale(INT category)83 get_current_locale (INT category)
84 {
85 STRING str = 0;
86 str = setlocale(category, NULL);
87 return str ? str : "C";
88 }
89 #endif /* HAVE_SETLOCALE */
90 /*==========================================
91 * save_original_locales -- grab current locales for later default
92 * We need these for an obscure problem. If user sets only
93 * locales for report, then when we switch back to GUI mode,
94 * we shouldn't stay in the customized report locale.
95 * Created: 2002/02/24 (Perry Rapp)
96 *========================================*/
97 void
save_original_locales(void)98 save_original_locales (void)
99 {
100 /* get collation locale, if available */
101 #ifdef HAVE_SETLOCALE
102 deflocale_coll = strsave(get_current_locale(LC_COLLATE));
103 current_coll = strsave(deflocale_coll);
104 #endif /* HAVE_SETLOCALE */
105
106 /* get messages locale (via locale or via environ.) */
107 #ifdef HAVE_SETLOCALE
108 #ifdef LC_MESSAGES
109 if (LC_MESSAGES >= 0 && LC_MESSAGES != 1729) {
110 /* 1729 is the gettext code when there wasn't any LC_MESSAGES */
111 deflocale_msgs = strsave(get_current_locale(LC_MESSAGES));
112 current_msgs = strsave(deflocale_msgs);
113 }
114 #endif /* LC_MESSAGES */
115 #endif /* HAVE_SETLOCALE */
116 /* fallback to the environment (see setmsgs) */
117 if (!deflocale_msgs) {
118 STRING msgs = getenv("LC_MESSAGES");
119 deflocale_msgs = strsave(msgs ? msgs : "");
120 current_msgs = strsave(deflocale_msgs);
121 }
122
123 }
124 /*==========================================
125 * get_original_locale_collate -- Get collation locale captured at startup
126 * caller may not alter string
127 * Created: 2002/06/15 (Perry Rapp)
128 *========================================*/
129 STRING
get_original_locale_collate(void)130 get_original_locale_collate (void)
131 {
132 #ifdef HAVE_SETLOCALE
133 return deflocale_coll;
134 #endif /* HAVE_SETLOCALE */
135 return "";
136 }
137 /*==========================================
138 * get_current_locale_collate -- Get collation locale (as last set)
139 * caller may not alter string
140 * Created: 2002/06/15 (Perry Rapp)
141 *========================================*/
142 STRING
get_current_locale_collate(void)143 get_current_locale_collate (void)
144 {
145 return current_coll;
146 }
147 /*==========================================
148 * get_original_locale_msgs -- Get LC_MESSAGES locale captured at startup
149 * caller may not alter string
150 * Created: 2002/06/15 (Perry Rapp)
151 *========================================*/
152 STRING
get_original_locale_msgs(void)153 get_original_locale_msgs (void)
154 {
155 return deflocale_msgs;
156 }
157 /*==========================================
158 * get_current_locale_msgs -- Get LC_MESSAGES locale (as last set)
159 * caller may not alter string
160 * Created: 2002/06/15 (Perry Rapp)
161 *========================================*/
162 STRING
get_current_locale_msgs(void)163 get_current_locale_msgs (void)
164 {
165 return current_msgs;
166 }
167 /*==========================================
168 * are_locales_supported -- locale support compiled in ?
169 * Created: 2002/06/15 (Perry Rapp)
170 *========================================*/
171 BOOLEAN
are_locales_supported(void)172 are_locales_supported (void)
173 {
174 #ifdef HAVE_SETLOCALE
175 return TRUE;
176 #endif /* HAVE_SETLOCALE */
177 return FALSE;
178 }
179 /*==========================================
180 * is_nls_supported -- is NLS (National Language Support) compiled in ?
181 * Created: 2002/06/15 (Perry Rapp)
182 *========================================*/
183 BOOLEAN
is_nls_supported(void)184 is_nls_supported (void)
185 {
186 #ifdef ENABLE_NLS
187 return TRUE;
188 #endif /* ENABLE_NLS */
189 return FALSE;
190 }
191 /*==========================================
192 * is_iconv_supported -- is iconv (codeset conversion library) compiled in ?
193 * Created: 2002/06/15 (Perry Rapp)
194 *========================================*/
195 BOOLEAN
is_iconv_supported(void)196 is_iconv_supported (void)
197 {
198 #ifdef HAVE_ICONV
199 return TRUE;
200 #endif /* HAVE_ICONV */
201 return FALSE;
202 }
203 /*==========================================
204 * ll_langinfo -- wrapper for nl_langinfo
205 * in case not provided (eg, MS-Windows)
206 *========================================*/
207 STRING
ll_langinfo(void)208 ll_langinfo (void)
209 {
210 STRING str = nl_langinfo(CODESET);
211 /* TODO: Should we apply norm_charmap.c ?
212 http://www.cl.cam.ac.uk/~mgk25/ucs/norm_charmap.c
213 */
214 /* TODO: In any case tho, Markus' nice replacement nl_langinfo gives the
215 wrong default codepages for MS-Windows I think -- eg, should be 1252 for
216 generic default instead of 8859-1 */
217
218 /* TODO: Check out libcharset (in the libiconv distribution)
219 It probably has the Win32 code in it */
220
221 return str ? str : ""; /* I don't know if nl_langinfo ever returns NULL */
222 }
223 /*==========================================
224 * termlocale -- free locale related variables
225 * Created: 2002/02/24 (Perry Rapp)
226 *========================================*/
227 void
termlocale(void)228 termlocale (void)
229 {
230 /* free & zero out globals */
231 strfree(&deflocale_coll);
232 strfree(¤t_coll);
233 strfree(&deflocale_msgs);
234 strfree(¤t_msgs);
235 }
236 /*==========================================
237 * uilocale -- set locale to GUI locale
238 * (eg, for displaying a sorted list of people)
239 * Created: 2001/08/02 (Perry Rapp)
240 *========================================*/
241 void
uilocale(void)242 uilocale (void)
243 {
244 update_textdomain_localedir(PACKAGE, "Ui");
245
246 customlocale("UiLocale");
247 }
248 /*==========================================
249 * rptlocale -- set locale to report locale
250 * (eg, for _namesort)
251 * Created: 2001/08/02 (Perry Rapp)
252 *========================================*/
253 void
rptlocale(void)254 rptlocale (void)
255 {
256 /* 2007-04-19, Perry:
257 I'm not sure what textdomain to use here
258 rptinfo has a textdomain (see llrpt_gettext)
259 but that is per-rptinfo
260 */
261
262 customlocale("RptLocale");
263 if (rptlocalestr) /* report has specified locale */
264 llsetlocale(LC_ALL, rptlocalestr);
265 }
266 /*==========================================
267 * rpt_setlocale -- set report locale to custom locale
268 * used by report language
269 * Created: 2002/06/27 (Perry Rapp)
270 *========================================*/
271 STRING
rpt_setlocale(STRING str)272 rpt_setlocale (STRING str)
273 {
274 strfree(&rptlocalestr);
275 rptlocalestr = llsetlocale(LC_ALL, str);
276 if (rptlocalestr)
277 rptlocalestr = strsave(rptlocalestr);
278 return rptlocalestr;
279 }
280 /*==========================================
281 * setmsgs -- set locale for LC_MESSAGES
282 * Returns non-null string if succeeds
283 *========================================*/
284 #ifdef ENABLE_NLS
285 static STRING
setmsgs(STRING localename)286 setmsgs (STRING localename)
287 {
288 STRING str;
289 if (eqstr_ex(current_msgs, localename))
290 return localename; /* skip it if already current */
291 #if defined(HAVE_SETLOCALE) && defined(HAVE_LC_MESSAGES)
292 str = setlocale(LC_MESSAGES, localename);
293 if (str) {
294 strfree(¤t_msgs);
295 current_msgs = strsave(str);
296 }
297 #else
298 str = llsetenv("LC_MESSAGES", localename);
299 if (str) {
300 strfree(¤t_msgs);
301 current_msgs = strsave(str);
302 }
303 #endif
304 if (str) {
305 notify_gettext_language_changed();
306 send_uilang_callbacks();
307 date_update_lang();
308 }
309 return str;
310 }
311 #endif /* ENABLE_NLS */
312 #ifdef ENABLE_NLS
313 #if ! ( defined(HAVE_SETLOCALE) && defined(HAVE_LC_MESSAGES) )
314 /*==========================================
315 * llsetenv -- assign a value to an environment variable
316 * Workaround for systems without HAVE_SETLOCALE && HAVE_LC_MESSAGES
317 * Returns value if it succeeded
318 *========================================*/
319 static STRING
llsetenv(STRING name,STRING value)320 llsetenv (STRING name, STRING value)
321 {
322 char buffer[128];
323 STRING str = 0;
324
325 buffer[0] = 0;
326 llstrappf(buffer, sizeof(buffer), uu8, "%s=%s", name, value);
327
328 #ifdef HAVE_SETENV
329 if (setenv(name, value, 1) != -1)
330 str = value;
331 #else
332 #ifdef HAVE_PUTENV
333 if (putenv(buffer) != -1)
334 str = value;
335 #else
336 #ifdef HAVE__PUTENV
337 if (_putenv(buffer) != -1)
338 str = value;
339 #endif /* HAVE__PUTENV */
340 #endif /* HAVE_PUTENV */
341 #endif /* HAVE_SETENV */
342 return str;
343 }
344 #endif /* !defined(HAVE_SETLOCALE) && !defined(HAVE_LC_MESSAGES) */
345 #endif /* ENABLE_NLS */
346 /*==========================================
347 * customlocale -- set locale to custom setting
348 * depending on user options
349 * prefix: [IN] option prefix (eg, "UiLocale")
350 * Created: 2002/02/24 (Perry Rapp)
351 *========================================*/
352 static void
customlocale(STRING prefix)353 customlocale (STRING prefix)
354 {
355 char option[64];
356 STRING str;
357 INT prefixlen = strlen(prefix);
358
359 if (prefixlen > 30) return;
360
361 strcpy(option, prefix);
362
363 #ifdef HAVE_SETLOCALE
364 /* did user set, eg, UiLocaleCollate option ? */
365 strcpy(option+prefixlen, "Collate");
366 str = getlloptstr(option, 0);
367 if (str) {
368 customized_loc = TRUE;
369 str = llsetlocale(LC_COLLATE, str);
370 }
371 if (!str) {
372 /* did user set, eg, UiLocale option ? */
373 option[prefixlen] = 0;
374 str = getlloptstr(option, 0);
375 if (str) {
376 customized_loc = TRUE;
377 str = llsetlocale(LC_COLLATE, str);
378 }
379 /* nothing set, so try to revert to startup value */
380 if (!str && customized_loc)
381 llsetlocale(LC_COLLATE, deflocale_coll);
382 }
383 #endif /* HAVE_SETLOCALE */
384
385 #ifdef ENABLE_NLS
386 /* did user set, eg, UiLocaleMessages option ? */
387 strcpy(option+prefixlen, "Messages");
388 str = getlloptstr(option, 0);
389 if (str) {
390 customized_msgs = TRUE;
391 str = setmsgs(str);
392 } else {
393 /* did user set, eg, UiLocale option ? */
394 option[prefixlen] = 0;
395 str = getlloptstr(option, 0);
396 if (str) {
397 customized_msgs = TRUE;
398 str = setmsgs(str);
399 }
400 if (!str && customized_msgs)
401 setmsgs(deflocale_msgs ? deflocale_msgs : "");
402 }
403 #endif /* ENABLE_NLS */
404 }
405 /*==========================================
406 * notify_gettext_language_changed --
407 * signal gettext that desired language has changed
408 * Created: 2002/06/15 (Perry Rapp)
409 *========================================*/
410 static void
notify_gettext_language_changed(void)411 notify_gettext_language_changed (void)
412 {
413 #if ENABLE_NLS
414 #if WIN32_INTL_SHIM
415 gt_notify_language_change();
416 #else
417 extern int _nl_msg_cat_cntr;
418 ++_nl_msg_cat_cntr;
419 #endif
420 #endif
421 }
422 /*==========================================
423 * llsetlocale -- wrapper for setlocale
424 * Handle MS-Windows annoying lack of LC_MESSAGES
425 * TODO: clean up other translat.c functions by calling this
426 *========================================*/
427 char *
llsetlocale(int category,char * locale)428 llsetlocale (int category, char * locale)
429 {
430 char * rtn = "C";
431 #ifdef HAVE_SETLOCALE
432 rtn = setlocale(category, locale);
433 if (!rtn && locale)
434 rtn = win32_setlocale(category, locale);
435 #endif /* HAVE_SETLOCALE */
436 #ifdef ENABLE_NLS
437 if (rtn && is_msgcategory(category)) {
438 setmsgs(locale);
439 }
440 #endif /* ENABLE_NLS */
441 return rtn;
442 }
443 /*==========================================
444 * is_msgcategory -- check for LC_ALL or LC_MESSAGES
445 *========================================*/
446 #ifdef ENABLE_NLS
447 static BOOLEAN
is_msgcategory(int category)448 is_msgcategory (int category)
449 {
450 #ifdef LC_MESSAGES
451 return category==LC_ALL || category==LC_MESSAGES;
452 #else
453 return category==LC_ALL;
454 #endif
455 }
456 #endif /* ENABLE_NLS */
457 /*==========================================
458 * win32_setlocale -- handle MS-Windows goofed up locale names
459 *========================================*/
460 static char *
win32_setlocale(int category,char * locale)461 win32_setlocale (int category, char * locale)
462 {
463 char * rtn = 0;
464 #if defined(_WIN32) && !defined(__CYGWIN__)
465 /* TODO: Obviously this needs work -- and move to win32 subdir ? */
466 if (locale) {
467 char w32loc[30]="";
468 char * ptr;
469 int i;
470 for (i=0; langs[i]; i += 2) {
471 if (eqstrn(locale, langs[i], 2)) {
472 llstrapps(w32loc, sizeof(w32loc), uu8, langs[i+1]);
473 break;
474 }
475 }
476 if (!langs[i])
477 return 0;
478 ptr = locale+strlen(langs[i]);
479 if (ptr[0]=='_') {
480 llstrappc(w32loc, sizeof(w32loc), ptr[0]);
481 for (i=0; countries[i]; i += 2) {
482 if (eqstrn(ptr+1, countries[i], 2)) {
483 llstrapps(w32loc, sizeof(w32loc), uu8, countries[i+1]);
484 break;
485 }
486 }
487 }
488 /* TODO: strip off codeset, because we don't want user's codeset anyway,
489 at least unless int_codeset == 0 */
490 rtn = setlocale(category, w32loc);
491 }
492 #else
493 category=category; /* unused */
494 locale=locale; /* unused */
495 #endif /* _WIN32 */
496 return rtn;
497 }
498 /*==========================================
499 * register_uilang_callback --
500 *========================================*/
501 void
register_uilang_callback(CALLBACK_FNC fncptr,VPTR uparm)502 register_uilang_callback (CALLBACK_FNC fncptr, VPTR uparm)
503 {
504 add_listener(&f_uilang_callbacks, fncptr, uparm);
505 }
506 /*==========================================
507 * unregister_uilang_callback --
508 *========================================*/
509 void
unregister_uilang_callback(CALLBACK_FNC fncptr,VPTR uparm)510 unregister_uilang_callback (CALLBACK_FNC fncptr, VPTR uparm)
511 {
512 delete_listener(&f_uilang_callbacks, fncptr, uparm);
513 }
514 /*==========================================
515 * send_uilang_callbacks --
516 *========================================*/
517 #ifdef ENABLE_NLS
518 static void
send_uilang_callbacks(void)519 send_uilang_callbacks (void)
520 {
521 notify_listeners(&f_uilang_callbacks);
522 }
523 #endif /* ENABLE_NLS */
524 /*==========================================
525 * register_uicodeset_callback --
526 *========================================*/
527 void
register_uicodeset_callback(CALLBACK_FNC fncptr,VPTR uparm)528 register_uicodeset_callback (CALLBACK_FNC fncptr, VPTR uparm)
529 {
530 add_listener(&f_uicodeset_callbacks, fncptr, uparm);
531 }
532 /*==========================================
533 * unregister_uicodeset_callback --
534 *========================================*/
535 void
unregister_uicodeset_callback(CALLBACK_FNC fncptr,VPTR uparm)536 unregister_uicodeset_callback (CALLBACK_FNC fncptr, VPTR uparm)
537 {
538 delete_listener(&f_uicodeset_callbacks, fncptr, uparm);
539 }
540 /*==========================================
541 * locales_notify_uicodeset_changes --
542 *========================================*/
543 void
locales_notify_uicodeset_changes(void)544 locales_notify_uicodeset_changes (void)
545 {
546 notify_listeners(&f_uicodeset_callbacks);
547 }
548 /*==========================================
549 * locales_notify_language_change -- notify gettext of change
550 *========================================*/
551 void
locales_notify_language_change(void)552 locales_notify_language_change (void)
553 {
554 notify_gettext_language_changed();
555 }
556
557