Hreflang in Next.js con traduzioni mancanti: un esempio verificato
Neaptide · 4 ottobre 2026 · 9 min di lettura
Un esperimento Next.js riproducibile e 18 URL pubblicati verificati: allineare hreflang, canonical, sitemap e selettore con traduzioni incomplete.
In questo articolo

Genera i link linguistici dalle traduzioni pubblicate della pagina, non da tutte le lingue del sito. Usa lo stesso registro di disponibilità per hreflang, sitemap e selettore. Il francese nella configurazione generale non dimostra che un determinato articolo abbia una versione francese.
Lo abbiamo verificato il 4 ottobre 2026. Una configurazione Next.js volutamente errata ha generato 14 link hreflang e 14 link di navigazione verso traduzioni mancanti, oltre a otto URL assenti nella sitemap. Usare la disponibilità della singola pagina ha eliminato tutte e tre le incoerenze. Separatamente, 18 URL pubblicati di Neaptide hanno superato i controlli descritti sotto. Nessuno dei due insiemi misura posizionamento o indicizzazione Google.
Due insiemi di dati con compiti diversi
Il campione pubblicato comprende tre percorsi in sei lingue: il servizio GEO, la guida al sito multilingue e l’articolo sul catalogo B2B. Abbiamo eseguito due passaggi HTTP, esaminato ogni pagina nel browser e provato un cambio lingua per URL. Una connessione è stata reimpostata; un nuovo tentativo registrato separatamente è riuscito. Il totale è di 37 tentativi e 36 risposte riuscite, senza nascondere il primo errore.
| Percorso | URL verificati | Metadati + sitemap | Cambi lingua |
|---|---|---|---|
| /services/geo | 6 | 6 / 6 | 6 / 6 |
| /blog/multilingual-website-guide | 6 | 6 / 6 | 6 / 6 |
| /blog/b2b-catalog-seo | 6 | 6 / 6 | 6 / 6 |
Tutti i 18 URL rispondevano con 200, mantenevano canonical propri e coincidevano con le annotazioni della sitemap. Ciascuno dichiarava sei alternative linguistiche e l’inglese come x-default. I 18 cambi provati conservavano il percorso. Si tratta di un’azione per URL di partenza, non di ogni coppia linguistica o dell’intero sito. Abbiamo controllato l’articolo B2B pubblicato, non la sua nuova bozza di ricerca locale.
Un esperimento controllato sulle traduzioni incomplete
Abbiamo creato un progetto App Router separato con Next.js 15.5.22, React 19.2.6 e Node.js v25.2.1. Prima delle misure abbiamo fissato tre profili: sei traduzioni, tre traduzioni e il solo originale inglese. Ogni fase comprende 18 combinazioni percorso/lingua: 10 pagine disponibili e otto assenti. Due passaggi HTTP prima e dopo hanno prodotto 72 osservazioni; tutte le 36 combinazioni sono state aperte anche nel browser.

Nella fase errata, tutti e tre gli output usavano l’elenco generale delle sei lingue. Dopo la correzione usavano i record delle traduzioni esistenti. Le pagine disponibili mantenevano canonical propri. Non abbiamo creato contenuti sostitutivi né reindirizzato le traduzioni mancanti alla home page.
| Profilo | Link hreflang errati: prima → dopo | Link del selettore errati: prima → dopo | URL assenti nella sitemap: prima → dopo |
|---|---|---|---|
| 6 lingue | 0 → 0 | 0 → 0 | 0 → 0 |
| 3 lingue | 9 → 0 | 9 → 0 | 3 → 0 |
| 1 lingua | 5 → 0 | 5 → 0 | 5 → 0 |
Le unità sono diverse: un link si conta per pagina di origine, un URL della sitemap per voce. Non sommarli come numero di pagine guaste. I due passaggi HTTP concordavano, come tutti i 36 controlli DOM con l’HTML. Cinque clic hanno confermato tre transizioni valide e due link volutamente errati diretti a una 404. Il test non misura la diffusione del difetto.
Usare un solo registro delle traduzioni disponibili
Nel progetto, TRANSLATIONS contiene solo contenuti pubblicati. availableLocales ne ricava le lingue e generateStaticParams crea quei percorsi. Con dynamicParams = false, le combinazioni assenti restano indisponibili; pagina e metadati verificano anche il record. L’estratto usa le stesse funzioni del progetto collaudato. L’archivio include entrambe le fasi: per la correzione, consulta il ramo after.
export const availableLocales = profile => Object.keys(TRANSLATIONS[profile] || {});
export const translation = (profile, locale) => TRANSLATIONS[profile]?.[locale];
export const pageUrl = (profile, locale, phase) => `${ORIGIN}/${locale}/${phase}/${profile}`;export async function generateMetadata({ params }) {
const { phase, profile, locale } = await params;
const content = translation(profile, locale);
if (!PHASES.includes(phase) || !content) notFound();
return {
title: content.title,
alternates: { canonical: pageUrl(profile, locale, phase), languages: languageAlternates(profile, phase) },
};
}Nel ramo after, languageAlternates restituisce solo equivalenti pubblicati. Aggiunge l’inglese come x-default ai profili multilingui e omette le annotazioni linguistiche per il solo originale inglese. È una scelta esplicita. Se l’originale è in un’altra lingua, scegli una destinazione esistente per quel gruppo. Se gli slug cambiano, memorizza gli URL equivalenti invece di sostituire un prefisso; gli slug tradotti non sono stati testati qui.
Canonical, hreflang e navigazione hanno ruoli distinti
Canonical identifica l’URL preferito; hreflang collega equivalenti localizzati. Le nostre traduzioni usano canonical propri. Il selettore deve portare allo stesso contenuto in una versione disponibile. Un eventuale link esplicito alla home page di un’altra lingua è normale navigazione, non la traduzione della pagina mancante.
Google descrive link linguistici assoluti e reciproci, inclusa l’autoreferenza. HTML e sitemap sono metodi alternativi; li abbiamo confrontati perché il progetto usa entrambi.
Quando una traduzione non è ancora pronta
Rimuovi la versione assente dal gruppo di traduzioni e dal selettore delle pagine equivalenti. Tieni le bozze fuori dalla sitemap pubblica. Se offri l’originale inglese, indica la lingua e collega il vero URL inglese. Una risposta 200 con testo inglese sotto un percorso francese non dimostra che esista una traduzione francese.
Nel nostro progetto statico, tutte le otto combinazioni assenti restituivano 404 e noindex in entrambe le fasi. Abbiamo verificato le risposte effettive. Un’implementazione dinamica o in streaming richiede controlli propri: la sola presenza di notFound() nel codice non è un’osservazione dello stato HTTP.
Verificare il risultato dopo ogni nuova traduzione
- Apri ogni equivalente pubblicato e controlla stato, URL finale, lingua e canonical.
- Confronta ogni alternativa dichiarata con il registro disponibile, poi controlla destinazione e link di ritorno.
- Confronta le voci della sitemap con il contenuto pubblicato. Se contiene annotazioni linguistiche, verifica che coincidano con l’HTML.
- Usa il selettore nelle pagine interessate. Prova sia una traduzione disponibile sia il comportamento con una mancante.
- Ripeti dopo la pubblicazione o il ritiro di una traduzione. Conserva data, percorso e prove; osserva l’indicizzazione separatamente in Search Console.
Riprodurre il test
Scarica progetto sorgente, osservazioni e metodo completo. Lo ZIP esclude dipendenze e output compilato. Compila e avvia il progetto sulla porta indicata, poi esegui il raccoglitore HTTP. I controlli originali nel browser sono documentati separatamente; il raccoglitore non li ripete.
È una verifica tecnica svolta nello stesso giorno da un solo ricercatore. Copre tre percorsi pubblicati e casi statici controllati, non la qualità di tutte le traduzioni, i canonical scelti da Google, l’indicizzazione, le posizioni, il traffico o le citazioni dell’IA.