Translations
A game shows each player its text in their language. Every word a player reads is a message in a catalog, and every catalog but the default one translates it. To translate a whole game, follow Translate a game.
| Text | Where you write it | Where it is translated |
|---|---|---|
| The client's words | /src/i18n/<locale>.json, shown with react-intl (Text in the player's language) |
the catalog of each locale |
| Log lines, offer labels, refusal reasons | msg! in the game logic, with its message in the catalogs (Messages from the game logic) |
the catalog of each locale |
| Bot names and descriptions | NAME and DESCRIPTION keys in the catalogs (Bots) |
the catalog of each locale |
| The game's name and description | update_game's name and description |
update_game's translations |
| Rules pages and the marketing page | /src/rules/*.bwss, /src/marketing/index.bwss |
a copy of each page in a folder named for its locale (Write the rules pages) |
Catalogs
A game's messages are catalogs, one per locale, in /src/i18n/:
- Each catalog is
/src/i18n/<locale>.json, where the locale is one ofen,de,es,fr,jaandzh-CN. The game's locales are the default locale and each locale it has a catalog for. - A catalog is one JSON object that maps each message key to its message, a string in ICU MessageFormat, such as
{"log.draws": "{count, plural, one {Draws a card} other {Draws # cards}}"}. Write a literal brace as'{'. - At most 256 KiB per catalog.
- The game's default locale (
defaultLocale, whichget_gamereturns andupdate_gamesets,enunless set) is the one every other locale falls back to, message by message. A game with any catalog needs one for its default locale. - Boardweaver makes
en-XA, its pseudo-locale, from the default catalog: each letter accented, the text longer and each message in brackets. A game never writes one.
write_file, patch_file and rename_file refuse a catalog Boardweaver could never use (any other .json path under /src/i18n/, a size over the limit, content that is not one object of strings, or a message that is not ICU MessageFormat), with INVALID_CATALOG. validate_code reports the same problems, and a missing catalog for the default locale, as errors. commit refuses them with BUILD_FAILED before it builds anything, and refuses with SOURCE_CHANGED when a catalog is deleted or renamed, or the default locale changes, while the game builds, so that the game no longer has a catalog for its default locale.
Messages from the game logic
The game logic sends no finished text. A log line, an offer's label and a refusal's reason are each a message: a key in the catalogs and the values of its placeholders, built with msg!. Each viewer's client formats it in their own language.
table.log(msg!("log.marks", cell = space_id.as_str()));
{ "log.marks": "{player} marks cell {cell}" }
{player}is the line's player. Boardweaver fills it, so amsg!never passes it. Place it where the language needs the name, or leave it out of a message that names no one.- A value is a number, text, a seated player, another message or a link. The table of values is in Log lines.
- The build checks each
msg!whose key is a string literal against the default catalog. A key built at run time is not checked, and shows as its key when no catalog has it.
Messages in the client
The client shows its words with react-intl, by key: <FormattedMessage id="status.yourMove" />. The frame gives the client one react-intl provider with the game's messages in the player's language. Never pass defaultMessage: the frame formats only messages that a catalog holds, and a defaultMessage would give the text a second home. How to write and format the client's messages is in Text in the player's language and react-intl.
Which locale a player sees
The frame shows the game in the first of the player's languages that is one of the game's locales, else in the default locale. The player's languages are the language they chose on Boardweaver, else their browser's. The game's locale is chosen apart from the website's, so a game can show a locale before Boardweaver itself is translated into it.
- A missing message falls back. A message a translation lacks shows in the default locale.
- A message no catalog has shows its key.
- The game's name, description and pages follow the same order: the first of the player's languages that has a version, else the default locale.
Translations
Every other catalog translates the default one. A translation keeps each message's placeholders, its tags, its select keys and the style of each number, date or time, and keeps {player} where the default message has it. A plural may list only its own language's categories, and may show # in only some of them. validate_code reports a message whose placeholders differ as an error, and commit refuses it with CATALOG_PLACEHOLDERS_MISMATCH, naming the file, the key and both sets of placeholders. A translation changed while the game builds so that its placeholders differ is refused with SOURCE_CHANGED, with the same lines.
Players see the game's name and description in the first of their languages that the game has one in, else in the default locale: in the game list, on the game's page and in a match's lobby. Boardweaver's emails and notices name the game in the reader's account language the same way. The game's page lists its languages: the default locale, then each locale with a catalog, a translated name or a translated description.
A translation may lack messages: each one shows in the default locale until it has one. validate_code lists, as warnings, the messages each translation lacks and the ones it has that the default catalog does not, which nothing shows. commit returns the same lines in catalogCoverage and never refuses for them. They also name each locale with a catalog whose game name or description has no translation: update_game's translations sets them, and a player in that locale sees the default locale's until it does. A game with no description, or an empty one, needs no translated description. Two kinds of translation are named because nothing stores them: one for the default locale, whose name and description are update_game's name and description, and a translated name or description of a game that has none in its default locale.
A bot's name and description are messages too (Bots). validate_code and commit check them against the catalogs once the Rust builds, and commit refuses with SOURCE_CHANGED when a catalog changes while the game builds so that a bot's text no longer reads.
Translating in the studio
A designer can also translate on the game's Translations page in the studio. The page edits the same catalogs, so a catalog the designer saves there is a catalog you read with read_file.
- The page lists the game's locales, the default locale first. For each translation it shows how many of the default catalog's messages it translates, the messages it lacks, the messages only it has and each placeholder mismatch.
- The page shows each message of the default catalog beside a field for the chosen locale. It edits translations only: edit the default catalog as a file.
- A save writes
/src/i18n/<locale>.jsonwith the same checks aswrite_file: one JSON object, two spaces deep, the default catalog's keys first, and no key whose field is empty. Add a language writes the new locale's catalog as{}. - The page lists what
commitrefuses, with the code and the linevalidate_codeandcommitgive it. - The page writes only what the designer types. Boardweaver translates nothing.
Checking a locale
Play a session to see the game in a locale before a player does (Playing a session):
start_gameandrender_gametakelocale: the default locale, a locale the game has a catalog for, oren-XA. It replaces the player's languages.start_game'slocaleis the onerender_gameshows the session in until arender_gamecall names its own. A render's answer names itslocale.en-XAis available through these tools, but cannot be chosen as an account or website language.- Render in
en-XAto find text with no message: it shows in plain English, with no accents and no brackets.en-XAis also longer than English, so it shows text that overflows its box. - A render keeps two things in English in every locale: Boardweaver's own words in
<History />and its log panel, which a player sees in their language, and the players' names,Player <id>, where a player sees each player's own name. Neither is text the game translates. - Render in each locale with glyphs English lacks, such as
jaorzh-CN, to check that the game's fonts have them. apply_actionanswers each committed decision's lines, andinspect_statewithout apaththe last committed decision's: each as its message (its key and values), its player and kind, and astext, formatted in the default locale with each seat namedPlayer <id>. A line whose key the default catalog lacks reads as its key.
start_game and render_game take a session's catalogs when it starts. Start a new session after you edit a catalog.
Checks and codes
| Code | Tool | Means |
|---|---|---|
INVALID_CATALOG |
write_file, patch_file, rename_file |
A catalog Boardweaver could never use (Catalogs). The message names the file and why. |
BUILD_FAILED |
commit |
A catalog cannot be used, the default locale has no catalog, or a bot's text does not read. validate_code lists the same problems as errors. |
CATALOG_PLACEHOLDERS_MISMATCH |
commit |
A translation's message places other placeholders, tags or select keys than the same message in the default catalog (Translations). |
SOURCE_CHANGED |
commit |
A catalog or the default locale changed while the game built, so a check no longer passes. |
LOCALE_NOT_IN_GAME |
start_game, render_game |
locale is not one of the game's locales or en-XA. Write the catalog, then start a new session. |
UNSUPPORTED_ON_THIS_VERSION |
start_game, render_game |
locale was passed for a framework v1 or v2 game, which shows only its own text. |
A missing translation is never an error: validate_code lists it as a warning and commit in catalogCoverage.