Localization (i18n)

Tiger never hardcodes a user-facing string. Everything is a semantic, owner-prefixed key, and the platform localizes for free.

Keys, not strings

Use a semantic key namespaced by owner: core.* (framework), app.* (your app), <module>.* (a module), then .area.type.name:

billing.invoice.created
core.api.error.not_allowed

Strings live in plain PHP array files — <module>/languages/<lang>/<module>.php returning ['key' => 'text'] — cascading core → package modules → app modules → app global (last wins). In views and forms use $this->_t('key'); API response messages translate automatically.

Language-only locales

Locales are language-only (en, es) — region subtags are reserved for genuine regional divergence, not required up front.

Semantic locale URLs

/es/anything works on every route — the prefix is resolved and stripped before routing. Resolution order is URL → cookie → browser → default, and the choice persists to a cookie (the header language switcher writes the same cookie + localStorage).

Live overrides, no deploy

A translation DB table overrides or adds any string at request time — same live-override pattern as config. Change a label for one org, or fix a typo in production, without a deploy.

See also