fix(ui): echte Umlaute in allen sichtbaren Texten

Menüs, Dialoge, Schaltflächen, Platzhalter, Hinweise und die
Fehlermeldungen der API standen in Ersatzschreibweise (oe/ae/ue). Sie
erscheinen als Toast oder Beschriftung direkt in der Oberfläche und
gehören damit richtig geschrieben; sechs sichtbare Texte mit ss statt ß
sind gleich mit erledigt ("Schließen", "Straße", "zu groß").

Unangetastet bleibt bewusst:

- Kommentare im Quelltext - die sind durchgängig ASCII, und der Diff wäre
  dreimal so groß ohne Nutzen für den Benutzer
- Protokollzeilen (console.*) - die landen im journal, nicht in der App
- Datenbankwerte und interne Kennungen: 'uebernommen', 'empfaenger',
  'vorschlaege', 'laeuft-bereits' stecken in Enums, SQL und
  Zustandsvergleichen. Ein Umlaut darin hätte still falsche Ergebnisse
  geliefert
- server/src/mail/extract.js - die Muster decken absichtlich beide
  Schreibweisen ab: /gr(ü|ue)(ß|ss)e/
- scanner/utils/crypto.js - der Ersatzschlüssel darf sich nicht ändern,
  sonst sind gespeicherte Portal-Passwörter nicht mehr lesbar

Geprüft mit einem Rundgang durch die Ansichten und Dialoge: sichtbarer
Text plus title/aria-label/placeholder enthalten keine
Ersatzschreibweisen mehr.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sascha Wolf
2026-08-11 09:37:24 +02:00
co-authored by Claude Opus 5
parent ea55acdb5c
commit 8b7ce448a6
42 changed files with 168 additions and 168 deletions
+2 -2
View File
@@ -40,7 +40,7 @@ export const AvatarPicker: React.FC<AvatarPickerProps> = ({
const result = await compressImage(file);
onChange(result.dataUrl);
addToast(
`Foto uebernommen (${formatBytes(result.sourceBytes)} → ${formatBytes(result.resultBytes)}).`,
`Foto übernommen (${formatBytes(result.sourceBytes)} → ${formatBytes(result.resultBytes)}).`,
'success'
);
} catch (err) {
@@ -62,7 +62,7 @@ export const AvatarPicker: React.FC<AvatarPickerProps> = ({
{isBusy ? (
<span className="h-5 w-5 animate-spin rounded-full border-2 border-white/40 border-t-white" />
) : (
'Aendern'
'Ändern'
)}
<input
type="file"
+3 -3
View File
@@ -75,7 +75,7 @@ export const DictateButton: React.FC<DictateButtonProps> = ({
isRecording
? 'Aufnahme beenden und erkennen'
: isBusy
? 'Erkennung laeuft...'
? 'Erkennung läuft...'
: `${label} diktieren`
}
aria-label={isRecording ? 'Aufnahme beenden' : `${label} diktieren`}
@@ -160,7 +160,7 @@ export const DictationHint: React.FC<DictationHintProps> = ({ onBeforeNavigate }
? 'Spracherkennung wird geladen...'
: status === 'error'
? 'Die Spracherkennung konnte nicht geladen werden.'
: 'Diktieren ist moeglich, das Modell fehlt aber noch.'}
: 'Diktieren ist möglich, das Modell fehlt aber noch.'}
</p>
<p className="text-xs text-ink-500">
{isLoading
@@ -169,7 +169,7 @@ export const DictationHint: React.FC<DictationHintProps> = ({ onBeforeNavigate }
: progressLabel || 'Bitte kurz warten.'
: isCached
? 'Das Modell liegt bereits im Browser - es muss nur noch aktiviert werden.'
: 'Das Modell wird einmalig in diesen Browser geladen und laeuft danach lokal auf diesem Geraet.'}
: 'Das Modell wird einmalig in diesen Browser geladen und läuft danach lokal auf diesem Gerät.'}
</p>
{isLoading && (
<div className="mt-2 h-1.5 overflow-hidden rounded-full bg-ink-200">
+2 -2
View File
@@ -49,7 +49,7 @@ export const Modal: React.FC<ModalProps> = ({
<button
onClick={onClose}
className="shrink-0 rounded-lg p-2 text-ink-400 transition hover:bg-ink-100 hover:text-ink-800"
aria-label="Dialog schliessen"
aria-label="Dialog schließen"
>
<svg className="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
@@ -78,7 +78,7 @@ interface ConfirmDialogProps {
export const ConfirmDialog: React.FC<ConfirmDialogProps> = ({
title,
message,
confirmLabel = 'Loeschen',
confirmLabel = 'Löschen',
onConfirm,
onCancel,
}) => (