From 49019cdc06f443428b3c6d7d273b4aa4a0c28011 Mon Sep 17 00:00:00 2001 From: Hwankyu Jhun Date: Thu, 2 Feb 2017 18:06:53 +0900 Subject: [PATCH] Add fallback for multilingual support Applying this patch, we provide the fallback about the multilingual support. It finds better fallbacks automatically. +-----------------+----------------+----------------------------+ | Settings | App Resources | Resource Resolution | +-----------------+----------------+----------------------------+ | 1. nl_BE | 1. default(en) | try nl_BE => fail | | 2. fr_BE | 2. fr | try nl => fail | | | 3. pa | try children of nl => fail | | | | try fr_BE => fail | | | | use fr | +-----------------+----------------+----------------------------+ Change-Id: I428fccec740b45db59c268565a0c715e3a0b08c7 Signed-off-by: Hwankyu Jhun --- src/appcore-i18n.c | 261 +++++++++++++++++++++++++++++++++++++++++++++++- src/base/appcore_base.c | 257 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 511 insertions(+), 7 deletions(-) diff --git a/src/appcore-i18n.c b/src/appcore-i18n.c index 3cbefcc..94289e6 100644 --- a/src/appcore-i18n.c +++ b/src/appcore-i18n.c @@ -24,23 +24,266 @@ #include #include #include - +#include +#include +#include +#include +#include #include #include "appcore-internal.h" static int _set; +static char locale_dir[PATH_MAX]; + +static void __free_children_langs(gpointer data) +{ + GList *list = (GList *)data; + + if (list == NULL) + return; + + g_list_free_full(list, (GDestroyNotify)free); +} + +static gint __compare_langs(gconstpointer a, gconstpointer b) +{ + return strcmp(a, b); +} + +static GHashTable *__get_lang_table(void) +{ + GHashTable *table; + DIR *dp; + struct dirent *dentry; + char buf[PATH_MAX]; + struct stat stat_buf; + int ret; + char *dup_lang; + char *token; + GList *list; + + if (locale_dir[0] == 0 || locale_dir[0] == '\0') + return NULL; + + table = g_hash_table_new_full(g_str_hash, g_str_equal, + free, __free_children_langs); + if (table == NULL) { + _ERR("Out of memory"); + return NULL; + } + + dp = opendir(locale_dir); + if (dp == NULL) { + g_hash_table_destroy(table); + return NULL; + } + + while ((dentry = readdir(dp)) != NULL) { + if (!strcmp(dentry->d_name, ".") || + !strcmp(dentry->d_name, "..")) + continue; + + snprintf(buf, sizeof(buf), "%s/%s", locale_dir, dentry->d_name); + ret = stat(buf, &stat_buf); + if (ret != 0 || !S_ISDIR(stat_buf.st_mode)) + continue; + + dup_lang = strdup(dentry->d_name); + if (dup_lang == NULL) { + _ERR("Out of memory"); + break; + } + + token = strtok(dup_lang, "_"); + if (token == NULL) { + free(dup_lang); + continue; + } + + list = (GList *)g_hash_table_lookup(table, token); + if (list == NULL) { + list = g_list_append(list, strdup(dentry->d_name)); + g_hash_table_insert(table, strdup(token), list); + } else { + list = g_list_append(list, strdup(dentry->d_name)); + } + free(dup_lang); + } + closedir(dp); + + return table; +} + +static GList *__append_langs(const char *lang, GList *list, GHashTable *table) +{ + GList *child_list; + GList *child_iter; + GList *found; + char *child_lang; + char *dup_lang; + char *token; + + if (lang == NULL) + return list; + + found = g_list_find_custom(g_list_first(list), lang, + __compare_langs); + if (found) { + list = g_list_remove_link(list, found); + free(found->data); + g_list_free(found); + list = g_list_append(list, strdup(lang)); + return list; + } + + dup_lang = strdup(lang); + if (dup_lang == NULL) + return list; + + token = strtok(dup_lang, "_"); + if (token == NULL) { + free(dup_lang); + return list; + } + + child_list = g_hash_table_lookup(table, token); + if (child_list == NULL) { + free(dup_lang); + return list; + } + + found = g_list_find_custom(g_list_first(child_list), + lang, __compare_langs); + if (found) { + list = g_list_append(list, strdup(lang)); + child_list = g_list_remove_link(child_list, found); + free(found->data); + g_list_free(found); + free(dup_lang); + return list; + } + + found = g_list_find_custom(g_list_first(child_list), + token, __compare_langs); + if (found) { + list = g_list_append(list, strdup(token)); + child_list = g_list_remove_link(child_list, found); + free(found->data); + g_list_free(found); + free(dup_lang); + return list; + } + free(dup_lang); + + child_iter = g_list_first(child_list); + while (child_iter) { + child_lang = (char *)child_iter->data; + child_iter = g_list_next(child_iter); + if (child_lang) { + list = g_list_append(list, strdup(child_lang)); + child_list = g_list_remove(child_list, child_lang); + free(child_lang); + break; + } + } + + return list; +} + +static GList *__split_language(const char *lang) +{ + GList *list = NULL; + char *dup_lang; + char *token; + + dup_lang = strdup(lang); + if (dup_lang == NULL) { + _ERR("Out of memory"); + return NULL; + } + + token = strtok(dup_lang, ":"); + while (token != NULL) { + list = g_list_append(list, strdup(token)); + token = strtok(NULL, ":"); + } + free(dup_lang); + + return list; +} + +static char *__get_language(const char *lang) +{ + GHashTable *table; + GList *list; + GList *lang_list = NULL; + GList *iter; + char *language; + char buf[LINE_MAX] = {'\0'}; + size_t n; + + list = __split_language(lang); + if (list == NULL) + return NULL; + + table = __get_lang_table(); + if (table == NULL) { + g_list_free_full(list, free); + return NULL; + } + + iter = g_list_first(list); + while (iter) { + language = (char *)iter->data; + lang_list = __append_langs(language, lang_list, table); + iter = g_list_next(iter); + } + g_list_free_full(list, free); + g_hash_table_destroy(table); + + iter = g_list_first(lang_list); + while (iter) { + language = (char *)iter->data; + if (language) { + if (buf[0] == '\0') { + snprintf(buf, sizeof(buf), "%s", language); + } else { + n = sizeof(buf) - strlen(buf) - 1; + strncat(buf, ":", n); + n = sizeof(buf) - strlen(buf) - 1; + strncat(buf, language, n); + } + } + iter = g_list_next(iter); + } + g_list_free_full(lang_list, free); + + n = sizeof(buf) - strlen(buf) - 1; + strncat(buf, ":", n); + n = sizeof(buf) - strlen(buf) - 1; + strncat(buf, "en_US:en_GB:en", n); + + return strdup(buf); +} void update_lang(void) { - char language[32]; + char *language; char *lang; char *r; lang = vconf_get_str(VCONFKEY_LANGSET); if (lang) { - snprintf(language, sizeof(language), "%s:en_US:en_GB:en", lang); - setenv("LANGUAGE", language, 1); + /* TODO: Use VCONFKEY_SETAPPL_LANGUAGES key */ + language = __get_language(lang); + if (language) { + _DBG("*****language(%s)", language); + setenv("LANGUAGE", language, 1); + free(language); + } else { + setenv("LANGUAGE", lang, 1); + } setenv("LANG", lang, 1); setenv("LC_MESSAGES", lang, 1); r = setlocale(LC_ALL, ""); @@ -113,10 +356,19 @@ static int __set_i18n(const char *domain, const char *dir) return 0; } +static void __set_locale_dir(const char *dirname) +{ + if (dirname == NULL) + return; + + snprintf(locale_dir, sizeof(locale_dir), "%s", dirname); +} + EXPORT_API int appcore_set_i18n(const char *domainname, const char *dirname) { int r; + __set_locale_dir(dirname); update_lang(); update_region(); @@ -131,6 +383,7 @@ int set_i18n(const char *domainname, const char *dirname) { _retv_if(_set, 0); + __set_locale_dir(dirname); update_lang(); update_region(); diff --git a/src/base/appcore_base.c b/src/base/appcore_base.c index 7c1b826..2f7298b 100644 --- a/src/base/appcore_base.c +++ b/src/base/appcore_base.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -62,6 +63,7 @@ static appcore_base_context __context; static GList *__events; static GDBusConnection *__bus; static guint __suspend_dbus_handler_initialized; +static char *__locale_dir; static void __invoke_callback(void *event, int type) { @@ -114,16 +116,255 @@ static void __on_low_battery(keynode_t *key, void *data) __invoke_callback(key, APPCORE_BASE_EVENT_LOW_BATTERY); } +static void __free_children_langs(gpointer data) +{ + GList *list = (GList *)data; + + if (list == NULL) + return; + + g_list_free_full(list, (GDestroyNotify)free); +} + +static gint __compare_langs(gconstpointer a, gconstpointer b) +{ + return strcmp(a, b); +} + +static GHashTable *__get_lang_table(void) +{ + GHashTable *table; + DIR *dp; + struct dirent *dentry; + char buf[PATH_MAX]; + struct stat stat_buf; + int ret; + char *dup_lang; + char *token; + GList *list; + + if (__locale_dir == NULL || __locale_dir[0] == '\0') + return NULL; + + table = g_hash_table_new_full(g_str_hash, g_str_equal, + free, __free_children_langs); + if (table == NULL) { + _ERR("Out of memory"); + return NULL; + } + + dp = opendir(__locale_dir); + if (dp == NULL) { + g_hash_table_destroy(table); + return NULL; + } + + while ((dentry = readdir(dp)) != NULL) { + if (!strcmp(dentry->d_name, ".") || + !strcmp(dentry->d_name, "..")) + continue; + + snprintf(buf, sizeof(buf), "%s/%s", + __locale_dir, dentry->d_name); + ret = stat(buf, &stat_buf); + if (ret != 0 || !S_ISDIR(stat_buf.st_mode)) + continue; + + dup_lang = strdup(dentry->d_name); + if (dup_lang == NULL) { + _ERR("Out of memory"); + break; + } + + token = strtok(dup_lang, "_"); + if (token == NULL) { + free(dup_lang); + continue; + } + + list = (GList *)g_hash_table_lookup(table, token); + if (list == NULL) { + list = g_list_append(list, strdup(dentry->d_name)); + g_hash_table_insert(table, strdup(token), list); + } else { + list = g_list_append(list, strdup(dentry->d_name)); + } + free(dup_lang); + } + closedir(dp); + + return table; +} + +static GList *__append_langs(const char *lang, GList *list, GHashTable *table) +{ + GList *child_list; + GList *child_iter; + GList *found; + char *child_lang; + char *dup_lang; + char *token; + + if (lang == NULL) + return list; + + found = g_list_find_custom(g_list_first(list), lang, + __compare_langs); + if (found) { + list = g_list_remove_link(list, found); + free(found->data); + g_list_free(found); + list = g_list_append(list, strdup(lang)); + return list; + } + + dup_lang = strdup(lang); + if (dup_lang == NULL) + return list; + + token = strtok(dup_lang, "_"); + if (token == NULL) { + free(dup_lang); + return list; + } + + child_list = g_hash_table_lookup(table, token); + if (child_list == NULL) { + free(dup_lang); + return list; + } + + found = g_list_find_custom(g_list_first(child_list), + lang, __compare_langs); + if (found) { + list = g_list_append(list, strdup(lang)); + child_list = g_list_remove_link(child_list, found); + free(found->data); + g_list_free(found); + free(dup_lang); + return list; + } + + found = g_list_find_custom(g_list_first(child_list), + token, __compare_langs); + if (found) { + list = g_list_append(list, strdup(token)); + child_list = g_list_remove_link(child_list, found); + free(found->data); + g_list_free(found); + free(dup_lang); + return list; + } + free(dup_lang); + + child_iter = g_list_first(child_list); + while (child_iter) { + child_lang = (char *)child_iter->data; + child_iter = g_list_next(child_iter); + if (child_lang) { + list = g_list_append(list, strdup(child_lang)); + child_list = g_list_remove(child_list, child_lang); + free(child_lang); + break; + } + } + + return list; +} + +static GList *__split_language(const char *lang) +{ + GList *list = NULL; + char *dup_lang; + char *token; + + dup_lang = strdup(lang); + if (dup_lang == NULL) { + _ERR("Out of memory"); + return NULL; + } + + token = strtok(dup_lang, ":"); + while (token != NULL) { + list = g_list_append(list, strdup(token)); + token = strtok(NULL, ":"); + } + free(dup_lang); + + return list; +} + +static char *__get_language(const char *lang) +{ + GHashTable *table; + GList *list; + GList *lang_list = NULL; + GList *iter; + char *language; + char buf[LINE_MAX] = {'\0'}; + size_t n; + + list = __split_language(lang); + if (list == NULL) + return NULL; + + table = __get_lang_table(); + if (table == NULL) { + g_list_free_full(list, free); + return NULL; + } + + iter = g_list_first(list); + while (iter) { + language = (char *)iter->data; + lang_list = __append_langs(language, lang_list, table); + iter = g_list_next(iter); + } + g_list_free_full(list, free); + g_hash_table_destroy(table); + + iter = g_list_first(lang_list); + while (iter) { + language = (char *)iter->data; + if (language) { + if (buf[0] == '\0') { + snprintf(buf, sizeof(buf), "%s", language); + } else { + n = sizeof(buf) - strlen(buf) - 1; + strncat(buf, ":", n); + n = sizeof(buf) - strlen(buf) - 1; + strncat(buf, language, n); + } + } + iter = g_list_next(iter); + } + g_list_free_full(lang_list, free); + + n = sizeof(buf) - strlen(buf) - 1; + strncat(buf, ":", n); + n = sizeof(buf) - strlen(buf) - 1; + strncat(buf, "en_US:en_GB:en", n); + + return strdup(buf); +} + static void __update_lang(void) { - char language[32]; + char *language; char *lang; char *r; lang = vconf_get_str(VCONFKEY_LANGSET); if (lang) { - snprintf(language, sizeof(language), "%s:en_US:en_GB:en", lang); - setenv("LANGUAGE", language, 1); + /* TODO: Use VCONFKEY_SETAPPL_LANGUAGES key */ + language = __get_language(lang); + if (language) { + _DBG("*****language(%s)", language); + setenv("LANGUAGE", language, 1); + free(language); + } else { + setenv("LANGUAGE", language, 1); + } setenv("LANG", lang, 1); setenv("LC_MESSAGES", lang, 1); r = setlocale(LC_ALL, ""); @@ -356,6 +597,12 @@ static int __set_i18n(const char *domain, const char *dir) if (r == NULL) _ERR("appcore: setlocale() error"); + if (dir) { + if (__locale_dir) + free(__locale_dir); + __locale_dir = strdup(dir); + } + r = bindtextdomain(domain, dir); if (r == NULL) _ERR("appcore: bindtextdomain() error"); @@ -444,6 +691,10 @@ EXPORT_API void appcore_base_fini(void) if (__context.ops.terminate) __context.ops.terminate(__context.data); + if (__locale_dir) { + free(__locale_dir); + __locale_dir = NULL; + } } EXPORT_API int appcore_base_flush_memory(void) -- 2.7.4