**TL;DR.** Hreflang indica ai motori di ricerca quale variante locale servire a ciascun utente. Assicurati di fare quattro cose corrette: (1) cluster reciproci completi con auto-riferimenti, (2) codici lingua ISO corretti, (3) fallback x-default, (4) gating per entità in modo da non pubblicizzare traduzioni inesistenti. Tutto il resto è dettaglio di implementazione.

## Cosa fa hreflang

Hreflang è un attributo HTML (e annotazione equivalente nella sitemap) che segnala a Google quale lingua e regione una pagina targetizza. Hreflang implementato correttamente:

- Serve agli utenti spagnoli il tuo URL spagnolo, non quello inglese.
- Previene che il tuo URL francese superi il tuo URL tedesco in Germania.
- Consolida l'equità dei link tra le varianti locali invece di trattarle come contenuti duplicati.

Hreflang non aumenta i ranking. Controlla quale variante locale si posiziona dove.

## Passo 1: Decidi dove emettere hreflang

Tre opzioni:

| Metodo               | Pro                                           | Contro                                       |
| -------------------- | --------------------------------------------- | -------------------------------------------- |
| Testa HTML           | Facile da debug; una fonte per pagina        | Aggiunge byte a ogni risposta HTML           |
| Intestazione HTTP `Link` | Funziona per risorse non HTML (PDF, ecc.)  | Difficile da debug; raramente necessario     |
| Sitemap XML          | Più compatto per cataloghi enormi            | Meno visibile per gli esseri umani; richiede disciplina nella sitemap |

Il default per la maggior parte dei siti e-commerce nel 2026 è **Testa HTML**. Passa a sitemap solo sopra 50.000 URL.

## Passo 2: Emetti un cluster completo

Per ogni URL tradotto, emetti hreflang per se stesso e per ogni altra variante.

Un cluster a 4 lingue (en, de, fr, es) per un prodotto:

```html
<link rel="alternate" hreflang="en" href="https://example.com/en/products/leather-bag" />
<link rel="alternate" hreflang="de" href="https://example.com/de/products/leather-bag" />
<link rel="alternate" hreflang="fr" href="https://example.com/fr/products/leather-bag" />
<link rel="alternate" hreflang="es" href="https://example.com/es/products/leather-bag" />
<link rel="alternate" hreflang="x-default" href="https://example.com/en/products/leather-bag" />
```

Questo cluster appare nell'intestazione di **ogni** variante locale di questo prodotto. L'URL tedesco emette lo stesso cluster, l'URL francese emette lo stesso cluster, ecc.

Il cluster deve essere reciproco — A punta a B, B punta a A. I cluster non reciproci vengono ignorati silenziosamente da Google.

## Passo 3: x-default

`x-default` è il fallback per gli utenti la cui lingua del browser non corrisponde a nessuna delle tue varianti. Tipicamente il tuo URL inglese (o URL di default).

```html
<link rel="alternate" hreflang="x-default" href="https://example.com/en/products/leather-bag" />
```

Lo stesso URL può essere sia `hreflang="en"` che `hreflang="x-default"`. Solo un URL nel cluster ottiene `x-default`.

## Passo 4: Filtra per entità effettivamente tradotte

Il più grande errore pratico: emettere un cluster a 9 lingue su un prodotto tradotto solo in 3 lingue. Gli URL pubblicizzati restituiscono 404; Google vede il cluster rotto e potrebbe ignorare l'hreflang dell'intero sito.

La soluzione: `availableLocales` per entità.

```ts
// Pseudocodice
interface ProductForSeo {
  slug: string;
  availableLocales: string[];  // e.g. ["en", "de", "fr"]
}

interface StoreForSeo {
  supportedLocales: string[];  // e.g. ["en", "de", "fr", "es", "it"]
  defaultLocale: string;
}

function buildHreflangCluster(product: ProductForSeo, store: StoreForSeo) {
  const effective = product.availableLocales.filter((l) =>
    store.supportedLocales.includes(l)
  );
  return effective.map((locale) => ({
    hreflang: locale,
    href: `https://example.com/${locale}/products/${product.slug}`,
  }));
}
```

Il `src/lib/seo/storefront.ts` di Ordiko implementa esattamente questa intersezione.

## Passo 5: Codici lingua corretti

Usa i codici lingua **ISO 639-1**:

| Lingua                  | Codice    |
| ----------------------- | --------- |
| Inglese                 | `en`      |
| Spagnolo                | `es`      |
| Francese                | `fr`      |
| Tedesco                 | `de`      |
| Portoghese              | `pt`      |
| Italiano                | `it`      |
| Russo                   | `ru`      |
| Ucraino                 | `uk`      |
| Cinese (semplificato)   | `zh-Hans` |
| Cinese (tradizionale)   | `zh-Hant` |
| Giapponese              | `ja`      |
| Coreano                 | `ko`      |
| Arabo                   | `ar`      |

Per lingua + regione (quando servi una variante locale-regione):

| Variante                | Codice    |
| ----------------------- | --------- |
| Inglese US              | `en-US`   |
| Inglese UK              | `en-GB`   |
| Inglese canadese        | `en-CA`   |
| Spagnolo messicano      | `es-MX`   |
| Spagnolo spagnolo       | `es-ES`   |
| Francese francese       | `fr-FR`   |
| Francese canadese       | `fr-CA`   |
| Portoghese brasiliano   | `pt-BR`   |
| Portoghese portoghese   | `pt-PT`   |

Non usare:

- `en-EN` (non valido)
- `gb` (usa `en-GB`)
- `cn` (usa `zh-Hans`)
- `kr` (usa `ko`)

## Passo 6: Convalida

Tre controlli:

1. **Auto-riferimento**: ogni URL deve includere se stesso nel proprio cluster.
2. **Reciprocità**: ogni variante nel cluster deve includere ogni altra variante.
3. **Raggiungibilità**: ogni URL nel cluster deve restituire 200.

Strumenti:

| Strumento                        | Cosa controlla                              |
| ------------------------------- | ------------------------------------------- |
| Google Search Console           | Errori hreflang nel mondo reale nel tempo  |
| [hreflang.org/validator](https://hreflang.org) | Correttezza del cluster                    |
| Sitebulb / Screaming Frog       | Reciproci e auto-riferimenti               |
| Ahrefs / Semrush                | Report hreflang nell'audit del sito        |
| `curl` manuale                  | Raggiungibilità per URL                    |

Pattern di test Vitest per CI:

```ts
import { describe, it, expect } from 'vitest';

describe('hreflang cluster', () => {
  it('ogni URL variante restituisce 200', async () => {
    const cluster = [
      'https://example.com/en/products/x',
      'https://example.com/de/products/x',
      'https://example.com/fr/products/x',
    ];
    for (const url of cluster) {
      const res = await fetch(url);
      expect(res.status).toBe(200);
    }
  });

  it('ogni variante emette hreflang auto-riferente', async () => {
    for (const url of cluster) {
      const html = await fetch(url).then((r) => r.text());
      expect(html).toContain(`hreflang="${getLocale(url)}" href="${url}"`);
    }
  });
});
```

## Casi speciali

**App a pagina singola**: emetti hreflang lato server nell'intestazione del documento. Non fare affidamento sull'iniezione lato client — il crawler di Google potrebbe non eseguire il tuo JS.

**ccTLD**: hreflang si applica comunque. `example.de` può specificare `hreflang="de-DE"` per se stesso e collegarsi a `example.com/en/...` per `en`.

**Regione senza differenza linguistica**: se servi `en-US` e `en-GB` con contenuti identici, hreflang li differenzia. Non condividere l'URL — dai a ciascuno il proprio URL anche se il contenuto è identico.

## Errori comuni

| Errore                                   | Effetto                                           |
| ---------------------------------------- | ------------------------------------------------- |
| Cluster non reciproco                    | Google ignora completamente hreflang              |
| URL variante restituisce 404             | Google ignora l'URL; potrebbe demotare il cluster |
| Codice lingua errato                     | Variante trattata come non targetizzata           |
| Più URL x-default                        | Uno viene ignorato arbitrariamente                |
| Hreflang solo su URL canonico            | Le sotto-pagine non hanno segnale hreflang       |
| Mischiare URL assoluti e relativi        | Usa sempre URL assoluti                          |
| Dominio diverso per locale senza hreflang coerente | Il cluster si rompe                       |

## Come Ordiko emette hreflang

`src/lib/seo/storefront.ts` calcola `alternates.languages` per ogni oggetto di metadati della pagina Next.js. La funzione prende:

- `store.supportedLocales`
- `entity.availableLocales` (opzionale, predefinito a store.supportedLocales)
- `store.defaultLocale` (usato per x-default)

E emette il cluster di intersezione nell'intestazione HTML. Auto-riferimento, reciprocità e x-default sono garantiti dall'implementazione. Il gating per entità è facoltativo tramite il campo `availableLocales` nel caricatore dell'entità.

## FAQ

**Testa HTML o sitemap — quale è meglio?**
La testa HTML è più facile da debug perché puoi verificare con Visualizza sorgente. La sitemap è più compatta per i negozi con oltre 50k URL perché hreflang viene emesso una volta per coppia di URL, non in ogni intestazione di pagina. Scegli HTML per negozi sotto 50k URL; sitemap sopra. Ordiko emette per default la testa HTML.

**Quale codice lingua devo usare per lo spagnolo latinoamericano rispetto allo spagnolo spagnolo?**
es-MX per il Messico, es-AR per l'Argentina, es-CO per la Colombia, es-ES per la Spagna. La combinazione lingua-regione è supportata. Per una singola locale latinoamericana che serve più paesi, es-419 (il codice ONU per America Latina e Caraibi) è valido ma meno ampiamente supportato.

**Posso avere più URL x-default?**
No. Uno x-default per cluster. Se la tua attività serve l'inglese a livello globale, il .com (o il tuo URL di default) è l'x-default. Lo stesso URL può essere sia hreflang='en' che hreflang='x-default'.

**Come gestisce Ordiko la correttezza di hreflang?**
La libreria SEO del negozio di Ordiko (src/lib/seo/storefront.ts) interseca gli availableLocales di ciascuna entità con le lingue supportate dal negozio quando emette alternates.languages. Un prodotto tradotto solo in en + de pubblicizzerà solo quelle due lingue, indipendentemente dall'insieme di lingue più ampio del negozio.