From 344645cb77d4b4282799ab1f80e2cd3d767cad8a Mon Sep 17 00:00:00 2001 From: DeeCee03 Date: Tue, 1 Sep 2026 19:45:08 +0200 Subject: [PATCH] Umlaute: der Export prueft sie, statt sich aufs Erinnern zu verlassen Auf einer Slide stand "Sanitaer" und "Klima / Lueftung". Beides kam woertlich aus dem Klickdummy, wo es schludrig eingetippt ist, und beides las sich im fertigen Beitrag wie ein Fehler -- weil es einer ist. Gefunden hat es niemand beim Bauen, sondern jemand, der auf den fertigen Post geschaut hat. Ein reines Muster genuegt als Pruefung nicht: Deutsch schreibt "neue", "Dauer" und "Michael" voellig zu Recht mit ae/oe/ue. Nachgezaehlt ueber den gezeichneten Text aller sieben Projekte sind es aber nur acht solche Woerter, alle davon richtig oder gewollt (die Kanal-Handles). Also Muster plus kurze Ausnahmeliste -- null Fehlalarme, "Sanitaer" faellt trotzdem auf. Bei ss ist es umgekehrt: 15 Treffer im gezeichneten Text, alle richtig (dass, Schluss, Netzanschluss). Deshalb dort eine Liste der wenigen Woerter, die ein scharfes s brauchen, statt eines Musters. Geprueft wird an zwei Stellen, weil es zwei Wege zum fertigen Bild gibt: renderSlide() bricht ab, bevor ein PNG entsteht animation.mjs an 24 Stellen ueber den Ablauf Der zweite Punkt war ein eigener Fehler: der erste Anlauf prueft nur den Anfangszustand und fand nichts, weil die falschen Woerter dort erst ab t = 0,4 erscheinen. Der Text einer Animation aendert sich mit der Zeit, die Pruefung muss es auch. Beide Tore sind mit absichtlich wieder eingebautem "Sanitaer" scheitern gesehen worden. Eine Pruefung, die man nie hat scheitern sehen, ist keine -- der erste Test lief durch, weil mein Testskript htmlToImage direkt aufruft und renderSlide dabei umgeht. Regel und Begruendung stehen in rules.md 4, die Falle in 8. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01U7RZ6ZpLD8Dp84Rtt4KHfv --- animation.mjs | 19 +++++++++++++++++++ post1.html | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ post2-alt.html | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ post2.html | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ post3.html | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ post4.html | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ post5.html | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ post6.html | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ rules.md | 30 +++++++++++++++++++++++++++++ 9 files changed, 406 insertions(+) diff --git a/animation.mjs b/animation.mjs index b311b16..7b30790 100644 --- a/animation.mjs +++ b/animation.mjs @@ -161,6 +161,25 @@ try { if (kasten.x !== 0 || kasten.y !== 0) throw new Error(`${a.key} sitzt bei ${kasten.x},${kasten.y} statt 0,0`); + /* Dieselbe Umlautpruefung wie der Bildexport. Das Bewegtbild laeuft nicht + durch renderSlide und waere sonst die Luecke: auf einer Slide faellt + "Sanitaer" beim Export auf, in 180 Bildern niemandem. + + Ueber den ganzen Ablauf und nicht nur einmal: der Text einer Animation + aendert sich mit t. Beim ersten Versuch lief die Pruefung nur auf dem + Anfangsbild und fand nichts -- die fehlerhaften Optionen erscheinen + erst ab t = 0,4. Vierundzwanzig Stellen decken jeden Zustand ab und + kosten zusammen weniger als eine Sekunde. */ + const gesehen = new Set(); + for (let p = 0; p < 24; p++) { + await cdp.werte(`window.animSetzen(${a.i}, ${p / 24})`); + const f = await cdp.werte("window.TEXT_PRUEFUNG ? window.TEXT_PRUEFUNG() : []"); + (f || []).forEach(w => gesehen.add(w)); + } + if (gesehen.size) + throw new Error(`${a.key}: Umlaut fehlt bei ${[...gesehen].join(", ")}. ` + + "Wenn das Wort so richtig ist, gehoert es in UMLAUT_ERLAUBT."); + const ordner = await mkdtemp(path.join(tmpdir(), "postgrid-bilder-")); process.stdout.write(` ${a.key}: ${anzahl} Bilder `); for (let n = 0; n < anzahl; n++) { diff --git a/post1.html b/post1.html index b3fe51a..0dedc50 100644 --- a/post1.html +++ b/post1.html @@ -2263,6 +2263,52 @@ if (location.search.indexOf("nur=streifen") >= 0) return n < 20 ? 1 : dunkel / n; } + + /* ─── Umlaute im gezeichneten Text ─────────────────────────────────────── + Auf einer Slide stand einmal "Sanitaer" und "Klima / Lueftung". Beides + kam woertlich aus der Quelle -- im Klickdummy war es dort schludrig + eingetippt --, und beides las sich im fertigen Beitrag wie ein Fehler, + weil es einer ist. Gefunden hat es niemand beim Bauen, sondern erst + jemand, der auf den fertigen Post geschaut hat. + + Deshalb prueft es jetzt der Export. Ein reines Muster genuegt dafuer + nicht: Deutsch schreibt "neue", "Dauer", "Michael" voellig zu Recht mit + ae/oe/ue. Nachgezaehlt ueber den gezeichneten Text aller Projekte sind es + aber nur eine Handvoll solcher Woerter -- eine Ausnahmeliste ist also + kurz und trifft trotzdem alles andere. + + ss ist der umgekehrte Fall: fast immer richtig (dass, Schluss, Adresse, + Netzanschluss), nur ein paar Woerter brauchen ein scharfes s. Die stehen + deshalb einzeln als Liste und nicht als Muster. + + Kanalnamen sind Absicht: "# waermepumpe-mueller" ist ein Handle, und die + schreiben Chatwerkzeuge klein und ohne Umlaute. */ + const UMLAUT_ERLAUBT = new Set([ + "michael", "dauer", "neue", "steuer", "feuer", "aktuell", "museum", + "waermepumpe-mueller", "waermepumpe-abnahme", + ]); + const BRAUCHT_SCHARFES_S = new Set([ + "aufmass", "strasse", "groesse", "groesser", "fuss", "fussboden", + "mass", "masse", "weiss", "heisst", "aussen", "gross", "grosse", "spass", + ]); + + function umlautFehler(wurzel) { + const raus = []; + wurzel.querySelectorAll("text, .headline, .body, .badge").forEach(e => { + String(e.textContent || "").split(/[^A-Za-zÄÖÜäöüß0-9./#@-]+/).forEach(w => { + if (!w) return; + const k = w.toLowerCase().replace(/^[@#]+/, "").replace(/[.,;:!?)(]+$/, ""); + if (!k || UMLAUT_ERLAUBT.has(k) || raus.includes(w)) return; + if (/ae|oe|ue/.test(k) || BRAUCHT_SCHARFES_S.has(k)) raus.push(w); + }); + }); + return raus; + } + + /* Fuer animation.mjs: das Bewegtbild laeuft nicht durch renderSlide und + braucht deshalb eine eigene Tuer zu derselben Pruefung. */ + window.TEXT_PRUEFUNG = () => umlautFehler(document); + async function renderSlide(i) { // Hilfslinien sind Werkzeug: koennten sie aus einem Tastendruck noch // stehen, landeten sie im Bild. @@ -2279,6 +2325,11 @@ if (location.search.indexOf("nur=streifen") >= 0) el.dataset.editmode = "aus"; try { await document.fonts.ready; + /* Umlaute pruefen, bevor gerendert wird. Lieber ein Abbruch mit + klarer Meldung als ein fertiges PNG mit "Sanitaer" darauf. */ + const uf = umlautFehler(el); + if (uf.length) throw new Error("Slide " + (i + 1) + ": Umlaut fehlt bei " + + uf.join(", ") + ". Wenn das Wort so richtig ist, gehoert es in UMLAUT_ERLAUBT."); const imgs = [...el.querySelectorAll("img")]; await Promise.all(imgs.map(im => im.complete ? im.decode().catch(() => {}) diff --git a/post2-alt.html b/post2-alt.html index e29432b..a7d028d 100644 --- a/post2-alt.html +++ b/post2-alt.html @@ -4216,6 +4216,52 @@ if (location.search.indexOf("nur=anim") >= 0) return n < 20 ? 1 : dunkel / n; } + + /* ─── Umlaute im gezeichneten Text ─────────────────────────────────────── + Auf einer Slide stand einmal "Sanitaer" und "Klima / Lueftung". Beides + kam woertlich aus der Quelle -- im Klickdummy war es dort schludrig + eingetippt --, und beides las sich im fertigen Beitrag wie ein Fehler, + weil es einer ist. Gefunden hat es niemand beim Bauen, sondern erst + jemand, der auf den fertigen Post geschaut hat. + + Deshalb prueft es jetzt der Export. Ein reines Muster genuegt dafuer + nicht: Deutsch schreibt "neue", "Dauer", "Michael" voellig zu Recht mit + ae/oe/ue. Nachgezaehlt ueber den gezeichneten Text aller Projekte sind es + aber nur eine Handvoll solcher Woerter -- eine Ausnahmeliste ist also + kurz und trifft trotzdem alles andere. + + ss ist der umgekehrte Fall: fast immer richtig (dass, Schluss, Adresse, + Netzanschluss), nur ein paar Woerter brauchen ein scharfes s. Die stehen + deshalb einzeln als Liste und nicht als Muster. + + Kanalnamen sind Absicht: "# waermepumpe-mueller" ist ein Handle, und die + schreiben Chatwerkzeuge klein und ohne Umlaute. */ + const UMLAUT_ERLAUBT = new Set([ + "michael", "dauer", "neue", "steuer", "feuer", "aktuell", "museum", + "waermepumpe-mueller", "waermepumpe-abnahme", + ]); + const BRAUCHT_SCHARFES_S = new Set([ + "aufmass", "strasse", "groesse", "groesser", "fuss", "fussboden", + "mass", "masse", "weiss", "heisst", "aussen", "gross", "grosse", "spass", + ]); + + function umlautFehler(wurzel) { + const raus = []; + wurzel.querySelectorAll("text, .headline, .body, .badge").forEach(e => { + String(e.textContent || "").split(/[^A-Za-zÄÖÜäöüß0-9./#@-]+/).forEach(w => { + if (!w) return; + const k = w.toLowerCase().replace(/^[@#]+/, "").replace(/[.,;:!?)(]+$/, ""); + if (!k || UMLAUT_ERLAUBT.has(k) || raus.includes(w)) return; + if (/ae|oe|ue/.test(k) || BRAUCHT_SCHARFES_S.has(k)) raus.push(w); + }); + }); + return raus; + } + + /* Fuer animation.mjs: das Bewegtbild laeuft nicht durch renderSlide und + braucht deshalb eine eigene Tuer zu derselben Pruefung. */ + window.TEXT_PRUEFUNG = () => umlautFehler(document); + async function renderSlide(i) { // Hilfslinien sind Werkzeug: koennten sie aus einem Tastendruck noch // stehen, landeten sie im Bild. @@ -4232,6 +4278,11 @@ if (location.search.indexOf("nur=anim") >= 0) el.dataset.editmode = "aus"; try { await document.fonts.ready; + /* Umlaute pruefen, bevor gerendert wird. Lieber ein Abbruch mit + klarer Meldung als ein fertiges PNG mit "Sanitaer" darauf. */ + const uf = umlautFehler(el); + if (uf.length) throw new Error("Slide " + (i + 1) + ": Umlaut fehlt bei " + + uf.join(", ") + ". Wenn das Wort so richtig ist, gehoert es in UMLAUT_ERLAUBT."); const imgs = [...el.querySelectorAll("img")]; await Promise.all(imgs.map(im => im.complete ? im.decode().catch(() => {}) diff --git a/post2.html b/post2.html index 90d1f5d..6291e18 100644 --- a/post2.html +++ b/post2.html @@ -3837,6 +3837,52 @@ if (location.search.indexOf("nur=anim") >= 0) return n < 20 ? 1 : dunkel / n; } + + /* ─── Umlaute im gezeichneten Text ─────────────────────────────────────── + Auf einer Slide stand einmal "Sanitaer" und "Klima / Lueftung". Beides + kam woertlich aus der Quelle -- im Klickdummy war es dort schludrig + eingetippt --, und beides las sich im fertigen Beitrag wie ein Fehler, + weil es einer ist. Gefunden hat es niemand beim Bauen, sondern erst + jemand, der auf den fertigen Post geschaut hat. + + Deshalb prueft es jetzt der Export. Ein reines Muster genuegt dafuer + nicht: Deutsch schreibt "neue", "Dauer", "Michael" voellig zu Recht mit + ae/oe/ue. Nachgezaehlt ueber den gezeichneten Text aller Projekte sind es + aber nur eine Handvoll solcher Woerter -- eine Ausnahmeliste ist also + kurz und trifft trotzdem alles andere. + + ss ist der umgekehrte Fall: fast immer richtig (dass, Schluss, Adresse, + Netzanschluss), nur ein paar Woerter brauchen ein scharfes s. Die stehen + deshalb einzeln als Liste und nicht als Muster. + + Kanalnamen sind Absicht: "# waermepumpe-mueller" ist ein Handle, und die + schreiben Chatwerkzeuge klein und ohne Umlaute. */ + const UMLAUT_ERLAUBT = new Set([ + "michael", "dauer", "neue", "steuer", "feuer", "aktuell", "museum", + "waermepumpe-mueller", "waermepumpe-abnahme", + ]); + const BRAUCHT_SCHARFES_S = new Set([ + "aufmass", "strasse", "groesse", "groesser", "fuss", "fussboden", + "mass", "masse", "weiss", "heisst", "aussen", "gross", "grosse", "spass", + ]); + + function umlautFehler(wurzel) { + const raus = []; + wurzel.querySelectorAll("text, .headline, .body, .badge").forEach(e => { + String(e.textContent || "").split(/[^A-Za-zÄÖÜäöüß0-9./#@-]+/).forEach(w => { + if (!w) return; + const k = w.toLowerCase().replace(/^[@#]+/, "").replace(/[.,;:!?)(]+$/, ""); + if (!k || UMLAUT_ERLAUBT.has(k) || raus.includes(w)) return; + if (/ae|oe|ue/.test(k) || BRAUCHT_SCHARFES_S.has(k)) raus.push(w); + }); + }); + return raus; + } + + /* Fuer animation.mjs: das Bewegtbild laeuft nicht durch renderSlide und + braucht deshalb eine eigene Tuer zu derselben Pruefung. */ + window.TEXT_PRUEFUNG = () => umlautFehler(document); + async function renderSlide(i) { // Hilfslinien sind Werkzeug: koennten sie aus einem Tastendruck noch // stehen, landeten sie im Bild. @@ -3853,6 +3899,11 @@ if (location.search.indexOf("nur=anim") >= 0) el.dataset.editmode = "aus"; try { await document.fonts.ready; + /* Umlaute pruefen, bevor gerendert wird. Lieber ein Abbruch mit + klarer Meldung als ein fertiges PNG mit "Sanitaer" darauf. */ + const uf = umlautFehler(el); + if (uf.length) throw new Error("Slide " + (i + 1) + ": Umlaut fehlt bei " + + uf.join(", ") + ". Wenn das Wort so richtig ist, gehoert es in UMLAUT_ERLAUBT."); const imgs = [...el.querySelectorAll("img")]; await Promise.all(imgs.map(im => im.complete ? im.decode().catch(() => {}) diff --git a/post3.html b/post3.html index fd13949..d845fd7 100644 --- a/post3.html +++ b/post3.html @@ -3562,6 +3562,52 @@ if (location.search.indexOf("nur=anim") >= 0) return n < 20 ? 1 : dunkel / n; } + + /* ─── Umlaute im gezeichneten Text ─────────────────────────────────────── + Auf einer Slide stand einmal "Sanitaer" und "Klima / Lueftung". Beides + kam woertlich aus der Quelle -- im Klickdummy war es dort schludrig + eingetippt --, und beides las sich im fertigen Beitrag wie ein Fehler, + weil es einer ist. Gefunden hat es niemand beim Bauen, sondern erst + jemand, der auf den fertigen Post geschaut hat. + + Deshalb prueft es jetzt der Export. Ein reines Muster genuegt dafuer + nicht: Deutsch schreibt "neue", "Dauer", "Michael" voellig zu Recht mit + ae/oe/ue. Nachgezaehlt ueber den gezeichneten Text aller Projekte sind es + aber nur eine Handvoll solcher Woerter -- eine Ausnahmeliste ist also + kurz und trifft trotzdem alles andere. + + ss ist der umgekehrte Fall: fast immer richtig (dass, Schluss, Adresse, + Netzanschluss), nur ein paar Woerter brauchen ein scharfes s. Die stehen + deshalb einzeln als Liste und nicht als Muster. + + Kanalnamen sind Absicht: "# waermepumpe-mueller" ist ein Handle, und die + schreiben Chatwerkzeuge klein und ohne Umlaute. */ + const UMLAUT_ERLAUBT = new Set([ + "michael", "dauer", "neue", "steuer", "feuer", "aktuell", "museum", + "waermepumpe-mueller", "waermepumpe-abnahme", + ]); + const BRAUCHT_SCHARFES_S = new Set([ + "aufmass", "strasse", "groesse", "groesser", "fuss", "fussboden", + "mass", "masse", "weiss", "heisst", "aussen", "gross", "grosse", "spass", + ]); + + function umlautFehler(wurzel) { + const raus = []; + wurzel.querySelectorAll("text, .headline, .body, .badge").forEach(e => { + String(e.textContent || "").split(/[^A-Za-zÄÖÜäöüß0-9./#@-]+/).forEach(w => { + if (!w) return; + const k = w.toLowerCase().replace(/^[@#]+/, "").replace(/[.,;:!?)(]+$/, ""); + if (!k || UMLAUT_ERLAUBT.has(k) || raus.includes(w)) return; + if (/ae|oe|ue/.test(k) || BRAUCHT_SCHARFES_S.has(k)) raus.push(w); + }); + }); + return raus; + } + + /* Fuer animation.mjs: das Bewegtbild laeuft nicht durch renderSlide und + braucht deshalb eine eigene Tuer zu derselben Pruefung. */ + window.TEXT_PRUEFUNG = () => umlautFehler(document); + async function renderSlide(i) { // Hilfslinien sind Werkzeug: koennten sie aus einem Tastendruck noch // stehen, landeten sie im Bild. @@ -3578,6 +3624,11 @@ if (location.search.indexOf("nur=anim") >= 0) el.dataset.editmode = "aus"; try { await document.fonts.ready; + /* Umlaute pruefen, bevor gerendert wird. Lieber ein Abbruch mit + klarer Meldung als ein fertiges PNG mit "Sanitaer" darauf. */ + const uf = umlautFehler(el); + if (uf.length) throw new Error("Slide " + (i + 1) + ": Umlaut fehlt bei " + + uf.join(", ") + ". Wenn das Wort so richtig ist, gehoert es in UMLAUT_ERLAUBT."); const imgs = [...el.querySelectorAll("img")]; await Promise.all(imgs.map(im => im.complete ? im.decode().catch(() => {}) diff --git a/post4.html b/post4.html index 5a62710..6710e24 100644 --- a/post4.html +++ b/post4.html @@ -4350,6 +4350,52 @@ if (location.search.indexOf("nur=anim") >= 0) return n < 20 ? 1 : dunkel / n; } + + /* ─── Umlaute im gezeichneten Text ─────────────────────────────────────── + Auf einer Slide stand einmal "Sanitaer" und "Klima / Lueftung". Beides + kam woertlich aus der Quelle -- im Klickdummy war es dort schludrig + eingetippt --, und beides las sich im fertigen Beitrag wie ein Fehler, + weil es einer ist. Gefunden hat es niemand beim Bauen, sondern erst + jemand, der auf den fertigen Post geschaut hat. + + Deshalb prueft es jetzt der Export. Ein reines Muster genuegt dafuer + nicht: Deutsch schreibt "neue", "Dauer", "Michael" voellig zu Recht mit + ae/oe/ue. Nachgezaehlt ueber den gezeichneten Text aller Projekte sind es + aber nur eine Handvoll solcher Woerter -- eine Ausnahmeliste ist also + kurz und trifft trotzdem alles andere. + + ss ist der umgekehrte Fall: fast immer richtig (dass, Schluss, Adresse, + Netzanschluss), nur ein paar Woerter brauchen ein scharfes s. Die stehen + deshalb einzeln als Liste und nicht als Muster. + + Kanalnamen sind Absicht: "# waermepumpe-mueller" ist ein Handle, und die + schreiben Chatwerkzeuge klein und ohne Umlaute. */ + const UMLAUT_ERLAUBT = new Set([ + "michael", "dauer", "neue", "steuer", "feuer", "aktuell", "museum", + "waermepumpe-mueller", "waermepumpe-abnahme", + ]); + const BRAUCHT_SCHARFES_S = new Set([ + "aufmass", "strasse", "groesse", "groesser", "fuss", "fussboden", + "mass", "masse", "weiss", "heisst", "aussen", "gross", "grosse", "spass", + ]); + + function umlautFehler(wurzel) { + const raus = []; + wurzel.querySelectorAll("text, .headline, .body, .badge").forEach(e => { + String(e.textContent || "").split(/[^A-Za-zÄÖÜäöüß0-9./#@-]+/).forEach(w => { + if (!w) return; + const k = w.toLowerCase().replace(/^[@#]+/, "").replace(/[.,;:!?)(]+$/, ""); + if (!k || UMLAUT_ERLAUBT.has(k) || raus.includes(w)) return; + if (/ae|oe|ue/.test(k) || BRAUCHT_SCHARFES_S.has(k)) raus.push(w); + }); + }); + return raus; + } + + /* Fuer animation.mjs: das Bewegtbild laeuft nicht durch renderSlide und + braucht deshalb eine eigene Tuer zu derselben Pruefung. */ + window.TEXT_PRUEFUNG = () => umlautFehler(document); + async function renderSlide(i) { // Hilfslinien sind Werkzeug: koennten sie aus einem Tastendruck noch // stehen, landeten sie im Bild. @@ -4366,6 +4412,11 @@ if (location.search.indexOf("nur=anim") >= 0) el.dataset.editmode = "aus"; try { await document.fonts.ready; + /* Umlaute pruefen, bevor gerendert wird. Lieber ein Abbruch mit + klarer Meldung als ein fertiges PNG mit "Sanitaer" darauf. */ + const uf = umlautFehler(el); + if (uf.length) throw new Error("Slide " + (i + 1) + ": Umlaut fehlt bei " + + uf.join(", ") + ". Wenn das Wort so richtig ist, gehoert es in UMLAUT_ERLAUBT."); const imgs = [...el.querySelectorAll("img")]; await Promise.all(imgs.map(im => im.complete ? im.decode().catch(() => {}) diff --git a/post5.html b/post5.html index 1669db0..d7894b0 100644 --- a/post5.html +++ b/post5.html @@ -4592,6 +4592,52 @@ if (location.search.indexOf("nur=anim") >= 0) return n < 20 ? 1 : dunkel / n; } + + /* ─── Umlaute im gezeichneten Text ─────────────────────────────────────── + Auf einer Slide stand einmal "Sanitaer" und "Klima / Lueftung". Beides + kam woertlich aus der Quelle -- im Klickdummy war es dort schludrig + eingetippt --, und beides las sich im fertigen Beitrag wie ein Fehler, + weil es einer ist. Gefunden hat es niemand beim Bauen, sondern erst + jemand, der auf den fertigen Post geschaut hat. + + Deshalb prueft es jetzt der Export. Ein reines Muster genuegt dafuer + nicht: Deutsch schreibt "neue", "Dauer", "Michael" voellig zu Recht mit + ae/oe/ue. Nachgezaehlt ueber den gezeichneten Text aller Projekte sind es + aber nur eine Handvoll solcher Woerter -- eine Ausnahmeliste ist also + kurz und trifft trotzdem alles andere. + + ss ist der umgekehrte Fall: fast immer richtig (dass, Schluss, Adresse, + Netzanschluss), nur ein paar Woerter brauchen ein scharfes s. Die stehen + deshalb einzeln als Liste und nicht als Muster. + + Kanalnamen sind Absicht: "# waermepumpe-mueller" ist ein Handle, und die + schreiben Chatwerkzeuge klein und ohne Umlaute. */ + const UMLAUT_ERLAUBT = new Set([ + "michael", "dauer", "neue", "steuer", "feuer", "aktuell", "museum", + "waermepumpe-mueller", "waermepumpe-abnahme", + ]); + const BRAUCHT_SCHARFES_S = new Set([ + "aufmass", "strasse", "groesse", "groesser", "fuss", "fussboden", + "mass", "masse", "weiss", "heisst", "aussen", "gross", "grosse", "spass", + ]); + + function umlautFehler(wurzel) { + const raus = []; + wurzel.querySelectorAll("text, .headline, .body, .badge").forEach(e => { + String(e.textContent || "").split(/[^A-Za-zÄÖÜäöüß0-9./#@-]+/).forEach(w => { + if (!w) return; + const k = w.toLowerCase().replace(/^[@#]+/, "").replace(/[.,;:!?)(]+$/, ""); + if (!k || UMLAUT_ERLAUBT.has(k) || raus.includes(w)) return; + if (/ae|oe|ue/.test(k) || BRAUCHT_SCHARFES_S.has(k)) raus.push(w); + }); + }); + return raus; + } + + /* Fuer animation.mjs: das Bewegtbild laeuft nicht durch renderSlide und + braucht deshalb eine eigene Tuer zu derselben Pruefung. */ + window.TEXT_PRUEFUNG = () => umlautFehler(document); + async function renderSlide(i) { // Hilfslinien sind Werkzeug: koennten sie aus einem Tastendruck noch // stehen, landeten sie im Bild. @@ -4608,6 +4654,11 @@ if (location.search.indexOf("nur=anim") >= 0) el.dataset.editmode = "aus"; try { await document.fonts.ready; + /* Umlaute pruefen, bevor gerendert wird. Lieber ein Abbruch mit + klarer Meldung als ein fertiges PNG mit "Sanitaer" darauf. */ + const uf = umlautFehler(el); + if (uf.length) throw new Error("Slide " + (i + 1) + ": Umlaut fehlt bei " + + uf.join(", ") + ". Wenn das Wort so richtig ist, gehoert es in UMLAUT_ERLAUBT."); const imgs = [...el.querySelectorAll("img")]; await Promise.all(imgs.map(im => im.complete ? im.decode().catch(() => {}) diff --git a/post6.html b/post6.html index e1580e0..bf4c413 100644 --- a/post6.html +++ b/post6.html @@ -5529,6 +5529,52 @@ if (location.search.indexOf("nur=anim") >= 0) return n < 20 ? 1 : dunkel / n; } + + /* ─── Umlaute im gezeichneten Text ─────────────────────────────────────── + Auf einer Slide stand einmal "Sanitaer" und "Klima / Lueftung". Beides + kam woertlich aus der Quelle -- im Klickdummy war es dort schludrig + eingetippt --, und beides las sich im fertigen Beitrag wie ein Fehler, + weil es einer ist. Gefunden hat es niemand beim Bauen, sondern erst + jemand, der auf den fertigen Post geschaut hat. + + Deshalb prueft es jetzt der Export. Ein reines Muster genuegt dafuer + nicht: Deutsch schreibt "neue", "Dauer", "Michael" voellig zu Recht mit + ae/oe/ue. Nachgezaehlt ueber den gezeichneten Text aller Projekte sind es + aber nur eine Handvoll solcher Woerter -- eine Ausnahmeliste ist also + kurz und trifft trotzdem alles andere. + + ss ist der umgekehrte Fall: fast immer richtig (dass, Schluss, Adresse, + Netzanschluss), nur ein paar Woerter brauchen ein scharfes s. Die stehen + deshalb einzeln als Liste und nicht als Muster. + + Kanalnamen sind Absicht: "# waermepumpe-mueller" ist ein Handle, und die + schreiben Chatwerkzeuge klein und ohne Umlaute. */ + const UMLAUT_ERLAUBT = new Set([ + "michael", "dauer", "neue", "steuer", "feuer", "aktuell", "museum", + "waermepumpe-mueller", "waermepumpe-abnahme", + ]); + const BRAUCHT_SCHARFES_S = new Set([ + "aufmass", "strasse", "groesse", "groesser", "fuss", "fussboden", + "mass", "masse", "weiss", "heisst", "aussen", "gross", "grosse", "spass", + ]); + + function umlautFehler(wurzel) { + const raus = []; + wurzel.querySelectorAll("text, .headline, .body, .badge").forEach(e => { + String(e.textContent || "").split(/[^A-Za-zÄÖÜäöüß0-9./#@-]+/).forEach(w => { + if (!w) return; + const k = w.toLowerCase().replace(/^[@#]+/, "").replace(/[.,;:!?)(]+$/, ""); + if (!k || UMLAUT_ERLAUBT.has(k) || raus.includes(w)) return; + if (/ae|oe|ue/.test(k) || BRAUCHT_SCHARFES_S.has(k)) raus.push(w); + }); + }); + return raus; + } + + /* Fuer animation.mjs: das Bewegtbild laeuft nicht durch renderSlide und + braucht deshalb eine eigene Tuer zu derselben Pruefung. */ + window.TEXT_PRUEFUNG = () => umlautFehler(document); + async function renderSlide(i) { // Hilfslinien sind Werkzeug: koennten sie aus einem Tastendruck noch // stehen, landeten sie im Bild. @@ -5545,6 +5591,11 @@ if (location.search.indexOf("nur=anim") >= 0) el.dataset.editmode = "aus"; try { await document.fonts.ready; + /* Umlaute pruefen, bevor gerendert wird. Lieber ein Abbruch mit + klarer Meldung als ein fertiges PNG mit "Sanitaer" darauf. */ + const uf = umlautFehler(el); + if (uf.length) throw new Error("Slide " + (i + 1) + ": Umlaut fehlt bei " + + uf.join(", ") + ". Wenn das Wort so richtig ist, gehoert es in UMLAUT_ERLAUBT."); const imgs = [...el.querySelectorAll("img")]; await Promise.all(imgs.map(im => im.complete ? im.decode().catch(() => {}) diff --git a/rules.md b/rules.md index 92f052b..a9d7e91 100644 --- a/rules.md +++ b/rules.md @@ -347,6 +347,35 @@ schon zweimal zu viel. - **Keine Em-Dashes (—)** in sichtbaren Texten. Komma, Punkt oder `·` - Feature-Texte wortgleich von der Website. Headlines dürfen gekürzt und umgestellt werden, aber **keine neuen Wörter erfinden** +- **Umlaute sind Umlaute.** Kein `Sanitaer`, kein `Aufmass`. Das gilt auch + dann, wenn die Quelle es so schreibt: eine ASCII-Ersatzschreibung im + Klickdummy ist ein Tippfehler und kein Produktbegriff, den man zitiert + +#### Der Export prüft das + +Aufgefallen ist es an „Sanitaer“ und „Klima / Lueftung“ auf einer fertigen +Slide — beim Bauen hatte es niemand gesehen, weil beides wörtlich aus der +Quelle kam. Seitdem prüft es die Maschine, an zwei Stellen: + +| Weg | Wo | +|---|---| +| Bildexport | `renderSlide()` bricht ab, bevor ein PNG entsteht | +| Bewegtbild | `animation.mjs` prüft an 24 Stellen über den Ablauf | + +**Ein reines Muster genügt dafür nicht.** Deutsch schreibt „neue“, „Dauer“ und +„Michael“ völlig zu Recht mit `ae`/`oe`/`ue`. Nachgezählt über den gezeichneten +Text aller Projekte sind es aber nur eine Handvoll solcher Wörter — deshalb +Muster **plus** Ausnahmeliste `UMLAUT_ERLAUBT`. Bei `ss` ist es umgekehrt: +fast immer richtig (dass, Schluss, Netzanschluss), nur wenige Wörter brauchen +ein scharfes s. Die stehen einzeln in `BRAUCHT_SCHARFES_S`. + +Schlägt die Prüfung bei einem Wort an, das so richtig ist, gehört es in die +Ausnahmeliste — nicht die Prüfung abgeschaltet. + +> **Beim Bewegtbild reicht ein Blick auf das erste Bild nicht.** Der erste +> Anlauf prüfte nur den Anfangszustand und fand nichts: die fehlerhaften +> Wörter erscheinen dort erst ab `t = 0,4`. Der Text einer Animation ändert +> sich mit der Zeit, die Prüfung muss es auch. --- @@ -873,6 +902,7 @@ etwas ins Statusfeld schreibt. **Nicht entfernen.** | Farbe „stimmt nicht" | Deckkraft der Ebene lag darüber. Eine Farbe ist nur exakt, wenn nichts sie multipliziert | | Muster wirkt zufällig hingeworfen | Formen liefen quer durch den Text. Dekoration bleibt in den Rändern, verankert und angeschnitten | | Nichts lädt, Bühne leer | Konstante vor ihrer Deklaration benutzt (TDZ) | +| „Sanitaer“ auf dem fertigen Beitrag | Wörtlich aus der Quelle übernommen, die es selbst falsch schreibt. §4 | ---