Files
Chris Tate fa7cc089a9 The en-US Intl slice: composed NumberFormat and toLocaleString go static, the rest fences by name
- new Intl.NumberFormat("en-US").format(x), the callable spelling, and x.toLocaleString("en-US") lower to one runtime formatter with default options: decimal notation, 0-3 fraction digits rounded half-up on the SHORTEST round-tripping decimal (ICU's rounding input, probed against Node — format(1.0005) is "1.001" though toFixed(3) answers "1.000"; 1e23 prints the shortest form's zeros, not the exact expansion), "," grouping, "∞"/"NaN" texts, and "-0" for negatives rounding to zero. The en-US/latn symbols are the whole embedded locale surface — no ICU is linked.
- The Ryū digit core factors out of scr_f64_to_str as scr_f64_digits (same bytes; the 1M-double oracle gate stays green) and feeds the formatter; a 30,000-double ad-hoc fuzz across magnitude regimes matched Node byte-for-byte on both backends.
- Every unlowered Intl form fences by NAME with its own reason: the environment-default locale a compiled binary cannot carry (no-locale forms), other locales and the rest of the namespace (DurationFormat, DateTimeFormat, PluralRules, getCanonicalLocales — ICU data the binary does not embed; Node 24's DurationFormat additionally throws V8-internal Temporal errors with embedded source paths, so byte parity there is a version-dependent landmine), options bags, formatter values (the composed form is the lowered shape), and non-number format arguments.
- Differential fixture 2562 pins the rounding discriminators, grouping, ±0/NaN/∞, tiny and huge edges, and both spellings byte-identically vs Node on both backends; the new intl diagnostics fixture pins every named fence, and the Intl hints across the identifier/member/type chokepoints now teach what lowers.
2026-07-23 20:30:47 -05:00

26 lines
1.3 KiB
TypeScript

// The Intl surface. What lowers: the COMPOSED en-US NumberFormat forms
// with default options — new Intl.NumberFormat("en-US").format(x) and
// x.toLocaleString("en-US"). Everything else fences by NAME: the
// environment-default locale a compiled binary cannot carry, other
// locales (ICU data the binary does not embed), options bags, formatter
// VALUES, and the rest of the Intl namespace (DurationFormat,
// DateTimeFormat, PluralRules, getCanonicalLocales).
// The unlowered NumberFormat variants, each with its own reason.
const noLocale = new Intl.NumberFormat().format(1234.5);
const otherLocale = new Intl.NumberFormat("de-DE").format(1234.5);
const withOptions = new Intl.NumberFormat("en-US", { maximumFractionDigits: 2 }).format(1234.5);
const hoisted = new Intl.NumberFormat("en-US");
const viaValue = hoisted.format(1234.5);
// toLocaleString's unlowered variants.
const envDefault = (1234.5).toLocaleString();
const german = (1234.5).toLocaleString("de-DE");
const grouped = (1234.5).toLocaleString("en-US", { useGrouping: false });
// The rest of Intl keeps the ICU story.
const when = new Intl.DateTimeFormat("en-US").format();
const dur = new Intl.DurationFormat("en-US").format({ hours: 1, minutes: 23 });
const rule = new Intl.PluralRules("en-US").select(1);
const canon = Intl.getCanonicalLocales("en-us");