HomeLearn → Language and locale mismatch

Language and locale mismatch

Setting a profile's language looks like one switch. It is at least four, and the ones people remember are the ones sites barely look at. The one that matters is not a fingerprinting signal at all — it is the format of every date and number the page prints, which comes from a setting most spoofing never touches. A browser that says en-GB and writes 1/15/2026 has contradicted itself in public, without anyone running a script to catch it.

The four places a language shows up

WhereWhat it isWho reads it
Accept-Language A request header, sent on every navigation The server, before a line of JavaScript runs
navigator.language
navigator.languages
The same preference, readable from the page Fingerprinting scripts, and apps choosing a translation
Intl resolved locale What DateTimeFormat, NumberFormat and Collator default to Anything that formats — usually without meaning to
toLocaleDateString(), toLocaleString() The output of the above, in the page's own HTML Everyone. It is on the screen

Almost every guide stops at the first two, because those are the two you can set. The last two are not settings — they are consequences of the browser's own locale, which lives further down than the request header does.

What the mismatch looks like

This is our own product, before we fixed it. A profile configured as British, on a London clock, asked what it thought it was:

SignalClaimedActually reported
navigator.languageen-GBen-GB
TimezoneEurope/LondonEurope/London
Intl localeen-US
toLocaleDateString()15/01/20261/15/2026
toLocaleTimeString()13:05:001:05:00 PM

Three signals agreed and the fourth did not, and the fourth is the one that ends up in the page. A checkout that prints a delivery date, a dashboard that prints a timestamp, a forum that prints "posted at" — each one renders in American format inside a browser telling everybody it is British.

Why this is worse than an odd fingerprint

Most fingerprinting signals need somebody to go looking: a script reads canvas, or enumerates fonts, or asks WebGL what it is. You can reason about who bothers.

Locale is not like that. It is produced by ordinary page code doing ordinary work, and once the contradiction exists, every page that prints a date is a detector whether the site wanted one or not. There is nothing to opt into and no library to blame.

It is also the kind of signal that survives everything else being right. A perfect canvas hash and a residential proxy do not help a browser that formats dates like a machine in a different country from the one it claims.

In an unmodified browser, these agree by construction

That is the part worth holding on to. In a browser nobody has interfered with, the request header, navigator.languages and the Intl default all descend from the same language preference. They are not four settings that happen to match — they are one setting seen from four angles.

Chrome does let a determined user separate them: the display language and the content-language list are different preferences, so a genuine person can end up with an en-GB header and a US interface. It happens. What does not happen naturally is for it to be systematic — the same split, the same direction, across every profile from one tool. An individual mismatch is unusual. A population of them is a signature.

The rest of the triangle

Language is one corner of a set of three that sites compare because comparing them is cheap:

A German IP, a Berlin clock and an en-US browser is unremarkable — plenty of people abroad keep an English browser. A German IP, a Berlin clock, a browser claiming de-DE, and dates rendering as 1/15/2026 is not a preference. It is a browser whose parts came from different places.

How to check your own

Open the console in whatever browser or profile you use for this and run:

[navigator.language, Intl.DateTimeFormat().resolvedOptions().locale, new Date().toLocaleDateString()]

The first two should match. The third should look the way the first one implies. If a profile claims a language and then writes dates in another country's format, that is the whole finding — no interpretation needed.

How Parallel handles it

Each profile's language now reaches the browser itself, not only the header and the navigator fields: Chromium is launched with the profile's own language as its UI locale, which is what Intl and every toLocale… call resolve from.

We know the shape of this problem because we shipped it. Language was applied to the header and to navigator.languages, and the browser's locale was left as the machine's, so every non-en-US profile formatted dates like the computer it was running on. It was found by testing our own claims rather than by a report, and fixed in v0.0.62 — the table above is the measurement from before, and the same profile now answers en-GB, 15/01/2026 and 13:05:00.

If you are evaluating any tool for this, it is a two-line check and it is worth running before you trust the rest.

Related reading

A profile that agrees with itself

Parallel gives each profile a language, a timezone and a locale that belong together, and a proxy to match — checked against the browser, not just the headers. Three profiles free, forever.

Download for Windows