Branch data Line data Source code
1 : : /* GLIB - Library of useful routines for C programming
2 : : * Copyright (C) 1995-1998 Peter Mattis, Spencer Kimball and Josh MacDonald
3 : : *
4 : : * SPDX-License-Identifier: LGPL-2.1-or-later
5 : : *
6 : : * This library is free software; you can redistribute it and/or
7 : : * modify it under the terms of the GNU Lesser General Public
8 : : * License as published by the Free Software Foundation; either
9 : : * version 2.1 of the License, or (at your option) any later version.
10 : : *
11 : : * This library is distributed in the hope that it will be useful,
12 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : : * Lesser General Public License for more details.
15 : : *
16 : : * You should have received a copy of the GNU Lesser General Public
17 : : * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 : : */
19 : :
20 : : /*
21 : : * Modified by the GLib Team and others 1997-2000. See the AUTHORS
22 : : * file for a list of people on the GLib Team. See the ChangeLog
23 : : * files for a list of changes. These files are distributed with
24 : : * GLib at ftp://ftp.gtk.org/pub/gtk/.
25 : : */
26 : :
27 : : #include "config.h"
28 : :
29 : : #include "ggettext.h"
30 : : #include "glibintl.h"
31 : : #include "glib-private.h"
32 : :
33 : : #include "galloca.h"
34 : : #include "gthread.h"
35 : : #include "gmem.h"
36 : : #ifdef G_OS_WIN32
37 : : #include "gwin32.h"
38 : : #include "gfileutils.h"
39 : : #include "gstrfuncs.h"
40 : : #include "glib-init.h"
41 : : #endif
42 : :
43 : : #include <string.h>
44 : : #include <locale.h>
45 : : #include <libintl.h>
46 : :
47 : : #ifdef G_OS_WIN32
48 : :
49 : : /**
50 : : * _glib_get_locale_dir:
51 : : *
52 : : * Return the path to the share\locale or lib\locale subfolder of the
53 : : * GLib installation folder. The path is in the system codepage. We
54 : : * have to use system codepage as bindtextdomain() doesn't have a
55 : : * UTF-8 interface.
56 : : */
57 : : gchar *
58 : : _glib_get_locale_dir (void)
59 : : {
60 : : gchar *install_dir = NULL, *locale_dir;
61 : : gchar *retval = NULL;
62 : :
63 : : if (glib_dll != NULL)
64 : : install_dir = g_win32_get_package_installation_directory_of_module (glib_dll);
65 : :
66 : : if (install_dir)
67 : : {
68 : : /*
69 : : * Append "/share/locale" or "/lib/locale" depending on whether
70 : : * autoconfigury detected GNU gettext or not.
71 : : */
72 : : const char *p = GLIB_LOCALE_DIR + strlen (GLIB_LOCALE_DIR);
73 : : while (*--p != '/')
74 : : ;
75 : : while (*--p != '/')
76 : : ;
77 : :
78 : : locale_dir = g_build_filename (install_dir, p, NULL);
79 : :
80 : : retval = g_win32_locale_filename_from_utf8 (locale_dir);
81 : :
82 : : g_free (install_dir);
83 : : g_free (locale_dir);
84 : : }
85 : :
86 : : if (retval)
87 : : return retval;
88 : : else
89 : : return g_strdup ("");
90 : : }
91 : :
92 : : #undef GLIB_LOCALE_DIR
93 : :
94 : : #endif /* G_OS_WIN32 */
95 : :
96 : :
97 : : static void
98 : 27004 : ensure_gettext_initialized (void)
99 : : {
100 : : static gsize initialised;
101 : :
102 : 27004 : if (g_once_init_enter (&initialised))
103 : : {
104 : : #ifdef G_OS_WIN32
105 : : gchar *tmp = _glib_get_locale_dir ();
106 : : bindtextdomain (GETTEXT_PACKAGE, tmp);
107 : : g_free (tmp);
108 : : #else
109 : 296 : bindtextdomain (GETTEXT_PACKAGE, GLIB_LOCALE_DIR);
110 : : #endif
111 : : # ifdef HAVE_BIND_TEXTDOMAIN_CODESET
112 : 296 : bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
113 : : # endif
114 : 296 : g_once_init_leave (&initialised, TRUE);
115 : : }
116 : 27004 : }
117 : :
118 : : /**
119 : : * glib_gettext:
120 : : * @str: The string to be translated
121 : : *
122 : : * Returns the translated string from the glib translations.
123 : : * This is an internal function and should only be used by
124 : : * the internals of glib (such as libgio).
125 : : *
126 : : * Returns: the translation of @str to the current locale
127 : : */
128 : : const gchar *
129 : 26944 : glib_gettext (const gchar *str)
130 : : {
131 : 26944 : ensure_gettext_initialized ();
132 : :
133 : 26944 : return g_dgettext (GETTEXT_PACKAGE, str);
134 : : }
135 : :
136 : : /**
137 : : * glib_pgettext:
138 : : * @msgctxtid: a combined message context and message id, separated
139 : : * by a \004 character
140 : : * @msgidoffset: the offset of the message id in @msgctxid
141 : : *
142 : : * This function is a variant of glib_gettext() which supports
143 : : * a disambiguating message context. See g_dpgettext() for full
144 : : * details.
145 : : *
146 : : * This is an internal function and should only be used by
147 : : * the internals of glib (such as libgio).
148 : : *
149 : : * Returns: the translation of @str to the current locale
150 : : */
151 : : const gchar *
152 : 60 : glib_pgettext (const gchar *msgctxtid,
153 : : gsize msgidoffset)
154 : : {
155 : 60 : ensure_gettext_initialized ();
156 : :
157 : 60 : return g_dpgettext (GETTEXT_PACKAGE, msgctxtid, msgidoffset);
158 : : }
159 : :
160 : : /**
161 : : * g_strip_context:
162 : : * @msgid: a string
163 : : * @msgval: another string
164 : : *
165 : : * An auxiliary function for gettext() support (see Q_()).
166 : : *
167 : : * Returns: @msgval, unless @msgval is identical to @msgid
168 : : * and contains a '|' character, in which case a pointer to
169 : : * the substring of msgid after the first '|' character is returned.
170 : : *
171 : : * Since: 2.4
172 : : */
173 : : const gchar *
174 : 4 : g_strip_context (const gchar *msgid,
175 : : const gchar *msgval)
176 : : {
177 : 4 : if (msgval == msgid)
178 : : {
179 : 3 : const char *c = strchr (msgid, '|');
180 : 3 : if (c != NULL)
181 : 2 : return c + 1;
182 : : }
183 : :
184 : 2 : return msgval;
185 : : }
186 : :
187 : : /**
188 : : * g_dpgettext:
189 : : * @domain: (nullable): the translation domain to use, or %NULL to use
190 : : * the domain set with textdomain()
191 : : * @msgctxtid: a combined message context and message id, separated
192 : : * by a \004 character
193 : : * @msgidoffset: the offset of the message id in @msgctxid
194 : : *
195 : : * This function is a variant of g_dgettext() which supports
196 : : * a disambiguating message context. GNU gettext uses the
197 : : * '\004' character to separate the message context and
198 : : * message id in @msgctxtid.
199 : : * If 0 is passed as @msgidoffset, this function will fall back to
200 : : * trying to use the deprecated convention of using "|" as a separation
201 : : * character.
202 : : *
203 : : * This uses g_dgettext() internally. See that functions for differences
204 : : * with dgettext() proper.
205 : : *
206 : : * Applications should normally not use this function directly,
207 : : * but use the C_() macro for translations with context.
208 : : *
209 : : * Returns: The translated string
210 : : *
211 : : * Since: 2.16
212 : : */
213 : : const gchar *
214 : 70 : g_dpgettext (const gchar *domain,
215 : : const gchar *msgctxtid,
216 : : gsize msgidoffset)
217 : : {
218 : : const gchar *translation;
219 : : gchar *sep;
220 : :
221 : 70 : translation = g_dgettext (domain, msgctxtid);
222 : :
223 : 70 : if (translation == msgctxtid)
224 : : {
225 : 70 : if (msgidoffset > 0)
226 : 65 : return msgctxtid + msgidoffset;
227 : 5 : sep = strchr (msgctxtid, '|');
228 : :
229 : 5 : if (sep)
230 : : {
231 : : /* try with '\004' instead of '|', in case
232 : : * xgettext -kQ_:1g was used
233 : : */
234 : 5 : gchar *tmp = g_alloca (strlen (msgctxtid) + 1);
235 : 5 : strcpy (tmp, msgctxtid);
236 : 5 : tmp[sep - msgctxtid] = '\004';
237 : :
238 : 5 : translation = g_dgettext (domain, tmp);
239 : :
240 : : /* g_dgettext() may return the value we pass to it, which will be on
241 : : * this stack frame since we allocated it with g_alloca(). If so, we
242 : : * return a pointer into our original input instead.
243 : : */
244 : 5 : if (translation == tmp)
245 : 5 : return sep + 1;
246 : : }
247 : : }
248 : :
249 : 0 : return translation;
250 : : }
251 : :
252 : : /* This function is taken from gettext.h
253 : : * GNU gettext uses '\004' to separate context and msgid in .mo files.
254 : : */
255 : : /**
256 : : * g_dpgettext2:
257 : : * @domain: (nullable): the translation domain to use, or %NULL to use
258 : : * the domain set with textdomain()
259 : : * @context: the message context
260 : : * @msgid: the message
261 : : *
262 : : * This function is a variant of g_dgettext() which supports
263 : : * a disambiguating message context. GNU gettext uses the
264 : : * '\004' character to separate the message context and
265 : : * message id in @msgctxtid.
266 : : *
267 : : * This uses g_dgettext() internally. See that functions for differences
268 : : * with dgettext() proper.
269 : : *
270 : : * This function differs from C_() in that it is not a macro and
271 : : * thus you may use non-string-literals as context and msgid arguments.
272 : : *
273 : : * Returns: The translated string
274 : : *
275 : : * Since: 2.18
276 : : */
277 : : const gchar *
278 : 5 : g_dpgettext2 (const gchar *domain,
279 : : const gchar *msgctxt,
280 : : const gchar *msgid)
281 : : {
282 : 5 : size_t msgctxt_len = strlen (msgctxt) + 1;
283 : 5 : size_t msgid_len = strlen (msgid) + 1;
284 : : const char *translation;
285 : : char* msg_ctxt_id;
286 : :
287 : 5 : msg_ctxt_id = g_alloca (msgctxt_len + msgid_len);
288 : :
289 : 5 : memcpy (msg_ctxt_id, msgctxt, msgctxt_len - 1);
290 : 5 : msg_ctxt_id[msgctxt_len - 1] = '\004';
291 : 5 : memcpy (msg_ctxt_id + msgctxt_len, msgid, msgid_len);
292 : :
293 : 5 : translation = g_dgettext (domain, msg_ctxt_id);
294 : :
295 : 5 : if (translation == msg_ctxt_id)
296 : : {
297 : : /* try the old way of doing message contexts, too */
298 : 5 : msg_ctxt_id[msgctxt_len - 1] = '|';
299 : 5 : translation = g_dgettext (domain, msg_ctxt_id);
300 : :
301 : : /* g_dgettext() may return the value we pass to it, which will be on this
302 : : * stack frame since we allocated it with g_alloca(). If so, we return our
303 : : * original input instead.
304 : : */
305 : 5 : if (translation == msg_ctxt_id)
306 : 5 : return msgid;
307 : : }
308 : :
309 : 0 : return translation;
310 : : }
311 : :
312 : : static gboolean
313 : 27175 : _g_dgettext_should_translate (void)
314 : : {
315 : : static gsize translate = 0;
316 : : enum {
317 : : SHOULD_TRANSLATE = 1,
318 : : SHOULD_NOT_TRANSLATE = 2
319 : : };
320 : :
321 : 27175 : if (G_UNLIKELY (g_once_init_enter (&translate)))
322 : : {
323 : 296 : gboolean should_translate = TRUE;
324 : :
325 : 296 : const char *default_domain = textdomain (NULL);
326 : 296 : const char *translator_comment = gettext ("");
327 : : #ifndef G_OS_WIN32
328 : 296 : const char *translate_locale = setlocale (LC_MESSAGES, NULL);
329 : : #else
330 : : const char *translate_locale = g_win32_getlocale ();
331 : : #endif
332 : : /* We should NOT translate only if all the following hold:
333 : : * - user has called textdomain() and set textdomain to non-default
334 : : * - default domain has no translations
335 : : * - locale does not start with "en_" and is not "C"
336 : : *
337 : : * Rationale:
338 : : * - If text domain is still the default domain, maybe user calls
339 : : * it later. Continue with old behavior of translating.
340 : : * - If locale starts with "en_", we can continue using the
341 : : * translations even if the app doesn't have translations for
342 : : * this locale. That is, en_UK and en_CA for example.
343 : : * - If locale is "C", maybe user calls setlocale(LC_ALL,"") later.
344 : : * Continue with old behavior of translating.
345 : : */
346 : 296 : if (!default_domain || !translator_comment || !translate_locale ||
347 : 296 : (0 != strcmp (default_domain, "messages") &&
348 : 47 : '\0' == *translator_comment &&
349 : 47 : 0 != strncmp (translate_locale, "en_", 3) &&
350 : 47 : 0 != strcmp (translate_locale, "C")))
351 : 0 : should_translate = FALSE;
352 : :
353 : 296 : g_once_init_leave (&translate,
354 : : should_translate ?
355 : : SHOULD_TRANSLATE :
356 : : SHOULD_NOT_TRANSLATE);
357 : : }
358 : :
359 : 27175 : return translate == SHOULD_TRANSLATE;
360 : : }
361 : :
362 : : /**
363 : : * g_dgettext:
364 : : * @domain: (nullable): the translation domain to use, or %NULL to use
365 : : * the domain set with textdomain()
366 : : * @msgid: message to translate
367 : : *
368 : : * This function is a wrapper of dgettext() which does not translate
369 : : * the message if the default domain as set with textdomain() has no
370 : : * translations for the current locale.
371 : : *
372 : : * The advantage of using this function over dgettext() proper is that
373 : : * libraries using this function (like GTK) will not use translations
374 : : * if the application using the library does not have translations for
375 : : * the current locale. This results in a consistent English-only
376 : : * interface instead of one having partial translations. For this
377 : : * feature to work, the call to textdomain() and setlocale() should
378 : : * precede any g_dgettext() invocations. For GTK, it means calling
379 : : * textdomain() before gtk_init or its variants.
380 : : *
381 : : * This function disables translations if and only if upon its first
382 : : * call all the following conditions hold:
383 : : *
384 : : * - @domain is not %NULL
385 : : *
386 : : * - textdomain() has been called to set a default text domain
387 : : *
388 : : * - there is no translations available for the default text domain
389 : : * and the current locale
390 : : *
391 : : * - current locale is not "C" or any English locales (those
392 : : * starting with "en_")
393 : : *
394 : : * Note that this behavior may not be desired for example if an application
395 : : * has its untranslated messages in a language other than English. In those
396 : : * cases the application should call textdomain() after initializing GTK.
397 : : *
398 : : * Applications should normally not use this function directly,
399 : : * but use the _() macro for translations.
400 : : *
401 : : * Returns: The translated string
402 : : *
403 : : * Since: 2.18
404 : : */
405 : : const gchar *
406 : 27114 : g_dgettext (const gchar *domain,
407 : : const gchar *msgid)
408 : : {
409 : 27114 : if (domain && G_UNLIKELY (!_g_dgettext_should_translate ()))
410 : 0 : return msgid;
411 : :
412 : 27114 : return dgettext (domain, msgid);
413 : : }
414 : :
415 : : /**
416 : : * g_dcgettext:
417 : : * @domain: (nullable): the translation domain to use, or %NULL to use
418 : : * the domain set with textdomain()
419 : : * @msgid: message to translate
420 : : * @category: a locale category
421 : : *
422 : : * This is a variant of g_dgettext() that allows specifying a locale
423 : : * category instead of always using `LC_MESSAGES`. See g_dgettext() for
424 : : * more information about how this functions differs from calling
425 : : * dcgettext() directly.
426 : : *
427 : : * Returns: the translated string for the given locale category
428 : : *
429 : : * Since: 2.26
430 : : */
431 : : const gchar *
432 : 0 : g_dcgettext (const gchar *domain,
433 : : const gchar *msgid,
434 : : gint category)
435 : : {
436 : 0 : if (domain && G_UNLIKELY (!_g_dgettext_should_translate ()))
437 : 0 : return msgid;
438 : :
439 : 0 : return dcgettext (domain, msgid, category);
440 : : }
441 : :
442 : : /**
443 : : * g_dngettext:
444 : : * @domain: (nullable): the translation domain to use, or %NULL to use
445 : : * the domain set with textdomain()
446 : : * @msgid: message to translate
447 : : * @msgid_plural: plural form of the message
448 : : * @n: the quantity for which translation is needed
449 : : *
450 : : * This function is a wrapper of dngettext() which does not translate
451 : : * the message if the default domain as set with textdomain() has no
452 : : * translations for the current locale.
453 : : *
454 : : * See g_dgettext() for details of how this differs from dngettext()
455 : : * proper.
456 : : *
457 : : * Returns: The translated string
458 : : *
459 : : * Since: 2.18
460 : : */
461 : : const gchar *
462 : 81 : g_dngettext (const gchar *domain,
463 : : const gchar *msgid,
464 : : const gchar *msgid_plural,
465 : : gulong n)
466 : : {
467 : 81 : if (domain && G_UNLIKELY (!_g_dgettext_should_translate ()))
468 : 0 : return n == 1 ? msgid : msgid_plural;
469 : :
470 : 81 : return dngettext (domain, msgid, msgid_plural, n);
471 : : }
472 : :
473 : : /**
474 : : * _:
475 : : * @String: the string to be translated
476 : : *
477 : : * Marks a string for translation, gets replaced with the translated string
478 : : * at runtime.
479 : : *
480 : : * Since: 2.4
481 : : */
482 : :
483 : : /**
484 : : * Q_:
485 : : * @String: the string to be translated, with a '|'-separated prefix
486 : : * which must not be translated
487 : : *
488 : : * Like _(), but handles context in message ids. This has the advantage
489 : : * that the string can be adorned with a prefix to guarantee uniqueness
490 : : * and provide context to the translator.
491 : : *
492 : : * One use case given in the gettext manual is GUI translation, where one
493 : : * could e.g. disambiguate two "Open" menu entries as "File|Open" and
494 : : * "Printer|Open". Another use case is the string "Russian" which may
495 : : * have to be translated differently depending on whether it's the name
496 : : * of a character set or a language. This could be solved by using
497 : : * "charset|Russian" and "language|Russian".
498 : : *
499 : : * See the C_() macro for a different way to mark up translatable strings
500 : : * with context.
501 : : *
502 : : * If you are using the Q_() macro, you need to make sure that you pass
503 : : * `--keyword=Q_` to xgettext when extracting messages.
504 : : * If you are using GNU gettext >= 0.15, you can also use
505 : : * `--keyword=Q_:1g` to let xgettext split the context
506 : : * string off into a msgctxt line in the po file.
507 : : *
508 : : * Returns: the translated message
509 : : *
510 : : * Since: 2.4
511 : : */
512 : :
513 : : /**
514 : : * C_:
515 : : * @Context: a message context, must be a string literal
516 : : * @String: a message id, must be a string literal
517 : : *
518 : : * Uses gettext to get the translation for @String. @Context is
519 : : * used as a context. This is mainly useful for short strings which
520 : : * may need different translations, depending on the context in which
521 : : * they are used.
522 : : * |[<!-- language="C" -->
523 : : * label1 = C_("Navigation", "Back");
524 : : * label2 = C_("Body part", "Back");
525 : : * ]|
526 : : *
527 : : * If you are using the C_() macro, you need to make sure that you pass
528 : : * `--keyword=C_:1c,2` to xgettext when extracting messages.
529 : : * Note that this only works with GNU gettext >= 0.15.
530 : : *
531 : : * Returns: the translated message
532 : : *
533 : : * Since: 2.16
534 : : */
535 : :
536 : : /**
537 : : * N_:
538 : : * @String: the string to be translated
539 : : *
540 : : * Only marks a string for translation. This is useful in situations
541 : : * where the translated strings can't be directly used, e.g. in string
542 : : * array initializers. To get the translated string, call gettext()
543 : : * at runtime.
544 : : * |[<!-- language="C" -->
545 : : * {
546 : : * static const char *messages[] = {
547 : : * N_("some very meaningful message"),
548 : : * N_("and another one")
549 : : * };
550 : : * const char *string;
551 : : * ...
552 : : * string
553 : : * = index > 1 ? _("a default message") : gettext (messages[index]);
554 : : *
555 : : * fputs (string);
556 : : * ...
557 : : * }
558 : : * ]|
559 : : *
560 : : * Since: 2.4
561 : : */
562 : :
563 : : /**
564 : : * NC_:
565 : : * @Context: a message context, must be a string literal
566 : : * @String: a message id, must be a string literal
567 : : *
568 : : * Only marks a string for translation, with context.
569 : : * This is useful in situations where the translated strings can't
570 : : * be directly used, e.g. in string array initializers. To get the
571 : : * translated string, you should call g_dpgettext2() at runtime.
572 : : *
573 : : * |[<!-- language="C" -->
574 : : * {
575 : : * static const char *messages[] = {
576 : : * NC_("some context", "some very meaningful message"),
577 : : * NC_("some context", "and another one")
578 : : * };
579 : : * const char *string;
580 : : * ...
581 : : * string
582 : : * = index > 1 ? g_dpgettext2 (NULL, "some context", "a default message")
583 : : * : g_dpgettext2 (NULL, "some context", messages[index]);
584 : : *
585 : : * fputs (string);
586 : : * ...
587 : : * }
588 : : * ]|
589 : : *
590 : : * If you are using the NC_() macro, you need to make sure that you pass
591 : : * `--keyword=NC_:1c,2` to xgettext when extracting messages.
592 : : * Note that this only works with GNU gettext >= 0.15. Intltool has support
593 : : * for the NC_() macro since version 0.40.1.
594 : : *
595 : : * Since: 2.18
596 : : */
|