Files
AfiS-Aktensystem/web/lib/i18n/de.ts
T
Harlekin ade977538d Web-Anwendung: Design-Tokens, Texte, Gesundheitsendpunkt
Design-Tokens aus docs/10-design-system.md als CSS-Variablen: Grundpalette,
die sechs Klassifizierungsfarben, Eckenradius durchgehend 2 px, keine Schatten
ausser dem Fokusring. Der Schwärzungsbalken mit dem einmaligen Durchwischen
respektiert prefers-reduced-motion. Vier Tests halten die Tokens gegen das
Dokument.

IBM Plex selbst gehostet als woff2, latin und latin-ext getrennt über
unicode-range, font-display: swap, immutable-Cache.

Alle Oberflächentexte in lib/i18n/de.ts. Ein Test durchsucht app/ und
components/ nach Umlauten im JSX und schlägt fehl, wenn dort deutscher Text
steht. Er hat beim ersten Lauf gleich einen Verstoss gefunden.

/api/health prüft Datenbank, Discord-Gateway und ClamAV. Die Gesamtbewertung
fällt nur auf "down", wenn die Datenbank weg ist: die anderen Dienste haben
eigene Healthchecks im Stack, und ein ausgefallener Virenscanner soll nicht
den Web-Container als tot markieren.

Strikte CSP mit Nonce, kein unsafe-inline für Skripte. Die erste Route liegt
bei 100 kB JavaScript nach Kompression, Budget sind 120 kB.
2026-09-01 00:01:41 +02:00

161 lines
4.5 KiB
TypeScript

/**
* Alle Oberflaechentexte. Nie deutsche Strings hart ins JSX, siehe CLAUDE.md.
* Die Begriffe folgen dem Glossar in docs/01-produkt.md.
*
* Schreibweise nach docs/10-design-system.md:
* - Aktive Verben, die sagen, was passiert: "Vermerk speichern", nicht "Absenden"
* - Dieselbe Bezeichnung durch den ganzen Ablauf
* - Fehler nennen Ursache und naechsten Schritt, ohne Entschuldigung
* - Leere Zustaende sind eine Aufforderung
* - Keine Ausrufezeichen, kein "bitte", kein "erfolgreich"
*/
export const de = {
app: {
name: 'AfiS-Aktensystem',
shortName: 'AfiS',
description: 'Fallakten, Personalakten und Ermittlungen der Abteilung für innere Sicherheit',
},
nav: {
dashboard: 'Übersicht',
cases: 'Fallakten',
personnel: 'Personalakten',
search: 'Suche',
admin: 'Verwaltung',
skipToContent: 'Zum Inhalt springen',
},
auth: {
signIn: 'Mit Discord anmelden',
signOut: 'Abmelden',
signedInAs: 'Angemeldet als',
notInGuild: {
title: 'Du bist nicht auf dem AfiS-Discord',
body: 'Die Anmeldung setzt eine Mitgliedschaft auf dem Discord-Server voraus. Wende dich an die Abteilungsleitung.',
},
sessionEnded: {
title: 'Sitzung beendet',
body: 'Deine Rollen haben sich geändert. Melde dich neu an.',
},
error: {
title: 'Anmeldung nicht möglich',
body: 'Der Anmeldevorgang wurde abgebrochen. Erneut versuchen.',
},
},
clearance: {
label: 'Freigabestufe',
classification: 'Klassifizierung',
level: (n: number) => `Stufe ${n}`,
names: {
0: 'Offen',
1: 'Intern',
2: 'Vertraulich',
3: 'Geheim',
4: 'Streng geheim',
5: 'Leitungssache',
} as Record<number, string>,
locked: 'Inhalt gesperrt',
lockedHint: (n: number) => `Sichtbar ab Stufe ${n}`,
redacted: 'Geschwärzt',
redactedHint: (n: number) => `Geschwärzt, ab Stufe ${n} sichtbar`,
},
case: {
one: 'Fallakte',
many: 'Fallakten',
number: 'Aktenzeichen',
title: 'Titel',
type: 'Fallart',
status: 'Status',
dueAt: 'Wiedervorlage',
outcome: 'Ergebnis',
createdBy: 'Angelegt von',
timeline: 'Verlauf',
sections: 'Abschnitte',
chat: 'Aktenchat',
changes: 'Änderungen',
participants: 'Beteiligte',
links: 'Verknüpfungen',
attachments: 'Anhänge',
empty: 'Noch keine Fallakte. Erste Fallakte anlegen.',
statusNames: {
DRAFT: 'Entwurf',
OPEN: 'Offen',
DORMANT: 'Ruhend',
REVIEW: 'In Prüfung',
CLOSED: 'Abgeschlossen',
ARCHIVED: 'Archiviert',
SEALED: 'Versiegelt',
} as Record<string, string>,
},
entry: {
one: 'Vermerk',
many: 'Vermerke',
create: 'Vermerk speichern',
empty: 'Noch kein Vermerk. Ersten Vermerk anlegen.',
edited: (v: number) => `bearbeitet · v${v}`,
history: 'Änderungsverlauf',
changeReason: 'Begründung',
retracted: (date: string, person: string, reason: string) =>
`Eintrag zurückgezogen am ${date} durch ${person} · Begründung: ${reason}`,
types: {
NOTE: 'Vermerk',
INTERROGATION: 'Befragung',
EVIDENCE: 'Beweismittel',
STATUS_CHANGE: 'Statuswechsel',
CORRECTION: 'Korrektur',
SYSTEM: 'System',
} as Record<string, string>,
},
personnel: {
one: 'Personalakte',
many: 'Personalakten',
serviceNumber: 'Dienstnummer',
rank: 'Dienstgrad',
department: 'Abteilung',
empty: 'Noch keine Personalakte. Erste Personalakte anlegen.',
},
common: {
save: 'Speichern',
cancel: 'Abbrechen',
undo: 'Rückgängig',
retry: 'Erneut versuchen',
loading: 'Wird geladen',
offline: 'Keine Verbindung, wird nachgesendet',
unknown: 'Unbekannt',
},
errors: {
notFound: {
title: 'Nicht gefunden',
body: 'Die Seite gibt es nicht, oder du darfst sie nicht sehen.',
},
unexpected: {
title: 'Nicht geladen',
body: 'Der Abruf ist fehlgeschlagen. Erneut versuchen.',
},
conflict: 'Nicht gespeichert, die Akte wurde zwischenzeitlich geändert. Fassungen vergleichen.',
},
health: {
heading: 'Systemzustand',
service: 'Dienst',
state: 'Zustand',
checkedAt: (when: string) => `Zustand der Dienste, abgefragt ${when}`,
ok: 'Betriebsbereit',
degraded: 'Eingeschränkt',
down: 'Gestört',
components: {
database: 'Datenbank',
discordGateway: 'Discord-Gateway',
clamav: 'Virenprüfung',
} as Record<string, string>,
},
} as const
export type Messages = typeof de