Files
2026-05-27 23:29:53 +01:00

8.3 KiB

Capability Spec: i18n-formatter-display-name (i18n-formatter-display-name)

Package: @suaveplan/i18n-formatter-display-name Status: ACTIVE

Every requirement below carries a **Verified by:** line at authoring time. (See openspec/AGENTS.md Section 13.)

Requirements

Requirement: DisplayNameFormatter class wrapping Intl.DisplayNames

The package SHALL export a DisplayNameFormatter class that wraps Intl.DisplayNames with construction-time Zod validation of options and a format(code: string, locale: string): string method. Construction with malformed options SHALL throw I18nError(I18N.INVALID_FORMATTER_OPTIONS). The type option is required.

Scenario: format with type: language

  • GIVEN const f = new DisplayNameFormatter({ type: "language" })
  • WHEN f.format("fr", "en-GB") is called
  • THEN the result equals "French"

Scenario: missing type throws INVALID_FORMATTER_OPTIONS

  • WHEN new DisplayNameFormatter({} as never) is called
  • THEN the call throws I18nError with code "I18N.INVALID_FORMATTER_OPTIONS"

Verified by: packages/i18n/i18n-formatter-display-name/src/formatter.test.ts::"DisplayNameFormatter formats language" AND packages/i18n/i18n-formatter-display-name/src/formatter.test.ts::"DisplayNameFormatter rejects missing type with INVALID_FORMATTER_OPTIONS"

Requirement: formatDisplayName standalone helper

The package SHALL export a formatDisplayName(code, locale, options) standalone helper that delivers the same behaviour as DisplayNameFormatter.format without requiring class construction. Defaults: style: "long", fallback: "code".

Scenario: standalone helper formats region

  • WHEN formatDisplayName("GB", "en-GB", { type: "region" }) is called
  • THEN the result equals "United Kingdom"

Scenario: standalone helper twins class output

  • WHEN both formatDisplayName(code, l, opts) and new DisplayNameFormatter(opts).format(code, l) are called for the same (code, l, opts)
  • THEN the results are identical

Verified by: packages/i18n/i18n-formatter-display-name/src/formatter.test.ts::"formatDisplayName formats region" AND packages/i18n/i18n-formatter-display-name/src/formatter.test.ts::"formatDisplayName output twins DisplayNameFormatter.format output"

Requirement: Intl.DisplayNames RangeError maps to UNSUPPORTED_LOCALE

The package SHALL catch every RangeError thrown by the Intl.DisplayNames constructor and re-throw it as I18nError(I18N.UNSUPPORTED_LOCALE) with the original error attached as cause and the locale string in context.

Scenario: bogus locale throws UNSUPPORTED_LOCALE

  • WHEN formatDisplayName("fr", "xx-bogus-tag-!!!", { type: "language" }) is called
  • THEN the call throws I18nError with code "I18N.UNSUPPORTED_LOCALE" AND the error's cause is the original RangeError

Verified by: packages/i18n/i18n-formatter-display-name/src/formatter.test.ts::"unsupported locale throws UNSUPPORTED_LOCALE with cause"

Requirement: Per-(locale, type, style, fallback) cache

The package SHALL maintain an internal Map<string, Intl.DisplayNames> keyed by ${locale}|${type}|${style}|${fallback}. Repeated calls with identical key SHALL reuse the same Intl.DisplayNames instance. The package SHALL export a clearDisplayNameCache() test-only helper.

Scenario: identical key reuses the same Intl.DisplayNames

  • GIVEN the cache is empty
  • WHEN formatDisplayName("fr", "en-GB", { type: "language" }) is called twice in sequence
  • THEN only one Intl.DisplayNames constructor invocation occurs

Scenario: distinct type creates distinct cache entries

  • WHEN formatDisplayName("fr", "en-GB", { type: "language" }) and formatDisplayName("fr", "en-GB", { type: "region" }) are called
  • THEN two distinct Intl.DisplayNames instances are created and cached

Verified by: packages/i18n/i18n-formatter-display-name/src/formatter.test.ts::"identical key reuses cached Intl.DisplayNames" AND packages/i18n/i18n-formatter-display-name/src/formatter.test.ts::"distinct type creates distinct cache entries"

Requirement: OTel histogram i18n.format.display_name.duration_ms

The package SHALL emit an OTel histogram named exactly i18n.format.display_name.duration_ms (unit ms) on every successful formatDisplayName call, tagged with locale, type, and style.

Scenario: histogram emitted on every call

  • GIVEN an OTel test exporter capturing histogram observations
  • WHEN formatDisplayName("fr", "en-GB", { type: "language", style: "long" }) is called
  • THEN exactly one observation appears under i18n.format.display_name.duration_ms with locale, type, and style attributes

Verified by: packages/i18n/i18n-formatter-display-name/src/observability.test.ts::"emits i18n.format.display_name.duration_ms histogram on every call"

Requirement: OTel cache-hit counter i18n.format.cache_hit

The package SHALL emit an OTel counter i18n.format.cache_hit tagged with formatter: "display_name" on every cache hit. The counter SHALL NOT increment on the first (cache-miss) call.

Scenario: cache-hit counter increments only on second call

  • GIVEN an OTel test exporter capturing counter observations and an empty cache
  • WHEN formatDisplayName("fr", "en-GB", { type: "language" }) is called twice in sequence
  • THEN the counter i18n.format.cache_hit{formatter="display_name"} shows exactly one increment

Verified by: packages/i18n/i18n-formatter-display-name/src/observability.test.ts::"cache-hit counter increments only on second call with same options"

Requirement: Ergonomic helpers — language, region, currency, script, calendar

The package SHALL export formatLanguageName, formatRegionName, formatCurrencyName, formatScriptName, and formatCalendarName ergonomic shortcuts pre-binding formatDisplayName with the corresponding type.

Scenario: formatLanguageName renders French

  • WHEN formatLanguageName("fr", "en-GB") is called
  • THEN the result equals "French"

Scenario: formatRegionName renders United Kingdom

  • WHEN formatRegionName("GB", "en-GB") is called
  • THEN the result equals "United Kingdom"

Scenario: formatCurrencyName renders US Dollar

  • WHEN formatCurrencyName("USD", "en-GB") is called
  • THEN the result equals "US Dollar"

Scenario: formatScriptName renders Latin

  • WHEN formatScriptName("Latn", "en-GB") is called
  • THEN the result equals "Latin"

Scenario: formatCalendarName renders gregorian

  • WHEN formatCalendarName("gregory", "en-GB") is called
  • THEN the result is the locale-correct gregorian display name (non-empty)

Verified by: packages/i18n/i18n-formatter-display-name/src/helpers.test.ts::"formatLanguageName renders French" AND packages/i18n/i18n-formatter-display-name/src/helpers.test.ts::"formatRegionName renders United Kingdom" AND packages/i18n/i18n-formatter-display-name/src/helpers.test.ts::"formatCurrencyName renders US Dollar" AND packages/i18n/i18n-formatter-display-name/src/helpers.test.ts::"formatScriptName renders Latin" AND packages/i18n/i18n-formatter-display-name/src/helpers.test.ts::"formatCalendarName renders gregorian display name"

Requirement: Subpath exports

The package SHALL declare subpath exports in package.json:

  • @suaveplan/i18n-formatter-display-name — root barrel
  • @suaveplan/i18n-formatter-display-name/formatter — DisplayNameFormatter class + formatDisplayName + types + schema only
  • @suaveplan/i18n-formatter-display-name/helpers — five ergonomic helpers only

Scenario: every documented subpath resolves

  • WHEN every documented subpath is imported in a consumer test fixture
  • THEN every import resolves with no Module not found errors

Verified by: packages/i18n/i18n-formatter-display-name/src/index.test.ts::"every documented subpath export resolves"

Requirement: Side-effect-free package

The package SHALL declare "sideEffects": false in package.json.

Scenario: package.json declares sideEffects false

  • WHEN package.json is parsed
  • THEN the sideEffects field equals false

Verified by: packages/i18n/i18n-formatter-display-name/src/index.test.ts::"package.json declares sideEffects false"