Come ottimizzare la SEO per un'app Next.js multilingue
Perché la SEO multilingue richiede un'attenzione particolare
La maggior parte degli sviluppatori che aggiunge l'i18n alla propria app Next.js si concentra sulla traduzione della UI e considera il lavoro finito. Ma se i motori di ricerca non riescono a trovare, indicizzare e associare correttamente le diverse versioni linguistiche, tutto quel lavoro di traduzione rimane invisibile.
Un sito multilingue senza una configurazione SEO adeguata presenta problemi:
- Google potrebbe indicizzare solo una versione linguistica e ignorare le altre
- Gli utenti che cercano in spagnolo si ritrovano sulla pagina in inglese
- Penalizzazioni per contenuti duplicati perché Google considera
/en/aboute/fr/aboutcome la stessa pagina - Lingua errata mostrata negli snippet dei risultati di ricerca
La buona notizia: configurare correttamente la SEO multilingue in Next.js non è complicato. Ci sono cinque aspetti che devi gestire correttamente, e questa guida li copre tutti usando gt-next.
1. Routing degli URL basato sul locale
Le basi della SEO multilingue consistono nell'avere URL distinti per ogni lingua. I motori di ricerca hanno bisogno di URL separati e indicizzabili per indicizzare in modo indipendente ogni versione linguistica.
Questo significa locale nell'URL — non i cookie, non i parametri di query, non il solo rilevamento di Accept-Language.
✅ generaltranslation.com/en/about
✅ generaltranslation.com/fr/about
✅ generaltranslation.com/es/about
❌ generaltranslation.com/about?lang=fr
❌ generaltranslation.com/about (con locale in un cookie)
Configurazione del routing delle impostazioni locali con gt-next
Per prima cosa, inserisci le tue pagine all'interno di un segmento dinamico [locale]:
app/
└── [locale]/
├── layout.tsx
├── page.tsx
└── about/
└── page.tsx
Quindi crea un middleware nella directory principale del progetto (proxy.ts per Next.js 16+ o middleware.ts per Next.js 15 e versioni precedenti):
import { createNextMiddleware } from 'gt-next/middleware';
export default createNextMiddleware();
export const config = {
matcher: ['/((?!api|static|.*\\..*|_next).*)'],
};Questo genera automaticamente URL con prefisso basato sul locale.
Per impostazione predefinita, il locale predefinito (ad es. inglese) non ha un prefisso — /about rimane invariato,
mentre gli utenti spagnoli vedono /es/about e quelli francesi vedono /fr/about.
2. Impostare l'attributo lang di HTML
L'attributo lang sull'elemento <html> indica ai browser e ai motori di ricerca in quale lingua è la pagina.
È una delle azioni più semplici e al tempo stesso più incisive che puoi intraprendere per l'accessibilità e la SEO.
Senza di esso, gli screen reader devono indovinare la lingua (spesso sbagliando) e i motori di ricerca hanno meno sicurezza nella loro classificazione della lingua.
gt-next fornisce l'hook useLocale che rende questa operazione molto semplice nel tuo root layout:
import { useLocale, GTProvider } from 'gt-next';
export default function RootLayout({ children }: { children: React.ReactNode }) {
const locale = useLocale();
return (
<html lang={locale}>
<body>
<GTProvider>
{children}
</GTProvider>
</body>
</html>
);
}useLocale restituisce il codice locale BCP 47 (ad esempio en-US, ar, zh-Hans).
3. URL canoniche
I tag canonici indicano ai motori di ricerca quale URL rappresenta la versione "principale" di una pagina. Per i siti multilingue, ogni versione linguistica dovrebbe indicare il proprio URL come canonico:
<!-- Su /fr/about -->
<link rel="canonical" href="https://example.com/fr/about" />In questo modo i motori di ricerca non trattano la tua pagina in francese come un duplicato della tua pagina in inglese.
In Next.js imposti i tag canonical tramite la metadata API.
Combinala con getLocale di gt-next per generare il tag canonical corretto per ogni locale:
import { getLocale } from 'gt-next/server';
const BASE_URL = 'https://example.com';
export async function generateMetadata() {
const locale = await getLocale();
return {
alternates: {
canonical: `${BASE_URL}/${locale}/about`,
},
};
}
export default function AboutPage() {
return <h1>Chi siamo</h1>;
}Per la locale predefinita senza prefisso, adegua di conseguenza:
import { getLocale, getDefaultLocale } from 'gt-next/server';
export async function generateMetadata() {
const locale = await getLocale();
const defaultLocale = getDefaultLocale();
const path = '/about';
const prefix = locale === defaultLocale ? '' : `/${locale}`;
return {
alternates: {
canonical: `${BASE_URL}${prefix}${path}`,
},
};
}4. Tag hreflang
I tag hreflang sono il segnale più importante per la SEO multilingue. Indicano ai motori di ricerca: «questa pagina esiste in queste altre lingue e questi sono gli URL».
Senza hreflang, Google deve indovinare quale versione linguistica mostrare nei risultati di ricerca — e spesso sbaglia.
<link rel="alternate" hreflang="en" href="https://example.com/about" />
<link rel="alternate" hreflang="fr" href="https://example.com/fr/about" />
<link rel="alternate" hreflang="es" href="https://example.com/es/about" />
<link rel="alternate" hreflang="x-default" href="https://example.com/about" />Il tag x-default indica ai motori di ricerca quale URL mostrare quando nessuna delle lingue specificate corrisponde alle preferenze dell'utente.
In Next.js, puoi aggiungere hreflang tramite la proprietà alternates.languages della metadata API.
Ecco un helper riutilizzabile che funziona con gt-next:
import { getLocale, getDefaultLocale } from 'gt-next/server';
const BASE_URL = 'https://example.com';
const SUPPORTED_LOCALES = ['en', 'fr', 'es'];
export async function getI18NMetadata(path: string) {
const locale = await getLocale();
const defaultLocale = getDefaultLocale();
const getUrl = (loc: string) => {
const prefix = loc === defaultLocale ? '' : `/${loc}`;
return `${BASE_URL}${prefix}${path}`;
};
const languages: Record<string, string> = {};
for (const loc of SUPPORTED_LOCALES) {
languages[loc] = getUrl(loc);
}
languages['x-default'] = getUrl(defaultLocale);
return {
alternates: {
canonical: getUrl(locale),
languages,
},
};
}Quindi usalo in qualsiasi pagina:
import { getI18NMetadata } from '@/lib/i18n-metadata';
export async function generateMetadata() {
return await getI18NMetadata('/about');
}Questo genera sia il tag canonico che tutti i tag hreflang in una sola chiamata.
5. Metadati tradotti
I motori di ricerca mostrano il titolo e la descrizione della tua pagina nei risultati. Se questi sono in inglese per una pagina in francese, è meno probabile che gli utenti facciano clic — e Google potrebbe penalizzare il risultato.
Usa la funzione getGT di gt-next per tradurre le stringhe di metadati:
import { getGT } from 'gt-next/server';
import { getI18NMetadata } from '@/lib/i18n-metadata';
export async function generateMetadata() {
const t = await getGT();
const i18nMeta = await getI18NMetadata('/about');
return {
title: t('Chi siamo'),
description: t('Scopri di più sulla nostra missione e sul nostro team.'),
...i18nMeta,
};
}Questo ti permette di avere titoli e descrizioni localizzati nei risultati di ricerca, il che migliora significativamente la percentuale di clic per le ricerche non in inglese.
6. Sitemap multilingue
Una sitemap aiuta i motori di ricerca a scoprire tutte le tue pagine, incluse le versioni in ogni lingua. Per i siti multilingue, dovresti includere anche le annotazioni hreflang nella sitemap.
Next.js supporta la generazione di sitemap programmatiche tramite un file sitemap.ts:
import { MetadataRoute } from 'next';
const BASE_URL = 'https://example.com';
const LOCALES = ['en', 'fr', 'es'];
const DEFAULT_LOCALE = 'en';
const PAGES = ['/', '/about', '/blog', '/contact'];
export default function sitemap(): MetadataRoute.Sitemap {
return PAGES.flatMap((path) => {
const getUrl = (locale: string) => {
const prefix = locale === DEFAULT_LOCALE ? '' : `/${locale}`;
return `${BASE_URL}${prefix}${path === '/' ? '' : path}`;
};
const languages: Record<string, string> = {};
for (const locale of LOCALES) {
languages[locale] = getUrl(locale);
}
return LOCALES.map((locale) => ({
url: getUrl(locale),
lastModified: new Date(),
alternates: { languages },
}));
});
}Questo genera una sitemap con una voce per ogni pagina e per ogni locale, e ogni voce include annotazioni hreflang che fanno riferimento a tutte le versioni linguistiche.
Checklist
Ecco un breve riepilogo di tutto ciò che abbiamo visto:
| Requisito SEO | Implementazione |
|---|---|
| Locale nell'URL | createNextMiddleware() con segmento dinamico [locale] |
Attributo HTML lang | useLocale() nel root layout |
| URL canoniche | getLocale() + Next.js metadata API alternates.canonical |
| Tag hreflang | Next.js metadata API alternates.languages con tutte le varianti locali supportate |
| Metadati tradotti | getGT() per i titoli e le descrizioni delle pagine |
| Sitemap multilingue | sitemap.ts con voci per ogni locale e alternates hreflang |
Prossimi passi
- gt-next quickstart per configurare l'intero stack di i18n
- Guida al middleware per la configurazione del routing
- Guida SSG per generare pagine multilingue in modo statico
- Supporto RTL per le lingue da destra a sinistra