]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
Shell: Document two more approaches.
authorBruno Haible <bruno@clisp.org>
Fri, 27 Jun 2025 21:19:00 +0000 (23:19 +0200)
committerBruno Haible <bruno@clisp.org>
Fri, 27 Jun 2025 21:19:00 +0000 (23:19 +0200)
* gettext-tools/doc/lang-sh.texi (sh): Tweaks.
(sh - Three approaches): New subsubsection.
(The gettext.sh approach): New subsubsection, incorporating the "gettext.sh"
subsubsection.
(The printf approach, The printf_gettext approach): New subsubsections.
(Preparing for gettext.sh): Renamed from "Preparing Shell Scripts".
(Preparing for printf, Preparing for printf_gettext): New subsubsections.
* gettext-tools/doc/gettext.texi: Update detailed node list.
* NEWS: Mention the change.

NEWS
gettext-tools/doc/gettext.texi
gettext-tools/doc/lang-sh.texi

diff --git a/NEWS b/NEWS
index 2394045e864fa90b0c4afa1651c54c194f0c8083..f6ebce9c64a1d254c88cd31d417e22c955be2756 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -12,13 +12,15 @@ Version 0.26 - July 2025
       in a context that requires a format string.  You can override this
       heuristic by using a comment of the form /* xgettext: c-format */.
   * Shell:
+    - The documentation now mentions two other approaches for
+      internationalizing messages with parameters in shell scripts.
     - xgettext now recognizes format strings in the 'printf' command syntax.
       They are marked as 'sh-printf-format' in POT and PO files.
-    - xgettext now recognizes the \c, \u, and \U escape sequences in dollar-
-      single-quoted strings $'...'.
     - Two new programs 'printf_gettext' and 'printf_ngettext' are provided,
       that do formatted output with a localized format string in a more
       efficient way (without spawning a subshell).
+    - xgettext now recognizes the \c, \u, and \U escape sequences in dollar-
+      single-quoted strings $'...'.
 
 # Bug fixes:
   - The AM_GNU_GETTEXT macro now rejects the dysfunctional gettext() function
index 6cb70fbc85886fbdb67275e435b791fdfd85fb01..b1c359c3a1ef28f70c3ae1fa1fd3d6d1040e6222 100644 (file)
@@ -463,13 +463,18 @@ Individual Programming Languages
 
 sh - Shell Script
 
-* Preparing Shell Scripts::     Preparing Shell Scripts for Internationalization
-* gettext.sh::                  Contents of @code{gettext.sh}
+* sh - Three approaches::       Three approaches for Shell Scripts
+* The gettext.sh approach::     The @code{gettext.sh} approach
+* The printf approach::         The @code{printf} approach
+* The printf_gettext approach:: The @code{printf_gettext} approach
+* Preparing for gettext.sh::    Preparing Shell Scripts for the @code{gettext.sh} approach
+* Preparing for printf::        Preparing Shell Scripts for the @code{printf} approach
+* Preparing for printf_gettext:: Preparing Shell Scripts for the @code{printf_gettext} approach
 * gettext Invocation::          Invoking the @code{gettext} program
 * ngettext Invocation::         Invoking the @code{ngettext} program
-* envsubst Invocation::         Invoking the @code{envsubst} program
 * printf_gettext Invocation::   Invoking the @code{printf_gettext} program
 * printf_ngettext Invocation::  Invoking the @code{printf_ngettext} program
+* 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
index 47d7f422b28ac66066f329333df79066507096e7..6db228b5bbfc59be3c59f132b8a34ecc57bace83 100644 (file)
@@ -28,6 +28,7 @@ bash, gettext-base
 @code{gettext}, @code{ngettext} programs
 @*@code{eval_gettext}, @code{eval_ngettext}, @code{eval_pgettext},
 @code{eval_npgettext} shell functions
+@*@code{printf_gettext}, @code{printf_ngettext} commands
 
 @item textdomain
 @vindex TEXTDOMAIN@r{, environment variable}
@@ -41,7 +42,7 @@ environment variable @code{TEXTDOMAINDIR}
 automatic
 
 @item Prerequisite
-@code{. gettext.sh}
+For the shell functions approach: @code{. gettext.sh}
 
 @item Use or emulate GNU gettext
 use
@@ -50,11 +51,7 @@ use
 @code{xgettext}
 
 @item Formatting with positions
-A POSIX compliant
-@url{https://pubs.opengroup.org/onlinepubs/9799919799/utilities/printf.html,
-     @code{printf}}
-command, such as the one from GNU coreutils 9.6 or newer.
-@c GNU Bash built-in?
+There are three approaches; see below for details.
 
 @item Portability
 fully portable
@@ -66,8 +63,13 @@ fully portable
 An example is available in the @file{examples} directory: @code{hello-sh}.
 
 @menu
-* Preparing Shell Scripts::     Preparing Shell Scripts for Internationalization
-* gettext.sh::                  Contents of @code{gettext.sh}
+* sh - Three approaches::       Three approaches for Shell Scripts
+* The gettext.sh approach::     The @code{gettext.sh} approach
+* The printf approach::         The @code{printf} approach
+* The printf_gettext approach:: The @code{printf_gettext} approach
+* Preparing for gettext.sh::    Preparing Shell Scripts for the @code{gettext.sh} approach
+* Preparing for printf::        Preparing Shell Scripts for the @code{printf} approach
+* Preparing for printf_gettext:: Preparing Shell Scripts for the @code{printf_gettext} approach
 * gettext Invocation::          Invoking the @code{gettext} program
 * ngettext Invocation::         Invoking the @code{ngettext} program
 * printf_gettext Invocation::   Invoking the @code{printf_gettext} program
@@ -79,13 +81,157 @@ An example is available in the @file{examples} directory: @code{hello-sh}.
 * eval_npgettext Invocation::   Invoking the @code{eval_npgettext} function
 @end menu
 
-@node Preparing Shell Scripts
-@subsubsection Preparing Shell Scripts for Internationalization
+@node sh - Three approaches
+@subsubsection Three approaches for Shell Scripts
+
+There are three approaches for internationalizing shell scripts.
+
+The common part is how to internationalize a message without parameters:
+This is done through the invocation of the @code{gettext} program,
+see @ref{gettext Invocation}.
+
+The approaches differ regarding how to internationalize a message with parameters, that is, what is called ``formatted output'' in other programming languages.
+
+@node The gettext.sh approach
+@subsubsection The @code{gettext.sh} approach
+
+This approach uses shell functions defined in the @code{gettext.sh} file.
+
+Here's an example that references a shell variable @code{pid}:
+@example
+eval_gettext "Running as process number \$pid."; echo
+@end example
+
+A ready-to-use example is available in the @file{hello-1.sh} file
+in the @code{examples/hello-sh} directory.
+
+@subheading Contents of @code{gettext.sh}
+
+@code{gettext.sh}, contained in the run-time package of GNU gettext,
+provides the following:
+
+@table @code
+@item $echo
+The variable @code{echo} is set to a command that outputs its first argument
+and a newline, without interpreting backslashes in the argument string.
+
+@item eval_gettext
+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 table
+
+@subheading Advantages and Drawbacks
+
+Advantages:
+@itemize @bullet
+@item
+Portability: This is fully portable.
+@end itemize
+
+Drawbacks:
+@itemize @bullet
+@item
+The strings passed to the @code{eval_gettext} et al.@: functions
+must not start nor end with a newline.
+This means, in order to add a newline after a message,
+you need to add an explicit invocation of @code{echo}.
+@item
+References to shell variables must be introduced with @samp{\$},
+not just @samp{$}.
+@item
+Speed:
+Each invocation of the @code{eval_gettext} et al.@: functions
+uses a pipe and a subshell.
+This is slower than the @code{printf_gettext} approach.
+@end itemize
+
+@node The printf approach
+@subsubsection The @code{printf} approach
+
+This approach uses a POSIX:2024 compliant @code{printf} command.
+
+Here's an example that references a shell variable @code{pid}:
+@example
+env printf "`gettext \"Running as process number %u.\"`"'\n' $pid
+@end example
+
+An example is available in the @file{hello-2.sh} file
+in the @code{examples/hello-sh} directory.
+
+@subheading Advantages and Drawbacks
+
+Drawbacks:
+@itemize @bullet
+@item
+Portability:
+A POSIX compliant
+@url{https://pubs.opengroup.org/onlinepubs/9799919799/utilities/printf.html,
+     @code{printf}}
+command, such as the one from GNU coreutils 9.6 or newer, is required.
+At the time of this writing (2025),
+no shell is known whose @code{printf} built-in is POSIX compliant.
+For this reason, the code needs to use @code{env printf}, not @code{printf},
+so as to avoid invoking the shell's @code{printf} built-in.
+@item
+The strings passed to the @code{gettext} et al.@: program inside a subshell
+must not start nor end with a newline.
+This means, in order to add a newline after a message, you either need
+to append a newline to the format string argument of @code{printf}, or
+to add an explicit invocation of @code{echo}.
+@item
+Some amount of backslashing is involved, see @ref{Preparing for printf}.
+It is easy to make editing mistakes during this process.
+@item
+Speed:
+Each invocation of @code{env printf} uses a subshell.
+This is slower than the @code{printf_gettext} approach.
+@end itemize
+
+@node The printf_gettext approach
+@subsubsection The @code{printf_gettext} approach
+
+This approach uses
+the @code{printf_gettext} and @code{printf_ngettext} programs,
+contained in the run-time package of GNU gettext 0.26 or newer.
+
+Here's an example that references a shell variable @code{pid}:
+@example
+printf_gettext 'Running as process number %u.' $pid; echo
+@end example
+
+An example is available in the @file{hello-3.sh} file
+in the @code{examples/hello-sh} directory.
+
+@subheading Advantages and Drawbacks
+
+Advantages:
+@itemize @bullet
+@item
+Portability: The only requirement is GNU gettext 0.26 or newer.
+@item
+Speed:
+Since no subshell needs to be created,
+this approach is about 30% faster than the two other approaches.
+@end itemize
+
+An example is available in the @file{hello-3.sh} file
+in the @code{examples/hello-sh} directory.
+
+@node Preparing for gettext.sh
+@subsubsection Preparing Shell Scripts for the @code{gettext.sh} approach
 @cindex preparing shell scripts for translation
 
 Preparing a shell script for internationalization is conceptually similar
-to the steps described in @ref{Sources}.  The concrete steps for shell
-scripts are as follows.
+to the steps described in @ref{Sources}.
+The concrete steps for shell scripts with this approach are as follows.
 
 @enumerate
 @item
@@ -95,8 +241,8 @@ Insert the line
 . gettext.sh
 @end smallexample
 
-near the top of the script.  @code{gettext.sh} is a shell function library
-that provides the functions
+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}),
 @code{eval_ngettext} (see @ref{eval_ngettext Invocation}),
 @code{eval_pgettext} (see @ref{eval_pgettext Invocation}), and
@@ -105,7 +251,8 @@ You have to ensure that @code{gettext.sh} can be found in the @code{PATH}.
 
 @item
 Set and export the @code{TEXTDOMAIN} and @code{TEXTDOMAINDIR} environment
-variables.  Usually @code{TEXTDOMAIN} is the package or program name, and
+variables.
+Usually @code{TEXTDOMAIN} is the package or program name, and
 @code{TEXTDOMAINDIR} is the absolute pathname corresponding to
 @code{$prefix/share/locale}, where @code{$prefix} is the installation location.
 
@@ -124,7 +271,8 @@ Simplify translatable strings so that they don't contain command substitution
 (@code{"`...`"} or @code{"$(...)"}), variable access with defaulting (like
 @code{$@{@var{variable}-@var{default}@}}), access to positional arguments
 (like @code{$0}, @code{$1}, ...) or highly volatile shell variables (like
-@code{$?}). This can always be done through simple local code restructuring.
+@code{$?}).
+This can always be done through simple local code restructuring.
 For example,
 
 @smallexample
@@ -156,14 +304,16 @@ For each translatable string, change the output command @samp{echo} or
 @samp{$echo} to @samp{gettext} (if the string contains no references to
 shell variables) or to @samp{eval_gettext} (if it refers to shell variables),
 followed by a no-argument @samp{echo} command (to account for the terminating
-newline). Similarly, for cases with plural handling, replace a conditional
+newline).
+Similarly, for cases with plural handling, replace a conditional
 @samp{echo} command with an invocation of @samp{ngettext} or
 @samp{eval_ngettext}, followed by a no-argument @samp{echo} command.
 
 When doing this, you also need to add an extra backslash before the dollar
 sign in references to shell variables, so that the @samp{eval_gettext}
 function receives the translatable string before the variable values are
-substituted into it. For example,
+substituted into it.
+For example,
 
 @smallexample
 echo "Remaining files: $filecount"
@@ -176,9 +326,11 @@ eval_gettext "Remaining files: \$filecount"; echo
 @end smallexample
 
 If the output command is not @samp{echo}, you can make it use @samp{echo}
-nevertheless, through the use of backquotes. However, note that inside
-backquotes, backslashes must be doubled to be effective (because the
-backquoting eats one level of backslashes). For example, assuming that
+nevertheless, through the use of backquotes.
+However, note that inside backquotes,
+backslashes must be doubled to be effective
+(because the backquoting eats one level of backslashes).
+For example, assuming that
 @samp{error} is a shell function that signals an error,
 
 @smallexample
@@ -198,29 +350,229 @@ error "`eval_gettext \"file not found: \\\$filename\"`"
 @end smallexample
 @end enumerate
 
-@node gettext.sh
-@subsubsection Contents of @code{gettext.sh}
+@node Preparing for printf
+@subsubsection Preparing Shell Scripts for the @code{printf} approach
+@cindex preparing shell scripts for translation
 
-@code{gettext.sh}, contained in the run-time package of GNU gettext, provides
-the following:
+Preparing a shell script for internationalization is conceptually similar
+to the steps described in @ref{Sources}.
+The concrete steps for shell scripts with this approach are as follows.
 
-@itemize @bullet
-@item $echo
-The variable @code{echo} is set to a command that outputs its first argument
-and a newline, without interpreting backslashes in the argument string.
+@enumerate
+@item
+Set and export the @code{TEXTDOMAIN} and @code{TEXTDOMAINDIR} environment
+variables.
+Usually @code{TEXTDOMAIN} is the package or program name, and
+@code{TEXTDOMAINDIR} is the absolute pathname corresponding to
+@code{$prefix/share/locale}, where @code{$prefix} is the installation location.
 
-@item eval_gettext
-See @ref{eval_gettext Invocation}.
+@smallexample
+TEXTDOMAIN=@@PACKAGE@@
+export TEXTDOMAIN
+TEXTDOMAINDIR=@@LOCALEDIR@@
+export TEXTDOMAINDIR
+@end smallexample
 
-@item eval_ngettext
-See @ref{eval_ngettext Invocation}.
+@item
+Prepare the strings for translation, as described in @ref{Preparing Strings}.
 
-@item eval_pgettext
-See @ref{eval_pgettext Invocation}.
+@item
+Simplify translatable strings so that they don't contain command substitution
+(@code{"`...`"} or @code{"$(...)"}), variable access with defaulting (like
+@code{$@{@var{variable}-@var{default}@}}), access to positional arguments
+(like @code{$0}, @code{$1}, ...) or highly volatile shell variables (like
+@code{$?}).
+This can always be done through simple local code restructuring.
+For example,
 
-@item eval_npgettext
-See @ref{eval_npgettext Invocation}.
-@end itemize
+@smallexample
+echo "Usage: $0 [OPTION] FILE..."
+@end smallexample
+
+becomes
+
+@smallexample
+program_name=$0
+echo "Usage: $program_name [OPTION] FILE..."
+@end smallexample
+
+Similarly,
+
+@smallexample
+echo "Remaining files: `ls | wc -l`"
+@end smallexample
+
+becomes
+
+@smallexample
+filecount="`ls | wc -l`"
+echo "Remaining files: $filecount"
+@end smallexample
+
+@item
+For each translatable string, change the output command @samp{echo} or
+@samp{$echo} to @samp{gettext} (if the string contains no references to
+shell variables)
+or to @samp{env printf "`gettext ... `"} (if it refers to shell variables),
+followed by a no-argument @samp{echo} command (to account for the terminating
+newline).
+Similarly, for cases with plural handling, replace a conditional
+@samp{echo} command with an invocation of @samp{ngettext} or
+@samp{env printf "`ngettext ... `"},
+followed by a no-argument @samp{echo} command.
+
+When doing this, you also need to replace the references to shell variables
+with format string directives (see @ref{sh-format}),
+so that the @samp{eval_gettext} function receives the translatable string
+before the variable values are substituted into it.
+For example,
+
+@smallexample
+echo "Remaining files: $filecount"
+@end smallexample
+
+becomes
+
+@smallexample
+env printf "`gettext \"Remaining files: %u.\"`"'\n' "$filecount"
+@end smallexample
+
+If the output command is not @samp{echo}, you can make it use @samp{echo}
+nevertheless, through the use of backquotes.
+However, note that inside backquotes,
+backslashes must be doubled to be effective
+(because the backquoting eats one level of backslashes).
+For example, assuming that
+@samp{error} is a shell function that signals an error,
+
+@smallexample
+error "file not found: $filename"
+@end smallexample
+
+is first transformed into
+
+@smallexample
+error "`echo \"file not found: \$filename\"`"
+@end smallexample
+
+which then becomes
+
+@smallexample
+error "`env printf \"\`gettext \\\"file not found: %s\\\"\`\" \"\$filename\"`"
+@end smallexample
+@end enumerate
+
+@node Preparing for printf_gettext
+@subsubsection Preparing Shell Scripts for the @code{printf_gettext} approach
+@cindex preparing shell scripts for translation
+
+Preparing a shell script for internationalization is conceptually similar
+to the steps described in @ref{Sources}.
+The concrete steps for shell scripts with this approach are as follows.
+
+@enumerate
+@item
+Set and export the @code{TEXTDOMAIN} and @code{TEXTDOMAINDIR} environment
+variables.
+Usually @code{TEXTDOMAIN} is the package or program name, and
+@code{TEXTDOMAINDIR} is the absolute pathname corresponding to
+@code{$prefix/share/locale}, where @code{$prefix} is the installation location.
+
+@smallexample
+TEXTDOMAIN=@@PACKAGE@@
+export TEXTDOMAIN
+TEXTDOMAINDIR=@@LOCALEDIR@@
+export TEXTDOMAINDIR
+@end smallexample
+
+@item
+Prepare the strings for translation, as described in @ref{Preparing Strings}.
+
+@item
+Simplify translatable strings so that they don't contain command substitution
+(@code{"`...`"} or @code{"$(...)"}), variable access with defaulting (like
+@code{$@{@var{variable}-@var{default}@}}), access to positional arguments
+(like @code{$0}, @code{$1}, ...) or highly volatile shell variables (like
+@code{$?}).
+This can always be done through simple local code restructuring.
+For example,
+
+@smallexample
+echo "Usage: $0 [OPTION] FILE..."
+@end smallexample
+
+becomes
+
+@smallexample
+program_name=$0
+echo "Usage: $program_name [OPTION] FILE..."
+@end smallexample
+
+Similarly,
+
+@smallexample
+echo "Remaining files: `ls | wc -l`"
+@end smallexample
+
+becomes
+
+@smallexample
+filecount="`ls | wc -l`"
+echo "Remaining files: $filecount"
+@end smallexample
+
+@item
+For each translatable string, change the output command @samp{echo} or
+@samp{$echo} to @samp{gettext} (if the string contains no references to
+shell variables)
+or to @samp{printf_gettext} (if it refers to shell variables),
+followed by a no-argument @samp{echo} command (to account for the terminating
+newline).
+Similarly, for cases with plural handling, replace a conditional
+@samp{echo} command with an invocation of @samp{ngettext} or
+@samp{printf_ngettext},
+followed by a no-argument @samp{echo} command.
+
+When doing this, you also need to replace the references to shell variables
+with format string directives (see @ref{sh-format}),
+so that the @samp{eval_gettext} function receives the translatable string
+before the variable values are substituted into it.
+For example,
+
+@smallexample
+echo "Remaining files: $filecount"
+@end smallexample
+
+becomes
+
+@smallexample
+printf_gettext "Remaining files: %u." "$filecount"; echo
+@end smallexample
+
+If the output command is not @samp{echo}, you can make it use @samp{echo}
+nevertheless, through the use of backquotes.
+However, note that inside backquotes,
+backslashes must be doubled to be effective
+(because the backquoting eats one level of backslashes).
+For example, assuming that
+@samp{error} is a shell function that signals an error,
+
+@smallexample
+error "file not found: $filename"
+@end smallexample
+
+is first transformed into
+
+@smallexample
+error "`echo \"file not found: \$filename\"`"
+@end smallexample
+
+which then becomes
+
+@smallexample
+error "`printf_gettext \"file not found: %s\" \"\$filename\"`"
+@end smallexample
+@end enumerate
 
 @node gettext Invocation
 @subsubsection Invoking the @code{gettext} program