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

app/i18n.json
{
  "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

app/i18n.ts
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
app/header.tsx
<h1>{() => t("classes|title")}</h1>          // "Classes"
<p>{() => t("welcome", { name: me.first_name })}</p>
  • |title, |upper and |lower change 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 bare t("x") is evaluated once.
  • The server merges published edits into i18n.json when 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.