Context
Surfaced during the v0.5.299 docs audit. The perry/i18n module exports 8 functions per types/perry/i18n/index.d.ts:
t, Currency, Percent, ShortDate, LongDate, FormatNumber, FormatTime, Raw
All 7 format wrappers have runtime implementations in crates/perry-runtime/src/i18n.rs:633-823:
perry_i18n_format_number
perry_i18n_format_currency
perry_i18n_format_percent
perry_i18n_format_date (used by ShortDate and LongDate)
perry_i18n_format_time
But crates/perry-codegen/src/lower_call.rs only wires the t() lookup. Calls to Currency(99.99), Percent(0.5), etc. compile + link cleanly (because the runtime symbols exist) but reach the receiver-less native-call early-out at lower_call.rs:2849-2854 and return NaN-boxed undefined at runtime.
Repro:
import { Currency } from "perry/i18n"
console.log(Currency(99.99)) // prints `undefined`, expected "$99.99" (or locale equivalent)
What to do
Add UiSig rows in crates/perry-codegen/src/lower_call.rs (the same table that already wires perry/ui and perry/system) that map each TS-callable name to its runtime symbol:
UiSig { method: "Currency", runtime: "perry_i18n_format_currency", args: &[UiArgKind::F64], ret: UiReturnKind::Str },
UiSig { method: "Percent", runtime: "perry_i18n_format_percent", args: &[UiArgKind::F64], ret: UiReturnKind::Str },
UiSig { method: "FormatNumber", runtime: "perry_i18n_format_number", args: &[UiArgKind::F64], ret: UiReturnKind::Str },
UiSig { method: "ShortDate", runtime: "perry_i18n_format_date_short", args: &[UiArgKind::F64], ret: UiReturnKind::Str },
UiSig { method: "LongDate", runtime: "perry_i18n_format_date_long", args: &[UiArgKind::F64], ret: UiReturnKind::Str },
UiSig { method: "FormatTime", runtime: "perry_i18n_format_time", args: &[UiArgKind::F64], ret: UiReturnKind::Str },
UiSig { method: "Raw", runtime: "perry_i18n_format_raw", args: &[UiArgKind::F64], ret: UiReturnKind::Str },
ShortDate and LongDate may need either two distinct runtime exports or a single perry_i18n_format_date(value, style_flag) shape — check what the runtime currently exposes and pick whichever is least invasive.
Acceptance criteria
Pointers
- Wrapper definitions:
types/perry/i18n/index.d.ts
- Runtime implementations:
crates/perry-runtime/src/i18n.rs:633-823
- Existing dispatcher pattern:
crates/perry-codegen/src/lower_call.rs — search for UiSig { method: "isDarkMode" or UiSig { method: "audioStart" for the most-similar shape (perry/system uses the exact same dispatch table for cross-module functions).
- Failing call site:
lower_call.rs:2849-2854 (receiver-less early-out that swallows args).
Estimated size
Small — ~15 lines of dispatcher code plus one runtime exit-style runtime export if format_date doesn't currently distinguish short/long.
Context
Surfaced during the v0.5.299 docs audit. The
perry/i18nmodule exports 8 functions pertypes/perry/i18n/index.d.ts:All 7 format wrappers have runtime implementations in
crates/perry-runtime/src/i18n.rs:633-823:perry_i18n_format_numberperry_i18n_format_currencyperry_i18n_format_percentperry_i18n_format_date(used byShortDateandLongDate)perry_i18n_format_timeBut
crates/perry-codegen/src/lower_call.rsonly wires thet()lookup. Calls toCurrency(99.99),Percent(0.5), etc. compile + link cleanly (because the runtime symbols exist) but reach the receiver-less native-call early-out atlower_call.rs:2849-2854and return NaN-boxedundefinedat runtime.Repro:
What to do
Add
UiSigrows incrates/perry-codegen/src/lower_call.rs(the same table that already wiresperry/uiandperry/system) that map each TS-callable name to its runtime symbol:ShortDateandLongDatemay need either two distinct runtime exports or a singleperry_i18n_format_date(value, style_flag)shape — check what the runtime currently exposes and pick whichever is least invasive.Acceptance criteria
docs/examples/i18n/snippets.tscan drop its// run: falsebanner — the harness should run it to a clean exit.docs/src/i18n/formatting.mdandoverview.mdno longer need their "format wrappers reach the receiver-less early-out" status notes._expected/.../*.stdoutbyte-diffed.Pointers
types/perry/i18n/index.d.tscrates/perry-runtime/src/i18n.rs:633-823crates/perry-codegen/src/lower_call.rs— search forUiSig { method: "isDarkMode"orUiSig { method: "audioStart"for the most-similar shape (perry/system uses the exact same dispatch table for cross-module functions).lower_call.rs:2849-2854(receiver-less early-out that swallows args).Estimated size
Small — ~15 lines of dispatcher code plus one runtime exit-style runtime export if
format_datedoesn't currently distinguish short/long.