diff --git a/CLAUDE.md b/CLAUDE.md index a51cd77..cb2fc17 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -120,6 +120,14 @@ and is more lenient, so **a clean pass locally does not prove Node 22**. bodies stored as markdown text, rendered via `renderMarkdown`. - Maintenance/preview mode: admin toggle → frontend serves a branded "we'll be right back" 503; `?preview=` cookie bypasses it. +- **Admin is multilingual** (13 locales, mirrors perry/landing). UI strings + live in bundled per-locale TS bags under `src/admin/i18n/messages/` + (`en.ts` is source of truth, en is the runtime fallback); never hardcode + user-facing chrome — use `t('key')`/`t.plural(...)` from `getT(c)`. + Per-request locale = `users.locale` → `skelpoAdminLang` cookie → + Accept-Language (`src/admin/i18n/middleware.ts`, mounted on `adminRoutes`). + Users set their own language at `/admin/profile` + the sidebar switcher. + Adding a locale = extend `i18n/locales.ts` + add a `messages/.ts`. ## Permissions diff --git a/src/admin/contentEditor.tsx b/src/admin/contentEditor.tsx index 3c303e9..5a61edd 100644 --- a/src/admin/contentEditor.tsx +++ b/src/admin/contentEditor.tsx @@ -10,6 +10,8 @@ import type { FC } from 'hono/jsx'; import type { FieldDef, ContentTypeRow } from '../content/types.js'; import type { ContentDbRow } from '../content/content.js'; import { AdminPage, StatusBadge } from './layout.js'; +import { makeT, type Translator } from './i18n/index.js'; +import { defaultAdminLocale } from './i18n/locales.js'; function val(fields: Record, name: string): string { const v = fields[name]; @@ -29,12 +31,12 @@ function galleryStr(v: unknown): string { // JS-light: a hidden