@@ -1205,36 +1205,81 @@ comma (unsigned col, struct pentry_state *pest)
indent_to (pest->stream, col);
}
-/* Help and usage output show the translated option name. *allocated
- holds a pointer that should be freed by the caller, or a NULL
- pointer. */
-static const char *
-translate_option_name (const char *name, char **allocated)
+/* Help and usage output show the translated option name. Since an
+ option name may have multiple translations, we return all of them.
+ The result is to be freed by the caller, each element of the array
+ first, and then the array itself. */
+static char **
+translate_option_name (const char *name)
{
/* Argp does not have a configuration for the context, so a default
one is used. */
+ char *msgid = NULL;
+ const char *full_translation = NULL;
+ size_t n_translations;
+ char **results = NULL;
/* FIXME: use pgettext_expr. */
- *allocated = NULL;
if (__libc_enable_secure)
/* Translations are disabled. */
- return name;
- if (__asprintf (allocated, "command-line option\004%s", name) == -1)
{
- /* *allocated is NULL */
- return name;
+ results = calloc (2, sizeof (char *));
+ if (results != NULL)
+ {
+ results[0] = __strdup (name);
+ if (results[0] == NULL)
+ {
+ free (results);
+ results = NULL;
+ }
+ }
+ return results;
}
- const char *translated = gettext (*allocated);
- if (strcmp (translated, *allocated) == 0)
+ if (__asprintf (&msgid, "command-line option\004%s", name) == -1)
+ /* Do not bother trying to strdup name. */
+ return NULL;
+ full_translation = gettext (msgid);
+ if (strcmp (full_translation, msgid) == 0)
+ full_translation = name;
+ /* Split full_translation into results. Do it in 2 passes: first
+ count, then copy. */
+ for (int pass = 0; pass < 2; pass++)
{
- /* No translation performed. */
- free (*allocated);
- *allocated = NULL;
- return name;
+ n_translations = 0;
+ const char *start = full_translation;
+ const char *end = NULL;
+ while (start != NULL)
+ {
+ end = strchr (start, ' ');
+ if (pass == 1)
+ {
+ if (end == NULL)
+ results[n_translations] = __strdup (start);
+ else
+ results[n_translations] = __strndup (start, end - start);
+ if (results[n_translations] == NULL)
+ {
+ /* Abort. */
+ for (size_t i = 0; i < n_translations; i++)
+ free (results[i]);
+ free (results);
+ return NULL;
+ }
+ }
+ start = end;
+ if (start != NULL)
+ /* Skip ' ' */
+ start++;
+ n_translations++;
+ }
+ if (pass == 0)
+ results = calloc (n_translations + 1, sizeof (char *));
+ if (results == NULL)
+ return NULL;
}
- /* FIXME: is it safe to discard *allocated early here? Won’t the
- return value alias it? */
- /* *allocated is to be freed by the caller. */
- return translated;
+ /* We did not touch that index, and allocated the array with
+ calloc. */
+ assert (results[n_translations] == NULL);
+ return results;
}
/* Print help for ENTRY to STREAM. */
@@ -1245,7 +1290,7 @@ hol_entry_help (struct hol_entry *entry, const struct argp_state *state,
unsigned num;
const struct argp_option *real = entry->opt, *opt;
char *so = entry->short_options;
- const char *translated_option_name;
+ char **translated_option_names;
int have_long_opt = 0; /* We have any long options. */
/* Saved margins. */
int old_lm = __argp_fmtstream_set_lmargin (stream, 0);
@@ -1304,19 +1349,35 @@ hol_entry_help (struct hol_entry *entry, const struct argp_state *state,
else
/* A real long option. */
{
+ bool needs_untranslated = true;
__argp_fmtstream_set_wmargin (stream, uparams.long_opt_col);
for (opt = real, num = entry->num; num > 0; opt++, num--)
if (opt->name && ovisible (opt))
{
comma (uparams.long_opt_col, &pest);
- char *name_allocated = NULL;
- translated_option_name = translate_option_name (opt->name, &name_allocated);
- __argp_fmtstream_printf (stream, "--%s", translated_option_name);
- arg (real, "=%s", "[=%s]",
- state == NULL ? NULL : state->root_argp->argp_domain, stream);
- if (strcmp (translated_option_name, opt->name))
+ translated_option_names = translate_option_name (opt->name);
+ for (size_t i = 0;
+ (translated_option_names != NULL
+ && translated_option_names[i] != NULL);
+ i++)
+ {
+ if (i != 0)
+ __argp_fmtstream_printf (stream, ", ");
+ __argp_fmtstream_printf (stream, "--%s", translated_option_names[i]);
+ /* Only display the argument for the first translation. */
+ if (i == 0)
+ arg (real, "=%s", "[=%s]",
+ state == NULL ? NULL : state->root_argp->argp_domain, stream);
+ /* If we see the untranslated name, we won’t repeat it. */
+ if (strcmp (translated_option_names[i], opt->name) == 0)
+ needs_untranslated = false;
+ free (translated_option_names[i]);
+ }
+ free (translated_option_names);
+ if (needs_untranslated)
__argp_fmtstream_printf (stream, " (--%s)", opt->name);
- free (name_allocated);
+ /* If memory allocation failed, the --help output will
+ just display the untranslated name in parenthesis. */
}
}
@@ -1458,39 +1519,46 @@ usage_long_opt (const struct argp_option *opt,
{
argp_fmtstream_t stream = cookie;
const char *arg = opt->arg;
- const char *translated_option_name = opt->name;
+ char **translated_option_names = NULL;
int flags = opt->flags | real->flags;
+ bool needs_untranslated = true;
if (! arg)
arg = real->arg;
if (! (flags & OPTION_NO_USAGE))
{
- char *name_allocated = NULL;
- translated_option_name =
- translate_option_name (opt->name, &name_allocated);
- int translation_differs =
- (strcmp (translated_option_name, opt->name) != 0);
+ translated_option_names =
+ translate_option_name (opt->name);
+ __argp_fmtstream_printf (stream, " [");
if (arg)
+ arg = dgettext (domain, arg);
+ for (size_t i = 0;
+ (translated_option_names != NULL
+ && translated_option_names[i] != NULL);
+ i++)
{
- arg = dgettext (domain, arg);
- if ((flags & OPTION_ARG_OPTIONAL) && translation_differs)
- __argp_fmtstream_printf (stream, " [--%s[=%s] (--%s)]",
- translated_option_name, arg, opt->name);
- else if (flags & OPTION_ARG_OPTIONAL)
- __argp_fmtstream_printf (stream, " [--%s[=%s]]", opt->name, arg);
- else if (translation_differs)
- __argp_fmtstream_printf (stream, " [--%s=%s (--%s)]",
- translated_option_name, arg, opt->name);
- else
- __argp_fmtstream_printf (stream, " [--%s=%s]", opt->name, arg);
+ if (i != 0)
+ __argp_fmtstream_printf (stream, " / ");
+ __argp_fmtstream_printf (stream, "--%s", translated_option_names[i]);
+ if (arg && i == 0)
+ {
+ if (flags & OPTION_ARG_OPTIONAL)
+ __argp_fmtstream_printf (stream, "[=%s]", arg);
+ else
+ __argp_fmtstream_printf (stream, "=%s", arg);
+ }
+ if (strcmp (translated_option_names[i], opt->name) == 0)
+ needs_untranslated = false;
+ free (translated_option_names[i]);
}
- else if (translation_differs)
- __argp_fmtstream_printf (stream, " [--%s (--%s)]",
- translated_option_name, opt->name);
- else
- __argp_fmtstream_printf (stream, " [--%s]", opt->name);
- free (name_allocated);
+ free (translated_option_names);
+ if (needs_untranslated)
+ __argp_fmtstream_printf (stream, " (--%s)", opt->name);
+ __argp_fmtstream_printf (stream, "]");
+ /* If memory allocation failed, the output will be like
+ [ (--option)]
+ */
}
return 0;
@@ -44,6 +44,8 @@ const struct argp_option options[] =
};
static bool color_set = false;
+static bool flavor_set = false;
+static bool texture_set = false;
static error_t
parse_opt (int key, char *arg, struct argp_state *state)
@@ -53,6 +55,14 @@ parse_opt (int key, char *arg, struct argp_state *state)
FAIL ("color already set.\n");
else if (key == 'c')
color_set = true;
+ else if (key == 'f' && flavor_set)
+ FAIL ("flavor already set.\n");
+ else if (key == 'f')
+ flavor_set = true;
+ else if (key == 't' && texture_set)
+ FAIL ("texture already set.\n");
+ else if (key == 't')
+ texture_set = true;
return 0;
}
@@ -64,6 +74,16 @@ do_test (void)
char *test1_argv[3] =
{ (char *) "/bin/tst-argphelp-localized", (char *) "--colour=yellow", NULL };
char *test2_argv[3] =
+ { (char *) "/bin/tst-argphelp-localized", (char *) "--color=yellow", NULL };
+ char *test3_argv[3] =
+ { (char *) "/bin/tst-argphelp-localized", (char *) "--coolur=yellow", NULL };
+ char *test4_argv[3] =
+ { (char *) "/bin/tst-argphelp-localized", (char *) "--flavour", NULL };
+ char *test5_argv[3] =
+ { (char *) "/bin/tst-argphelp-localized", (char *) "--flavor", NULL };
+ char *test6_argv[3] =
+ { (char *) "/bin/tst-argphelp-localized", (char *) "--texture", NULL };
+ char *test7_argv[3] =
{ (char *) "/bin/tst-argphelp-localized", (char *) "--help", NULL };
unsetenv ("LANGUAGE");
@@ -72,18 +92,47 @@ do_test (void)
OBJPFX "domaindir") != NULL);
TEST_VERIFY_EXIT (textdomain ("tst-argphelp-localized") != NULL);
/* Check that the catalog is OK: */
- TEST_COMPARE_STRING (gettext ("command-line option\004color"), "colour");
+ TEST_COMPARE_STRING (gettext ("command-line option\004color"),
+ "colour coolur");
TEST_COMPARE_STRING (gettext ("COOKIE"), "BISCUIT");
argp_parse (&argp, 2, test1_argv, 0, 0, NULL);
TEST_VERIFY (color_set);
+ TEST_VERIFY (!flavor_set);
+ TEST_VERIFY (!texture_set);
color_set = false;
+ argp_parse (&argp, 2, test2_argv, 0, 0, NULL);
+ TEST_VERIFY (color_set);
+ TEST_VERIFY (!flavor_set);
+ TEST_VERIFY (!texture_set);
+ color_set = false;
+ argp_parse (&argp, 2, test3_argv, 0, 0, NULL);
+ TEST_VERIFY (color_set);
+ TEST_VERIFY (!flavor_set);
+ TEST_VERIFY (!texture_set);
+ color_set = false;
+ argp_parse (&argp, 2, test4_argv, 0, 0, NULL);
+ TEST_VERIFY (!color_set);
+ TEST_VERIFY (flavor_set);
+ TEST_VERIFY (!texture_set);
+ flavor_set = false;
+ argp_parse (&argp, 2, test5_argv, 0, 0, NULL);
+ TEST_VERIFY (!color_set);
+ TEST_VERIFY (flavor_set);
+ TEST_VERIFY (!texture_set);
+ flavor_set = false;
+ argp_parse (&argp, 2, test6_argv, 0, 0, NULL);
+ TEST_VERIFY (!color_set);
+ TEST_VERIFY (!flavor_set);
+ TEST_VERIFY (texture_set);
+ texture_set = false;
/* This is the last chance to fail. */
if (support_record_failure_is_failed ())
- FAIL_EXIT1 ("There were test failures before the final invocation of --help");
+ FAIL_EXIT1 (
+ "There were test failures before the final invocation of --help");
/* This last test will exit the program with code 0 and ignore
previous failures. */
- argp_parse (&argp, 2, test2_argv, 0, 0, NULL);
+ argp_parse (&argp, 2, test7_argv, 0, 0, NULL);
FAIL_EXIT1 ("--help did not exit the program");
return 0;
}
@@ -15,7 +15,7 @@ msgstr ""
#: tst-argphelp-localized.c:73
msgctxt "command-line option"
msgid "color"
-msgstr "colour"
+msgstr "colour coolur"
msgctxt "command-line option"
msgid "flavor"
@@ -23,3 +23,4 @@ msgstr "flavour"
msgid "COOKIE"
msgstr "BISCUIT"
+
@@ -64,7 +64,8 @@ do_test (void)
OBJPFX "domaindir") != NULL);
TEST_VERIFY_EXIT (textdomain ("tst-argphelp-localized") != NULL);
/* Check that the catalog is OK: */
- TEST_COMPARE_STRING (gettext ("command-line option\004color"), "colour");
+ TEST_COMPARE_STRING (gettext ("command-line option\004color"),
+ "colour coolur");
TEST_COMPARE_STRING (gettext ("COOKIE"), "BISCUIT");
/* This is the last chance to fail. */
if (support_record_failure_is_failed ())
@@ -257,6 +257,11 @@ invocation of your program, the program users should be encouraged to
use untranslated option names or publish the locale used for this
invocation.
+If the translation of an option name contains a space character, then
+it means multiple translations recognize the same option name. This
+is useful to upgrade a translation without disrupting the user's
+workflow.
+
Since option names may be short words instead of long sentences, they
may have different translations in different contexts within the same
program. @xref{Contexts, , Using contexts for solving ambiguities,
@@ -146,7 +146,8 @@ while (my $line = <$pofile>) {
} elsif ($parser_state == 3 && $line eq "") {
$parser_state = 1;
} elsif ($parser_state == 3 && $line =~ /^msgstr\s*"([^"]*)"$/) {
- $translations{$entry_msgid} = $1;
+ my @translations_for_this = split(/\s+/, $1);
+ $translations{$entry_msgid} = \@translations_for_this;
$parser_state = 1;
}
}
@@ -156,13 +157,14 @@ my $number_of_errors = 0;
# Verify that every option name is unique.
my %untranslated_name;
for my $option_name (sort(keys %translations)) {
- my $translation = $translations{$option_name};
- my @existing;
- if (exists $untranslated_name{$translation}) {
- @existing = @{$untranslated_name{$translation}};
+ for my $translation (@{$translations{$option_name}}) {
+ my @existing;
+ if (exists $untranslated_name{$translation}) {
+ @existing = @{$untranslated_name{$translation}};
+ }
+ push(@existing, $option_name);
+ $untranslated_name{$translation} = \@existing;
}
- push(@existing, $option_name);
- $untranslated_name{$translation} = \@existing;
}
for my $translation (sort(keys %untranslated_name)) {
my $names = $untranslated_name{$translation};
@@ -180,10 +182,12 @@ for my $translation (sort(keys %untranslated_name)) {
for my $option_name (sort(keys %translations)) {
for my $other_option_name (sort(keys %translations)) {
if ($option_name ne $other_option_name) {
- if ($translations{$option_name} eq $other_option_name) {
- print STDERR "${translations{$option_name}} is a translation of ${option_name}, but it is also a different option.\n";
- ++$number_of_errors;
- }
+ for my $translation (@{$translations{$option_name}}) {
+ if ($translation eq $other_option_name) {
+ print STDERR "${translation} is a translation of ${option_name}, but it is also a different option.\n";
+ ++$number_of_errors;
+ }
+ }
}
}
}
@@ -182,6 +182,50 @@ exchange (char **argv, struct _getopt_data *d)
d->__last_nonopt = d->optind;
}
+/* Return TRUE if other is equal to reference. */
+static bool
+complete_match (const char *reference,
+ size_t reference_length,
+ const char *other)
+{
+ return (strncmp (reference, other, reference_length) == 0
+ /* So, reference is a prefix of other */
+ && other[reference_length] == '\0');
+}
+
+/* Match a string against a space-separated string list. We save an
+ allocated copy of the exact match for the collision checker. */
+static bool
+match_any_translation (const char *reference,
+ size_t reference_length,
+ const char *translation,
+ char **match)
+{
+ const char *start = translation;
+ const char *end;
+
+ if (match)
+ *match = NULL;
+ while (start != NULL)
+ {
+ end = strchr (start, ' ');
+ if ((end == NULL && complete_match (reference, reference_length, start))
+ || (end != NULL
+ && end - start == reference_length
+ && strncmp (reference, start, reference_length) == 0))
+ {
+ if (match)
+ *match = __strndup (reference, reference_length);
+ return true;
+ }
+ start = end;
+ if (start != NULL)
+ /* Skip the space character. */
+ start++;
+ }
+ return false;
+}
+
/* Return true iff translation_context is not NULL, a translation for
opt_name has been found and it matches the substring from argument,
length argument_length.
@@ -202,16 +246,42 @@ match_translated_option_name (char *(*translate) (const char *, const char *,
if (translate != NULL && !__libc_enable_secure)
translated = translate (opt_textdomain, translation_context,
opt_name, &translation_buffer);
-
- if (strncmp (translated, argument, argument_length) != 0)
- matches = false;
- else
- /* We know that argument is a prefix of translated. */
- matches = translated[argument_length] == '\0';
+ matches = match_any_translation (argument, argument_length, translated, NULL);
free (translation_buffer);
return matches;
}
+/* Translate opt_name, but only keep the first item of the list. This
+ is used for error messages. */
+static const char *
+first_translation (char *(*translate) (const char *, const char *,
+ const char *, char **),
+ const char *translation_context,
+ const char *opt_textdomain,
+ const char *opt_name,
+ char **allocated)
+{
+ char *translation_buffer = NULL;
+ const char *all_translations =
+ translate (opt_textdomain, translation_context, opt_name,
+ &translation_buffer);
+ const char *end = strchr (all_translations, ' ');
+ if (end == NULL)
+ {
+ /* There is only 1 translation for opt_name. No extra
+ processing is needed. */
+ *allocated = translation_buffer;
+ return all_translations;
+ }
+ *allocated = __strndup (all_translations, end - all_translations);
+ free (translation_buffer);
+ if (*allocated == NULL)
+ /* Memory allocation failed; return opt_name. No extra memory
+ needs to be kept around. */
+ return opt_name;
+ return *allocated;
+}
+
/* Process the argument starting with d->__nextchar as a long option.
d->optind should *not* have been advanced over this argument.
@@ -393,9 +463,9 @@ process_long_option (int argc, char **argv, const char *optstring,
{
if (print_errors)
{
- translated_option_name = translate (d->opttextdomain, d->optctxt,
- pfound->name,
- &translation_buffer);
+ translated_option_name =
+ first_translation (translate, d->optctxt, d->opttextdomain,
+ pfound->name, &translation_buffer);
if (strcmp (translated_option_name, pfound->name) != 0)
/* Print both names of the option. */
fprintf (stderr,
@@ -423,9 +493,9 @@ process_long_option (int argc, char **argv, const char *optstring,
{
/* Same dichotomy as when the option does not allow an
argument. */
- translated_option_name = translate (d->opttextdomain, d->optctxt,
- pfound->name,
- &translation_buffer);
+ translated_option_name =
+ first_translation (translate, d->optctxt, d->opttextdomain,
+ pfound->name, &translation_buffer);
if (strcmp (translated_option_name, pfound->name) != 0)
fprintf (stderr,
_("%s: option '%s%s' / '%s%s' requires an argument\n"),
@@ -488,6 +558,32 @@ _getopt_initialize (_GL_UNUSED int argc,
}
+/* Match any item of a string list against any item of another. This
+ is used by the collision checker. */
+static bool
+match_any_translation_pair (const char *list_a,
+ const char *list_b,
+ char **match)
+{
+ const char *start = list_a;
+ const char *end;
+
+ while (start != NULL)
+ {
+ end = strchr (start, ' ');
+ if ((end == NULL
+ && match_any_translation (start, strlen (start), list_b, match))
+ || (end != NULL
+ && match_any_translation (start, end - start, list_b, match)))
+ return true;
+ start = end;
+ if (start != NULL)
+ /* Skip the space character. */
+ start++;
+ }
+ return false;
+}
+
static bool
has_translation_collisions (const char *domain,
const char *context,
@@ -508,6 +604,7 @@ has_translation_collisions (const char *domain,
char *b_buffer = NULL;
const char *b_name = NULL;
const struct option *option_b;
+ char *collision = NULL;
bool has_collision = false;
if (do_translate == NULL || context == NULL)
@@ -532,7 +629,9 @@ has_translation_collisions (const char *domain,
{
option_b = &(long_options[option_index_b]);
b_name = do_translate (domain, context, option_b->name, &b_buffer);
- if (strcmp (option_a->name, b_name) == 0)
+ collision = NULL;
+ if (match_any_translation (option_a->name, strlen (option_a->name), b_name,
+ &collision))
{
if (print_errors)
/* Since we do not consider a particular use of an
@@ -546,13 +645,15 @@ has_translation_collisions (const char *domain,
argv0,
domain, context,
option_a->name,
- option_b->name, b_name);
+ option_b->name, collision);
has_collision = true;
}
- if (strcmp (a_name, b_name) == 0
+ free (collision);
+ collision = NULL;
+ if (option_index_a < option_index_b
&& strcmp (option_a->name, a_name) != 0
&& strcmp (option_b->name, b_name) != 0
- && option_index_a < option_index_b)
+ && match_any_translation_pair (a_name, b_name, &collision))
{
if (print_errors)
fprintf (stderr,
@@ -560,9 +661,10 @@ has_translation_collisions (const char *domain,
"domain '%s', context '%s': "
"both '%s' and '%s' translate to '%s'\n"),
argv0, domain, context,
- option_a->name, option_b->name, a_name);
+ option_a->name, option_b->name, collision);
has_collision = true;
}
+ free (collision);
free (b_buffer);
}
free (a_buffer);
@@ -27,17 +27,17 @@ msgstr "bar"
# This is the --foo option.
msgctxt "command-line option"
msgid "foo"
-msgstr "toto"
+msgstr "tata toto"
# This is the --bar option. Oops, I translated with toto here too.
msgctxt "command-line option"
msgid "bar"
-msgstr "toto"
+msgstr "titi toto"
# Let’s go to the --pub!
msgctxt "command-line option"
msgid "pub"
-msgstr "bar"
+msgstr "bar club"
# Wait, it’s OK if baz is translated to baz though.
msgctxt "command-line option"
@@ -42,6 +42,8 @@
In the third, we don’t translate anything:
foo -> foo
bar -> bar
+
+ For added fun, we add some noise to the translations.
*/
static const struct option options[] =
@@ -62,13 +64,13 @@ setup_catalog (void)
TEST_VERIFY_EXIT (textdomain ("tst-getopt_long_collision") != NULL);
/* Check that the catalog is OK: */
TEST_COMPARE_STRING (dgettext ("tst-getopt_long_collision", "kind 1\004foo"),
- "bar");
+ "bar noise1");
TEST_COMPARE_STRING (dgettext ("tst-getopt_long_collision", "kind 1\004bar"),
- "baz");
+ "noise2 baz noise3");
TEST_COMPARE_STRING (dgettext ("tst-getopt_long_collision", "kind 2\004foo"),
- "same");
+ "same noise4");
TEST_COMPARE_STRING (dgettext ("tst-getopt_long_collision", "kind 2\004bar"),
- "same");
+ "noise5 same");
TEST_COMPARE_STRING (dgettext ("tst-getopt_long_collision", "kind 3\004foo"),
"kind 3\004foo");
TEST_COMPARE_STRING (dgettext ("tst-getopt_long_collision", "kind 3\004bar"),
@@ -17,16 +17,16 @@ msgstr ""
msgctxt "kind 1"
msgid "foo"
-msgstr "bar"
+msgstr "bar noise1"
msgctxt "kind 1"
msgid "bar"
-msgstr "baz"
+msgstr "noise2 baz noise3"
msgctxt "kind 2"
msgid "foo"
-msgstr "same"
+msgstr "same noise4"
msgctxt "kind 2"
msgid "bar"
-msgstr "same"
+msgstr "noise5 same"
@@ -31,10 +31,27 @@
This echoes tstgetopt.c, where --colour was an option name alias
for --color, so it had to be listed twice. */
-/* This uses the en_GB locale so that colour means color. We also
- check that getopt only matches translations for actual options, by
- having the user pass --flavour (which is a known translation of
- flavor) without the program recognizing a --flavor option. */
+/* This uses the en_GB locale so that colour means color.
+
+ Oh no! The translator made a mistake and translated color as
+ “coolur”. A bug-fix has been released, but in the mean time, a
+ popular British influencer made a blog post about how you can use
+ “coolur” and it turned into a meme. Lots of people have
+ copy-pasted a custom script to check if the program supports
+ British values, and it does so by checking whether --coolur prints
+ “as red as a sunburnt tourist”. To the point where ChatGPT and
+ other LLMs now consider this the pinnacle of British
+ exceptionalism. The UK MPs have voted to make the script a
+ mandatory part of any operating systems used by British people
+ anywhere in the world, with severe fines for anyone not using the
+ exact version prescribed by the law. It has thus been decided to
+ support both “colour” and “coolur”, without creating a new option
+ (only “color” exists for the developers).
+
+ We also check that getopt only matches
+ translations for actual options, by having the user pass --flavour
+ (which is a known translation of flavor) without the program
+ recognizing a --flavor option. */
#define TRANSLATION_CONTEXT "command-line option"
@@ -48,7 +65,7 @@ prepare_localedir (void)
/* Check that the catalog is OK: */
TEST_COMPARE_STRING (dgettext ("tstgetoptl",
TRANSLATION_CONTEXT "\004" "color"),
- "colour");
+ "colour coolur");
TEST_COMPARE_STRING (dgettext ("tstgetoptl",
TRANSLATION_CONTEXT "\004" "flavor"),
"flavour");
@@ -61,7 +78,7 @@ prepare_argv (int *argc)
{
(char *) "tstgetoptl", (char *) "--required", (char *) "foobar",
(char *) "--optional=bazbug", (char *) "--col", (char *) "--color",
- (char *) "--colour", (char *) "--flavour", NULL
+ (char *) "--colour", (char *) "--coolur", (char *) "--flavour", NULL
};
*argc = array_length (argv) - 1;
return argv;
@@ -79,7 +96,8 @@ do_my_test (bool with_optctxt)
{"required", required_argument, NULL, 'r'},
{"optional", optional_argument, NULL, 'o'},
{"color", no_argument, NULL, 'C'},
- /* Now colour is handled as a translation of color. */
+ /* Now colour (and coolur) are handled as a translation of
+ color. */
/* Note that there’s no "--flavor" option, so the "flavor" ->
"flavour" translation is useless. */
{NULL, 0, NULL, 0 }
@@ -139,7 +157,7 @@ do_my_test (bool with_optctxt)
printf ("Cflags = %d\n", Cflag);
if (with_optctxt)
- TEST_COMPARE (Cflag, 3);
+ TEST_COMPARE (Cflag, 4);
else
TEST_COMPARE (Cflag, 2);
@@ -7,7 +7,7 @@ msgstr ""
"Project-Id-Version: tstgetoptl 0.0.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-05-27 19:29+0200\n"
-"PO-Revision-Date: 2025-05-27 19:30+0200\n"
+"PO-Revision-Date: 2025-06-06 21:22+0200\n"
"Language-Team: English (British) <(nothing)>\n"
"Language: en_GB\n"
"MIME-Version: 1.0\n"
@@ -18,7 +18,7 @@ msgstr ""
#: xxx.c:yy
msgctxt "command-line option"
msgid "color"
-msgstr "colour"
+msgstr "colour coolur"
#: xxx.c:yy
msgctxt "command-line option"