#!/usr/bin/env python3 """Findet Woerter, die im deutschen TTS vermutlich falsch ausgesprochen werden. Idee: Was ein deutsches Rechtschreibwoerterbuch (hunspell) nicht kennt, ist meist ein englischer Fachbegriff oder ein fremder Eigenname — also genau das, was Chatterbox auf Deutsch falsch liest. Bereits im Aussprache-Woerterbuch stehende Begriffe werden ausgeblendet. Das Ergebnis ist eine VORSCHLAGSLISTE, kein fertiges Woerterbuch: die Umschrift muss ein Mensch schreiben (oder pruefen). Genau dafuer gibt --json ein Geruest aus, das man in pronunciation/de.json einfuegen und ausfuellen kann. python pronunciation_candidates.py transkript.txt python pronunciation_candidates.py *.txt --min-count 2 cat text.md | python pronunciation_candidates.py - --json Braucht: hunspell + deutsches Woerterbuch (Debian/Ubuntu: hunspell hunspell-de-de). """ from __future__ import annotations import argparse import json import re import shutil import subprocess import sys from collections import Counter from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent)) from pronunciation import load_pronunciation_dict # noqa: E402 (leicht, kein torch) # Woerter mit Buchstaben, optional Bindestrich/Apostroph im Inneren. WORD_RE = re.compile(r"[A-Za-zÄÖÜäöüß][A-Za-zÄÖÜäöüß'-]*") # Kurze Grossbuchstabenfolgen (API, GPU) kennt hunspell auch nicht — die sind aber # oft schon als Akronym korrekt buchstabiert. Trotzdem anzeigen, nur markieren. ACRONYM_RE = re.compile(r"^[A-ZÄÖÜ]{2,6}s?$") DICT_CANDIDATES = ["de_DE", "de_DE_frami", "de_AT", "de_CH"] def pick_dictionary() -> str: for d in DICT_CANDIDATES: probe = subprocess.run(["hunspell", "-d", d, "-l"], input="Test\n", capture_output=True, text=True) if probe.returncode == 0: return d sys.exit("FEHLER: kein deutsches hunspell-Woerterbuch gefunden " "(Debian/Ubuntu: sudo apt install hunspell hunspell-de-de).") def unknown_words(words: list[str], dictionary: str) -> set[str]: """hunspell -l gibt genau die Woerter zurueck, die es NICHT kennt.""" out = subprocess.run(["hunspell", "-d", dictionary, "-l"], input="\n".join(words), capture_output=True, text=True) return set(out.stdout.split()) def main() -> int: ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) ap.add_argument("files", nargs="+", help="Textdateien ('-' = stdin)") ap.add_argument("--min-count", type=int, default=1, metavar="N", help="nur Woerter, die mindestens N-mal vorkommen (Default: 1)") ap.add_argument("--lang", default="de", help="Sprache des Woerterbuchs (Default: de)") ap.add_argument("--json", action="store_true", help="Geruest fuer pronunciation/.json ausgeben (Umschrift leer)") args = ap.parse_args() if not shutil.which("hunspell"): return int(bool(sys.stderr.write( "FEHLER: hunspell nicht installiert (sudo apt install hunspell hunspell-de-de).\n"))) text = "\n".join(sys.stdin.read() if f == "-" else Path(f).read_text(encoding="utf-8") for f in args.files) counts = Counter(WORD_RE.findall(text)) known = load_pronunciation_dict(args.lang) # Auch Mehrwort-Eintraege beruecksichtigen ("Donald Trump" deckt "Trump" mit ab). covered = {w for phrase in known for w in WORD_RE.findall(phrase)} dictionary = pick_dictionary() unknown = unknown_words(sorted(counts), dictionary) hits = [(w, n) for w, n in counts.most_common() if w in unknown and w not in covered and n >= args.min_count] if not hits: print("Keine neuen Kandidaten gefunden.") return 0 if args.json: print(json.dumps({w: "" for w, _ in hits}, ensure_ascii=False, indent=2)) print("\n// Umschrift eintragen und nach pronunciation/" f"{args.lang}.json uebernehmen.", file=sys.stderr) return 0 print(f"Woerterbuch: {dictionary} | bereits abgedeckt: {len(known)} Eintraege") print(f"Kandidaten ({len(hits)}), haeufigste zuerst:\n") for w, n in hits: mark = " (Akronym)" if ACRONYM_RE.match(w) else "" print(f" {n:4d}x {w}{mark}") print("\nMit --json ein Geruest zum Ausfuellen erzeugen.") return 0 if __name__ == "__main__": sys.exit(main())