Localization
Localization is configured in the creator via Game / Locales.
Locale data uses TOML. Each top-level table is one language, for example [en], [de], or [fr].
Format
[en]
combat.damage.incoming = "{attacker} hits you for {amount} damage"
combat.damage.outgoing = "You hit {defender} for {amount} damage"
actions.not_ready = "{action} is not ready yet"
spells.not_enough_mp = "Not enough MP to cast {spell}"
system.cant_do_that_yet = "Can't do that yet"
system.cant_afford = "You can't afford that"
system.you_bought = "You bought"
system.exit_menu = "Goodbye"
[de]
combat.damage.incoming = "{attacker} trifft dich fuer {amount} Schaden"
combat.damage.outgoing = "Du triffst {defender} fuer {amount} Schaden"
system.cant_do_that_yet = "Das geht noch nicht"
system.cant_afford = "Du kannst dir das nicht leisten"
system.you_bought = "Du hast gekauft"
system.exit_menu = "Auf Wiedersehen"
The active locale is selected in Game / Settings:
[game]
locale = "en"
Use locale = "auto" to follow the system locale.
Key Names
Use namespaced keys.
system.*: built-in runtime and UI stringscombat.*: combat-related text- your own domains like
quest.*,dialog.*,merchant.*,ui.*
This keeps project strings organized and avoids collisions.
Built-in System Keys
The currently used built-in system keys are:
actions.*: ruleset action feedback such as missing target, missing item, cooldown, and no effectspells.*: ruleset spell feedback such as missing target, missing MP, cooldown, and healingsystem.cant_do_thatsystem.cant_do_that_yetsystem.too_far_awaysystem.cant_affordsystem.you_boughtsystem.exit_menu
These are used by server/client systems directly, so they should exist in every supported locale.
Message Resolution
Localized text is resolved by key first, then placeholders inside the final string are filled.
Example:
[en]
combat.damage.outgoing = "You hit {defender} for {amount} damage"
If rules reference combat.damage.outgoing, the runtime first loads that locale string, then replaces placeholders like:
{attacker}{defender}{amount}{kind}{from_id}{target_id}
For your own custom message(...) calls, you can pass named parameters on the locale key itself.
Example locale entry:
[en]
dialog.hit = "You hit {target} for {amount} damage"
Example script message:
message(id(), "{dialog.hit,target=target.class_name,amount=N:3}", "system")
Supported parameter value forms:
E:<id>.<attr>for entity attributes likeE:11.nameorE:11.class_nameIt:<id>.<attr>/Item:<id>.<attr>for item attributesN:<value>for integersF:<value>for floatsself.<attr>,sender.<attr>,attacker.<attr>,target.<attr>,item.<attr>for message-context shortcuts- plain text like
target=Urg
That means:
E:11.nameusually gives the instance name, for exampleUrgE:11.class_namegives the template/class name, for exampleOrctarget.nameusually gives the sender instance name in a received messagetarget.class_nameusually gives the sender class name in a received messageself.namegives the receiver entity name
Rules Integration
Rules-driven combat messages should usually use locale keys, not hardcoded English strings.
[combat.messages]
incoming_key = "combat.damage.incoming"
incoming_category = "system"
outgoing_key = "combat.damage.outgoing"
outgoing_category = "system"
This lets you translate combat text without changing gameplay rules.
Official rulesets also bundle their own locale defaults. The current v1 ruleset
ships English strings in rulesets/eldiron/v1/locales.toml, and Eldiron loads
those first. Game / Locales is then merged on top, so projects only need to
store changed or additional text.
The official ruleset also stores runtime feedback keys under [messages]:
[messages.actions]
not_ready_key = "actions.not_ready"
missing_target_key = "actions.missing_target"
gathered_key = "actions.gathered"
[messages.spells]
not_enough_mp_key = "spells.not_enough_mp"
heal_key = "spells.heal"
This keeps actions and spells on the same key-based path as combat and progression messages, while still giving each project a clean override layer. The bundled English ruleset locale also includes text command feedback for inventory, containers, gathering, recipes, and action failures.
Text Widgets
Screen text widgets use the same locale-key system.
Example widget text:
Money: {PLAYER.MONEY}
ATK: {PLAYER.ATTACK}
DEF: {PLAYER.ARMOR}
Class: {PLAYER.CLASS}
LV: {PLAYER.LEVEL}
XP: {PLAYER.EXP}
Weapon DMG: {PLAYER.WEAPON.DMG}
Armor Total: {PLAYER.ARMOR.ARMOR}
{ui.quest.ready}
Example locale entry:
[en]
ui.quest.ready = "Quest ready"
Current behavior:
- locale keys such as
{ui.quest.ready}are resolved throughGame / Locales - existing
PLAYER.*status placeholders still work in text widgets PLAYER.MONEYshows the current player wallet formatted through the ruleset economyPLAYER.FUNDSshows the raw current player wallet in base unitsPLAYER.CLASSshows the player's rules class, falling back to the character template namePLAYER.RACEshows the player's rules racePLAYER.<ATTR>shows a player attribute valuePLAYER.LEVELresolves throughgame.levelPLAYER.EXPandPLAYER.EXPERIENCEresolve throughgame.experiencePLAYER.ATTACKsumsDMGacross the player's equipped weapon slotsPLAYER.ARMORsumsARMORacross the player's equipped gear slotsPLAYER.WEAPON.<ATTR>sums an attribute across the player's configured weapon slotsPLAYER.EQUIPPED.<ATTR>sums an attribute across all equipped itemsPLAYER.ARMOR.<ATTR>sums an attribute across the player's configured gear slotsWORLD.HOUR,WORLD.MINUTE,WORLD.TIME,WORLD.TIME_12, andWORLD.TIME_24show the current in-game time
For localized messages and templates, the same derived values are available through entity references and context aliases, for example:
self.weapon.DMGself.armor.ARMORtarget.weapon.DMGE:11.equipped.ARMOR