Translations
Translations live in i18n.json, one entry per phrase with a value per language. Translators edit them in the admin UI or directly on the page, and every open page updates without a reload.
The file
{
"locales": ["en", "fr", "de"],
"classes": { "en": "classes", "fr": "classes", "de": "klassen" },
"welcome": { "en": "welcome to {{app}}, {{name}}", "fr": "bienvenue sur {{app}}, {{name}}" }
}- Keys are lower case.
{{name}}is interpolated from the params you pass.{{app}}is always the app’s name.- Missing languages fall back to English. A key missing in English too renders as
?[key.locale], so gaps are visible instead of blank.
In the app
import { createI18n } from "sluurp/i18n";
import table from "./i18n.json" with { type: "json" };
export const i18n = createI18n({ table, appName: "My app" });
export const t = i18n.t;
i18n.connect(sluurp); // live updates<h1>{() => t("classes|title")}</h1> // "Classes"
<p>{() => t("welcome", { name: me.first_name })}</p>|title,|upperand|lowerchange case, so “classes” and “Classes” don’t need separate entries.- Wrapping in an arrow function makes it reactive:
i18n.setLocale("fr")or a translator’s edit re-renders it. A baret("x")is evaluated once. - The server merges published edits into
i18n.jsonwhen serving it, so the import is always current.
Translating
- Admin UI: Translations lists every key with a field per language.
- In place: turn on translate mode, click any text and edit it where it appears. Clicks go to the editor, so links don’t navigate.
- Collaboratively: a translation session holds drafts visible only to its translators. Everyone’s cursor shows in their colour, typing appears live, and the session is published in one step.
- Guests: a session link lets someone without an account edit (but not publish).
Only users with the translator role can translate. The UI kit’s built-in strings (Close, Search…, month names) come pre-translated. sluurp i18n lists unused keys.