/** * sprechtext.ts — Normalisierung vor der Sprachsynthese. * * Befund 2026-09-06 mit Fish S2 Pro: Betraege ("1.248,50 Euro") und Daten * ("14. August 2026") liest das Modell richtig, eine Postleitzahl dagegen * nicht -- "83646" wird als Zahl gelesen und die Hausnummer davor verschmilzt * damit. Andreas' Entscheidung: Ziffern bleiben Ziffern (uebersetzbar), aber * mit Leerzeichen getrennt, und davor eine Ansage mit Pause: * "Bergstraße 7, 83646 Bad Tölz" -> "Bergstraße 7; Postleitzahl 8 3 6 4 6, Bad Tölz" * Das war Variante 4 von vier gehoerten; die stummen Trenner (Punkt, * Gedankenstrich, Auslassung) trennten schlechter. * * Bewusst eng: nur fuenf Ziffern, denen ein grossgeschriebener Ortsname folgt. * Betraege (Komma/Punkt im Inneren, "Euro" dahinter), Daten und Jahreszahlen * bleiben unberuehrt -- die kann das Modell. */ // Ortsname: grossgeschriebenes Wort, das keine Einheit ist ("83646 Euro" ist ein Betrag) const KEIN_ORT = "Euro|EUR|Uhr|Prozent|Stück|Stueck|Meter|Kilometer|Gramm|Kilo|Liter|Personen|Tage|Jahre"; const ORT = `(?!(?:${KEIN_ORT})\\b)[A-ZÄÖÜ][\\wäöüß.-]+`; /** Postleitzahl vor Ortsname: Ziffern einzeln, mit Ansage und Pause davor. */ export function postleitzahlenAussprechen(text: string): string { // vor der PLZ: Textanfang, oder ein Zeichen (kein Punkt/Bindestrich: "2026-83646" ist eine Nummer) // plus Komma/Leerzeichen als Trenner -- so bleibt die Hausnummer "7" ein eigenes Wort. return text.replace( new RegExp(`(^|[^\\s.,-](?:,\\s*|\\s+))(\\d{5})(?=\\s+${ORT})`, "gu"), (_, vor: string, plz: string) => { const ziffern = plz.split("").join(" "); const kern = vor.replace(/[,\s]+$/u, ""); if (kern === "") return `Postleitzahl ${ziffern},`; // nach Doppelpunkt reicht die Ansage, sonst Semikolon als Pause zur Hausnummer return kern.endsWith(":") ? `${kern} Postleitzahl ${ziffern},` : `${kern}; Postleitzahl ${ziffern},`; }, ); } /** Alle Regeln fuer Sprechtext; heute nur die Postleitzahl. */ export function sprechtextNormalisieren(text: string): string { return postleitzahlenAussprechen(text); }