Andrea Grossi
← Torna ai progetti

Lavoro · 2026

Cibamus: ricette, quattro lingue e dati strutturati fatti come si deve

Un sito di ricette in Hugo, in italiano, inglese, francese e spagnolo. Ogni pagina emette JSON-LD schema.org/Recipe, ogni lingua ha il suo URL keyword-rich, e lo stack di analytics si carica solo in produzione.

Cibamus pubblica cucina di casa italiana in quattro lingue — italiano, inglese, francese e spagnolo — con oltre 130 ricette online. È un sito statico in Hugo, e quasi ogni decisione al suo interno nasce da una domanda sola: di cosa ha bisogno una ricetta per essere trovata, non solo per essere leggibile?

Una ricetta, quattro URL, una identità

Il modo ingenuo di fare un sito di ricette multilingua è tradurre la pagina e tenere l’URL. È un errore che noti mesi dopo, quando la pagina francese non si posiziona su niente di quello che un francese digiterebbe davvero.

Ogni ricetta vive in un bundle Hugo che porta un nome base corto e stabile, con un file per lingua dentro:

content/ricette/risotto-ai-funghi/
  index.it.md
  index.en.md
  index.fr.md
  index.es.md
  risotto-ai-funghi.webp

La cartella, l’immagine condivisa e il translationKey usano quel nome base neutro, ed è quello a legare insieme le quattro versioni per l’hreflang. Lo slug fa l’opposto: è keyword-rich e localizzato per lingua, così un piatto diventa ricetta-…, …-recipe, recette-…, receta-…, e una preparazione di base diventa come-fare-…, how-to-make-… e così via. Anche categorie e cucine sono tradotte. I campi neutri — date, tempi, valori nutrizionali, badge, ingredienti, strumenti — restano identici tra le quattro, perché descrivono il piatto, non la lingua.

Quella separazione è tutto il progetto: l’identità è condivisa, l’indirizzo è locale.

Il JSON-LD come output, non come ripensamento

Ogni pagina ricetta emette schema.org/Recipe in JSON-LD, generato dal frontmatter invece che scritto a mano. Tempi, porzioni, ingredienti, valori nutrizionali e immagine arrivano tutti da campi che devono già essere corretti perché la pagina stessa si disegni.

È la parte del progetto più vicina al mio lavoro quotidiano. I dati strutturati sono il punto in cui un sito di contenuti ottiene i rich result oppure, in silenzio, no — e il modo in cui si rompe è sempre lo stesso: markup mantenuto separatamente dal contenuto che descrive, che divergono una modifica alla volta. Derivarlo dalla stessa fonte rende quella possibilità inesistente.

Gli strumenti consigliati seguono la stessa regola. Si definiscono una volta sola in data/tools.yaml, con le etichette in tutte e quattro le lingue, e si richiamano dalle ricette per nome. La descrizione di uno strumento esiste in un posto, non copiata dentro quaranta ricette.

Una pagina ricetta Una pagina ricetta. Tempi, porzioni e ingredienti si disegnano qui e alimentano il JSON-LD dagli stessi campi.

Analytics che stanno al loro posto

Google Tag Manager si carica solo in produzione, con un controllo su IsProduction di Hugo; GA4 gira come tag dentro GTM invece che come secondo script. La Content Security Policy è stretta di default e si allarga solo in produzione, e solo verso i domini GTM e Analytics che le servono davvero.

Il motivo del controllo non è privacy di facciata, è qualità del dato: un server di sviluppo locale che spara gli stessi tag della produzione inquina esattamente i numeri per cui il tracciamento è stato messo.

Build e pubblicazione

Il sito si builda in locale e si pubblica su Hostinger via rsync e SSH, con una 404 multilingua servita dall’.htaccess. In parallelo, un workflow GitHub Actions builda e pubblica su GitHub Pages a ogni push: esiste come verifica continua che il build funzioni ancora, non come sito live.

Quel workflow è pinnato: action referenziate per commit SHA invece che per tag mobile, e il binario di Hugo verificato con il checksum prima di girare. Una pipeline di build che tira un tag mutabile è una catena di fornitura che non controlli.

Dove entra l’IA, e dove no

Le ricette nuove possono partire da JSON generato dall’IA, importato con uno script difensivo che valida il file prima di scrivere qualsiasi cosa dentro l’albero dei contenuti.

Le traduzioni non sono output automatico. Sono un passaggio editoriale, fatto a parte, per la stessa ragione per cui gli slug sono localizzati e non tradotti: una ricetta che si legge come una traduzione non viene cucinata due volte.