# General Translation Knowledge Base: Best practice per il selettore della lingua: UX, SEO e accessibilità
URL: https://generaltranslation.com/it/kb/language-switcher.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: Indica ogni lingua con il suo nome nativo, ad esempio Deutsch o 日本語, ed evita le bandiere, che rappresentano paesi. Collega ogni opzione alla stessa pagina in quella lingua tramite un link scansionabile, memorizza la scelta in un cookie e non reindirizzare mai automaticamente un URL localizzato.
Category: Internazionalizzazione
Last updated: 2026-10-20
Authors: Kevin Wu, Jackie Chen
Published: 2026-10-20

Chi ha più bisogno del tuo selettore della lingua è proprio chi non riesce a leggere la pagina su cui è arrivato. Per questo tutte le scelte descritte di seguito seguono un&#39;unica regola: il controllo deve risultare comprensibile anche a chi non legge la tua lingua predefinita.

## Cosa rende efficace un selettore della lingua?

### 1. Indicare ogni lingua con il suo nome nativo

Su una pagina in inglese, un lettore giapponese cerca 日本語, non &quot;Japanese&quot;, e un lettore tedesco cerca &quot;Deutsch&quot;. Le [linee guida del W3C sui menu di selezione della lingua](https://www.w3.org/International/questions/qa-navigation-select) raccomandano di scrivere &quot;français&quot; anziché &quot;French&quot;. Questi nomi nativi si chiamano endonimi, e in JavaScript [`Intl.DisplayNames`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DisplayNames) li genera a partire dal codice locale:

```js
const locales = ['en', 'fr', 'de', 'ja', 'ar', 'pt-BR', 'zh-Hant'];

for (const code of locales) {
  // Nomina ogni lingua nella lingua stessa (il suo endonimo)
  const own = new Intl.DisplayNames([code], { type: 'language' }).of(code);
  // Lo stesso nome in inglese, a titolo di confronto
  const english = new Intl.DisplayNames(['en'], { type: 'language' }).of(code);
  console.log(`${code}: ${own} (${english})`);
}
```

Output su Node.js 24 (ICU 78.3, CLDR 48.0):

```text
en: English (English)
fr: français (French)
de: Deutsch (German)
ja: 日本語 (Japanese)
ar: العربية (Arabic)
pt-BR: português (Brasil) (Brazilian Portuguese)
zh-Hant: 繁體中文 (Traditional Chinese)
```

In francese e in portoghese i nomi delle lingue non vanno in maiuscolo a metà frase, quindi CLDR restituisce &quot;français&quot;. In un menu, dove ogni nome compare da solo, metti in maiuscolo la prima lettera. Aggiungi la regione solo se offri due varianti della stessa lingua, come &quot;Português (Brasil)&quot; accanto a &quot;Português (Portugal)&quot;.

### 2. Non usare le bandiere per le lingue

Le [best practice del W3C sulle dichiarazioni di lingua](https://www.w3.org/TR/i18n-html-tech-lang/) lo dicono senza mezzi termini: &quot;Non usare icone di bandiere per indicare le lingue&quot;. E ne spiegano il [motivo](https://www.w3.org/International/questions/qa-link-lang): &quot;Le bandiere rappresentano paesi, non lingue&quot;. Una lingua può essere parlata in molti paesi (lo spagnolo in Spagna, Messico e Argentina), un paese può avere più lingue ufficiali (la Svizzera) e una bandiera può evocare connotazioni nazionali sgradite ad altri parlanti della stessa lingua. Una bandiera è adatta a un selettore di paese o di negozio, dove la scelta riguarda la regione; in quel caso, affiancale i nomi delle lingue disponibili.

### 3. Posizionalo dove gli utenti guardano, con un attivatore riconoscibile

Il W3C [consiglia](https://www.w3.org/International/questions/qa-navigation-select) di collocare il controllo &quot;nella parte superiore della pagina o nelle sue vicinanze&quot; e, poiché un&#39;etichetta testuale è language-specific, di affiancarle un&#39;immagine riconoscibile. Non esiste ancora un simbolo riconosciuto universalmente, anche se il W3C osserva che le icone a forma di globo sono sempre più diffuse. Un&#39;icona a forma di globo accanto al nome della lingua corrente è comprensibile per chiunque, in qualsiasi lingua legga. Molti siti ripetono il selettore nel footer come secondo punto di ingresso, ma evita che il footer sia l&#39;unico.

### 4. Passa alla stessa pagina, non alla homepage

Chi sta leggendo la tua pagina dei prezzi in inglese e passa al francese vuole la pagina dei prezzi in francese. Rimandarlo alla homepage francese gli fa perdere il punto in cui si trovava e lo costringe a navigare di nuovo in una lingua che forse solo ora riesce a leggere. Collega ogni opzione all&#39;equivalente tradotto dell&#39;URL corrente e, se una pagina non è ancora tradotta, collegala alla pagina padre tradotta più vicina, segnalandolo all&#39;utente.

### 5. Usa link scansionabili con `hreflang` e `lang`

In genere Google può [scansionare un link solo se è un elemento `<a>` con un `href`](https://developers.google.com/search/docs/crawling-indexing/links-crawlable) e chiede ai siti multilingue di [aggiungere collegamenti ipertestuali alle altre versioni linguistiche](https://developers.google.com/search/docs/specialty/international/managing-multi-regional-sites), così che gli utenti possano sceglierne una con un clic. Un `<select>` il cui gestore dell&#39;evento change imposta `window.location` non è un link, quindi non offre ai crawler nulla da seguire.

Aggiungi due attributi a ogni link. L&#39;attributo `hreflang` su `<a>` [suggerisce la lingua della pagina collegata](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/a); non ha alcun effetto predefinito nel browser ed è distinto dalle annotazioni `<link rel="alternate" hreflang>` lette dai motori di ricerca (vedi [Che cos&#39;è hreflang](/kb/what-is-hreflang)). L&#39;attributo `lang` indica la lingua del testo del link, così uno screen reader che legge la pagina può pronunciare &quot;Deutsch&quot; secondo le regole del tedesco. Il criterio [Lingua delle parti](https://www.w3.org/WAI/WCAG22/Understanding/language-of-parts.html) delle WCAG cita come esempio proprio i link alle lingue indicati con il nome nella lingua stessa. Segnala però anche un limite: quando una tecnologia assistiva annuncia un link solo tramite il suo nome accessibile, ad esempio in un elenco dei link della pagina, la pronuncia non cambia.

### 6. Ricordare la scelta esplicita

Quando un utente sceglie una lingua, salvala in un cookie o nel suo account e usala nelle visite successive: per decidere la destinazione a partire da una radice neutra come `/` e per mostrare la scelta corrente nel selettore di lingua. Non lasciare che il cookie sovrascriva un URL localizzato aperto dall&#39;utente. Il W3C [raccomanda](https://www.w3.org/International/questions/qa-accept-lang-locales) di «memorizzare i risultati in un database o in un cookie per le visite successive» e [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Accept-Language) afferma che un server «non dovrebbe mai sovrascrivere una scelta esplicita della lingua da parte dell&#39;utente».

### 7. Non cambiare lingua automaticamente a ogni visita

Reindirizzare in base alla lingua del browser a ogni richiesta lascia bloccati chi viaggia, chi usa un computer condiviso e chiunque abbia aperto un link in una lingua specifica. Usa la preferenza del browser solo per mostrare un suggerimento chiudibile («Diese Seite gibt es auch auf Deutsch») e lascia che sia l&#39;utente a decidere. [Routing delle impostazioni regionali e SEO](/kb/locale-routing-seo) spiega quale segnale dovrebbe avere la precedenza e l&#39;unico caso in cui un reindirizzamento è sicuro.

### 8. Rendilo accessibile da tastiera e con gli screen reader

Un elenco di semplici link, o un `<select>` nativo, funziona da subito con la tastiera e con le tecnologie assistive. Se crei un menu a discesa, segui il [pattern disclosure](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/) di WAI-ARIA: un `<button>` mostra e nasconde l&#39;elenco, i tasti Invio e Spazio lo aprono e lo chiudono, e `aria-expanded` ne indica lo stato. Assegna al controllo un nome accessibile nella lingua della pagina (`aria-label="Sprache"` su una pagina in tedesco) e indica la lingua corrente con `aria-current="page"`.

## Come si crea un selettore della lingua?

### HTML semplice

Questo menu applica tutte e otto le buone pratiche: endonimi, un&#39;icona a forma di globo al posto delle bandiere, link alla stessa pagina, `hreflang` e `lang` su ogni link, un pulsante di apertura/chiusura e un cookie per memorizzare la scelta esplicita dell&#39;utente.

```html title="On /de/pricing"
<nav aria-label="Sprache">
  <button type="button" aria-expanded="false" aria-controls="language-menu">
    <svg aria-hidden="true" width="16" height="16"><!-- icona a forma di globo --></svg>
    Deutsch
  </button>
  <ul id="language-menu" hidden>
    <li><a href="/en/pricing" hreflang="en" lang="en">English</a></li>
    <li><a href="/fr/pricing" hreflang="fr" lang="fr">Français</a></li>
    <li><a href="/de/pricing" hreflang="de" lang="de" aria-current="page">Deutsch</a></li>
    <li><a href="/ja/pricing" hreflang="ja" lang="ja">日本語</a></li>
  </ul>
</nav>
```

```js title="switcher.js"
const button = document.querySelector('[aria-controls="language-menu"]');
const menu = document.getElementById('language-menu');

// Apre e chiude il menu (Invio e Spazio attivano un <button> in modo nativo)
button.addEventListener('click', () => {
  const open = button.getAttribute('aria-expanded') === 'true';
  button.setAttribute('aria-expanded', String(!open));
  menu.hidden = open;
});

// Esc chiude il menu a comparsa e riporta il focus sul relativo pulsante.
button.closest('nav').addEventListener('keydown', (event) => {
  if (event.key === 'Escape' && !menu.hidden) {
    menu.hidden = true;
    button.setAttribute('aria-expanded', 'false');
    button.focus();
  }
});

// Memorizza la scelta esplicita per le visite successive
menu.addEventListener('click', (event) => {
  const link = event.target.closest('a[hreflang]');
  if (link) {
    document.cookie = `locale=${link.hreflang}; path=/; max-age=31536000; samesite=lax`;
  }
});
```

### React

Questo componente utilizza semplici link. Passa in `currentHref` il percorso corrente, la query string e il frammento, insieme all&#39;impostazione regionale corrente. Il componente presuppone che ogni impostazione regionale abbia un prefisso nell&#39;URL, che ogni pagina esista in tutte le impostazioni regionali elencate e che gli slug siano identici in tutte le lingue. Se gli slug sono tradotti o alcune pagine mancano, anziché sostituire il prefisso risolvi ogni destinazione a partire dai dati delle route o del CMS.

```jsx title="LanguageSwitcher.jsx"
const LOCALES = ['en', 'fr', 'de', 'ja'];

// Conserva percorso, barra finale, query e frammento mentre cambia l'impostazione regionale.
function hrefForLocale(currentHref, locale) {
  const url = new URL(currentHref, 'https://example.invalid');
  const trailingSlash = url.pathname.endsWith('/');
  const segments = url.pathname.split('/').filter(Boolean);
  if (LOCALES.includes(segments[0])) segments.shift();
  url.pathname = `/${[locale, ...segments].join('/')}${trailingSlash ? '/' : ''}`;
  return `${url.pathname}${url.search}${url.hash}`;
}

// Il nome della lingua nella lingua stessa, con l'iniziale maiuscola per il menu
function endonym(locale) {
  const name = new Intl.DisplayNames([locale], { type: 'language' }).of(locale);
  return name.charAt(0).toLocaleUpperCase(locale) + name.slice(1);
}

function rememberChoice(locale) {
  document.cookie = `locale=${locale}; path=/; max-age=31536000; samesite=lax`;
}

export function LanguageSwitcher({ currentHref, currentLocale, label }) {
  return (
    <nav aria-label={label}>
      <ul>
        {LOCALES.map((locale) => (
          <li key={locale}>
            <a
              href={hrefForLocale(currentHref, locale)}
              hrefLang={locale}
              lang={locale}
              aria-current={locale === currentLocale ? 'page' : undefined}
              onClick={() => rememberChoice(locale)}
            >
              {endonym(locale)}
            </a>
          </li>
        ))}
      </ul>
    </nav>
  );
}
```

Con `currentHref="/fr/pricing/?plan=pro#faq"`, `currentLocale="fr"` e `label="Langue"`, il link tedesco è `/de/pricing/?plan=pro#faq`, mentre il link francese viene contrassegnato con `aria-current="page"`. Mantieni i parametri di query e i frammenti solo se hanno lo stesso significato nella pagina di destinazione. Se gli ID delle ancore sono tradotti, risolvi anche il frammento corrispondente.

### Con General Translation

Per le pagine Next.js pubbliche, il componente [`<Link>`](/docs/react/nextjs/link) di GT genera link locale-aware. Con `prefixDefaultLocale: true`, questi link puntano a `/en/pricing` e `/de/pricing`. In questo esempio si presuppone che le pagine dei prezzi in inglese e in tedesco siano già pubblicate. Passa l&#39;impostazione regionale corrente e un&#39;etichetta di navigazione localizzata:

```tsx
import Link from 'gt-next/link';

export function PricingLanguages({ currentLocale, label }: { currentLocale: string; label: string }) {
  return (
    <nav aria-label={label}>
      <Link href="/pricing" locale="en" hrefLang="en" lang="en" aria-current={currentLocale === 'en' ? 'page' : undefined}>English</Link>
      {' · '}
      <Link href="/pricing" locale="de" hrefLang="de" lang="de" aria-current={currentLocale === 'de' ? 'page' : undefined}>Deutsch</Link>
    </nav>
  );
}
```

Localizza l&#39;etichetta di navigazione della pagina corrente. Se ti serve un controllo nativo compatto, ad esempio all&#39;interno di un&#39;app con autenticazione, usa il selettore delle impostazioni locali:

[`<LocaleSelector>`](/docs/react/reference/components/locale-selector), disponibile in `gt-react`, `gt-next` e nel pacchetto sperimentale `gt-tanstack-start`, renderizza un `<select>` nativo con le impostazioni regionali configurate, mostra ciascuna nella propria lingua e salva la scelta in un cookie. Gli attributi standard di `<select>` vengono inoltrati, quindi puoi passare un `aria-label` tradotto con [`useGT`](/docs/react/reference/hooks/use-gt):

```tsx
'use client';
import { LocaleSelector, useGT } from 'gt-next';

export function Header() {
  const gt = useGT();
  return <LocaleSelector aria-label={gt('Language')} />;
}
```

Per le pagine pubbliche, usa i link indicati sopra per garantire una navigazione scansionabile dai crawler e dichiara le versioni alternative hreflang nell&#39;head della pagina, come illustrato in [SEO multilingue con Next.js](/kb/multilingual-nextjs-seo). Per un design personalizzato, [`useLocaleSelector`](/docs/react/reference/hooks/use-locale-selector) restituisce il locale attivo, l&#39;elenco delle impostazioni regionali e `setLocale`. In Vue, crea un selettore di lingua con [`useLocale`](/docs/vue/reference/composables/use-locale) e [`useSetLocale`](/docs/vue/reference/composables/use-set-locale), seguendo la [guida alle impostazioni regionali di Vue](/docs/vue/guides/managing-locales#switcher).

## Punti chiave

* Usa gli endonimi come etichette delle opzioni, ad esempio Français e 日本語; `Intl.DisplayNames` li genera automaticamente, in minuscolo per il francese e il portoghese.
* Usa un&#39;icona a forma di globo, non le bandiere: il W3C raccomanda «Do not use flag icons to indicate languages.»
* Inserisci un link alla stessa pagina in ciascuna lingua con semplici link `<a href>` dotati degli attributi `hreflang` e `lang`, così i crawler possono seguirli e gli screen reader pronunciano correttamente ogni nome.
* Memorizza la scelta esplicita dell&#39;utente e non reindirizzare mai da un URL localizzato in base alla lingua del browser.
* Il componente [`<LocaleSelector>`](/docs/react/reference/components/locale-selector) di General Translation renderizza un `<select>` nativo con etichette nella lingua di ciascuna opzione e salva la scelta in un cookie; aggiungi semplici link per i crawler.

## Related questions

- [Come si rileva la lingua del browser di un utente?](https://generaltranslation.com/it/kb/detect-user-language.mdx): Leggi l'elenco ordinato delle lingue del browser (navigator.languages in JavaScript o l'intestazione Accept-Language sul server), ordinato per q-value, e confrontalo con le impostazioni regionali supportate, prevedendo un fallback. Safari e Chrome in incognito restituiscono una sola lingua, mentre la geolocalizzazione tramite IP rivela la posizione, non la lingua.
- [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.
- [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 rende multilingue un sito web?](https://generaltranslation.com/it/kb/make-website-multilingual.mdx): Scegli dove gestire le traduzioni (in un proxy, in un plugin del CMS, in un site builder o nel tuo codice), poi assegna a ogni lingua un URL dedicato, come raccomanda Google. Traduci le pagine per intero, compresi titoli e meta description, collega le versioni con hreflang e offri un selettore della lingua anziché reindirizzare automaticamente i visitatori.

## Previous and next questions

- Previous question: [Qual è la migliore libreria i18n per un'app React?](https://generaltranslation.com/it/kb/best-react-i18n-library.mdx)
- Next question: [Che cos'è hreflang e come si configura?](https://generaltranslation.com/it/kb/what-is-hreflang.mdx)

## Sitemap

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