dorfteich/apps/web/src/i18n/index.ts
Claude Fable 5 418aafd5ec
Some checks failed
CI / Lint, typecheck, test (pull_request) Successful in 4m43s
CI / Build container images (pull_request) Successful in 1m11s
CI / Auth e2e pack (pull_request) Successful in 7m14s
CI / Import/export fidelity gate (pull_request) Successful in 56s
CD / Deploy to Test (push) Blocked by required conditions
CD / Smoke tests against Test (push) Blocked by required conditions
CD / Promote to Int (push) Blocked by required conditions
CI / Auth e2e pack (push) Blocked by required conditions
CI / Import/export fidelity gate (push) Blocked by required conditions
CI / Build container images (push) Blocked by required conditions
CD / Build and push images (push) Has been cancelled
CI / Lint, typecheck, test (push) Has been cancelled
#163: Dokumentsprache und Seitentitel der SPA
i18n spiegelt die aktive Sprache auf <html lang> (Init + languageChanged;
der User-Locale-Wechsel in auth-context läuft über dasselbe Event). Neuer
useDocumentTitle-Hook setzt je Route einen sprechenden Titel
(Seite — Teich — Dorfteich), verdrahtet in allen Routen-Komponenten;
dynamische Titel folgen den geladenen Daten.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGM8jo3hwoV9wsCVGfy8iq
2026-07-21 13:52:59 +02:00

146 lines
5.9 KiB
TypeScript

import deAccess from '@dorfteich/shared/i18n/de/access.json';
import deAuth from '@dorfteich/shared/i18n/de/auth.json';
import deComments from '@dorfteich/shared/i18n/de/comments.json';
import deCommon from '@dorfteich/shared/i18n/de/common.json';
import deEditor from '@dorfteich/shared/i18n/de/editor.json';
import deErrors from '@dorfteich/shared/i18n/de/errors.json';
import deExport from '@dorfteich/shared/i18n/de/export.json';
import deFiles from '@dorfteich/shared/i18n/de/files.json';
import deFont from '@dorfteich/shared/i18n/de/font.json';
import deGraph from '@dorfteich/shared/i18n/de/graph.json';
import deImport from '@dorfteich/shared/i18n/de/import.json';
import deLabels from '@dorfteich/shared/i18n/de/labels.json';
import deLegal from '@dorfteich/shared/i18n/de/legal.json';
import deLinks from '@dorfteich/shared/i18n/de/links.json';
import deMembers from '@dorfteich/shared/i18n/de/members.json';
import deNotifications from '@dorfteich/shared/i18n/de/notifications.json';
import dePlugins from '@dorfteich/shared/i18n/de/plugins.json';
import dePublic from '@dorfteich/shared/i18n/de/public.json';
import deQuotas from '@dorfteich/shared/i18n/de/quotas.json';
import deSearch from '@dorfteich/shared/i18n/de/search.json';
import deSetup from '@dorfteich/shared/i18n/de/setup.json';
import deApiTokens from '@dorfteich/shared/i18n/de/apiTokens.json';
import deSystem from '@dorfteich/shared/i18n/de/system.json';
import deTasks from '@dorfteich/shared/i18n/de/tasks.json';
import deUsers from '@dorfteich/shared/i18n/de/users.json';
import deWatches from '@dorfteich/shared/i18n/de/watches.json';
import deSettings from '@dorfteich/shared/i18n/de/settings.json';
import enAccess from '@dorfteich/shared/i18n/en/access.json';
import enAuth from '@dorfteich/shared/i18n/en/auth.json';
import enComments from '@dorfteich/shared/i18n/en/comments.json';
import enCommon from '@dorfteich/shared/i18n/en/common.json';
import enEditor from '@dorfteich/shared/i18n/en/editor.json';
import enErrors from '@dorfteich/shared/i18n/en/errors.json';
import enExport from '@dorfteich/shared/i18n/en/export.json';
import enFiles from '@dorfteich/shared/i18n/en/files.json';
import enFont from '@dorfteich/shared/i18n/en/font.json';
import enGraph from '@dorfteich/shared/i18n/en/graph.json';
import enImport from '@dorfteich/shared/i18n/en/import.json';
import enLabels from '@dorfteich/shared/i18n/en/labels.json';
import enLegal from '@dorfteich/shared/i18n/en/legal.json';
import enLinks from '@dorfteich/shared/i18n/en/links.json';
import enMembers from '@dorfteich/shared/i18n/en/members.json';
import enNotifications from '@dorfteich/shared/i18n/en/notifications.json';
import enPlugins from '@dorfteich/shared/i18n/en/plugins.json';
import enPublic from '@dorfteich/shared/i18n/en/public.json';
import enQuotas from '@dorfteich/shared/i18n/en/quotas.json';
import enSearch from '@dorfteich/shared/i18n/en/search.json';
import enSetup from '@dorfteich/shared/i18n/en/setup.json';
import enApiTokens from '@dorfteich/shared/i18n/en/apiTokens.json';
import enSystem from '@dorfteich/shared/i18n/en/system.json';
import enTasks from '@dorfteich/shared/i18n/en/tasks.json';
import enUsers from '@dorfteich/shared/i18n/en/users.json';
import enWatches from '@dorfteich/shared/i18n/en/watches.json';
import enSettings from '@dorfteich/shared/i18n/en/settings.json';
import i18n from 'i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import { initReactI18next } from 'react-i18next';
/**
* Translation resources are bundled (no async loading): the whole catalog
* is small, and the offline-capable editor (ADR 0003) must not depend on
* fetching language files. Detection: ?lng=… wins, then the browser
* language; the user-profile setting (issue #17) will be layered on top.
*/
void i18n
.use(LanguageDetector)
.use(initReactI18next)
.init({
resources: {
en: {
common: enCommon,
comments: enComments,
errors: enErrors,
access: enAccess,
auth: enAuth,
settings: enSettings,
editor: enEditor,
export: enExport,
files: enFiles,
font: enFont,
graph: enGraph,
import: enImport,
labels: enLabels,
legal: enLegal,
links: enLinks,
members: enMembers,
notifications: enNotifications,
plugins: enPlugins,
public: enPublic,
quotas: enQuotas,
search: enSearch,
setup: enSetup,
apiTokens: enApiTokens,
system: enSystem,
tasks: enTasks,
users: enUsers,
watches: enWatches,
},
de: {
common: deCommon,
comments: deComments,
errors: deErrors,
access: deAccess,
auth: deAuth,
settings: deSettings,
editor: deEditor,
export: deExport,
files: deFiles,
font: deFont,
graph: deGraph,
import: deImport,
labels: deLabels,
legal: deLegal,
links: deLinks,
members: deMembers,
notifications: deNotifications,
plugins: dePlugins,
public: dePublic,
quotas: deQuotas,
search: deSearch,
setup: deSetup,
apiTokens: deApiTokens,
system: deSystem,
tasks: deTasks,
users: deUsers,
watches: deWatches,
},
},
defaultNS: 'common',
fallbackLng: 'en',
supportedLngs: ['de', 'en'],
interpolation: { escapeValue: false }, // React already escapes.
detection: { order: ['querystring', 'navigator'], lookupQuerystring: 'lng', caches: [] },
});
// Mirror the active language onto <html lang> (issue #163, WCAG 3.1.1):
// index.html ships a static fallback, and screen readers pick their voice
// from this attribute. Fires on init and on every later switch (e.g. the
// user-profile locale applied in auth-context).
i18n.on('languageChanged', (lng) => {
document.documentElement.lang = lng;
});
if (i18n.resolvedLanguage) document.documentElement.lang = i18n.resolvedLanguage;
export default i18n;