Travail · 2026
Cibamus : des recettes, quatre langues et des données structurées faites correctement
Un site de recettes sous Hugo, en italien, anglais, français et espagnol. Chaque page émet du JSON-LD schema.org/Recipe, chaque langue a son URL riche en mots-clés, et la pile analytique ne se charge qu'en production.
Cibamus publie de la cuisine familiale italienne en quatre langues — italien, anglais, français et espagnol — avec plus de 130 recettes en ligne. C’est un site statique sous Hugo, et presque chaque décision qu’il contient découle d’une seule question : de quoi une recette a-t-elle besoin pour être trouvée, et pas seulement pour être lisible ?
Une recette, quatre URL, une identité
La façon naïve de faire un site de recettes multilingue consiste à traduire la page en gardant l’URL. C’est une erreur qu’on ne remarque que des mois plus tard, quand la page française ne se positionne sur rien de ce qu’un Français taperait réellement.
Chaque recette vit dans un bundle Hugo portant un nom de base court et stable, avec un fichier par langue à l’intérieur :
content/ricette/risotto-ai-funghi/
index.it.md
index.en.md
index.fr.md
index.es.md
risotto-ai-funghi.webp
Le dossier, l’image partagée et le translationKey utilisent tous ce nom de
base neutre, et c’est lui qui relie les quatre versions pour le hreflang. Le
slug, lui, fait l’inverse : il est riche en mots-clés et localisé par
langue, si bien qu’un plat devient ricetta-…, …-recipe, recette-…,
receta-…, et une préparation de base come-fare-…, how-to-make-…, et ainsi
de suite. Les catégories et les cuisines sont traduites elles aussi. Les champs
neutres — dates, temps, valeurs nutritionnelles, badges, ingrédients, ustensiles
— restent identiques dans les quatre langues, parce qu’ils décrivent le plat et
non la langue.
Cette séparation, c’est tout le projet : l’identité est partagée, l’adresse est locale.
Le JSON-LD comme sortie, pas comme pensée après coup
Chaque page de recette émet schema.org/Recipe en JSON-LD, généré depuis le
front-matter plutôt qu’écrit à la main. Temps, portions, ingrédients, valeurs
nutritionnelles et image proviennent tous de champs qui doivent déjà être justes
pour que la page elle-même s’affiche.
C’est la partie du projet la plus proche de mon métier. Les données structurées sont l’endroit où un site de contenu obtient des résultats enrichis ou, en silence, n’en obtient pas — et la façon dont cela casse est toujours la même : un balisage maintenu séparément du contenu qu’il décrit, qui s’en éloigne une modification à la fois. Le dériver de la même source supprime cette possibilité.
Les ustensiles recommandés suivent la même règle. Ils sont définis une seule
fois dans data/tools.yaml, avec leurs libellés dans les quatre langues, et
appelés depuis les recettes par leur nom. La description d’un ustensile existe à
un seul endroit, pas recopiée dans quarante recettes.
Une page de recette. Temps, portions et ingrédients s’affichent ici et alimentent le JSON-LD depuis les mêmes champs.
Une analytique qui reste à sa place
Google Tag Manager ne se charge qu’en production, conditionné au IsProduction
de Hugo ; GA4 tourne comme balise à l’intérieur de GTM plutôt que comme second
script. La Content Security Policy est stricte par défaut et ne s’élargit *qu’*en
production, et uniquement vers les domaines GTM et Analytics dont elle a
réellement besoin.
La raison de ce verrou n’est pas une posture sur la vie privée, c’est la qualité de la donnée : un serveur de développement local qui déclenche les mêmes balises que la production pollue exactement les chiffres pour lesquels le suivi a été mis en place.
Build et publication
Le site se construit en local et se publie sur Hostinger via rsync et SSH, avec
une page 404 multilingue servie par le .htaccess. En parallèle, un workflow
GitHub Actions construit et publie sur GitHub Pages à chaque push : il existe
comme vérification continue que le build fonctionne toujours, pas comme site en
production.
Ce workflow est épinglé : actions référencées par SHA de commit plutôt que par étiquette mobile, et binaire Hugo vérifié par somme de contrôle avant exécution. Une chaîne de build qui tire une étiquette mutable est une chaîne d’approvisionnement que vous ne contrôlez pas.
Là où l’IA intervient, et là où elle n’intervient pas
Les nouvelles recettes peuvent partir d’un JSON généré par IA, importé via un script défensif qui valide le fichier avant d’écrire quoi que ce soit dans l’arborescence de contenu.
Les traductions ne sont pas une sortie automatique. C’est une étape éditoriale, menée à part, pour la même raison que les slugs sont localisés plutôt que traduits : une recette qui se lit comme une traduction n’est pas cuisinée deux fois.