Creating a mod
Translate the mod name and description
Name the mod, its windows and its signals in the game language while keeping identifiers stable.
Declare text once
Prerequisites: a mod declared with signalMod or toolMod and an assets/translations.json file. This page translates the displayed mod name and description, then construction names. It complements button and message translation.
metadata(
author = "Your name",
name = tr("mod.name"),
description = tr("mod.description")
){
"fallback": "fr",
"languages": {
"fr": {
"mod.name": "Mes signaux",
"mod.description": "Des signaux pour mon réseau."
},
"en": {
"mod.name": "My signals",
"mod.description": "Signals for my network."
}
}
}You can mix plain text and tr. author remains the author’s credit. The display name is optional; omitting it uses the name from mod.json. References also support tr’s named parameters, resolved at build time. Every key must exist in the fallback language.
Distinguish the mod, group and window
A tool has several visible names. Use the product name for the mod and a short title describing each window’s purpose. For example, the BB Timechange group contains a Date and time row; repeating BB Timechange in that row adds no information.
| Displayed text | Declaration to use |
|---|---|
| Name in the game’s mod lists and details | metadata(name = tr("mod.name"), ...) |
| Group in Options → NRF Hub | The title of toolMod or signalMod. With toolMod(modInfo), modInfo.title comes from name in mod.json. |
| Window title and row under Shortcuts | window("clock", tr("window.clock"), ...) |
metadata.name does not replace the group title. In this example, the project sets name: "BB Timechange" in mod.json; the catalogue gives the mod details the same name and translates the window title separately.
fun createMod() = toolMod(modInfo) {
metadata(
author = "Your name",
name = tr("mod.name"),
description = tr("mod.description")
)
window("clock", tr("window.clock"), shortcut = "F9") { event ->
showWindow(event, clock().dateTime().toString(), emptyList())
}
}{
"fallback": "en",
"languages": {
"fr": {
"mod.name": "BB Timechange",
"mod.description": "Consulter la date et l’heure du jeu.",
"window.clock": "Date et heure"
},
"en": {
"mod.name": "BB Timechange",
"mod.description": "View the game date and time.",
"window.clock": "Date and time"
}
}
}The keys mod.name and window.clock are your choice: you can rename them by updating the corresponding tr calls. The position of the Kotlin call determines where the text appears. However, "clock" is the window identifier: keep it stable, without tr, to preserve the player’s shortcut.
F9 is only this example’s default shortcut. The player can change or disable it in Options → NRF Hub, under Shortcuts. An existing compatible preference is preserved even if you change the translated title or default value in an update.
Where translations appear
| Location | Behaviour |
|---|---|
| mod.txt | Name and description in the fallback language. This generated text remains readable when SDK localization is unavailable. |
| nrf-metadata.json | Resolved text for each language, generated alongside mod.txt. No second file needs manual maintenance. |
| New-game mod list | The name and details follow the active game language: exact locale, base language, then fallback. |
| In-game mod manager | The list name, details title and description are adapted as they are displayed. Metadata stored in the save remains intact. |
| Construction and mod contents | construction(name = tr(...), catalogueName = tr(...)) generates translated signal and texture catalogue names. The manager’s resource details also use this text. |
| Hub and Steam publishing | They retain the metadata from their own manifest. This feature does not translate the Steam page or Hub interface. |
A description may contain line breaks; the name remains single-line. Each catalogue belongs to its mod: two packages may use the same keys without sharing their text. Check long names and descriptions in both languages.
Translate each signal model
construction(
states = listOf("imgs/closed.png", "imgs/open.png"),
name = tr("signal.construction"),
catalogueName = tr("signal.catalogue")
){
"fallback": "fr",
"languages": {
"fr": {
"signal.construction": "Mon signal",
"signal.catalogue": "Mes feux"
},
"en": {
"signal.construction": "My signal",
"signal.catalogue": "My lights"
}
}
}name appears in the construction menu; catalogueName names the texture set. Omit catalogueName to reuse name. Plain strings remain supported. With tr, leave nameKey and catalogueNameKey unset: generation associates text with the correct mod and model.
The fallback catalogue name is English when that translation exists, otherwise the fallback language. Rebuild the package after editing JSON and check both languages in the game. A translation changes display text, never identifiers, paths or texture order.