Add fallback for multilingual support 28/113328/10
authorHwankyu Jhun <h.jhun@samsung.com>
Thu, 2 Feb 2017 09:06:53 +0000 (18:06 +0900)
committerHwankyu Jhun <h.jhun@samsung.com>
Mon, 20 Feb 2017 11:25:33 +0000 (20:25 +0900)
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 <h.jhun@samsung.com>
src/appcore-i18n.c
src/base/appcore_base.c

index 3cbefcc..94289e6 100644 (file)
 #include <libintl.h>
 #include <stdlib.h>
 #include <errno.h>
-
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <linux/limits.h>
+#include <glib.h>
 #include <vconf.h>
 
 #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();
 
index 7c1b826..2f7298b 100644 (file)
@@ -22,6 +22,7 @@
 #include <string.h>
 #include <stdlib.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <unistd.h>
 #include <malloc.h>
 #include <locale.h>
@@ -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)