Add i18n support: per-locale catalogs, URL-driven locale, translated core and examples
CI / full-gate (push) Successful in 2m37s

This commit is contained in:
2026-08-03 22:37:27 +02:00
parent c30cd95ebd
commit 245d1ad5b5
93 changed files with 2480 additions and 464 deletions
+42
View File
@@ -0,0 +1,42 @@
// This plugin's own catalog, and the baseline its other locales are written against. Keys are
// looked up here first and fall back to the host's, so a plugin owns its words without prefixing
// them, and `shifts.count` shows the plural form (host: README → Translating).
import type { PluralMessage } from "#plugin-api";
const messages = {
"scheduling.cancel": "Cancel",
"scheduling.field.assignee": "Assignee",
"scheduling.field.end": "End",
"scheduling.field.start": "Start",
"scheduling.field.title": "Shift title",
"scheduling.filter.label": "Filter shifts",
"scheduling.filter.search": "Search",
"scheduling.filter.searchLabel": "Search shifts",
"scheduling.filter.searchPlaceholder": "Search title or assignee…",
"scheduling.form.submit": "Create shift",
"scheduling.nav.overview": "Overview",
"scheduling.nav.section": "Scheduling",
"scheduling.nav.shifts": "Shifts",
"scheduling.new.title": "New shift",
"scheduling.overview.lead":
"Scheduling coordinates shifts across your team. Anyone can read this overview; the shift list itself is available to people with the <code>scheduling:read</code> permission.",
"scheduling.overview.signIn": "Sign in to view shifts",
"scheduling.overview.title": "Scheduling",
"scheduling.overview.view": "View shifts",
"scheduling.shifts.count": { one: "{{count}} shift", other: "{{count}} shifts" } as PluralMessage,
"scheduling.shifts.new": "New shift",
"scheduling.shifts.title": "Shifts",
"scheduling.table.assignee": "Assignee",
"scheduling.table.end": "End",
"scheduling.table.shift": "Shift",
"scheduling.table.start": "Start",
"scheduling.upstream.create": "Couldn't save the shift — the scheduling service is unavailable.",
"scheduling.upstream.list": "Couldn't reach the scheduling service — try again shortly.",
"scheduling.validation.assignee": "Assign the shift to someone.",
"scheduling.validation.title": "A shift needs a title.",
};
export type SchedulingMessages = typeof messages;
export default messages;
+36
View File
@@ -0,0 +1,36 @@
import type { SchedulingMessages } from "./en-US.ts";
const messages: SchedulingMessages = {
"scheduling.cancel": "Avbryt",
"scheduling.field.assignee": "Tilldelad",
"scheduling.field.end": "Slut",
"scheduling.field.start": "Start",
"scheduling.field.title": "Passets namn",
"scheduling.filter.label": "Filtrera pass",
"scheduling.filter.search": "Sök",
"scheduling.filter.searchLabel": "Sök pass",
"scheduling.filter.searchPlaceholder": "Sök på namn eller person…",
"scheduling.form.submit": "Skapa pass",
"scheduling.nav.overview": "Översikt",
"scheduling.nav.section": "Schemaläggning",
"scheduling.nav.shifts": "Pass",
"scheduling.new.title": "Nytt pass",
"scheduling.overview.lead":
"Schemaläggningen samordnar teamets pass. Alla kan läsa den här översikten; själva passlistan kräver behörigheten <code>scheduling:read</code>.",
"scheduling.overview.signIn": "Logga in för att se passen",
"scheduling.overview.title": "Schemaläggning",
"scheduling.overview.view": "Visa pass",
"scheduling.shifts.count": { one: "{{count}} pass", other: "{{count}} pass" },
"scheduling.shifts.new": "Nytt pass",
"scheduling.shifts.title": "Pass",
"scheduling.table.assignee": "Tilldelad",
"scheduling.table.end": "Slut",
"scheduling.table.shift": "Pass",
"scheduling.table.start": "Start",
"scheduling.upstream.create": "Passet kunde inte sparas — schemaläggningstjänsten är otillgänglig.",
"scheduling.upstream.list": "Vi når inte schemaläggningstjänsten — försök igen om en stund.",
"scheduling.validation.assignee": "Passet måste tilldelas någon.",
"scheduling.validation.title": "Passet behöver ett namn.",
};
export default messages;