diff --git a/app/pipeline/tts_normalizer.py b/app/pipeline/tts_normalizer.py index d406377..124b361 100644 --- a/app/pipeline/tts_normalizer.py +++ b/app/pipeline/tts_normalizer.py @@ -112,12 +112,15 @@ def _apply_units(text: str, units: dict) -> str: return text -def _apply_dict(text: str, mapping: dict) -> str: +def _apply_dict(text: str, mapping: dict, ignore_case: bool = False) -> str: # Längste Schlüssel zuerst, wortgrenzen-bewusst (Abkürzungen enden oft auf "."). + flags = re.IGNORECASE if ignore_case else 0 for key in sorted(mapping, key=len, reverse=True): repl = mapping[key] if key.isalnum(): # reines Wort/Akronym -> mit Wortgrenzen - text = re.sub(rf"\b{re.escape(key)}\b", repl, text) + text = re.sub(rf"\b{re.escape(key)}\b", repl, text, flags=flags) + elif ignore_case: + text = re.sub(re.escape(key), repl, text, flags=re.IGNORECASE) else: # enthält Punkte/Sonderzeichen -> direkte Ersetzung text = text.replace(key, repl) return text @@ -143,12 +146,12 @@ class TTSNormalizer: t = _apply_ordinals(t) t = _apply_units(t, {**_DEFAULT_UNITS_DE, **units}) t = _apply_dict(t, {**_DEFAULT_ABBREVS_DE, **abbrevs}) - t = _apply_dict(t, terms) + t = _apply_dict(t, terms, ignore_case=True) t = _apply_dict(t, _DEFAULT_SYMBOLS_DE) else: t = _apply_units(t, units) t = _apply_dict(t, abbrevs) - t = _apply_dict(t, terms) + t = _apply_dict(t, terms, ignore_case=True) t = _apply_dict(t, _DEFAULT_SYMBOLS_EN) # Slash zwischen Wörtern/Zahlen sprachfreundlich, Striche entschärfen. diff --git a/config/pronunciation.de.yaml b/config/pronunciation.de.yaml index c9ac2db..265b65c 100644 --- a/config/pronunciation.de.yaml +++ b/config/pronunciation.de.yaml @@ -18,6 +18,17 @@ abbreviations: units: # "kWh": "Kilowattstunden" +# Aussprache-Korrekturen: espeak-ng (in Piper) verkürzt bei einigen Wortformen +# fälschlich einen langen Vokal. Umschreibung mit "h" -> langer Vokal. +# Alle Einträge mit `espeak-ng -v de -x ""` verifiziert (Y:/E:/o: = lang). +# Matching ist case-insensitiv (deckt auch Satzanfänge ab). terms: - # "Linux": "Linuks" - # "CPU": "C P U" + "strömt": "ströhmt" + "strömte": "ströhmte" + "löst": "löhst" + "tönt": "töhnt" + "bläst": "blähst" + "büßt": "bühßt" + "grüßt": "grühßt" + "Mond": "Mohnd" + # "Linux": "Linuks" # Beispiel für Eigennamen/IT-Begriffe diff --git a/tests/test_tts_normalizer.py b/tests/test_tts_normalizer.py index 12db111..cd6bf5a 100644 --- a/tests/test_tts_normalizer.py +++ b/tests/test_tts_normalizer.py @@ -83,6 +83,16 @@ def test_adapter_numbered_list_to_ordinal(): assert "erstens, Milch" in out and "zweitens, Eier" in out +def test_terms_dict_is_case_insensitive(): + from app.pipeline.tts_normalizer import _apply_dict + mapping = {"strömt": "ströhmt"} + assert _apply_dict("Der Bach strömt.", mapping, ignore_case=True) == "Der Bach ströhmt." + # Satzanfang (Großschreibung) wird ebenfalls erfasst. + assert _apply_dict("Strömt es?", mapping, ignore_case=True) == "ströhmt es?" + # Teilwort wird NICHT getroffen (Wortgrenze). + assert _apply_dict("Strömung", mapping, ignore_case=True) == "Strömung" + + def test_german_numbers_table(): assert gn.ordinal_attributive(1) == "erster" assert gn.ordinal_adverbial(3) == "drittens"