Translations

An app's words live in i18n.json, one entry per phrase with each language beside it. Translators change them in the admin UI, or on the page itself, and every open page shows the change 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 filled from the values given. {{app}} is always the app’s name.
  • A missing language falls back to English. A key missing from English too shows as ?[key.locale], so the gap is visible rather than 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: "MikroSchool" });
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 the case, so “classes” and “Classes” don’t need two entries.
  • The arrow keeps a string live: switching language with i18n.setLocale("fr"), or a translator’s change, redraws it. t("x") without the arrow is read once.
  • The server serves i18n.json with the published changes already laid over it, so the import is always the current words.

Translating

  • In the admin UI, under Translations, every key with a field per language.
  • On the page: a translator switches translate mode on, clicks any text, and edits it where it’s shown. Clicks go to the editor, not the page, so a link doesn’t navigate.
  • Together: a translation session keeps drafts that only its translators see. Each person’s cursor is outlined in their colour, typing shows to the others as it happens, and the session is published in one step when it’s ready.
  • With a link: a session’s link lets someone without an account join as a guest, to edit but not publish.

Only people with the translator role may translate. The UI kit’s own words (Close, Search…, the months of the calendar) come translated already. sluurp i18n lists keys that nothing uses any more.