Skip to content
All fingerprint checksEnvironment & locale

every Intl constructor resolves the same locale

Check id intl-locale-coherence

What an ordinary browser yields

All four Intl constructors resolve the same base locale tag, and the two that expose numberingSystem agree on it.

What a detector infers

Every Intl constructor — NumberFormat, DateTimeFormat, PluralRules, Collator — resolves its locale through the same underlying ICU data, so all four must report the same base language tag. This check constructs each one, reads resolvedOptions().locale, strips any -u- extension (each constructor legitimately keeps only its own extension keys, so comparing full tags would flag a genuine de-DE-u-co-phonebk user), and compares the base tags. It separately compares numberingSystem across NumberFormat and DateTimeFormat only, since Collator and PluralRules do not expose that field. A disagreement is informative because it indicates a partial patch: the common timezone/locale spoof targets Intl.DateTimeFormat.prototype.resolvedOptions and leaves the other three constructors reporting the real ICU locale. Severity is warn; if fewer than two constructors resolve, the check reports N/A.

How to resolve it

Set the locale at the ICU/engine level so all Intl constructors read the same data. If you patch resolvedOptions in JS, cover NumberFormat, DateTimeFormat, PluralRules and Collator consistently rather than DateTimeFormat alone.

What is a timezone/IP mismatch?

Read in the wild by

CreepJS

CreepJS, a research and education demo rather than a production detector, treats resolvedOptions on more than one Intl constructor (DateTimeFormat and RelativeTimeFormat) as a tamper surface and rejects its own timezone result if either one trips its lie battery — it tests those functions for patching rather than comparing locale values across constructors.

src/lies/index.ts: `searchLies(() => Intl.DateTimeFormat, { target: ['format', 'formatRange', 'formatToParts', 'resolvedOptions'] })` and `searchLies(() => Intl.RelativeTimeFormat, { target: ['resolvedOptions'] })`; src/timezone/index.ts: `const lied = (lieProps['Date.getTimezoneOffset'] || lieProps['Intl.DateTimeFormat.resolvedOptions'] || lieProps['Intl.RelativeTimeFormat.resolvedOptions']) || false`

Kasada

Reads the whole Intl family, not just the timezone.

intl_date_time_format, intl_collator, intl_number_format, intl_plural_rules

ips.js 427-probe teardown (emro.cat, Apr 2026)

Every attribution traces to a published artifact. See the sources and their limits.

Nearby checks in Environment & locale

See all 23 checks in Environment & locale
Who builds this test

Clearcote is a browser built for fingerprint coherence

It is a Chromium fork, maintained by the same people who wrote this reference. It ships as a compiled browser rather than as a stealth script injected into someone else's — which is a description of how it is built, and is not an argument about how it behaves on this check.

This audit takes no position on how Clearcote scores on Environment & locale checks, on this one, or anywhere else. It has no baseline corpus of other people's fingerprints to rank you against and no vendor scoreboard — nearly every check is self-referential, asking one browser the same question through two independent APIs and reporting whether both answers can be true at once. It runs identically on any browser, including ours. Run it on yours and read the result yourself.

See the other checks in Environment & locale — the family intl-locale-coherence belongs to.