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.
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
- mimeTypes are consistent with navigator.plugins
plugins-mimetypesThis walks navigator.mimeTypes, and for each entry reads .enabledPlugin and checks that the exact object is present in a Set built from… - proprietary codecs (H.264 / AAC) present for a Chrome UA
codec-supportThe check calls canPlayType on a video and audio element for H.264 ('avc1.42E01E') and AAC ('mp4a.40.2'), plus MP4/WebM/Ogg for context, and… - Blink-only Web APIs present for a Chrome UA
blink-web-apisWhen the UA matches Chrome/\d+ and the page is a secure context, this probes four Blink-only surfaces: navigator.connection… - CSS feature support matches the engine (Blink for a Chrome UA)
css-engine-surfaceFor any UA containing 'Chrome/', this requires CSS.supports('-webkit-app-region', 'drag') to be true and the Gecko markers — MozAppearance… - navigator.plugins is a real platform object (structured-clone probe)
plugins-structured-cloneThe structured-clone algorithm is implemented in C++ and interrogates objects about what they actually are, not what they claim. - canPlayType answers identically for <video>, <audio> and the prototype
canplaytype-element-agnosticcanPlayType is implemented exactly once, on HTMLMediaElement.prototype, so <video> and <audio> inherit the same C++ function. - canPlayType behaves like a real codec parser (bogus + quoting)
canplaytype-parser-trapsA real canPlayType is an RFC-6381 parser, not a dictionary. This check exercises two properties only a parser has. - AudioContext invariants + a deterministic offline render
audio-context-coherenceThe check constructs a live AudioContext and reads four invariants — sampleRate, baseLatency, the destination node's channelCount against…
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.