# General Translation Knowledge Base: Come si rileva la lingua del browser di un utente?
URL: https://generaltranslation.com/it/kb/detect-user-language.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: 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.
Category: Internazionalizzazione
Last updated: 2026-10-20
Authors: Kevin Wu, Jackie Chen
Published: 2026-10-20

La risposta è già nel browser: un elenco ordinato di lingue che l&#39;utente ha impostato nel browser o nel sistema operativo. Si tratta di leggere correttamente quell&#39;elenco, confrontarlo con le impostazioni regionali per cui hai delle traduzioni e capire quando deve prevalere un segnale più forte.

## Da dove proviene la preferenza linguistica di un utente?

Sono cinque i segnali che possono indicarti quale lingua mostrare. Solo alcuni di essi riguardano davvero la lingua.

| Segnale                             | Dove leggerlo                                    | Cosa indica                                        | Affidabilità                                                           | Quando usarlo                                                 |
| ----------------------------------- | ------------------------------------------------ | -------------------------------------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------- |
| `navigator.languages`               | JavaScript del browser                           | Preferenze linguistiche in ordine di priorità      | Buona, ma alcuni browser espongono una sola lingua                     | Pagine renderizzate sul client alla prima visita              |
| Intestazione `Accept-Language`      | Ogni richiesta HTTP                              | Lo stesso elenco, ponderato con q-value           | Buona, ma spesso l&#39;utente non può controllarla quando è in viaggio | Pagine server-rendered alla prima visita                      |
| Impostazione regionale nell&#39;URL | Il percorso della richiesta, ad esempio `/fr/`   | La versione richiesta dal visitatore               | Alta                                                                   | Pagine pubbliche e condivisibili                              |
| Preferenza salvata                  | Un cookie o un&#39;impostazione dell&#39;account | Una scelta già fatta in precedenza dall&#39;utente | Alta                                                                   | Visite successive                                             |
| Geolocalizzazione IP                | Un servizio di ricerca                           | Posizione approssimativa                           | Non è un segnale linguistico                                           | Valori predefiniti per regione, come la valuta, non la lingua |

### Nel browser: `navigator.language` e `navigator.languages`

[`navigator.languages`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/languages) restituisce le lingue preferite dall&#39;utente come tag BCP 47, in ordine di preferenza decrescente, mentre [`navigator.language`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/language) corrisponde al primo elemento di tale array:

```js
navigator.language;  // "en-US"
navigator.languages; // ["en-US", "zh-CN", "ja-JP"]
```

Quando l&#39;utente modifica le proprie preferenze, il browser emette un evento `languagechange` su `window`. Per limitare il fingerprinting, Safari restituisce sempre una sola lingua, e lo stesso vale per Chrome in modalità di navigazione in incognito: tieni quindi presente che l&#39;elenco potrebbe essere più breve rispetto alle preferenze complete dell&#39;utente.

### Sul server: l&#39;intestazione `Accept-Language`

I browser inviano le stesse preferenze a ogni richiesta. [RFC 9110](https://www.rfc-editor.org/rfc/rfc9110.html#name-accept-language) definisce l&#39;intestazione come un elenco di intervalli di lingua separati da virgole, ciascuno con un valore di qualità (`q`) facoltativo compreso tra 0 e 1. Se `q` è assente vale 1, mentre `q=0` significa &quot;non accettabile&quot;. Ecco l&#39;esempio riportato nella RFC stessa:

```http
Accept-Language: da, en-gb;q=0.8, en;q=0.7
```

In altre parole: &quot;Preferisco il danese, ma accetto anche l&#39;inglese britannico e altre varianti di inglese&quot;. Il carattere jolly `*` [rappresenta](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Accept-Language) qualsiasi lingua non elencata. I browser aggiungono anche dei fallback con la sola lingua: secondo [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/languages), Chrome e Safari inviano `en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7` per un utente il cui `navigator.languages` è `["en-US", "zh-CN"]`. Ordina le voci in base a `q`, perché secondo la RFC sull&#39;ordine in cui sono elencate &quot;non si può fare affidamento&quot;.

L&#39;intestazione è solo un&#39;indicazione. MDN afferma che un server &quot;non dovrebbe mai sovrascrivere una scelta esplicita della lingua da parte dell&#39;utente&quot; e fa notare che spesso l&#39;intestazione sfugge al controllo dell&#39;utente, ad esempio quando è in viaggio. Se metti in cache pagine selezionate in base all&#39;intestazione, aggiungi [`Vary: Accept-Language`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Vary), così la cache non servirà a un visitatore la lingua di quello precedente.

### Perché la geolocalizzazione IP è il segnale sbagliato

Una posizione geografica non è una lingua. Come [osserva](https://www.w3.org/International/questions/qa-link-lang) il W3C, &quot;numerosi paesi hanno più di una lingua ufficiale&quot;, e viaggiatori, espatriati e utenti di VPN leggono in una lingua che il loro indirizzo IP non lascia prevedere. Google è chiaro in proposito: &quot;Non utilizzare l&#39;analisi dell&#39;IP per adattare i contenuti&quot;, anche perché &quot;il crawler Googlebot proviene solitamente dagli Stati Uniti&quot;, come indicato nelle sue [linee guida per i siti multiregionali](https://developers.google.com/search/docs/specialty/international/managing-multi-regional-sites). Una pagina che sceglie la lingua in base all&#39;IP mostra ai crawler una sola versione e nasconde tutte le altre. La posizione può comunque servire a scegliere una valuta predefinita o una regione di spedizione appropriata, ma tieni questa decisione separata da quella sulla lingua.

## Confrontare le preferenze con le tue impostazioni regionali

Un utente che richiede `pt-PT` su un sito che offre `pt-BR` ha bisogno di una regola di fallback. Esistono due algoritmi, e le [API `Intl`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl) usano gli stessi nomi:

* **Ricerca** segue la [RFC 4647](https://www.rfc-editor.org/rfc/rfc4647.html#section-3.4): prova uno alla volta ogni tag preferito, rimuovendo i subtag a partire dalla fine (`zh-Hant-CN`, poi `zh-Hant`, poi `zh`) finché non trova una corrispondenza; altrimenti restituisce il valore predefinito.
* **Best fit** consente all&#39;implementazione di restituire un&#39;impostazione regionale &quot;adatta almeno quanto, ma possibilmente più di&quot; quella che restituirebbe la ricerca, ad esempio una variante regionale della stessa lingua.

La differenza emerge con le varianti regionali. Usando [`@formatjs/intl-localematcher`](https://www.npmjs.com/package/@formatjs/intl-localematcher) 0.9.0 con `['en', 'fr', 'de', 'pt-BR']` e valore predefinito `en`:

| Richiesta     | Ricerca | Best fit |
| ------------- | ------ | -------- |
| `pt-PT`       | `en`   | `pt-BR`  |
| `en-GB`       | `en`   | `en`     |
| `fr-CA`, `de` | `fr`   | `fr`     |
| `zh-TW`       | `en`   | `en`     |

La ricerca è prevedibile e facile da spiegare. Il best fit offre a più persone una lingua che sono in grado di leggere; stabilire se un lettore portoghese preferisca il portoghese brasiliano all&#39;inglese è una scelta di prodotto, quindi prova entrambi gli algoritmi con il tuo elenco di impostazioni regionali.

## Esempio: negoziazione sul server

Questo server Node.js controlla prima l&#39;URL, poi un cookie salvato e infine l&#39;intestazione. Usa [`negotiator`](https://www.npmjs.com/package/negotiator) 1.1.0 per analizzare `Accept-Language` e ordinarne i valori in base al q-value, e `@formatjs/intl-localematcher` per individuare la corrispondenza migliore con l&#39;algoritmo best fit.

```bash
npm install negotiator @formatjs/intl-localematcher
```

```js title="server.mjs"
import { createServer } from 'node:http';
import Negotiator from 'negotiator';
import { match } from '@formatjs/intl-localematcher';

const SUPPORTED = ['en', 'fr', 'de', 'pt-BR'];
const DEFAULT_LOCALE = 'en';

// Scarta "*" e i tag malformati: con questi match() solleva un'eccezione RangeError.
function isValidTag(tag) {
  try {
    Intl.getCanonicalLocales(tag);
    return true;
  } catch {
    return false;
  }
}

function pickLocale(req) {
  // 1. Un'impostazione regionale nel percorso dell'URL, come /fr/pricing
  const { pathname } = new URL(req.url, 'http://localhost');
  const fromPath = pathname.split('/')[1];
  if (SUPPORTED.includes(fromPath)) return { locale: fromPath, source: 'url' };

  // 2. Una scelta salvata durante una visita precedente
  const fromCookie = /(?:^|;\s*)locale=([^;]+)/.exec(req.headers.cookie ?? '')?.[1];
  if (SUPPORTED.includes(fromCookie)) return { locale: fromCookie, source: 'cookie' };

  // 3. Accept-Language, ordinato per q-value e confrontato con le tue impostazioni regionali
  const requested = new Negotiator(req).languages().filter(isValidTag);
  if (requested.length === 0) return { locale: DEFAULT_LOCALE, source: 'default' };
  return { locale: match(requested, SUPPORTED, DEFAULT_LOCALE), source: 'header' };
}

createServer((req, res) => {
  const { locale, source } = pickLocale(req);
  res.setHeader('Content-Language', locale);
  res.setHeader('Vary', 'Accept-Language, Cookie');
  res.end(`${locale} (from ${source})\n`);
}).listen(3000);
```

Avvialo con `node server.mjs`, poi invia le richieste con `curl -H "Accept-Language: ..." localhost:3000/`. Output su Node.js 24:

```text
da, en-gb;q=0.8, en;q=0.7                      -> en (from header)
pt-PT,pt;q=0.9,en;q=0.8                        -> pt-BR (from header)
fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5   -> fr (from header)
ja, de;q=0.5                                   -> de (from header)
en;q=0.5, de                                   -> de (from header)
*                                              -> en (from default)
(no header)                                    -> en (from default)
fr, with cookie locale=de                      -> de (from cookie)
/fr/pricing, with cookie locale=de             -> fr (from url)
```

Il filtro `isValidTag` è fondamentale: senza di esso, un&#39;intestazione contenente `*` fa sì che `match()` sollevi l&#39;eccezione `RangeError: Incorrect locale information provided`, mandando in crash la richiesta.

## Esempio: rilevamento nel browser

In una pagina renderizzata lato client puoi applicare lo stesso principio a `navigator.languages`. Questa versione implementa un semplice algoritmo di ricerca RFC 4647, senza dipendenze:

```js title="detect-locale.js"
const SUPPORTED = ['en', 'fr', 'de', 'pt-BR'];
const DEFAULT_LOCALE = 'en';

// Ricerca: prova ciascun tag preferito, eliminando i subtag a partire dalla fine.
function lookup(requested, supported, fallback) {
  const lower = supported.map((tag) => tag.toLowerCase());
  for (const tag of requested) {
    const parts = tag.split('-');
    while (parts.length) {
      const i = lower.indexOf(parts.join('-').toLowerCase());
      if (i !== -1) return supported[i];
      parts.pop();
    }
  }
  return fallback;
}

function preferredLocale(nav) {
  const requested = nav.languages?.length ? nav.languages : [nav.language];
  return lookup(requested, SUPPORTED, DEFAULT_LOCALE);
}

// Nel browser: preferredLocale(navigator)
```

Output per alcuni elenchi di preferenze di esempio, ottenuto in Node.js 24 con un oggetto `navigator` simulato:

```text
["fr-CA","en-US"] -> fr
["pt-BR","pt"]    -> pt-BR
["pt-PT","en"]    -> en
["ja-JP"]         -> en
["de-AT","de"]    -> de
```

Prima di chiamare `preferredLocale`, verifica se esiste una scelta salvata e usa il risultato come punto di partenza: renderizza la pagina in quella lingua e mostra il selettore. Non usarlo per effettuare un reindirizzamento.

## Quale segnale dovrebbe prevalere?

Il rilevamento è solo un&#39;ipotesi iniziale per la prima visita. Quando i segnali sono in conflitto, segui questo ordine:

1. **L&#39;URL.** Un link `/de/` condiviso mostra il tedesco, e scegliere una lingua nel selettore porta all&#39;URL di quella lingua.
2. **La scelta salvata.** Un cookie o un&#39;impostazione dell&#39;account. Determina verso quale lingua una radice neutra reindirizza l&#39;utente o cosa suggerirgli, ma non riscrive mai un URL localizzato.
3. **L&#39;intestazione `Accept-Language`**, oppure `navigator.languages` sul client. Si usa solo in assenza di una scelta salvata, e solo nella radice o come suggerimento.
4. **L&#39;impostazione locale predefinita.** Il fallback quando nessun altro segnale corrisponde.

Il W3C consiglia di usare l&#39;intestazione come [punto di partenza](https://www.w3.org/International/questions/qa-accept-lang-locales), di consentire agli utenti di cambiare lingua e di memorizzare il risultato &quot;in un database o in un cookie per le visite successive&quot;. Reindirizzamenti, caching e SEO relativi a questo ordine sono trattati in [routing delle impostazioni regionali e SEO](/kb/locale-routing-seo), mentre il selettore vero e proprio è descritto in [creare un selettore della lingua](/kb/language-switcher).

## Come gt-next, gt-react e gt-node risolvono l&#39;impostazione regionale

In `gt-next` di General Translation, [`createNextMiddleware`](/docs/react/nextjs/reference/functions/create-next-middleware) risolve l&#39;impostazione regionale di ogni richiesta in questo ordine: un&#39;impostazione regionale appena selezionata dal visitatore (tracciata da un cookie di reset), l&#39;impostazione regionale nell&#39;URL (ad esempio `/es/about`), il cookie con l&#39;impostazione regionale salvata, un cookie con l&#39;impostazione regionale della route client precedente, l&#39;intestazione `Accept-Language` e infine il tuo `defaultLocale`. Per impostazione predefinita, il risultato viene salvato nel cookie `generaltranslation.locale`, che puoi rinominare tramite [`headersAndCookies`](/docs/react/nextjs/config#headers-and-cookies). Imposta [`ignoreBrowserLocales`](/docs/react/nextjs/config#ignore-browser-locales) per ignorare del tutto l&#39;intestazione.

Se l&#39;impostazione regionale richiesta non è supportata, le librerie scelgono la corrispondenza supportata più adatta tra le preferenze del browser, poi un&#39;impostazione regionale con la stessa lingua (`en-GB` per `en-US`) e infine quella predefinita. Puoi leggere il risultato con [`useLocale`](/docs/react/reference/hooks/use-locale) oppure, nel codice asincrono, con [`getLocale`](/docs/react/nextjs/reference/functions/get-locale) da `gt-next/server`.

In una single-page app React, [`initializeGTSPA`](/docs/react/reference/config#initialize-spa) di `gt-react` risolve l&#39;impostazione regionale dai cookie e dal browser prima del primo rendering. Su un server Node.js, [`getRequestLocale`](/docs/node/reference/functions/get-request-locale) di `gt-node` confronta `Accept-Language` con le impostazioni regionali passate a [`initializeGT`](/docs/node/reference/functions/initialize-gt). Con `en`, `fr`, `de` e `pt-BR` configurate, ha restituito `fr` per `fr-CH, fr;q=0.9, en;q=0.8` e `pt-BR` per `pt-PT,pt;q=0.9`. Anteponi un controllo sul cookie o sul percorso, come mostrato nella [guida all&#39;impostazione regionale di gt-node](/docs/node/guides/detecting-locale).

## Punti chiave

* `navigator.languages` e `Accept-Language` contengono le stesse preferenze in ordine di priorità; Safari e la modalità in incognito di Chrome espongono una sola lingua.
* Analizza i q-value (da 0 a 1, predefinito 1), ordinali di conseguenza ed elimina `*` e i tag non validi prima della corrispondenza; `@formatjs/intl-localematcher` solleva un&#39;eccezione con `*`.
* Con la ricerca (ricerca), `pt-PT` viene associato all&#39;impostazione regionale predefinita se disponi solo di `pt-BR`; con il best fit viene associato a `pt-BR`. Scegli in modo consapevole.
* Risolvi la lingua controllando nell&#39;ordine l&#39;URL, la scelta salvata, `Accept-Language` e infine il valore predefinito. La geolocalizzazione tramite IP non è mai un indicatore affidabile della lingua.
* Dopo un&#39;impostazione regionale appena selezionata, `gt-next` controlla l&#39;URL, il cookie `generaltranslation.locale`, `Accept-Language` e infine `defaultLocale`.

## Related questions

- [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.
- [Best practice per il selettore della lingua: UX, SEO e accessibilità](https://generaltranslation.com/it/kb/language-switcher.mdx): 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.
- [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: [Come si rende multilingue un sito web?](https://generaltranslation.com/it/kb/make-website-multilingual.mdx)
- Next question: [Come si supportano le lingue RTL (right-to-left) in CSS?](https://generaltranslation.com/it/kb/right-to-left-css.mdx)

## Sitemap

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