]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
Add support for translation lookup with context in shell scripts.
authorBruno Haible <bruno@clisp.org>
Sat, 15 Sep 2018 20:54:24 +0000 (22:54 +0200)
committerBruno Haible <bruno@clisp.org>
Sat, 15 Sep 2018 21:01:11 +0000 (23:01 +0200)
Reported by Markus Gothe <nietzsche@lysator.liu.se>
in <https://savannah.gnu.org/bugs/?49899>.

* gettext-runtime/src/gettext.c (long_options): Add option '--context'.
(main): Handle option -c/--context.
(usage): Document option -c/--context. Split usage message.
* gettext-runtime/src/ngettext.c (long_options): Add option '--context'.
(main): Handle option -c/--context.
(usage): Document option -c/--context. Split usage message.
* gettext-runtime/src/gettext.sh.in (eval_pgettext, eval_npgettext): New
functions.
* gettext-tools/src/xgettext.h (struct arglist_parser): Add 'next_is_msgctxt'
field.
(arglist_parser_remember_msgctxt): New declaration.
* gettext-tools/src/xgettext.c (arglist_parser_alloc, arglist_parser_clone):
Update accordingly.
(arglist_parser_remember_msgctxt): New function.
* gettext-tools/src/x-sh.c (init_keywords): Recognize also eval_pgettext and
eval_npgettext.
(init_flag_table_sh): Set flags for eval_pgettext, eval_npgettext.
(substring_of_word): New function.
(read_command): Recognize and handle -c/--context argument of 'gettext' and
'ngettext'.
* gettext-tools/tests/lang-sh: Add test of message lookup with context.
* gettext-runtime/doc/rt-gettext.texi: Mention the --context option.
* gettext-runtime/doc/rt-ngettext.texi: Likewise.
* gettext-tools/doc/gettext.texi (sh, Preparing Shell Scripts, gettext.sh):
Mention the new shell functions.
(eval_pgettext Invocation, eval_npgettext Invocation): New subsubsections.
* gettext-tools/doc/xgettext.texi: Mention the support for eval_pgettext,
eval_npgettext.
* NEWS: Mention the changes.

12 files changed:
NEWS
gettext-runtime/doc/rt-gettext.texi
gettext-runtime/doc/rt-ngettext.texi
gettext-runtime/src/gettext.c
gettext-runtime/src/gettext.sh.in
gettext-runtime/src/ngettext.c
gettext-tools/doc/gettext.texi
gettext-tools/doc/xgettext.texi
gettext-tools/src/x-sh.c
gettext-tools/src/xgettext.c
gettext-tools/src/xgettext.h
gettext-tools/tests/lang-sh

diff --git a/NEWS b/NEWS
index 15d309d8d48d754341f5b198eda8c8091f60618a..a6d7771406819d220a2ab9e9f2515c9bc31b6656 100644 (file)
--- a/NEWS
+++ b/NEWS
   - C++:
     xgettext now supports single-quotes in number tokens, as specified in
     C++14.
+  - Shell:
+    o The programs 'gettext', 'ngettext' now support a --context argument.
+    o gettext.sh contains new function eval_pgettext and eval_npgettext
+      for producing translations of messages with context.
   - Java:
     o xgettext now supports UTF-8 encoded .properties files (a new feature
       of Java 9).
index 68dc3fe78e739784eb03e02314a655957e05b5b7..884041896c98911acbfb3259e6767b516bb712ea 100644 (file)
@@ -12,6 +12,13 @@ textual message.
 @noindent @strong{Arguments}
 
 @table @samp
+@item -c @var{context}
+@itemx --context=@var{context}
+@opindex -c@r{, @code{gettext} option}
+@opindex --context@r{, @code{gettext} option}
+Specify the context for the messages to be translated.
+See @ref{Contexts} for details.
+
 @item -d @var{textdomain}
 @itemx --domain=@var{textdomain}
 @opindex -d@r{, @code{gettext} option}
index 1b8c5a7032b788965b8cce66decd018ea3b50d55..acb589c5465f8ec6085d00be094fe50e28a71b2e 100644 (file)
@@ -11,6 +11,13 @@ textual message whose grammatical form depends on a number.
 @noindent @strong{Arguments}
 
 @table @samp
+@item -c @var{context}
+@itemx --context=@var{context}
+@opindex -c@r{, @code{ngettext} option}
+@opindex --context@r{, @code{ngettext} option}
+Specify the context for the messages to be translated.
+See @ref{Contexts} for details.
+
 @item -d @var{textdomain}
 @itemx --domain=@var{textdomain}
 @opindex -d@r{, @code{ngettext} option}
index 32d6d49711b0d9d9762d679ca86e1cec00b47f51..f14675f0230c590f68c225fe940836cebb950bc2 100644 (file)
@@ -49,6 +49,7 @@ static bool do_expand;
 /* Long options.  */
 static const struct option long_options[] =
 {
+  { "context", required_argument, NULL, 'c' },
   { "domain", required_argument, NULL, 'd' },
   { "help", no_argument, NULL, 'h' },
   { "shell-script", no_argument, NULL, 's' },
@@ -76,6 +77,7 @@ main (int argc, char *argv[])
   bool do_version = false;
   const char *domain = getenv ("TEXTDOMAIN");
   const char *domaindir = getenv ("TEXTDOMAINDIR");
+  const char *context = NULL;
   add_newline = true;
   do_expand = false;
 
@@ -95,12 +97,15 @@ main (int argc, char *argv[])
   atexit (close_stdout);
 
   /* Parse command line options.  */
-  while ((optchar = getopt_long (argc, argv, "+d:eEhnsV", long_options, NULL))
+  while ((optchar = getopt_long (argc, argv, "+c:d:eEhnsV", long_options, NULL))
          != EOF)
     switch (optchar)
     {
     case '\0':          /* Long option.  */
       break;
+    case 'c':
+      context = optarg;
+      break;
     case 'd':
       domain = optarg;
       break;
@@ -186,7 +191,10 @@ There is NO WARRANTY, to the extent permitted by law.\n\
             bindtextdomain (domain, domaindir);
 
           /* Write out the result.  */
-          fputs (dgettext (domain, msgid), stdout);
+          fputs ((context != NULL
+                  ? dpgettext_expr (domain, context, msgid)
+                  : dgettext (domain, msgid)),
+                 stdout);
         }
     }
   else
@@ -212,7 +220,10 @@ There is NO WARRANTY, to the extent permitted by law.\n\
                 msgid = expand_escape (msgid);
 
               /* Write out the result.  */
-              fputs (domain == NULL ? msgid : dgettext (domain, msgid),
+              fputs ((domain == NULL ? msgid :
+                      context != NULL
+                      ? dpgettext_expr (domain, context, msgid)
+                      : dgettext (domain, msgid)),
                      stdout);
 
               /* We separate the arguments by a single ' '.  */
@@ -252,15 +263,26 @@ Display native language translation of a textual message.\n"));
       printf ("\n");
       /* xgettext: no-wrap */
       printf (_("\
-  -d, --domain=TEXTDOMAIN   retrieve translated messages from TEXTDOMAIN\n\
-  -e                        enable expansion of some escape sequences\n\
-  -E                        (ignored for compatibility)\n\
-  -h, --help                display this help and exit\n\
-  -n                        suppress trailing newline\n\
-  -V, --version             display version information and exit\n\
+  -d, --domain=TEXTDOMAIN   retrieve translated messages from TEXTDOMAIN\n"));
+      printf (_("\
+  -c, --context=CONTEXT     specify context for MSGID\n"));
+      printf (_("\
+  -e                        enable expansion of some escape sequences\n"));
+      printf (_("\
+  -n                        suppress trailing newline\n"));
+      printf (_("\
+  -E                        (ignored for compatibility)\n"));
+      printf (_("\
   [TEXTDOMAIN] MSGID        retrieve translated message corresponding\n\
                             to MSGID from TEXTDOMAIN\n"));
       printf ("\n");
+      printf (_("\
+Informative output:\n"));
+      printf (_("\
+  -h, --help                display this help and exit\n"));
+      printf (_("\
+  -V, --version             display version information and exit\n"));
+      printf ("\n");
       /* xgettext: no-wrap */
       printf (_("\
 If the TEXTDOMAIN parameter is not given, the domain is determined from the\n\
index 06dc04dc7ec4361b971a2bc3bb90c4d8a910cddc..218e998c5479a3a3604b1887d30349cab2868b5c 100644 (file)
@@ -95,6 +95,20 @@ eval_ngettext () {
   ngettext "$1" "$2" "$3" | (export PATH `envsubst --variables "$1 $2"`; envsubst "$1 $2")
 }
 
+# eval_pgettext MSGCTXT MSGID
+# looks up the translation of MSGID in the context MSGCTXT and substitutes
+# shell variables in the result.
+eval_pgettext () {
+  gettext --context="$1" "$2" | (export PATH `envsubst --variables "$2"`; envsubst "$2")
+}
+
+# eval_npgettext MSGCTXT MSGID MSGID-PLURAL COUNT
+# looks up the translation of MSGID / MSGID-PLURAL for COUNT in the context
+# MSGCTXT and substitutes shell variables in the result.
+eval_npgettext () {
+  ngettext --context="$1" "$2" "$3" "$4" | (export PATH `envsubst --variables "$2 $3"`; envsubst "$2 $3")
+}
+
 # Note: This use of envsubst is much safer than using the shell built-in 'eval'
 # would be.
 # 1) The security problem with Chinese translations that happen to use a
index ae307099948a297ac3e0876b971b336dea20d19c..59a0126b9b5b5832b7faa3e3bae59f3a197a6e93 100644 (file)
@@ -44,6 +44,7 @@ static int do_expand;
 /* Long options.  */
 static const struct option long_options[] =
 {
+  { "context", required_argument, NULL, 'c' },
   { "domain", required_argument, NULL, 'd' },
   { "help", no_argument, NULL, 'h' },
   { "version", no_argument, NULL, 'V' },
@@ -72,6 +73,7 @@ main (int argc, char *argv[])
   bool do_version = false;
   const char *domain = getenv ("TEXTDOMAIN");
   const char *domaindir = getenv ("TEXTDOMAINDIR");
+  const char *context = NULL;
   do_expand = false;
 
   /* Set program name for message texts.  */
@@ -90,12 +92,15 @@ main (int argc, char *argv[])
   atexit (close_stdout);
 
   /* Parse command line options.  */
-  while ((optchar = getopt_long (argc, argv, "+d:eEhV", long_options, NULL))
+  while ((optchar = getopt_long (argc, argv, "+c:d:eEhV", long_options, NULL))
          != EOF)
     switch (optchar)
     {
     case '\0':          /* Long option.  */
       break;
+    case 'c':
+      context = optarg;
+      break;
     case 'd':
       domain = optarg;
       break;
@@ -192,7 +197,10 @@ There is NO WARRANTY, to the extent permitted by law.\n\
         bindtextdomain (domain, domaindir);
 
       /* Write out the result.  */
-      fputs (dngettext (domain, msgid, msgid_plural, n), stdout);
+      fputs ((context != NULL
+              ? dnpgettext_expr (domain, context, msgid, msgid_plural, n)
+              : dngettext (domain, msgid, msgid_plural, n)),
+             stdout);
     }
 
   exit (EXIT_SUCCESS);
@@ -220,14 +228,26 @@ form depends on a number.\n"));
       printf ("\n");
       /* xgettext: no-wrap */
       printf (_("\
-  -d, --domain=TEXTDOMAIN   retrieve translated message from TEXTDOMAIN\n\
-  -e                        enable expansion of some escape sequences\n\
-  -E                        (ignored for compatibility)\n\
-  -h, --help                display this help and exit\n\
-  -V, --version             display version information and exit\n\
-  [TEXTDOMAIN]              retrieve translated message from TEXTDOMAIN\n\
-  MSGID MSGID-PLURAL        translate MSGID (singular) / MSGID-PLURAL (plural)\n\
+  -d, --domain=TEXTDOMAIN   retrieve translated message from TEXTDOMAIN\n"));
+      printf (_("\
+  -c, --context=CONTEXT     specify context for MSGID\n"));
+      printf (_("\
+  -e                        enable expansion of some escape sequences\n"));
+      printf (_("\
+  -E                        (ignored for compatibility)\n"));
+      printf (_("\
+  [TEXTDOMAIN]              retrieve translated message from TEXTDOMAIN\n"));
+      printf (_("\
+  MSGID MSGID-PLURAL        translate MSGID (singular) / MSGID-PLURAL (plural)\n"));
+      printf (_("\
   COUNT                     choose singular/plural form based on this value\n"));
+      printf ("\n");
+      printf (_("\
+Informative output:\n"));
+      printf (_("\
+  -h, --help                display this help and exit\n"));
+      printf (_("\
+  -V, --version             display version information and exit\n"));
       printf ("\n");
       /* xgettext: no-wrap */
       printf (_("\
index ca8c638fbd87c3e98914bc9cd0c395f3edda5852..fc8fa879fc15327e2f3cbffb1e23b00999852689 100644 (file)
@@ -450,6 +450,8 @@ sh - Shell Script
 * envsubst Invocation::         Invoking the @code{envsubst} program
 * eval_gettext Invocation::     Invoking the @code{eval_gettext} function
 * eval_ngettext Invocation::    Invoking the @code{eval_ngettext} function
+* eval_pgettext Invocation::    Invoking the @code{eval_pgettext} function
+* eval_npgettext Invocation::   Invoking the @code{eval_npgettext} function
 
 Perl
 
@@ -9582,7 +9584,8 @@ bash, gettext-base
 @pindex gettext
 @pindex ngettext
 @code{gettext}, @code{ngettext} programs
-@*@code{eval_gettext}, @code{eval_ngettext} shell functions
+@*@code{eval_gettext}, @code{eval_ngettext}, @code{eval_pgettext},
+@code{eval_npgettext} shell functions
 
 @item textdomain
 @vindex TEXTDOMAIN@r{, environment variable}
@@ -9624,6 +9627,8 @@ An example is available in the @file{examples} directory: @code{hello-sh}.
 * envsubst Invocation::         Invoking the @code{envsubst} program
 * eval_gettext Invocation::     Invoking the @code{eval_gettext} function
 * eval_ngettext Invocation::    Invoking the @code{eval_ngettext} function
+* eval_pgettext Invocation::    Invoking the @code{eval_pgettext} function
+* eval_npgettext Invocation::   Invoking the @code{eval_npgettext} function
 @end menu
 
 @node Preparing Shell Scripts, gettext.sh, sh, sh
@@ -9644,8 +9649,10 @@ Insert the line
 
 near the top of the script.  @code{gettext.sh} is a shell function library
 that provides the functions
-@code{eval_gettext} (see @ref{eval_gettext Invocation}) and
-@code{eval_ngettext} (see @ref{eval_ngettext Invocation}).
+@code{eval_gettext} (see @ref{eval_gettext Invocation}),
+@code{eval_ngettext} (see @ref{eval_ngettext Invocation}),
+@code{eval_pgettext} (see @ref{eval_pgettext Invocation}), and
+@code{eval_npgettext} (see @ref{eval_npgettext Invocation}).
 You have to ensure that @code{gettext.sh} can be found in the @code{PATH}.
 
 @item
@@ -9759,6 +9766,12 @@ See @ref{eval_gettext Invocation}.
 
 @item eval_ngettext
 See @ref{eval_ngettext Invocation}.
+
+@item eval_pgettext
+See @ref{eval_pgettext Invocation}.
+
+@item eval_npgettext
+See @ref{eval_npgettext Invocation}.
 @end itemize
 
 @node gettext Invocation, ngettext Invocation, gettext.sh, sh
@@ -9797,7 +9810,7 @@ This function outputs the native language translation of a textual message,
 performing dollar-substitution on the result.  Note that only shell variables
 mentioned in @var{msgid} will be dollar-substituted in the result.
 
-@node eval_ngettext Invocation,  , eval_gettext Invocation, sh
+@node eval_ngettext Invocation, eval_pgettext Invocation, eval_gettext Invocation, sh
 @subsubsection Invoking the @code{eval_ngettext} function
 
 @cindex @code{eval_ngettext} function, usage
@@ -9811,6 +9824,35 @@ whose grammatical form depends on a number, performing dollar-substitution
 on the result.  Note that only shell variables mentioned in @var{msgid} or
 @var{msgid-plural} will be dollar-substituted in the result.
 
+@node eval_pgettext Invocation, eval_npgettext Invocation, eval_ngettext Invocation, sh
+@subsubsection Invoking the @code{eval_pgettext} function
+
+@cindex @code{eval_pgettext} function, usage
+@example
+eval_pgettext @var{msgctxt} @var{msgid}
+@end example
+
+@cindex lookup message translation with context
+This function outputs the native language translation of a textual message
+in the given context @var{msgctxt} (see @ref{Contexts}), performing
+dollar-substitution on the result.  Note that only shell variables mentioned
+in @var{msgid} will be dollar-substituted in the result.
+
+@node eval_npgettext Invocation,  , eval_pgettext Invocation, sh
+@subsubsection Invoking the @code{eval_npgettext} function
+
+@cindex @code{eval_npgettext} function, usage
+@example
+eval_npgettext @var{msgctxt} @var{msgid} @var{msgid-plural} @var{count}
+@end example
+
+@cindex lookup plural message translation with context
+This function outputs the native language translation of a textual message
+whose grammatical form depends on a number in the given context @var{msgctxt}
+(see @ref{Contexts}), performing dollar-substitution on the result.  Note
+that only shell variables mentioned in @var{msgid} or @var{msgid-plural}
+will be dollar-substituted in the result.
+
 @node bash, Python, sh, List of Programming Languages
 @subsection bash - Bourne-Again Shell Script
 @cindex bash
index dc67fe6c8dc762d3f54575454bca31a32d3faabc..1072c7f1b3b1e6925b3d82db5b38e39bca53b3b9 100644 (file)
@@ -276,7 +276,8 @@ For Objective C: Like for C, and also @code{NSLocalizedString}, @code{_},
 
 @item
 For Shell scripts: @code{gettext}, @code{ngettext:1,2}, @code{eval_gettext},
-@code{eval_ngettext:1,2}.
+@code{eval_ngettext:1,2}, @code{eval_pgettext:1c,2},
+@code{eval_npgettext:1c,2,3}.
 
 @item
 For Python: @code{gettext}, @code{ugettext}, @code{dgettext:2},
index 76567c73e3a56fe87d9af11bbc6c392622c57364..599f40b71bdf690dc1c1a9ca1aefaa1e8b674df3 100644 (file)
@@ -1,5 +1,5 @@
 /* xgettext sh backend.
-   Copyright (C) 2003, 2005-2009, 2015-2016 Free Software Foundation, Inc.
+   Copyright (C) 2003, 2005-2009, 2015-2016, 2018 Free Software Foundation, Inc.
    Written by Bruno Haible <bruno@clisp.org>, 2003.
 
    This program is free software: you can redistribute it and/or modify
@@ -112,8 +112,12 @@ init_keywords ()
          xgettext.texi!  */
       x_sh_keyword ("gettext");
       x_sh_keyword ("ngettext:1,2");
+      /* Note: There is also special handling for 'gettext' and 'ngettext'
+         in read_command, below.  */
       x_sh_keyword ("eval_gettext");
       x_sh_keyword ("eval_ngettext:1,2");
+      x_sh_keyword ("eval_pgettext:1c,2");
+      x_sh_keyword ("eval_npgettext:1c,2,3");
       default_keywords = false;
     }
 }
@@ -127,6 +131,9 @@ init_flag_table_sh ()
   xgettext_record_flag ("eval_gettext:1:sh-format");
   xgettext_record_flag ("eval_ngettext:1:sh-format");
   xgettext_record_flag ("eval_ngettext:2:sh-format");
+  xgettext_record_flag ("eval_pgettext:2:sh-format");
+  xgettext_record_flag ("eval_npgettext:2:sh-format");
+  xgettext_record_flag ("eval_npgettext:3:sh-format");
 }
 
 
@@ -468,6 +475,24 @@ string_of_word (const struct word *wp)
   return str;
 }
 
+/* Convert a t_string token to a char*, ignoring the first OFFSET bytes.  */
+static char *
+substring_of_word (const struct word *wp, size_t offset)
+{
+  char *str;
+  int n;
+
+  if (!(wp->type == t_string))
+    abort ();
+  n = wp->token->charcount;
+  if (!(offset <= n))
+    abort ();
+  str = XNMALLOC (n - offset + 1, char);
+  memcpy (str, wp->token->chars + offset, n - offset);
+  str[n - offset] = '\0';
+  return str;
+}
+
 
 /* Whitespace recognition.  */
 
@@ -1250,6 +1275,8 @@ read_command (int looking_for, flag_context_ty outer_context)
         }
       else
         {
+          bool matters_for_argparser = true;
+
           if (argparser == NULL)
             {
               /* This is the function position.  */
@@ -1282,24 +1309,67 @@ read_command (int looking_for, flag_context_ty outer_context)
             {
               /* These are the argument positions.  */
               if (inner.type == t_string)
-                arglist_parser_remember (argparser, arg,
-                                         string_of_word (&inner),
-                                         inner_context,
-                                         logical_file_name,
-                                         inner.line_number_at_start,
-                                         savable_comment);
-
-              if (arglist_parser_decidedp (argparser, arg))
                 {
-                  /* Stop looking for arguments of the last function_name.  */
-                  /* FIXME: What about context_iter?  */
-                  arglist_parser_done (argparser, arg);
-                  shapes = NULL;
-                  argparser = NULL;
+                  bool accepts_context =
+                    ((argparser->keyword_len == 7
+                      && memcmp (argparser->keyword, "gettext", 7) == 0)
+                     || (argparser->keyword_len == 8
+                         && memcmp (argparser->keyword, "ngettext", 8) == 0));
+                  if (accepts_context && argparser->next_is_msgctxt)
+                    {
+                      argparser->next_is_msgctxt = false;
+                      arglist_parser_remember_msgctxt (argparser,
+                                                       string_of_word (&inner),
+                                                       inner_context,
+                                                       logical_file_name,
+                                                       inner.line_number_at_start);
+                      matters_for_argparser = false;
+                    }
+                  else if (accepts_context
+                           && ((inner.token->charcount == 2
+                                && memcmp (inner.token->chars, "-c", 2) == 0)
+                               || (inner.token->charcount == 9
+                                   && memcmp (inner.token->chars, "--context", 9) == 0)))
+                    {
+                      argparser->next_is_msgctxt = true;
+                      matters_for_argparser = false;
+                    }
+                  else if (accepts_context
+                           && (inner.token->charcount >= 10
+                               && memcmp (inner.token->chars, "--context=", 10) == 0))
+                    {
+                      argparser->next_is_msgctxt = false;
+                      arglist_parser_remember_msgctxt (argparser,
+                                                       substring_of_word (&inner, 10),
+                                                       inner_context,
+                                                       logical_file_name,
+                                                       inner.line_number_at_start);
+                      matters_for_argparser = false;
+                    }
+                  else
+                    {
+                      arglist_parser_remember (argparser, arg,
+                                               string_of_word (&inner),
+                                               inner_context,
+                                               logical_file_name,
+                                               inner.line_number_at_start,
+                                               savable_comment);
+                    }
                 }
+
+              if (matters_for_argparser)
+                if (arglist_parser_decidedp (argparser, arg))
+                  {
+                    /* Stop looking for arguments of the last function_name.  */
+                    /* FIXME: What about context_iter?  */
+                    arglist_parser_done (argparser, arg);
+                    shapes = NULL;
+                    argparser = NULL;
+                  }
             }
 
-          arg++;
+          if (matters_for_argparser)
+            arg++;
         }
 
       free_word (&inner);
index 2943f759e50e4307dfa466ebe5706c03011a97f3..968472dac452c7f2a641be5dc0238cc8f8ad966e 100644 (file)
@@ -2961,6 +2961,7 @@ arglist_parser_alloc (message_list_ty *mlp, const struct callshapes *shapes)
       ap->mlp = mlp;
       ap->keyword = NULL;
       ap->keyword_len = 0;
+      ap->next_is_msgctxt = false;
       ap->nalternatives = 0;
 
       return ap;
@@ -2977,6 +2978,7 @@ arglist_parser_alloc (message_list_ty *mlp, const struct callshapes *shapes)
       ap->mlp = mlp;
       ap->keyword = shapes->keyword;
       ap->keyword_len = shapes->keyword_len;
+      ap->next_is_msgctxt = false;
       ap->nalternatives = shapes->nshapes;
       for (i = 0; i < shapes->nshapes; i++)
         {
@@ -3023,6 +3025,7 @@ arglist_parser_clone (struct arglist_parser *ap)
   copy->mlp = ap->mlp;
   copy->keyword = ap->keyword;
   copy->keyword_len = ap->keyword_len;
+  copy->next_is_msgctxt = ap->next_is_msgctxt;
   copy->nalternatives = ap->nalternatives;
   for (i = 0; i < ap->nalternatives; i++)
     {
@@ -3116,6 +3119,7 @@ arglist_parser_remember_literal (struct arglist_parser *ap,
     free (string);
 }
 
+
 void
 arglist_parser_remember (struct arglist_parser *ap,
                          int argnum, char *string,
@@ -3128,6 +3132,36 @@ arglist_parser_remember (struct arglist_parser *ap,
                                    comment, LET_NONE);
 }
 
+
+void
+arglist_parser_remember_msgctxt (struct arglist_parser *ap,
+                                 char *string,
+                                 flag_context_ty context,
+                                 char *file_name, size_t line_number)
+{
+  bool stored_string = false;
+  size_t nalternatives = ap->nalternatives;
+  size_t i;
+
+  for (i = 0; i < nalternatives; i++)
+    {
+      struct partial_call *cp = &ap->alternative[i];
+
+      cp->msgctxt = string;
+      cp->msgctxt_escape = LET_NONE;
+      cp->msgctxt_pos.file_name = file_name;
+      cp->msgctxt_pos.line_number = line_number;
+      stored_string = true;
+      /* Mark msgctxt as done.  */
+      cp->argnumc = 0;
+    }
+  /* Note: There is a memory leak here: When string was stored but is later
+     not used by arglist_parser_done, we don't free it.  */
+  if (!stored_string)
+    free (string);
+}
+
+
 bool
 arglist_parser_decidedp (struct arglist_parser *ap, int argnum)
 {
index 1b6b20309fd3a4dd93a6e599a0c6f355376022e3..926881e37de8bc2fc85e84cf642ba79a2a751141 100644 (file)
@@ -1,6 +1,5 @@
 /* xgettext common functions.
-   Copyright (C) 2001-2003, 2005-2006, 2008-2009, 2011, 2015-2016 Free Software
-   Foundation, Inc.
+   Copyright (C) 2001-2003, 2005-2006, 2008-2009, 2011, 2015-2016, 2018 Free Software Foundation, Inc.
    Written by Peter Miller <millerp@canb.auug.org.au>
    and Bruno Haible <haible@clisp.cons.org>, 2001.
 
@@ -327,6 +326,7 @@ struct arglist_parser
   message_list_ty *mlp;         /* list where the message shall be added */
   const char *keyword;          /* the keyword, not NUL terminated */
   size_t keyword_len;           /* the keyword's length */
+  bool next_is_msgctxt;         /* true if the next argument is the msgctxt */
   size_t nalternatives;         /* number of partial_call alternatives */
   struct partial_call alternative[1]; /* partial_call alternatives */
 };
@@ -361,6 +361,14 @@ extern void arglist_parser_remember_literal (struct arglist_parser *ap,
                                              char *file_name, size_t line_number,
                                              refcounted_string_list_ty *comment,
                                              enum literalstring_escape_type type);
+/* Adds a string argument as msgctxt to an arglist_parser, without incrementing
+   the current argument number.
+   STRING must be malloc()ed string; its ownership is passed to the callee.
+   FILE_NAME must be allocated with indefinite extent.  */
+extern void arglist_parser_remember_msgctxt (struct arglist_parser *ap,
+                                             char *string,
+                                             flag_context_ty context,
+                                             char *file_name, size_t line_number);
 /* Tests whether an arglist_parser has is not waiting for more arguments after
    argument ARGNUM.  */
 extern bool arglist_parser_decidedp (struct arglist_parser *ap, int argnum);
index 5f0173264fe4229804e488c6c61bbfe911b9f0bc..8eed6ed22cc3ee5151aa007ff6e911182d47df29 100755 (executable)
@@ -27,6 +27,14 @@ export TEXTDOMAINDIR
 $echo "`gettext \"'Your command, please?', asked the waiter.\"`"
 
 $echo "`eval_ngettext \"a piece of cake\" \"\\$n pieces of cake\" $n`"
+
+$echo "`gettext -c File \"Open\"`"
+
+$echo "`gettext --context File \"Close\"`"
+
+$echo "`gettext --context=File \"Save\"`"
+
+$echo "`eval_npgettext File \"\\$n file open\" \"\\$n files open\" $n`"
 EOF
 
 : ${XGETTEXT=xgettext}
@@ -42,6 +50,25 @@ msgid "a piece of cake"
 msgid_plural "$n pieces of cake"
 msgstr[0] ""
 msgstr[1] ""
+
+msgctxt "File"
+msgid "Open"
+msgstr ""
+
+msgctxt "File"
+msgid "Close"
+msgstr ""
+
+msgctxt "File"
+msgid "Save"
+msgstr ""
+
+#, sh-format
+msgctxt "File"
+msgid "$n file open"
+msgid_plural "$n files open"
+msgstr[0] ""
+msgstr[1] ""
 EOF
 
 : ${DIFF=diff}
@@ -62,6 +89,25 @@ msgid "a piece of cake"
 msgid_plural "$n pieces of cake"
 msgstr[0] "un morceau de gateau"
 msgstr[1] "$n morceaux de gateau"
+
+msgctxt "File"
+msgid "Open"
+msgstr "Ouvrir"
+
+msgctxt "File"
+msgid "Close"
+msgstr "Fermer"
+
+msgctxt "File"
+msgid "Save"
+msgstr "Sauvegarder"
+
+#, sh-format
+msgctxt "File"
+msgid "$n file open"
+msgid_plural "$n files open"
+msgstr[0] "$n fichier ouvert"
+msgstr[1] "$n fichiers ouverts"
 EOF
 
 : ${MSGMERGE=msgmerge}
@@ -109,10 +155,18 @@ fi
 cat <<\EOF > prog.ok
 «Votre commande, s'il vous plait», dit le garçon.
 2 morceaux de gateau
+Ouvrir
+Fermer
+Sauvegarder
+2 fichiers ouverts
 EOF
 cat <<\EOF > prog.oku
 «Votre commande, s'il vous plait», dit le garçon.
 2 morceaux de gateau
+Ouvrir
+Fermer
+Sauvegarder
+2 fichiers ouverts
 EOF
 
 : ${LOCALE_FR=fr_FR}