7.7 KiB
@suaveplan/i18n-formatter-display-name — Intl.DisplayNames wrapper
Package: @suaveplan/i18n-formatter-display-name (NEW)
Status: PROPOSED
Type: feature (new universal-tier package; verbatim port of formatting/display-name/ plus class wrapper + Zod schema + observability uplift)
Date: 2026-05-05
Wave: 3 (depends on Wave 1 foundation; required by Wave 6 core slim)
Design doc: openspec/cross-cutting-designs/2026-05-05-i18n-modular-split.md (especially §5 formatter package shape, §7.1)
Path: packages/i18n/i18n-formatter-display-name/
Tier: universal
Why
The 14-package i18n modular split lifts each Intl.* wrapper out of the monolith into its own sibling package. @suaveplan/i18n-formatter-display-name ports the existing formatting/display-name/formatDisplayName standalone helper, adds the DisplayNameFormatter class wrapper for symmetry with the other formatters, and brings the package up to the canonical Wave-3 shape.
Today formatting/display-name/ ships a thin slice:
formatDisplayName(code, locale, options)standalone helper (packages/i18n/i18n/src/formatting/display-name/display-name.tsL57–L64)clearDisplayNameCachetest-only helper (display-name.tsL67–L69)- Per-(locale, type, style, fallback) cache (
display-name.tsL21, L23–L47) UNSUPPORTED_LOCALEmapping (display-name.tsL40–L46)DisplayNameType,DisplayNameStyle,DisplayNameFallback,DisplayNameOptionstype aliases (display-name.tsL4–L19)
The split adds the class wrapper, Zod-validated options, INVALID_FORMATTER_OPTIONS mapping, and OTel histogram + cache-hit counter so the package is consistent with the rest of the formatter family.
What changes
New package: @suaveplan/i18n-formatter-display-name
- ADDED:
packages/i18n/i18n-formatter-display-name/package scaffold (package.json,tsconfig.json,vite.config.ts,README.md). - ADDED:
src/formatter.ts—DisplayNameFormatterclass (NEW);formatDisplayNamestandalone (port of monolith L57–L64);clearDisplayNameCache(port of L67–L69). Per-(locale, type, style, fallback) cache key from monolith L27–L29. - ADDED:
src/formatter.types.ts—DisplayNameType("language" | "region" | "script" | "currency" | "calendar" | "dateTimeField"),DisplayNameStyle("long" | "short" | "narrow"),DisplayNameFallback("code" | "none"),DisplayNameFormatterOptions,DisplayNameFormatterCacheKey. Sourced from monolithdisplay-name.tsL4–L19 plus new types for the class surface. - ADDED:
src/formatter.schemas.ts—DisplayNameFormatterOptionsSchemaZod schema. NEW (the monolith does not currently Zod-validate options). - ADDED:
src/helpers.ts—formatLanguageName(code, locale, style?),formatRegionName,formatCurrencyName,formatScriptName,formatCalendarNameergonomic shortcuts that pre-bindformatDisplayNamewith the correspondingtype. - ADDED:
src/observability.ts— local OTel histogrami18n.format.display_name.duration_ms+ counteri18n.format.cache_hit{formatter="display_name"}. - ADDED:
src/formatter.test.ts— port of monolithdisplay-name.test.ts(93 lines) plus new tests coveringDisplayNameFormatterclass, Zod-validation, cache-hit behaviour. - ADDED:
src/formatter.spec-conformance.test.ts— every requirement scenario in this delta. - ADDED:
src/helpers.test.ts— covers all five ergonomic helpers. - ADDED:
src/index.ts— explicit named re-exports. - ADDED:
src/formatter.md,src/helpers.md,src/observability.md— sibling docs.
Subpath exports
.— root barrel./formatter—DisplayNameFormatterclass +formatDisplayName+ types + schema only./helpers—formatLanguageName,formatRegionName,formatCurrencyName,formatScriptName,formatCalendarNameonly
Dependencies
@suaveplan/i18n-shared workspace:^—I18N_CODES,I18nError@suaveplan/types workspace:^@suaveplan/error workspace:^@opentelemetry/api catalog:observabilitytslib catalog:buildzod catalog:forms- No dep on any other formatter package, on
@suaveplan/i18ncore, or on any backend package.
Out of scope
- Removing
formatting/display-name/frompackages/i18n/i18n/src/— happens in Wave 6. formatToPartsfor display-name output — out of scope;Intl.DisplayNamesdoes not expose it.- CLDR-based fallback strategies beyond
code/none— out of scope; matchesIntl.DisplayNamesfallback semantics.
Source mapping
| Requirement | Source file | Source lines | Notes |
|---|---|---|---|
formatDisplayName standalone |
packages/i18n/i18n/src/formatting/display-name/display-name.ts |
L57–L64 | Verbatim port |
clearDisplayNameCache |
packages/i18n/i18n/src/formatting/display-name/display-name.ts |
L67–L69 | Verbatim port |
| Per-(locale, type, style, fallback) cache | packages/i18n/i18n/src/formatting/display-name/display-name.ts |
L21, L23–L47 | Verbatim port |
UNSUPPORTED_LOCALE mapping |
packages/i18n/i18n/src/formatting/display-name/display-name.ts |
L40–L46 | Verbatim port; throws I18nError from @suaveplan/i18n-shared |
Type aliases (DisplayNameType, DisplayNameStyle, DisplayNameFallback, DisplayNameOptions) |
packages/i18n/i18n/src/formatting/display-name/display-name.ts |
L4–L19 | Verbatim port; rename DisplayNameOptions → DisplayNameFormatterOptions for symmetry |
Requirements with no source line
| Requirement | Justification |
|---|---|
DisplayNameFormatter class wrapper |
New — symmetric with NumberFormatter, RelativeTimeFormatter, ListFormatter, DateFormatter |
DisplayNameFormatterOptionsSchema Zod schema |
New — current monolith does not validate options; canonical Wave-3 formatter shape mandates it |
INVALID_FORMATTER_OPTIONS mapping |
Bound to the new Zod schema |
OTel histogram i18n.format.display_name.duration_ms + cache-hit counter |
New — canonical Wave-3 formatter shape mandates these |
Five ergonomic helpers (formatLanguageName, formatRegionName, formatCurrencyName, formatScriptName, formatCalendarName) |
Symmetric with formatPercent / formatCompact for the number formatter; pre-binds the required type so consumers don't have to remember the discriminant string |
Verification approach
packages/i18n/i18n-formatter-display-name/src/formatter.test.ts— port of monolithdisplay-name.test.ts; cover all sixtypevalues; long / short / narrow styles;code/nonefallback; class wrapper twins helper output; Zod failure →INVALID_FORMATTER_OPTIONS; bogus locale →UNSUPPORTED_LOCALEpackages/i18n/i18n-formatter-display-name/src/formatter.spec-conformance.test.ts— every requirement scenario runs as an executable testpackages/i18n/i18n-formatter-display-name/src/helpers.test.ts—formatLanguageName("fr", "en-GB")→"French";formatRegionName("GB", "en-GB")→"United Kingdom";formatCurrencyName("USD", "en-GB")→"US Dollar";formatScriptName("Latn", "en-GB")→"Latin";formatCalendarName("gregory", "en-GB")returns the gregorian display namepackages/i18n/i18n-formatter-display-name/src/observability.test.ts— histogram namedi18n.format.display_name.duration_ms; counteri18n.format.cache_hit{formatter="display_name"}increments only on cache hitpackages/i18n/i18n-formatter-display-name/src/index.test.ts— every documented public export resolves; subpaths resolve
Impact
- New universal-tier package — joins the rest of the i18n family.
- Indirect consumer in Wave 6:
@suaveplan/i18nslim core re-exportsDisplayNameFormatterandformatDisplayNameunder the back-compat root barrel. MANIFEST.mdandCLAUDE.mdupdated post-merge.