Skip to content

Add translations (i18n)

What you're doing

Every user-visible string on a page is an i18n key, not literal text. Page titles, button labels, column headers, KPI labels, enum option labels — all keys. You supply the actual words in spk-assembly/metadata/i18n/en.json (and fr.json, de.json, … for other locales).

If a key has no entry, the UI renders the raw key (office-equipment.equipment-register.title) — ugly, and flagged by the linter.

The naming convention

<pluginId>.<page-name>.<what>

Examples from the tutorial:

KeyValue
office-equipment.equipment-register.titleEquipment Register
office-equipment.equipment-register.newBtnNew
office-equipment.equipment-register.column.asset_tagAsset Tag
office-equipment.equipment-register.kpi.availableAvailable
office-equipment.equipment-register.status.AVAILABLEAvailable
office-equipment.equipment-checkout.status.OVERDUEOverdue
office-equipment.application.titleOffice Equipment
office-equipment.module.titleEquipment

The complete example

spk-assembly/metadata/i18n/en.json (excerpt — the real file has ~70 keys):

json
{
  "office-equipment.application.title": "Office Equipment",
  "office-equipment.module.title": "Equipment",
  "office-equipment.equipment-register.title": "Equipment Register",
  "office-equipment.equipment-register.subtitle": "Office Equipment tutorial module",
  "office-equipment.equipment-register.newBtn": "New",
  "office-equipment.equipment-register.exportBtn": "Export CSV",
  "office-equipment.equipment-register.kpi.total": "Total",
  "office-equipment.equipment-register.kpi.available": "Available",
  "office-equipment.equipment-register.kpi.assigned": "Assigned",
  "office-equipment.equipment-register.column.asset_tag": "Asset Tag",
  "office-equipment.equipment-register.column.name": "Name",
  "office-equipment.equipment-register.column.status": "Status",
  "office-equipment.equipment-register.status.AVAILABLE": "Available",
  "office-equipment.equipment-register.status.ASSIGNED": "Assigned",
  "office-equipment.equipment-register.form.title": "Equipment",
  "office-equipment.equipment-register.form.asset_tag": "Asset Tag",
  "office-equipment.equipment-register.form.saveBtn": "Save",
  "office-equipment.equipment-register.form.cancelBtn": "Cancel"
}

It's a flat object — dotted keys, string values. No nesting.

Line by line — where each key type comes from

In a page filei18n key referenced
definition.titlethe page title
a block's properties.textKey / labelKey (with source: "static")any static label
a grid column's headerKeycolumn header
a core.select option's labelKeydropdown option label
designer.displayNameKey / descriptionKeyStudio explorer label
metadata/application/*.json titleKey, metadata/module/*.json titleKeyapp / module name

Every one of those must have a matching entry in en.json.

Adding another language

Add fr.json with the same keys, French values. Missing keys in fr.json fall back to en.json. The ERP picks the file by the user's locale.

Keeping it in sync automatically

You don't have to hand-track keys. A small script that walks your page/menu/app files collecting every *Key / headerKey / static title value, then writes en.json with a humanised default for any missing key, is the practical approach — the tutorial's build uses exactly this. The linter is your safety net.

How to verify it worked

bash
erp plugin test office-equipment/spk-assembly

A missing key is reported as:

I18N-001  equipment-register.json: i18n key
          "office-equipment.equipment-register.kpi.total" not found in metadata/i18n/en.json

A clean run means every referenced key resolves.

Common mistakes

SymptomCauseFix
UI shows office-equipment.x.y literallykey missing from en.jsonadd it (linter tells you which)
en.json is nested objectswrong shapeflat { "a.b.c": "text" }
enum chips show the raw enum valueno ...status.<VALUE> keysadd one per enum value
translation not appliedfr.json missing keys, or wrong locale on the userkeys fall back to en; check the user's locale