fix(i18n): Umlaute in de.json korrigieren

Die deutsche Oberflaeche schrieb Umlaute als Buchstabenpaare aus
("fuer" statt "für", "Loeschen" statt "Löschen"). Legt
apps/web/src/messages/umlaut-dictionary.ts als einzige Wahrheitsquelle
fuer eine explizite Token-Ersetzungsliste sowie eine Positivliste
bereits korrekter Woerter (Passwort, Quelle, manuell, neue, ...) an
und korrigiert damit 67 falsche Tokens (140 Fundstellen) in de.json.
Ersetzt wird ausschliesslich per Ganzwort-Treffer, nie per
Zeichenregel, damit Woerter wie "Quelle" oder "manuell" unangetastet
bleiben. Der Platzhalterwert "empfaenger@example.com" bleibt
unveraendert, da er eine Beispiel-E-Mail-Adresse ist. Schluessel und
Reihenfolge in de.json sind unveraendert, de.json und en.json tragen
weiterhin denselben Satz von 787 Schluesseln.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
This commit is contained in:
2026-09-07 13:03:42 +02:00
parent bec0530a2b
commit 3d351c6a99
2 changed files with 277 additions and 117 deletions
+160
View File
@@ -0,0 +1,160 @@
/**
* umlaut-dictionary — single source of truth for the ae/oe/ue/ss umlaut
* correction in `de.json` and for the `umlaut-guard.spec.ts` regression
* guard.
*
* Why an explicit token list instead of a character rule (`ue` -> `ü`,
* `oe` -> `ö`, `ae` -> `ä`): a blanket character substitution destroys
* correct German. `Quelle` would become `Qülle`, `manuell` would become
* `manüll`, `neuen` would become `nün`, `Erzeugnisse` would become
* `Erzügnisse`. Replacement therefore only ever targets a whole token
* that is an exact key in `UMLAUT_REPLACEMENTS`.
*
* `UMLAUT_ALLOWLIST` lists every token that legitimately contains
* `ae`/`oe`/`ue`/`ss` and is already correct German — it must never be
* replaced. `empfaenger` sits on this list purely because it is part of
* the example placeholder address `empfaenger@example.com`
* (key `testToPlaceholder`): an umlaut inside a placeholder email address
* would wrongly suggest umlaut addresses are the expected format, so that
* value is skipped entirely (any string containing `@`) rather than
* "corrected".
*/
/** Wrong substitute spelling -> correct German spelling, exact token match only. */
export const UMLAUT_REPLACEMENTS: Record<string, string> = {
loeschen: 'löschen',
Loeschen: 'Löschen',
geloescht: 'gelöscht',
hinzufuegen: 'hinzufügen',
Hinzufuegen: 'Hinzufügen',
hinzuzufuegen: 'hinzuzufügen',
einfuegen: 'einfügen',
fuege: 'füge',
Fuege: 'Füge',
Fuegen: 'Fügen',
fuer: 'für',
pruefen: 'prüfen',
Pruefen: 'Prüfen',
Pruefe: 'Prüfe',
Pruefung: 'Prüfung',
geprueft: 'geprüft',
ueberpruefen: 'überprüfen',
Ueberpruefe: 'Überprüfe',
Zertifikatspruefung: 'Zertifikatsprüfung',
aendern: 'ändern',
geaendert: 'geändert',
Moechten: 'Möchten',
moeglich: 'möglich',
moegliche: 'mögliche',
koennen: 'können',
verfuegbar: 'verfügbar',
Verfuegbar: 'Verfügbar',
Verfuegbare: 'Verfügbare',
Verfuegbarkeit: 'Verfügbarkeit',
Zurueck: 'Zurück',
Zuruecksetzen: 'Zurücksetzen',
zuruecksetzen: 'zurücksetzen',
zurueckgesetzt: 'zurückgesetzt',
rueckgaengig: 'rückgängig',
Zusammenfuehren: 'Zusammenführen',
zusammenfuehren: 'zusammenführen',
bestaetigen: 'bestätigen',
Passwoerter: 'Passwörter',
spaeter: 'später',
ueberein: 'überein',
uebersprungen: 'übersprungen',
ueber: 'über',
ungueltig: 'ungültig',
Verschluesselung: 'Verschlüsselung',
entschluesselt: 'entschlüsselt',
ausgewaehlt: 'ausgewählt',
Ausgewaehlt: 'Ausgewählt',
ausgewaehlte: 'ausgewählte',
Ausgewaehlte: 'Ausgewählte',
waehle: 'wähle',
benoetigt: 'benötigt',
noetig: 'nötig',
muessen: 'müssen',
Domaene: 'Domäne',
Einschraenkung: 'Einschränkung',
Empfaenger: 'Empfänger',
Oberflaeche: 'Oberfläche',
Postfaecher: 'Postfächer',
Praefix: 'Präfix',
Sicherheitsgruenden: 'Sicherheitsgründen',
unterstuetzt: 'unterstützt',
vertrauenswuerdigen: 'vertrauenswürdigen',
zusaetzlich: 'zusätzlich',
zusaetzliche: 'zusätzliche',
Groesse: 'Größe',
Schliessen: 'Schließen',
ausschliessen: 'ausschließen',
};
/**
* Tokens containing `ae`/`oe`/`ue`/`ss` that are already correct German
* and must never be touched by the replacement or flagged by the guard.
*/
export const UMLAUT_ALLOWLIST: readonly string[] = [
'manuell',
'Manuell',
'Quelle',
'Quellen',
'Quell',
'Quellportal',
'Kalenderquelle',
'Kalenderquellen',
'Energiequellen',
'neue',
'neuen',
'Neue',
'Neues',
'aktuelle',
'Aktuell',
'Aktuelle',
'Aktuelles',
'zuerst',
'dauerhaft',
'Schauen',
'Taktfrequenz',
'Feuerlösch',
'Bergbauerzeugnisse',
'true',
'guest',
'Passwort',
'Adresse',
'Absenderadresse',
'muss',
'musste',
'müssen',
'lassen',
'vergessen',
'stattdessen',
'Wasser',
'Abwasser',
'Hessen',
'Erzeugnisse',
'Druckerzeugnisse',
'Gummierzeugnisse',
'Mineralölerzeugnisse',
'Ausschreibung',
'Ausschreibungen',
'Ausschreibungs',
'Ausschreibungsdetails',
'erfasst',
'erfasster',
'Erfassung',
'Ergebnisse',
'gedrosselt',
'ausgeschlossen',
'Ausgeschlossen',
'Ausschlussliste',
'Informationssysteme',
'Zeitmessung',
'Tessera',
'Passen',
'Passt',
'Fasst',
'Verschlüsselung',
'empfaenger',
];