Blender  V3.3
blt_lang.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2008 Blender Foundation. All rights reserved. */
3 
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 
14 #ifndef _WIN32
15 # include <locale.h>
16 #endif
17 
18 #include "RNA_types.h"
19 
20 #include "BLT_lang.h" /* own include */
21 #include "BLT_translation.h"
22 
23 #include "BLI_path_util.h"
24 #include "BLI_string.h"
25 #include "BLI_utildefines.h"
26 
27 #include "BKE_appdir.h"
28 
29 #include "IMB_thumbs.h"
30 
31 #include "DNA_userdef_types.h"
32 
33 #include "MEM_guardedalloc.h"
34 
35 #ifdef WITH_INTERNATIONAL
36 
37 # include "BLI_fileops.h"
38 # include "BLI_linklist.h"
39 
40 # include "boost_locale_wrapper.h"
41 
42 /* Locale options. */
43 static const char **locales = NULL;
44 static int num_locales = 0;
45 static EnumPropertyItem *locales_menu = NULL;
46 static int num_locales_menu = 0;
47 
48 static void free_locales(void)
49 {
50  if (locales) {
51  int idx = num_locales_menu - 1; /* Last item does not need to be freed! */
52  while (idx--) {
53  MEM_freeN((void *)locales_menu[idx].identifier);
54  MEM_freeN((void *)locales_menu[idx].name);
55  MEM_freeN((void *)locales_menu[idx].description); /* Also frees locales's relevant value! */
56  }
57 
58  MEM_freeN((void *)locales);
59  locales = NULL;
60  }
61  MEM_SAFE_FREE(locales_menu);
62  num_locales = num_locales_menu = 0;
63 }
64 
65 static void fill_locales(void)
66 {
67  const char *const languages_path = BKE_appdir_folder_id(BLENDER_DATAFILES, "locale");
68  char languages[FILE_MAX];
69  LinkNode *lines = NULL, *line;
70  char *str;
71  int idx = 0;
72 
73  free_locales();
74 
75  BLI_join_dirfile(languages, FILE_MAX, languages_path, "languages");
76  line = lines = BLI_file_read_as_lines(languages);
77 
78  /* This whole "parsing" code is a bit weak, in that it expects strictly formatted input file...
79  * Should not be a problem, though, as this file is script-generated! */
80 
81  /* First loop to find highest locale ID */
82  while (line) {
83  int t;
84  str = (char *)line->link;
85  if (ELEM(str[0], '#', '\0')) {
86  line = line->next;
87  continue; /* Comment or void... */
88  }
89  t = atoi(str);
90  if (t >= num_locales) {
91  num_locales = t + 1;
92  }
93  num_locales_menu++;
94  line = line->next;
95  }
96  num_locales_menu++; /* The "closing" void item... */
97 
98  /* And now, build locales and locale_menu! */
99  locales_menu = MEM_callocN(num_locales_menu * sizeof(EnumPropertyItem), __func__);
100  line = lines;
101  /* Do not allocate locales with zero-sized mem,
102  * as LOCALE macro uses NULL locales as invalid marker! */
103  if (num_locales > 0) {
104  locales = MEM_callocN(num_locales * sizeof(char *), __func__);
105  while (line) {
106  int id;
107  char *loc, *sep1, *sep2, *sep3;
108 
109  str = (char *)line->link;
110  if (ELEM(str[0], '#', '\0')) {
111  line = line->next;
112  continue;
113  }
114 
115  id = atoi(str);
116  sep1 = strchr(str, ':');
117  if (sep1) {
118  sep1++;
119  sep2 = strchr(sep1, ':');
120  if (sep2) {
121  locales_menu[idx].value = id;
122  locales_menu[idx].icon = 0;
123  locales_menu[idx].name = BLI_strdupn(sep1, sep2 - sep1);
124 
125  sep2++;
126  sep3 = strchr(sep2, ':');
127 
128  if (sep3) {
129  locales_menu[idx].identifier = loc = BLI_strdupn(sep2, sep3 - sep2);
130  }
131  else {
132  locales_menu[idx].identifier = loc = BLI_strdup(sep2);
133  }
134 
135  if (id == 0) {
136  /* The DEFAULT/Automatic item... */
137  if (BLI_strnlen(loc, 2)) {
138  locales[id] = "";
139  /* Keep this tip in sync with the one in rna_userdef
140  * (rna_enum_language_default_items). */
141  locales_menu[idx].description = BLI_strdup(
142  "Automatically choose system's defined language "
143  "if available, or fall-back to English");
144  }
145  /* Menu "label", not to be stored in locales! */
146  else {
147  locales_menu[idx].description = BLI_strdup("");
148  }
149  }
150  else {
151  locales[id] = locales_menu[idx].description = BLI_strdup(loc);
152  }
153  idx++;
154  }
155  }
156 
157  line = line->next;
158  }
159  }
160 
161  /* Add closing item to menu! */
162  locales_menu[idx].identifier = NULL;
163  locales_menu[idx].value = locales_menu[idx].icon = 0;
164  locales_menu[idx].name = locales_menu[idx].description = "";
165 
166  BLI_file_free_lines(lines);
167 }
168 #endif /* WITH_INTERNATIONAL */
169 
171 {
172 #ifdef WITH_INTERNATIONAL
173  return locales_menu;
174 #else
175  return NULL;
176 #endif
177 }
178 
179 void BLT_lang_init(void)
180 {
181 #ifdef WITH_INTERNATIONAL
182  const char *const messagepath = BKE_appdir_folder_id(BLENDER_DATAFILES, "locale");
183 #endif
184 
185  /* Make sure LANG is correct and wouldn't cause #std::runtime_error. */
186 #ifndef _WIN32
187  /* TODO(sergey): This code only ensures LANG is set properly, so later when
188  * Cycles will try to use file system API from boost there will be no runtime
189  * exception generated by #std::locale() which _requires_ having proper LANG
190  * set in the environment.
191  *
192  * Ideally we also need to ensure LC_ALL, LC_MESSAGES and others are also
193  * set to a proper value, but currently it's not a huge deal and doesn't
194  * cause any headache.
195  *
196  * Would also be good to find nicer way to check if LANG is correct.
197  */
198  const char *lang = BLI_getenv("LANG");
199  if (lang != NULL) {
200  char *old_locale = setlocale(LC_ALL, NULL);
201  /* Make a copy so subsequent #setlocale() doesn't interfere. */
202  old_locale = BLI_strdup(old_locale);
203  if (setlocale(LC_ALL, lang) == NULL) {
204  setenv("LANG", "C", 1);
205  printf("Warning: Falling back to the standard locale (\"C\")\n");
206  }
207  setlocale(LC_ALL, old_locale);
208  MEM_freeN(old_locale);
209  }
210 #endif
211 
212 #ifdef WITH_INTERNATIONAL
213  if (messagepath) {
214  bl_locale_init(messagepath, TEXT_DOMAIN_NAME);
215  fill_locales();
216  }
217  else {
218  printf("%s: 'locale' data path for translations not found, continuing\n", __func__);
219  }
220 #else
221 #endif
222 }
223 
224 void BLT_lang_free(void)
225 {
226 #ifdef WITH_INTERNATIONAL
227  free_locales();
228 #else
229 #endif
230 }
231 
232 #ifdef WITH_INTERNATIONAL
233 # define ULANGUAGE \
234  ((U.language >= ULANGUAGE_AUTO && U.language < num_locales) ? U.language : ULANGUAGE_ENGLISH)
235 # define LOCALE(_id) (locales ? locales[(_id)] : "")
236 #endif
237 
238 void BLT_lang_set(const char *str)
239 {
240 #ifdef WITH_INTERNATIONAL
241  int ulang = ULANGUAGE;
242  const char *short_locale = str ? str : LOCALE(ulang);
243  const char *short_locale_utf8 = NULL;
244 
245  /* We want to avoid locales like '.UTF-8'! */
246  if (short_locale[0]) {
247  /* Hooray! Encoding needs to be placed *before* variant! */
248  char *variant = strchr(short_locale, '@');
249  if (variant) {
250  char *locale = BLI_strdupn(short_locale, variant - short_locale);
251  short_locale_utf8 = BLI_sprintfN("%s.UTF-8%s", locale, variant);
252  MEM_freeN(locale);
253  }
254  else {
255  short_locale_utf8 = BLI_sprintfN("%s.UTF-8", short_locale);
256  }
257  bl_locale_set(short_locale_utf8);
258  MEM_freeN((void *)short_locale_utf8);
259  }
260  else {
261  bl_locale_set(short_locale);
262  }
263 #else
264  (void)str;
265 #endif
267 }
268 
269 const char *BLT_lang_get(void)
270 {
271 #ifdef WITH_INTERNATIONAL
272  if (BLT_translate()) {
273  const char *locale = LOCALE(ULANGUAGE);
274  if (locale[0] == '\0') {
275  /* Default locale, we have to find which one we are actually using! */
276  locale = bl_locale_get();
277  }
278  return locale;
279  }
280  return "en_US"; /* Kind of default locale in Blender when no translation enabled. */
281 #else
282  return "";
283 #endif
284 }
285 
286 #undef LOCALE
287 #undef ULANGUAGE
288 
289 void BLT_lang_locale_explode(const char *locale,
290  char **language,
291  char **country,
292  char **variant,
293  char **language_country,
294  char **language_variant)
295 {
296  char *m1, *m2, *_t = NULL;
297 
298  m1 = strchr(locale, '_');
299  m2 = strchr(locale, '@');
300 
301  if (language || language_variant) {
302  if (m1 || m2) {
303  _t = m1 ? BLI_strdupn(locale, m1 - locale) : BLI_strdupn(locale, m2 - locale);
304  if (language) {
305  *language = _t;
306  }
307  }
308  else if (language) {
309  *language = BLI_strdup(locale);
310  }
311  }
312  if (country) {
313  if (m1) {
314  *country = m2 ? BLI_strdupn(m1 + 1, m2 - (m1 + 1)) : BLI_strdup(m1 + 1);
315  }
316  else {
317  *country = NULL;
318  }
319  }
320  if (variant) {
321  if (m2) {
322  *variant = BLI_strdup(m2 + 1);
323  }
324  else {
325  *variant = NULL;
326  }
327  }
328  if (language_country) {
329  if (m1) {
330  *language_country = m2 ? BLI_strdupn(locale, m2 - locale) : BLI_strdup(locale);
331  }
332  else {
333  *language_country = NULL;
334  }
335  }
336  if (language_variant) {
337  if (m2) {
338  *language_variant = m1 ? BLI_strdupcat(_t, m2) : BLI_strdup(locale);
339  }
340  else {
341  *language_variant = NULL;
342  }
343  }
344  if (_t && !language) {
345  MEM_freeN(_t);
346  }
347 }
const char * BKE_appdir_folder_id(int folder_id, const char *subfolder)
Definition: appdir.c:672
@ BLENDER_DATAFILES
Definition: BKE_appdir.h:154
File and directory operations.
void BLI_file_free_lines(struct LinkNode *lines)
Definition: storage.c:564
struct LinkNode * BLI_file_read_as_lines(const char *file) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: storage.c:518
#define FILE_MAX
const char * BLI_getenv(const char *env) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
Definition: path_util.c:1168
void BLI_join_dirfile(char *__restrict dst, size_t maxlen, const char *__restrict dir, const char *__restrict file) ATTR_NONNULL()
Definition: path_util.c:1531
size_t size_t char * BLI_sprintfN(const char *__restrict format,...) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_PRINTF_FORMAT(1
size_t BLI_strnlen(const char *str, size_t maxlen) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: string.c:899
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL() ATTR_MALLOC
Definition: string.c:42
char * BLI_strdupn(const char *str, size_t len) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: string.c:33
char * BLI_strdupcat(const char *__restrict str1, const char *__restrict str2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL() ATTR_MALLOC
Definition: string.c:47
#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:30
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
void BLT_lang_locale_explode(const char *locale, char **language, char **country, char **variant, char **language_country, char **language_variant)
Definition: blt_lang.c:289
const char * BLT_lang_get(void)
Definition: blt_lang.c:269
EnumPropertyItem * BLT_lang_RNA_enum_properties(void)
Definition: blt_lang.c:170
void BLT_lang_free(void)
Definition: blt_lang.c:224
void BLT_lang_init(void)
Definition: blt_lang.c:179
void BLT_lang_set(const char *str)
Definition: blt_lang.c:238
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)
SyclQueue void void size_t num_bytes void
#define str(s)
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:31
const char * identifier
Definition: RNA_types.h:461
const char * name
Definition: RNA_types.h:465
const char * description
Definition: RNA_types.h:467