#include "e.h" static Ecore_Exe *_e_intl_input_method_exec = NULL; static Ecore_Event_Handler *_e_intl_exit_handler = NULL; static char *_e_intl_orig_lang = NULL; static char *_e_intl_orig_language = NULL; static char *_e_intl_language = NULL; static char *_e_intl_language_alias = NULL; static char *_e_intl_orig_xmodifiers = NULL; static char *_e_intl_orig_qt_im_module = NULL; static char *_e_intl_orig_gtk_im_module = NULL; static char *_e_intl_orig_ecore_imf_module = NULL; static const char *_e_intl_imc_personal_path = NULL; static const char *_e_intl_imc_system_path = NULL; #define E_EXE_STOP(EXE) if (EXE) { ecore_exe_terminate(EXE); ecore_exe_free(EXE); EXE = NULL; } #define E_EXE_IS_VALID(EXE) (!((!EXE) || (EXE[0] == 0))) /* All locale parts */ #define E_INTL_LOC_ALL E_INTL_LOC_LANG | \ E_INTL_LOC_REGION | \ E_INTL_LOC_CODESET | \ E_INTL_LOC_MODIFIER /* Locale parts which are significant when Validating */ #define E_INTL_LOC_SIGNIFICANT E_INTL_LOC_LANG | \ E_INTL_LOC_REGION | \ E_INTL_LOC_CODESET /* Language Setting and Listing */ static char *_e_intl_language_path_find(char *language); static Eina_List *_e_intl_language_dir_scan(const char *dir); static int _e_intl_language_list_find(Eina_List *language_list, char *language); /* Locale Validation and Discovery */ static Eina_Hash *_e_intl_locale_alias_hash_get(void); static char *_e_intl_locale_alias_get(const char *language); static Eina_List *_e_intl_locale_system_locales_get(void); static Eina_List *_e_intl_locale_search_order_get(const char *locale); static int _e_intl_locale_validate(const char *locale); static void _e_intl_locale_hash_free(Eina_Hash *language_hash); static Eina_Bool _e_intl_locale_hash_free_cb(const Eina_Hash *hash, const void *key, void *data, void *fdata); /* Input Method Configuration and Management */ static Eina_Bool _e_intl_cb_exit(void *data, int type, void *event); static Eina_List *_e_intl_imc_dir_scan(const char *dir); EINTERN int e_intl_init(void) { char *s; e_intl_data_init(); if ((s = getenv("LANG"))) _e_intl_orig_lang = strdup(s); if ((s = getenv("LANGUAGE"))) _e_intl_orig_language = strdup(s); if ((s = getenv("GTK_IM_MODULE"))) _e_intl_orig_gtk_im_module = strdup(s); if ((s = getenv("QT_IM_MODULE"))) _e_intl_orig_qt_im_module = strdup(s); if ((s = getenv("XMODIFIERS"))) _e_intl_orig_xmodifiers = strdup(s); if ((s = getenv("ECORE_IMF_MODULE"))) _e_intl_orig_ecore_imf_module = strdup(s); return 1; } EINTERN int e_intl_shutdown(void) { E_FREE(_e_intl_language); E_FREE(_e_intl_orig_lang); E_FREE(_e_intl_orig_language); E_FREE(_e_intl_orig_gtk_im_module); E_FREE(_e_intl_orig_qt_im_module); E_FREE(_e_intl_orig_xmodifiers); E_FREE(_e_intl_orig_ecore_imf_module); if (_e_intl_imc_personal_path) eina_stringshare_del(_e_intl_imc_personal_path); if (_e_intl_imc_system_path) eina_stringshare_del(_e_intl_imc_system_path); e_intl_data_shutdown(); return 1; } /* Setup configuration settings and start services */ EINTERN int e_intl_post_init(void) { if ((e_config->language) && (e_config->language[0] != 0)) e_intl_language_set(e_config->language); else e_intl_language_set(NULL); if ((e_config->input_method) && (e_config->input_method[0] != 0)) e_intl_input_method_set(e_config->input_method); _e_intl_exit_handler = ecore_event_handler_add(ECORE_EXE_EVENT_DEL, _e_intl_cb_exit, NULL); return 1; } EINTERN int e_intl_post_shutdown(void) { if (_e_intl_exit_handler) { ecore_event_handler_del(_e_intl_exit_handler); _e_intl_exit_handler = NULL; } e_intl_input_method_set(NULL); e_intl_language_set(NULL); E_FREE(_e_intl_language_alias); E_EXE_STOP(_e_intl_input_method_exec); return 1; } /* * TODO * - Add error dialogs explaining any errors while setting the locale * * Locale aliases need to be configured * * Locale is invalid * * Message files are not found for this locale, then we have (en_US, POSIX, C) * - Add support of compound locales i.e. (en_US;zh_CN;C) ==Defer== * - Add Configuration for to-be-set environment variables */ E_API void e_intl_language_set(const char *lang) { int set_envars; int ok; set_envars = 1; /* NULL lang means set everything back to the original environment * defaults */ if (!lang) { e_util_env_set("LANG", _e_intl_orig_lang); if (!lang) lang = getenv("LC_ALL"); if (!lang) lang = getenv("LANG"); set_envars = 0; } E_FREE(_e_intl_language_alias); _e_intl_language_alias = _e_intl_locale_alias_get(lang); E_FREE(_e_intl_language); if (lang) _e_intl_language = strdup(lang); else _e_intl_language = NULL; ok = 1; if (strcmp(_e_intl_language_alias, "C")) { ok = _e_intl_locale_validate(_e_intl_language_alias); if (!ok) { char *p, *new_lang; new_lang = _e_intl_language_alias; p = strchr(new_lang, '.'); if (p) *p = 0; _e_intl_language_alias = strdup(new_lang); E_FREE(new_lang); ok = _e_intl_locale_validate(_e_intl_language_alias); } } if (!ok) { fprintf(stderr, "The locale '%s' cannot be found on your " "system. Please install this locale or try " "something else.", _e_intl_language_alias); return; } /* Only set env vars is a non NULL locale was passed */ if (set_envars) { e_util_env_set("LANG", lang); /* Unset LANGUAGE, apparently causes issues if set */ e_util_env_set("LANGUAGE", NULL); efreet_lang_reset(); setlocale(LC_ALL, lang); } else { setlocale(LC_ALL, ""); } if (_e_intl_language) { char *locale_path; locale_path = _e_intl_language_path_find(_e_intl_language_alias); if (!locale_path) { E_Locale_Parts *locale_parts; locale_parts = e_intl_locale_parts_get(_e_intl_language_alias); /* If locale is C or some form of en don't report an error */ if ((!locale_parts) && (strcmp(_e_intl_language_alias, "C"))) { fprintf(stderr, "An error occurred setting your locale. \n\n" "The locale you have chosen '%s' appears to \n" "be an alias, however, it can not be resolved.\n" "Please make sure you have a 'locale.alias' \n" "file in your 'messages' path which can resolve\n" "this alias.\n\n" "Enlightenment will not be translated.\n", _e_intl_language_alias); } else if ((locale_parts) && (locale_parts->lang) && (strcmp(locale_parts->lang, "en"))) { fprintf(stderr, "An error occurred setting your locale. \n\n" "The translation files for the locale you \n" "have chosen (%s) cannot be found in your \n" "'messages' path. \n\n" "Enlightenment will not be translated.\n", _e_intl_language_alias); } e_intl_locale_parts_free(locale_parts); } else { #ifdef HAVE_GETTEXT bindtextdomain(PACKAGE, locale_path); textdomain(PACKAGE); bind_textdomain_codeset(PACKAGE, "UTF-8"); #endif free(locale_path); } } } E_API const char * e_intl_language_get(void) { return _e_intl_language; } E_API const char * e_intl_language_alias_get(void) { return _e_intl_language_alias; } E_API Eina_List * e_intl_language_list(void) { Eina_List *next; Eina_List *dir_list; Eina_List *all_languages; E_Path_Dir *epd; all_languages = NULL; dir_list = e_path_dir_list_get(path_messages); EINA_LIST_FOREACH(dir_list, next, epd) { Eina_List *dir_languages; char *language; dir_languages = _e_intl_language_dir_scan(epd->dir); EINA_LIST_FREE(dir_languages, language) if ((_e_intl_language_list_find(all_languages, language)) || ((strlen(language) > 2) && (!_e_intl_locale_validate(language)))) { free(language); } else all_languages = eina_list_append(all_languages, language); } e_path_dir_list_free(dir_list); return all_languages; } static int _e_intl_language_list_find(Eina_List *language_list, char *language) { Eina_List *l; char *lang; if ((!language_list) || (!language)) return 0; EINA_LIST_FOREACH(language_list, l, lang) if (!strcmp(lang, language)) return 1; return 0; } E_API void e_intl_input_method_set(const char *imc_path) { if (!imc_path) { E_EXE_STOP(_e_intl_input_method_exec); e_util_env_set("GTK_IM_MODULE", _e_intl_orig_gtk_im_module); e_util_env_set("QT_IM_MODULE", _e_intl_orig_qt_im_module); e_util_env_set("XMODIFIERS", _e_intl_orig_xmodifiers); e_util_env_set("ECORE_IMF_MODULE", _e_intl_orig_ecore_imf_module); } if (imc_path) { Eet_File *imc_ef; E_Input_Method_Config *imc; imc_ef = eet_open(imc_path, EET_FILE_MODE_READ); if (imc_ef) { imc = e_intl_input_method_config_read(imc_ef); eet_close(imc_ef); if (imc) { e_util_env_set("GTK_IM_MODULE", imc->gtk_im_module); e_util_env_set("QT_IM_MODULE", imc->qt_im_module); e_util_env_set("XMODIFIERS", imc->xmodifiers); e_util_env_set("ECORE_IMF_MODULE", imc->ecore_imf_module); E_EXE_STOP(_e_intl_input_method_exec); if (E_EXE_IS_VALID(imc->e_im_exec)) { // if you see valgrind complain about memory // definitely lost here... it's wrong. _e_intl_input_method_exec = e_util_exe_safe_run (imc->e_im_exec, NULL); ecore_exe_tag_set(_e_intl_input_method_exec, "E/im_exec"); if ((!_e_intl_input_method_exec) || (!ecore_exe_pid_get(_e_intl_input_method_exec))) e_util_dialog_show(_("Input Method Error"), _("Error starting the input method executable" "please make sure that your input" "method configuration is correct and" "that your configuration's" "executable is in your PATH")); } e_intl_input_method_config_free(imc); } } } } E_API Eina_List * e_intl_input_method_list(void) { Eina_List *input_methods; Eina_List *im_list; im_list = NULL; /* Personal Path */ im_list = _e_intl_imc_dir_scan(e_intl_imc_personal_path_get()); /* System Path */ input_methods = _e_intl_imc_dir_scan(e_intl_imc_system_path_get()); im_list = eina_list_merge(im_list, input_methods); return im_list; } const char * e_intl_imc_personal_path_get(void) { if (!_e_intl_imc_personal_path) { char buf[4096]; e_user_dir_concat_static(buf, "input_methods"); _e_intl_imc_personal_path = eina_stringshare_add(buf); } return _e_intl_imc_personal_path; } const char * e_intl_imc_system_path_get(void) { if (!_e_intl_imc_system_path) { char buf[4096]; e_prefix_data_concat_static(buf, "data/input_methods"); _e_intl_imc_system_path = eina_stringshare_add(buf); } return _e_intl_imc_system_path; } static Eina_Bool _e_intl_cb_exit(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) { Ecore_Exe_Event_Del *ev; ev = event; if (!ev->exe) return ECORE_CALLBACK_PASS_ON; if (ev->exe == _e_intl_input_method_exec) _e_intl_input_method_exec = NULL; return ECORE_CALLBACK_PASS_ON; } static void _e_intl_locale_hash_free(Eina_Hash *locale_hash) { if (!locale_hash) return; eina_hash_foreach(locale_hash, _e_intl_locale_hash_free_cb, NULL); eina_hash_free(locale_hash); } 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) { free(data); return 1; } /* * get the directory associated with the language. Language Must be valid alias * i.e. Already validated and already de-aliased. * * NULL means: * 1) The user does not have an enlightenment translation for this locale * 2) The user does not have their locale.aliases configured correctly * * @return NULL if not found. */ static char * _e_intl_language_path_find(char *language) { char *directory; char *data; Eina_List *dir_list; Eina_List *search_list; Eina_List *next_dir; Eina_List *next_search; E_Path_Dir *epd; char *search_locale; int found; search_list = _e_intl_locale_search_order_get(language); if (!search_list) return NULL; directory = NULL; found = 0; dir_list = e_path_dir_list_get(path_messages); /* For each directory in the path */ EINA_LIST_FOREACH(dir_list, next_dir, epd) { if (found) break; /* Match canonicalized locale against each possible search */ EINA_LIST_FOREACH(search_list, next_search, search_locale) { char message_path[PATH_MAX]; snprintf(message_path, sizeof(message_path), "%s/%s/LC_MESSAGES/%s.mo", epd->dir, search_locale, PACKAGE); if ((ecore_file_exists(message_path)) && (!ecore_file_is_dir(message_path))) { directory = strdup(epd->dir); found = 1; break; } } } e_path_dir_list_free(dir_list); EINA_LIST_FREE(search_list, data) free(data); return directory; } static Eina_List * _e_intl_language_dir_scan(const char *dir) { Eina_List *languages; Eina_List *files; char *file; languages = NULL; files = ecore_file_ls(dir); if (!files) return NULL; if (files) { EINA_LIST_FREE(files, file) { char file_path[PATH_MAX]; snprintf(file_path, sizeof(file_path), "%s/%s/LC_MESSAGES/%s.mo", dir, file, PACKAGE); if (ecore_file_exists(file_path) && !ecore_file_is_dir(file_path)) languages = eina_list_append(languages, file); else free(file); } } return languages; } /* get the alias for a locale * * return pointer to allocated alias string. never returns NULL * String will be the same if its a valid locale already or there * is no alias. */ static char * _e_intl_locale_alias_get(const char *language) { Eina_Hash *alias_hash; char *alias; char lbuf[256]; char *lower_language = lbuf; if ((!language) || (!strncmp(language, "POSIX", strlen("POSIX")))) return strdup("C"); alias_hash = _e_intl_locale_alias_hash_get(); if (!alias_hash) /* No alias file available */ return strdup(language); strncpy(lbuf, language, sizeof(lbuf) - 1); lbuf[sizeof(lbuf) - 1] = '\0'; eina_str_tolower(&lower_language); alias = eina_hash_find(alias_hash, lower_language); if (alias) alias = strdup(alias); else alias = strdup(language); _e_intl_locale_hash_free(alias_hash); return alias; } static Eina_Hash * _e_intl_locale_alias_hash_get(void) { Eina_List *next; Eina_List *dir_list; E_Path_Dir *epd; Eina_Hash *alias_hash; dir_list = e_path_dir_list_get(path_messages); alias_hash = NULL; EINA_LIST_FOREACH(dir_list, next, epd) { char buf[4096]; FILE *f; snprintf(buf, sizeof(buf), "%s/locale.alias", epd->dir); f = fopen(buf, "r"); if (f) { char alias[4096], locale[4096]; /* read locale alias lines */ while (fscanf(f, "%4095s %4095[^\n]\n", alias, locale) == 2) { /* skip comments */ if ((alias[0] == '!') || (alias[0] == '#')) continue; /* skip dupes */ if (eina_hash_find(alias_hash, alias)) continue; if (!alias_hash) alias_hash = eina_hash_string_superfast_new(NULL); eina_hash_add(alias_hash, alias, strdup(locale)); } fclose(f); } } e_path_dir_list_free(dir_list); return alias_hash; } /* return parts of the locale that are requested in the mask * return null if the locale looks to be invalid (Does not have * ll_DD) * * the returned string needs to be freed */ /* * Not canonic, just gets the parts */ E_API E_Locale_Parts * e_intl_locale_parts_get(const char *locale) { /* Parse Results */ E_Locale_Parts *locale_parts; char language[4] = {0}; char territory[4] = {0}; char codeset[32] = {0}; char modifier[32] = {0}; /* Parse State */ int state = 0; /* start out looking for the language */ unsigned int locale_idx; int tmp_idx = 0; /* Parse Loop - Seperators are _ . @ */ for (locale_idx = 0; locale_idx < strlen(locale); locale_idx++) { char locale_char; locale_char = locale[locale_idx]; /* we have finished scanning the locale string */ if (!locale_char) break; /* scan this character based on the current start */ switch (state) { case 0: /* Gathering Language */ if (tmp_idx == 2 && locale_char == '_') { state++; language[tmp_idx] = 0; tmp_idx = 0; } else if ((tmp_idx < 2) && (islower(locale_char))) language[tmp_idx++] = locale_char; else if (locale_char == '.') // no territory { state = 2; language[tmp_idx] = 0; tmp_idx = 0; } else return NULL; break; case 1: /* Gathering Territory */ if (tmp_idx == 2 && locale_char == '.') { state++; territory[tmp_idx] = 0; tmp_idx = 0; } else if ((tmp_idx == 2) && (locale_char == '@')) { state += 2; territory[tmp_idx] = 0; codeset[0] = 0; tmp_idx = 0; } else if ((tmp_idx < 2) && isupper(locale_char)) territory[tmp_idx++] = locale_char; else return NULL; break; case 2: /* Gathering Codeset */ if (locale_char == '@') { state++; codeset[tmp_idx] = 0; tmp_idx = 0; } else if (tmp_idx < 31) codeset[tmp_idx++] = locale_char; else return NULL; break; case 3: /* Gathering modifier */ if (tmp_idx < 31) modifier[tmp_idx++] = locale_char; else return NULL; break; default: break; } } /* set end-of-string \0 */ /* There are no breaks here on purpose */ switch (state) { case 0: language[tmp_idx] = 0; tmp_idx = 0; EINA_FALLTHROUGH; /* no break */ case 1: territory[tmp_idx] = 0; tmp_idx = 0; EINA_FALLTHROUGH; /* no break */ case 2: codeset[tmp_idx] = 0; tmp_idx = 0; EINA_FALLTHROUGH; /* no break */ case 3: modifier[tmp_idx] = 0; EINA_FALLTHROUGH; /* no break */ default: break; } if ((!language[0]) && (!territory[0]) && (!codeset[0]) && (!modifier[0])) return NULL; locale_parts = E_NEW(E_Locale_Parts, 1); /* Put the parts of the string together */ if (language[0] != 0) { locale_parts->mask |= E_INTL_LOC_LANG; locale_parts->lang = eina_stringshare_add(language); } if (territory[0] != 0) { locale_parts->mask |= E_INTL_LOC_REGION; locale_parts->region = eina_stringshare_add(territory); } if (codeset[0] != 0) { locale_parts->mask |= E_INTL_LOC_CODESET; locale_parts->codeset = eina_stringshare_add(codeset); } if (modifier[0] != 0) { locale_parts->mask |= E_INTL_LOC_MODIFIER; locale_parts->modifier = eina_stringshare_add(modifier); } return locale_parts; } E_API void e_intl_locale_parts_free(E_Locale_Parts *locale_parts) { if (locale_parts) { if (locale_parts->lang) eina_stringshare_del(locale_parts->lang); if (locale_parts->region) eina_stringshare_del(locale_parts->region); if (locale_parts->codeset) eina_stringshare_del(locale_parts->codeset); if (locale_parts->modifier) eina_stringshare_del(locale_parts->modifier); E_FREE(locale_parts); } } E_API char * e_intl_locale_parts_combine(E_Locale_Parts *locale_parts, int mask) { int locale_size; char *locale; if (!locale_parts) return NULL; if ((mask & locale_parts->mask) != mask) return NULL; /* Construct the clean locale string */ locale_size = 0; /* determine the size */ if (mask & E_INTL_LOC_LANG) locale_size = strlen(locale_parts->lang) + 1; if (mask & E_INTL_LOC_REGION) locale_size += strlen(locale_parts->region) + 1; if (mask & E_INTL_LOC_CODESET) locale_size += strlen(locale_parts->codeset) + 1; if (mask & E_INTL_LOC_MODIFIER) locale_size += strlen(locale_parts->modifier) + 1; if (!locale_size) return NULL; /* Allocate memory */ locale = (char *)malloc(locale_size); locale[0] = 0; if (mask & E_INTL_LOC_LANG) strcat(locale, locale_parts->lang); if (mask & E_INTL_LOC_REGION) { if (locale[0] != 0) strcat(locale, "_"); strcat(locale, locale_parts->region); } if (mask & E_INTL_LOC_CODESET) { if (locale[0] != 0) strcat(locale, "."); strcat(locale, locale_parts->codeset); } if (mask & E_INTL_LOC_MODIFIER) { if (locale[0] != 0) strcat(locale, "@"); strcat(locale, locale_parts->modifier); } return locale; } E_API char * e_intl_locale_charset_canonic_get(const char *charset) { char charset_canonic[32]; char c; int i, i_tmp; i = 0; i_tmp = 0; while ((c = charset[i++]) != 0) { if (isalnum(c)) charset_canonic[i_tmp++] = tolower(c); } charset_canonic[i_tmp] = 0; if (!strcmp(charset, charset_canonic)) return NULL; return strdup(charset_canonic); } static Eina_List * _e_intl_locale_system_locales_get(void) { Eina_List *locales; FILE *output; locales = NULL; /* FIXME: Maybe needed for other BSD OS, or even Solaris */ #ifdef __OpenBSD__ output = popen("ls /usr/share/locale", "r"); #else output = popen("locale -a", "r"); #endif if (output) { char line[32]; while (fscanf(output, "%31[^\n]\n", line) == 1) locales = eina_list_append(locales, strdup(line)); pclose(output); } return locales; } /* * must be an un aliased locale; */ static int _e_intl_locale_validate(const char *locale) { Eina_List *all_locales; E_Locale_Parts *locale_parts; char *locale_next; char *locale_lr = NULL; char *locale_cs_canonic; int found; found = 0; locale_parts = e_intl_locale_parts_get(locale); /* Gather the search information */ if (locale_parts) { if (locale_parts->mask & E_INTL_LOC_REGION) locale_lr = e_intl_locale_parts_combine(locale_parts, E_INTL_LOC_LANG | E_INTL_LOC_REGION); else if (locale_parts->lang) locale_lr = strdup(locale_parts->lang); } if (!locale_lr) { /* Not valid locale, maybe its an alias */ locale_lr = strdup(locale); locale_cs_canonic = NULL; } else { if (locale_parts && locale_parts->codeset) locale_cs_canonic = e_intl_locale_charset_canonic_get(locale_parts->codeset); else locale_cs_canonic = NULL; } /* Get list of all available locales and aliases */ all_locales = _e_intl_locale_system_locales_get(); /* Match locale with one from the list */ EINA_LIST_FREE(all_locales, locale_next) { if (found == 0) { E_Locale_Parts *locale_parts_next; char *locale_lr_next = NULL; locale_parts_next = e_intl_locale_parts_get(locale_next); if (locale_parts_next) { if (locale_parts_next->mask & E_INTL_LOC_REGION) locale_lr_next = e_intl_locale_parts_combine(locale_parts_next, E_INTL_LOC_LANG | E_INTL_LOC_REGION); else if (locale_parts_next->lang) locale_lr_next = strdup(locale_parts_next->lang); } if ((locale_parts) && (locale_lr_next) && (!strcmp(locale_lr, locale_lr_next))) { /* Matched lang/region part, now if CS matches */ if ((!locale_parts->codeset) && (!locale_parts_next->codeset)) { /* Lang/Region parts match and no charsets, * we have a match */ found = 1; } else if (locale_parts->codeset && locale_parts_next->codeset) { if (!strcmp(locale_parts->codeset, locale_parts_next->codeset)) { /* Lang/Region and charsets match */ found = 1; } else if (locale_cs_canonic) { char *locale_cs_canonic_next; /* try to match charsets in canonic form */ locale_cs_canonic_next = e_intl_locale_charset_canonic_get(locale_parts_next->codeset); if (locale_cs_canonic_next) { if (!strcmp(locale_cs_canonic, locale_cs_canonic_next)) { /* Lang/Resion and charsets in canonic * form match */ found = 1; } free(locale_cs_canonic_next); } else { if (!strcmp(locale_cs_canonic, locale_parts_next->codeset)) { /* Lang/Resion and charsets in canonic * form match */ found = 1; } } } } } else { /* Its an alias */ if (!strcmp(locale_lr, locale_next)) found = 1; } e_intl_locale_parts_free(locale_parts_next); E_FREE(locale_lr_next); } free(locale_next); } e_intl_locale_parts_free(locale_parts); free(locale_lr); E_FREE(locale_cs_canonic); return found; } /* * arg local must be an already validated and unaliased locale * returns the locale search order e.g. * en_US.UTF-8 -> * Mask (b) Locale (en_US.UTF-8) * Mask (a) Locale (en_US) * Mask (9) Locale (en.UTF-8) * Mask (8) Locale (en) */ static Eina_List * _e_intl_locale_search_order_get(const char *locale) { Eina_List *search_list; E_Locale_Parts *locale_parts; char *masked_locale; int mask; locale_parts = e_intl_locale_parts_get(locale); if (!locale_parts) return NULL; search_list = NULL; for (mask = E_INTL_LOC_ALL; mask >= E_INTL_LOC_LANG; mask--) { if ((mask & locale_parts->mask) == mask) { /* Only append if the mask we need is available */ masked_locale = e_intl_locale_parts_combine(locale_parts, mask); search_list = eina_list_append(search_list, masked_locale); } } e_intl_locale_parts_free(locale_parts); return search_list; } static Eina_List * _e_intl_imc_dir_scan(const char *dir) { Eina_List *imcs = NULL; Eina_List *files; char *file; files = ecore_file_ls(dir); EINA_LIST_FREE(files, file) { if (strstr(file, ".imc")) { char buf[PATH_MAX]; snprintf(buf, sizeof(buf), "%s/%s", dir, file); imcs = eina_list_append(imcs, strdup(buf)); } free(file); } return imcs; }