Fix for [#32992] Switching language does nothing under Windows.
[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
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35 #include "BKE_global.h"
36
37 #include "BLF_api.h"
38 #include "BLF_translation.h" /* own include */
39
40 #ifdef WITH_INTERNATIONAL
41
42 #include <locale.h>
43
44 #include "libintl.h"
45
46 #include "DNA_userdef_types.h"
47
48 #include "RNA_types.h"
49
50 #include "MEM_guardedalloc.h"
51
52 #include "BLI_string.h"
53 #include "BLI_utildefines.h"
54 #include "BLI_path_util.h"
55 #include "BLI_fileops.h"
56 #include "BLI_linklist.h"
57 #include "BLI_string.h"
58
59 #define SYSTEM_ENCODING_DEFAULT "UTF-8"
60 #define FONT_SIZE_DEFAULT 12
61
62 /* Locale options. */
63 static char global_messagepath[1024];
64 static char global_language[32];
65 static char global_encoding_name[32];
66
67 static const char **locales = NULL;
68 static char **long_locales = NULL; /* XXX Temp fix until we get a final solution with modern intl lib under windows! */
69 static int num_locales = 0;
70 static EnumPropertyItem *locales_menu = NULL;
71 static int num_locales_menu = 0;
72
73 #define ULANGUAGE ((U.language >= 0 && U.language < num_locales) ? U.language : 0)
74 #define LOCALE(_id) (locales ? locales[_id] : "")
75 #define LONG_LOCALE(_id) (long_locales ? long_locales[_id] : "")
76
77 static void free_locales(void)
78 {
79         if (locales) {
80                 int idx = num_locales_menu - 1; /* Last item does not need to be freed! */
81                 while (idx--) {
82                         MEM_freeN((void*)locales_menu[idx].identifier);
83                         MEM_freeN((void*)locales_menu[idx].name);
84                         MEM_freeN((void*)locales_menu[idx].description); /* Also frees locales's relevant value! */
85                 }
86
87                 idx = num_locales;
88                 while (idx--) {
89                         if (long_locales[idx]) {
90                                 MEM_freeN(long_locales[idx]);
91                         }
92                 }
93
94                 MEM_freeN(locales);
95                 locales = NULL;
96                 MEM_freeN(long_locales);
97                 long_locales = NULL;
98         }
99         if (locales_menu) {
100                 MEM_freeN(locales_menu);
101                 locales_menu = NULL;
102         }
103         num_locales = num_locales_menu = 0;
104 }
105
106 static void fill_locales(void)
107 {
108         char *languages_path = BLI_get_folder(BLENDER_DATAFILES, "locale");
109         LinkNode *lines = NULL, *line;
110         char *str;
111         int idx = 0;
112
113         free_locales();
114
115         BLI_join_dirfile(languages_path, FILE_MAX, languages_path, "languages");
116         line = lines = BLI_file_read_as_lines(languages_path);
117
118         /* This whole "parsing" code is a bit weak, in that it expects strictly formated input file...
119          * Should not be a problem, though, as this file is script-generated! */
120
121         /* First loop to find highest locale ID */
122         while (line) {
123                 int t;
124                 str = (char*) line->link;
125                 if (str[0] == '#' || str[0] == '\0') {
126                         line = line->next;
127                         continue; /* Comment or void... */
128                 }
129                 t = atoi(str);
130                 if (t >= num_locales)
131                         num_locales = t + 1;
132                 num_locales_menu++;
133                 line = line->next;
134         }
135         num_locales_menu++; /* The "closing" void item... */
136
137         /* And now, buil locales and locale_menu! */
138         locales_menu = MEM_callocN(num_locales_menu * sizeof(EnumPropertyItem), __func__);
139         line = lines;
140         /* Do not allocate locales with zero-sized mem, as LOCALE macro uses NULL locales as invalid marker! */
141         if (num_locales > 0) {
142                 locales = MEM_callocN(num_locales * sizeof(char*), __func__);
143                 long_locales = MEM_callocN(num_locales * sizeof(char*), __func__);
144                 while (line) {
145                         int id;
146                         char *loc, *sep1, *sep2, *sep3;
147
148                         str = (char*) line->link;
149                         if (str[0] == '#' || str[0] == '\0') {
150                                 line = line->next;
151                                 continue;
152                         }
153
154                         id = atoi(str);
155                         sep1 = strchr(str, ':');
156                         if (sep1) {
157                                 sep1++;
158                                 sep2 = strchr(sep1, ':');
159                                 if (sep2) {
160                                         locales_menu[idx].value = id;
161                                         locales_menu[idx].icon = 0;
162                                         locales_menu[idx].name = BLI_strdupn(sep1, sep2 - sep1);
163
164                                         sep2++;
165                                         sep3 = strchr(sep2, ':');
166                                         if (sep3) {
167                                                 locales_menu[idx].identifier = loc = BLI_strdupn(sep2, sep3 - sep2);
168
169                                                 if (id == 0) {
170                                                         /* The DEFAULT item... */
171                                                         if (BLI_strnlen(loc, 2)) {
172                                                                 locales[id] = locales_menu[idx].description = BLI_strdup("");
173                                                                 long_locales[id] = BLI_strdup("");
174                                                         }
175                                                         /* Menu "label", not to be stored in locales! */
176                                                         else {
177                                                                 locales_menu[idx].description = BLI_strdup("");
178                                                         }
179                                                 }
180                                                 else {
181                                                         locales[id] = locales_menu[idx].description = BLI_strdup(loc);
182                                                         long_locales[id] = BLI_strdup(sep3 + 1);
183                                                 }
184                                                 idx++;
185                                         }
186                                 }
187                         }
188
189                         line = line->next;
190                 }
191         }
192
193         /* Add closing item to menu! */
194         locales_menu[idx].identifier = NULL;
195         locales_menu[idx].value = locales_menu[idx].icon = 0;
196         locales_menu[idx].name = locales_menu[idx].description = "";
197
198         BLI_file_free_lines(lines);
199 }
200
201 EnumPropertyItem *BLF_RNA_lang_enum_properties(void)
202 {
203         return locales_menu;
204 }
205
206 void BLF_lang_init(void)
207 {
208         char *messagepath = BLI_get_folder(BLENDER_DATAFILES, "locale");
209
210         BLI_strncpy(global_encoding_name, SYSTEM_ENCODING_DEFAULT, sizeof(global_encoding_name));
211
212         if (messagepath) {
213                 BLI_strncpy(global_messagepath, messagepath, sizeof(global_messagepath));
214                 fill_locales();
215         }
216         else {
217                 printf("%s: 'locale' data path for translations not found, continuing\n", __func__);
218                 global_messagepath[0] = '\0';
219         }
220 }
221
222 void BLF_lang_free(void)
223 {
224         free_locales();
225 }
226
227 /* Get LANG/LANGUAGE environment variable. */
228 static void get_language_variable(const char *varname, char *var, const size_t maxlen)
229 {
230         char *env = getenv(varname);
231
232         if (env) {
233                 char *s;
234
235                 /* Store defaul locale. */
236                 BLI_strncpy(var, env, maxlen);
237
238                 /* Use first language as default. */
239                 s = strchr(var, ':');
240                 if (s)
241                         s[0] = 0;
242         }
243 }
244
245 /* Get language to be used based on locale (which might be empty when using default language) and
246  * LANG environment variable.
247  */
248 static void get_language(const char *locale, const char *lang, char *language, const size_t maxlen)
249 {
250         if (locale[0]) {
251                 BLI_strncpy(language, locale, maxlen);
252         }
253         else {
254                 char *s;
255
256                 BLI_strncpy(language, lang, maxlen);
257
258                 s = strchr(language, '.');
259                 if (s)
260                         s[0] = 0;
261         }
262 }
263
264 /* XXX WARNING!!! In osx somehow the previous function call jumps in this one??? (ton, ppc) */
265 void BLF_lang_set(const char *str)
266 {
267         char *locreturn;
268         int ok = TRUE;
269         int ulang = ULANGUAGE;
270
271         if ((U.transopts & USER_DOTRANSLATE) == 0)
272                 return;
273
274 #if defined(_WIN32) && !defined(FREE_WINDOWS)
275         {
276                 const char *long_locale = str ? str : LONG_LOCALE(ulang);
277                 if (long_locale) {
278                         char *envStr;
279
280                         if (ulang)
281                                 envStr = BLI_sprintfN("LANG=%s", long_locale);
282                         else /* Use system setting. */
283                                 envStr = BLI_sprintfN("LANG=%s", getenv("LANG"));
284
285                         gettext_putenv(envStr);
286                         MEM_freeN(envStr);
287                 }
288
289                 locreturn = setlocale(LC_ALL, long_locale);
290
291                 if (locreturn == NULL) {
292                         if (G.debug & G_DEBUG)
293                                 printf("Could not change locale to %s\n", long_locale);
294
295                         ok = FALSE;
296                 }
297         }
298 #else
299         {
300                 const char *short_locale = str ? str : LOCALE(ulang);
301                 static char default_lang[64] = "\0";
302                 static char default_language[64] = "\0";
303
304                 if (default_lang[0] == 0)
305                         get_language_variable("LANG", default_lang, sizeof(default_lang));
306
307                 if (default_language[0] == 0)
308                         get_language_variable("LANGUAGE", default_language, sizeof(default_language));
309
310                 if (short_locale[0]) {
311                         char *short_locale_utf8 = BLI_sprintfN("%s.UTF-8", short_locale);
312
313                         if (G.debug & G_DEBUG)
314                                 printf("Setting LANG and LANGUAGE to %s\n", short_locale_utf8);
315
316                         locreturn = setlocale(LC_ALL, short_locale_utf8);
317
318                         if (locreturn != NULL) {
319                                 BLI_setenv("LANG", short_locale_utf8);
320                                 BLI_setenv("LANGUAGE", short_locale_utf8);
321                         }
322                         else {
323                                 if (G.debug & G_DEBUG)
324                                         printf("Setting LANG and LANGUAGE to %s\n", short_locale);
325
326                                 locreturn = setlocale(LC_ALL, short_locale);
327
328                                 if (locreturn != NULL) {
329                                         BLI_setenv("LANG", short_locale);
330                                         BLI_setenv("LANGUAGE", short_locale);
331                                 }
332                         }
333
334                         if (G.debug & G_DEBUG && locreturn == NULL)
335                                 printf("Could not change locale to %s nor %s\n", short_locale, short_locale_utf8);
336
337                         MEM_freeN(short_locale_utf8);
338                 }
339                 else {
340                         if (G.debug & G_DEBUG)
341                                 printf("Setting LANG=%s and LANGUAGE=%s\n", default_lang, default_language);
342
343                         BLI_setenv("LANG", default_lang);
344                         BLI_setenv("LANGUAGE", default_language);
345                         locreturn = setlocale(LC_ALL, "");
346
347                         if (G.debug & G_DEBUG && locreturn == NULL)
348                                 printf("Could not reset locale\n");
349                 }
350
351                 if (locreturn == NULL) {
352                         char language[65];
353
354                         get_language(short_locale, default_lang, language, sizeof(language));
355
356                         if (G.debug & G_DEBUG)
357                                 printf("Fallback to LANG=%s and LANGUAGE=%s\n", default_lang, language);
358
359                         /* Fallback to default settings. */
360                         BLI_setenv("LANG", default_lang);
361                         BLI_setenv("LANGUAGE", language);
362
363                         locreturn = setlocale(LC_ALL, "");
364
365                         ok = FALSE;
366                 }
367         }
368 #endif
369
370         if (ok) {
371                 /*printf("Change locale to %s\n", locreturn ); */
372                 BLI_strncpy(global_language, locreturn, sizeof(global_language));
373         }
374
375         setlocale(LC_NUMERIC, "C");
376
377         textdomain(TEXT_DOMAIN_NAME);
378         bindtextdomain(TEXT_DOMAIN_NAME, global_messagepath);
379         bind_textdomain_codeset(TEXT_DOMAIN_NAME, global_encoding_name);
380 }
381
382 const char *BLF_lang_get(void)
383 {
384         int uilang = ULANGUAGE;
385         return LOCALE(uilang);
386 }
387
388 void BLF_lang_encoding(const char *str)
389 {
390         BLI_strncpy(global_encoding_name, str, sizeof(global_encoding_name));
391         /* bind_textdomain_codeset(TEXT_DOMAIN_NAME, encoding_name); */
392 }
393
394 #undef LOCALE
395 #undef ULANGUAGE
396
397 #else /* ! WITH_INTERNATIONAL */
398
399 void BLF_lang_init(void)
400 {
401         return;
402 }
403
404 void BLF_lang_free(void)
405 {
406         return;
407 }
408
409 void BLF_lang_encoding(const char *str)
410 {
411         (void)str;
412         return;
413 }
414
415 void BLF_lang_set(const char *str)
416 {
417         (void)str;
418         return;
419 }
420
421 const char *BLF_lang_get(void)
422 {
423         return "";
424 }
425
426 #endif /* WITH_INTERNATIONAL */