#!/usr/bin/env python3 """Erkennt und korrigiert fehlende deutsche Umlaute/ß in HTML-Artikeln. Problem: write_file kann bei HTML-Dateien sämtliche Umlaute (äöüÄÖÜ) und ß durch ihre ASCII-Grundformen (aouAOU, ss) ersetzen. Das ist ein Encoding-Bug, der den gesamten Artikel unlesbar macht. Dieses Skript erkennt die häufigsten Muster und ersetzt sie durch korrekte Umlaute. Es arbeitet wortbasiert (nicht zeichenbasiert), um falsche Ersetzungen in nicht-deutschen Kontexten zu vermeiden. AUFRUF: python3 fix_umlauts.py artikel_*.html """ import sys import re # Mapping: (falsches_muster, korrektur) — längste zuerst, um Teilstring- # Kollisionen zu vermeiden. Nur Wörter, die EINDEUTIG falsch sind. REPLACEMENTS = [ # ß-Fälle (ss → ß) ("erschlieszen", "erschließen"), ("groszere", "größere"), ("groszeren", "größeren"), ("groszten", "größten"), ("grosster", "größter"), ("zahlenmaszig", "zahlenmäßig"), ("zahlenmassige", "zahlenmäßige"), ("Strasze", "Straße"), ("Schlieszung", "Schließung"), # ü-Fälle ("uberdurchschnittlich", "überdurchschnittlich"), ("Ubernahmezielen", "Übernahmezielen"), ("Ubernahme", "Übernahme"), ("Uberblick", "Überblick"), ("uberhaupt", "überhaupt"), ("ubernahm", "übernahm"), ("uberarbeitet", "überarbeitet"), ("uberwiegende", "überwiegende"), ("uberall", "überall"), ("Ruckgang", "Rückgang"), ("rucklaufig", "rückläufig"), ("unterstutzt", "unterstützt"), ("fur", "für"), ("Fur", "Für"), ("fuhren", "führen"), ("fuhrt", "führt"), ("grundete", "gründete"), ("Grundete", "Gründete"), ("kundigte", "kündigte"), ("Ankundigung", "Ankündigung"), ("angekundigt", "angekündigt"), ("mussen", "müssen"), ("konnen", "können"), ("konnte", "könnte"), ("durfen", "dürfen"), ("durfte", "dürfte"), ("gunstigeren", "günstigeren"), ("gunstig", "günstig"), ("endgultig", "endgültig"), ("vollstandig", "vollständig"), ("vollstandige", "vollständige"), ("zunachst", "zunächst"), # ö-Fälle ("franzosische", "französische"), ("Franzosischer", "Französischer"), ("franzosischen", "französischen"), ("offentlichen", "öffentlichen"), ("veroffentlicht", "veröffentlicht"), ("Veroffentlichung", "Veröffentlichung"), ("veroffentlichten", "veröffentlichten"), ("europaischen", "europäischen"), ("ermoglicht", "ermöglicht"), ("ermoglichen", "ermöglichen"), ("gehort", "gehört"), ("Gehort", "Gehört"), # ä-Fälle ("Geschaft", "Geschäft"), ("Geschaftsjahr", "Geschäftsjahr"), ("Geschaftsjahres", "Geschäftsjahres"), ("Kerngeschaft", "Kerngeschäft"), ("Verlagsgeschaft", "Verlagsgeschäft"), ("Markten", "Märkten"), ("starker", "stärker"), ("starken", "stärken"), ("Starken", "Stärken"), ("Starke", "Stärke"), ("jahrliches", "jährliches"), ("jahrlich", "jährlich"), ("Wahrend", "Während"), ("wahrend", "während"), ("entfallt", "entfällt"), ("halt", "hält"), ("Halt", "Hält"), ("Marz", "März"), ("Prasentation", "Präsentation"), ("getatigt", "getätigt"), ("abhangig", "abhängig"), ("erganzend", "ergänzend"), ("erganzt", "ergänzt"), ("Enthalt", "Enthält"), ("enthalt", "enthält"), ("Verhaltnis", "Verhältnis"), ("Lizenzverhaltnis", "Lizenzverhältnis"), ("Ursprunglicher", "Ursprünglicher"), ("ursprunglich", "ursprünglich"), ("zuganglichen", "zugänglichen"), ("wachst", "wächst"), ("erklarte", "erklärte"), ("erlauterte", "erläuterte"), # Neue Muster aus KW25-Crowdfunding-Session (21.06.2026) — 17 Wörter # die der write_file-Encoding-Bug verschluckt hatte ("grosse", "große"), ("grosste", "größte"), ("grossten", "größten"), ("muhsame", "mühsame"), ("muhsam", "mühsam"), ("lauft", "läuft"), ("funf", "fünf"), ("bluhenden", "blühenden"), ("bluhender", "blühender"), ("ungewohnlich", "ungewöhnlich"), ("ungewohnlichsten", "ungewöhnlichsten"), ("wochentliche", "wöchentliche"), ("Wochentliche", "Wöchentliche"), ("Uberraschung", "Überraschung"), ("samtliche", "sämtliche"), ("samtlicher", "sämtlicher"), ("Unterstutzende", "Unterstützende"), ("Unterstutzenden", "Unterstützenden"), ("Konig", "König"), ("Konigin", "Königin"), ("verandert", "verändert"), ("verandern", "verändern"), ("verfugbar", "verfügbar"), ("Verfugbarkeit", "Verfügbarkeit"), ("Prozentsatzen", "Prozentsätzen"), ("nachste", "nächste"), ("nachster", "nächster"), ("nachsten", "nächsten"), ("gepruft", "geprüft"), ("fundig", "fündig"), ("lasst", "lässt"), ("Ubersehene", "Übersehene"), ("Ubersicht", "Übersicht"), # Sonderfälle (keine Umlaute aber trotzdem falsch) ("Werwolfe", "Werwölfe"), ("Dusterwald", "Düsterwald"), ("Netto-Erlose", "Netto-Erlöse"), ("TCG-Erlose", "TCG-Erlöse"), ("Pokemon", "Pokémon"), # korrekte Marken-Schreibweise # === NEUE MUSTER (29.06.2026) — BGA-Update-Artikel, 30+ Fehler === # ae → ä ("Machtkaempfe", "Machtkämpfe"), ("bekaempfen", "bekämpfen"), ("daemmeriger", "dämmeriger"), ("Haeuschen", "Häuschen"), ("Maerchen", "Märchen"), ("Atmosphaere", "Atmosphäre"), ("Komplexitaet", "Komplexität"), # oe → ö ("Voegel", "Vögel"), ("ploetzlich", "plötzlich"), ("Ausloeschung", "Auslöschung"), ("boeser", "böser"), # ue → ü ("duesteren", "düsteren"), ("ausserirdische", "außerirdische"), ("schliesslich", "schließlich"), ("Grossmutters", "Großmutters"), # ss → ß (muss vor den ae/oe/ue-Ersetzungen stehen!) # Allgemeine ae/oe/ue-Muster für Wörter die EINDEUTIG falsch sind ("umschwaermen", "umschwärmen"), ("Vogelhaeusern", "Vogelhäusern"), ("Wuerfeln", "Würfeln"), ("Wuerfel", "Würfel"), ] # SPURIOUS_UMLAUTS: Der Encoding-Bug kann Umlaute NICHT NUR entfernen, # sondern auch FALSCH EINFÜGEN — aus "halten" wird "hälten", aus # "Inhalte" wird "Inhälte". Diese Reverse-Map korrigiert falsch # eingefügte Umlaute zurück zu den korrekten ASCII-Formen. # (18.06.2026: 4 Wörter im Conspiracy-Artikel betroffen, vom Skript # nicht erkannt weil es nur MISSING-Umlaut-Muster prüft.) SPURIOUS_UMLAUTS = [ # halt-Stamm: a wird fälschlich zu ä ("Inhälte", "Inhalte"), ("Inhält", "Inhalt"), ("anhälten", "anhalten"), ("anhälte", "anhalte"), ("anhäl tend", "anhaltend"), ("anhäl tende", "anhaltende"), ("erhälten", "erhalten"), # "erhält" IST KORREKT (3.Pers.Sg) — aus SPURIOUS entfernt ("hälten", "halten"), # "hält" IST KORREKT (3.Pers.Sg) — aus SPURIOUS entfernt ("enthälten", "enthalten"), # "enthält" IST KORREKT (3.Pers.Sg von "enthalten") — NICHT zurücksetzen! ("vorenthälten", "vorenthalten"), ("behälten", "behalten"), ("vorbehälten", "vorbehalten"), ("Hälten", "Halten"), ("Hält", "Halt"), # Weitere Muster (wachsen wenn neue Fälle auftauchen) ] # Wörter, die fälschlich durch "würde" ersetzt wurden, aber "wurde" (Passiv) sein müssen WURDE_PASSIV_PATTERNS = [ r'würde\s+(von\s+)', # "würde von Peter Sarrett entworfen" r'würde\s+(im\s+\w+\s+\w+)', # "würde im Lauf der Jahre adaptiert" r'würde\s+(im\s+Rahmen)', # "würde im Rahmen der Präsentation gefragt" ] def fix_umlauts(text): """Ersetzt alle bekannten Umlaut-freien Muster durch korrekte Formen.""" for wrong, correct in REPLACEMENTS: text = text.replace(wrong, correct) return text def fix_wurde_passiv(text): """Korrigiert falsches 'würde' (Konjunktiv) zu 'wurde' (Passiv-Präteritum).""" for pattern in WURDE_PASSIV_PATTERNS: text = re.sub(pattern, r'wurde \1', text) return text def fix_spurious_umlauts(text): """Korrigiert FALSCH EINGEFÜGTE Umlaute zurück zu ASCII. Der Encoding-Bug kann Umlaute nicht nur entfernen, sondern auch fälschlich einfügen — z.B. 'halten' → 'hälten', 'Inhalte' → 'Inhälte'. Diese Funktion kehrt solche Fehler um. (18.06.2026: 4 Wörter im Conspiracy-Artikel betroffen.) """ for wrong, correct in SPURIOUS_UMLAUTS: text = text.replace(wrong, correct) return text def scan_remaining(text): """Sucht nach verbliebenen Umlaut-freien deutschen Wörtern.""" # Whitelist: korrekte deutsche Wörter, die Substrings enthalten, # die fälschlich als Umlaut-fehlerhaft erkannt werden könnten WHITELIST = { # "druck"-Substring (kein Umlaut-Fehler, sondern Wortstamm) 'beeindruckend', 'beeindruckende', 'beeindruckender', 'beeindruckt', 'eindrucksvoll', 'eindrucksvolle', 'eindrucksvoller', 'eindrucksvollsten', 'Druck', 'Druckauflage', 'Kunstdrucke', 'Kunstdruck', 'Ausdruck', 'Eindruck', 'Nachdruck', 'Vordruck', 'Aufdruck', 'Abdruck', 'drucken', 'druckt', 'gedruckt', 'Zolldruck', 'Kaufdruck', # "stark"-Substring (kein Umlaut-Fehler, sondern Wortstamm) 'starkes', 'starker', 'starkem', 'starken', 'verstarken', 'Verstarkung', # "Markt"-Substring (nicht immer Umlaut-Fehler) 'Markt', 'Marktes', 'Markte', 'Markten', 'Marktanteil', 'Marktanteile', 'Marktfuhrer', 'Marktplatz', 'Marktwert', 'Marktanalyse', 'Marktdynamik', 'Marktpreise', 'Marktforschungsinstitut', 'siebbedrucktem', 'siebbedruckt', 'Supermarkt', 'Jahrmarkt', 'Flohmarkt', # Weitere korrekte Wörter mit verdächtigen Substrings 'halt', 'Halt', # Modalpartikel ("das ist halt so") — NICHT blind ersetzen! 'Gestalt', 'Aufenthalt', 'Haushalt', 'Haushalte', 'Anhalt', 'Anhalte', 'Vorbehalt', 'Ruckhalt', 'Inhalt', 'Inhalte', 'Gehalt', 'Gehalter', 'erhalt', 'erhalte', # Imperativ/1.Pers ("ich erhalte") 'behalt', 'behalte', 'enthalten', 'enthalte', 'vorenthalten', 'vorenthalte', 'aufhalten', 'aufhalte', 'festhalten', 'festhalte', 'unterhalten', 'unterhalte', 'abhalten', 'abhalte', 'hinhalten', 'hinhalte', 'durchhalten', 'durchhalte', 'aushalten', 'aushalte', 'mithalten', 'mithalte', 'zuruckhalten', 'zuruckhalte', 'bereithalten', 'bereithalte', 'gegenhalten', 'gegenhalte', 'innehalten', 'innehalte', 'standhalten', 'standhalte', 'zusammenhalten', 'zusammenhalte', 'fernhalten', 'fernhalte', 'warmhalten', 'warmhalte', 'kleinhalten', 'kleinhalte', 'offenhalten', 'offenhalte', 'freihalten', 'freihalte', 'hochhalten', 'hochhalte', 'stillhalten', 'stillhalte', 'kurzhalten', 'kurzhalte', 'reinhalten', 'reinhalte', 'raushalten', 'raushalte', 'heraushalten', 'heraushalte', 'abhalten', 'abhalte', 'anhalten', 'anhalte', 'aufrechterhalten', 'aufrechterhalte', 'bereithalten', 'bereithalte', 'dagegenhalten', 'dagegenhalte', 'danebenhalten', 'danebenhalte', 'davonhalten', 'davonhalte', 'dazuhalten', 'dazuhalte', 'draufhalten', 'draufhalte', 'dranhalten', 'dranhalte', 'drinhalten', 'drinhalte', 'drunterhalten', 'drunterhalte', 'emporhalten', 'emporhalte', 'entgegenhalten', 'entgegenhalte', 'feilhalten', 'feilhalte', 'fernhalten', 'fernhalte', 'festhalten', 'festhalte', 'flachhalten', 'flachhalte', 'freihalten', 'freihalte', 'frischhalten', 'frischhalte', 'gefangenhalten', 'gefangenhalte', 'geheimhalten', 'geheimhalte', 'geringhalten', 'geringhalte', 'gesundhalten', 'gesundhalte', 'glatthalten', 'glatthalte', 'heilighalten', 'heilighalte', 'heilig halten', 'heilig halte', 'heraushalten', 'heraushalte', 'hereinhalten', 'hereinhalte', 'herhalten', 'herhalte', 'hinhalten', 'hinhalte', 'hinterherhalten', 'hinterherhalte', 'hochhalten', 'hochhalte', 'instandhalten', 'instandhalte', 'kleinhalten', 'kleinhalte', 'kurzhalten', 'kurzhalte', 'langhalten', 'langhalte', 'lockerhalten', 'lockerhalte', 'loshalten', 'loshalte', 'maulhalten', 'maulhalte', 'mithalten', 'mithalte', 'nachhalten', 'nachhalte', 'nebeneinanderhalten', 'nebeneinanderhalte', 'niederhalten', 'niederhalte', 'offenhalten', 'offenhalte', 'parathalten', 'parathalte', 'probehalten', 'probehalte', 'raushalten', 'raushalte', 'rechthaben', 'rechthabe', 'reinhalten', 'reinhalte', 'ruhighalten', 'ruhighalte', 'sauberhalten', 'sauberhalte', 'saubere', 'sauberer', 'sauberes', 'sauberem', 'sauberen', 'stark', 'starke', # korrektes deutsches Adjektiv, kein Umlaut-Fehler 'lautstarke', 'lautstarker', 'lautstarkes', 'lautstarkem', 'lautstarken', # korrektes deutsches Adjektiv 'schadloshalten', 'schadloshalte', 'schiefhalten', 'schiefhalte', 'schmallhalten', 'schmallhalte', 'schritthalten', 'schritthalte', 'standhalten', 'standhalte', 'steifhalten', 'steifhalte', 'stillhalten', 'stillhalte', 'tiefhalten', 'tiefhalte', 'Trucker', 'trucker', 'verzaubert', 'Verzaubert', 'Verflixt', 'verflixt', 'Zauber', 'zauber', 'Winterzauber', 'winterzauber', 'Zauberer', 'zauberer', 'Zauberei', 'zauberei', 'Zaubertrank', 'zaubertrank', 'Zauberstab', 'zauberstab', 'trockenhalten', 'trockenhalte', 'ubrigbehalten', 'ubrigbehalte', 'umhalten', 'umhalte', 'unterhalten', 'unterhalte', 'verhalten', 'verhalte', 'vorhalten', 'vorhalte', 'wachhalten', 'wachhalte', 'warmhalten', 'warmhalte', 'weghalten', 'weghalte', 'weiterhalten', 'weiterhalte', 'werthalten', 'werthalte', 'wiederhalten', 'wiederhalte', 'zuruckhalten', 'zuruckhalte', 'zusammenhalten', 'zusammenhalte', 'zuhalten', 'zuhalte', # Korrekte deutsche Wörter mit "uber"-Substring (kein Umlaut-Fehler) 'Neuberechnung', # Korrekte deutsche Wörter, die "offentlich" als Substring enthalten 'hoffentlich', } suspicious = [] for word in re.findall(r'\b[A-Za-zäöüÄÖÜß]+\b', text): # Überspringe Wörter die bereits Umlaute enthalten if any(c in word for c in 'äöüÄÖÜß'): continue # Überspringe Whitelist-Wörter if word in WHITELIST: continue # Überspringe HTML-Entities und englische Standardwörter if word.lower() in {'the', 'and', 'for', 'with', 'from', 'this', 'that', 'have', 'been', 'will', 'would', 'could', 'should', 'their', 'there', 'these', 'those', 'about', 'which'}: continue # Verdächtige Muster for pattern in ['uber', 'fur', 'Geschaft', 'ruck', 'offentlich', 'veroffentlicht', 'franzosisch', 'grundete', 'kundigte', 'stark', 'Markt', 'Prasentation', 'getatigt', 'abhangig', 'erganz', 'Enthalt', 'Verhaltnis', 'Ursprung']: if pattern in word: suspicious.append(word) break return suspicious def main(): if len(sys.argv) < 2: print("Aufruf: python3 fix_umlauts.py [datei2 ...]") sys.exit(1) for filename in sys.argv[1:]: with open(filename, 'r') as f: text = f.read() original = text text = fix_umlauts(text) text = fix_wurde_passiv(text) text = fix_spurious_umlauts(text) remaining = scan_remaining(text) if remaining: print(f"⚠ {filename}: {len(remaining)} verdächtige Wörter verblieben:") for w in remaining[:10]: print(f" - {w}") else: print(f"✅ {filename}: sauber") if text != original: with open(filename, 'w') as f: f.write(text) print(f" → Datei überschrieben ({len(original)} → {len(text)} Zeichen)") else: print(f" → keine Änderungen nötig") if __name__ == '__main__': main()