1 #include "e.h"
2
3 static Ecore_Exe *_e_intl_input_method_exec = NULL;
4 static Ecore_Event_Handler *_e_intl_exit_handler = NULL;
5
6 static char *_e_intl_orig_lang = NULL;
7 static char *_e_intl_orig_language = NULL;
8 static char *_e_intl_language = NULL;
9
10 static char *_e_intl_language_alias = NULL;
11
12 static char *_e_intl_orig_xmodifiers = NULL;
13 static char *_e_intl_orig_qt_im_module = NULL;
14 static char *_e_intl_orig_gtk_im_module = NULL;
15 static char *_e_intl_orig_ecore_imf_module = NULL;
16
17 static const char *_e_intl_imc_personal_path = NULL;
18 static const char *_e_intl_imc_system_path = NULL;
19
20 #define E_EXE_STOP(EXE) if (EXE) { ecore_exe_terminate(EXE); ecore_exe_free(EXE); EXE = NULL; }
21 #define E_EXE_IS_VALID(EXE) (!((!EXE) || (EXE[0] == 0)))
22
23 /* All locale parts */
24 #define E_INTL_LOC_ALL E_INTL_LOC_LANG | \
25 E_INTL_LOC_REGION | \
26 E_INTL_LOC_CODESET | \
27 E_INTL_LOC_MODIFIER
28
29 /* Locale parts which are significant when Validating */
30 #define E_INTL_LOC_SIGNIFICANT E_INTL_LOC_LANG | \
31 E_INTL_LOC_REGION | \
32 E_INTL_LOC_CODESET
33
34 /* Language Setting and Listing */
35 static char *_e_intl_language_path_find(char *language);
36 static Eina_List *_e_intl_language_dir_scan(const char *dir);
37 static int _e_intl_language_list_find(Eina_List *language_list, char *language);
38
39 /* Locale Validation and Discovery */
40 static Eina_Hash *_e_intl_locale_alias_hash_get(void);
41 static char *_e_intl_locale_alias_get(const char *language);
42 static Eina_List *_e_intl_locale_system_locales_get(void);
43 static Eina_List *_e_intl_locale_search_order_get(const char *locale);
44 static int _e_intl_locale_validate(const char *locale);
45 static void _e_intl_locale_hash_free(Eina_Hash *language_hash);
46 static Eina_Bool _e_intl_locale_hash_free_cb(const Eina_Hash *hash, const void *key, void *data, void *fdata);
47
48 /* Input Method Configuration and Management */
49 static Eina_Bool _e_intl_cb_exit(void *data, int type, void *event);
50 static Eina_List *_e_intl_imc_dir_scan(const char *dir);
51
52 EINTERN int
e_intl_init(void)53 e_intl_init(void)
54 {
55 char *s;
56
57 e_intl_data_init();
58
59 if ((s = getenv("LANG"))) _e_intl_orig_lang = strdup(s);
60 if ((s = getenv("LANGUAGE"))) _e_intl_orig_language = strdup(s);
61
62 if ((s = getenv("GTK_IM_MODULE"))) _e_intl_orig_gtk_im_module = strdup(s);
63 if ((s = getenv("QT_IM_MODULE"))) _e_intl_orig_qt_im_module = strdup(s);
64 if ((s = getenv("XMODIFIERS"))) _e_intl_orig_xmodifiers = strdup(s);
65 if ((s = getenv("ECORE_IMF_MODULE"))) _e_intl_orig_ecore_imf_module = strdup(s);
66
67 return 1;
68 }
69
70 EINTERN int
e_intl_shutdown(void)71 e_intl_shutdown(void)
72 {
73 E_FREE(_e_intl_language);
74 E_FREE(_e_intl_orig_lang);
75 E_FREE(_e_intl_orig_language);
76
77 E_FREE(_e_intl_orig_gtk_im_module);
78 E_FREE(_e_intl_orig_qt_im_module);
79 E_FREE(_e_intl_orig_xmodifiers);
80 E_FREE(_e_intl_orig_ecore_imf_module);
81
82 if (_e_intl_imc_personal_path)
83 eina_stringshare_del(_e_intl_imc_personal_path);
84 if (_e_intl_imc_system_path)
85 eina_stringshare_del(_e_intl_imc_system_path);
86
87 e_intl_data_shutdown();
88
89 return 1;
90 }
91
92 /* Setup configuration settings and start services */
93 EINTERN int
e_intl_post_init(void)94 e_intl_post_init(void)
95 {
96 if ((e_config->language) && (e_config->language[0] != 0))
97 e_intl_language_set(e_config->language);
98 else
99 e_intl_language_set(NULL);
100
101 if ((e_config->input_method) && (e_config->input_method[0] != 0))
102 e_intl_input_method_set(e_config->input_method);
103
104 _e_intl_exit_handler = ecore_event_handler_add(ECORE_EXE_EVENT_DEL,
105 _e_intl_cb_exit, NULL);
106 return 1;
107 }
108
109 EINTERN int
e_intl_post_shutdown(void)110 e_intl_post_shutdown(void)
111 {
112 if (_e_intl_exit_handler)
113 {
114 ecore_event_handler_del(_e_intl_exit_handler);
115 _e_intl_exit_handler = NULL;
116 }
117
118 e_intl_input_method_set(NULL);
119
120 e_intl_language_set(NULL);
121 E_FREE(_e_intl_language_alias);
122
123 E_EXE_STOP(_e_intl_input_method_exec);
124 return 1;
125 }
126
127 /*
128 * TODO
129 * - Add error dialogs explaining any errors while setting the locale
130 * * Locale aliases need to be configured
131 * * Locale is invalid
132 * * Message files are not found for this locale, then we have (en_US, POSIX, C)
133 * - Add support of compound locales i.e. (en_US;zh_CN;C) ==Defer==
134 * - Add Configuration for to-be-set environment variables
135 */
136 E_API void
e_intl_language_set(const char * lang)137 e_intl_language_set(const char *lang)
138 {
139 int set_envars;
140 int ok;
141
142 set_envars = 1;
143 /* NULL lang means set everything back to the original environment
144 * defaults
145 */
146 if (!lang)
147 {
148 e_util_env_set("LANG", _e_intl_orig_lang);
149
150 if (!lang) lang = getenv("LC_ALL");
151 if (!lang) lang = getenv("LANG");
152
153 set_envars = 0;
154 }
155
156 E_FREE(_e_intl_language_alias);
157 _e_intl_language_alias = _e_intl_locale_alias_get(lang);
158 E_FREE(_e_intl_language);
159
160 if (lang)
161 _e_intl_language = strdup(lang);
162 else
163 _e_intl_language = NULL;
164
165 ok = 1;
166 if (strcmp(_e_intl_language_alias, "C"))
167 {
168 ok = _e_intl_locale_validate(_e_intl_language_alias);
169 if (!ok)
170 {
171 char *p, *new_lang;
172
173 new_lang = _e_intl_language_alias;
174 p = strchr(new_lang, '.');
175 if (p) *p = 0;
176 _e_intl_language_alias = strdup(new_lang);
177 E_FREE(new_lang);
178 ok = _e_intl_locale_validate(_e_intl_language_alias);
179 }
180 }
181 if (!ok)
182 {
183 fprintf(stderr, "The locale '%s' cannot be found on your "
184 "system. Please install this locale or try "
185 "something else.", _e_intl_language_alias);
186 return;
187 }
188 /* Only set env vars is a non NULL locale was passed */
189 if (set_envars)
190 {
191 e_util_env_set("LANG", lang);
192 /* Unset LANGUAGE, apparently causes issues if set */
193 e_util_env_set("LANGUAGE", NULL);
194 efreet_lang_reset();
195 setlocale(LC_ALL, lang);
196 }
197 else
198 {
199 setlocale(LC_ALL, "");
200 }
201
202 if (_e_intl_language)
203 {
204 char *locale_path;
205
206 locale_path = _e_intl_language_path_find(_e_intl_language_alias);
207 if (!locale_path)
208 {
209 E_Locale_Parts *locale_parts;
210
211 locale_parts = e_intl_locale_parts_get(_e_intl_language_alias);
212
213 /* If locale is C or some form of en don't report an error */
214 if ((!locale_parts) &&
215 (strcmp(_e_intl_language_alias, "C")))
216 {
217 fprintf(stderr,
218 "An error occurred setting your locale. \n\n"
219
220 "The locale you have chosen '%s' appears to \n"
221 "be an alias, however, it can not be resolved.\n"
222 "Please make sure you have a 'locale.alias' \n"
223 "file in your 'messages' path which can resolve\n"
224 "this alias.\n\n"
225
226 "Enlightenment will not be translated.\n",
227 _e_intl_language_alias);
228 }
229 else if ((locale_parts) && (locale_parts->lang) &&
230 (strcmp(locale_parts->lang, "en")))
231 {
232 fprintf(stderr,
233 "An error occurred setting your locale. \n\n"
234
235 "The translation files for the locale you \n"
236 "have chosen (%s) cannot be found in your \n"
237 "'messages' path. \n\n"
238
239 "Enlightenment will not be translated.\n",
240 _e_intl_language_alias);
241 }
242 e_intl_locale_parts_free(locale_parts);
243 }
244 else
245 {
246 #ifdef HAVE_GETTEXT
247 bindtextdomain(PACKAGE, locale_path);
248 textdomain(PACKAGE);
249 bind_textdomain_codeset(PACKAGE, "UTF-8");
250 #endif
251 free(locale_path);
252 }
253 }
254 }
255
256 E_API const char *
e_intl_language_get(void)257 e_intl_language_get(void)
258 {
259 return _e_intl_language;
260 }
261
262 E_API const char *
e_intl_language_alias_get(void)263 e_intl_language_alias_get(void)
264 {
265 return _e_intl_language_alias;
266 }
267
268 E_API Eina_List *
e_intl_language_list(void)269 e_intl_language_list(void)
270 {
271 Eina_List *next;
272 Eina_List *dir_list;
273 Eina_List *all_languages;
274 E_Path_Dir *epd;
275
276 all_languages = NULL;
277 dir_list = e_path_dir_list_get(path_messages);
278 EINA_LIST_FOREACH(dir_list, next, epd)
279 {
280 Eina_List *dir_languages;
281 char *language;
282
283 dir_languages = _e_intl_language_dir_scan(epd->dir);
284
285 EINA_LIST_FREE(dir_languages, language)
286 if ((_e_intl_language_list_find(all_languages, language)) ||
287 ((strlen(language) > 2) && (!_e_intl_locale_validate(language))))
288 {
289 free(language);
290 }
291 else
292 all_languages = eina_list_append(all_languages, language);
293 }
294
295 e_path_dir_list_free(dir_list);
296
297 return all_languages;
298 }
299
300 static int
_e_intl_language_list_find(Eina_List * language_list,char * language)301 _e_intl_language_list_find(Eina_List *language_list, char *language)
302 {
303 Eina_List *l;
304 char *lang;
305
306 if ((!language_list) || (!language)) return 0;
307
308 EINA_LIST_FOREACH(language_list, l, lang)
309 if (!strcmp(lang, language)) return 1;
310
311 return 0;
312 }
313
314 E_API void
e_intl_input_method_set(const char * imc_path)315 e_intl_input_method_set(const char *imc_path)
316 {
317 if (!imc_path)
318 {
319 E_EXE_STOP(_e_intl_input_method_exec);
320 e_util_env_set("GTK_IM_MODULE", _e_intl_orig_gtk_im_module);
321 e_util_env_set("QT_IM_MODULE", _e_intl_orig_qt_im_module);
322 e_util_env_set("XMODIFIERS", _e_intl_orig_xmodifiers);
323 e_util_env_set("ECORE_IMF_MODULE", _e_intl_orig_ecore_imf_module);
324 }
325
326 if (imc_path)
327 {
328 Eet_File *imc_ef;
329 E_Input_Method_Config *imc;
330
331 imc_ef = eet_open(imc_path, EET_FILE_MODE_READ);
332 if (imc_ef)
333 {
334 imc = e_intl_input_method_config_read(imc_ef);
335 eet_close(imc_ef);
336
337 if (imc)
338 {
339 e_util_env_set("GTK_IM_MODULE", imc->gtk_im_module);
340 e_util_env_set("QT_IM_MODULE", imc->qt_im_module);
341 e_util_env_set("XMODIFIERS", imc->xmodifiers);
342 e_util_env_set("ECORE_IMF_MODULE", imc->ecore_imf_module);
343
344 E_EXE_STOP(_e_intl_input_method_exec);
345
346 if (E_EXE_IS_VALID(imc->e_im_exec))
347 {
348 // if you see valgrind complain about memory
349 // definitely lost here... it's wrong.
350 _e_intl_input_method_exec = e_util_exe_safe_run
351 (imc->e_im_exec, NULL);
352 ecore_exe_tag_set(_e_intl_input_method_exec, "E/im_exec");
353
354 if ((!_e_intl_input_method_exec) ||
355 (!ecore_exe_pid_get(_e_intl_input_method_exec)))
356 e_util_dialog_show(_("Input Method Error"),
357 _("Error starting the input method executable<ps/><ps/>"
358 "please make sure that your input<ps/>"
359 "method configuration is correct and<ps/>"
360 "that your configuration's<ps/>"
361 "executable is in your PATH<ps/>"));
362 }
363 e_intl_input_method_config_free(imc);
364 }
365 }
366 }
367 }
368
369 E_API Eina_List *
e_intl_input_method_list(void)370 e_intl_input_method_list(void)
371 {
372 Eina_List *input_methods;
373 Eina_List *im_list;
374
375 im_list = NULL;
376
377 /* Personal Path */
378 im_list = _e_intl_imc_dir_scan(e_intl_imc_personal_path_get());
379
380 /* System Path */
381 input_methods = _e_intl_imc_dir_scan(e_intl_imc_system_path_get());
382 im_list = eina_list_merge(im_list, input_methods);
383
384 return im_list;
385 }
386
387 const char *
e_intl_imc_personal_path_get(void)388 e_intl_imc_personal_path_get(void)
389 {
390 if (!_e_intl_imc_personal_path)
391 {
392 char buf[4096];
393
394 e_user_dir_concat_static(buf, "input_methods");
395 _e_intl_imc_personal_path = eina_stringshare_add(buf);
396 }
397 return _e_intl_imc_personal_path;
398 }
399
400 const char *
e_intl_imc_system_path_get(void)401 e_intl_imc_system_path_get(void)
402 {
403 if (!_e_intl_imc_system_path)
404 {
405 char buf[4096];
406
407 e_prefix_data_concat_static(buf, "data/input_methods");
408 _e_intl_imc_system_path = eina_stringshare_add(buf);
409 }
410 return _e_intl_imc_system_path;
411 }
412
413 static Eina_Bool
_e_intl_cb_exit(void * data EINA_UNUSED,int type EINA_UNUSED,void * event)414 _e_intl_cb_exit(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
415 {
416 Ecore_Exe_Event_Del *ev;
417
418 ev = event;
419 if (!ev->exe) return ECORE_CALLBACK_PASS_ON;
420 if (ev->exe == _e_intl_input_method_exec)
421 _e_intl_input_method_exec = NULL;
422 return ECORE_CALLBACK_PASS_ON;
423 }
424
425 static void
_e_intl_locale_hash_free(Eina_Hash * locale_hash)426 _e_intl_locale_hash_free(Eina_Hash *locale_hash)
427 {
428 if (!locale_hash) return;
429 eina_hash_foreach(locale_hash, _e_intl_locale_hash_free_cb, NULL);
430 eina_hash_free(locale_hash);
431 }
432
433 static Eina_Bool
_e_intl_locale_hash_free_cb(const Eina_Hash * hash EINA_UNUSED,const void * key EINA_UNUSED,void * data,void * fdata EINA_UNUSED)434 _e_intl_locale_hash_free_cb(const Eina_Hash *hash EINA_UNUSED, const void *key EINA_UNUSED, void *data, void *fdata EINA_UNUSED)
435 {
436 free(data);
437 return 1;
438 }
439
440 /*
441 * get the directory associated with the language. Language Must be valid alias
442 * i.e. Already validated and already de-aliased.
443 *
444 * NULL means:
445 * 1) The user does not have an enlightenment translation for this locale
446 * 2) The user does not have their locale.aliases configured correctly
447 *
448 * @return NULL if not found.
449 */
450 static char *
_e_intl_language_path_find(char * language)451 _e_intl_language_path_find(char *language)
452 {
453 char *directory;
454 char *data;
455 Eina_List *dir_list;
456 Eina_List *search_list;
457 Eina_List *next_dir;
458 Eina_List *next_search;
459 E_Path_Dir *epd;
460 char *search_locale;
461 int found;
462
463 search_list = _e_intl_locale_search_order_get(language);
464
465 if (!search_list) return NULL;
466
467 directory = NULL;
468 found = 0;
469 dir_list = e_path_dir_list_get(path_messages);
470
471 /* For each directory in the path */
472 EINA_LIST_FOREACH(dir_list, next_dir, epd)
473 {
474 if (found) break;
475
476 /* Match canonicalized locale against each possible search */
477 EINA_LIST_FOREACH(search_list, next_search, search_locale)
478 {
479 char message_path[PATH_MAX];
480
481 snprintf(message_path, sizeof(message_path), "%s/%s/LC_MESSAGES/%s.mo", epd->dir, search_locale, PACKAGE);
482
483 if ((ecore_file_exists(message_path)) && (!ecore_file_is_dir(message_path)))
484 {
485 directory = strdup(epd->dir);
486 found = 1;
487 break;
488 }
489 }
490 }
491
492 e_path_dir_list_free(dir_list);
493
494 EINA_LIST_FREE(search_list, data)
495 free(data);
496
497 return directory;
498 }
499
500 static Eina_List *
_e_intl_language_dir_scan(const char * dir)501 _e_intl_language_dir_scan(const char *dir)
502 {
503 Eina_List *languages;
504 Eina_List *files;
505 char *file;
506
507 languages = NULL;
508
509 files = ecore_file_ls(dir);
510 if (!files) return NULL;
511
512 if (files)
513 {
514 EINA_LIST_FREE(files, file)
515 {
516 char file_path[PATH_MAX];
517
518 snprintf(file_path, sizeof(file_path), "%s/%s/LC_MESSAGES/%s.mo",
519 dir, file, PACKAGE);
520 if (ecore_file_exists(file_path) && !ecore_file_is_dir(file_path))
521 languages = eina_list_append(languages, file);
522 else
523 free(file);
524 }
525 }
526 return languages;
527 }
528
529 /* get the alias for a locale
530 *
531 * return pointer to allocated alias string. never returns NULL
532 * String will be the same if its a valid locale already or there
533 * is no alias.
534 */
535 static char *
_e_intl_locale_alias_get(const char * language)536 _e_intl_locale_alias_get(const char *language)
537 {
538 Eina_Hash *alias_hash;
539 char *alias;
540 char lbuf[256];
541 char *lower_language = lbuf;
542
543 if ((!language) || (!strncmp(language, "POSIX", strlen("POSIX"))))
544 return strdup("C");
545
546 alias_hash = _e_intl_locale_alias_hash_get();
547 if (!alias_hash) /* No alias file available */
548 return strdup(language);
549
550 strncpy(lbuf, language, sizeof(lbuf) - 1);
551 lbuf[sizeof(lbuf) - 1] = '\0';
552 eina_str_tolower(&lower_language);
553 alias = eina_hash_find(alias_hash, lower_language);
554
555 if (alias)
556 alias = strdup(alias);
557 else
558 alias = strdup(language);
559
560 _e_intl_locale_hash_free(alias_hash);
561
562 return alias;
563 }
564
565 static Eina_Hash *
_e_intl_locale_alias_hash_get(void)566 _e_intl_locale_alias_hash_get(void)
567 {
568 Eina_List *next;
569 Eina_List *dir_list;
570 E_Path_Dir *epd;
571 Eina_Hash *alias_hash;
572
573 dir_list = e_path_dir_list_get(path_messages);
574 alias_hash = NULL;
575
576 EINA_LIST_FOREACH(dir_list, next, epd)
577 {
578 char buf[4096];
579 FILE *f;
580
581 snprintf(buf, sizeof(buf), "%s/locale.alias", epd->dir);
582 f = fopen(buf, "r");
583 if (f)
584 {
585 char alias[4096], locale[4096];
586
587 /* read locale alias lines */
588 while (fscanf(f, "%4095s %4095[^\n]\n", alias, locale) == 2)
589 {
590 /* skip comments */
591 if ((alias[0] == '!') || (alias[0] == '#'))
592 continue;
593
594 /* skip dupes */
595 if (eina_hash_find(alias_hash, alias))
596 continue;
597
598 if (!alias_hash) alias_hash = eina_hash_string_superfast_new(NULL);
599 eina_hash_add(alias_hash, alias, strdup(locale));
600 }
601 fclose(f);
602 }
603 }
604 e_path_dir_list_free(dir_list);
605
606 return alias_hash;
607 }
608
609 /* return parts of the locale that are requested in the mask
610 * return null if the locale looks to be invalid (Does not have
611 * ll_DD)
612 *
613 * the returned string needs to be freed
614 */
615 /*
616 * Not canonic, just gets the parts
617 */
618 E_API E_Locale_Parts *
e_intl_locale_parts_get(const char * locale)619 e_intl_locale_parts_get(const char *locale)
620 {
621 /* Parse Results */
622 E_Locale_Parts *locale_parts;
623 char language[4] = {0};
624 char territory[4] = {0};
625 char codeset[32] = {0};
626 char modifier[32] = {0};
627
628 /* Parse State */
629 int state = 0; /* start out looking for the language */
630 unsigned int locale_idx;
631 int tmp_idx = 0;
632
633 /* Parse Loop - Separators are _ . @ */
634 for (locale_idx = 0; locale_idx < strlen(locale); locale_idx++)
635 {
636 char locale_char;
637 locale_char = locale[locale_idx];
638
639 /* we have finished scanning the locale string */
640 if (!locale_char)
641 break;
642
643 /* scan this character based on the current start */
644 switch (state)
645 {
646 case 0: /* Gathering Language */
647 if (tmp_idx == 2 && locale_char == '_')
648 {
649 state++;
650 language[tmp_idx] = 0;
651 tmp_idx = 0;
652 }
653 else if ((tmp_idx < 2) && (islower(locale_char)))
654 language[tmp_idx++] = locale_char;
655 else if (locale_char == '.') // no territory
656 {
657 state = 2;
658 language[tmp_idx] = 0;
659 tmp_idx = 0;
660 }
661 else
662 return NULL;
663 break;
664
665 case 1: /* Gathering Territory */
666 if (tmp_idx == 2 && locale_char == '.')
667 {
668 state++;
669 territory[tmp_idx] = 0;
670 tmp_idx = 0;
671 }
672 else if ((tmp_idx == 2) && (locale_char == '@'))
673 {
674 state += 2;
675 territory[tmp_idx] = 0;
676 codeset[0] = 0;
677 tmp_idx = 0;
678 }
679 else if ((tmp_idx < 2) && isupper(locale_char))
680 territory[tmp_idx++] = locale_char;
681 else
682 return NULL;
683 break;
684
685 case 2: /* Gathering Codeset */
686 if (locale_char == '@')
687 {
688 state++;
689 codeset[tmp_idx] = 0;
690 tmp_idx = 0;
691 }
692 else if (tmp_idx < 31)
693 codeset[tmp_idx++] = locale_char;
694 else
695 return NULL;
696 break;
697
698 case 3: /* Gathering modifier */
699 if (tmp_idx < 31)
700 modifier[tmp_idx++] = locale_char;
701 else
702 return NULL;
703 break;
704
705 default:
706 break;
707 }
708 }
709
710 /* set end-of-string \0 */
711 /* There are no breaks here on purpose */
712 switch (state)
713 {
714 case 0:
715 language[tmp_idx] = 0;
716 tmp_idx = 0;
717 EINA_FALLTHROUGH;
718 /* no break */
719
720 case 1:
721 territory[tmp_idx] = 0;
722 tmp_idx = 0;
723 EINA_FALLTHROUGH;
724 /* no break */
725
726 case 2:
727 codeset[tmp_idx] = 0;
728 tmp_idx = 0;
729 EINA_FALLTHROUGH;
730 /* no break */
731
732 case 3:
733 modifier[tmp_idx] = 0;
734 EINA_FALLTHROUGH;
735 /* no break */
736
737 default:
738 break;
739 }
740
741 if ((!language[0]) && (!territory[0]) && (!codeset[0]) && (!modifier[0])) return NULL;
742 locale_parts = E_NEW(E_Locale_Parts, 1);
743
744 /* Put the parts of the string together */
745 if (language[0] != 0)
746 {
747 locale_parts->mask |= E_INTL_LOC_LANG;
748 locale_parts->lang = eina_stringshare_add(language);
749 }
750 if (territory[0] != 0)
751 {
752 locale_parts->mask |= E_INTL_LOC_REGION;
753 locale_parts->region = eina_stringshare_add(territory);
754 }
755 if (codeset[0] != 0)
756 {
757 locale_parts->mask |= E_INTL_LOC_CODESET;
758 locale_parts->codeset = eina_stringshare_add(codeset);
759 }
760 if (modifier[0] != 0)
761 {
762 locale_parts->mask |= E_INTL_LOC_MODIFIER;
763 locale_parts->modifier = eina_stringshare_add(modifier);
764 }
765
766 return locale_parts;
767 }
768
769 E_API void
e_intl_locale_parts_free(E_Locale_Parts * locale_parts)770 e_intl_locale_parts_free(E_Locale_Parts *locale_parts)
771 {
772 if (locale_parts)
773 {
774 if (locale_parts->lang) eina_stringshare_del(locale_parts->lang);
775 if (locale_parts->region) eina_stringshare_del(locale_parts->region);
776 if (locale_parts->codeset) eina_stringshare_del(locale_parts->codeset);
777 if (locale_parts->modifier) eina_stringshare_del(locale_parts->modifier);
778 E_FREE(locale_parts);
779 }
780 }
781
782 E_API char *
e_intl_locale_parts_combine(E_Locale_Parts * locale_parts,int mask)783 e_intl_locale_parts_combine(E_Locale_Parts *locale_parts, int mask)
784 {
785 int locale_size;
786 char *locale;
787
788 if (!locale_parts) return NULL;
789
790 if ((mask & locale_parts->mask) != mask) return NULL;
791
792 /* Construct the clean locale string */
793 locale_size = 0;
794
795 /* determine the size */
796 if (mask & E_INTL_LOC_LANG)
797 locale_size = strlen(locale_parts->lang) + 1;
798
799 if (mask & E_INTL_LOC_REGION)
800 locale_size += strlen(locale_parts->region) + 1;
801
802 if (mask & E_INTL_LOC_CODESET)
803 locale_size += strlen(locale_parts->codeset) + 1;
804
805 if (mask & E_INTL_LOC_MODIFIER)
806 locale_size += strlen(locale_parts->modifier) + 1;
807
808 if (!locale_size) return NULL;
809
810 /* Allocate memory */
811 locale = (char *)malloc(locale_size);
812 locale[0] = 0;
813
814 if (mask & E_INTL_LOC_LANG)
815 strcat(locale, locale_parts->lang);
816
817 if (mask & E_INTL_LOC_REGION)
818 {
819 if (locale[0] != 0) strcat(locale, "_");
820 strcat(locale, locale_parts->region);
821 }
822
823 if (mask & E_INTL_LOC_CODESET)
824 {
825 if (locale[0] != 0) strcat(locale, ".");
826 strcat(locale, locale_parts->codeset);
827 }
828
829 if (mask & E_INTL_LOC_MODIFIER)
830 {
831 if (locale[0] != 0) strcat(locale, "@");
832 strcat(locale, locale_parts->modifier);
833 }
834
835 return locale;
836 }
837
838 E_API char *
e_intl_locale_charset_canonic_get(const char * charset)839 e_intl_locale_charset_canonic_get(const char *charset)
840 {
841 char charset_canonic[32];
842 char c;
843 int i, i_tmp;
844
845 i = 0;
846 i_tmp = 0;
847 while ((c = charset[i++]) != 0)
848 {
849 if (isalnum(c))
850 charset_canonic[i_tmp++] = tolower(c);
851 }
852 charset_canonic[i_tmp] = 0;
853
854 if (!strcmp(charset, charset_canonic))
855 return NULL;
856
857 return strdup(charset_canonic);
858 }
859
860 static Eina_List *
_e_intl_locale_system_locales_get(void)861 _e_intl_locale_system_locales_get(void)
862 {
863 Eina_List *locales;
864 FILE *output;
865
866 locales = NULL;
867 /* FIXME: Maybe needed for other BSD OS, or even Solaris */
868 #ifdef __OpenBSD__
869 output = popen("ls /usr/share/locale", "r");
870 #else
871 output = popen("locale -a", "r");
872 #endif
873 if (output)
874 {
875 char line[32];
876 while (fscanf(output, "%31[^\n]\n", line) == 1)
877 locales = eina_list_append(locales, strdup(line));
878
879 pclose(output);
880 }
881 return locales;
882 }
883
884 /*
885 * must be an un aliased locale;
886 */
887 static int
_e_intl_locale_validate(const char * locale)888 _e_intl_locale_validate(const char *locale)
889 {
890 Eina_List *all_locales;
891 E_Locale_Parts *locale_parts;
892 char *locale_next;
893 char *locale_lr = NULL;
894 char *locale_cs_canonic;
895 int found;
896
897 found = 0;
898
899 locale_parts = e_intl_locale_parts_get(locale);
900
901 /* Gather the search information */
902 if (locale_parts)
903 {
904 if (locale_parts->mask & E_INTL_LOC_REGION)
905 locale_lr = e_intl_locale_parts_combine(locale_parts, E_INTL_LOC_LANG | E_INTL_LOC_REGION);
906 else if (locale_parts->lang)
907 locale_lr = strdup(locale_parts->lang);
908 }
909 if (!locale_lr)
910 {
911 /* Not valid locale, maybe its an alias */
912 locale_lr = strdup(locale);
913 locale_cs_canonic = NULL;
914 }
915 else
916 {
917 if (locale_parts && locale_parts->codeset)
918 locale_cs_canonic = e_intl_locale_charset_canonic_get(locale_parts->codeset);
919 else
920 locale_cs_canonic = NULL;
921 }
922
923 /* Get list of all available locales and aliases */
924 all_locales = _e_intl_locale_system_locales_get();
925
926 /* Match locale with one from the list */
927 EINA_LIST_FREE(all_locales, locale_next)
928 {
929 if (found == 0)
930 {
931 E_Locale_Parts *locale_parts_next;
932 char *locale_lr_next = NULL;
933
934 locale_parts_next = e_intl_locale_parts_get(locale_next);
935 if (locale_parts_next)
936 {
937 if (locale_parts_next->mask & E_INTL_LOC_REGION)
938 locale_lr_next = e_intl_locale_parts_combine(locale_parts_next,
939 E_INTL_LOC_LANG | E_INTL_LOC_REGION);
940 else if (locale_parts_next->lang)
941 locale_lr_next = strdup(locale_parts_next->lang);
942 }
943 if ((locale_parts) && (locale_lr_next) &&
944 (!strcmp(locale_lr, locale_lr_next)))
945 {
946 /* Matched lang/region part, now if CS matches */
947 if ((!locale_parts->codeset) && (!locale_parts_next->codeset))
948 {
949 /* Lang/Region parts match and no charsets,
950 * we have a match
951 */
952 found = 1;
953 }
954 else if (locale_parts->codeset && locale_parts_next->codeset)
955 {
956 if (!strcmp(locale_parts->codeset, locale_parts_next->codeset))
957 {
958 /* Lang/Region and charsets match */
959 found = 1;
960 }
961 else if (locale_cs_canonic)
962 {
963 char *locale_cs_canonic_next;
964 /* try to match charsets in canonic form */
965
966 locale_cs_canonic_next =
967 e_intl_locale_charset_canonic_get(locale_parts_next->codeset);
968
969 if (locale_cs_canonic_next)
970 {
971 if (!strcmp(locale_cs_canonic, locale_cs_canonic_next))
972 {
973 /* Lang/Resion and charsets in canonic
974 * form match
975 */
976 found = 1;
977 }
978 free(locale_cs_canonic_next);
979 }
980 else
981 {
982 if (!strcmp(locale_cs_canonic, locale_parts_next->codeset))
983 {
984 /* Lang/Resion and charsets in canonic
985 * form match
986 */
987 found = 1;
988 }
989 }
990 }
991 }
992 }
993 else
994 {
995 /* Its an alias */
996 if (!strcmp(locale_lr, locale_next)) found = 1;
997 }
998 e_intl_locale_parts_free(locale_parts_next);
999 E_FREE(locale_lr_next);
1000 }
1001 free(locale_next);
1002 }
1003
1004 e_intl_locale_parts_free(locale_parts);
1005 free(locale_lr);
1006 E_FREE(locale_cs_canonic);
1007 return found;
1008 }
1009
1010 /*
1011 * arg local must be an already validated and unaliased locale
1012 * returns the locale search order e.g.
1013 * en_US.UTF-8 ->
1014 * Mask (b) Locale (en_US.UTF-8)
1015 * Mask (a) Locale (en_US)
1016 * Mask (9) Locale (en.UTF-8)
1017 * Mask (8) Locale (en)
1018 */
1019 static Eina_List *
_e_intl_locale_search_order_get(const char * locale)1020 _e_intl_locale_search_order_get(const char *locale)
1021 {
1022 Eina_List *search_list;
1023 E_Locale_Parts *locale_parts;
1024 char *masked_locale;
1025 int mask;
1026
1027 locale_parts = e_intl_locale_parts_get(locale);
1028 if (!locale_parts) return NULL;
1029
1030 search_list = NULL;
1031 for (mask = E_INTL_LOC_ALL; mask >= E_INTL_LOC_LANG; mask--)
1032 {
1033 if ((mask & locale_parts->mask) == mask)
1034 {
1035 /* Only append if the mask we need is available */
1036 masked_locale = e_intl_locale_parts_combine(locale_parts, mask);
1037 search_list = eina_list_append(search_list, masked_locale);
1038 }
1039 }
1040 e_intl_locale_parts_free(locale_parts);
1041 return search_list;
1042 }
1043
1044 static Eina_List *
_e_intl_imc_dir_scan(const char * dir)1045 _e_intl_imc_dir_scan(const char *dir)
1046 {
1047 Eina_List *imcs = NULL;
1048 Eina_List *files;
1049 char *file;
1050
1051 files = ecore_file_ls(dir);
1052
1053 EINA_LIST_FREE(files, file)
1054 {
1055 if (strstr(file, ".imc"))
1056 {
1057 char buf[PATH_MAX];
1058
1059 snprintf(buf, sizeof(buf), "%s/%s", dir, file);
1060 imcs = eina_list_append(imcs, strdup(buf));
1061 }
1062 free(file);
1063 }
1064 return imcs;
1065 }
1066
1067