@c This file is part of the GNU gettext manual.
@c Copyright (C) 1995-2025 Free Software Foundation, Inc.
@c See the file gettext.texi for copying conditions.

@node sh
@subsection sh - Shell Script
@cindex shell scripts

@table @asis
@item RPMs
bash, gettext

@item Ubuntu packages
bash, gettext-base

@item File extension
@code{sh}

@item String syntax
@code{"abc"}, @code{'abc'}, @code{abc}

@item gettext shorthand
@code{"`gettext \"abc\"`"}

@item gettext/ngettext functions
@pindex gettext
@pindex ngettext
@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}
environment variable @code{TEXTDOMAIN}

@item bindtextdomain
@vindex TEXTDOMAINDIR@r{, environment variable}
environment variable @code{TEXTDOMAINDIR}

@item setlocale
automatic

@item Prerequisite
For the shell functions approach: @code{. gettext.sh}

@item Use or emulate GNU gettext
use

@item Extractor
@code{xgettext}

@item Formatting with positions
There are three approaches; see below for details.

@item Portability
fully portable

@item po-mode marking
---
@end table

An example is available in the @file{examples} directory: @code{hello-sh}.

@menu
* 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
* 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
* eval_npgettext Invocation::   Invoking the @code{eval_npgettext} function
@end menu

@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 is required, such as the one
@c Test case:  $ env printf 'abc %2$5.3g %1$s\n' foo 3.1415926
from GNU coreutils 9.6 or newer,
from FreeBSD 11 or newer,
or from Solaris 11 OpenIndiana or Solaris 11 OmniOS. @c since 2014.
At the time of this writing (2025),
the only known shells whose @code{printf} built-in is POSIX compliant
are @code{ksh93} and @code{zsh};
@code{bash} and @code{dash} are not.
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 with this approach are as follows.

@enumerate
@item
Insert the line

@smallexample
. gettext.sh
@end smallexample

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
@code{eval_npgettext} (see @ref{eval_npgettext Invocation}).
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
@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{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
@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,

@smallexample
echo "Remaining files: $filecount"
@end smallexample

becomes

@smallexample
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
@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 "`eval_gettext \"file not found: \\\$filename\"`"
@end smallexample
@end enumerate

@node Preparing for printf
@subsubsection Preparing Shell Scripts for the @code{printf} 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{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

@include rt-gettext.texi

Note: @code{xgettext} supports only the one-argument form of the
@code{gettext} invocation, where no options are present and the
@var{textdomain} is implicit, from the environment.

@node ngettext Invocation
@subsubsection Invoking the @code{ngettext} program

@include rt-ngettext.texi

Note: @code{xgettext} supports only the three-arguments form of the
@code{ngettext} invocation, where no options are present and the
@var{textdomain} is implicit, from the environment.

@node printf_gettext Invocation
@subsubsection Invoking the @code{printf_gettext} program

@include rt-printf_gettext.texi

@node printf_ngettext Invocation
@subsubsection Invoking the @code{printf_ngettext} program

@include rt-printf_ngettext.texi

@node envsubst Invocation
@subsubsection Invoking the @code{envsubst} program

@include rt-envsubst.texi

@node eval_gettext Invocation
@subsubsection Invoking the @code{eval_gettext} function

@cindex @code{eval_gettext} function, usage
@example
eval_gettext @var{msgid}
@end example

@cindex lookup message translation
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
@subsubsection Invoking the @code{eval_ngettext} function

@cindex @code{eval_ngettext} function, usage
@example
eval_ngettext @var{msgid} @var{msgid-plural} @var{count}
@end example

@cindex lookup plural message translation
This function outputs the native language translation of a textual message
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
@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
@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.
