Guide
Localisation
The game is English, and translatable. There are two kinds of text, translated two ways:
| Text | Where it lives | How a mod translates it |
|---|---|---|
| Interface strings: menus, buttons, labels, settings, the match screen, hub screens | the game's catalogue, one string per key | locale/<lang>.json: the same keys, your text |
| Content text: story events, press lines, social posts, commentary, tour and event names | records in the content packs (core is one) | locale/<lang>/patches/*.json: patches on records |
The player picks a language in Settings → Language. The game ships in English; a mod can add any other language, or change the English strings.
Interface strings#
A locale file is one flat JSON object of "key": "text":
my-translation/
mod.json
locale/
fr.json{
"language.name": "Français",
"language.intl": "fr-FR",
"menu.new": "Nouvelle carrière",
"menu.new.detail": "Crée un joueur et commence sur le circuit du comté",
"mods.errors": "{n, plural, one {# erreur} other {# erreurs}}"
}- The file name is the language: a BCP 47 tag such as
fr,de,pt-BR. A regional file falls back to its language (pt-BR→pt), then to English. - Every key the game uses is in English in
StreamingAssets/locales/en/(split by area; the keys are global). language.nameis the language's name in itself; it's what the Language setting shows.language.intlis the locale used for numbers, money and dates (12.345 £and14. Märzinde-DE); leave it out and the file's tag is used.- Anything you don't translate shows in English, so a translation can be partial and grow.
- To change a few English strings (say, rename "Tour Card" everywhere), ship
locale/en.jsonwith just those keys. - Several mods can provide the same language: later mods (load order on the Mods screen) win key by key. A mod's strings beat the game's own for that language.
Strings from mods load when the game starts and whenever the Mods screen reloads or toggles mods; the interface switches language immediately.
Message syntax#
Messages use a small subset of ICU MessageFormat:
| Syntax | Result |
|---|---|
Welcome back, {name} | the value as given |
{n, number} | a number in the language's style (1,234 / 1.234) |
{p, number, percent} | a percentage (42% / 42 %) |
{n, plural, =0 {no darts} one {# dart} other {# darts}} | picks by the language's plural rules; # is the number |
{n, selectordinal, one {#st} two {#nd} few {#rd} other {#th}} | ordinals |
{device, select, keys {keyboard} other {controller}} | picks by value |
- Plural categories are the language's own (CLDR): English and German use
oneandother; Polish addsfewandmany; Japanese only hasother. Every plural and select needs another.=0,=1… match exact numbers first. - Keep the placeholders the English message has. You can drop one or move it anywhere in the sentence; a placeholder English doesn't have is never filled (it would show as
{name}). - Apostrophes and quotes are plain text (
It’s,don't). There is no escaping, so a message can't contain a literal{or}.
Checks#
Locale files are checked when mods load, like every other file, and problems show on the Mods screen with the file, line and key:
- Errors (the mod is disabled): invalid JSON, a value that isn't a string, a message with a syntax error (an unclosed
{, a plural withoutother). - Warnings (the string still loads): a key the game doesn't use (usually a typo, or a string from another game version), a placeholder English doesn't fill, a file name that isn't a language tag.
Your mod's own strings#
Keys that start with your mod's id and a dot (showcase.corner.title in the mod showcase) are yours, not the game's: they aren't checked against the game's keys, so they never warn as unknown. Your script reads them with dtj.t, in the player's language, falling back to English:
locale/en.json
{
"showcase.corner.title": "Pundit’s Corner",
"showcase.corner.climber": "{name} is up {gain, plural, one {# place} other {# places}} this week."
}locale/de.json
{
"showcase.corner.title": "Expertenecke",
"showcase.corner.climber": "{name} klettert diese Woche {gain, plural, one {# Platz} other {# Plätze}}."
}They use the same message syntax, so plurals and number formats follow the language. A money amount goes in as a value (dtj.t('corner.leader', { amount: '{money:12000}' })), because a message can't contain a literal {. The strings travel with the script into a career and follow the installed mod (same version) when the career loads. The showcase mod writes all of its script's text this way, in English and German.
The Mod Studio's validation checks a pack's locale files the same way.
Content text#
Content is data, so its translation is data too: merge patches, like data/patches/, that only apply when their language is chosen. Put them in locale/<lang>/patches/ (any file names):
my-translation/
locale/
fr.json
fr/
patches/
stories.json
press.jsonlocale/fr/patches/stories.json
[
{
"target": "narrative:core.narrative.young-fan-letter",
"patch": {
"title": "Une lettre d’Ellie",
"body": "Une lettre écrite au crayon, avec soin : « Cher {first}, j’ai 10 ans… »",
"choices": [
{ "id": "write-back", "label": "Lui répondre, avec des ailettes signées" },
{ "id": "busy", "label": "La poser sur la pile", "outcome": "Elle reste sur la pile." }
]
}
},
{
"target": "mediaLine:core.line.headline-result-1",
"patch": { "text": "EN PLEIN DANS LE MILLE ! {subjectLast} remporte le {event}" }
},
{
"target": "commentary:core.commentary.180a",
"patch": { "text": "Premier 180 de la soirée pour {player} !" }
}
]- Targets are the same as for patches#patching-instead-of-replacing): the type key and the record id. Translate any text field: story titles, bodies, choice labels and outcomes; press lines; interview questions and answers (answers are matched by
id; see Interviews); social posts and account names; card titles; tour, event, venue and sponsor names; nation names (Nations); equipment names and descriptions; staff roles; exhibition formats. - Translation patches apply after every other patch, so they translate whatever the installed packs ended up with. A regional language applies its base language's patches first (
pt-BRgetspt's, then its own). - One difference from ordinary patches, so that you only restate text: lists of objects merge item by item instead of being replaced. Items are matched by
idwhen your items have one (story choices), otherwise by position (a choice'soutcomes). Effects, weights and conditions you leave out stay as they are, so a story's balance can change later without breaking its translations. - Keep placeholders like
{first},{subjectLast},{event}or{player}exactly as they are; they're filled in by the game. The same goes for money tokens like{money:500}(see Money). - Translation patches are checked in every language (a malformed file disables the mod even when another language is chosen); a target that doesn't exist is a warning, reported when that language is in use.
When content text changes language#
Content is snapshotted into a career when it starts (see getting started#where-mods-live)), so:
- New careers get all content in the chosen language.
- Existing careers pick up translated stories, the press pack, interviews, the social feed, cards and nation names the next time they're loaded; tours, events, players and the rest stay as the career started.
- Text the career has already written (inbox messages, news stories, interview answers) stays in the language it was written in.
The game's own sentences#
The sentences the game writes itself (inbox messages, news stories, season goals, the reasons an event is closed to you, ledger lines) are content too: text records in data/texts/, one per sentence.
[
{ "id": "core.text.qschool.passed.body", "text": "You’ve come through {text:name.qschool}. Welcome to the {tour:core.tour.pro} — your card runs to the end of {until}." }
]| In a text | Becomes |
|---|---|
{until}, {name}… | a value the game fills in. Keep the ones the text has; you can move or drop them |
{tour:<tour id>} | that tour's name, as the packs have it |
{text:<key>} | another text ({text:name.qschool} is core.text.name.qschool) |
{money:500} | a money amount (see Money) |
- Change one with a patch:
{ "target": "text:core.text.qschool.passed.body", "patch": { "text": "…" } }indata/patches/. - Translate one with the same patch in
locale/<lang>/patches/. - Names used in many sentences are texts of their own in
data/texts/names.json:core.text.name.qschool("Trial Week"),core.text.name.season-opener,core.text.name.card-tours. Change the name once and every sentence follows. Tour names come from the tours themselves. - The core pack's files are the list of every text:
data/texts/*.json, grouped by the part of the game that writes them. Keys such ascore.text.session.start-event.4were made by a tool from the code; the sentence beside the key says what it is. - A career picks up changed texts the next time it is loaded. Messages it has already written stay as they were.
- A text the packs don't have is shown as its key, so a typo in an id is easy to spot.
What's new#
The main menu's What's new list is texts too, in data/texts/whats-new.json: a line is core.text.whats-new.<version>.<section>.<n> and a section's heading is core.text.whats-new.section.<section>. The menu shows the lines of the game's own version. A line can say where it shows with platform: "phone" (the phone layouts), "pc" (the desktop layouts) or "both"; a line without it shows on both. A section with no line for this platform is left out, and a version with none shows no What's new there.
[
{ "id": "core.text.whats-new.0.50.17.new.1", "text": "Shown on the phone and the PC." },
{ "id": "core.text.whats-new.0.50.17.new.2", "text": "Shown on the phone only.", "platform": "phone" },
{ "id": "core.text.whats-new.0.50.17.new.3", "text": "Shown on the PC only.", "platform": "pc" }
]The platform follows the layouts the game shows, not the device: a Windows build started with the phone layouts shows the phone's list.
The referee and the walk-on announcer#
What the referee calls and what the announcer says at a walk-on are texts too (data/texts/referee.json in the core pack). They are spoken by the text-to-speech voice, so write them as they should be said.
Key (after core.text.) | Core text | Placeholders |
|---|---|---|
referee.visit.no-score | No score | |
referee.visit.<score> | only referee.visit.180: One hundred and eighty! | A score with a text of its own is called with it instead of the number's words: add core.text.referee.visit.100 ("Ton") in a mod's data/texts/ to give 100 its own call. |
referee.throw-first | {name} to throw first | name: the first word of the player's name |
referee.you-require | {name}, you require {score} | score: the number in words |
referee.game-shot.match, .set, .leg | Game shot, and the match! / Game shot, and the {ordinal} set! / Game shot, and the {ordinal} leg | ordinal: first, second... |
announcer.first, announcer.second | Ladies and gentlemen… from {country}… {name}! / And now… from {country}… {name}! | name is built by one of the next two |
announcer.name, announcer.name.nickname | {first} {last} / {first} '{nickname}' {last} | |
referee.number.0 to .19, .20, .30 ... .90 | zero ... nineteen, twenty ... ninety | |
referee.number.tens-ones, .hundreds, .hundreds-and | {tens}-{ones} / {hundreds} hundred / {hundreds} hundred and {rest} | how a number is put together |
referee.ordinal.1 to .13, referee.ordinal.other | first ... thirteenth / {n}th |
Which call is made, and whether it is a big one (180, 140 and over, a game shot), is the game's; the words are the pack's. These texts are read from the installed packs when the call is made, so a change is heard in the next match, in careers already under way too.
Money#
The player picks a currency in Settings → Language → Currency: pounds (the default), euros, US, Australian, Canadian or New Zealand dollars, Swedish or Danish kronor, Swiss francs or yen. It only changes how money is shown: the game keeps and simulates everything in pounds, so every amount in content data (prize tables, fees, prices, wages, a story's money effect) is in pounds, whatever the player sees. Other currencies are converted at a fixed rate and rounded to a figure a tour would print (£500,000 shows as €575,000); "Symbol only" keeps the pound figures and only swaps the sign.
So don't write £ into content text. Write the amount as a money token and the game shows it in the player's currency and language style:
| Token | English, pounds | English, euros | German, euros |
|---|---|---|---|
{money:500} | £500 | €575 | 575 € |
{money:1250000} | £1,250,000 | €1,450,000 | 1.450.000 € |
{money:1250000:compact} | £1.3M | €1.5M | 1,5 Mio. € |
- The number is whole pounds, no separators or sign:
{money:1000}, not{money:1,000}or{money:£1000}. A negative amount is{money:-300}. - Tokens work in any content text: story bodies, choice labels and outcomes, press lines, social posts, interview answers, card titles and subtitles. Placeholders that stand for money (a story's
{money}, the Nations Cup champions'{prize}) already come as amounts in the player's currency: write "They share {prize}", not "£{prize}". - In a translation patch, keep the token as it is and move it wherever the sentence needs it; the symbol's position and the digit grouping follow the language (
language.intl). - Interface strings (
locale/<lang>.json) don't need tokens: the game passes amounts to them already formatted ("team.perWeek": "{money}/wk"). - Mod scripts and panels can use tokens in the text they hand to the game in the same way.
Not translatable yet#
Some text is still written in English by the game's code rather than looked up by key; it isn't reachable by locale files yet:
- Some short labels the simulation hands to the screens (a single word such as "Winner", a round's name) and a few sentences other code looks for by their wording. Most of what the simulation writes is now in the pack: see The game's own sentences.
- The referee's calls and the walk-on announcer: they're spoken by the English voice.
- The Mod Studio and the mod loader's own messages (they're for mod authors).
- Developer screens.
- Release notes on the main menu.
Testing a translation#
- Switch language in Settings → Language; no restart needed. After editing a locale file, press Reload mods.
- Long words wrap badly in narrow buttons and chips; prefer the shorter word where space is tight.