patch [#34103]
[blender.git] / source / blender / blenfont / intern / blf_lang.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version. 
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2008 Blender Foundation.
19  * All rights reserved.
20  * 
21  * Contributor(s): Blender Foundation.
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/blenfont/intern/blf_lang.c
27  *  \ingroup blf
28  */
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33
34 #include "RNA_types.h"
35
36 #include "BLF_translation.h" /* own include */
37
38 #include "BLI_fileops.h"
39 #include "BLI_linklist.h"
40 #include "BLI_path_util.h"
41 #include "BLI_string.h"
42
43 #include "BKE_global.h"
44
45 #include "DNA_userdef_types.h"
46
47 #include "MEM_guardedalloc.h"
48
49 #ifdef WITH_INTERNATIONAL
50
51 #include "boost_locale_wrapper.h"
52
53 /* Locale options. */
54 static const char **locales = NULL;
55 static int num_locales = 0;
56 static EnumPropertyItem *locales_menu = NULL;
57 static int num_locales_menu = 0;
58
59 static void free_locales(void)
60 {
61         if (locales) {
62                 int idx = num_locales_menu - 1; /* Last item does not need to be freed! */
63                 while (idx--) {
64                         MEM_freeN((void *)locales_menu[idx].identifier);
65                         MEM_freeN((void *)locales_menu[idx].name);
66                         MEM_freeN((void *)locales_menu[idx].description); /* Also frees locales's relevant value! */
67                 }
68
69                 MEM_freeN(locales);
70                 locales = NULL;
71         }
72         if (locales_menu) {
73                 MEM_freeN(locales_menu);
74                 locales_menu = NULL;
75         }
76         num_locales = num_locales_menu = 0;
77 }
78
79 static void fill_locales(void)
80 {
81         const char * const languages_path = BLI_get_folder(BLENDER_DATAFILES, "locale");
82         char languages[FILE_MAX];
83         LinkNode *lines = NULL, *line;
84         char *str;
85         int idx = 0;
86
87         free_locales();
88
89         BLI_join_dirfile(languages, FILE_MAX, languages_path, "languages");
90         line = lines = BLI_file_read_as_lines(languages);
91
92         /* This whole "parsing" code is a bit weak, in that it expects strictly formated input file...
93          * Should not be a problem, though, as this file is script-generated! */
94
95         /* First loop to find highest locale ID */
96         while (line) {
97                 int t;
98                 str = (char *)line->link;
99                 if (str[0] == '#' || str[0] == '\0') {
100                         line = line->next;
101                         continue; /* Comment or void... */
102                 }
103                 t = atoi(str);
104                 if (t >= num_locales)
105                         num_locales = t + 1;
106                 num_locales_menu++;
107                 line = line->next;
108         }
109         num_locales_menu++; /* The "closing" void item... */
110
111         /* And now, build locales and locale_menu! */
112         locales_menu = MEM_callocN(num_locales_menu * sizeof(EnumPropertyItem), __func__);
113         line = lines;
114         /* Do not allocate locales with zero-sized mem, as LOCALE macro uses NULL locales as invalid marker! */
115         if (num_locales > 0) {
116                 locales = MEM_callocN(num_locales * sizeof(char *), __func__);
117                 while (line) {
118                         int id;
119                         char *loc, *sep1, *sep2, *sep3;
120
121                         str = (char *)line->link;
122                         if (str[0] == '#' || str[0] == '\0') {
123                                 line = line->next;
124                                 continue;
125                         }
126
127                         id = atoi(str);
128                         sep1 = strchr(str, ':');
129                         if (sep1) {
130                                 sep1++;
131                                 sep2 = strchr(sep1, ':');
132                                 if (sep2) {
133                                         locales_menu[idx].value = id;
134                                         locales_menu[idx].icon = 0;
135                                         locales_menu[idx].name = BLI_strdupn(sep1, sep2 - sep1);
136
137                                         sep2++;
138                                         sep3 = strchr(sep2, ':');
139
140                                         if (sep3) {
141                                                 locales_menu[idx].identifier = loc = BLI_strdupn(sep2, sep3 - sep2);
142                                         }
143                                         else {
144                                                 locales_menu[idx].identifier = loc = BLI_strdup(sep2);
145                                         }
146
147                                         if (id == 0) {
148                                                 /* The DEFAULT item... */
149                                                 if (BLI_strnlen(loc, 2)) {
150                                                         locales[id] = locales_menu[idx].description = BLI_strdup("");
151                                                 }
152                                                 /* Menu "label", not to be stored in locales! */
153                                                 else {
154                                                         locales_menu[idx].description = BLI_strdup("");
155                                                 }
156                                         }
157                                         else {
158                                                 locales[id] = locales_menu[idx].description = BLI_strdup(loc);
159                                         }
160                                         idx++;
161                                 }
162                         }
163
164                         line = line->next;
165                 }
166         }
167
168         /* Add closing item to menu! */
169         locales_menu[idx].identifier = NULL;
170         locales_menu[idx].value = locales_menu[idx].icon = 0;
171         locales_menu[idx].name = locales_menu[idx].description = "";
172
173         BLI_file_free_lines(lines);
174 }
175 #endif  /* WITH_INTERNATIONAL */
176
177 EnumPropertyItem *BLF_RNA_lang_enum_properties(void)
178 {
179 #ifdef WITH_INTERNATIONAL
180         return locales_menu;
181 #else
182         return NULL;
183 #endif
184 }
185
186 void BLF_lang_init(void)
187 {
188 #ifdef WITH_INTERNATIONAL
189         const char * const messagepath = BLI_get_folder(BLENDER_DATAFILES, "locale");
190
191         if (messagepath) {
192                 bl_locale_init(messagepath, TEXT_DOMAIN_NAME);
193                 fill_locales();
194         }
195         else {
196                 printf("%s: 'locale' data path for translations not found, continuing\n", __func__);
197         }
198 #else
199 #endif
200 }
201
202 void BLF_lang_free(void)
203 {
204 #ifdef WITH_INTERNATIONAL
205         free_locales();
206 #else
207 #endif
208 }
209
210 #define ULANGUAGE ((U.language >= 0 && U.language < num_locales) ? U.language : 0)
211 #define LOCALE(_id) (locales ? locales[(_id)] : "")
212
213 void BLF_lang_set(const char *str)
214 {
215 #ifdef WITH_INTERNATIONAL
216         int ulang = ULANGUAGE;
217         const char *short_locale = str ? str : LOCALE(ulang);
218         const char *short_locale_utf8 = NULL;
219
220         if ((U.transopts & USER_DOTRANSLATE) == 0)
221                 return;
222
223         /* We want to avoid locales like '.UTF-8'! */
224         if (short_locale[0]) {
225                 /* Hurrey! encoding needs to be placed *before* variant! */
226                 char *variant = strchr(short_locale, '@');
227                 if (variant) {
228                         char *locale = BLI_strdupn(short_locale, variant - short_locale);
229                         short_locale_utf8 = BLI_sprintfN("%s.UTF-8%s", locale, variant);
230                         MEM_freeN(locale);
231                 }
232                 else {
233                         short_locale_utf8 = BLI_sprintfN("%s.UTF-8", short_locale);
234                 }
235         }
236         else {
237                 short_locale_utf8 = short_locale;
238         }
239
240         bl_locale_set(short_locale_utf8);
241
242         if (short_locale[0]) {
243                 MEM_freeN((void *)short_locale_utf8);
244         }
245 #else
246         (void)str;
247 #endif
248 }
249
250 /* Get the current locale (short code, e.g. es_ES). */
251 const char *BLF_lang_get(void)
252 {
253 #ifdef WITH_INTERNATIONAL
254         const char *locale = LOCALE(ULANGUAGE);
255         if (locale[0] == '\0') {
256                 /* Default locale, we have to find which one we are actually using! */
257                 locale = bl_locale_get();
258         }
259         return locale;
260 #else
261         return "";
262 #endif
263 }
264
265 #undef LOCALE
266 #undef ULANGUAGE
267
268 /* Get locale's elements (if relevant pointer is not NULL and element actually exists, e.g. if there is no variant,
269  * *variant and *language_variant will always be NULL).
270  * Non-null elements are always MEM_mallocN'ed, it's the caller's responsibility to free them.
271  * NOTE: Keep that one always available, you never know, may become useful even in no-WITH_INTERNATIONAL context...
272  */
273 void BLF_locale_explode(const char *locale, char **language, char **country, char **variant,
274                         char **language_country, char **language_variant)
275 {
276         char *m1, *m2, *_t = NULL;
277
278         m1 = strchr(locale, '_');
279         m2 = strchr(locale, '@');
280
281         if (language || language_variant) {
282                 if (m1 || m2) {
283                         _t = m1 ? BLI_strdupn(locale, m1 - locale) : BLI_strdupn(locale, m2 - locale);
284                         if (language)
285                                 *language = _t;
286                 }
287                 else if (language) {
288                         *language = BLI_strdup(locale);
289                 }
290         }
291         if (country) {
292                 if (m1)
293                         *country = m2 ? BLI_strdupn(m1 + 1, m2 - (m1 + 1)) : BLI_strdup(m1 + 1);
294                 else
295                         *country = NULL;
296         }
297         if (variant) {
298                 if (m2)
299                         *variant = BLI_strdup(m2 + 1);
300                 else
301                         *variant = NULL;
302         }
303         if (language_country) {
304                 if (m1)
305                         *language_country = m2 ? BLI_strdupn(locale, m2 - locale) : BLI_strdup(locale);
306                 else
307                         *language_country = NULL;
308         }
309         if (language_variant) {
310                 if (m2)
311                         *language_variant = m1 ? BLI_strdupcat(_t, m2) : BLI_strdup(locale);
312                 else
313                         *language_variant = NULL;
314         }
315         if (_t && !language) {
316                 MEM_freeN(_t);
317         }
318 }