Blender  V2.93
blt_lang.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2008 Blender Foundation.
17  * All rights reserved.
18  */
19 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #ifndef _WIN32
31 # include <locale.h>
32 #endif
33 
34 #include "RNA_types.h"
35 
36 #include "BLT_lang.h" /* own include */
37 #include "BLT_translation.h"
38 
39 #include "BLI_path_util.h"
40 #include "BLI_string.h"
41 #include "BLI_utildefines.h"
42 
43 #include "BKE_appdir.h"
44 
45 #include "IMB_thumbs.h"
46 
47 #include "DNA_userdef_types.h"
48 
49 #include "MEM_guardedalloc.h"
50 
51 #ifdef WITH_INTERNATIONAL
52 
53 # include "BLI_fileops.h"
54 # include "BLI_linklist.h"
55 
56 # include "boost_locale_wrapper.h"
57 
58 /* Locale options. */
59 static const char **locales = NULL;
60 static int num_locales = 0;
61 static EnumPropertyItem *locales_menu = NULL;
62 static int num_locales_menu = 0;
63 
64 static void free_locales(void)
65 {
66  if (locales) {
67  int idx = num_locales_menu - 1; /* Last item does not need to be freed! */
68  while (idx--) {
69  MEM_freeN((void *)locales_menu[idx].identifier);
70  MEM_freeN((void *)locales_menu[idx].name);
71  MEM_freeN((void *)locales_menu[idx].description); /* Also frees locales's relevant value! */
72  }
73 
74  MEM_freeN((void *)locales);
75  locales = NULL;
76  }
77  if (locales_menu) {
78  MEM_freeN(locales_menu);
79  locales_menu = NULL;
80  }
81  num_locales = num_locales_menu = 0;
82 }
83 
84 static void fill_locales(void)
85 {
86  const char *const languages_path = BKE_appdir_folder_id(BLENDER_DATAFILES, "locale");
87  char languages[FILE_MAX];
88  LinkNode *lines = NULL, *line;
89  char *str;
90  int idx = 0;
91 
92  free_locales();
93 
94  BLI_join_dirfile(languages, FILE_MAX, languages_path, "languages");
95  line = lines = BLI_file_read_as_lines(languages);
96 
97  /* This whole "parsing" code is a bit weak, in that it expects strictly formatted input file...
98  * Should not be a problem, though, as this file is script-generated! */
99 
100  /* First loop to find highest locale ID */
101  while (line) {
102  int t;
103  str = (char *)line->link;
104  if (ELEM(str[0], '#', '\0')) {
105  line = line->next;
106  continue; /* Comment or void... */
107  }
108  t = atoi(str);
109  if (t >= num_locales) {
110  num_locales = t + 1;
111  }
112  num_locales_menu++;
113  line = line->next;
114  }
115  num_locales_menu++; /* The "closing" void item... */
116 
117  /* And now, build locales and locale_menu! */
118  locales_menu = MEM_callocN(num_locales_menu * sizeof(EnumPropertyItem), __func__);
119  line = lines;
120  /* Do not allocate locales with zero-sized mem,
121  * as LOCALE macro uses NULL locales as invalid marker! */
122  if (num_locales > 0) {
123  locales = MEM_callocN(num_locales * sizeof(char *), __func__);
124  while (line) {
125  int id;
126  char *loc, *sep1, *sep2, *sep3;
127 
128  str = (char *)line->link;
129  if (ELEM(str[0], '#', '\0')) {
130  line = line->next;
131  continue;
132  }
133 
134  id = atoi(str);
135  sep1 = strchr(str, ':');
136  if (sep1) {
137  sep1++;
138  sep2 = strchr(sep1, ':');
139  if (sep2) {
140  locales_menu[idx].value = id;
141  locales_menu[idx].icon = 0;
142  locales_menu[idx].name = BLI_strdupn(sep1, sep2 - sep1);
143 
144  sep2++;
145  sep3 = strchr(sep2, ':');
146 
147  if (sep3) {
148  locales_menu[idx].identifier = loc = BLI_strdupn(sep2, sep3 - sep2);
149  }
150  else {
151  locales_menu[idx].identifier = loc = BLI_strdup(sep2);
152  }
153 
154  if (id == 0) {
155  /* The DEFAULT/Automatic item... */
156  if (BLI_strnlen(loc, 2)) {
157  locales[id] = "";
158  /* Keep this tip in sync with the one in rna_userdef
159  * (rna_enum_language_default_items). */
160  locales_menu[idx].description = BLI_strdup(
161  "Automatically choose system's defined language "
162  "if available, or fall-back to English");
163  }
164  /* Menu "label", not to be stored in locales! */
165  else {
166  locales_menu[idx].description = BLI_strdup("");
167  }
168  }
169  else {
170  locales[id] = locales_menu[idx].description = BLI_strdup(loc);
171  }
172  idx++;
173  }
174  }
175 
176  line = line->next;
177  }
178  }
179 
180  /* Add closing item to menu! */
181  locales_menu[idx].identifier = NULL;
182  locales_menu[idx].value = locales_menu[idx].icon = 0;
183  locales_menu[idx].name = locales_menu[idx].description = "";
184 
185  BLI_file_free_lines(lines);
186 }
187 #endif /* WITH_INTERNATIONAL */
188 
190 {
191 #ifdef WITH_INTERNATIONAL
192  return locales_menu;
193 #else
194  return NULL;
195 #endif
196 }
197 
198 void BLT_lang_init(void)
199 {
200 #ifdef WITH_INTERNATIONAL
201  const char *const messagepath = BKE_appdir_folder_id(BLENDER_DATAFILES, "locale");
202 #endif
203 
204  /* Make sure LANG is correct and wouldn't cause #std::runtime_error. */
205 #ifndef _WIN32
206  /* TODO(sergey): This code only ensures LANG is set properly, so later when
207  * Cycles will try to use file system API from boost there will be no runtime
208  * exception generated by #std::locale() which _requires_ having proper LANG
209  * set in the environment.
210  *
211  * Ideally we also need to ensure LC_ALL, LC_MESSAGES and others are also
212  * set to a proper value, but currently it's not a huge deal and doesn't
213  * cause any headache.
214  *
215  * Would also be good to find nicer way to check if LANG is correct.
216  */
217  const char *lang = BLI_getenv("LANG");
218  if (lang != NULL) {
219  char *old_locale = setlocale(LC_ALL, NULL);
220  /* Make a copy so subsequent #setlocale() doesn't interfere. */
221  old_locale = BLI_strdup(old_locale);
222  if (setlocale(LC_ALL, lang) == NULL) {
223  setenv("LANG", "C", 1);
224  printf("Warning: Falling back to the standard locale (\"C\")\n");
225  }
226  setlocale(LC_ALL, old_locale);
227  MEM_freeN(old_locale);
228  }
229 #endif
230 
231 #ifdef WITH_INTERNATIONAL
232  if (messagepath) {
233  bl_locale_init(messagepath, TEXT_DOMAIN_NAME);
234  fill_locales();
235  }
236  else {
237  printf("%s: 'locale' data path for translations not found, continuing\n", __func__);
238  }
239 #else
240 #endif
241 }
242 
243 void BLT_lang_free(void)
244 {
245 #ifdef WITH_INTERNATIONAL
246  free_locales();
247 #else
248 #endif
249 }
250 
251 #ifdef WITH_INTERNATIONAL
252 # define ULANGUAGE \
253  ((U.language >= ULANGUAGE_AUTO && U.language < num_locales) ? U.language : ULANGUAGE_ENGLISH)
254 # define LOCALE(_id) (locales ? locales[(_id)] : "")
255 #endif
256 
257 void BLT_lang_set(const char *str)
258 {
259 #ifdef WITH_INTERNATIONAL
260  int ulang = ULANGUAGE;
261  const char *short_locale = str ? str : LOCALE(ulang);
262  const char *short_locale_utf8 = NULL;
263 
264  /* We want to avoid locales like '.UTF-8'! */
265  if (short_locale[0]) {
266  /* Hooray! Encoding needs to be placed *before* variant! */
267  char *variant = strchr(short_locale, '@');
268  if (variant) {
269  char *locale = BLI_strdupn(short_locale, variant - short_locale);
270  short_locale_utf8 = BLI_sprintfN("%s.UTF-8%s", locale, variant);
271  MEM_freeN(locale);
272  }
273  else {
274  short_locale_utf8 = BLI_sprintfN("%s.UTF-8", short_locale);
275  }
276  bl_locale_set(short_locale_utf8);
277  MEM_freeN((void *)short_locale_utf8);
278  }
279  else {
280  bl_locale_set(short_locale);
281  }
282 #else
283  (void)str;
284 #endif
286 }
287 
288 /* Get the current locale (short code, e.g. es_ES). */
289 const char *BLT_lang_get(void)
290 {
291 #ifdef WITH_INTERNATIONAL
292  if (BLT_translate()) {
293  const char *locale = LOCALE(ULANGUAGE);
294  if (locale[0] == '\0') {
295  /* Default locale, we have to find which one we are actually using! */
296  locale = bl_locale_get();
297  }
298  return locale;
299  }
300  return "en_US"; /* Kind of default locale in Blender when no translation enabled. */
301 #else
302  return "";
303 #endif
304 }
305 
306 #undef LOCALE
307 #undef ULANGUAGE
308 
318 void BLT_lang_locale_explode(const char *locale,
319  char **language,
320  char **country,
321  char **variant,
322  char **language_country,
323  char **language_variant)
324 {
325  char *m1, *m2, *_t = NULL;
326 
327  m1 = strchr(locale, '_');
328  m2 = strchr(locale, '@');
329 
330  if (language || language_variant) {
331  if (m1 || m2) {
332  _t = m1 ? BLI_strdupn(locale, m1 - locale) : BLI_strdupn(locale, m2 - locale);
333  if (language) {
334  *language = _t;
335  }
336  }
337  else if (language) {
338  *language = BLI_strdup(locale);
339  }
340  }
341  if (country) {
342  if (m1) {
343  *country = m2 ? BLI_strdupn(m1 + 1, m2 - (m1 + 1)) : BLI_strdup(m1 + 1);
344  }
345  else {
346  *country = NULL;
347  }
348  }
349  if (variant) {
350  if (m2) {
351  *variant = BLI_strdup(m2 + 1);
352  }
353  else {
354  *variant = NULL;
355  }
356  }
357  if (language_country) {
358  if (m1) {
359  *language_country = m2 ? BLI_strdupn(locale, m2 - locale) : BLI_strdup(locale);
360  }
361  else {
362  *language_country = NULL;
363  }
364  }
365  if (language_variant) {
366  if (m2) {
367  *language_variant = m1 ? BLI_strdupcat(_t, m2) : BLI_strdup(locale);
368  }
369  else {
370  *language_variant = NULL;
371  }
372  }
373  if (_t && !language) {
374  MEM_freeN(_t);
375  }
376 }
377 
378 /* Note that "lang" here is the _output_ display language. We used to restrict
379  * IME for keyboard _input_ language because our multilingual font was only used
380  * when some output languages were selected. That font is used all the time now. */
382 {
383 #ifdef WITH_INPUT_IME
384  return true;
385 #else
386  return false;
387 #endif
388 }
@ BLENDER_DATAFILES
Definition: BKE_appdir.h:78
const char * BKE_appdir_folder_id(const int folder_id, const char *subfolder)
Definition: appdir.c:674
File and directory operations.
void BLI_file_free_lines(struct LinkNode *lines)
Definition: storage.c:639
struct LinkNode * BLI_file_read_as_lines(const char *file) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: storage.c:590
#define FILE_MAX
void BLI_join_dirfile(char *__restrict dst, const size_t maxlen, const char *__restrict dir, const char *__restrict file) ATTR_NONNULL()
Definition: path_util.c:1737
const char * BLI_getenv(const char *env) ATTR_NONNULL(1)
Definition: path_util.c:1313
size_t size_t char * BLI_sprintfN(const char *__restrict format,...) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_PRINTF_FORMAT(1
char * BLI_strdupn(const char *str, const size_t len) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: string.c:54
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL() ATTR_MALLOC
Definition: string.c:70
size_t BLI_strnlen(const char *str, const size_t maxlen) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: string.c:878
char * BLI_strdupcat(const char *__restrict str1, const char *__restrict str2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL() ATTR_MALLOC
Definition: string.c:81
#define ELEM(...)
#define TEXT_DOMAIN_NAME
bool BLT_translate(void)
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble u2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble w _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat w _GL_VOID_RET _GL_VOID GLint GLint GLint w _GL_VOID_RET _GL_VOID GLshort GLshort GLshort w _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble y2 _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat y2 _GL_VOID_RET _GL_VOID GLint GLint GLint y2 _GL_VOID_RET _GL_VOID GLshort GLshort GLshort y2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLuint *buffer _GL_VOID_RET _GL_VOID GLdouble t _GL_VOID_RET _GL_VOID GLfloat t _GL_VOID_RET _GL_VOID GLint t _GL_VOID_RET _GL_VOID GLshort t _GL_VOID_RET _GL_VOID GLdouble t
void IMB_thumb_clear_translations(void)
Definition: thumbs_font.c:44
Read Guarded memory(de)allocation.
void BLT_lang_locale_explode(const char *locale, char **language, char **country, char **variant, char **language_country, char **language_variant)
Definition: blt_lang.c:318
const char * BLT_lang_get(void)
Definition: blt_lang.c:289
bool BLT_lang_is_ime_supported(void)
Definition: blt_lang.c:381
EnumPropertyItem * BLT_lang_RNA_enum_properties(void)
Definition: blt_lang.c:189
void BLT_lang_free(void)
Definition: blt_lang.c:243
void BLT_lang_init(void)
Definition: blt_lang.c:198
void BLT_lang_set(const char *str)
Definition: blt_lang.c:257
const char * bl_locale_get(void)
void bl_locale_set(const char *locale)
void bl_locale_init(const char *_messages_path, const char *_default_domain)
#define str(s)
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:45
const char * identifier
Definition: RNA_types.h:446
const char * name
Definition: RNA_types.h:450
const char * description
Definition: RNA_types.h:452