From 29965c11647490cd9d0632b08d9134593178ba96 Mon Sep 17 00:00:00 2001 From: mAi Date: Thu, 30 Apr 2026 02:50:08 +0200 Subject: [PATCH] mAi: #9 - GEO Schema-Slot {{schema_jsonld}} in templates/base.html + render.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Schema-Markup-Mechanismus für templated Sites (custom Sites bleiben unberührt). - templates/base.html: {{schema_jsonld}} Slot im . - site.yaml: optionaler `schema:` Block. `type:` -> `@type`, `@context` wird automatisch ergänzt. Fehlt der Block, bleibt der Slot leer. - render.sh: liest schema via `yq -o=json`, transformiert mit jq, fügt Template-Default für `type` ein (person-* -> Person, product-* -> Product, editorial -> Article). - render.sh: literal-string replace (lreplace) statt awk gsub für multiline- Substitution. Behebt nebenbei einen latenten Bug, bei dem `©` im template_body als `{{body}}copy;` corrupted wurde (gsub interpretierte `&` als matched text). - tests/schema-test.sh + 4 Fixtures: validiert explicit type, Template- Defaults für 3 Templates, leerer Slot ohne schema-Block. - README.md: Schema.org-Konvention dokumentiert (Block-Format, Defaults, Custom-Sites-Hinweis, Schema.org-Validator-Link). QA: ./build.sh -> 59 sites OK, custom Sites byte-identical zur Source, 3 templated Fixtures rendern valides JSON-LD (Person/Product/Article), no-schema-Fixture produziert keinen ` inside `` (slot `{{schema_jsonld}}` in `templates/base.html`). See `docs/geo-seo-guideline.md` §3.3 for rationale. + +### Conventions + +- `schema.type` → `@type` (the YAML key is `type`, the rendered key is `@type`). +- `@context: https://schema.org` is added automatically. +- Nested objects use the JSON-LD form directly: write `"@type": Organization` (quoted because of the `@`). +- Array fields like `sameAs:` are passed through as JSON arrays. +- If `schema:` is absent, no `" + fi +fi + # Read template file and extract CSS/body sections template_content=$(cat "$TEMPLATE_FILE") @@ -184,6 +211,7 @@ echo "$css_animations" > "$tmpdir/css_animations" echo "$css_noise" > "$tmpdir/css_noise" echo "$template_css" > "$tmpdir/template_css" printf '%b' "$template_body" > "$tmpdir/template_body" +printf '%s' "$schema_jsonld" > "$tmpdir/schema_jsonld" # Use awk for reliable multiline substitution awk -v vars="$tmpdir/css_variables" \ @@ -192,6 +220,7 @@ awk -v vars="$tmpdir/css_variables" \ -v noise="$tmpdir/css_noise" \ -v tcss="$tmpdir/template_css" \ -v tbody="$tmpdir/template_body" \ + -v schema="$tmpdir/schema_jsonld" \ -v fonts_file=<(echo "$fonts") \ ' function read_file(path, line, content) { @@ -200,6 +229,15 @@ function read_file(path, line, content) { close(path) return content } +# Literal string replace — avoids gsub replacement-string interpretation of & and \ +function lreplace(haystack, needle, repl, pos, out) { + out = "" + while ((pos = index(haystack, needle)) > 0) { + out = out substr(haystack, 1, pos - 1) repl + haystack = substr(haystack, pos + length(needle)) + } + return out haystack +} BEGIN { cv = read_file(vars) cr = read_file(resp) @@ -207,15 +245,18 @@ BEGIN { cn = read_file(noise) tc = read_file(tcss) tb = read_file(tbody) + sc = read_file(schema) } { - gsub(/\{\{css_variables\}\}/, cv) - gsub(/\{\{css_responsive\}\}/, cr) - gsub(/\{\{css_animations\}\}/, ca) - gsub(/\{\{css_noise\}\}/, cn) - gsub(/\{\{template_css\}\}/, tc) - gsub(/\{\{body\}\}/, tb) - print + line = $0 + line = lreplace(line, "{{css_variables}}", cv) + line = lreplace(line, "{{css_responsive}}", cr) + line = lreplace(line, "{{css_animations}}", ca) + line = lreplace(line, "{{css_noise}}", cn) + line = lreplace(line, "{{template_css}}", tc) + line = lreplace(line, "{{body}}", tb) + line = lreplace(line, "{{schema_jsonld}}", sc) + print line } ' <<< "$output" | sed \ -e "s|{{title}}|${title}|g" \ diff --git a/templates/base.html b/templates/base.html index 2148d21..bb7372a 100644 --- a/templates/base.html +++ b/templates/base.html @@ -8,6 +8,7 @@ {{fonts}} +{{schema_jsonld}}