Texts in several languages¶
A game shows its players texts: a score, a hint, the end screen's message, the labels of its menus. Written straight into the scripts, they are in one language. Kept in translation files, the same game shows them in English to one player and in Portuguese to another.
Translation files¶
They go in the game's locales/ folder, one per language, named for it: locales/en.json,
locales/pt.json, locales/pt_BR.json. Each is a JSON object of keys and the texts they stand
for:
{
"hud.coins": "Coins {count}/{total}",
"hud.hint": "Grab every coin, then reach the flag!",
"over.win": "You win!"
}
A key is any name you like; words joined by dots keep them in order (hud.coins, menu.credits).
A text names the values put into it in braces ({count}). A long text may be a list of lines,
which are joined with line breaks: "credits": ["Made by Ana", "Music by Rui"].
English is the one every other language falls back to, so en.json has every key; a translation
has the keys it translates, and a key it lacks shows in English.
The templates come with a locales/en.json, and the platformer's (games/platformer/locales/) is
a full example.
Showing a text¶
I18n.t gives the text of a key in the language in use, with its values filled in:
UI.text(I18n.t("hud.coins", count: @coins, total: @total), x: 20, y: 40, size: 28, color: :gold)
GameMenu.instance.finish(I18n.t("over.win"))
Each value is shown as its to_s. A number with a set number of decimals is formatted first:
I18n.t("hud.best", seconds: format("%.1f", @best)). Keep a whole sentence in one text, values
and all, rather than gluing pieces together: another language may want the values elsewhere, or
a different word for one and for many ("{place}: 1 other player" and
"{place}: {count} other players" are two keys).
A key no file has a text for shows as itself (hud.coins), so a missing text is easy to spot on
the screen.
The language¶
I18n.locale is the language texts are shown in, as a Symbol (:en). It starts as the player's:
the language chosen in RedGem's settings, else the system's, matched with the game's files
whatever their case and whether - or _ is written: the same language and region (pt_BR.json
for pt-BR), else the language alone (pt.json), else another region of it. When the game has
no file in the player's language, it starts in the language RedGem shows its own texts in
(English when it has none in the player's). A text a region's file lacks is looked for in the
language's own file (pt_BR, then pt). A game may offer a choice:
I18n.available_locales # => [:en, :pt]: the languages the game has files for
I18n.locale = :pt # from now on, in Portuguese
A text already looked up keeps its language. GameMenu looks its labels and headings up each
time it draws them, so they follow I18n.locale; a game's own option or heading does too when it
is given as a lookup rather than a text:
menu.screens[:pause].add_option(-> { I18n.t("menu.credits") }, index: 1) { menu.show(:credits) }
GameMenu::Screen.new(heading: -> { I18n.t("credits.heading") })
The engine's own texts¶
The labels of GameMenu (Play, Quit, Play again, Resume, Restart, Paused, and those of its
settings screen and controls page) come from the engine, in the player's language. A game's files may change them or translate them, with the same keys:
{
"game.menu.play": "Start",
"game.menu.resume": "Continuar"
}
The keys are game.menu.play, game.menu.quit, game.menu.play_again, game.menu.resume,
game.menu.restart and game.menu.paused; the settings screen's are game.settings.*, and the
controls page's game.controls.* (its rows, and the names of keys and buttons: see
The controls page). A text is looked for in the game's files in the
language in use, then among the engine's texts in that language, then in the game's English, then
in the engine's English; else the key itself is shown. (So a game whose English changes a label
still shows the engine's translation of it to a player who reads another language.)
What is read, and what is not¶
Translation files travel with the game: builds, Android packages and dedicated servers carry the
locales/ folder like the rest. They are read once, when the game's scripts start. Only files
named <language>.json are read (a language's name is letters, digits, - and _), none larger
than 1 MB and none reached through a symbolic link. A file that is not a translation file (broken
JSON, or a value that is neither a text nor a list of lines) is left out whole: the log says which
file and why, and the game goes on with the others.
On a dedicated server each room has its own I18n.locale. A text a server sends players, such as
the reason a join was turned down, is in the server's language: a game that wants it in each
player's sends a key and lets the player's machine look it up.