navigator.languages: Pick the Right Locale From Browser Language Hints
navigator.languages returns ordered browser language hints. Learn a negotiation order that respects explicit choice and keeps a complete fallback.
BotBrowser Team
Want the structured docs for Platform?
This article lives in the editorial library. For step-by-step setup, reference material, and ongoing updates, jump into the docs section.
Browser language preferences can help an application choose a first translation, but they are only a hint. navigator.language reports the browser's primary language preference, while navigator.languages exposes an ordered list when the browser provides one. Neither value proves where a person lives, which country they belong to, or which language they want for every task. A careful application negotiates a supported locale, explains the choice, keeps an explicit language control, and preserves a usable fallback when a resource is missing.
BotBrowser profile settings can set browser languages and locale, including proxy-derived auto values, but BotBrowser does not translate application content or replace an explicit in-app language choice. Keep that capability boundary visible when testing localization: the browser hint can affect negotiation inputs, while the application owns translation, consent, and fallback behavior.
TL;DR
navigator.language and navigator.languages are localization hints: they help an application pick a first locale, but they say nothing reliable about identity or location. Negotiate against a supported list, keep an explicit in-app language control ahead of the browser hint, and always ship a complete default.
Contents
- What the Navigator language values mean
- Negotiate supported locales safely
- Keep explicit choice in control of the experience
- Load resources without breaking content
- Test preference, fallback, and accessibility paths
- Privacy and boundary decisions
- Practical conclusion
What the Navigator language values mean
The Navigator interface exposes language information selected by the user or browser configuration. The MDN Navigator.language reference describes the primary language tag. The WHATWG definition of navigator.languages describes an ordered preference list used by web applications and related language negotiation. A tag such as fr-CA communicates a language and regional preference for content; it does not establish a physical location.
The values can be absent, contain only one entry, or change when a person changes browser settings. Some browsers expose a broad language such as es while a person may prefer a regional variant such as es-MX. Treat the values as input to a selection process, not as a permanent user profile. Read them when a page needs to select resources, and avoid retaining detailed lists when the application only needs the selected locale.
Language preference also differs from document language. An HTML document can declare its content language with lang, and an individual element can override it. Screen readers and other assistive technology use those declarations to choose pronunciation and parsing rules. A page that chooses Spanish resources must still set the document language and update it when the visible language changes. Localization is therefore a content and accessibility decision, not merely a JavaScript property lookup.
Negotiate supported locales safely
Start with a finite list of locales the application actually supports. Normalize tags with the platform's internationalization facilities, compare the most specific supported tag first, then apply a documented parent-language fallback. For example, an application supporting en, fr, and fr-CA can choose fr-CA for that exact request, fr for another French regional request, and en when no French resource exists. Do not silently invent a translation by treating an unrelated region as equivalent.
The W3C Internationalization material explains why language tags, script, and region subtags carry distinct meaning. A matching algorithm should preserve that distinction. A Chinese request may include a script or region preference that matters to typography and terminology; a Serbian request may require a script choice; Portuguese variants can have different vocabulary. If the application does not have the requested variant, say which fallback is in use and make another language available in the selector.
For structured matching, parse and canonicalize tags with Intl.Locale before comparing supported values. A server may also receive an HTTP Accept-Language header and use it to choose an initial response, but that header is a preference hint, not proof of identity or location. Keep the header and the browser list to the minimum needed for negotiation, and let a saved explicit application choice override both; BotBrowser can configure the browser language inputs but does not choose the server policy or the app's final locale.
A simple negotiation order is:
- an explicit application choice already saved for this person;
- the first supported value from
navigator.languages; navigator.languagewhen the list is unavailable;- a stable product default selected for the service.
The order keeps an intentional choice ahead of a later browser change. It also works when privacy settings or an embedded web view provide less information than a full browser. The default should be a complete experience, not a page that exposes untranslated keys or blocks the user until a locale is selected.
Make the matching stages observable in the application rather than hiding them in a single fallback helper. First canonicalize each configured tag, then compare exact supported tags, then apply only the parent-language fallbacks the product has approved. For a request such as fr-BE, an app that supports fr may use that bundle and label the result as French; it should not claim Belgian-specific wording. If the list contains fr-CA, en, choose the first candidate that the same matcher can resolve, not simply the first string in the list. Keep the supported list in one place so the selector, server response, and client hydration do not implement subtly different rules.
Treat the quality value as part of the match. A server parsing Accept-Language can receive weighted preferences such as fr-CA,fr;q=0.8,en;q=0.6; parse the weights according to the header format and preserve their ordering instead of comparing raw substrings. Browser APIs may expose only the ordered array, so do not assume that a web page can recover every original header detail. Resolve the client and server inputs through the same supported-locale policy, and return the selected locale explicitly with the rendered page. On hydration, reconcile that result with any saved application choice before replacing visible text. This avoids a page that first renders one language and then changes after JavaScript starts.
Fallback should preserve meaning, not just fill a missing key. A complete parent bundle is safer than mixing one translated warning with a default-language button that appears to confirm it. If partial bundles are a product requirement, define which content classes may fall back and test that critical actions, consent text, and error recovery remain understandable together. Store a bundle completeness marker alongside its revision so the release check can reject a locale accidentally promoted before its required keys are present.
Keep explicit choice in control of the experience
Automatic selection is useful on a first visit, but it should never trap a person in a language they cannot read. Provide a visible language selector with translated names, a clear current value, and keyboard access. Store the locale only after a person chooses it, and restore that choice before consulting navigator.languages on later visits. Scope the setting to the account or device deliberately so a shared device does not change another person's account preference.
Explain whether the selector changes the current page, the account, or future visits. If the choice requires a new resource bundle, retain the current content while it loads and announce the completed change. Do not discard form data, a draft, or a pending transaction during a locale switch. If a translated resource fails to load, keep the last readable locale and offer retry or another language. A failure message should itself be understandable in the current language.
Do not infer consent from a language preference. A French preference does not authorize promotional messages in French, an English preference does not authorize a particular data use, and a regional tag does not establish legal jurisdiction. Consent, account settings, and service eligibility need their own explicit controls. Language negotiation changes presentation; it does not change the policy that governs the feature.
Choose persistence scope from the behavior the control promises. A page-level menu can keep a choice only for that route; a device preference can use a first-party setting that survives a visit; an account preference can follow the signed-in person across devices. Tell the person which scope applies before saving, and provide a visible way to change it. Do not copy a device choice into an account merely because the person later signs in. Likewise, signing out should not silently clear a device-wide preference if the interface promised it would persist. For shared workstations, offer a session-only choice or an easy reset so the next person does not inherit the previous person's display language.
Keep the account decision separate from browser updates. A saved account locale remains authoritative when a user changes navigator.languages, while an unsaved first-visit choice can be renegotiated on the next visit. If an account setting cannot be fetched, use a documented temporary fallback and avoid writing that fallback back as if the person had selected it. Record the source of the current decision in application state, such as account, device, browser, or default; this small distinction makes conflict handling testable without storing the full browser preference list.
Load resources without breaking content
Keep translation resources versioned and validate that every key needed by a route exists in the selected bundle. During a transition, show the source-language or previously loaded string rather than a raw key. Mark incomplete translations in development and release checks so missing entries are fixed before users encounter them. Avoid concatenating sentences from independent translated fragments: grammatical order and plural rules differ across languages.
Use the locale as a presentation input for dates, numbers, lists, and relative time, preferably through the Intl APIs. The locale does not change the meaning of stored timestamps or amounts. Store canonical data, then format it for the selected locale at the point of display. Keep units and currency explicit, and avoid using a language tag to guess a user's tax country, address, or payment method. The related Intl locale formatting guide covers portable date and number presentation.
Language selection should also cover metadata and navigation. Update the document lang, page title, descriptions, alternate links, and direction when a locale changes. For right-to-left languages, apply direction to the document and test mixed numbers, code, and punctuation. Localized URLs should resolve to the same content intent and provide a visible route back to another language. A missing translation should not redirect a valid localized URL to an unrelated homepage.
Test preference, fallback, and accessibility paths
Test with an empty preference list, an unsupported region, a script-specific tag, and a browser that exposes only navigator.language. Verify that the application reaches its complete default experience in every case. Change the browser preference after an explicit application choice and confirm that the saved choice remains in control. Clear that choice and confirm that the next negotiation is predictable and documented.
Test resource loading on a slow connection, an interrupted request, and a stale cache. The page should preserve user input, provide progress information that is not color-only, and expose a recoverable retry. Exercise keyboard navigation through the selector, verify focus after a locale change, and check that screen readers receive the updated lang and translated labels. Test long translated strings, plural forms, dates, decimal separators, and text expansion at zoom levels used by real users.
Do not use visual equality as the only localization test. Compare meaning, required actions, safety warnings, and limitations across locales. A translation that omits a fallback condition can turn a conditional browser behavior into an unconditional promise. Review messages about unavailable languages, data handling, and user choice with the same care as feature labels. Keep a small fixture set representing each supported locale, and run it when resource keys or negotiation rules change.
Use a fixed acceptance fixture so a rule change can be reproduced without relying on the tester's personal browser settings. Set the supported list to en, fr, and fr-CA, then run each row with browser preferences and saved choices supplied by the fixture. Record the selected locale, its decision source, the loaded bundle revision, and the document lang after rendering. The expected result is the same in the server response and after hydration; a mismatch is a failure even if the page eventually settles on a readable language.
| Fixture input | Expected selection | Acceptance check |
|---|---|---|
Saved account fr-CA; browser en | fr-CA from account | Changing browser input does not replace account choice |
No saved choice; browser fr-BE,en | fr parent fallback | Selector names the fallback; document lang is fr |
No saved choice; browser de | en default | Complete English bundle loads without raw keys |
Device choice en; account fr | fr while signed in | Sign-out returns to device-scoped en |
fr-CA bundle unavailable | Complete fr or en bundle | No mixed critical controls; retry remains available |
After each case, change the browser preference, reload, and repeat the decision. Clear the saved choice and verify that the next selection comes from the browser input. Run one offline case after loading a complete bundle, then request an unavailable locale: the page should retain the cached language, preserve form values, and expose a retry action. Finally, use keyboard-only navigation to switch the selector and confirm focus remains on a meaningful control. Save these inputs and outcomes as a fixture with the resource revision, not as an analytics record tied to a person's full language list.
Privacy and boundary decisions
Language preferences can contribute to a recognizable browser configuration when combined with many other values. An application that only needs a locale should use the minimum value needed for the current selection and avoid sending the full ordered list to analytics. Do not combine language tags with account identifiers to infer nationality, residence, or a sensitive attribute. A language setting is not a reliable proxy for any of those facts.
If the application offers remote translation, identify which service receives text, how long it retains it, and what action starts the transfer. A missing local resource must not upload private content silently; the article on Google Translate page translation with BotBrowser describes the separate browser-side translation path.
Publish the supported locale list with familiar language names and mark complete locales separately from parent-language fallbacks. Keep a complete default available when a translation is partial. A server may use Accept-Language for the initial response, but the client must honor a saved choice after hydration and update the document's lang value without switching a checkout or recovery flow mid-task.
Use fixtures that record the chosen locale, supported list, and resource revision. Do not put the full browser preference list in analytics: a coarse selection is usually enough to diagnose negotiation. Reject unknown locale values and load a known fallback rather than constructing resource paths from untrusted input. Keep account, workspace, and device preference scopes explicit, and provide a way to change or clear a saved choice.
When a bundle is unavailable or the device is offline, retain a cached complete locale and offer retry or another language. Preserve form and transaction state while labels change. Review headings, validation messages, notifications, generated documents, and alternate text so translated controls keep the same action and warnings retain their conditions. Use locale-aware plural rules and formatting; for dates and numbers, keep the locale separate from stored data as shown in portable Intl formatting. The browser privacy choices guide covers the broader tradeoffs around limiting preference data.
Keep the choice understandable in every supported locale, and tell people how to return to the default language. Preserve the current task when the locale changes.
Practical conclusion
Treat browser language values as one input to a documented negotiation order: saved explicit choice first, then navigator.languages, then navigator.language, then a complete default. Keep the selector, the fallback, and the task state working in every supported locale, and re-test the path when browser privacy controls or resource packaging change. Record the supported locales in product documentation so a missing phrase is diagnosable as a boundary rather than a defect.
Claim-to-source map
- MDN
Navigator.languagedefines the primary language value and its browser-facing limits. - WHATWG
Navigator.languagesdefines the ordered language preference list used for negotiation. - W3C Internationalization supports the distinction between language, script, and region subtags and the need for locale-aware content.
- MDN
Navigatordocuments the browser-facing interface; application identity, location, and consent remain separate product decisions.
BotBrowser profile settings can set browser languages and locale, including proxy-derived auto values; BotBrowser does not translate an application or replace an explicit language choice, so keep an in-app language control and fallback.
Public sources
This article is part of the guide: Web Platform Surfaces: A Consistency Review Map
Related Articles
Timezone, Locale, and Language in Browser APIs
PlatformNetwork Information API: Hints, Privacy Limits, and Fallbacks
PlatformIntl API Locale Data and Portable Browser Formatting
Getting StartedGoogle Translate Page Translation with BotBrowser
Platformnavigator.canShare() and the Web Share API: Sharing Boundaries
PlatformNetwork Information API: A Fallback-First Guide
Run the same check in BotBrowser
Load a profile, keep the signals you just read about consistent inside the browser, and confirm the result on your own machine before a rollout.