Python f-string-style value formatting for catalog messages: {dmg:03d}, {hp:.1%},
{name:>12}. Plain {token} interpolation stays exactly as it was; a format spec after
a colon picks a deterministic, locale-independent rendering (padded damage numbers, hex
ids, fixed-precision percentages), the counterpart to Format.ts, whose Intl wrappers
render the locale-aware way instead ("1,234" vs "1 234").
Supported subset of
https://docs.python.org/3/library/string.html#format-specification-mini-language -
[[fill]align][sign][z][#][0][width][,|_][.precision][type] with types
b c d e E f F g G n o s x X %, verified case by case against CPython itself
(two-digit exponents, stripped g precision, _ grouping binary/octal/hex in fours).
Deliberately outside the subset: nested replacement fields in the spec ({:{width}}),
which are left for a game to resolve before calling t(). Anything outside the subset
returns undefined rather than guessing, and t() leaves that placeholder untouched
the way it already does for a missing token.
One divergence no implementation choice can close: JavaScript numbers carry no
int/float distinction, so integer-valued floats always take the integer path (3.0
renders as 3, and takes d where CPython would raise for a float). Games that need
3.0 spell it with an explicit float type ({v:.1f}).
Python f-string-style value formatting for catalog messages:
{dmg:03d},{hp:.1%},{name:>12}. Plain{token}interpolation stays exactly as it was; a format spec after a colon picks a deterministic, locale-independent rendering (padded damage numbers, hex ids, fixed-precision percentages), the counterpart toFormat.ts, whoseIntlwrappers render the locale-aware way instead ("1,234" vs "1 234").Supported subset of https://docs.python.org/3/library/string.html#format-specification-mini-language -
[[fill]align][sign][z][#][0][width][,|_][.precision][type]with typesb c d e E f F g G n o s x X %, verified case by case against CPython itself (two-digit exponents, strippedgprecision,_grouping binary/octal/hex in fours). Deliberately outside the subset: nested replacement fields in the spec ({:{width}}), which are left for a game to resolve before callingt(). Anything outside the subset returnsundefinedrather than guessing, andt()leaves that placeholder untouched the way it already does for a missing token.One divergence no implementation choice can close: JavaScript numbers carry no int/float distinction, so integer-valued floats always take the integer path (
3.0renders as3, and takesdwhere CPython would raise for a float). Games that need3.0spell it with an explicit float type ({v:.1f}).Example