diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index 9b167f8b4..b1117f7f7 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -30,6 +30,8 @@ jobs: run: npm run lint - name: Check formatting run: npm run format:check + - name: Check dashes + run: npm run check:dashes - name: Build run: npm run build - name: Validate built pages diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..0e29a1485 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,19 @@ +# CLAUDE.md + +Die Seite ist auf Deutsch. Texte folgen der deutschen Rechtschreibung und Typografie (Duden), nicht der englischen. + +## Striche + +| Zeichen | Name | Verwendung | Beispiel | +| ------- | -------------------------------- | ------------------------------------------------- | ---------------------------------------------------------- | +| `-` | Bindestrich | Zusammensetzungen, Ergänzungen | `Mitarbeiter-App`, `Arbeitszeit- und Abwesenheitsdaten` | +| `–` | Gedankenstrich (mit Leerzeichen) | Einschub, Nachtrag, Trenner in Titeln | `Zeiten erfassen – mit der App`, `title: Kalender – Hilfe` | +| `–` | Bis-Strich (ohne Leerzeichen) | Bereiche bei Zahlen, Zeiten, Wochentagen | `8–12 Uhr`, `S. 1–32`, `Mo–Fr` | +| `—` | Geviertstrich | **nicht verwenden**, das ist englische Typografie | – | + +- Den Gedankenstrich und den Bis-Strich immer als Zeichen `–` (U+2013) schreiben, nie als `–`, `—` oder `-`. + `title` und `description` aus dem Front Matter werden mit `{{title}}` escaped ausgegeben, ein `–` stünde dort als Text im Browser-Tab und im Suchergebnis. +- Bei „von … bis“ das Wort ausschreiben statt eines Strichs: `vom 23.11. bis 27.11.`, nicht `vom 23.11.–27.11.`. + +`npm run check:dashes` (läuft auch in der CI) meldet Geviertstriche, `–`/`—`, einen Bindestrich mit Leerzeichen als Gedankenstrich im Fließtext und Bereiche mit Leerzeichen um den Strich. +Bereiche ohne Leerzeichen mit Bindestrich (`8-12 Uhr`) erkennt der Check nicht, da sie von Datumsangaben, Versionen und Hausnummern nicht zu unterscheiden sind. diff --git a/README.md b/README.md index e9041ba5e..71421eaf8 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ Diese Checks laufen auch in der CI bei jedem Pull Request: ```bash npm run lint # ESLint für JavaScript npm run format:check # Prettier für CSS, HTML, Markdown und SVG +npm run check:dashes # Gedankenstriche und Bis-Striche in src (siehe CLAUDE.md) npm run check:dist # validiert die gebauten Seiten in dist (vorher npm run build) ``` diff --git a/package.json b/package.json index ede543417..fcf2429d7 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "scripts": { "lint": "eslint src lib scripts .eleventy.js", "format:check": "prettier --check 'src/**/*.{css,html,md,svg}'", + "check:dashes": "node scripts/check-dashes.js", "clean": "rm -rf $npm_package_config_outdir", "predev": "npm run clean", "dev": "concurrently 'npm:dev:*'", diff --git a/scripts/check-dashes.js b/scripts/check-dashes.js new file mode 100644 index 000000000..709a686df --- /dev/null +++ b/scripts/check-dashes.js @@ -0,0 +1,81 @@ +// Checks the dashes in the page sources (German typography, see CLAUDE.md). +// +// The site wrote the Gedankenstrich four different ways (" - ", " – ", " — " +// and "–"). Only the literal en dash "–" is allowed: the em dash is +// English typography, and entities end up as visible text when a front matter +// title or description is rendered escaped with {{title}}. + +import fs from "node:fs"; +import path from "node:path"; + +const root = "src"; + +const everywhere = [ + { pattern: /—/g, message: 'em dash "—", use "–"' }, + { + pattern: /&(ndash|mdash|#821[12]|#x201[34]);/gi, + message: 'dash entity, write the character "–" instead', + }, +]; + +const prose = [ + { + pattern: /[\p{L}.,!?)*_"”’] - [\p{L}(*_„"]/gu, + message: 'hyphen as Gedankenstrich, use " – "', + }, + { pattern: /\d - \d/g, message: 'spaced range, use "–" without spaces' }, +]; + +// Markup and code in which " - " is not a dash, replaced by spaces so that +// the reported columns stay correct. +const notProse = [ + //g, + //g, + /^```[\s\S]*?^```/gm, + /\{\{[\s\S]*?\}\}/g, + /`[^`\n]*`/g, + /^\s*- /gm, +]; + +function blank(text) { + return notProse.reduce( + (result, pattern) => + result.replace(pattern, match => match.replace(/[^\n]/g, " ")), + text, + ); +} + +function check(file) { + const source = fs.readFileSync(file, "utf8"); + const rules = file.endsWith(".js") ? everywhere : [...everywhere, ...prose]; + const text = file.endsWith(".js") ? source : blank(source); + const lines = text.split("\n"); + + const errors = []; + for (const [index, line] of lines.entries()) { + for (const { pattern, message } of rules) { + for (const match of line.matchAll(pattern)) { + const column = match.index + match[0].search(/[-—&]/) + 1; + errors.push(`${file}:${index + 1}:${column} ${message}`); + } + } + } + return errors; +} + +const files = fs + .readdirSync(root, { recursive: true }) + .map(entry => path.join(root, entry)) + .filter(file => /\.(md|hbs)$|\.11tydata\.js$/.test(file)) + .sort(); + +const errors = files.flatMap(check); +for (const error of errors) { + console.error(error); +} + +if (errors.length > 0) { + process.exit(1); +} + +console.log(`${files.length} files checked, no problems found.`); diff --git a/src/404.hbs b/src/404.hbs index cb805c167..f6f410db1 100644 --- a/src/404.hbs +++ b/src/404.hbs @@ -1,7 +1,7 @@ --- layout: layout eleventyExcludeFromCollections: true -title: Es tut uns leid - hier ist etwas schiefgelaufen +title: Es tut uns leid – hier ist etwas schiefgelaufen description: Die angeforderte Seite wurde leider nicht gefunden. permalink: 404.html --- diff --git a/src/_includes/partials/footer.hbs b/src/_includes/partials/footer.hbs index 01e8409be..574cfe5a5 100644 --- a/src/_includes/partials/footer.hbs +++ b/src/_includes/partials/footer.hbs @@ -25,7 +25,7 @@