Internationalisation of GameShell
We rely on GNU's tool for internationalisation: gettext
.
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 (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.
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.pot
to$GSH_ROOT/i18n/it.po
(for example) and translate it. -
Each of the directories in
$GSH_ROOT/i18n
contains 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.po
contains 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
.)
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.
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.
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.
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.