JustBrowser
Tutorials13 min read

Intl and CLDR Locale Data: The Quiet Way Your Region Leaks Through Formatting

JustBrowser Platform Team·

Three weeks ago I watched a colleague's Japanese e-commerce account, one he had been using normally for two months, get stuck behind repeated verification prompts. Residential Osaka IP. No blacklist history. Canvas passed. WebGL passed. Timezone was Asia/Tokyo.

Everything looked perfect.

Then I ran one line of JavaScript:

new Intl.NumberFormat().resolvedOptions()

Returned { locale: "en-US", numberingSystem: "latn" }. His browser was formatting numbers American-style — comma for thousands, period for decimals — while claiming to be a Japanese user. 1,000 instead of 1.000. The detection script didn't even need canvas analysis. The Intl API told them everything.

This is the fingerprinting surface nobody talks about. Everyone obsesses over canvas and WebGL fingerprinting. Meanwhile, the ECMAScript Intl API quietly exposes your regional formatting preferences through three objects: Intl.DateTimeFormat, Intl.NumberFormat, and Intl.Collator. Each one has a resolvedOptions() method that tells detection scripts exactly how your browser is configured.

I find it almost insulting, honestly. You spend hours getting canvas right, and some script checks your number formatting and you're done.

By the end of this tutorial, you'll know how to align your Intl settings with your proxy's region — so a German IP actually formats dates like a German would.

Check your own fingerprint first. We built a public checker at justbrowser.app/fingerprint-checker — 17 rows, client-side, no signup. It grades what it claims rather than guessing: WebRTC candidates are compared against your public IP, and the IP's location is graded on whether its UTC offset actually agrees with your browser's timezone. Worth running before you read the rest of this, so the signals below have your numbers attached to them.

Prerequisites

Before we get into it:

  • JustBrowser installed — the 7-day trial is plenty for this walkthrough
  • A configured proxy — you need to know the target country
  • Browser developer console familiarity — we'll run JavaScript to verify configurations
  • 15 minutes — once you understand the concepts, the actual setup takes five

If you haven't configured timezone and geolocation settings yet, do that first. Intl settings build on top of geographic consistency. No point fixing Intl if your timezone is still wrong.

Step 1: Understand What the Intl API Actually Exposes

The Intl object in JavaScript provides three main formatters. Each one leaks regional information.

Intl.DateTimeFormat — Formats dates and times according to locale conventions.

new Intl.DateTimeFormat('de-DE').format(new Date())
// "7.7.2026" (German: day.month.year)

new Intl.DateTimeFormat('en-US').format(new Date())
// "7/7/2026" (American: month/day/year)

new Intl.DateTimeFormat('ja-JP').format(new Date())
// "2026/7/7" (Japanese: year/month/day)

Intl.NumberFormat — Formats numbers including decimals and currency.

new Intl.NumberFormat('de-DE').format(1234567.89)
// "1.234.567,89" (German: period for thousands, comma for decimals)

new Intl.NumberFormat('en-US').format(1234567.89)
// "1,234,567.89" (American: comma for thousands, period for decimals)

Intl.Collator — Sorts strings according to locale-specific rules.

['ä', 'z', 'a'].sort(new Intl.Collator('de').compare)
// German sorts ä near a

['ä', 'z', 'a'].sort(new Intl.Collator('sv').compare)
// Swedish sorts ä after z

Here's the kicker: each of these has a resolvedOptions() method that returns the actual settings in use:

new Intl.DateTimeFormat().resolvedOptions()
// Returns: { locale: "en-US", calendar: "gregory", numberingSystem: "latn", timeZone: "America/New_York", ... }

Detection scripts love this. They don't even need to trick you into formatting something — they just ask the browser what settings it would use. And if those settings don't match your proxy's region? Red flag.

The other piece is CLDR — the Common Locale Data Repository. This is the Unicode database that defines all those formatting rules. Browsers embed a specific CLDR version, and the exact formatting output can vary slightly between versions. CreepJS actually checks for CLDR-related inconsistencies. It's not the biggest signal, but it contributes to an overall fraud score when combined with other mismatches.

Step 2: Diagnose Your Current Intl Configuration

Before changing anything, check what your profile currently exposes. Open a JustBrowser profile and run these in the developer console:

Check DateTimeFormat:

console.log(Intl.DateTimeFormat().resolvedOptions());

Look at the locale field. Is it what you'd expect for your proxy's country?

Check NumberFormat:

console.log(Intl.NumberFormat().resolvedOptions());

The locale and numberingSystem fields matter here. Most Western locales use "latn" (Latin numerals), but the locale itself should match.

Check Collator:

console.log(Intl.Collator().resolvedOptions());

This shows locale and collation rules.

Quick formatting test:

console.log('Date:', new Intl.DateTimeFormat().format(new Date()));
console.log('Number:', new Intl.NumberFormat().format(1234567.89));
console.log('Currency:', new Intl.NumberFormat(undefined, {style: 'currency', currency: 'EUR'}).format(1234.56));

If you're using a German proxy but see "7/7/2026" instead of "7.7.2026", or "1,234,567.89" instead of "1.234.567,89", your Intl settings are leaking your real region.

I spent an embarrassing amount of time on this when I was running accounts for a client last year. Everything passed CreepJS except this one section I'd never even looked at. Felt pretty dumb.

Sometimes the obvious stuff isn't what gets you.

Step 3: Configure Intl Locale in JustBrowser

Open your profile settings in JustBrowser. Navigate to the Fingerprint configuration section.

Find the Locale / Regional Settings panel. JustBrowser lets you set:

  1. Languages — drives navigator.language/navigator.languages, the Accept-Language header and the Intl default locale together
  2. Timezone — Already covered in the timezone tutorial

In JustBrowser one languages setting covers both navigator.language and the Intl locale, so they cannot drift apart — set the first language to the proxy's region and both follow.

Set the languages list to match your proxy region:

  • Germany proxy → de-DE
  • France proxy → fr-FR
  • Japan proxy → ja-JP
  • UK proxy → en-GB
  • US proxy → en-US
  • Brazil proxy → pt-BR

Why one setting is the point:

A mismatch — navigator.language saying "de-DE" but Intl returning "en-US" — is exactly what detection scripts look for, and it's the failure mode you get when the two are wired separately.

JustBrowser's native Chromium engine patches this at the C++ level. When you set a German locale, the identity parameters that depend on it align: navigator.language, the Accept-Language header and the Intl defaults. Extension-based antidetect tools can override navigator.language easily, but they often can't touch the underlying Intl system locale — it's too deep in the browser.

That's the difference between extension-based and native engine modifications. Extensions operate in JavaScript userland. The Intl API calls down into Chromium's ICU (International Components for Unicode) library, which uses embedded CLDR data. You can't intercept that from an extension.

You need engine-level patches. Full stop.

Step 4: Verify the Configuration

After saving your profile settings, relaunch the profile and run the diagnostics again:

// Full verification script
console.log('=== Intl Configuration Test ===');
console.log('navigator.language:', navigator.language);
console.log('navigator.languages:', navigator.languages);
console.log('');
console.log('DateTimeFormat resolved:', Intl.DateTimeFormat().resolvedOptions());
console.log('NumberFormat resolved:', Intl.NumberFormat().resolvedOptions());
console.log('Collator resolved:', Intl.Collator().resolvedOptions());
console.log('');
console.log('Date format:', new Intl.DateTimeFormat().format(new Date()));
console.log('Number format:', new Intl.NumberFormat().format(1234567.89));

For a German proxy, you should see:

  • navigator.language: "de-DE" or "de"
  • DateTimeFormat locale: "de-DE" or "de"
  • Date format: "7.7.2026" (day.month.year with periods)
  • Number format: "1.234.567,89" (period for thousands, comma for decimals)

Run these external tests too:

  • CreepJS — Check the "Intl" and "Locale" sections specifically
  • BrowserLeaks — Look at JavaScript → Intl section
  • Pixelscan — Check for any locale-related flags

If CreepJS shows green on Intl and your formatted outputs match expectations for your target region, you're good. Move on. Don't overthink it.

Step 5: Handle Edge Cases

Mixed language preferences

Real users often have secondary languages. A German user might have ["de-DE", "de", "en-US", "en"] — German primary, English fallback. This is actually more realistic than pure single-language configs. JustBrowser lets you specify the full language list.

Numbering systems

Most locales use "latn" (Latin digits: 0-9). But some don't:

  • Arabic locales might use "arab" (Arabic-Indic digits)
  • Some Asian locales might use native scripts

If you're spoofing a region with non-Latin numerals and your browser returns "latn", that could be a signal. For most Western proxies, "latn" is expected. Check what real users in that region would have.

Calendar systems

Most locales use "gregory" (Gregorian calendar). Some use others:

  • Japan: "japanese" calendar alongside gregorian
  • Islamic countries: "islamic" variants
  • Thailand: "buddhist" calendar

Again, know your target region. A Japanese proxy might legitimately have either "gregory" or "japanese" calendar reported. I'll admit I still look this stuff up every time — there's no shame in double-checking.

Currency formatting

If you're testing with currency formatters, know the expected format:

  • EUR in Germany: "1.234,56 €" (symbol after, with space)
  • USD in US: "$1,234.56" (symbol before, no space)
  • JPY in Japan: "¥1,235" (symbol before, typically no decimals)

This gets checked by sophisticated anti-fraud detection systems and ad verification platforms. They'll render a price and see if it matches regional expectations.

Common Errors and How to Fix Them

resolvedOptions() shows wrong locale after configuration

Cause: Profile wasn't fully restarted, or the setting didn't save properly.

Fix: Close the profile completely in JustBrowser (not just the browser window). Relaunch. Check again. If still wrong, verify the setting is actually saved in your profile config.

Cause: These are separate settings in some antidetect tools. navigator.language was changed but system locale wasn't.

Fix: In JustBrowser, set the profile's languages list with the target region first — navigator.language and the Intl locale are derived from the same setting — then relaunch the profile. If you're using a different antidetect tool that can't set system locale, that's the limitation of extension-based architecture.

CreepJS flags "Intl locale mismatch"

Cause: One of the Intl objects is returning a different locale than others, or the locale doesn't match timezone/language signals.

Fix: Run the verification script from Step 4. Check all three — DateTimeFormat, NumberFormat, and Collator. They should all return the same locale. Also verify your timezone matches. CreepJS cross-references these.

Number formatting uses wrong separators

Cause: Locale is set correctly but the browser's CLDR data isn't matching. Rare, but happens with some stripped-down browser builds.

Fix: In JustBrowser, this shouldn't happen — the native engine includes full CLDR data. If you see it, check you're on the latest JustBrowser version. Older builds might have incomplete locale data.

Currency symbol positioning is wrong

Cause: Currency formatting rules are locale-specific and sometimes surprising. EUR displayed in Germany vs France vs Ireland all have slightly different conventions.

Fix: Match the exact locale, not just the language. "de-DE" and "de-AT" (Germany vs Austria) format some things differently. Know your target.

Next Steps

With Intl settings aligned, your profiles are one step closer to passing deep inspection. This was one of the trickier detection surfaces because it's not obvious — nobody thinks about number formatting as a fingerprint until a platform starts asking for extra verification because of it.

Kind of like password hygiene. Nobody cares until it bites them.

For the complete fingerprinting picture, check out our audio and hardware fingerprinting guide. And if you're running automation through these profiles — Playwright, Puppeteer, Selenium — the REST API integration guide covers how to set locale programmatically when spawning profiles.

If you're also running email outreach campaigns alongside accounts you are allowed to hold, consistent locale signals matter there too. Mailbox providers check language and regional signals. Same principle: consistency across all surfaces.

The colleague with the Japanese account? He set up the locale correctly in JustBrowser, so the profile behaved like an ordinary user from that region. Sometimes the fix really is that simple — you just have to know where to look.

And honestly? That's the frustrating part. These aren't sophisticated attacks. They're basic consistency checks. The platforms aren't running advanced ML fingerprinting on your Intl.NumberFormat output. They're just checking: does this user's browser behave like someone from where they claim to be?

When it doesn't, that can be enough to trigger a security check. If a platform restricts an account, use its own support and appeal route, and keep to what its rules allow.

Frequently Asked Questions

What is CLDR and why does it matter for browser fingerprinting?

CLDR (Common Locale Data Repository) is the Unicode Consortium's database that defines how dates, numbers, currencies, and text sorting work for every locale. Browsers embed CLDR data, and the specific version they ship affects formatting output. When you call Intl.DateTimeFormat('de-DE').format(), the browser uses CLDR rules for German formatting. Detection scripts can probe which CLDR version is embedded and cross-reference it against your browser's claimed version — or notice that your formatting patterns don't match your IP's expected region. A Tokyo proxy with en-US CLDR formatting stands out.

Can resolvedOptions() reveal my real locale even after spoofing navigator.language?

Yes. navigator.language can be spoofed separately from the Intl system locale. If you change navigator.language to 'ja-JP' but your underlying system locale is still en-US, calling Intl.DateTimeFormat().resolvedOptions() returns the actual locale the formatter is using — which might be 'en-US'. Detection scripts specifically call resolvedOptions() because it bypasses simple navigator overrides. You need both aligned: navigator.language AND the Intl system locale must match your proxy's region.

How do NumberFormat and Collator contribute to fingerprinting?

Intl.NumberFormat reveals decimal separators (1,000 vs 1.000), currency positioning, and digit grouping conventions that vary by locale. Intl.Collator exposes string sorting rules — German treats 'ä' differently than Swedish. Both expose resolvedOptions() with locale, numbering system, and collation rules. If your proxy is German but your NumberFormat uses US conventions (comma for thousands, period for decimals) and your Collator sorts with English rules, detection systems see the mismatch. These aren't dramatic leaks individually, but combined with timezone and language signals, they add evidence to a fraud score.

Does the Intl API expose my operating system's locale settings?

It depends on how the browser is configured. Stock browsers inherit from the OS locale — a Windows machine set to German will have German Intl defaults. Antidetect browsers like JustBrowser intercept these calls at the engine level, letting you set locale per profile independent of your system. But extension-based antidetect tools often can't override this deeply, which is why your Mac set to en-US might leak American formatting conventions even through a Japan proxy. Native engine patches are the only reliable fix.


Try JustBrowser

Native Chromium antidetect browser — not extension-based. Real C++ engine patches at the canvas / WebGL / audio / font / screen layer, so 40+ identity parameters are genuine, not faked. REST API for Playwright, Puppeteer, Selenium. $9.99/month or $99.99/year. 7-day free trial, card required — cancel any time in the seven days and you are not charged. Unlimited profiles.

Get started → · How it differs from Multilogin / GoLogin / AdsPower

Ready to manage multiple accounts?

Seven days free, then $9.99/month — one plan, everything included.

We'd like to use Google Analytics, a Google service, to understand how our website is used. It sets two cookies in your browser and runs only if you click Accept. You can change your choice at any time with Cookie settings. Cookie Policy

Sign-in cookies and the cookie that remembers this choice are always on; the website needs them to work.

Google Analytics, a Google service, helps us understand how our website is used. It sets two cookies, _ga and _ga_TVZHQ99TZW. It is now onoff in this browser. If your browser sends a Global Privacy Control or Do Not Track signal, it stays off. Cookie Policy