Indonesia Singapore ไทย Pilipinas Việt Nam Malaysia မြန်မာ ລາວ
← Back to Blog

Intl.NumberFormat and the Multilingual Data Layer Problem

Use Intl.NumberFormat to standardise currency and unit formatting in your data layer before values hit analytics or ad platforms.

A developer sitting at a desk surrounded by floating currency symbols from multiple Southeast Asian countries, each connecting to a central data stream
Illustrated by Mikael Venne

Intl.NumberFormat does more than format numbers — it's a native fix for multilingual tracking mismatches costing SEA brands real conversion data.

Somewhere in your analytics stack right now, a revenue figure is wrong. Not catastrophically wrong — just subtly, consistently wrong in a way that only surfaces when someone in finance asks why Thai Baht totals look off in your Google Ads reports. The culprit is almost never the tracking tag. It’s the string that fed it.

The Formatting Problem Nobody Puts in a Tracking Spec

Stefan Judis recently highlighted something that looks like a minor JavaScript curiosity: the native Intl.NumberFormat API can localise not just currencies, but physical units — kilometres per hour, kilobytes, and more — adapting symbol placement, decimal separators, and even unit translations based on locale.

For a developer building a single-market site, this is a neat trick. For a tracking architect maintaining a data layer across five Southeast Asian markets, it’s a structural fix for a recurring headache.

The problem is this: when revenue values or quantity fields are formatted inconsistently before they’re pushed to dataLayer, downstream tools — Google Tag Manager, GA4, Meta CAPI, your CDP — receive strings instead of clean numerics, or numerics formatted with the wrong decimal convention. Thai Baht (฿) has no subunit, so ฿1,500 and 1500 mean the same thing to a human but wildly different things to a regex-dependent tag. Indonesian Rupiah routinely runs to seven digits, which breaks formatting assumptions baked into tag templates written for USD.

What Intl.NumberFormat Actually Solves at the Data Layer Level

The correct architecture is to use Intl.NumberFormat to render localised values in the UI, while pushing raw numeric values — unformatted, locale-agnostic — into dataLayer. These are two separate concerns that too many implementations collapse into one.

Here’s the pattern:

// What the user sees
const display = new Intl.NumberFormat('th-TH', {
  style: 'currency',
  currency: 'THB'
}).format(1500); // "฿1,500.00"

// What goes into dataLayer
window.dataLayer.push({
  event: 'purchase',
  value: 1500,        // raw number, always
  currency: 'THB'     // ISO 4217 string, always
});

This separation means your tag logic never has to parse a formatted string, your GA4 purchase events stay clean, and your Meta Conversions API payload passes server-side validation without a custom transformation step. The Intl API handles the display layer; your data layer stays machine-readable.

For teams running server-side tagging through a proxy like sGTM, this also removes the need for GTM variable transformations that are brittle under locale switches — a common failure point when a user changes their preferred language mid-session on a Lazada or Shopee-style marketplace.


Unit Localisation and the Metrics Reporting Gap

The unit formatting capability — style: 'unit' — is less immediately obvious in a tracking context, but it matters for product and content teams analysing engagement with specification-heavy pages. Think automotive brands listing fuel efficiency across Thai, Vietnamese, and Philippine markets, or logistics platforms showing delivery ETAs in hours versus days depending on locale.

If those unit values are being captured as event parameters (say, a view_item event with a product_weight or range_km field), inconsistent unit strings will silently fragment your reporting. Aggregating "50 km/h", "50kmh", and "50 กม./ชม." across locales produces three separate dimension values in GA4’s item-scoped reporting — none of which are comparable.

The fix is the same structural one: render with Intl, transmit with a canonical numeric value plus a separate unit string. Define the unit vocabulary in your data layer spec upfront and enforce it in your QA layer before anything ships. No tag fires without a matching schema check — that’s not a policy, it’s a deployment gate.

Making the Spec Change Stick with Engineering

The harder problem here isn’t technical — it’s organisational. Tracking specs that distinguish between display formatting and data layer values require buy-in from front-end engineers who are, reasonably, optimising for shipping speed rather than analytics data quality.

The argument that lands is not “our data will be cleaner.” It’s “this saves you from a regression ticket in three months when the localisation team adds a new market and your revenue tracking breaks silently.” Framing data layer hygiene as a prevention of future engineering work — rather than an abstract quality standard — tends to shorten the negotiation.

A practical step: include a data layer linting rule in your CI pipeline that validates value fields as typeof number before merge. Tools like Avo, schema registries in Segment Protocols, or even a lightweight custom validator in your test suite can enforce this without requiring anyone to manually audit tag implementations. If it fails the type check, it doesn’t ship.

The Intl API has been available across all modern browsers for years. The gap isn’t browser support — it’s the absence of a clear architectural pattern that separates rendering concerns from tracking concerns. That pattern is worth documenting once, enforcing consistently, and never relitigating market by market.

Key Takeaways

  • Push raw numeric values and ISO currency codes to dataLayer — use Intl.NumberFormat exclusively for UI rendering, never for formatting values before tracking ingestion.
  • Define a canonical unit vocabulary in your data layer spec upfront; inconsistent unit strings will silently split reporting dimensions across multilingual markets.
  • Enforce value field type validation in your CI pipeline so locale-formatting regressions are caught at merge, not in a finance review six weeks post-launch.

As Southeast Asian brands scale across four, five, six markets simultaneously, the data layer spec is increasingly the document that determines whether your attribution model is trustworthy or merely plausible. The technical fix here is simple. The structural question worth sitting with: how many of your current event schemas were written with a single market in mind?


At grzzly, we’ve built and audited data layer architectures for brands operating across SEA’s most complex multi-market, multi-language environments — the kind where a tracking spec has to survive contact with five currencies, three alphabets, and two CDPs at once. If your current setup was designed for one market and has been quietly stretched to cover more, it’s probably time to look under the hood. Let’s talk

A developer sitting at a desk surrounded by floating currency symbols from multiple Southeast Asian countries, each connecting to a central data stream
Illustrated by Mikael Venne
Cryptic Grizzly

Written by

Cryptic Grizzly

Fluent in server-side tagging, consent-mode logic, and the intricate diplomacy of getting marketing and engineering to agree on a data layer. Nothing ships without a QA plan.

Enjoyed this?
Let's talk.

Start a conversation