Internationalisation of GameShell
January 11, 2022 ยท View on GitHub
We rely on GNU's tool for internationalisation: gettext.
Internationalisation of missions
A language is specified using a strings language[_territory] (e.g., fr
denotes French, en_GB denotes British English, etc.). In most cases, the
"locale" of the system will be used to choose the language for GameShell. In
the other cases, you can specify it with the -L option.
On GNU systems, just giving the 2 letters language code should be enough. It
will set the LANGUAGE variable that lists preferred languages for messages
$ ./gameshell.sh -L fr
On non GNU systems (macOS, BSD), you'll need to specify a valid locale name
$ ./gameshell.sh -L fr_FR.utf8
(You can get the list of available locales with locale -a.)
In the code, a string that needs translating is used either as
gettext "STRING"when the string is "static" (it contains no variable)- or
eval_gettext "STRING"when the string is "dynamic" (it contains shell variables)
Note that the shell expands variables inside strings, so dollar signs need to be escaped in both cases. Here is a typical line from a script:
mkdir -p "$(eval_gettext "\$GSH_HOME/Castle")"
All parts of mission source files that need to be translated (be they strings, file names, etc.) must appear in this way.
If no translation is found for a given string, gettext will simply use its
argument. It is thus important that all string be meaningful in English!
Translation files
Translation files (extension .po) list English strings together with their
translated version. Those files are written in the mission's i18n directory.
Here is part of a French translation file (file i18n/fr.po):
msgid "$GSH_HOME/Castle"
msgstr "$GSH_HOME/Chateau"
Translating an existing mission is thus only a matter of writing such a file and translating some text files.
Workflow: translating the code of GameShell to a new language
All language specific parts of the code of GameShell are in the
$GSH_ROOT/i18n/ directory. To add support for a new language:
-
Copy
$GSH_ROOT/i18n/template.potto$GSH_ROOT/i18n/it.po(for example) and translate it. -
Each of the directories in
$GSH_ROOT/i18ncontains one text files per language (for example:$GSH_ROOT/i18n/start-help/en.txt) and$GSH_ROOT/i18n/start-help/fr.txt). Create a new text file for your language in each of those directory (for example,$GSH_ROOT/i18n/start-help/it.txt), and don't forget to check that the file$GSH_ROOT/i18n/it.pocontains the linesmsgid "$GSH_ROOT/i18n/start-help/en.txt" msgstr "$GSH_ROOT/i18n/start-help/it.txt"
You can add the position (file and line number) of each translation string in
the .po file by running make add-locations from the $GSH_ROOT directory.
That can be useful to check how the message is actually used in the code.
(Remove those positions before submitting a PR by running make all.)
Workflow: translating a mission that already uses gettext
Initializing translation files for a new language
If you want to translate a mission with existing translations, the mission
should come with an existing i18n directory containing a file
i18n/template.pot. You just need to copy this file to, for example,
i18n/it.po and start translating.
Goal text files.
Translating a mission goal is slightly different. The goal is usually stored
in a text file goal.txt. It the mission already has translations, the goal
is instead stored in goal/LANGUAGE.txt (for example, goal/fr.txt).
To translate the goal, you need to create a new file (for example, goal/it.txt)
and add the following string to your .po file
msgid "$MISSION_DIR/goal/en.txt"
msgstr "$MISSION_DIR/goal/it.txt"
Note: treasure message files and skip messages are treated similarly.
Note: using the Makefile
If the mission was created using the utils/new_mission.sh, it should contain a
Makefile. The target new will ask for a language name and create the
corresponding .po file.
Workflow: adding internationalisation support to a mission
Template generation
The tool xgettext can parse source file and extract the strings that need to
be translated. Rather than calling xgettext by hand, we advise using the
Makefile generated with utils/new_mission.sh. (If the mission already
exists, your can call utils/new_mission.sh -M to just get the Makefile.)
The default target (all) will
- update
i18n/template.pot, - update all language files.
The target add-locations does the same, but adds comments to each pair
msgid / msgstr giving locations (filename:line_number) where the string
appears. That's useful during development.
The Makefile will look into all *.sh files, but if you need it to look
into other files (like, into a bin/... file), you can add them to the
OTHER_FILES variable.
Note: if one of the *.sh file is a link to another mission's file, you need
to add it to the EXCEPTIONS variable.