Appearance
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:
| Key | Value |
|---|---|
office-equipment.equipment-register.title | Equipment Register |
office-equipment.equipment-register.newBtn | New |
office-equipment.equipment-register.column.asset_tag | Asset Tag |
office-equipment.equipment-register.kpi.available | Available |
office-equipment.equipment-register.status.AVAILABLE | Available |
office-equipment.equipment-checkout.status.OVERDUE | Overdue |
office-equipment.application.title | Office Equipment |
office-equipment.module.title | Equipment |
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 file | i18n key referenced |
|---|---|
definition.title | the page title |
a block's properties.textKey / labelKey (with source: "static") | any static label |
a grid column's headerKey | column header |
a core.select option's labelKey | dropdown option label |
designer.displayNameKey / descriptionKey | Studio explorer label |
metadata/application/*.json titleKey, metadata/module/*.json titleKey | app / 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-assemblyA missing key is reported as:
I18N-001 equipment-register.json: i18n key
"office-equipment.equipment-register.kpi.total" not found in metadata/i18n/en.jsonA clean run means every referenced key resolves.
Common mistakes
| Symptom | Cause | Fix |
|---|---|---|
UI shows office-equipment.x.y literally | key missing from en.json | add it (linter tells you which) |
en.json is nested objects | wrong shape | flat { "a.b.c": "text" } |
| enum chips show the raw enum value | no ...status.<VALUE> keys | add one per enum value |
| translation not applied | fr.json missing keys, or wrong locale on the user | keys fall back to en; check the user's locale |