6.4 KiB
6.4 KiB
Tasks — i18n number and relative-time formatters
Every checkbox MUST reflect work actually done in this change. Do not retroactively tick.
Audit Note (2026-05-02)
Minimal progress — DEFERRED, do not archive.
packages/i18n/i18n/src/formatting/only containscurrencyanddate. Nonumber/orrelative-time/directories.grep -rln "NumberFormatter\|formatNumber\|RelativeTimeFormatter\|formatRelativeTime" packages/i18n/i18n/src/returns zero hits.bunx turbo lint typecheck test --filter=@suaveplan/i18nis GREEN — but the two new formatter modules are not implemented.- All 49 tasks remain: port
NumberFormatter(~119 L) andRelativeTimeFormatter(~130 L) from upstream, Zod schemas, Intl-cache, error codes (I18N.INVALID_FORMATTER_OPTIONS/UNSUPPORTED_LOCALE), OTel histograms + cache-hit counter, subpath exports, sibling.mds, README links.
1. Spec authoring
- 1.1 Read
../suaveplan/enterprise/i18n.mdend-to-end - 1.2 Read
openspec/specs/i18n/spec.md(canonical capability spec) - 1.3 Read
openspec/AGENTS.mdSections 13 and 14 - 1.4 Draft
proposal.mdfromopenspec/templates/proposal.md - 1.5 Fill
## Source mappingtable — cite upstreamnumber.ts/relative-time.tsline ranges - 1.6 Draft
specs/i18n/spec.mdfromopenspec/templates/spec-delta.md - 1.7 Every requirement carries a
**Verified by:**line naming concreteit(...)titles - 1.8
openspec change validate i18n-formattersexits with no errors
2. Implementation — NumberFormatter
- 2.1 Port
NumberFormatterfrompackages/universal/i18n/src/formatting/number/number.ts(L1-L119) intopackages/i18n/i18n/src/formatting/number/number.ts - 2.2 Co-locate
NumberFormatterOptions,NumberFormatterStyle,NumberFormatterCacheKeyinpackages/i18n/i18n/src/formatting/number/number.types.ts - 2.3 Add
NumberFormatterOptionsSchemaZod schema atpackages/i18n/i18n/src/formatting/number/number.schema.ts - 2.4 Implement (locale, options-hash) cache for
Intl.NumberFormat; exposeclearCache()for tests - 2.5 Throw
I18nErrorwithI18N.INVALID_FORMATTER_OPTIONSon Zod failure;I18N.UNSUPPORTED_LOCALEonRangeErrorfromIntl.NumberFormatconstructor - 2.6 Export
formatNumber(value, locale, options?)standalone helper
3. Implementation — RelativeTimeFormatter
- 3.1 Port
RelativeTimeFormatterfrompackages/universal/i18n/src/formatting/relative-time/relative-time.ts(L1-L130) intopackages/i18n/i18n/src/formatting/relative-time/relative-time.ts - 3.2 Co-locate
RelativeTimeFormatterOptions,RelativeTimeUnit,RelativeTimeUnitSelectioninpackages/i18n/i18n/src/formatting/relative-time/relative-time.types.ts - 3.3 Add
RelativeTimeFormatterOptionsSchemaZod schema - 3.4 Port unit threshold table (seconds → minutes → hours → days → weeks → months → years) verbatim from L40-L96
- 3.5 Implement (locale, options-hash) cache for
Intl.RelativeTimeFormat - 3.6 Export
formatRelativeTime(deltaMs, locale, options?)standalone helper
4. Error codes and observability
- 4.1 Add
I18N.INVALID_FORMATTER_OPTIONS,I18N.UNSUPPORTED_LOCALEtopackages/i18n/i18n/src/errors/error-codes.tsviadefineCodes - 4.2 Emit OTel histograms
i18n.format.number.duration_msandi18n.format.relative_time.duration_msfrom production paths - 4.3 Emit OTel counter
i18n.format.cache_hit(tagged by formatter type) on cache reuse
5. Wiring
- 5.1 Update
packages/i18n/i18n/src/formatting/index.tsto re-exportNumberFormatter,formatNumber,RelativeTimeFormatter,formatRelativeTimeand their types - 5.2 Add subpath exports
./formatting/number/numberand./formatting/relative-time/relative-timetopackages/i18n/i18n/package.jsonexports - 5.3 Update root
README.md## Modulessection to link the two new co-located.mds
6. Tests
- 6.1
packages/i18n/i18n/src/formatting/number/number.test.ts— decimal, percent, currency styles round-trip; fraction-digit clamping; useGrouping toggle; notation modes (standard/compact/scientific/engineering) - 6.2
packages/i18n/i18n/src/formatting/number/number.spec-conformance.test.ts— Zod rejects unknown style →INVALID_FORMATTER_OPTIONS; bogus locale →UNSUPPORTED_LOCALE; cache hit increments OTel counter - 6.3
packages/i18n/i18n/src/formatting/relative-time/relative-time.test.ts— auto-unit selection across boundaries (60s → 1m, 60m → 1h, etc.); negative deltas; style and numeric overrides - 6.4
packages/i18n/i18n/src/formatting/relative-time/relative-time.spec-conformance.test.ts— Zod rejects unknown style; histogram recordsduration_ms;expectTypeOf<RelativeTimeUnit>matches the literal union - 6.5 Every
it(...)carries{ timeout: 5000 }
7. Documentation
- 7.1 Sibling
.mdfornumber.ts(≥200 words, basic + advanced examples, full API reference) - 7.2 Sibling
.mdforrelative-time.ts(≥200 words, basic + advanced examples) - 7.3 README links both new
.mds under## Modules
8. Pre-archive verification (the 5-gate triple)
- 8.1
bun run check— Biome 0/0/0 across the monorepo - 8.2
bunx turbo lint typecheck test --filter=@suaveplan/i18n— green - 8.3
bunx turbo test:coverage --filter=@suaveplan/i18n— 100/100/100/100 - 8.4
bun scripts/openspec-conformance.ts @suaveplan/i18n— exit 0, noerrorfindings - 8.5
bunx turbo build --filter=@suaveplan/i18n— green
9. Pre-archive 7-row checklist (AGENTS.md Section 13)
- 9.1 Generics threaded;
expectTypeOfassertions onNumberFormatterStyleandRelativeTimeUnitliteral unions - 9.2 Zod schemas wired at every public boundary (
NumberFormatterOptionsSchema,RelativeTimeFormatterOptionsSchema) - 9.3 Both new error codes thrown from at least one production path
- 9.4 Strict-mode tested: bogus locale fallback vs throw paths
- 9.5 OTel histograms / counters emitted from production paths and asserted with a real in-memory
MeterProvider - 9.6 Lifecycle dedup: cache reuse verified;
clearCache()empties the map - 9.7 No retroactive tick — every box reflects work done in this change
10. Archive
- 10.1 Fold spec delta into
openspec/specs/i18n/spec.md - 10.2 Move
openspec/changes/i18n-formatters/→openspec/archive/<yyyy-mm-dd>-i18n-formatters/ - 10.3 Refresh
MANIFEST.mdif package set or deps changed