Release version 1.4.8
[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 struct lang_info_s {
37         char *parent;
38         GList *list;
39 };
40
41 static int _set;
42 static char locale_dir[PATH_MAX];
43
44 static void __destroy_lang_info(gpointer data)
45 {
46         struct lang_info_s *info = (struct lang_info_s *)data;
47
48         if (info == NULL)
49                 return;
50
51         if (info->list)
52                 g_list_free_full(info->list, free);
53         if (info->parent)
54                 free(info->parent);
55         free(info);
56 }
57
58 static struct lang_info_s *__create_lang_info(const char *lang)
59 {
60         struct lang_info_s *info;
61
62         info = calloc(1, sizeof(struct lang_info_s));
63         if (info == NULL) {
64                 _ERR("Out of memory");
65                 return NULL;
66         }
67
68         info->parent = strdup(lang);
69         if (info->parent == NULL) {
70                 _ERR("Out of memory");
71                 free(info);
72                 return NULL;
73         }
74
75         return info;
76 }
77
78 static gint __compare_langs(gconstpointer a, gconstpointer b)
79 {
80         if (!a || !b)
81                 return -1;
82
83         return strcmp(a, b);
84 }
85
86 static char *__get_string_before(const char *str, const char *delim)
87 {
88         char *new_str;
89         char *dup_str;
90         char *token;
91
92         dup_str = strdup(str);
93         if (dup_str == NULL)
94                 return NULL;
95
96         token = strtok(dup_str, delim);
97         if (token == NULL) {
98                 free(dup_str);
99                 return NULL;
100         }
101
102         new_str = strdup(token);
103         free(dup_str);
104
105         return new_str;
106 }
107
108 static GHashTable *__get_lang_table(void)
109 {
110         GHashTable *table;
111         DIR *dp;
112         struct dirent *dentry;
113         char buf[PATH_MAX];
114         struct stat stat_buf;
115         int ret;
116         char *parent_lang;
117         struct lang_info_s *info;
118
119         if (locale_dir[0] == 0 || locale_dir[0] == '\0')
120                 return NULL;
121
122         table = g_hash_table_new_full(g_str_hash, g_str_equal,
123                         NULL, __destroy_lang_info);
124         if (table == NULL) {
125                 _ERR("Out of memory");
126                 return NULL;
127         }
128
129         dp = opendir(locale_dir);
130         if (dp == NULL) {
131                 g_hash_table_destroy(table);
132                 return NULL;
133         }
134
135         while ((dentry = readdir(dp)) != NULL) {
136                 if (!strcmp(dentry->d_name, ".") ||
137                                 !strcmp(dentry->d_name, ".."))
138                         continue;
139
140                 snprintf(buf, sizeof(buf), "%s/%s", locale_dir, dentry->d_name);
141                 ret = stat(buf, &stat_buf);
142                 if (ret != 0 || !S_ISDIR(stat_buf.st_mode))
143                         continue;
144
145                 parent_lang = __get_string_before(dentry->d_name, "_");
146                 if (parent_lang == NULL) {
147                         _ERR("Out of memory");
148                         break;
149                 }
150
151                 info = g_hash_table_lookup(table, parent_lang);
152                 if (info == NULL) {
153                         info = __create_lang_info(parent_lang);
154                         if (info == NULL) {
155                                 free(parent_lang);
156                                 break;
157                         }
158                         g_hash_table_insert(table, info->parent, info);
159                 }
160                 info->list = g_list_append(info->list, strdup(dentry->d_name));
161                 free(parent_lang);
162         }
163         closedir(dp);
164
165         return table;
166 }
167
168 static GList *__append_langs(const char *lang, GList *list, GHashTable *table)
169 {
170         struct lang_info_s *info;
171         GList *found;
172         char *parent_lang = NULL;
173         char *extract_lang;
174
175         if (lang == NULL)
176                 return list;
177
178         extract_lang = __get_string_before(lang, ".");
179         if (extract_lang == NULL)
180                 return list;
181
182         found = g_list_find_custom(list, extract_lang, __compare_langs);
183         if (found) {
184                 list = g_list_remove_link(list, found);
185                 list = g_list_concat(list, found);
186                 goto end;
187         }
188
189         parent_lang = __get_string_before(extract_lang, "_");
190         if (parent_lang == NULL)
191                 goto end;
192
193         info = g_hash_table_lookup(table, parent_lang);
194         if (info == NULL)
195                 goto end;
196
197         found = g_list_find_custom(info->list, extract_lang, __compare_langs);
198         if (found) {
199                 info->list = g_list_remove_link(info->list, found);
200                 list = g_list_concat(list, found);
201                 goto end;
202         }
203
204         found = g_list_find_custom(info->list, parent_lang, __compare_langs);
205         if (found) {
206                 info->list = g_list_remove_link(info->list, found);
207                 list = g_list_concat(list, found);
208                 goto end;
209         }
210
211         found = g_list_first(info->list);
212         if (found) {
213                 info->list = g_list_remove_link(info->list, found);
214                 list = g_list_concat(list, found);
215         }
216
217 end:
218         if (extract_lang)
219                 free(extract_lang);
220         if (parent_lang)
221                 free(parent_lang);
222
223         return list;
224 }
225
226 static GList *__split_language(const char *lang)
227 {
228         GList *list = NULL;
229         char *dup_lang;
230         char *token;
231
232         dup_lang = strdup(lang);
233         if (dup_lang == NULL) {
234                 _ERR("Out of memory");
235                 return NULL;
236         }
237
238         token = strtok(dup_lang, ":");
239         while (token != NULL) {
240                 list = g_list_append(list, strdup(token));
241                 token = strtok(NULL, ":");
242         }
243         free(dup_lang);
244
245         return list;
246 }
247
248 static GList *__append_default_langs(GList *list)
249 {
250         const char *langs[] = {"en_US", "en_GB", "en"};
251         unsigned int i;
252         GList *found;
253
254         for (i = 0; i < (sizeof(langs) / sizeof(langs[0])); i++) {
255                 found = g_list_find_custom(list, langs[i], __compare_langs);
256                 if (found == NULL)
257                         list = g_list_append(list, strdup(langs[i]));
258         }
259
260         return list;
261 }
262
263 static char *__get_language(const char *lang)
264 {
265         GHashTable *table;
266         GList *list;
267         GList *lang_list = NULL;
268         GList *iter;
269         char *language;
270         char buf[LINE_MAX] = {'\0'};
271         size_t n;
272
273         list = __split_language(lang);
274         if (list == NULL)
275                 return NULL;
276
277         table = __get_lang_table();
278         if (table == NULL) {
279                 g_list_free_full(list, free);
280                 return NULL;
281         }
282
283         iter = g_list_first(list);
284         while (iter) {
285                 language = (char *)iter->data;
286                 lang_list = __append_langs(language, lang_list, table);
287                 iter = g_list_next(iter);
288         }
289         g_list_free_full(list, free);
290         g_hash_table_destroy(table);
291
292         lang_list = __append_default_langs(lang_list);
293         iter = g_list_first(lang_list);
294         while (iter) {
295                 language = (char *)iter->data;
296                 if (language) {
297                         if (buf[0] == '\0') {
298                                 snprintf(buf, sizeof(buf), "%s", language);
299                         } else {
300                                 n = sizeof(buf) - strlen(buf) - 1;
301                                 strncat(buf, ":", n);
302                                 n = sizeof(buf) - strlen(buf) - 1;
303                                 strncat(buf, language, n);
304                         }
305                 }
306                 iter = g_list_next(iter);
307         }
308         g_list_free_full(lang_list, free);
309
310         return strdup(buf);
311 }
312
313 void update_lang(void)
314 {
315         char *language;
316         char *lang;
317         char *r;
318
319         lang = vconf_get_str(VCONFKEY_LANGSET);
320         if (lang) {
321                 /* TODO: Use VCONFKEY_SETAPPL_LANGUAGES key */
322                 language = __get_language(lang);
323                 if (language) {
324                         _DBG("*****language(%s)", language);
325                         setenv("LANGUAGE", language, 1);
326                         free(language);
327                 } else {
328                         setenv("LANGUAGE", lang, 1);
329                 }
330                 setenv("LANG", lang, 1);
331                 setenv("LC_MESSAGES", lang, 1);
332                 r = setlocale(LC_ALL, "");
333                 if (r == NULL) {
334                         r = setlocale(LC_ALL, lang);
335                         if (r != NULL)
336                                 _DBG("*****appcore setlocale=%s\n", r);
337                 }
338                 free(lang);
339         }
340 }
341
342 void update_region(void)
343 {
344         char *region;
345         char *r;
346
347         region = vconf_get_str(VCONFKEY_REGIONFORMAT);
348         if (region) {
349                 setenv("LC_CTYPE", region, 1);
350                 setenv("LC_NUMERIC", region, 1);
351                 setenv("LC_TIME", region, 1);
352                 setenv("LC_COLLATE", region, 1);
353                 setenv("LC_MONETARY", region, 1);
354                 setenv("LC_PAPER", region, 1);
355                 setenv("LC_NAME", region, 1);
356                 setenv("LC_ADDRESS", region, 1);
357                 setenv("LC_TELEPHONE", region, 1);
358                 setenv("LC_MEASUREMENT", region, 1);
359                 setenv("LC_IDENTIFICATION", region, 1);
360                 r = setlocale(LC_ALL, "");
361                 if (r != NULL)
362                         _DBG("*****appcore setlocale=%s\n", r);
363
364                 free(region);
365         }
366 }
367
368 static int __set_i18n(const char *domain, const char *dir)
369 {
370         char *r;
371         char *lan;
372
373         if (domain == NULL) {
374                 errno = EINVAL;
375                 return -1;
376         }
377
378         r = setlocale(LC_ALL, "");
379         /* if locale is not set properly, try again to set as language base */
380         if (r == NULL) {
381                 lan = vconf_get_str(VCONFKEY_LANGSET);
382                 if (lan != NULL) {
383                         r = setlocale(LC_ALL, lan);
384                         _DBG("*****appcore setlocale=%s\n", r);
385                         free(lan);
386                 }
387         }
388         if (r == NULL)
389                 _ERR("appcore: setlocale() error");
390
391         r = bindtextdomain(domain, dir);
392         if (r == NULL)
393                 _ERR("appcore: bindtextdomain() error");
394
395         r = textdomain(domain);
396         if (r == NULL)
397                 _ERR("appcore: textdomain() error");
398
399         return 0;
400 }
401
402 static void __set_locale_dir(const char *dirname)
403 {
404         if (dirname == NULL)
405                 return;
406
407         snprintf(locale_dir, sizeof(locale_dir), "%s", dirname);
408 }
409
410 EXPORT_API int appcore_set_i18n(const char *domainname, const char *dirname)
411 {
412         int r;
413
414         __set_locale_dir(dirname);
415         update_lang();
416         update_region();
417
418         r = __set_i18n(domainname, dirname);
419         if (r == 0)
420                 _set = 1;
421
422         return r;
423 }
424
425 int set_i18n(const char *domainname, const char *dirname)
426 {
427         _retv_if(_set, 0);
428
429         __set_locale_dir(dirname);
430         update_lang();
431         update_region();
432
433         return __set_i18n(domainname, dirname);
434 }
435
436 EXPORT_API int appcore_get_timeformat(enum appcore_time_format *timeformat)
437 {
438         int r;
439
440         if (timeformat == NULL) {
441                 errno = EINVAL;
442                 return -1;
443         }
444
445         r = vconf_get_int(VCONFKEY_REGIONFORMAT_TIME1224, (int *)timeformat);
446
447         if (r < 0) {
448                 *timeformat = APPCORE_TIME_FORMAT_UNKNOWN;
449                 return -1;
450         } else
451                 return 0;
452 }