Andrea Grossi
← Retour aux projets

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 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.