Internationalization
The i18n key formats for app settings pages, widgets, workflow actions, action inputs and triggers, with locale fallback rules.
The i18n object holds every string your app shows in Kustomer's own UI: settings page labels, widget titles, and the names of your actions, their inputs and your triggers in the workflow builder. Keys are grouped by locale; Kustomer shows the agent's locale when you provide it and falls back to en_us.
{
"i18n": {
"en_us": { "<key>": "<text>" },
"fr_fr": { "<key>": "<texte>" }
}
}Always provide en_us. Missing keys fall back to en_us, and with no en_us entry Kustomer shows the raw key or system name.
Strings inside your KViews, widgets and custom settings pages are yours to translate; i18n covers only the UI Kustomer renders for you.
Settings page strings
<app> is your app name as written in the definition.
{
"en_us": {
"acme_orders.settings.page.title": "Acme Orders",
"acme_orders.settings.page.description": "Connect your Acme store.",
"acme_orders.settings.path.default.apiKey.displayName": "API key",
"acme_orders.settings.path.default.apiKey.description": "Create one in Acme under **Settings › API**."
}
}Widget strings
Workflow builder strings
Actions and triggers are labelled under an app.<app>.workflow.builder prefix followed by their full, prefixed name:
<action name> and <event name> are the full names, including kustomer.app.<app>., so the app name appears twice:
{
"en_us": {
"app.acme_orders.workflow.builder.actions.kustomer.app.acme_orders.refund.displayName": "Refund order",
"app.acme_orders.workflow.builder.actions.kustomer.app.acme_orders.refund.description": "Issue a full or partial refund in Acme.",
"app.acme_orders.workflow.builder.actions.kustomer.app.acme_orders.refund.properties.orderId.displayName": "Order ID",
"app.acme_orders.workflow.builder.actions.kustomer.app.acme_orders.refund.properties.orderId.exampleText": "4417",
"app.acme_orders.workflow.builder.actions.kustomer.app.acme_orders.refund.properties.amount.displayName": "Amount",
"app.acme_orders.workflow.builder.triggers.kustomer.app.acme_orders.order-updated.displayName": "Acme order updated",
"app.acme_orders.workflow.builder.triggers.kustomer.app.acme_orders.order-updated.description": "Runs whenever Acme sends an order update. Later steps can use the order ID, customer email, status and total."
}
}Keep keys in sync
Keys are long and easy to mistype, and a typo fails silently: the builder just shows the raw name. Generate them from the definition in your build step instead of writing them by hand:
const app = 'acme_orders'
const prefix = `app.${app}.workflow.builder`
export function actionStrings(actions, labels) {
const out = {}
for (const action of actions) {
const l = labels[action.name] ?? {}
out[`${prefix}.actions.${action.name}.displayName`] = l.displayName ?? action.description
if (l.description) out[`${prefix}.actions.${action.name}.description`] = l.description
for (const input of Object.keys(action.inputSchema?.properties ?? {})) {
const p = l.properties?.[input] ?? {}
out[`${prefix}.actions.${action.name}.properties.${input}.displayName`] = p.displayName ?? input
if (p.exampleText) out[`${prefix}.actions.${action.name}.properties.${input}.exampleText`] = p.exampleText
}
}
return out
}And check for gaps before you register. This prints the label key of every setting that doesn't have one:
jq -r '
.app as $app | .i18n.en_us as $t
| .settings | to_entries[] | .key as $category
| .value | keys[]
| "\($app).settings.path.\($category).\(.).displayName"
| select($t[.] == null)
' dist/acme_orders.json