Aller au contenu
Intelligence artificielle

Mon premier article de blog

Ludo Fourrage Ludo Fourrage 8 avril 2026 10 min de lecture
Mon premier article de blog

Problématique :

La plateforme ne dispose d’aucun canal de content marketing. Un blog permettrait à chaque tenant de publier des articles multilingues optimisés pour le SEO, rédigés dans Strapi CMS et rendus dans l’application SvelteKit — pour générer du trafic organique, asseoir l’autorité de marque et soutenir le pipeline i18n existant de 13 locales.

Critères de réussite

  • Les articles de blog rédigés dans Strapi sont affichés sur le frontend en respectant l’identité de marque du tenant
  • Les articles sont traduits automatiquement dans les 13 locales via le pipeline de webhook existant (aucune modification de l’infrastructure de traduction)
  • Les pages de blog sont bien référencées dans les moteurs de recherche (données structurées, sitemap, hreflang, og:article)
  • Les tenants sans le feature flag blog ne voient aucune route de blog (404)

User Stories

  • En tant que créateur de contenu, je veux rédiger des articles de blog dans Strapi avec du markdown enrichi, des images et des vidéos intégrées afin de pouvoir publier du contenu sans toucher au code
  • En tant que visiteur du site, je veux parcourir les articles de blog filtrés par catégorie afin de trouver du contenu pertinent
  • En tant que visiteur du site, je veux une table des matières sur les longs articles afin de pouvoir naviguer rapidement
  • En tant que visiteur du site, je veux lire les articles dans ma langue, avec une traduction automatique depuis l’anglais
  • En tant que visiteur du site, je veux partager les articles sur les réseaux sociaux
  • En tant que visiteur du site, je veux découvrir des articles similaires et en savoir plus sur l’auteur
  • En tant que responsable SEO, je veux que chaque article dispose de balises meta correctes, de données structurées et d’entrées dans le sitemap

Exigences fonctionnelles

Indispensable (MVP)

  1. Types de contenu Strapi : blog-posts, blog-authors, blog-categories — tous avec les champs tenant, locale, inherit
  2. Page de liste de blog (/blog) — grille paginée d’articles, filtre par catégorie via paramètre de requête
  3. Page de détail d’un article (/blog/[slug]) — rendu markdown complet avec sommaire, carte auteur, articles liés, partage social
  4. Page catégorie (/blog/category/[slug]) — liste filtrée réutilisant les mêmes composants de listing
  5. Page auteur (/blog/author/[slug]) — bio de l’auteur + ses articles
  6. Table des matières — générée automatiquement à partir des titres h2/h3, barre latérale fixe sur desktop, repliable sur mobile, mise en évidence du titre actif via Intersection Observer
  7. Intégrations YouTube/Vimeo — les URLs brutes dans le markdown sont automatiquement converties en iframes responsives (youtube-nocookie.com pour la confidentialité)
  8. Temps de lecture — calculé côté serveur à partir du nombre de mots (Math.ceil(words / 200))
  9. Boutons de partage social — X, Facebook, LinkedIn, e‑mail, copie du lien (Web Share API lorsque disponible)
  10. Articles liés — même catégorie, exclure l’article courant, limite 3, triés par publishedAt desc
  11. Flux RSS (/blog/rss.xml) — RSS 2.0, locale via paramètre de requête
  12. SEO — SeoComponent par article, balises og:article, schéma JSON-LD BlogPosting, URLs canoniques
  13. Sitemap — URLs dynamiques des articles / catégories / auteurs ajoutées au sitemap existant avec hreflang
  14. Feature flagblog: boolean dans TenantConfig.features, garde de layout renvoyant une 404 quand désactivé
  15. i18n — clés de messages Paraglide pour toutes les chaînes UI (navigation, libellés, pagination), contenu Strapi auto‑traduit via le webhook existant
  16. Pagination — basée sur les pages en utilisant la pagination native de Strapi (9 articles par page)
  17. Article mis en avant — booléen featured sur les blog-posts ; l’article mis en avant est affiché comme carte « hero » en haut de la page de liste /blog (carte plus grande, pleine largeur, au‑dessus de la grille)

Optionnel (post-MVP)

  1. Recherche en texte intégral sur les articles du blog
  2. Séries d’articles / articles en plusieurs parties
  3. Barre de progression de lecture
  4. Commentaires (probablement via une intégration tierce)
  5. CTA d’inscription à la newsletter dans les articles
  6. Compteur de vues / intégration analytics

Exigences techniques

Modèle de données (Types de contenu Strapi)

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

Champ Type Localisé Notes
title Texte (obligatoire) Oui Mis en correspondance par la config de traduction
slug UID (à partir de title) Non Exclu de la traduction
excerpt Texte Oui Pour les cartes + meta description
body Rich Text (Markdown) Oui Rendu via marked
featuredImage Média (simple) Non URL + alternativeText
category Relation (manyToOne -> blog-category) Non
author Relation (manyToOne -> blog-author) Non
featured Booléen (false par défaut) Non Article mis en avant affiché comme carte héro sur la liste
seo Composant (shared.seo) Oui Réutilise le SeoComponent existant
tenant Texte Non Filtre tenant standard
inherit Booléen (true par défaut) Non Indicateur d’auto‑traduction

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

Champ Type Localisé Notes
name Texte (obligatoire) Oui
slug UID (à partir de name) Non
bio Rich Text (Markdown) Oui
avatar Média (simple) Non
socialLinks JSON Non { twitter?, linkedin?, website? }
tenant Texte Non
inherit Booléen (true par défaut) Non

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

Champ Type Localisé Notes
name Texte (obligatoire) Oui
slug UID (à partir de name) Non
description Texte Oui
color Texte Non Couleur hex pour les badges UI
tenant Texte Non
inherit Booléen (true par défaut) Non

Interfaces TypeScript

Voir src/lib/services/strapi-types.ts pour BlogPost, BlogAuthor, BlogCategory.

Routes

Toutes sous src/routes/(marketing)/blog/ :

Route Server Load Objectif
blog/+layout.server.ts Feature guard + chargement des catégories Porte d’accès + données de navigation
blog/+page.server.ts Articles paginés, filtre ?category= optionnel Listing
blog/[slug]/+page.server.ts Article par slug + articles associés + calcul du temps de lecture Détail
blog/category/[slug]/+page.server.ts Articles filtrés par catégorie Listing par catégorie
blog/author/[slug]/+page.server.ts Auteur + ses articles Page auteur
blog/rss.xml/+server.ts XML RSS 2.0, paramètre ?locale= Flux

Composants UI

Tous dans src/lib/components/blog/ :

Composant Props Notes
BlogPostCard.svelte post: BlogPost Carte avec image, badge de catégorie, titre, extrait, auteur, date, temps de lecture
BlogPostContent.svelte body: string Markdown vers HTML via marked avec extension d’intégration vidéo + IDs de titres
TableOfContents.svelte entries: TocEntry[] Barre latérale fixe, Intersection Observer pour l’état actif, repliable sur mobile
AuthorCard.svelte author: BlogAuthor Avatar, nom, bio, liens sociaux
SocialShare.svelte url: string, title: string Boutons de partage avec repli via Web Share API
RelatedPosts.svelte posts: BlogPost[] Grille de BlogPostCard
BlogPagination.svelte pagination: StrapiPagination, basePath: string Précédent/suivant + numéros de page

Sécurité & Authentification

  • Le blog est public (aucune authentification requise) — contenu marketing
  • Accès en lecture à l’API Strapi via le jeton STRAPI_JWT existant
  • Les intégrations vidéo utilisent youtube-nocookie.com pour la conformité RGPD

Cas limites & gestion des erreurs

  • Article introuvable : 404 via SvelteKit error()
  • Aucun article dans la catégorie : état vide avec message blog_no_posts
  • Image à la une manquante : la carte s’affiche sans image
  • Traduction non disponible : loadContentWithFallback() revient par défaut à l’anglais
  • Langues RTL : la mise en page respecte dir="rtl" (ar, he, ur)

Plan de test E2E

Les fichiers de tests se trouvent dans tests/blog/. Suivre les modèles existants : paramétrés selon les viewports, Axe pour l’accessibilité (a11y), networkidle attend que le contenu Strapi soit prêt.

Tests du parcours critique

  1. Chargement de la liste des articles/blog affiche une grille de cartes d’articles avec titre, extrait, badge de catégorie, auteur, date et temps de lecture
  2. Pagination fonctionnelle/blog?page=2 affiche la deuxième page de résultats ; les boutons précédent/suivant naviguent correctement ; la page 1 n’a pas de bouton « Précédent »
  3. Filtre par catégorie — cliquer sur un chip de catégorie filtre les articles ; l’URL est mise à jour vers ?category=<slug> ; le chip « Tous » supprime le filtre
  4. Affichage du détail d’un article/blog/[slug] affiche le titre, l’image à la une, le corps mis en forme, la carte auteur, le temps de lecture et la date de publication
  5. Table des matières — la page de détail d’un article génère une table des matières à partir des titres h2/h3 ; cliquer sur une entrée de la table des matières fait défiler la page jusqu’au titre correspondant
  6. Articles connexes — la page de détail affiche jusqu’à 3 articles connexes de la même catégorie (en excluant l’article courant)
  7. Page catégorie/blog/category/[slug] affiche le nom de la catégorie, sa description et uniquement les articles de cette catégorie
  8. Page auteur/blog/author/[slug] affiche la biographie de l’auteur, son avatar, ses liens sociaux et ses articles
  9. Boutons de partage social — les boutons de partage sont visibles sur la page de détail ; « Copier le lien » copie l’URL et affiche un texte de confirmation
  10. Flux RSSGET /blog/rss.xml renvoie un XML valide avec Content-Type: application/rss+xml et contient des entrées d’articles
  11. Navigation — le lien « Blog » dans le menu du site mène à /blog ; le fil d’Ariane affiche la bonne hiérarchie
  12. Article mis en avant/blog affiche l’article en vedette sous forme de grande carte « hero » au-dessus de la grille standard

Tests de cas limites et d’erreurs

  1. Article introuvable/blog/nonexistent-slug renvoie une page 404
  2. Catégorie vide — une catégorie sans articles affiche le message d’état vide blog_no_posts
  3. Image à la une manquante — la carte d’article s’affiche correctement sans image (aucune <img> cassée)
  4. Auteur/catégorie manquant·e — un article sans auteur ou sans catégorie s’affiche simplement sans ces éléments (sans crash)
  5. Feature flag désactivé — lorsque blog: false, /blog renvoie une 404

Tests transverses

  1. i18n : changement de langue — naviguer vers /fr/blog, vérifier que les chaînes d’interface (titre de page, « Lire la suite », libellés de pagination) sont en français ; le contenu de l’article revient à l’anglais s’il n’est pas traduit
  2. i18n : mise en page RTL — naviguer vers /ar/blog et /ar/blog/[slug], vérifier que dir="rtl" est défini et que la mise en page s’affiche correctement (table des matières du bon côté)
  3. SEO : balises meta — la page de détail d’un article contient og:type=article, og:title, og:description, article:published_time et un lien canonique
  4. SEO : JSON-LD — la page de détail d’un article contient un <script type="application/ld+json"> avec @type: BlogPosting, le titre, l’auteur et la datePublished
  5. SEO : sitemap/sitemap.xml inclut les URL des articles de blog avec les alternances hreflang
  6. Accessibilité — lancer des audits Axe sur /blog, /blog/[slug], /blog/author/[slug] pour les viewports mobile et desktop (en suivant le modèle de tests a11y existant)
  7. Navigation clavier — les entrées de la table des matières et les boutons de pagination sont focalisables et actionnables au clavier ; les indicateurs de focus sont visibles

Hors périmètre

  • Commentaires sur les articles de blog
  • Recherche en texte intégral
  • Intégration de newsletters au sein des articles
  • Analyses / nombre de vues
Ludo Fourrage
Ludo Fourrage

Ludo est un enseignant et entrepreneur dans le domaine de la tech, animé par une conviction simple : une éducation de qualité devrait être accessible à tous.