Add fallback for multilingual support
[platform/core/appfw/app-core.git] / src / appcore-i18n.c
1 /*
2  *  app-core
3  *
4  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: Jayoun Lee <airjany@samsung.com>, Sewook Park <sewook7.park@samsung.com>, Jaeho Lee <jaeho81.lee@samsung.com>
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  */
21
22
23 #include <locale.h>
24 #include <libintl.h>
25 #include <stdlib.h>
26 #include <errno.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30 #include <linux/limits.h>
31 #include <glib.h>
32 #include <vconf.h>
33
34 #include "appcore-internal.h"
35
36 static int _set;
37 static char locale_dir[PATH_MAX];
38
39 static void __free_children_langs(gpointer data)
40 {
41         GList *list = (GList *)data;
42
43         if (list == NULL)
44                 return;
45
46         g_list_free_full(list, (GDestroyNotify)free);
47 }
48
49 static gint __compare_langs(gconstpointer a, gconstpointer b)
50 {
51         return strcmp(a, b);
52 }
53
54 static GHashTable *__get_lang_table(void)
55 {
56         GHashTable *table;
57         DIR *dp;
58         struct dirent *dentry;
59         char buf[PATH_MAX];
60         struct stat stat_buf;
61         int ret;
62         char *dup_lang;
63         char *token;
64         GList *list;
65
66         if (locale_dir[0] == 0 || locale_dir[0] == '\0')
67                 return NULL;
68
69         table = g_hash_table_new_full(g_str_hash, g_str_equal,
70                         free, __free_children_langs);
71         if (table == NULL) {
72                 _ERR("Out of memory");
73                 return NULL;
74         }
75
76         dp = opendir(locale_dir);
77         if (dp == NULL) {
78                 g_hash_table_destroy(table);
79                 return NULL;
80         }
81
82         while ((dentry = readdir(dp)) != NULL) {
83                 if (!strcmp(dentry->d_name, ".") ||
84                                 !strcmp(dentry->d_name, ".."))
85                         continue;
86
87                 snprintf(buf, sizeof(buf), "%s/%s", locale_dir, dentry->d_name);
88                 ret = stat(buf, &stat_buf);
89                 if (ret != 0 || !S_ISDIR(stat_buf.st_mode))
90                         continue;
91
92                 dup_lang = strdup(dentry->d_name);
93                 if (dup_lang == NULL) {
94                         _ERR("Out of memory");
95                         break;
96                 }
97
98                 token = strtok(dup_lang, "_");
99                 if (token == NULL) {
100                         free(dup_lang);
101                         continue;
102                 }
103
104                 list = (GList *)g_hash_table_lookup(table, token);
105                 if (list == NULL) {
106                         list = g_list_append(list, strdup(dentry->d_name));
107                         g_hash_table_insert(table, strdup(token), list);
108                 } else {
109                         list = g_list_append(list, strdup(dentry->d_name));
110                 }
111                 free(dup_lang);
112         }
113         closedir(dp);
114
115         return table;
116 }
117
118 static GList *__append_langs(const char *lang, GList *list, GHashTable *table)
119 {
120         GList *child_list;
121         GList *child_iter;
122         GList *found;
123         char *child_lang;
124         char *dup_lang;
125         char *token;
126
127         if (lang == NULL)
128                 return list;
129
130         found = g_list_find_custom(g_list_first(list), lang,
131                         __compare_langs);
132         if (found) {
133                 list = g_list_remove_link(list, found);
134                 free(found->data);
135                 g_list_free(found);
136                 list = g_list_append(list, strdup(lang));
137                 return list;
138         }
139
140         dup_lang = strdup(lang);
141         if (dup_lang == NULL)
142                 return list;
143
144         token = strtok(dup_lang, "_");
145         if (token == NULL) {
146                 free(dup_lang);
147                 return list;
148         }
149
150         child_list = g_hash_table_lookup(table, token);
151         if (child_list == NULL) {
152                 free(dup_lang);
153                 return list;
154         }
155
156         found = g_list_find_custom(g_list_first(child_list),
157                         lang, __compare_langs);
158         if (found) {
159                 list = g_list_append(list, strdup(lang));
160                 child_list = g_list_remove_link(child_list, found);
161                 free(found->data);
162                 g_list_free(found);
163                 free(dup_lang);
164                 return list;
165         }
166
167         found = g_list_find_custom(g_list_first(child_list),
168                         token, __compare_langs);
169         if (found) {
170                 list = g_list_append(list, strdup(token));
171                 child_list = g_list_remove_link(child_list, found);
172                 free(found->data);
173                 g_list_free(found);
174                 free(dup_lang);
175                 return list;
176         }
177         free(dup_lang);
178
179         child_iter = g_list_first(child_list);
180         while (child_iter) {
181                 child_lang = (char *)child_iter->data;
182                 child_iter = g_list_next(child_iter);
183                 if (child_lang) {
184                         list = g_list_append(list, strdup(child_lang));
185                         child_list = g_list_remove(child_list, child_lang);
186                         free(child_lang);
187                         break;
188                 }
189         }
190
191         return list;
192 }
193
194 static GList *__split_language(const char *lang)
195 {
196         GList *list = NULL;
197         char *dup_lang;
198         char *token;
199
200         dup_lang = strdup(lang);
201         if (dup_lang == NULL) {
202                 _ERR("Out of memory");
203                 return NULL;
204         }
205
206         token = strtok(dup_lang, ":");
207         while (token != NULL) {
208                 list = g_list_append(list, strdup(token));
209                 token = strtok(NULL, ":");
210         }
211         free(dup_lang);
212
213         return list;
214 }
215
216 static char *__get_language(const char *lang)
217 {
218         GHashTable *table;
219         GList *list;
220         GList *lang_list = NULL;
221         GList *iter;
222         char *language;
223         char buf[LINE_MAX] = {'\0'};
224         size_t n;
225
226         list = __split_language(lang);
227         if (list == NULL)
228                 return NULL;
229
230         table = __get_lang_table();
231         if (table == NULL) {
232                 g_list_free_full(list, free);
233                 return NULL;
234         }
235
236         iter = g_list_first(list);
237         while (iter) {
238                 language = (char *)iter->data;
239                 lang_list = __append_langs(language, lang_list, table);
240                 iter = g_list_next(iter);
241         }
242         g_list_free_full(list, free);
243         g_hash_table_destroy(table);
244
245         iter = g_list_first(lang_list);
246         while (iter) {
247                 language = (char *)iter->data;
248                 if (language) {
249                         if (buf[0] == '\0') {
250                                 snprintf(buf, sizeof(buf), "%s", language);
251                         } else {
252                                 n = sizeof(buf) - strlen(buf) - 1;
253                                 strncat(buf, ":", n);
254                                 n = sizeof(buf) - strlen(buf) - 1;
255                                 strncat(buf, language, n);
256                         }
257                 }
258                 iter = g_list_next(iter);
259         }
260         g_list_free_full(lang_list, free);
261
262         n = sizeof(buf) - strlen(buf) - 1;
263         strncat(buf, ":", n);
264         n = sizeof(buf) - strlen(buf) - 1;
265         strncat(buf, "en_US:en_GB:en", n);
266
267         return strdup(buf);
268 }
269
270 void update_lang(void)
271 {
272         char *language;
273         char *lang;
274         char *r;
275
276         lang = vconf_get_str(VCONFKEY_LANGSET);
277         if (lang) {
278                 /* TODO: Use VCONFKEY_SETAPPL_LANGUAGES key */
279                 language = __get_language(lang);
280                 if (language) {
281                         _DBG("*****language(%s)", language);
282                         setenv("LANGUAGE", language, 1);
283                         free(language);
284                 } else {
285                         setenv("LANGUAGE", lang, 1);
286                 }
287                 setenv("LANG", lang, 1);
288                 setenv("LC_MESSAGES", lang, 1);
289                 r = setlocale(LC_ALL, "");
290                 if (r == NULL) {
291                         r = setlocale(LC_ALL, lang);
292                         if (r != NULL)
293                                 _DBG("*****appcore setlocale=%s\n", r);
294                 }
295                 free(lang);
296         }
297 }
298
299 void update_region(void)
300 {
301         char *region;
302         char *r;
303
304         region = vconf_get_str(VCONFKEY_REGIONFORMAT);
305         if (region) {
306                 setenv("LC_CTYPE", region, 1);
307                 setenv("LC_NUMERIC", region, 1);
308                 setenv("LC_TIME", region, 1);
309                 setenv("LC_COLLATE", region, 1);
310                 setenv("LC_MONETARY", region, 1);
311                 setenv("LC_PAPER", region, 1);
312                 setenv("LC_NAME", region, 1);
313                 setenv("LC_ADDRESS", region, 1);
314                 setenv("LC_TELEPHONE", region, 1);
315                 setenv("LC_MEASUREMENT", region, 1);
316                 setenv("LC_IDENTIFICATION", region, 1);
317                 r = setlocale(LC_ALL, "");
318                 if (r != NULL)
319                         _DBG("*****appcore setlocale=%s\n", r);
320
321                 free(region);
322         }
323 }
324
325 static int __set_i18n(const char *domain, const char *dir)
326 {
327         char *r;
328         char *lan;
329
330         if (domain == NULL) {
331                 errno = EINVAL;
332                 return -1;
333         }
334
335         r = setlocale(LC_ALL, "");
336         /* if locale is not set properly, try again to set as language base */
337         if (r == NULL) {
338                 lan = vconf_get_str(VCONFKEY_LANGSET);
339                 if (lan != NULL) {
340                         r = setlocale(LC_ALL, lan);
341                         _DBG("*****appcore setlocale=%s\n", r);
342                         free(lan);
343                 }
344         }
345         if (r == NULL)
346                 _ERR("appcore: setlocale() error");
347
348         r = bindtextdomain(domain, dir);
349         if (r == NULL)
350                 _ERR("appcore: bindtextdomain() error");
351
352         r = textdomain(domain);
353         if (r == NULL)
354                 _ERR("appcore: textdomain() error");
355
356         return 0;
357 }
358
359 static void __set_locale_dir(const char *dirname)
360 {
361         if (dirname == NULL)
362                 return;
363
364         snprintf(locale_dir, sizeof(locale_dir), "%s", dirname);
365 }
366
367 EXPORT_API int appcore_set_i18n(const char *domainname, const char *dirname)
368 {
369         int r;
370
371         __set_locale_dir(dirname);
372         update_lang();
373         update_region();
374
375         r = __set_i18n(domainname, dirname);
376         if (r == 0)
377                 _set = 1;
378
379         return r;
380 }
381
382 int set_i18n(const char *domainname, const char *dirname)
383 {
384         _retv_if(_set, 0);
385
386         __set_locale_dir(dirname);
387         update_lang();
388         update_region();
389
390         return __set_i18n(domainname, dirname);
391 }
392
393 EXPORT_API int appcore_get_timeformat(enum appcore_time_format *timeformat)
394 {
395         int r;
396
397         if (timeformat == NULL) {
398                 errno = EINVAL;
399                 return -1;
400         }
401
402         r = vconf_get_int(VCONFKEY_REGIONFORMAT_TIME1224, (int *)timeformat);
403
404         if (r < 0) {
405                 *timeformat = APPCORE_TIME_FORMAT_UNKNOWN;
406                 return -1;
407         } else
408                 return 0;
409 }