# General Translation Knowledge Base: Come si crea una mappa del sito per un sito web multilingue?
URL: https://generaltranslation.com/it/kb/multilingual-sitemap.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: Elenca ogni versione linguistica di ciascuna pagina come voce url a sé stante. Se la mappa del sito contiene gli hreflang, assegna a ogni voce alternative xhtml:link per tutte le versioni, compresa quella della voce stessa, più un x-default facoltativo, e suddividi i file al raggiungimento di 50.000 URL o 50 MB. gt-next di General Translation assegna a ogni impostazione regionale un URL dedicato, che la mappa del sito può elencare.
Category: SEO multilingue
Last updated: 2026-10-28
Authors: General Translation Team
Published: 2026-10-28

Una mappa del sito multilingue elenca ogni versione linguistica di una pagina come voce `<url>` a sé stante: `/en/pricing`, `/de/preise` e `/pt-BR/precos` sono quindi tre voci distinte. Se la mappa del sito dichiara anche gli hreflang, ogni voce elenca come alternative `xhtml:link` tutte le versioni di quella pagina, compresa quella della voce stessa. Google tratta gli hreflang presenti in una mappa del sito esattamente come quelli nell&#39;HTML, quindi la mappa del sito può sostituire i tag nelle tue pagine, a condizione che sia completa e coerente con i tuoi URL canonical.

## Hreflang va inserito nella mappa del sito o nell&#39;HTML?

La [guida alle versioni localizzate](https://developers.google.com/search/docs/specialty/international/localized-versions) di Google descrive tre metodi equivalenti per dichiarare le versioni linguistiche e precisa che usarne più di uno non offre alcun vantaggio nella Ricerca. Ogni URL linguistico che vuoi far indicizzare deve comunque comparire nella mappa del sito: la scelta riguarda solo dove collocare le annotazioni hreflang.

| Metodo                            | Dove si trova                    | Indicato quando                                                                                                                       | Attenzione a                                                     |
| --------------------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| Tag HTML `<link>`                 | Nell&#39;`<head>` di ogni pagina | Il tuo framework renderizza i metadati pagina per pagina, ad esempio con `alternates.languages` di Next.js                            | Ogni modello deve generare l&#39;insieme completo                |
| Intestazione HTTP `Link`          | Intestazioni della risposta      | File non HTML, come i PDF                                                                                                             | Configurazione del server per ogni tipo di file                  |
| `xhtml:link` nella mappa del sito | Una o più mappe del sito         | Un CMS o una fase di build conosce già tutte le pagine e le impostazioni regionali, oppure non puoi modificare i modelli delle pagine | Le pagine assenti dalla mappa del sito restano senza annotazioni |

Usa i tag HTML se il tuo framework li renderizza già pagina per pagina, e la mappa del sito se un unico punto della build conosce tutte le pagine e le relative impostazioni regionali. Non gestirli entrambi a mano: due elenchi separati finiscono inevitabilmente per non coincidere più. (Consulta [che cos&#39;è hreflang](/kb/what-is-hreflang) per la sintassi del tag e l&#39;intestazione HTTP.)

## Come strutturare una mappa del sito multilingue

Queste regole provengono dalla [guida alle versioni localizzate](https://developers.google.com/search/docs/specialty/international/localized-versions) di Google, dalla sua [guida alle mappe del sito](https://developers.google.com/search/docs/crawling-indexing/sitemaps/build-sitemap) e dal [protocollo delle mappe del sito](https://www.sitemaps.org/protocol.html):

* **Un `<url>` per versione.** Una pagina in tre lingue corrisponde a tre voci, ciascuna con il proprio `<loc>`.
* **Ogni voce elenca tutte le versioni, compresa se stessa.** L&#39;insieme di `xhtml:link` è identico in tutte le voci di una pagina e Google ignora una coppia di pagine che non si referenziano a vicenda.
* **`x-default` è facoltativo.** Aggiungilo se hai un selettore di lingua o una versione predefinita da mostrare ai visitatori la cui lingua non è disponibile.
* **Dichiara il namespace.** L&#39;elemento `<urlset>` richiede un attributo `xmlns:xhtml` impostato su `http://www.w3.org/1999/xhtml`.
* **Usa URL definitivi e canonical.** Devono essere assoluti (`https://example.com/de/preise`, non `/de/preise`), restituire `200` e non essere contrassegnati come `noindex`.
* **Codici validi.** Una lingua ISO 639-1, eventualmente seguita da una regione ISO 3166-1 alpha-2 (`en-GB`) o da un sistema di scrittura ISO 15924 (`zh-Hant`).
* **Esegui escape e codifica.** Il file è in UTF-8, `&` si scrive `&amp;` e i percorsi non ASCII vanno codificati in percentuale: `/de/über-uns` diventa quindi `/de/%C3%BCber-uns`.
* **Un `lastmod` per ogni versione.** Google lo utilizza solo se è sempre accurato, cioè se indica la data dell&#39;ultima modifica significativa a quell&#39;URL. Una traduzione tedesca aggiornata in un giorno diverso rispetto alla pagina inglese avrà una data propria.

Ogni file può contenere al massimo 50.000 URL e 50MB non compressi. I figli `xhtml:link` non rientrano nel limite di URL, ma incidono sulla dimensione del file. Con URL di circa 45 caratteri, ogni alternativa occupa circa 100 byte e una voce con 20 lingue circa 2,2KB. Secondo le nostre stime, un file raggiunge i 50MB con circa 23.700 voci di questo tipo, ben prima di arrivare a 50.000.

### Suddividere per impostazione regionale con un indice della mappa del sito

Per la maggior parte dei siti, un file per ogni impostazione regionale basta a mantenere i file di dimensioni ridotte, ma non garantisce di restare entro i limiti. Secondo la stima precedente, un&#39;impostazione regionale con più di circa 23.700 pagine in 20 lingue richiede più di un file: in questo caso, suddividila ulteriormente, ad esempio in `sitemap-de-1.xml` e `sitemap-de-2.xml`. Per far leggere a Google tutti i file elencati è sufficiente inviare l&#39;indice; inviare anche i singoli file è facoltativo e consente di avere una riga dedicata per ciascuno nel report Sitemap di Search Console. Un [indice della mappa del sito](https://developers.google.com/search/docs/crawling-indexing/sitemaps/large-sitemaps) elenca i file:

```xml title="sitemap.xml"
<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <sitemap>
    <loc>https://example.com/sitemap-en.xml</loc>
  </sitemap>
  <sitemap>
    <loc>https://example.com/sitemap-de.xml</loc>
  </sitemap>
  <sitemap>
    <loc>https://example.com/sitemap-pt-br.xml</loc>
  </sitemap>
</sitemapindex>
```

Ogni file per impostazione regionale elenca solo i propri URL, ma ciascuna voce contiene comunque l&#39;insieme completo delle alternative della pagina:

```xml title="sitemap-de.xml"
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
        xmlns:xhtml="http://www.w3.org/1999/xhtml">
  <url>
    <loc>https://example.com/de/preise</loc>
    <lastmod>2026-10-03</lastmod>
    <xhtml:link rel="alternate" hreflang="en" href="https://example.com/en/pricing"/>
    <xhtml:link rel="alternate" hreflang="de" href="https://example.com/de/preise"/>
    <xhtml:link rel="alternate" hreflang="pt-BR" href="https://example.com/pt-BR/precos"/>
    <xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/en/pricing"/>
  </url>
  <url>
    <loc>https://example.com/de/ueber-uns</loc>
    <lastmod>2026-09-12</lastmod>
    <xhtml:link rel="alternate" hreflang="en" href="https://example.com/en/about"/>
    <xhtml:link rel="alternate" hreflang="de" href="https://example.com/de/ueber-uns"/>
    <xhtml:link rel="alternate" hreflang="pt-BR" href="https://example.com/pt-BR/sobre"/>
    <xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/en/about"/>
  </url>
</urlset>
```

`sitemap-en.xml` e `sitemap-pt-br.xml` hanno la stessa struttura, ciascuno con i propri valori `<loc>` e `lastmod`. Tieni i file nella radice del sito: secondo Google, una mappa del sito copre solo gli URL presenti nella sua directory o nelle sottodirectory, a meno che non venga inviata tramite Search Console, e un indice può fare riferimento solo a mappe del sito che si trovano nella sua stessa directory o in quelle sottostanti.

## Come generarne una in Next.js

A partire dalla versione 14.2, Next.js genera le alternative `xhtml:link` dal campo `alternates.languages` di ogni voce in [`app/sitemap.ts`](https://nextjs.org/docs/app/api-reference/file-conventions/metadata/sitemap#generate-a-localized-sitemap). Questa versione costruisce le alternative a partire da un unico elenco di impostazioni regionali e di slug localizzati, così ogni voce include sé stessa e `x-default`. `i18n/routing.ts` contiene questi elenchi e definisce quali pagine vengono servite per ciascuna impostazione regionale; [la sezione su General Translation](#how-general-translation-fits) ne mostra una versione che legge `gt.config.json`.

```ts title="app/sitemap.ts"
import type { MetadataRoute } from 'next';
import {
  BASE_URL,
  DEFAULT_LOCALE,
  LOCALES,
  localeRoutes,
  pathConfig,
} from '@/i18n/routing';

// Percorsi condivisi delle pagine che vuoi indicizzare.
const PAGES = ['/', '/about', '/pricing'];

// Un'impostazione regionale serve una pagina, a meno che localeRoutes non la escluda.
const serves = (locale: string, page: string) =>
  !localeRoutes[locale] || localeRoutes[locale].includes(page);

// L'URL pubblico di una pagina in una data impostazione regionale, con lo slug localizzato, se presente.
function localeUrl(locale: string, page: string) {
  const path = pathConfig[page]?.[locale] ?? page;
  return `${BASE_URL}/${locale}${path === '/' ? '' : path}`;
}

export default function sitemap(): MetadataRoute.Sitemap {
  return PAGES.flatMap((page) => {
    const locales = LOCALES.filter((locale) => serves(locale, page));

    // Ogni versione della pagina, inclusa quella in <loc>, più x-default.
    const languages: Record<string, string> = Object.fromEntries(
      locales.map((locale) => [locale, localeUrl(locale, page)]),
    );
    languages['x-default'] = localeUrl(DEFAULT_LOCALE, page);

    // Un <url> per versione, ognuno con le stesse alternative.
    return locales.map((locale) => ({
      url: localeUrl(locale, page),
      alternates: { languages },
    }));
  });
}
```

Compilata con Next.js 16.4.3 e `gt-next` 11.4.16, la pagina `/about` ha generato quattro voci come questa, ciascuna con gli stessi cinque link:

```xml title="/sitemap.xml"
<url>
<loc>https://example.com/en/about</loc>
<xhtml:link rel="alternate" hreflang="en" href="https://example.com/en/about" />
<xhtml:link rel="alternate" hreflang="de" href="https://example.com/de/ueber-uns" />
<xhtml:link rel="alternate" hreflang="fr" href="https://example.com/fr/qui-sommes-nous" />
<xhtml:link rel="alternate" hreflang="pt-BR" href="https://example.com/pt-BR/about" />
<xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/en/about" />
</url>
```

Tre dettagli che la documentazione di Next.js non chiarisce:

* **Aggiungi l&#39;autoriferimento.** L&#39;esempio localizzato nella documentazione di Next.js elenca solo le alternative `es` e `de` sotto l&#39;URL inglese e non assegna voci proprie agli URL spagnolo e tedesco. Google si aspetta invece che ogni voce elenchi anche se stessa e che ogni versione abbia il proprio `<url>`.
* **Esegui tu l&#39;escape degli URL.** Nel nostro test, Next.js 16.4.3 ha scritto gli URL nell&#39;XML così com&#39;erano: un `&` in una stringa di query ha prodotto un file che `xmllint` rifiuta, e la `ü` non è stata codificata. Evita le stringhe di query negli URL della mappa del sito e costruisci i percorsi con `new URL(path, BASE_URL).href`, che applica la codifica percentuale.
* **La suddivisione non crea un indice.** [`generateSitemaps`](https://nextjs.org/docs/app/api-reference/functions/generate-sitemaps) può restituire un id per ogni impostazione regionale, e Next.js serve ciascun file su `/sitemap/en.xml`, `/sitemap/de.xml` e così via. Nel nostro test, però, `/sitemap.xml` restituiva 404: invia quindi ciascun file in Search Console, così da superare anche la restrizione di directory imposta da Google per i file all&#39;interno di `/sitemap/`.

## Errori comuni

* **Autoriferimenti o link di ritorno mancanti.** Se `/en/pricing` elenca `/de/preise` ma `/de/preise` non elenca `/en/pricing`, Google ignora la coppia. Di solito succede quando un&#39;impostazione regionale viene aggiunta solo ad alcune pagine.
* **Codici non validi.** `en-UK` non ha alcun effetto perché `UK` è un codice riservato; usa `en-GB`. `es-419` è un codice BCP 47 valido, ma non è supportato in hreflang; usa `es` o paesi specifici come `es-MX`.
* **URL con reindirizzamento.** Elenca l&#39;URL a cui il server risponde con `200`. Con gli slug dell&#39;esempio precedente, `/de/about` reindirizza a `/de/ueber-uns`, quindi nella mappa del sito va inserito solo lo slug localizzato.
* **Pagine `noindex` o non tradotte.** La mappa del sito è l&#39;elenco degli URL che vuoi far comparire nei risultati di ricerca. Escludi le impostazioni regionali non ancora pubblicate e rimuovile anche dalle alternative delle altre voci.
* **`lastmod` impostato sul momento della build.** Rigenerare la mappa del sito non equivale a modificare il contenuto. Usa la data dell&#39;ultima modifica di ciascuna versione oppure ometti il campo.

## Come verificarla prima dell&#39;invio

Esegui tre controlli prima di inviare la mappa del sito tramite il report Sitemap di Search Console o di aggiungere una riga `Sitemap:` a `robots.txt`:

1. **L&#39;XML è ben formato:** `xmllint --noout sitemap-de.xml`.
2. **Ogni gruppo è completo e reciproco.** Questo script legge uno o più file di mappa del sito e segnala le voci che non elencano sé stesse, le alternative prive di una propria voce, gli insiemi non corrispondenti e i codici non validi:

   ```python title="check_sitemap.py"
   import re
   import sys
   import xml.etree.ElementTree as ET

   NS = {
       's': 'http://www.sitemaps.org/schemas/sitemap/0.9',
       'x': 'http://www.w3.org/1999/xhtml',
   }
   # Lingua, sistema di scrittura facoltativo, regione facoltativa: intercetta es-419, ma non tutti i codici non validi.
   CODE = re.compile(r'^[a-z]{2}(-[a-z]{4})?(-[a-z]{2})?$', re.IGNORECASE)
   RESERVED = ('-uk', '-eu', '-un')

   # Legge ogni <url> da ogni file della mappa del sito: loc -> {hreflang: href}.
   entries = {}
   for path in sys.argv[1:]:
       for url in ET.parse(path).getroot().findall('s:url', NS):
           loc = url.findtext('s:loc', namespaces=NS).strip()
           entries[loc] = {
               link.get('hreflang'): link.get('href')
               for link in url.findall('x:link', NS)
               if link.get('rel') == 'alternate'
           }

   problems = []
   for loc, alternates in entries.items():
       if alternates and loc not in alternates.values():
           problems.append(f'{loc} does not list itself')
       for lang, href in alternates.items():
           if lang == 'x-default':
               continue
           if not CODE.match(lang) or lang.lower().endswith(RESERVED):
               problems.append(f'{loc} uses an invalid hreflang code: {lang}')
           if href not in entries:
               problems.append(f'{loc} lists {href}, which has no <url> entry')
           elif entries[href] != alternates:
               problems.append(f'{href} does not list the same alternates as {loc}')

   print('\n'.join(problems) or f'{len(entries)} URLs, no problems found')
   sys.exit(1 if problems else 0)
   ```

   Eseguilo su tutti i file delle impostazioni regionali in un&#39;unica volta, `python3 check_sitemap.py sitemap-en.xml sitemap-de.xml sitemap-pt-br.xml`; con l&#39;esempio precedente stampa `6 URLs, no problems found`.
3. **Ogni URL risponde con `200`:**

   ```bash
   curl -s https://example.com/sitemap-de.xml | grep -o '<loc>[^<]*' | sed 's/<loc>//' |
     while read -r url; do echo "$(curl -s -o /dev/null -w '%{http_code}' "$url") $url"; done
   ```

## Il ruolo di General Translation

`gt-next` di General Translation assegna a ogni impostazione regionale un proprio URL, ed è proprio questo che una mappa del sito multilingue elenca. [`createNextMiddleware`](/docs/react/nextjs/reference/functions/create-next-middleware) gestisce i percorsi `/de/...` e `/pt-BR/...`, `prefixDefaultLocale: true` colloca anche la lingua di origine sotto `/en/`, [`pathConfig`](/docs/react/nextjs/reference/functions/create-next-middleware#path-config) associa una pagina a uno slug localizzato e [`localeRoutes`](/docs/react/nextjs/reference/functions/create-next-middleware#locale-routes) limita le pagine servite da ciascuna impostazione regionale. Raccogli queste impostazioni in un unico modulo e importalo sia nel middleware sia nella mappa del sito: in questo modo la mappa del sito elencherà esattamente gli URL a cui risponde il middleware:

```ts title="i18n/routing.ts"
import gtConfig from '../gt.config.json';

export const BASE_URL = 'https://example.com';
export const DEFAULT_LOCALE = gtConfig.defaultLocale;

// Tutte le impostazioni regionali servite, da gt.config.json, a partire dalla lingua di origine.
export const LOCALES = [...new Set([gtConfig.defaultLocale, ...gtConfig.locales])];

// Condiviso con proxy.ts, in modo che la mappa del sito elenchi esattamente gli URL serviti dal middleware.
export const pathConfig: Record<string, Record<string, string>> = {
  '/about': { de: '/ueber-uns', fr: '/qui-sommes-nous' },
};
export const localeRoutes: Record<string, string[]> = {
  'pt-BR': ['/', '/about'], // la pagina dei prezzi non è ancora tradotta in portoghese brasiliano
};
```

```ts title="proxy.ts"
import { createNextMiddleware } from 'gt-next/middleware';
import { localeRoutes, pathConfig } from './i18n/routing';

export default createNextMiddleware({
  prefixDefaultLocale: true,
  pathConfig,
  localeRoutes,
});

export const config = {
  matcher: ['/((?!api|static|.*\\..*|_next).*)'],
};
```

Con `en`, `de`, `fr` e `pt-BR` elencati in `gt.config.json`, la build ha prodotto 11 voci: tre pagine in quattro impostazioni regionali, esclusa la pagina dei prezzi in portoghese brasiliano. Quell&#39;URL resta comunque accessibile ai visitatori, perché il middleware reindirizza `/pt-BR/pricing` a `/en/pricing` con un `307`, e un URL che reindirizza non va inserito in una mappa del sito. Tutti gli URL elencati hanno restituito `200` e il controllo riportato sopra non ha rilevato problemi. Il controllo `serves` confronta i percorsi esatti, quindi se le tue `localeRoutes` usano modelli come `/blog/[[...slug]]`, espandili nelle singole pagine che elenchi.

Se instradi un&#39;impostazione regionale tramite un alias, ad esempio `/cn` per il cinese, usa il codice canonical nella chiave hreflang e l&#39;alias nell&#39;URL; la [guida SEO sugli alias delle impostazioni regionali](/docs/react/nextjs/locale-alias-seo#sitemap) li converte con [`resolveCanonicalLocale`](/docs/react/nextjs/reference/functions/resolve-canonical-locale). Per i titoli e le descrizioni tradotti delle stesse pagine, consulta [Come traduco i titoli SEO e le meta description?](/kb/translate-seo-metadata).

## Punti chiave

* Assegna a ogni versione linguistica una propria voce `<url>` ed elenca tutti gli URL che vuoi far indicizzare, indipendentemente dal metodo hreflang che utilizzi.
* In una mappa del sito con hreflang, ogni voce elenca tutte le versioni della pagina, inclusa sé stessa, e l&#39;insieme è identico in ogni voce; `x-default` è facoltativo.
* Ogni file può contenere al massimo 50.000 URL e 50 MB; le alternative non contano ai fini del limite di URL, ma incidono sulla dimensione, quindi con molte impostazioni regionali si raggiunge prima il limite di dimensione.
* Suddividi la mappa per impostazione regionale (e ulteriormente per quelle più grandi) sotto un indice di mappe del sito nella radice del sito; invia l&#39;indice dopo aver verificato la reciprocità e i codici di stato.
* Con `gt-next`, condividi `pathConfig` e `localeRoutes` tra il middleware e `app/sitemap.ts`, in modo che la mappa del sito elenchi esattamente gli URL serviti dalla tua app.

## Related questions

- [Che cos'è hreflang e come si configura?](https://generaltranslation.com/it/kb/what-is-hreflang.mdx): Hreflang indica a Google quali URL sono versioni linguistiche o regionali della stessa pagina, così che possa mostrare quella giusta nei risultati di ricerca. Puoi aggiungerlo con tag link HTML, intestazioni HTTP Link o una mappa del sito: assicurati che ogni collegamento sia reciproco e usa codici validi come en-GB, non en-UK.
- [Come si configura la SEO per un'app Next.js multilingue?](https://generaltranslation.com/it/kb/multilingual-nextjs-seo.mdx): Assegna a ogni lingua un proprio URL con prefisso, come /en/about e /es/about, quindi pubblica html lang, un canonical autoreferenziale, alternative hreflang con x-default, un titolo e una descrizione tradotti e una voce nella mappa del sito per ogni lingua pubblicata. Contrassegna con noindex le versioni non pubblicate. gt-next di General Translation instrada ogni lingua verso il proprio URL e traduce i titoli con getGT.
- [Reindirizzamenti per lingua e SEO: URL, cookie o Accept-Language?](https://generaltranslation.com/it/kb/locale-routing-seo.mdx): Lascia che sia l'URL a stabilire la lingua di ogni pagina che vuoi far indicizzare, perché Googlebot non invia alcuna intestazione Accept-Language ed esegue la scansione soprattutto da indirizzi statunitensi. Usa la scelta salvata in un cookie e, in subordine, Accept-Language solo per decidere verso quale pagina reindirizzare una radice neutra come /, con un reindirizzamento temporaneo 302 o 307.
- [Sottodirectory, sottodominio o ccTLD per la SEO multilingue](https://generaltranslation.com/it/kb/multilingual-url-structure.mdx): Usa sottodirectory come example.com/de/ quando un unico prodotto condivide sito e processo di rilascio. Solo un ccTLD indica un paese, i sottodomini consentono un hosting separato, Google sconsiglia i parametri ?lang= e tratta i domini di fantasia come .io e .ai come generici.
- [Come si traducono i titoli SEO e le meta description?](https://generaltranslation.com/it/kb/translate-seo-metadata.mdx): Scrivi il titolo e la meta description di ogni versione linguistica nella lingua corrispondente, usando le parole che gli utenti di quel mercato cercano, spesso diverse da una traduzione letterale. Poi localizza i tag correlati, tra cui Open Graph, canonical, hreflang, html lang e dati strutturati. gt-next di General Translation traduce titoli e descrizioni all'interno di generateMetadata con getGT.

## Previous and next questions

- Previous question: [Come si aggiunge l'i18n a TanStack Start?](https://generaltranslation.com/it/kb/add-i18n-to-tanstack-start.mdx)
- Next question: [Come si localizza un'app Android con strings.xml?](https://generaltranslation.com/it/kb/localize-android-app.mdx)

## Sitemap

See the [Knowledge Base sitemap](https://generaltranslation.com/it/kb/sitemap.md) for every article.
