1 /*
2 This file is part of Warzone 2100.
3 Copyright (C) 2005-2020 Warzone 2100 Project
4
5 Warzone 2100 is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 Warzone 2100 is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with Warzone 2100; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19 #include "frame.h"
20
21 #include <sstream>
22 #include <vector>
23
24 #include <locale.h>
25 #include <physfs.h>
26 #include "wzpaths.h"
27
28 #ifdef WZ_OS_MAC
29 # include <CoreFoundation/CoreFoundation.h>
30 # include <CoreFoundation/CFURL.h>
31 #endif
32
33 /* Always use fallbacks on Windows */
34 #if defined(WZ_OS_WIN)
35 # undef LOCALEDIR
36 #endif
37
38 #if !defined(LOCALEDIR)
39 # define LOCALEDIR "locale"
40 #endif
41
42 #ifdef PORTABLE
43 # ifdef PACKAGE
44 # undef PACKAGE
45 # define PACKAGE "warzone2100_portable"
46 # endif
47 #endif
48 // Language names (http://en.wikipedia.org/wiki/List_of_ISO_639-2_codes)
49 #define LANG_NAME_BASQUE "euskara"
50 #define LANG_NAME_BULGARIAN "български език"
51 #define LANG_NAME_CATALAN "català"
52 #define LANG_NAME_CHINESE_SIMPLIFIED "汉语"
53 #define LANG_NAME_CHINESE_TRADITIONAL "漢語"
54 #define LANG_NAME_CROATIAN "Hrvatski"
55 #define LANG_NAME_CZECH "česky"
56 #define LANG_NAME_DANISH "Dansk"
57 #define LANG_NAME_DUTCH "Nederlands"
58 #define LANG_NAME_ENGLISH "English"
59 #define LANG_NAME_ENGLISH_UK "British English"
60 #define LANG_NAME_ESTONIAN "Eesti Keel"
61 #define LANG_NAME_FINNISH "suomi"
62 #define LANG_NAME_FRENCH "Français"
63 #define LANG_NAME_FRISIAN_NETHERLANDS "frysk"
64 #define LANG_NAME_GERMAN "Deutsch"
65 #define LANG_NAME_GREEK "Ελληνικά"
66 #define LANG_NAME_HUNGARIAN "magyar"
67 #define LANG_NAME_INDONESIAN "Bahasa Indonesia"
68 #define LANG_NAME_IRISH "Imruagadh"
69 #define LANG_NAME_ITALIAN "Italiano"
70 #define LANG_NAME_KOREAN "한국어"
71 #define LANG_NAME_LATIN "latine"
72 #define LANG_NAME_LATVIAN "latviešu valoda"
73 #define LANG_NAME_LITHUANIAN "lietuvių kalba"
74 #define LANG_NAME_NORWEGIAN "Norsk"
75 #define LANG_NAME_NORWEGIAN_NYNORSK "nynorsk"
76 #define LANG_NAME_POLISH "Polski"
77 #define LANG_NAME_PORTUGUESE_BRAZILIAN "Português Brasileiro"
78 #define LANG_NAME_PORTUGUESE "Português"
79 #define LANG_NAME_ROMANIAN "română"
80 #define LANG_NAME_RUSSIAN "Русский"
81 #define LANG_NAME_SLOVAK "Slovensky"
82 #define LANG_NAME_SLOVENIAN "Slovenski"
83 #define LANG_NAME_SPANISH "Español"
84 #define LANG_NAME_SWEDISH "svenska"
85 #define LANG_NAME_SWEDISH_SWEDEN "svenska (Sverige)"
86 #define LANG_NAME_TURKISH "Türkçe"
87 #define LANG_NAME_UKRAINIAN "Українська"
88 #define LANG_NAME_UZBEK_CYRILLIC "Ўзбек"
89
90 #if defined(WZ_OS_WIN)
91 /*
92 * See msdn.microsoft.com for this stuff, esp.
93 * http://msdn.microsoft.com/en-us/library/ms693062%28VS.85,printer%29.aspx
94 * http://msdn.microsoft.com/en-us/library/dd318693%28VS.85,printer%29.aspx
95 */
96 static const struct
97 {
98 const char *language;
99 const char *name;
100 USHORT usPrimaryLanguage;
101 USHORT usSubLanguage;
102 } map[] =
103 {
104 { "", N_("System locale"), LANG_NEUTRAL, SUBLANG_DEFAULT },
105 # if defined(ENABLE_NLS)
106 { "bg", LANG_NAME_BULGARIAN, LANG_BULGARIAN, SUBLANG_DEFAULT },
107 { "ca", LANG_NAME_CATALAN, LANG_CATALAN, SUBLANG_DEFAULT },
108 { "cs", LANG_NAME_CZECH, LANG_CZECH, SUBLANG_DEFAULT },
109 { "da", LANG_NAME_DANISH, LANG_DANISH, SUBLANG_DEFAULT },
110 { "de", LANG_NAME_GERMAN, LANG_GERMAN, SUBLANG_GERMAN },
111 { "el", LANG_NAME_GREEK, LANG_GREEK, SUBLANG_DEFAULT },
112 { "en", LANG_NAME_ENGLISH, LANG_ENGLISH, SUBLANG_DEFAULT },
113 { "en_GB", LANG_NAME_ENGLISH_UK, LANG_ENGLISH, SUBLANG_ENGLISH_UK },
114 { "es", LANG_NAME_SPANISH, LANG_SPANISH, SUBLANG_SPANISH },
115 { "et_EE", LANG_NAME_ESTONIAN, LANG_ESTONIAN, SUBLANG_DEFAULT },
116 // { "eu", LANG_NAME_BASQUE, LANG_BASQUE, SUBLANG_DEFAULT },
117 { "fi", LANG_NAME_FINNISH, LANG_FINNISH, SUBLANG_DEFAULT },
118 { "fr", LANG_NAME_FRENCH, LANG_FRENCH, SUBLANG_FRENCH },
119 /* Our Frisian translation is the "West Frisian" variation of it. This
120 * variation is mostly spoken in the Dutch province Friesland (Fryslân
121 * in Frisian) and has ISO 639-3 code "fry".
122 *
123 * FIXME: We should really use a sub-language code for this. E.g.
124 * fy_XX.
125 */
126 { "fy", LANG_NAME_FRISIAN_NETHERLANDS, LANG_FRISIAN, SUBLANG_FRISIAN_NETHERLANDS },
127 { "ga", LANG_NAME_IRISH, LANG_IRISH, SUBLANG_IRISH_IRELAND },
128 { "hr", LANG_NAME_CROATIAN, LANG_CROATIAN, SUBLANG_DEFAULT },
129 { "hu", LANG_NAME_HUNGARIAN, LANG_HUNGARIAN, SUBLANG_DEFAULT },
130 { "id", LANG_NAME_INDONESIAN, LANG_INDONESIAN, SUBLANG_DEFAULT },
131 { "it", LANG_NAME_ITALIAN, LANG_ITALIAN, SUBLANG_ITALIAN },
132 { "ko_KR", LANG_NAME_KOREAN, LANG_KOREAN, SUBLANG_DEFAULT },
133 // { "la", LANG_NAME_LATIN, LANG_LATIN, SUBLANG_DEFAULT },
134 { "lt", LANG_NAME_LITHUANIAN, LANG_LITHUANIAN, SUBLANG_DEFAULT },
135 // { "lv", LANG_NAME_LATVIAN, LANG_LATVIAN, SUBLANG_DEFAULT },
136 // MSDN uses "no"...
137 { "nb", LANG_NAME_NORWEGIAN, LANG_NORWEGIAN, SUBLANG_DEFAULT },
138 // { "nn", LANG_NAME_NORWEGIAN_NYNORSK, LANG_NORWEGIAN, SUBLANG_NORWEGIAN_NYNORSK },
139 { "nl", LANG_NAME_DUTCH, LANG_DUTCH, SUBLANG_DUTCH },
140 { "pl", LANG_NAME_POLISH, LANG_POLISH, SUBLANG_DEFAULT },
141 { "pt_BR", LANG_NAME_PORTUGUESE_BRAZILIAN, LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN },
142 { "pt", LANG_NAME_PORTUGUESE, LANG_PORTUGUESE, SUBLANG_DEFAULT },
143 { "ro", LANG_NAME_ROMANIAN, LANG_ROMANIAN, SUBLANG_DEFAULT },
144 { "ru", LANG_NAME_RUSSIAN, LANG_RUSSIAN, SUBLANG_DEFAULT },
145 { "sk", LANG_NAME_SLOVAK, LANG_SLOVAK, SUBLANG_DEFAULT },
146 { "sl", LANG_NAME_SLOVENIAN, LANG_SLOVENIAN, SUBLANG_DEFAULT },
147 #if (WINVER >= 0x0600)
148 // { "sv_SE", LANG_NAME_SWEDISH_SWEDEN, LANG_SWEDISH, SUBLANG_SWEDISH_SWEDEN },
149 #else
150 // { "sv_SE", LANG_NAME_SWEDISH_SWEDEN, LANG_SWEDISH, SUBLANG_SWEDISH },
151 #endif
152 // { "sv", LANG_NAME_SWEDISH, LANG_SWEDISH, SUBLANG_DEFAULT },
153 { "tr", LANG_NAME_TURKISH, LANG_TURKISH, SUBLANG_DEFAULT },
154 // { "uz", LANG_NAME_UZBEK_CYRILLIC, LANG_UZBEK, SUBLANG_UZBEK_CYRILLIC },
155 { "uk_UA", LANG_NAME_UKRAINIAN, LANG_UKRAINIAN, SUBLANG_DEFAULT },
156 { "zh_CN", LANG_NAME_CHINESE_SIMPLIFIED, LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED },
157 { "zh_TW", LANG_NAME_CHINESE_TRADITIONAL, LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL },
158 # endif
159 };
160 #else
161 static const struct
162 {
163 const char *language;
164 const char *name;
165 const char *locale;
166 const char *localeFallback;
167 } map[] =
168 {
169 { "", N_("System locale"), "", "" },
170 # if defined(ENABLE_NLS)
171 { "bg", LANG_NAME_BULGARIAN, "bg_BG.UTF-8", "bg" },
172 { "ca_ES", LANG_NAME_CATALAN, "ca_ES.UTF-8", "ca" },
173 { "cs_CZ", LANG_NAME_CZECH, "cs_CZ.UTF-8", "cs" },
174 { "da", LANG_NAME_DANISH, "da_DK.UTF-8", "da_DK" },
175 { "de", LANG_NAME_GERMAN, "de_DE.UTF-8", "de_DE" },
176 { "el_GR", LANG_NAME_GREEK, "el_GR.UTF-8", "el" },
177 { "en", LANG_NAME_ENGLISH, "en_US.UTF-8", "en_US" },
178 { "en_GB", LANG_NAME_ENGLISH_UK, "en_GB.UTF-8", "en_GB" },
179 { "es", LANG_NAME_SPANISH, "es_ES.UTF-8", "es_ES" },
180 { "et_EE", LANG_NAME_ESTONIAN, "et_EE.UTF-8", "et" },
181 // { "eu_ES", LANG_NAME_BASQUE, "eu_ES.UTF-8", "eu" },
182 { "fi", LANG_NAME_FINNISH, "fi_FI.UTF-8", "fi_FI" },
183 { "fr", LANG_NAME_FRENCH, "fr_FR.UTF-8", "fr_FR" },
184 /* Our Frisian translation is the "West Frisian" variation of it. This
185 * variation is mostly spoken in the Dutch province Friesland (Fryslân
186 * in Frisian) and has ISO 639-3 code "fry".
187 */
188 { "fy_NL", LANG_NAME_FRISIAN_NETHERLANDS, "fy_NL.UTF-8", "fy" },
189 { "ga_IE", LANG_NAME_IRISH, "ga_IE.UTF-8", "ga" },
190 { "hr", LANG_NAME_CROATIAN, "hr_HR.UTF-8", "hr_HR" },
191 { "hu", LANG_NAME_HUNGARIAN, "hu_HU.UTF-8", "hu_HU" },
192 { "id", LANG_NAME_INDONESIAN, "id_ID.UTF-8", "id" },
193 { "it", LANG_NAME_ITALIAN, "it_IT.UTF-8", "it_IT" },
194 { "ko_KR", LANG_NAME_KOREAN, "ko_KR.UTF-8", "ko" },
195 { "la", LANG_NAME_LATIN, "la.UTF-8", "la" },
196 { "lt", LANG_NAME_LITHUANIAN, "lt_LT.UTF-8", "lt_LT" },
197 // { "lv", LANG_NAME_LATVIAN, "lv_LV.UTF-8", "lv_LV" },
198 { "nb_NO", LANG_NAME_NORWEGIAN, "nb_NO.UTF-8", "nb" },
199 // { "nn_NO", LANG_NAME_NORWEGIAN_NYNORSK, "nn_NO.UTF-8", "nn" },
200 { "nl", LANG_NAME_DUTCH, "nl_NL.UTF-8", "nl_NL" },
201 { "pl", LANG_NAME_POLISH, "pl_PL.UTF-8", "pl_PL" },
202 { "pt_BR", LANG_NAME_PORTUGUESE_BRAZILIAN, "pt_BR.UTF-8", "pt_BR" },
203 { "pt", LANG_NAME_PORTUGUESE, "pt_PT.UTF-8", "pt_PT" },
204 { "ro", LANG_NAME_ROMANIAN, "ro_RO.UTF-8", "ro_RO" },
205 { "ru", LANG_NAME_RUSSIAN, "ru_RU.UTF-8", "ru_RU" },
206 { "sk", LANG_NAME_SLOVAK, "sk_SK.UTF-8", "sk_SK" },
207 { "sl_SI", LANG_NAME_SLOVENIAN, "sl_SI.UTF-8", "sl" },
208 // { "sv_SE", LANG_NAME_SWEDISH_SWEDEN, "sv_SE.UTF-8", "sv" },
209 // { "sv", LANG_NAME_SWEDISH, "sv.UTF-8", "sv" },
210 { "tr", LANG_NAME_TURKISH, "tr_TR.UTF-8", "tr_TR" },
211 // { "uz", LANG_NAME_UZBEK_CYRILLIC, "uz_UZ.UTF-8", "uz_UZ" },
212 { "uk_UA", LANG_NAME_UKRAINIAN, "uk_UA.UTF-8", "uk" },
213 { "zh_CN", LANG_NAME_CHINESE_SIMPLIFIED, "zh_CN.UTF-8", "zh_CN" },
214 { "zh_TW", LANG_NAME_CHINESE_TRADITIONAL, "zh_TW.UTF-8", "zh_TW" },
215 # endif
216 };
217 #endif
218
219 static unsigned int selectedLanguage = 0;
220
221 static char *compileDate = nullptr;
222
223 /*!
224 * Return the language part of the selected locale
225 */
226 #if !defined(ENABLE_NLS)
getLanguage()227 const char *getLanguage()
228 {
229 return "";
230 }
231 #elif defined(WZ_OS_WIN)
getLanguage()232 const char *getLanguage()
233 {
234 USHORT usPrimaryLanguage = PRIMARYLANGID(LANGIDFROMLCID(GetThreadLocale()));
235 unsigned int i;
236
237 if (selectedLanguage == 0)
238 {
239 return ""; // Return empty string for system default
240 }
241
242 for (i = 0; i < ARRAY_SIZE(map); i++)
243 {
244 if (usPrimaryLanguage == map[i].usPrimaryLanguage)
245 {
246 return map[i].language;
247 }
248 }
249
250 return "";
251 }
252 #else
getLanguage()253 const char *getLanguage()
254 {
255 static char language[6] = { '\0' }; // large enough for xx_YY
256 const char *localeName = setlocale(LC_MESSAGES, NULL);
257 char *delim = NULL;
258
259 if (selectedLanguage == 0 || localeName == NULL)
260 {
261 return ""; // Return empty string for system default and errors
262 }
263
264 sstrcpy(language, localeName);
265
266 // cut anything after a '.' to get rid of the encoding part
267 delim = strchr(language, '.');
268 if (delim)
269 {
270 *delim = '\0';
271 }
272
273 // if language is xx_XX, cut the _XX part
274 delim = strchr(language, '_');
275 if (delim)
276 {
277 if (!strncasecmp(language, delim + 1, 2))
278 {
279 *delim = '\0';
280 }
281 }
282
283 return language;
284 }
285 #endif
286
287
getLanguageName()288 const char *getLanguageName()
289 {
290 const char *language = getLanguage();
291 unsigned int i;
292
293 for (i = 0; i < ARRAY_SIZE(map); i++)
294 {
295 if (strcmp(language, map[i].language) == 0)
296 {
297 return gettext(map[i].name);
298 }
299 }
300
301 return language;
302 }
303
304
305 #if defined(ENABLE_NLS)
306 # if defined(WZ_OS_WIN)
setLocaleWindows(USHORT usPrimaryLanguage,USHORT usSubLanguage)307 static bool setLocaleWindows(USHORT usPrimaryLanguage, USHORT usSubLanguage)
308 {
309 bool success = SUCCEEDED(SetThreadLocale(MAKELCID(MAKELANGID(usPrimaryLanguage, usSubLanguage), SORT_DEFAULT)));
310
311 if (!success)
312 {
313 wz_info("Failed to set locale to \"%d\"", usPrimaryLanguage);
314 }
315 else
316 {
317 debug(LOG_WZ, "Requested locale \"%d\"", usPrimaryLanguage);
318 }
319
320 setlocale(LC_NUMERIC, "C"); // set radix character to the period (".")
321
322 return success;
323 }
324 # else
325 /*!
326 * Set the prefered locale
327 * \param locale The locale, NOT just the language part
328 * \note Use this instead of setlocale(), because we need the default radix character
329 */
setLocaleUnix(const char * locale)330 static bool setLocaleUnix(const char *locale)
331 {
332 #ifdef WZ_OS_MAC
333 // Set the appropriate language environment variable *before* the call to libintl's `setlocale`.
334
335 // First, check if LC_ALL or LC_MESSAGES is set, as these may override LANG
336 const char *pEnvLang = getenv("LC_ALL");
337 if ((pEnvLang != nullptr) && (pEnvLang[0] != '\0'))
338 {
339 debug(LOG_WARNING, "Environment variable \"LC_ALL\" is set, and may override runtime language changes.");
340 }
341 pEnvLang = getenv("LC_MESSAGES");
342 if ((pEnvLang != nullptr) && (pEnvLang[0] != '\0'))
343 {
344 debug(LOG_WARNING, "Environment variable \"LC_MESSAGES\" is set, and may override runtime language changes.");
345 }
346
347 // Set the LANG env-var (temporarily saving the old value)
348 pEnvLang = getenv("LANG");
349 std::string prior_LANG((pEnvLang != nullptr) ? pEnvLang : "");
350 # if defined HAVE_SETENV
351 setenv("LANG", locale, 1);
352 # else
353 # warning "No supported method to set environment variables"
354 # endif
355 #endif // (ifdef WZ_OS_MAC)
356
357 const char *actualLocale = setlocale(LC_ALL, locale);
358
359 if (actualLocale == NULL)
360 {
361 wz_info("Failed to set locale to \"%s\"", locale);
362 #ifdef WZ_OS_MAC
363 # if defined HAVE_SETENV
364 setenv("LANG", prior_LANG.c_str(), 1);
365 # else
366 # warning "No supported method to set environment variables"
367 # endif
368 #endif
369 }
370 else
371 {
372 if (strcmp(locale, actualLocale))
373 {
374 debug(LOG_WZ, "Requested locale \"%s\", got \"%s\" instead", locale, actualLocale);
375 }
376 }
377
378 const char * numericLocale = setlocale(LC_NUMERIC, "C"); // set radix character to the period (".")
379 debug(LOG_WZ, "LC_NUMERIC: \"%s\"", (numericLocale != nullptr) ? numericLocale : "<null>");
380
381 return (actualLocale != NULL);
382 }
383 # endif
384 #endif
385
386
setLanguage(const char * language)387 bool setLanguage(const char *language)
388 {
389 #if !defined(ENABLE_NLS)
390 return true;
391 #else
392 unsigned int i;
393
394 for (i = 0; i < ARRAY_SIZE(map); i++)
395 {
396 if (strcmp(language, map[i].language) == 0)
397 {
398 selectedLanguage = i;
399 debug(LOG_WZ, "Setting language to \"%s\" (%s)", map[i].name, map[i].language);
400
401 # if defined(WZ_OS_WIN)
402 return setLocaleWindows(map[i].usPrimaryLanguage, map[i].usSubLanguage);
403 # else
404 return setLocaleUnix(map[i].locale) || setLocaleUnix(map[i].localeFallback);
405 # endif
406 }
407 }
408
409 debug(LOG_ERROR, "Requested language \"%s\" not supported.", language);
410
411 return false;
412 #endif
413 }
414
415
setNextLanguage(bool prev)416 void setNextLanguage(bool prev)
417 {
418 selectedLanguage = (selectedLanguage + ARRAY_SIZE(map) + (prev? -1 : 1)) % ARRAY_SIZE(map);
419
420 if (!setLanguage(map[selectedLanguage].language) && selectedLanguage != 0)
421 {
422 setNextLanguage(prev); // try next
423 }
424 }
425
wzBindTextDomain(const char * domainname,const char * dirname)426 std::string wzBindTextDomain(const char *domainname, const char *dirname)
427 {
428 #if (LIBINTL_VERSION >= 0x001500) && defined(_WIN32) && !defined(__CYGWIN__)
429 // gettext 0.21+ provides a wbindtextdomain function on native Windows platforms
430 // that properly supports Unicode paths
431
432 // convert the dirname from UTF-8 to UTF-16
433 int wstr_len = MultiByteToWideChar(CP_UTF8, 0, dirname, -1, NULL, 0);
434 if (wstr_len <= 0)
435 {
436 DWORD dwError = GetLastError();
437 debug(LOG_ERROR, "Could not not convert string from UTF-8; MultiByteToWideChar failed with error %d: %s\n", dwError, dirname);
438 return std::string();
439 }
440 auto wstr_dirname = std::vector<wchar_t>(wstr_len, L'\0');
441 if (MultiByteToWideChar(CP_UTF8, 0, dirname, -1, &wstr_dirname[0], wstr_len) == 0)
442 {
443 DWORD dwError = GetLastError();
444 debug(LOG_ERROR, "Could not not convert string from UTF-8; MultiByteToWideChar[2] failed with error %d: %s\n", dwError, dirname);
445 return std::string();
446 }
447
448 // and call wbindtextdomain
449 const wchar_t *pResult = wbindtextdomain(domainname, wstr_dirname.data());
450 if (!pResult)
451 {
452 debug(LOG_ERROR, "wbindtextdomain failed");
453 return std::string();
454 }
455
456 // convert the result back to UTF-8
457 std::vector<char> utf8Buffer;
458 int utf8Len = WideCharToMultiByte(CP_UTF8, 0, pResult, -1, NULL, 0, NULL, NULL);
459 if ( utf8Len <= 0 )
460 {
461 // Encoding conversion error
462 DWORD dwError = GetLastError();
463 debug(LOG_ERROR, "Could not not convert string to UTF-8; WideCharToMultiByte failed with error %d\n", dwError);
464 return std::string();
465 }
466 utf8Buffer.resize(utf8Len, 0);
467 if ( (utf8Len = WideCharToMultiByte(CP_UTF8, 0, pResult, -1, &utf8Buffer[0], utf8Len, NULL, NULL)) <= 0 )
468 {
469 // Encoding conversion error
470 DWORD dwError = GetLastError();
471 debug(LOG_ERROR, "Could not not convert string to UTF-8; WideCharToMultiByte[2] failed with error %d\n", dwError);
472 return std::string();
473 }
474 return std::string(utf8Buffer.data(), utf8Len - 1);
475 #else
476 // call the normal bindtextdomain function
477 const char * pResult = bindtextdomain(domainname, dirname);
478 if (!pResult)
479 {
480 return std::string();
481 }
482 return std::string(pResult);
483 #endif
484 }
485
initI18n()486 void initI18n()
487 {
488 std::string textdomainDirectory;
489
490 if (!setLanguage("")) // set to system default
491 {
492 // no system default?
493 debug(LOG_ERROR, "initI18n: No system language found");
494 }
495 #ifdef WZ_OS_MAC
496 {
497 char resourcePath[PATH_MAX];
498 CFURLRef resourceURL = CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle());
499 if (CFURLGetFileSystemRepresentation(resourceURL, true, (UInt8 *) resourcePath, PATH_MAX))
500 {
501 sstrcat(resourcePath, "/locale");
502 textdomainDirectory = wzBindTextDomain(PACKAGE, resourcePath);
503 }
504 else
505 {
506 debug(LOG_ERROR, "Could not change to resources directory.");
507 }
508
509 if (resourceURL != NULL)
510 {
511 CFRelease(resourceURL);
512 }
513
514 debug(LOG_INFO, "resourcePath is %s", resourcePath);
515 }
516 #else
517 # ifdef WZ_LOCALEDIR
518 // New locale-dir setup (CMake)
519 #ifndef WZ_LOCALEDIR_ISABSOLUTE
520 // Treat WZ_LOCALEDIR as a relative path - append to the install PREFIX
521 const std::string prefixDir = getWZInstallPrefix();
522 const std::string dirSeparator(PHYSFS_getDirSeparator());
523 std::string localeDir = prefixDir + dirSeparator + WZ_LOCALEDIR;
524 textdomainDirectory = wzBindTextDomain(PACKAGE, localeDir.c_str());
525 #else
526 // Treat WZ_LOCALEDIR as an absolute path, and use directly
527 textdomainDirectory = wzBindTextDomain(PACKAGE, WZ_LOCALEDIR);
528 #endif
529 # else
530 // Old locale-dir setup (autotools)
531 textdomainDirectory = wzBindTextDomain(PACKAGE, LOCALEDIR);
532 # endif
533 #endif // ifdef WZ_OS_MAC
534 if (textdomainDirectory.empty())
535 {
536 debug(LOG_ERROR, "initI18n: bindtextdomain failed!");
537 }
538
539 (void)bind_textdomain_codeset(PACKAGE, "UTF-8");
540 (void)textdomain(PACKAGE);
541 }
542
543 // convert macro __DATE__ to ISO 8601 format
getCompileDate()544 const char *getCompileDate()
545 {
546 if (compileDate == nullptr)
547 {
548 std::istringstream date(__DATE__);
549 std::string monthName;
550 int day = 0, month = 0, year = 0;
551 date >> monthName >> day >> year;
552
553 std::string monthNames[] = {
554 "Jan", "Feb", "Mar", "Apr",
555 "May", "Jun", "Jul", "Aug",
556 "Sep", "Oct", "Nov", "Dec"
557 };
558 for (int i = 0; i < 12; i++)
559 {
560 if (monthNames[i] == monthName)
561 {
562 month = i + 1;
563 break;
564 }
565 }
566 asprintfNull(&compileDate, "%04d-%02d-%02d", year, month, day);
567 }
568 return compileDate;
569 }
570