Coverage Report

Created: 2025-07-01 07:09

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