Skip to main content

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 strings
  • combat.*: 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 effect
  • spells.*: ruleset spell feedback such as missing target, missing MP, cooldown, and healing
  • system.cant_do_that
  • system.cant_do_that_yet
  • system.too_far_away
  • system.cant_afford
  • system.you_bought
  • system.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 like E:11.name or E:11.class_name
  • It:<id>.<attr> / Item:<id>.<attr> for item attributes
  • N:<value> for integers
  • F:<value> for floats
  • self.<attr>, sender.<attr>, attacker.<attr>, target.<attr>, item.<attr> for message-context shortcuts
  • plain text like target=Urg

That means:

  • E:11.name usually gives the instance name, for example Urg
  • E:11.class_name gives the template/class name, for example Orc
  • target.name usually gives the sender instance name in a received message
  • target.class_name usually gives the sender class name in a received message
  • self.name gives 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 through Game / Locales
  • existing PLAYER.* status placeholders still work in text widgets
  • PLAYER.MONEY shows the current player wallet formatted through the ruleset economy
  • PLAYER.FUNDS shows the raw current player wallet in base units
  • PLAYER.CLASS shows the player's rules class, falling back to the character template name
  • PLAYER.RACE shows the player's rules race
  • PLAYER.<ATTR> shows a player attribute value
  • PLAYER.LEVEL resolves through game.level
  • PLAYER.EXP and PLAYER.EXPERIENCE resolve through game.experience
  • PLAYER.ATTACK sums DMG across the player's equipped weapon slots
  • PLAYER.ARMOR sums ARMOR across the player's equipped gear slots
  • PLAYER.WEAPON.<ATTR> sums an attribute across the player's configured weapon slots
  • PLAYER.EQUIPPED.<ATTR> sums an attribute across all equipped items
  • PLAYER.ARMOR.<ATTR> sums an attribute across the player's configured gear slots
  • WORLD.HOUR, WORLD.MINUTE, WORLD.TIME, WORLD.TIME_12, and WORLD.TIME_24 show 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.DMG
  • self.armor.ARMOR
  • target.weapon.DMG
  • E:11.equipped.ARMOR