Zum Inhalt springen
KI

Mein erster Blog

Ludo Fourrage Ludo Fourrage 8. April 2026 8 Min. Lesezeit
Mein erster Blog

Problemstellung:

Der Plattform fehlt ein Content-Marketing-Kanal. Ein Blog ermöglicht es jedem Tenant, SEO-optimierte, mehrsprachige Artikel in Strapi CMS zu verfassen und in der SvelteKit-App auszuliefern – um organischen Traffic zu steigern, Autorität aufzubauen und die bestehende i18n-Pipeline mit 13 Sprachvarianten zu unterstützen.

Erfolgskriterien

  • In Strapi verfasste Blogposts werden im Frontend im Branding des Tenants gerendert
  • Posts werden über die bestehende Webhook-Pipeline automatisch in alle 13 Sprachen übersetzt (keine Änderungen an der Übersetzungs-Infrastruktur)
  • Blogseiten ranken in Suchmaschinen (Structured Data, Sitemap, hreflang, og:article)
  • Tenants ohne das Feature-Flag blog sehen keine Blog-Routen (404)

User Stories

  • Als Content-Autor möchte ich Blogbeiträge in Strapi mit umfangreichem Markdown, Bildern und Video-Einbettungen schreiben, damit ich Inhalte veröffentlichen kann, ohne Code anzufassen.
  • Als Website-Besucher möchte ich Blogbeiträge nach Kategorien filtern können, damit ich relevante Inhalte finde.
  • Als Website-Besucher möchte ich ein Inhaltsverzeichnis bei langen Beiträgen haben, damit ich schnell navigieren kann.
  • Als Website-Besucher möchte ich Beiträge in meiner Sprache lesen, mit automatischer Übersetzung aus dem Englischen.
  • Als Website-Besucher möchte ich Beiträge in sozialen Netzwerken teilen können.
  • Als Website-Besucher möchte ich verwandte Beiträge entdecken und mehr über den Autor erfahren.
  • Als SEO-Manager möchte ich, dass jeder Beitrag passende Meta-Tags, strukturierte Daten und Sitemap-Einträge hat.

Funktionale Anforderungen

Must Have (MVP)

  1. Strapi-Content-Types: blog-posts, blog-authors, blog-categories — alle mit tenant-, locale- und inherit-Feldern
  2. Blog-Übersichtsseite (/blog) — paginierte Grid-Ansicht der Beiträge, Kategoriefilter über Query-Parameter
  3. Beitragsdetailseite (/blog/[slug]) — vollständiges Markdown-Rendering mit Inhaltsverzeichnis, Autorenkarte, verwandten Beiträgen, Social-Sharing
  4. Kategorie-Seite (/blog/category/[slug]) — gefilterte Liste, die dieselben Listing-Komponenten wiederverwendet
  5. Autorenseite (/blog/author/[slug]) — Autorenbio + deren Beiträge
  6. Inhaltsverzeichnis — automatisch generiert aus h2/h3-Überschriften, sticky Sidebar auf Desktop, einklappbar auf Mobile, aktives Überschriften-Highlighting via Intersection Observer
  7. YouTube/Vimeo-Einbettungen — nackte URLs im Markdown werden automatisch in responsive iframes konvertiert (youtube-nocookie.com für Datenschutz)
  8. Lesezeit — serverseitig aus der Wortanzahl berechnet (Math.ceil(words / 200))
  9. Social-Share-Buttons — X, Facebook, LinkedIn, E-Mail, Link kopieren (Web Share API, wo verfügbar)
  10. Verwandte Beiträge — gleiche Kategorie, aktueller Beitrag ausgeschlossen, Limit 3, sortiert nach publishedAt absteigend
  11. RSS-Feed (/blog/rss.xml) — RSS 2.0, Locale über Query-Parameter
  12. SEO — SeoComponent pro Beitrag, og:article-Tags, BlogPosting JSON-LD-Schema, kanonische URLs
  13. Sitemap — dynamische Blog-Post-/Kategorie-/Autoren-URLs werden der bestehenden Sitemap mit hreflang hinzugefügt
  14. Feature Flagblog: boolean in TenantConfig.features, Layout-Guard gibt 404 zurück, wenn deaktiviert
  15. i18n — Paraglide-Message-Keys für alle UI-Strings (Navigation, Labels, Pagination), Strapi-Content wird über bestehenden Webhook automatisch übersetzt
  16. Pagination — seitenbasiert mit Strapis nativer Pagination (9 Beiträge pro Seite)
  17. Featured Post — boolesches featured-Flag auf blog-posts; Featured Post wird als Hero-Karte oben auf der /blog-Übersichtsseite angezeigt (größere Karte, volle Breite, über dem Grid)

Nice to Have (Post-MVP)

  1. Volltextsuche über alle Blogbeiträge
  2. Beitragsserien / mehrteilige Artikel
  3. Lesefortschrittsbalken
  4. Kommentare (vermutlich via Third-Party-Embed)
  5. Newsletter-Signup-CTA innerhalb der Beiträge
  6. View-Count- / Analytics-Integration

Technische Anforderungen

Datenmodell (Strapi Content Types)

blog-posts (api::blog-post.blog-post):

Feld Typ Lokalisiert Hinweise
title Text (erforderlich) Ja Durch Übersetzungskonfiguration abgedeckt
slug UID (aus title) Nein Von Übersetzung ausgeschlossen
excerpt Text Ja Für Karten + Meta-Description
body Rich Text (Markdown) Ja Gerendert über marked
featuredImage Media (einzeln) Nein URL + alternativeText
category Relation (manyToOne -> blog-category) Nein
author Relation (manyToOne -> blog-author) Nein
featured Boolean (Standard false) Nein Featured-Post wird als Hero-Karte im Listing angezeigt
seo Component (shared.seo) Ja Vorhandene SeoComponent wiederverwenden
tenant Text Nein Standard-Tenant-Filter
inherit Boolean (Standard true) Nein Auto-Übersetzungs-Flag

blog-authors (api::blog-author.blog-author):

Feld Typ Lokalisiert Hinweise
name Text (erforderlich) Ja
slug UID (aus name) Nein
bio Rich Text (Markdown) Ja
avatar Media (einzeln) Nein
socialLinks JSON Nein { twitter?, linkedin?, website? }
tenant Text Nein
inherit Boolean (Standard true) Nein

blog-categories (api::blog-category.blog-category):

Feld Typ Lokalisiert Hinweise
name Text (erforderlich) Ja
slug UID (aus name) Nein
description Text Ja
color Text Nein Hex-Farbe für UI-Badges
tenant Text Nein
inherit Boolean (Standard true) Nein

TypeScript-Interfaces

Siehe src/lib/services/strapi-types.ts für BlogPost, BlogAuthor, BlogCategory.

Routen

Alle unter src/routes/(marketing)/blog/:

Route Server Load Zweck
blog/+layout.server.ts Feature-Guard + Laden der Kategorien Gate + Navigationsdaten
blog/+page.server.ts Paginierte Posts, optionaler ?category=-Filter Listing
blog/[slug]/+page.server.ts Post nach slug + verwandte Posts + Berechnung der Lesezeit Detail
blog/category/[slug]/+page.server.ts Posts gefiltert nach Kategorie Kategorie-Listing
blog/author/[slug]/+page.server.ts Autor + seine/ihre Posts Autorenseite
blog/rss.xml/+server.ts RSS-2.0-XML, ?locale=-Parameter Feed

UI-Komponenten

Alle in src/lib/components/blog/:

Komponente Props Hinweise
BlogPostCard.svelte post: BlogPost Karte mit Bild, Kategorie-Badge, Titel, Auszug, Autor, Datum, Lesezeit
BlogPostContent.svelte body: string Markdown zu HTML über marked mit Video-Embed-Erweiterung + Heading-IDs
TableOfContents.svelte entries: TocEntry[] Sticky Sidebar, Intersection Observer für aktiven Zustand, mobil einklappbar
AuthorCard.svelte author: BlogAuthor Avatar, Name, Bio, Social Links
SocialShare.svelte url: string, title: string Share-Buttons mit Web-Share-API-Fallback
RelatedPosts.svelte posts: BlogPost[] Grid aus BlogPostCard
BlogPagination.svelte pagination: StrapiPagination, basePath: string Vor/Zurück + Seitenzahlen

Sicherheit & Authentifizierung

  • Blog ist öffentlich (keine Authentifizierung erforderlich) — Marketing-Inhalte
  • Strapi-API-Lesezugriff über vorhandenes STRAPI_JWT-Token
  • Video-Einbettungen nutzen youtube-nocookie.com für DSGVO-Konformität

Randfälle & Fehlerbehandlung

  • Beitrag nicht gefunden: 404 über SvelteKit error()
  • Keine Beiträge in Kategorie: Leerzustand mit blog_no_posts-Hinweis
  • Fehlendes Beitragsbild: Karte wird ohne Bild gerendert
  • Übersetzung nicht verfügbar: loadContentWithFallback() fällt auf Englisch zurück
  • RTL-Sprachen: Layout respektiert dir="rtl" (ar, he, ur)

E2E-Testplan

Testdateien liegen in tests/blog/. Folge den bestehenden Mustern: parameterisiert über Viewports, Axe für Barrierefreiheit (a11y), networkidle wartet auf Strapi-Content.

Critical-Path-Tests

  1. Blog-Listing lädt/blog rendert ein Grid aus Post-Karten mit Titel, Auszug, Kategorie-Badge, Autor, Datum und Lesezeit.
  2. Pagination funktioniert/blog?page=2 zeigt die zweite Ergebnisseite; Zurück-/Weiter-Buttons navigieren korrekt; Seite 1 hat keinen „Zurück“-Button.
  3. Kategorie-Filter — Klick auf einen Kategorie-Chip filtert Posts; URL aktualisiert sich zu ?category=<slug>; „Alle“-Chip entfernt den Filter.
  4. Post-Detailansicht rendert/blog/[slug] zeigt Titel, Feature-Bild, formatierten Body, Autor-Card, Lesezeit und Veröffentlichungsdatum.
  5. Table of Contents — Detailseite erzeugt ein Inhaltsverzeichnis (ToC) aus h2/h3-Überschriften; Klick auf einen ToC-Eintrag scrollt zur Überschrift.
  6. Verwandte Posts — Detailseite zeigt bis zu 3 verwandte Posts aus derselben Kategorie (ohne den aktuellen).
  7. Kategorie-Seite/blog/category/[slug] zeigt Kategorienamen, Beschreibung und ausschließlich Posts aus dieser Kategorie.
  8. Autor-Seite/blog/author/[slug] zeigt Autorenbio, Avatar, Social Links und deren Posts.
  9. Social-Share-Buttons — Share-Buttons sind in der Detailansicht sichtbar; „Link kopieren“ kopiert die URL und zeigt einen Bestätigungstext.
  10. RSS-FeedGET /blog/rss.xml liefert gültiges XML mit Content-Type: application/rss+xml und enthält Post-Einträge.
  11. Navigation — Blog-Link im Site-Menü navigiert zu /blog; Breadcrumbs zeigen die korrekte Hierarchie.
  12. Featured Post/blog zeigt den hervorgehobenen Post als größere Hero-Karte oberhalb des regulären Grids.

Edge-Case- & Fehler-Tests

  1. Post nicht gefunden/blog/nonexistent-slug liefert eine 404-Seite.
  2. Leere Kategorie — Eine Kategorie ohne Posts zeigt die leere Zustandsmeldung blog_no_posts.
  3. Fehlendes Feature-Bild — Post-Karte rendert sauber ohne Bild (kein kaputtes <img>).
  4. Fehlender Autor/Kategorie — Post ohne Autor oder Kategorie rendert ohne diese Elemente (kein Crash).
  5. Feature Flag deaktiviert — Wenn blog: false, liefert /blog einen 404.

Querschnitts-Tests

  1. i18n: Locale-Switch — Zu /fr/blog navigieren, prüfen, dass UI-Texte (Seitentitel, „Weiterlesen“, Pagination-Labels) auf Französisch sind; Post-Content fällt auf Englisch zurück, wenn nicht übersetzt.
  2. i18n: RTL-Layout — Zu /ar/blog und /ar/blog/[slug] navigieren, prüfen, dass dir="rtl" gesetzt ist und das Layout korrekt rendert (ToC auf der richtigen Seite).
  3. SEO: Meta-Tags — Detailseite eines Posts hat og:type=article, og:title, og:description, article:published_time und einen Canonical-Link.
  4. SEO: JSON-LD — Detailseite eines Posts enthält ein <script type="application/ld+json"> mit @type: BlogPosting, Headline, Autor, datePublished.
  5. SEO: Sitemap/sitemap.xml enthält Blog-Post-URLs mit hreflang-Alternativen.
  6. Accessibility — Axe-Audits auf /blog, /blog/[slug], /blog/author/[slug] bei mobilen und Desktop-Viewports ausführen (folgt dem bestehenden a11y-Testmuster).
  7. Tastaturnavigation — ToC-Einträge und Pagination-Buttons sind per Tastatur fokussier- und aktivierbar; Fokus-Indikatoren sind sichtbar.

Nicht im Umfang

  • Blog-Kommentare
  • Volltextsuche
  • Newsletter-Integration innerhalb von Beiträgen
  • Analytics/Ansichtszahlen
  • Benutzerdefinierte Beitragstemplates pro Tenant
Ludo Fourrage
Ludo Fourrage

Ludo ist Lehrer und Unternehmer im Technologiebereich, angetrieben von einem einfachen Glauben: Hochwertige Bildung sollte für alle zugänglich sein.