Add i18n support: per-locale catalogs, URL-driven locale, translated core and examples #44

Merged
lilleman merged 14 commits from i18n into main 2026-08-04 19:58:32 +02:00
Owner

Every user-facing string now comes from a per-locale catalog: src/i18n/locales/<tag>.ts for the host, plugins/<id>/i18n/<tag>.ts for a plugin (looked up plugin-first, then core). en-US and sv-SE ship.

A request is served by ?locale=sv-SEAccept-Language → en-US. Exact on a full tag; a lone language takes the first regional catalog for it. No cookie — when the URL asked for a language, the host carries ?locale onto the links it renders and ctx.localeHref() does the same for a plugin's.

ctx.t(key, vars) in handlers; t/locale/locales/localeHref/dir are merged into every view at any include depth. {{var}} interpolation, plurals via Intl.PluralRules, and an unknown key renders as itself — which is what lets a nav label be either a catalog key or plain text.

Every catalog is checked against its set's en-US at boot (keys, string-vs-plural kind, plural categories); a mismatch stops startup. Kratos' own flow text is mapped by numeric id (only ids verified against a live Kratos; its generic trait-label id is deliberately unmapped, field labels key on the input name instead).

Also: zero-JS language picker in the shell and on the auth/consent pages, <html lang dir> from the locale, and both example plugins translated with their own catalogs.

apiVersion stays 1.0.0 — the contract additions are additive and nothing is installed against it yet.

Every user-facing string now comes from a per-locale catalog: `src/i18n/locales/<tag>.ts` for the host, `plugins/<id>/i18n/<tag>.ts` for a plugin (looked up plugin-first, then core). en-US and sv-SE ship. A request is served by `?locale=sv-SE` → `Accept-Language` → en-US. Exact on a full tag; a lone language takes the first regional catalog for it. No cookie — when the URL asked for a language, the host carries `?locale` onto the links it renders and `ctx.localeHref()` does the same for a plugin's. `ctx.t(key, vars)` in handlers; `t`/`locale`/`locales`/`localeHref`/`dir` are merged into every view at any include depth. `{{var}}` interpolation, plurals via Intl.PluralRules, and an unknown key renders as itself — which is what lets a nav label be either a catalog key or plain text. Every catalog is checked against its set's en-US at boot (keys, string-vs-plural kind, plural categories); a mismatch stops startup. Kratos' own flow text is mapped by numeric id (only ids verified against a live Kratos; its generic trait-label id is deliberately unmapped, field labels key on the input name instead). Also: zero-JS language picker in the shell and on the auth/consent pages, `<html lang dir>` from the locale, and both example plugins translated with their own catalogs. apiVersion stays 1.0.0 — the contract additions are additive and nothing is installed against it yet.
lilleman added 1 commit 2026-08-03 22:37:39 +02:00
lilleman marked the pull request as work in progress 2026-08-03 22:37:49 +02:00
lilleman added 1 commit 2026-08-03 23:12:18 +02:00
lilleman added 1 commit 2026-08-03 23:21:37 +02:00
lilleman added 1 commit 2026-08-03 23:25:52 +02:00
lilleman added 1 commit 2026-08-03 23:51:38 +02:00
lilleman added 1 commit 2026-08-04 00:10:29 +02:00
lilleman added 1 commit 2026-08-04 00:31:17 +02:00
lilleman added 1 commit 2026-08-04 00:42:52 +02:00
lilleman marked the pull request as ready for review 2026-08-04 08:47:22 +02:00
lilleman added 1 commit 2026-08-04 09:37:01 +02:00
lilleman added 1 commit 2026-08-04 09:58:57 +02:00
lilleman added 1 commit 2026-08-04 10:31:06 +02:00
lilleman added 1 commit 2026-08-04 10:40:36 +02:00
lilleman added 1 commit 2026-08-04 10:43:07 +02:00
Merge branch 'main' into i18n
CI / full-gate (push) Successful in 2m39s
c063b71d19
lilleman added 1 commit 2026-08-04 19:55:53 +02:00
Merge branch 'main' into i18n
CI / full-gate (push) Successful in 2m40s
Mirror / github-mirror (push) Successful in 7s
38f4cffa43
lilleman scheduled this pull request to auto merge when all checks succeed 2026-08-04 19:56:00 +02:00
lilleman merged commit 38f4cffa43 into main 2026-08-04 19:58:32 +02:00
lilleman deleted branch i18n 2026-08-04 19:58:33 +02:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: larvit/plainpages#44