3 static Ecore_Exe *_e_intl_input_method_exec = NULL;
4 static Ecore_Event_Handler *_e_intl_exit_handler = NULL;
6 static char *_e_intl_orig_lang = NULL;
7 static char *_e_intl_orig_language = NULL;
8 static char *_e_intl_language = NULL;
10 static char *_e_intl_language_alias = NULL;
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;
17 static const char *_e_intl_imc_personal_path = NULL;
18 static const char *_e_intl_imc_system_path = NULL;
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)))
23 /* All locale parts */
24 #define E_INTL_LOC_ALL E_INTL_LOC_LANG | \
26 E_INTL_LOC_CODESET | \
29 /* Locale parts which are significant when Validating */
30 #define E_INTL_LOC_SIGNIFICANT E_INTL_LOC_LANG | \
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);
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);
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);
59 if ((s = getenv("LANG"))) _e_intl_orig_lang = strdup(s);
60 if ((s = getenv("LANGUAGE"))) _e_intl_orig_language = strdup(s);
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);
73 E_FREE(_e_intl_language);
74 E_FREE(_e_intl_orig_lang);
75 E_FREE(_e_intl_orig_language);
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);
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);
87 e_intl_data_shutdown();
92 /* Setup configuration settings and start services */
94 e_intl_post_init(void)
96 if ((e_config->language) && (e_config->language[0] != 0))
97 e_intl_language_set(e_config->language);
99 e_intl_language_set(NULL);
101 if ((e_config->input_method) && (e_config->input_method[0] != 0))
102 e_intl_input_method_set(e_config->input_method);
104 _e_intl_exit_handler = ecore_event_handler_add(ECORE_EXE_EVENT_DEL,
105 _e_intl_cb_exit, NULL);
110 e_intl_post_shutdown(void)
112 if (_e_intl_exit_handler)
114 ecore_event_handler_del(_e_intl_exit_handler);
115 _e_intl_exit_handler = NULL;
118 e_intl_input_method_set(NULL);
120 e_intl_language_set(NULL);
121 E_FREE(_e_intl_language_alias);
123 E_EXE_STOP(_e_intl_input_method_exec);
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
137 e_intl_language_set(const char *lang)
143 /* NULL lang means set everything back to the original environment
148 e_util_env_set("LANG", _e_intl_orig_lang);
150 if (!lang) lang = getenv("LC_ALL");
151 if (!lang) lang = getenv("LANG");
156 E_FREE(_e_intl_language_alias);
157 _e_intl_language_alias = _e_intl_locale_alias_get(lang);
158 E_FREE(_e_intl_language);
161 _e_intl_language = strdup(lang);
163 _e_intl_language = NULL;
166 if (strcmp(_e_intl_language_alias, "C"))
168 ok = _e_intl_locale_validate(_e_intl_language_alias);
173 new_lang = _e_intl_language_alias;
174 p = strchr(new_lang, '.');
176 _e_intl_language_alias = strdup(new_lang);
178 ok = _e_intl_locale_validate(_e_intl_language_alias);
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);
188 /* Only set env vars is a non NULL locale was passed */
191 e_util_env_set("LANG", lang);
192 /* Unset LANGUAGE, apparently causes issues if set */
193 e_util_env_set("LANGUAGE", NULL);
195 setlocale(LC_ALL, lang);
199 setlocale(LC_ALL, "");
202 if (_e_intl_language)
206 locale_path = _e_intl_language_path_find(_e_intl_language_alias);
209 E_Locale_Parts *locale_parts;
211 locale_parts = e_intl_locale_parts_get(_e_intl_language_alias);
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")))
218 "An error occurred setting your locale. \n\n"
220 "The locale you have chosen '%s' appears to \n"
221 "be an alias, however, it can not be resloved.\n"
222 "Please make sure you have a 'locale.alias' \n"
223 "file in your 'messages' path which can resolve\n"
226 "Enlightenment will not be translated.\n",
227 _e_intl_language_alias);
229 else if ((locale_parts) && (locale_parts->lang) &&
230 (strcmp(locale_parts->lang, "en")))
233 "An error occurred setting your locale. \n\n"
235 "The translation files for the locale you \n"
236 "have chosen (%s) cannot be found in your \n"
237 "'messages' path. \n\n"
239 "Enlightenment will not be translated.\n",
240 _e_intl_language_alias);
242 e_intl_locale_parts_free(locale_parts);
247 bindtextdomain(PACKAGE, locale_path);
249 bind_textdomain_codeset(PACKAGE, "UTF-8");
257 e_intl_language_get(void)
259 return _e_intl_language;
263 e_intl_language_alias_get(void)
265 return _e_intl_language_alias;
269 e_intl_language_list(void)
273 Eina_List *all_languages;
276 all_languages = NULL;
277 dir_list = e_path_dir_list_get(path_messages);
278 EINA_LIST_FOREACH(dir_list, next, epd)
280 Eina_List *dir_languages;
283 dir_languages = _e_intl_language_dir_scan(epd->dir);
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))))
292 all_languages = eina_list_append(all_languages, language);
295 e_path_dir_list_free(dir_list);
297 return all_languages;
301 _e_intl_language_list_find(Eina_List *language_list, char *language)
306 if ((!language_list) || (!language)) return 0;
308 EINA_LIST_FOREACH(language_list, l, lang)
309 if (!strcmp(lang, language)) return 1;
315 e_intl_input_method_set(const char *imc_path)
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);
329 E_Input_Method_Config *imc;
331 imc_ef = eet_open(imc_path, EET_FILE_MODE_READ);
334 imc = e_intl_input_method_config_read(imc_ef);
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);
344 E_EXE_STOP(_e_intl_input_method_exec);
346 if (E_EXE_IS_VALID(imc->e_im_exec))
348 e_util_library_path_strip();
349 _e_intl_input_method_exec = ecore_exe_run(imc->e_im_exec, NULL);
350 e_util_library_path_restore();
351 ecore_exe_tag_set(_e_intl_input_method_exec, "E/im_exec");
353 if ((!_e_intl_input_method_exec) ||
354 (!ecore_exe_pid_get(_e_intl_input_method_exec)))
355 e_util_dialog_show(_("Input Method Error"),
356 _("Error starting the input method executable<br><br>"
357 "please make sure that your input<br>"
358 "method configuration is correct and<br>"
359 "that your configuration's<br>"
360 "executable is in your PATH<br>"));
362 e_intl_input_method_config_free(imc);
369 e_intl_input_method_list(void)
371 Eina_List *input_methods;
377 im_list = _e_intl_imc_dir_scan(e_intl_imc_personal_path_get());
380 input_methods = _e_intl_imc_dir_scan(e_intl_imc_system_path_get());
381 im_list = eina_list_merge(im_list, input_methods);
387 e_intl_imc_personal_path_get(void)
389 if (!_e_intl_imc_personal_path)
393 e_user_dir_concat_static(buf, "input_methods");
394 _e_intl_imc_personal_path = eina_stringshare_add(buf);
396 return _e_intl_imc_personal_path;
400 e_intl_imc_system_path_get(void)
402 if (!_e_intl_imc_system_path)
406 e_prefix_data_concat_static(buf, "data/input_methods");
407 _e_intl_imc_system_path = eina_stringshare_add(buf);
409 return _e_intl_imc_system_path;
413 _e_intl_cb_exit(void *data __UNUSED__, int type __UNUSED__, void *event)
415 Ecore_Exe_Event_Del *ev;
418 if (!ev->exe) return ECORE_CALLBACK_PASS_ON;
420 if (!(ecore_exe_tag_get(ev->exe) &&
421 (!strcmp(ecore_exe_tag_get(ev->exe), "E/im_exec")))) return 1;
423 _e_intl_input_method_exec = NULL;
424 return ECORE_CALLBACK_PASS_ON;
428 _e_intl_locale_hash_free(Eina_Hash *locale_hash)
430 if (!locale_hash) return;
431 eina_hash_foreach(locale_hash, _e_intl_locale_hash_free_cb, NULL);
432 eina_hash_free(locale_hash);
436 _e_intl_locale_hash_free_cb(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *data, void *fdata __UNUSED__)
443 * get the directory associated with the language. Language Must be valid alias
444 * i.e. Already validated and already de-aliased.
447 * 1) The user does not have an enlightenment translation for this locale
448 * 2) The user does not have their locale.aliases configured correctly
450 * @return NULL if not found.
453 _e_intl_language_path_find(char *language)
458 Eina_List *search_list;
460 Eina_List *next_search;
465 search_list = _e_intl_locale_search_order_get(language);
467 if (!search_list) return NULL;
471 dir_list = e_path_dir_list_get(path_messages);
473 /* For each directory in the path */
474 EINA_LIST_FOREACH(dir_list, next_dir, epd)
478 /* Match canonicalized locale against each possible search */
479 EINA_LIST_FOREACH(search_list, next_search, search_locale)
481 char message_path[PATH_MAX];
483 snprintf(message_path, sizeof(message_path), "%s/%s/LC_MESSAGES/%s.mo", epd->dir, search_locale, PACKAGE);
485 if ((ecore_file_exists(message_path)) && (!ecore_file_is_dir(message_path)))
487 directory = strdup(epd->dir);
493 e_path_dir_list_free(dir_list);
495 EINA_LIST_FREE(search_list, data)
502 _e_intl_language_dir_scan(const char *dir)
504 Eina_List *languages;
510 files = ecore_file_ls(dir);
511 if (!files) return NULL;
515 EINA_LIST_FREE(files, file)
517 char file_path[PATH_MAX];
519 snprintf(file_path, sizeof(file_path), "%s/%s/LC_MESSAGES/%s.mo",
521 if (ecore_file_exists(file_path) && !ecore_file_is_dir(file_path))
522 languages = eina_list_append(languages, file);
530 /* get the alias for a locale
532 * return pointer to allocated alias string. never returns NULL
533 * String will be the same if its a valid locale already or there
537 _e_intl_locale_alias_get(const char *language)
539 Eina_Hash *alias_hash;
541 char *lower_language;
544 if ((!language) || (!strncmp(language, "POSIX", strlen("POSIX"))))
547 alias_hash = _e_intl_locale_alias_hash_get();
548 if (!alias_hash) /* No alias file available */
549 return strdup(language);
551 lower_language = malloc(strlen(language) + 1);
552 for (i = 0; i < strlen(language); i++)
553 lower_language[i] = tolower(language[i]);
554 lower_language[i] = 0;
556 alias = eina_hash_find(alias_hash, lower_language);
557 free(lower_language);
560 alias = strdup(alias);
562 alias = strdup(language);
564 _e_intl_locale_hash_free(alias_hash);
570 _e_intl_locale_alias_hash_get(void)
575 Eina_Hash *alias_hash;
577 dir_list = e_path_dir_list_get(path_messages);
580 EINA_LIST_FOREACH(dir_list, next, epd)
585 snprintf(buf, sizeof(buf), "%s/locale.alias", epd->dir);
589 char alias[4096], locale[4096];
591 /* read locale alias lines */
592 while (fscanf(f, "%4090s %[^\n]\n", alias, locale) == 2)
595 if ((alias[0] == '!') || (alias[0] == '#'))
599 if (eina_hash_find(alias_hash, alias))
602 if (!alias_hash) alias_hash = eina_hash_string_superfast_new(NULL);
603 eina_hash_add(alias_hash, alias, strdup(locale));
608 e_path_dir_list_free(dir_list);
613 /* return parts of the locale that are requested in the mask
614 * return null if the locale looks to be invalid (Does not have
617 * the returned string needs to be freed
620 * Not canonic, just gets the parts
622 EAPI E_Locale_Parts *
623 e_intl_locale_parts_get(const char *locale)
626 E_Locale_Parts *locale_parts;
633 int state = 0; /* start out looking for the language */
634 unsigned int locale_idx;
637 /* Parse Loop - Seperators are _ . @ */
638 for (locale_idx = 0; locale_idx < strlen(locale); locale_idx++)
641 locale_char = locale[locale_idx];
643 /* we have finished scanning the locale string */
647 /* scan this character based on the current start */
650 case 0: /* Gathering Language */
651 if (tmp_idx == 2 && locale_char == '_')
654 language[tmp_idx] = 0;
657 else if ((tmp_idx < 2) && (islower(locale_char)))
658 language[tmp_idx++] = locale_char;
663 case 1: /* Gathering Territory */
664 if (tmp_idx == 2 && locale_char == '.')
667 territory[tmp_idx] = 0;
670 else if ((tmp_idx == 2) && (locale_char == '@'))
673 territory[tmp_idx] = 0;
677 else if ((tmp_idx < 2) && isupper(locale_char))
678 territory[tmp_idx++] = locale_char;
683 case 2: /* Gathering Codeset */
684 if (locale_char == '@')
687 codeset[tmp_idx] = 0;
690 else if (tmp_idx < 32)
691 codeset[tmp_idx++] = locale_char;
696 case 3: /* Gathering modifier */
698 modifier[tmp_idx++] = locale_char;
708 /* set end-of-string \0 */
709 /* There are no breaks here on purpose */
713 language[tmp_idx] = 0;
717 territory[tmp_idx] = 0;
721 codeset[tmp_idx] = 0;
725 modifier[tmp_idx] = 0;
731 locale_parts = E_NEW(E_Locale_Parts, 1);
733 /* Put the parts of the string together */
734 if (language[0] != 0)
736 locale_parts->mask |= E_INTL_LOC_LANG;
737 locale_parts->lang = eina_stringshare_add(language);
739 if (territory[0] != 0)
741 locale_parts->mask |= E_INTL_LOC_REGION;
742 locale_parts->region = eina_stringshare_add(territory);
746 locale_parts->mask |= E_INTL_LOC_CODESET;
747 locale_parts->codeset = eina_stringshare_add(codeset);
749 if (modifier[0] != 0)
751 locale_parts->mask |= E_INTL_LOC_MODIFIER;
752 locale_parts->modifier = eina_stringshare_add(modifier);
759 e_intl_locale_parts_free(E_Locale_Parts *locale_parts)
763 if (locale_parts->lang) eina_stringshare_del(locale_parts->lang);
764 if (locale_parts->region) eina_stringshare_del(locale_parts->region);
765 if (locale_parts->codeset) eina_stringshare_del(locale_parts->codeset);
766 if (locale_parts->modifier) eina_stringshare_del(locale_parts->modifier);
767 E_FREE(locale_parts);
772 e_intl_locale_parts_combine(E_Locale_Parts *locale_parts, int mask)
777 if (!locale_parts) return NULL;
779 if ((mask & locale_parts->mask) != mask) return NULL;
781 /* Construct the clean locale string */
784 /* determine the size */
785 if (mask & E_INTL_LOC_LANG)
786 locale_size = strlen(locale_parts->lang) + 1;
788 if (mask & E_INTL_LOC_REGION)
789 locale_size += strlen(locale_parts->region) + 1;
791 if (mask & E_INTL_LOC_CODESET)
792 locale_size += strlen(locale_parts->codeset) + 1;
794 if (mask & E_INTL_LOC_MODIFIER)
795 locale_size += strlen(locale_parts->modifier) + 1;
797 if (!locale_size) return NULL;
799 /* Allocate memory */
800 locale = (char *)malloc(locale_size);
803 if (mask & E_INTL_LOC_LANG)
804 strcat(locale, locale_parts->lang);
806 if (mask & E_INTL_LOC_REGION)
808 if (locale[0] != 0) strcat(locale, "_");
809 strcat(locale, locale_parts->region);
812 if (mask & E_INTL_LOC_CODESET)
814 if (locale[0] != 0) strcat(locale, ".");
815 strcat(locale, locale_parts->codeset);
818 if (mask & E_INTL_LOC_MODIFIER)
820 if (locale[0] != 0) strcat(locale, "@");
821 strcat(locale, locale_parts->modifier);
828 e_intl_locale_charset_canonic_get(const char *charset)
830 char charset_canonic[32];
836 while ((c = charset[i++]) != 0)
839 charset_canonic[i_tmp++] = tolower(c);
841 charset_canonic[i_tmp] = 0;
843 if (!strcmp(charset, charset_canonic))
846 return strdup(charset_canonic);
850 _e_intl_locale_system_locales_get(void)
856 /* FIXME: Maybe needed for other BSD OS, or even Solaris */
858 output = popen("ls /usr/share/locale", "r");
860 output = popen("locale -a", "r");
865 while (fscanf(output, "%[^\n]\n", line) == 1)
866 locales = eina_list_append(locales, strdup(line));
874 * must be an un aliased locale;
877 _e_intl_locale_validate(const char *locale)
879 Eina_List *all_locales;
880 E_Locale_Parts *locale_parts;
883 char *locale_cs_canonic;
888 locale_parts = e_intl_locale_parts_get(locale);
890 /* Gather the search information */
892 e_intl_locale_parts_combine(locale_parts,
893 E_INTL_LOC_LANG | E_INTL_LOC_REGION);
896 /* Not valid locale, maybe its an alias */
897 locale_lr = strdup(locale);
898 locale_cs_canonic = NULL;
902 if (locale_parts && locale_parts->codeset)
903 locale_cs_canonic = e_intl_locale_charset_canonic_get(locale_parts->codeset);
905 locale_cs_canonic = NULL;
908 /* Get list of all available locales and aliases */
909 all_locales = _e_intl_locale_system_locales_get();
911 /* Match locale with one from the list */
912 EINA_LIST_FREE(all_locales, locale_next)
916 E_Locale_Parts *locale_parts_next;
917 char *locale_lr_next;
919 locale_parts_next = e_intl_locale_parts_get(locale_next);
920 locale_lr_next = e_intl_locale_parts_combine(locale_parts_next,
921 E_INTL_LOC_LANG | E_INTL_LOC_REGION);
923 if ((locale_parts) && (locale_lr_next) &&
924 (!strcmp(locale_lr, locale_lr_next)))
926 /* Matched lang/region part, now if CS matches */
927 if ((!locale_parts->codeset) && (!locale_parts_next->codeset))
929 /* Lang/Region parts match and no charsets,
934 else if (locale_parts->codeset && locale_parts_next->codeset)
936 if (!strcmp(locale_parts->codeset, locale_parts_next->codeset))
938 /* Lang/Region and charsets match */
941 else if (locale_cs_canonic)
943 char *locale_cs_canonic_next;
944 /* try to match charsets in canonic form */
946 locale_cs_canonic_next =
947 e_intl_locale_charset_canonic_get(locale_parts_next->codeset);
949 if (locale_cs_canonic_next)
951 if (!strcmp(locale_cs_canonic, locale_cs_canonic_next))
953 /* Lang/Resion and charsets in canonic
958 free(locale_cs_canonic_next);
962 if (!strcmp(locale_cs_canonic, locale_parts_next->codeset))
964 /* Lang/Resion and charsets in canonic
976 if (!strcmp(locale_lr, locale_next)) found = 1;
978 e_intl_locale_parts_free(locale_parts_next);
979 E_FREE(locale_lr_next);
984 e_intl_locale_parts_free(locale_parts);
986 E_FREE(locale_cs_canonic);
991 * arg local must be an already validated and unaliased locale
992 * returns the locale search order e.g.
994 * Mask (b) Locale (en_US.UTF-8)
995 * Mask (a) Locale (en_US)
996 * Mask (9) Locale (en.UTF-8)
997 * Mask (8) Locale (en)
1000 _e_intl_locale_search_order_get(const char *locale)
1002 Eina_List *search_list;
1003 E_Locale_Parts *locale_parts;
1004 char *masked_locale;
1007 locale_parts = e_intl_locale_parts_get(locale);
1008 if (!locale_parts) return NULL;
1011 for (mask = E_INTL_LOC_ALL; mask >= E_INTL_LOC_LANG; mask--)
1013 if ((mask & locale_parts->mask) == mask)
1015 /* Only append if the mask we need is available */
1016 masked_locale = e_intl_locale_parts_combine(locale_parts, mask);
1017 search_list = eina_list_append(search_list, masked_locale);
1020 e_intl_locale_parts_free(locale_parts);
1025 _e_intl_imc_dir_scan(const char *dir)
1027 Eina_List *imcs = NULL;
1031 files = ecore_file_ls(dir);
1033 EINA_LIST_FREE(files, file)
1035 if (strstr(file, ".imc"))
1039 snprintf(buf, sizeof(buf), "%s/%s", dir, file);
1040 imcs = eina_list_append(imcs, strdup(buf));