Translate Shell Messages with gettext
gettext looks up a message identifier in a GNU catalogue and prints its translation for the active locale. A successful exit status never proves it actually found one. This walkthrough covers picking a domain explicitly and translating several messages from the command line, using gettext-runtime 0.21 from the installed gettext-base package, version 0.21-14ubuntu2.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about ten minutes. You need a shell and a message catalogue containing the message you want to translate. This guide only reads catalogues and writes to standard output: it does not install translations, change locale settings, or edit application files, and no elevated privileges are needed.
1. Check the Installed Command
Confirm which executable is being used and record its version. These are ordinary read-only checks:
$ command -v gettext
/usr/bin/gettext
$ gettext --version
gettext (GNU gettext-runtime) 0.21
$ dpkg-query -W -f='${Package} ${Version}\n' gettext-base
gettext-base 0.21-14ubuntu2
The local manual describes two forms: the first translates one message, optionally with a text domain; the second, -s, translates each argument the way an echo-like utility would.
2. Translate One Message with an Explicit Domain
Pass the domain with -d and put the original message after it. The domain is normally the application name, not the language name:
$ LANG=de_DE.UTF-8 gettext -d apt 'Reading package lists'
Reading package lists
This machine has the apt catalogue, but the displayed text can stay in English when the selected locale is unavailable, the catalogue has no entry for that message, or the message is already untranslated. A successful exit status does not prove a translation was found: compare the output with the original message if that distinction matters.
Checkpoint
Test the fallback deliberately:
$ gettext -d definitely-not-a-domain 'Message used for a fallback test'
Message used for a fallback test
$ printf 'exit status: %s\n' "$?"
exit status: 0
A missing catalogue is not a shell error in this invocation. Treat unchanged output as a possible missing translation, not as evidence the requested language is active.
3. Let the Environment Select the Domain
Omit the domain argument and gettext reads the TEXTDOMAIN environment variable instead. Set it for the one command rather than exporting it across a whole interactive session:
$ TEXTDOMAIN=apt LANG=de_DE.UTF-8 gettext 'Reading package lists'
Reading package lists
The domain is only one part of catalogue selection: locale variables still decide which language is requested. If your application uses a non-standard catalogue directory, set TEXTDOMAINDIR to its parent directory:
$ TEXTDOMAINDIR=/path/to/locale TEXTDOMAIN=my-app \
LANG=fr_FR.UTF-8 gettext 'Welcome'
Welcome
Replace /path/to/locale and my-app with real values. Do not use sudo to compensate for a wrong domain or directory; check first that the catalogue exists below the directory, in the locale and domain layout your application expects.
4. Translate Several Fixed Messages
Use -s when each message is a separate argument, which keeps the command simple for a short list:
$ TEXTDOMAIN=apt LANG=C gettext -s 'Reading package lists' 'Reading state information'
Reading package lists Reading state information
As with echo, the arguments come out on one line with spaces between them and a trailing newline. Each message is still translated individually, not as one combined sentence. Use separate calls when the messages need to land on separate lines:
$ TEXTDOMAIN=apt gettext 'Reading package lists'
Reading package lists
$ TEXTDOMAIN=apt gettext 'Reading state information'
Reading state information
Keep the original message stable in a script. Catalogues use that exact message, the message identifier, as the lookup key, so changing punctuation, capitalisation or whitespace can make a lookup miss even when a similar translation exists.
5. Preserve or Expand Escape Sequences Intentionally
By default, backslash escapes are not expanded. -e enables expansion of some escape sequences, while -n suppresses the final newline:
$ gettext -e 'first line\nsecond line'
first line
second line
$ gettext -n 'no trailing newline'
no trailing newline$
The prompt returns immediately after the final word in that second example, so the next shell prompt lands on the same line. Useful when composing output, awkward for ordinary status messages, so keep -n out of normal diagnostic commands unless the caller supplies its own line ending.
Warning
Do not pass untrusted text as a shell fragment. Quote message identifiers, and remember shell expansion happens before gettext ever sees the argument. If a message comes from a file or a pipeline, check what input the command accepts before using the stream form.
6. Add Context Only When the Catalogue Has It
The -c option supplies a context string for a message whose wording is shared across different parts of an application. It does not create a translation, and it does not make an ordinary entry more specific:
$ gettext -c 'menu' -d apt 'Reading package lists'
Reading package lists
A context lookup only succeeds when the catalogue was built with the same context and message identifier. If the catalogue has an uncontexted entry instead, gettext can fall back to the original identifier. Keep the context aligned exactly between the program and the translation source.
7. Diagnose an Unexpected Result
Work through these checks in order:
- Run
command -v gettextandgettext --versionto confirm the binary. - Set
-d DOMAINexplicitly so an inheritedTEXTDOMAINcannot distract you. - Print the locale variables with
localeand check the requested locale is generated on the host. - Check
TEXTDOMAINDIRand the catalogue path without changing files. - Compare the exact message identifier, punctuation and whitespace included, with the source used to build the catalogue.
Use gettext --help for the installed option set. The manual lists -E as ignored for compatibility, so do not add it to new scripts. The standard search directory on this installation is /usr/share/locale.
Done means
- Versions confirmed. You checked the installed gettext-runtime and gettext-base versions.
- One message translated. An identifier resolved through an explicit domain.
- Silent failure understood. Unchanged output can mean a missing locale, catalogue or entry, not success.
- Domain and directory set correctly. You can select a domain with
TEXTDOMAINand a custom catalogue root withTEXTDOMAINDIR. - Options used with intent.
-s,-e,-nand-conly appear when their output or lookup behaviour is actually wanted. - Nothing else touched. No system files, catalogues or service configuration were changed.