Advanced Bash-Scripting Guide: An in-depth exploration of the art of shell scripting
Prev |  | Next

# Appendix K. Localization

Localization is an undocumented Bash feature.

A localized shell script echoes its text output in the language defined as the system's locale. A Linux user in Berlin, Germany, would get script output in German, whereas his cousin in Berlin, Maryland, would get output from the same script in English.

To create a localized script, use the following template to write all messages to the user (error messages, prompts, etc.).

       1 #!/bin/bash
       2 # localized.sh
       3 #  Script by Stéphane Chazelas,
       4 #+ modified by Bruno Haible, bugfixed by Alfredo Pironti.
       5
       6 . gettext.sh
       7
       8 E_CDERROR=65
       9
      10 error()
      11 {
      12   printf "$@" >&2
      13   exit $E_CDERROR
      14 }
      15
      16 cd $var || error "`eval_gettext \"Can\'t cd to \\\$var.\"`"
      17 #  The triple backslashes (escapes) in front of $var needed
      18 #+ "because eval_gettext expects a string
      19 #+ where the variable values have not yet been substituted."
      20 #    -- per Bruno Haible
      21 read -p "`gettext \"Enter the value: \"`" var
      22 #  ...
      23
      24
      25 #  ------------------------------------------------------------------
      26 #  Alfredo Pironti comments:
      27
      28 #  This script has been modified to not use the $"..." syntax in
      29 #+ favor of the "`gettext \"...\"`" syntax.
      30 #  This is ok, but with the new localized.sh program, the commands
      31 #+ "bash -D filename" and "bash --dump-po-string filename"
      32 #+ will produce no output
      33 #+ (because those command are only searching for the $"..." strings)!
      34 #  The ONLY way to extract strings from the new file is to use the
      35 # 'xgettext' program. However, the xgettext program is buggy.
      36
      37 # Note that 'xgettext' has another bug.
      38 #
      39 # The shell fragment:
      40 #    gettext -s "I like Bash"
      41 # will be correctly extracted, but . . .
      42 #    xgettext -s "I like Bash"
      43 # . . . fails!
      44 #  'xgettext' will extract "-s" because
      45 #+ the command only extracts the
      46 #+ very first argument after the 'gettext' word.
      47
      48
      49 #  Escape characters:
      50 #
      51 #  To localize a sentence like
      52 #     echo -e "Hello\tworld!"
      53 #+ you must use
      54 #     echo -e "`gettext \"Hello\\tworld\"`"
      55 #  The "double escape character" before the `t' is needed because
      56 #+ 'gettext' will search for a string like: 'Hello\tworld'
      57 #  This is because gettext will read one literal `\')
      58 #+ and will output a string like "Bonjour\tmonde",
      59 #+ so the 'echo' command will display the message correctly.
      60 #
      61 #  You may not use
      62 #     echo "`gettext -e \"Hello\tworld\"`"
      63 #+ due to the xgettext bug explained above.
      64
      65
      66
      67 # Let's localize the following shell fragment:
      68 #     echo "-h display help and exit"
      69 #
      70 # First, one could do this:
      71 #     echo "`gettext \"-h display help and exit\"`"
      72 #  This way 'xgettext' will work ok,
      73 #+ but the 'gettext' program will read "-h" as an option!
      74 #
      75 # One solution could be
      76 #     echo "`gettext -- \"-h display help and exit\"`"
      77 #  This way 'gettext' will work,
      78 #+ but 'xgettext' will extract "--", as referred to above.
      79 #
      80 # The workaround you may use to get this string localized is
      81 #     echo -e "`gettext \"\\0-h display help and exit\"`"
      82 #  We have added a \0 (NULL) at the beginning of the sentence.
      83 #  This way 'gettext' works correctly, as does 'xgettext.'
      84 #  Moreover, the NULL character won't change the behavior
      85 #+ of the 'echo' command.
      86 #  ------------------------------------------------------------------

     bash$ bash -D localized.sh
     "Can't cd to %s."
     "Enter the value: "

This lists all the localized text. (The `-D` option lists double-quoted strings prefixed by a $, without executing the script.)

     bash$ bash --dump-po-strings localized.sh
     #: a:6
     msgid "Can't cd to %s."
     msgstr ""
     #: a:7
     msgid "Enter the value: "
     msgstr ""

The `--dump-po-strings` option to Bash resembles the `-D` option, but uses gettext "po" format.

Bruno Haible points out:

Starting with gettext-0.12.2, xgettext -o - localized.sh is recommended instead of bash --dump-po-strings localized.sh, because xgettext . . .

1. understands the gettext and eval_gettext commands (whereas bash --dump-po-strings understands only its deprecated $"..." syntax)

2. can extract comments placed by the programmer, intended to be read by the translator.

This shell code is then not specific to Bash any more; it works the same way with Bash 1.x and other /bin/sh implementations.

Now, build a `language.po` file for each language that the script will be translated into, specifying the `msgstr`. Alfredo Pironti gives the following example:

fr.po:

       1 #: a:6
       2 msgid "Can't cd to $var."
       3 msgstr "Impossible de se positionner dans le repertoire $var."
       4 #: a:7
       5 msgid "Enter the value: "
       6 msgstr "Entrez la valeur : "
       7
       8 #  The string are dumped with the variable names, not with the %s syntax,
       9 #+ similar to C programs.
      10 #+ This is a very cool feature if the programmer uses
      11 #+ variable names that make sense!

Then, run msgfmt.

`msgfmt -o localized.sh.mo fr.po`

Place the resulting `localized.sh.mo` file in the `/usr/local/share/locale/fr/LC_MESSAGES` directory, and at the beginning of the script, insert the lines:

       1 TEXTDOMAINDIR=/usr/local/share/locale
       2 TEXTDOMAIN=localized.sh

If a user on a French system runs the script, she will get French messages.

With older versions of Bash or other shells, localization requires gettext, using the `-s` option. In this case, the script becomes:

       1 #!/bin/bash
       2 # localized.sh
       3
       4 E_CDERROR=65
       5
       6 error() {
       7   local format=$1
       8   shift
       9   printf "$(gettext -s "$format")" "$@" >&2
      10   exit $E_CDERROR
      11 }
      12 cd $var || error "Can't cd to %s." "$var"
      13 read -p "$(gettext -s "Enter the value: ")" var
      14 # ...

The `TEXTDOMAIN` and `TEXTDOMAINDIR` variables need to be set and exported to the environment. This should be done within the script itself.

---

This appendix written by Stéphane Chazelas, with modifications suggested by Alfredo Pironti, and by Bruno Haible, maintainer of GNU gettext.

Prev | Home | Next
An Introduction to Programmable Completion |  | History Commands