Andreas Schipplock
I'm not an assembly line worker. I'm a software developer.
I'm in favour of GNU.

Bash Localization

by Andreas Schipplock

published on


Introduction

So you want to display text in your bash scripts. You assume people can understand english or your mother tongue. So you simply "echo" strings to the console in english or your mother tongue. But what if the user, that is executing your script, can't understand english or your mother tongue? You can handle this! Bash can output translated strings. And it's not too hard to implement.

How not to do it:

1: #!/bin/bash
2: echo "hello world!"

In this example all users would see the string Hello world!. Even users who have set their systems to output german strings. A german user would expect Hallo Welt!.

So how can you output this string based on the localization of the user that is executing your script?

First install the required tools:

On Debian you have to install gettext. The same is valid for CentOS. I haven't tried any other Linux distribution. It's important to note that the user who is executing your script doesn't need gettext. You need the msgfmt binary to convert *.po files to *.mo files. The *.mo files are used by Bash to display translated strings.

Create your shell script, name it test.sh:

1: #!/bin/bash
2: set -euo pipefail
3: script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
4:
5: TEXTDOMAIN="test"
6: TEXTDOMAINDIR="${script_dir}/locales"
7:
8: echo $"Hello World"

chmod +x test.sh and execute your script:

# ./test.sh

You will get the following output:

# Hello World

If you execute this on a german system, it would still output "Hello World" which is wrong. Zee germans expect the german words for it. So let's help them poor souls.

  1. Create a locales directory in your scripts directory
  2. Inside the locales directory create both the directories de/LC_MESSAGES and en/LC_MESSAGES

Inside the de/LC_MESSAGES directory create the file de.po:

msgid "Hello World"
msgstr "Hallo Welt"

And now create the *.mo file:

# msgfmt -o locales/de/LC_MESSAGES/test.mo locales/de/LC_MESSAGES/de.po

This will create a test.mo file which will be used by your bash script. It's important to understand that the *.mo file will be called test.mo. Why? Because in your test.sh script you defined TEXTDOMAIN with the value of "test" so bash tries to read its translations from the "test.mo" file.

Execute your script again:

# LANG="de_DE.UTF-8"
# ./test.sh

This time it will output:

# Hallo Welt

But only if the system is set to "german". English users will still see "Hello World".

Create the file locales/en/LC_MESSAGES/en.po:

msgid "Hello World"
msgstr "Howdy!"

And now create the *.mo file:

# msgfmt -o locales/en/LC_MESSAGES/test.mo locales/en/LC_MESSAGES/en.po

Execute your script again but this time on a system that's "english" (fake it!):

# LANG="en_US.UTF-8"
# ./test.sh

This time it will output:

# Howdy!

On Debian you may want to generate all locales before you can test your localization:

# dpkg-reconfigure locales

Select "all" and generate all locales. Then you can set LANG="en_US.UTF-8" or whatever you want.


Conclusion

With this information you are able to ship your shell script to users and translate all the strings so they can understand it. You can download an example script with all the required files in case you just want to try it out.

Oh, and before I forget it: if you wonder how to translate more than just one "string", you can do this in your *.po file:

msgid "Hello World"
msgstr "Howdy!"
msgid "Wrong Key"
msgstr "You pressed the wrong key! try again"

Just add as many keys and translations as you want. You don't need extra files for it. Just put it in one file. And never forget to transform your *.po file to the *.mo file. Otherwise Bash won't display the right strings :). If bash can't find a translation it will use the key. So choose easy-to-understand keys so people can at least guess if there's no translation available :).

I hope this text was useful.