Choose Translated Singular and Plural Text with ngettext
You will finish with a shell command that selects singular or plural text from a gettext message catalogue, plus a reliable way to choose the catalogue and check the result. The examples use GNU gettext-runtime 0.21, supplied here by Ubuntu package gettext-base 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 the ngettext command. This guide reads existing catalogues only. It does not create, edit or install translations, and none of the examples need elevated privileges.
1. Check the installed command
Confirm which executable will run and record its version:
$ command -v ngettext
/usr/bin/ngettext
$ ngettext --version
ngettext (GNU gettext-runtime) 0.21
Checkpoint: if the command is missing, stop here and install the package through your normal system administration process. Do not work around a missing catalogue by changing system files.
2. Give ngettext both message forms and a count
The basic form is ngettext MSGID MSGID-PLURAL COUNT. The first two arguments are the source singular and plural messages. The count tells gettext which grammatical form to request:
$ printf '%s\n' "$(ngettext 'one file' 'many files' 1)"
one file
$ printf '%s\n' "$(ngettext 'one file' 'many files' 2)"
many files
The command writes the selected message, but it does not add a newline. The surrounding printf makes terminal output and log lines easier to read. The command substitution also removes trailing newlines if a translated message contains one, so use a direct redirection when preserving exact output matters.
With no matching translation catalogue, GNU gettext falls back to the message text supplied on the command line. That makes the example reproducible in the default C locale, but it is not evidence that a translation was found.
3. Understand what the count does
Do not hard-code an English rule such as "only 1 is singular" around a translated message. The catalogue's language supplies the plural rules. A language can have more than two plural forms, and the same count can select a different form in another language.
For a quick local check, these counts select the plural fallback in the installed command:
$ ngettext 'one file' 'many files' 0
many files$ ngettext 'one file' 'many files' -1
many files
The output runs together because ngettext does not append a newline. The important boundary is the argument COUNT, not text parsing. Pass the number you are actually describing, and let the active catalogue apply its language-specific rule.
Checkpoint: if a script needs to test success, save the status immediately after the command. A successful status means the lookup command completed; it does not guarantee that a non-empty or translated string was returned.
4. Select the message catalogue
A text domain identifies the catalogue, commonly by application or package. Set it explicitly with -d when a script should not depend on the caller's environment:
$ printf '%s\n' "$(ngettext --domain=my-app 'one report' 'many reports' 3)"
many reports
This example uses a domain name but no installed my-app catalogue, so the output is the untranslated fallback. The long option is useful in scripts because its purpose is visible beside the value.
The positional form is also supported:
$ printf '%s\n' "$(ngettext my-app 'one report' 'many reports' 1)"
one report
If the domain is omitted, ngettext reads TEXTDOMAIN from the environment. Catalogue lookup normally uses /usr/share/locale; set TEXTDOMAINDIR when the catalogue is stored elsewhere. These variables select where to look. They do not generate translations.
5. Add context when the same words have different meanings
Use -c or --context when one source phrase needs separate translations in different situations. The context becomes part of the lookup key, so it must match the context used when the catalogue was produced:
$ printf '%s\n' "$(ngettext --context=menu 'Open' 'Open' 1)"
Open
Because this machine has no matching application catalogue for that example, it prints the source singular text. Context does not select a translation by itself, and changing the context will not repair a missing or incorrectly installed catalogue.
Keep message IDs stable. Changing the singular text, plural text, domain or context can make an existing translation unreachable until the catalogue is regenerated.
6. Handle escape sequences deliberately
By default, backslash sequences are returned as text. The -e option expands the escape sequences documented for this command, including \n for a newline:
$ ngettext -e 'one\nfile' 'many\nfiles' 1
one
file
Use -e only when escape expansion is part of the message contract. It is easy to make a translated backslash sequence change meaning unexpectedly. The -E option is accepted for compatibility and is ignored, so it does not enable expansion.
7. Diagnose the common failures
If the output is still English, first check the domain, locale and catalogue directory. Then verify that the catalogue contains the exact singular, plural and context keys. A fallback message is not an error by itself: it is the expected behaviour when no translation is available.
If the output is joined to the next shell prompt or log entry, add a newline at the output boundary, for example with printf '%s\n' "$(...)". Do not assume that ngettext supplied one.
For syntax and version details, use the installed help and version output:
$ ngettext --help
$ ngettext --version
ngettext (GNU gettext-runtime) 0.21
There is no undo step for these checks. They read catalogues and environment variables without changing files, services or persistent system state.
Done means
- You supplied singular text, plural text and a count in the correct order.
- You know that plural selection comes from the active language catalogue, not a shell-side English rule.
- You set the domain explicitly when the caller's
TEXTDOMAINshould not matter. - You used context only when the catalogue was produced with the same context.
- You handled the command's lack of an automatic newline and used
-eonly deliberately.