Relocate the ~6k-file docs tree from the repo root into plugins/document-navigator/docs/ so the plugin is self-contained and portable. docs/superpowers/ (specs and plans) stays at the repo root. Moved via cp+rm rather than git mv because directory rename() fails with EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the identical-content files at commit time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
75 KiB
@suaveplan/react-i18n - React bindings for @suaveplan/i18n: TranslationProvider, hooks, , , locale-detection chain glue, branded TranslationKey contract.
Summary
React bindings for @suaveplan/i18n. Ships the <TranslationProvider>,
twelve hooks, four headless components, the class-component HOC, the
nine built-in locale detectors plus the default chain, the
useLocaleDetection hook, the branded TranslationKey typing contract,
the tDynamic runtime escape hatch, the React-tier OTel instruments,
and the warn-once / identity-translator soft-failure substrate. The
package is the leaf-most React library in the project — every other
React package augments it (via ContributedTranslationKeys) but no
React package may declare it as a downstream dependency. The
check-no-backwards-deps.ts
CI gate enforces that invariant on every commit.
Documentation tree
- i18n-suspense — (no submodules)
- fallback-bundle — Ships the strings that
<I18nSuspense>needs before the active catalog loads its own copies. Consumers can override th… - i18n-suspense — (no submodules)
- fallback-bundle — Ships the strings that
- language-switcher — (no submodules)
- language-switcher — (no submodules)
- missing-key-overlay — (no submodules)
- missing-key-overlay — (no submodules)
- trans — (no submodules)
- stringify-children — Helpers that translate a
<Trans>childrenblock into a canonical ICU message body string so the message parser rend… - trans — (no submodules)
- stringify-children — Helpers that translate a
- create-i18n-config — Builds a validated
I18nConfigfrom a caller-supplied option bag and is the canonical entry point for `<TranslationPro… - to-i18n-instance-options — Translates a Zod-parsed
I18nConfig(the React-tier shape) into the universal-coreI18nInstanceOptionsaccepted by `… - accept-language — (no submodules)
- accept-language — (no submodules)
- cloudflare-ip-geo — (no submodules)
- cloudflare-ip-geo — (no submodules)
- cookie — (no submodules)
- cookie — (no submodules)
- default-chain — (no submodules)
- default-chain — (no submodules)
- fallback — (no submodules)
- fallback — (no submodules)
- html-lang — (no submodules)
- html-lang — (no submodules)
- local-storage — (no submodules)
- local-storage — (no submodules)
- navigator — (no submodules)
- navigator — (no submodules)
- querystring — (no submodules)
- querystring — (no submodules)
- user-preference — (no submodules)
- user-preference — (no submodules)
- error-codes — Registers the five stable error codes used by
@suaveplan/react-i18nunder theREACT_I18Nnamespace via `defineCodes… - react-i18n-error — Defines
ReactI18nError, the single error class thrown by the React surface of@suaveplan/react-i18n. It extends the… - with-translation — (no submodules)
- with-translation — (no submodules)
- use-currency — (no submodules)
- use-currency — (no submodules)
- use-date — (no submodules)
- use-date — (no submodules)
- use-direction — (no submodules)
- use-direction — (no submodules)
- use-error-message — (no submodules)
- use-error-message — (no submodules)
- use-formatter — (no submodules)
- use-formatter — (no submodules)
- use-i18n — (no submodules)
- use-i18n — (no submodules)
- use-list — (no submodules)
- use-list — (no submodules)
- use-locale — (no submodules)
- use-locale — (no submodules)
- use-locale-adapter — (no submodules)
- use-locale-adapter — (no submodules)
- use-locale-detection — (no submodules)
- detector-chain-context —
DetectorChainContextis the React context that carries the configured locale-detector chain (plus any SSR-authoritati… - use-locale-detection — (no submodules)
- detector-chain-context —
- use-number — (no submodules)
- use-number — (no submodules)
- use-plural — (no submodules)
- use-plural — (no submodules)
- use-relative-time — (no submodules)
- use-relative-time — (no submodules)
- use-translate — (no submodules)
- use-translate — (no submodules)
- use-translation — (no submodules)
- use-translation — (no submodules)
- observability — (no submodules)
- react-i18n-metrics — (no submodules)
- provider — (no submodules)
- provider-context — Defines the React context that carries the active
I18nInstancefrom@suaveplan/i18nto every descendant of `<Transl… - provider — (no submodules)
- provider-context — Defines the React context that carries the active
- registry — (no submodules) - built-in — _(no submodules)…
- built-in — (no submodules)
- built-in — (no submodules)
- built-in — (no submodules)
- built-in — (no submodules)
- post-processors — (no submodules)
- post-processors — (no submodules)
- registry-context — Module-scoped React context that carries the active
ReactI18nRegistryto descendants of<RegistryProvider>. Direct… - registry-provider —
<RegistryProvider>is the React-context surface forReactI18nRegistry. It mounts a fresh registry per tree, registe… - registry — (no submodules) - built-in — _(no submodules)…
- registry.schemas — Strict-object Zod schemas for every plugin kind accepted by
ReactI18nRegistry. The schemas are the boundary at which… - use-registry — These hooks read the active
ReactI18nRegistryfrom React context. The strict variantuseReactI18nRegistry()throws…
- built-in — (no submodules)
- schemas — (no submodules)
- i18n-config.schemas — (no submodules)
- translation-key — (no submodules)
- t-dynamic —
tDynamic(i18n, key, values?)is the rare-use escape hatch for translation lookups whose key cannot be statically know… - translation-key — (no submodules)
- t-dynamic —
- env — Provides the single environment-detection helper the React surface uses to switch between dev-mode loud-failure and pro…
- identity-instance — Provides the identity translator used as the production-mode fallback when a
useTranslation()(or any sibling hook) i… - warn-once-per-process — Implements the warn-once-per-process dedup ring used by every production-mode soft-failure path in `@suaveplan/react-i1…
- i18n-suspense — (no submodules)- language-switcher — (no submodules)- language-switcher — (no submodules)- missing-key-overlay — (no submodules)- missing-key-overlay — (no submodules)- trans — (no submodules)- stringify-children — Helpers that translate a
<Trans>childrenblock into a canonical ICU message body string so the message parser rend… - trans — (no submodules)- create-i18n-config — Builds a validated
I18nConfigfrom a caller-supplied option bag and is the canonical entry point for `<TranslationPro… - to-i18n-instance-options — Translates a Zod-parsed
I18nConfig(the React-tier shape) into the universal-coreI18nInstanceOptionsaccepted by `… - accept-language — (no submodules)- accept-language — (no submodules)- cloudflare-ip-geo — (no submodules)- cloudflare-ip-geo — (no submodules)- cookie — (no submodules)- cookie — (no submodules)- default-chain — (no submodules)- default-chain — (no submodules)- fallback — (no submodules)- fallback — (no submodules)- html-lang — (no submodules)- html-lang — (no submodules)- local-storage — (no submodules)- local-storage — (no submodules)- navigator — (no submodules)- navigator — (no submodules)- querystring — (no submodules)- querystring — (no submodules)- user-preference — (no submodules)- user-preference — (no submodules)- error-codes — Registers the five stable error codes used by
@suaveplan/react-i18nunder theREACT_I18Nnamespace via `defineCodes… - react-i18n-error — Defines
ReactI18nError, the single error class thrown by the React surface of@suaveplan/react-i18n. It extends the… - with-translation — (no submodules)- with-translation — (no submodules)- use-currency — (no submodules)- use-currency — (no submodules)- use-date — (no submodules)- use-date — (no submodules)- use-direction — (no submodules)- use-direction — (no submodules)- use-error-message — (no submodules)- use-error-message — (no submodules)- use-formatter — (no submodules)- use-formatter — (no submodules)- use-i18n — (no submodules)- use-i18n — (no submodules)- use-list — (no submodules)- use-list — (no submodules)- use-locale — (no submodules)- use-locale — (no submodules)- use-locale-adapter — (no submodules)- use-locale-adapter — (no submodules)- use-locale-detection — (no submodules)- detector-chain-context —
DetectorChainContextis the React context that carries the configured locale-detector chain (plus any SSR-authoritati…- use-locale-detection — (no submodules)- use-number — (no submodules)- use-number — (no submodules)- use-plural — (no submodules)- use-plural — (no submodules)- use-relative-time — (no submodules)- use-relative-time — (no submodules)- use-translate — (no submodules)- use-translate — (no submodules)- use-translation — (no submodules)- use-translation — (no submodules)- observability — (no submodules)- react-i18n-metrics — (no submodules)- provider — (no submodules)- provider-context — Defines the React context that carries the active
I18nInstancefrom@suaveplan/i18nto every descendant of `<Transl… - provider — (no submodules)- registry — (no submodules)- built-in — (no submodules)- built-in — (no submodules)- built-in — (no submodules)- built-in — (no submodules)- post-processors — (no submodules)- post-processors — (no submodules)- registry-context — Module-scoped React context that carries the active
ReactI18nRegistryto descendants of<RegistryProvider>. Direct… - registry-provider —
<RegistryProvider>is the React-context surface forReactI18nRegistry. It mounts a fresh registry per tree, registe… - registry — (no submodules)- registry.schemas — Strict-object Zod schemas for every plugin kind accepted by
ReactI18nRegistry. The schemas are the boundary at which… - use-registry — These hooks read the active
ReactI18nRegistryfrom React context. The strict variantuseReactI18nRegistry()throws…
- use-locale-detection — (no submodules)- use-number — (no submodules)- use-number — (no submodules)- use-plural — (no submodules)- use-plural — (no submodules)- use-relative-time — (no submodules)- use-relative-time — (no submodules)- use-translate — (no submodules)- use-translate — (no submodules)- use-translation — (no submodules)- use-translation — (no submodules)- observability — (no submodules)- react-i18n-metrics — (no submodules)- provider — (no submodules)- provider-context — Defines the React context that carries the active
- schemas — (no submodules)- i18n-config.schemas — (no submodules)- translation-key — (no submodules)- t-dynamic —
tDynamic(i18n, key, values?)is the rare-use escape hatch for translation lookups whose key cannot be statically know…- translation-key — (no submodules)- env — Provides the single environment-detection helper the React surface uses to switch between dev-mode loud-failure and pro…
- identity-instance — Provides the identity translator used as the production-mode fallback when a
useTranslation()(or any sibling hook) i… - warn-once-per-process — Implements the warn-once-per-process dedup ring used by every production-mode soft-failure path in `@suaveplan/react-i1…
- fallback-bundle — Ships the strings that
<I18nSuspense>needs before the active catalog loads its own copies. Consumers can override th… - i18n-suspense — (no submodules)- language-switcher — (no submodules)- language-switcher — (no submodules)- missing-key-overlay — (no submodules)- missing-key-overlay — (no submodules)- trans — (no submodules)- stringify-children — Helpers that translate a
<Trans>childrenblock into a canonical ICU message body string so the message parser rend…- trans — (no submodules)- create-i18n-config — Builds a validated
I18nConfigfrom a caller-supplied option bag and is the canonical entry point for `<TranslationPro…
- trans — (no submodules)- create-i18n-config — Builds a validated
- to-i18n-instance-options — Translates a Zod-parsed
I18nConfig(the React-tier shape) into the universal-coreI18nInstanceOptionsaccepted by `… - accept-language — (no submodules)- accept-language — (no submodules)- cloudflare-ip-geo — (no submodules)- cloudflare-ip-geo — (no submodules)- cookie — (no submodules)- cookie — (no submodules)- default-chain — (no submodules)- default-chain — (no submodules)- fallback — (no submodules)- fallback — (no submodules)- html-lang — (no submodules)- html-lang — (no submodules)- local-storage — (no submodules)- local-storage — (no submodules)- navigator — (no submodules)- navigator — (no submodules)- querystring — (no submodules)- querystring — (no submodules)- user-preference — (no submodules)- user-preference — (no submodules)- error-codes — Registers the five stable error codes used by
@suaveplan/react-i18nunder theREACT_I18Nnamespace via `defineCodes… - react-i18n-error — Defines
ReactI18nError, the single error class thrown by the React surface of@suaveplan/react-i18n. It extends the… - with-translation — (no submodules)- with-translation — (no submodules)- use-currency — (no submodules)- use-currency — (no submodules)- use-date — (no submodules)- use-date — (no submodules)- use-direction — (no submodules)- use-direction — (no submodules)- use-error-message — (no submodules)- use-error-message — (no submodules)- use-formatter — (no submodules)- use-formatter — (no submodules)- use-i18n — (no submodules)- use-i18n — (no submodules)- use-list — (no submodules)- use-list — (no submodules)- use-locale — (no submodules)- use-locale — (no submodules)- use-locale-adapter — (no submodules)- use-locale-adapter — (no submodules)- use-locale-detection — (no submodules)- detector-chain-context —
DetectorChainContextis the React context that carries the configured locale-detector chain (plus any SSR-authoritati…- use-locale-detection — (no submodules)- use-number — (no submodules)- use-number — (no submodules)- use-plural — (no submodules)- use-plural — (no submodules)- use-relative-time — (no submodules)- use-relative-time — (no submodules)- use-translate — (no submodules)- use-translate — (no submodules)- use-translation — (no submodules)- use-translation — (no submodules)- observability — (no submodules)- react-i18n-metrics — (no submodules)- provider — (no submodules)- provider-context — Defines the React context that carries the active
I18nInstancefrom@suaveplan/i18nto every descendant of `<Transl… - provider — (no submodules)- registry — (no submodules)- built-in — <!-- begin:auto-d…
- built-in — (no submodules)- built-in — (no submodules)- built-in — (no submodules)- built-in — (no submodules)- post-processors — (no submodules)- post-processors — (no submodules)- registry-context — Module-scoped React context that carries the active
ReactI18nRegistryto descendants of<RegistryProvider>. Direct… - registry-provider —
<RegistryProvider>is the React-context surface forReactI18nRegistry. It mounts a fresh registry per tree, registe… - registry — (no submodules)- built-in — <!-- begin:auto-d…
- registry.schemas — Strict-object Zod schemas for every plugin kind accepted by
ReactI18nRegistry. The schemas are the boundary at which… - use-registry — These hooks read the active
ReactI18nRegistryfrom React context. The strict variantuseReactI18nRegistry()throws…
- use-locale-detection — (no submodules)- use-number — (no submodules)- use-number — (no submodules)- use-plural — (no submodules)- use-plural — (no submodules)- use-relative-time — (no submodules)- use-relative-time — (no submodules)- use-translate — (no submodules)- use-translate — (no submodules)- use-translation — (no submodules)- use-translation — (no submodules)- observability — (no submodules)- react-i18n-metrics — (no submodules)- provider — (no submodules)- provider-context — Defines the React context that carries the active
- schemas — (no submodules)- i18n-config.schemas — (no submodules)- translation-key — (no submodules)- t-dynamic —
tDynamic(i18n, key, values?)is the rare-use escape hatch for translation lookups whose key cannot be statically know…- translation-key — (no submodules)- env — Provides the single environment-detection helper the React surface uses to switch between dev-mode loud-failure and pro…
- identity-instance — Provides the identity translator used as the production-mode fallback when a
useTranslation()(or any sibling hook) i… - warn-once-per-process — Implements the warn-once-per-process dedup ring used by every production-mode soft-failure path in `@suaveplan/react-i1…
- fallback-bundle — Ships the strings that
<I18nSuspense>needs before the active catalog loads its own copies. Consumers can override th… - i18n-suspense — (no submodules)- language-switcher — (no submodules)- language-switcher — (no submodules)- missing-key-overlay — (no submodules)- missing-key-overlay — (no submodules)- trans — (no submodules)- stringify-children — Helpers that translate a
<Trans>childrenblock into a canonical ICU message body string so the message parser rend…- trans — (no submodules)- create-i18n-config — Builds a validated
I18nConfigfrom a caller-supplied option bag and is the canonical entry point for `<TranslationPro…
- trans — (no submodules)- create-i18n-config — Builds a validated
- to-i18n-instance-options — Translates a Zod-parsed
I18nConfig(the React-tier shape) into the universal-coreI18nInstanceOptionsaccepted by `… - accept-language — (no submodules)- accept-language — (no submodules)- cloudflare-ip-geo — (no submodules)- cloudflare-ip-geo — (no submodules)- cookie — (no submodules)- cookie — (no submodules)- default-chain — (no submodules)- default-chain — (no submodules)- fallback — (no submodules)- fallback — (no submodules)- html-lang — (no submodules)- html-lang — (no submodules)- local-storage — (no submodules)- local-storage — (no submodules)- navigator — (no submodules)- navigator — (no submodules)- querystring — (no submodules)- querystring — (no submodules)- user-preference — (no submodules)- user-preference — (no submodules)- error-codes — Registers the five stable error codes used by
@suaveplan/react-i18nunder theREACT_I18Nnamespace via `defineCodes… - react-i18n-error — Defines
ReactI18nError, the single error class thrown by the React surface of@suaveplan/react-i18n. It extends the… - with-translation — (no submodules)- with-translation — (no submodules)- use-currency — (no submodules)- use-currency — (no submodules)- use-date — (no submodules)- use-date — (no submodules)- use-direction — (no submodules)- use-direction — (no submodules)- use-error-message — (no submodules)- use-error-message — (no submodules)- use-formatter — (no submodules)- use-formatter — (no submodules)- use-i18n — (no submodules)- use-i18n — (no submodules)- use-list — (no submodules)- use-list — (no submodules)- use-locale — (no submodules)- use-locale — (no submodules)- use-locale-adapter — (no submodules)- use-locale-adapter — (no submodules)- use-locale-detection — (no submodules)- detector-chain-context —
DetectorChainContextis the React context that carries the configured locale-detector chain (plus any SSR-authoritati…- use-locale-detection — (no submodules)- use-number — (no submodules)- use-number — (no submodules)- use-plural — (no submodules)- use-plural — (no submodules)- use-relative-time — (no submodules)- use-relative-time — (no submodules)- use-translate — (no submodules)- use-translate — (no submodules)- use-translation — (no submodules)- use-translation — (no submodules)- observability — (no submodules)- react-i18n-metrics — (no submodules)- provider — (no submodules)- provider-context — Defines the React context that carries the active
I18nInstancefrom@suaveplan/i18nto every descendant of `<Transl… - provider — (no submodules)- registry — (no submodules)- built-in — _(no submodules…
- built-in — (no submodules)- built-in — (no submodules)- built-in — (no submodules)- built-in — (no submodules)- post-processors — (no submodules)- post-processors — (no submodules)- registry-context — Module-scoped React context that carries the active
ReactI18nRegistryto descendants of<RegistryProvider>. Direct… - registry-provider —
<RegistryProvider>is the React-context surface forReactI18nRegistry. It mounts a fresh registry per tree, registe… - registry — (no submodules)- built-in — _(no submodules…
- registry.schemas — Strict-object Zod schemas for every plugin kind accepted by
ReactI18nRegistry. The schemas are the boundary at which… - use-registry — These hooks read the active
ReactI18nRegistryfrom React context. The strict variantuseReactI18nRegistry()throws…
- use-locale-detection — (no submodules)- use-number — (no submodules)- use-number — (no submodules)- use-plural — (no submodules)- use-plural — (no submodules)- use-relative-time — (no submodules)- use-relative-time — (no submodules)- use-translate — (no submodules)- use-translate — (no submodules)- use-translation — (no submodules)- use-translation — (no submodules)- observability — (no submodules)- react-i18n-metrics — (no submodules)- provider — (no submodules)- provider-context — Defines the React context that carries the active
- schemas — (no submodules)- i18n-config.schemas — (no submodules)- translation-key — (no submodules)- t-dynamic —
tDynamic(i18n, key, values?)is the rare-use escape hatch for translation lookups whose key cannot be statically know…- translation-key — (no submodules)- env — Provides the single environment-detection helper the React surface uses to switch between dev-mode loud-failure and pro…
- identity-instance — Provides the identity translator used as the production-mode fallback when a
useTranslation()(or any sibling hook) i… - warn-once-per-process — Implements the warn-once-per-process dedup ring used by every production-mode soft-failure path in `@suaveplan/react-i1…
- fallback-bundle — Ships the strings that
<I18nSuspense>needs before the active catalog loads its own copies. Consumers can override th… - i18n-suspense — (no submodules)- language-switcher — (no submodules) - language-switcher — (no submodules)- missing-key-overlay — (no submodules) - missing-key-overlay — (no submodules)- trans — (no submodules) - stringify-children — Helpers that translate a
<Trans>childrenblock into a canonical ICU message body string so the message parser rend…- trans — (no submodules)- create-i18n-config — Builds a validated
I18nConfigfrom a caller-supplied option bag and is the canonical entry point for `<TranslationPro…
- trans — (no submodules)- create-i18n-config — Builds a validated
- to-i18n-instance-options — Translates a Zod-parsed
I18nConfig(the React-tier shape) into the universal-coreI18nInstanceOptionsaccepted by `… - accept-language — (no submodules) - accept-language — (no submodules)- cloudflare-ip-geo — (no submodules) - cloudflare-ip-geo — (no submodules)- cookie — (no submodules) - cookie — (no submodules)- default-chain — (no submodules) - default-chain — (no submodules)- fallback — (no submodules) - fallback — (no submodules)- html-lang — (no submodules) - html-lang — (no submodules)- local-storage — (no submodules) - local-storage — (no submodules)- navigator — (no submodules) - navigator — (no submodules)- querystring — (no submodules) - querystring — (no submodules)- user-preference — (no submodules) - user-preference — (no submodules)- error-codes — Registers the five stable error codes used by
@suaveplan/react-i18nunder theREACT_I18Nnamespace via `defineCodes… - react-i18n-error — Defines
ReactI18nError, the single error class thrown by the React surface of@suaveplan/react-i18n. It extends the… - with-translation — (no submodules) - with-translation — (no submodules)- use-currency — (no submodules) - use-currency — (no submodules)- use-date — (no submodules) - use-date — (no submodules)- use-direction — (no submodules) - use-direction — (no submodules)- use-error-message — (no submodules) - use-error-message — (no submodules)- use-formatter — (no submodules) - use-formatter — (no submodules)- use-i18n — (no submodules) - use-i18n — (no submodules)- use-list — (no submodules) - use-list — (no submodules)- use-locale — (no submodules) - use-locale — (no submodules)- use-locale-adapter — (no submodules) - use-locale-adapter — (no submodules)- use-locale-detection — (no submodules) - detector-chain-context —
DetectorChainContextis the React context that carries the configured locale-detector chain (plus any SSR-authoritati…- use-locale-detection — (no submodules)- use-number — (no submodules) - use-number — (no submodules)- use-plural — (no submodules) - use-plural — (no submodules)- use-relative-time — (no submodules) - use-relative-time — (no submodules)- use-translate — (no submodules) - use-translate — (no submodules)- use-translation — (no submodules) - use-translation — (no submodules)- observability — (no submodules) - react-i18n-metrics — (no submodules)- provider — (no submodules) - provider-context — Defines the React context that carries the active
I18nInstancefrom@suaveplan/i18nto every descendant of `<Transl… - provider — (no submodules)- registry — (no submodules) - built-in — (no submodules) - built-in — (no submodules) - built-in — (no submodules) - built-in — (no submodules) - post-processors — (no submodules) - post-processors — (no submodules) - registry-context — Module-scoped React context that carries the active
ReactI18nRegistryto descendants of<RegistryProvider>. Direct… - registry-provider —
<RegistryProvider>is the React-context surface forReactI18nRegistry. It mounts a fresh registry per tree, registe… - registry — (no submodules) - registry.schemas — Strict-object Zod schemas for every plugin kind accepted by
ReactI18nRegistry. The schemas are the boundary at which… - use-registry — These hooks read the active
ReactI18nRegistryfrom React context. The strict variantuseReactI18nRegistry()throws…
- use-locale-detection — (no submodules)- use-number — (no submodules) - use-number — (no submodules)- use-plural — (no submodules) - use-plural — (no submodules)- use-relative-time — (no submodules) - use-relative-time — (no submodules)- use-translate — (no submodules) - use-translate — (no submodules)- use-translation — (no submodules) - use-translation — (no submodules)- observability — (no submodules) - react-i18n-metrics — (no submodules)- provider — (no submodules) - provider-context — Defines the React context that carries the active
- schemas — (no submodules) - i18n-config.schemas — (no submodules)- translation-key — (no submodules) - t-dynamic —
tDynamic(i18n, key, values?)is the rare-use escape hatch for translation lookups whose key cannot be statically know…- translation-key — (no submodules)- env — Provides the single environment-detection helper the React surface uses to switch between dev-mode loud-failure and pro…
- identity-instance — Provides the identity translator used as the production-mode fallback when a
useTranslation()(or any sibling hook) i… - warn-once-per-process — Implements the warn-once-per-process dedup ring used by every production-mode soft-failure path in `@suaveplan/react-i1…
Install
bun add @suaveplan/react-i18n
Peer-installs:
bun add react react-dom
The package targets react ^18.0.0 || ^19.0.0 so useSyncExternalStore
is available without the use-sync-external-store/shim polyfill. All
internal @suaveplan/* deps resolve through the workspace.
Quick start
The minimum tree wires a Provider, a Trans component, and a hook-driven
locale switch. The t function exposed by useTranslation() is the
ergonomic-options-bag form from @suaveplan/i18n.
import {
Trans,
TranslationProvider,
createI18nConfig,
useTranslation,
} from "@suaveplan/react-i18n";
const config = createI18nConfig({
defaultLocale: "en-GB",
fallbackLocale: "en-GB",
supportedLocales: ["en-GB", "fr-FR"],
initialMessages: {
"en-GB": { greeting: "Hello, <bold>{name}</bold>" },
"fr-FR": { greeting: "Bonjour, <bold>{name}</bold>" },
},
});
function Welcome({ name }: { name: string }): JSX.Element {
const { t } = useTranslation();
return (
<>
<p>{t("welcome", { defaultValue: "Welcome" })}</p>
<p>
<Trans
i18nKey="greeting"
values={{ name }}
components={{ bold: <strong /> }}
/>
</p>
</>
);
}
export function App({ children }: { children: React.ReactNode }) {
return (
<TranslationProvider config={config}>
<Welcome name="Alice" />
{children}
</TranslationProvider>
);
}
Modules
Every implementation file ships a sibling .md with the canonical
sections (Purpose, Features, Basic + Advanced examples, API Reference,
Implementation Notes). Read them inline alongside the source.
Provider and context
<TranslationProvider>— mounts anI18nInstanceand exposes it through React context. Accepts either a caller-managedi18nprop (borrowed; not disposed) or a Provider-managedconfigprop (created on mount, disposed on unmount).TranslationContext— the raw context object for advanced consumers (mostly tests).
Hooks
useI18n— returns the activeI18nInstance(ornullwhen outside a Provider).useTranslation— Suspense-aware{ t, i18n, ready }triple. Auto-triggersi18n.catalog.preload(locale, namespace)when a backend is registered and the namespace is not yet loaded.useTranslate— terse alias returning the boundtfunction directly.useLocale—{ locale, setLocale }re-rendering onlanguageChanged.useDirection—{ direction, locale }derived fromgetTextDirection(locale).useErrorMessage— maps aSuaveplanError | unknownto a translated string.usePlural— the plural category + rule for a count + locale.useDate— memoisedIntl.DateTimeFormatwrapper.useNumber— memoisedIntl.NumberFormatwrapper with theclearUseNumberCache()test helper.useCurrency— memoised currency formatter.useRelativeTime— memoisedIntl.RelativeTimeFormatwrapper.useList— memoisedIntl.ListFormatwrapper.useLocaleDetection— runs the configured detector chain on mount, exposes{ detected, source, isPending, redetect }.DetectorChainContext/DetectorChainProvider— the context wiring that exposes the chain to descendants.
Components
<Trans>— rich-text component usingMessageParser.formatChunks<ReactNode>. Supports tag interpolation, nested tags, plural viacount, and a children-as- default-message fallback.<LanguageSwitcher>— headless render-prop component. Receives{ locales, current, change, isPending }. WrapschangeinuseTransitionso namespace loads on locale change do not block urgent updates.<MissingKeyOverlay>— dev-mode overlay listing untranslated keys in real-time, capped at 50 entries witharia-live="polite". Rendersnullin production.<I18nSuspense>— convenience wrapper around React's<Suspense>with a defaultaria-live="polite"fallback and a fallback-render counter (react_i18n.suspense.fallback.total). The default loading label is resolved from the active catalog with the bundled English fallback supplied byfallback-bundle.stringifyChild/childrenToMessageString— internal<Trans>helpers exposed for advanced consumers building custom rich-text components.
HOC
withTranslation— class-component escape hatch. Injectstandi18nprops.
Locale detectors
Each detector is independently importable from
@suaveplan/react-i18n (and tree-shaken when unused).
querystringDetector—?lang=...(server URL or browserlocation.search).userPreferenceDetector— async fetch against a configurable endpoint; persists oncache().cookieDetector— read + write a configurable cookie.localStorageDetector— browser-only; persists to a configurable key.htmlLangDetector— readsdocument.documentElement.lang.acceptLanguageDetector— server-only; parsesAccept-Languageper RFC 9110.navigatorDetector— iteratesnavigator.languages.cloudflareIpGeoDetector— server-only; readscf-ipcountryand maps via@suaveplan/locale-data.fallbackDetector— always returns the configured fallback. Must appear last in any chain.defaultDetectorChain— assembles the canonical 9-entry (or 8-entry, when nouserPreferenceEndpointis supplied) chain in spec order.
Branded TranslationKey
TranslationKey/AppTranslationKeys/ContributedTranslationKeys— branded string union pattern. Both interfaces start empty; downstream packages augmentContributedTranslationKeysand consumer apps augmentAppTranslationKeys.tDynamic— runtime escape hatch. ThrowsReactI18nError(REACT_I18N.UNKNOWN_KEY)on a missing key; emits awarnlog on every call so observability surfaces every use site.
Errors and observability
ReactI18nError—extends SuaveplanError<ReactI18nCode>. The only error type the React surface throws.REACT_I18N_CODES— five frozen codes:NO_PROVIDER,NAMESPACE_LOAD_FAILED,HYDRATION_MISMATCH,TRANS_INVALID_CHILD,UNKNOWN_KEY.react_i18n.*OTel instruments — counters (missing_key.total,suspense.fallback.total), histogram (render.duration_ms), in-memory metrics state ring for tests, plus the publicgetReactI18nMetrics()/recordMissingKey()/recordSuspenseFallback()/recordRenderDuration()helpers.
Schemas, config, and utils
createI18nConfig— Zod validator that returns a typedI18nConfig. RoutesinitialMessagesfailures toReactI18nError(HYDRATION_MISMATCH)and other shape failures toI18nError(I18N.INVALID_OPTIONS)so observability dashboards group on the React-tier code where appropriate.toI18nInstanceOptions— pure mapping fromI18nConfigto the universal-coreI18nInstanceOptionsshape.I18nConfigSchema/TranslationProviderPropsSchema— Zod boundary schemas for the Provider's accepted prop shape and the config bag.isProductionEnv— single source of truth for the dev-vs-prod branch every soft-failure path uses.getIdentityTranslator/IDENTITY_TRANSLATION_RETURN— no-op identity translator returned byuseTranslationoutside a Provider in production builds.warnOncePerProcess— global bag-of-warnings dedup so a misbehaving consumer logs each diagnostic exactly once across the process lifetime.
Detector chain wiring
The detector chain is the same on the server and the browser; the SSR
render captures the resolved locale, embeds it in initialLocale, and
the client hydrates without re-running browser-only detectors (per the
spec's "browser detectors skipped during hydration" requirement).
import {
DetectorChainProvider,
TranslationProvider,
cookieDetector,
defaultDetectorChain,
useLocaleDetection,
} from "@suaveplan/react-i18n";
const detectors = defaultDetectorChain({
fallback: "en-GB",
userPreferenceEndpoint: "/api/me/locale",
});
function Bootstrap({ children }: { children: React.ReactNode }) {
return (
<TranslationProvider config={config}>
<DetectorChainProvider detectors={detectors}>
{children}
</DetectorChainProvider>
</TranslationProvider>
);
}
function LocaleStatus(): JSX.Element {
const { detected, source, isPending, redetect } = useLocaleDetection();
return (
<output aria-busy={isPending}>
<span data-testid="locale">{detected ?? "(unresolved)"}</span>
<span data-testid="source">via {source ?? "(none)"}</span>
<button type="button" onClick={() => void redetect()}>
Re-detect
</button>
</output>
);
}
For ad-hoc chains, import detectors individually:
import {
DetectorChainProvider,
cookieDetector,
fallbackDetector,
navigatorDetector,
} from "@suaveplan/react-i18n";
const detectors = [
cookieDetector({ name: "lang", maxAgeSeconds: 31_536_000 }),
navigatorDetector(),
fallbackDetector("en-GB"),
];
Branded TranslationKey
The branded type prevents misspelled translation keys from passing
typecheck. Both the consumer-app interface (AppTranslationKeys) and
the downstream-package interface (ContributedTranslationKeys) start
empty; augmentation contributes the keys.
Consumer app augmentation
In your app, generate the augmentation file from the canonical
locales/en-GB.json via the
generate-translation-types.ts
script:
bun packages/react/react-i18n/scripts/generate-translation-types.ts \
--source locales/en-GB.json \
--target src/generated/translation-keys.d.ts \
--interface AppTranslationKeys \
--module @suaveplan/react-i18n
The script emits a .d.ts like:
declare module "@suaveplan/react-i18n" {
interface AppTranslationKeys {
"app.welcome": true;
"app.signin.title": true;
"app.signin.submit": true;
}
}
After importing the generated file once at the app entrypoint, every
typed t(literalKey) call typechecks the key against the registered
union.
import type { TranslationKey } from "@suaveplan/react-i18n";
const k: TranslationKey = "app.welcome"; // ✅ typechecks
const bad: TranslationKey = "appp.welcome"; // ✗ TS error: not assignable
Downstream package augmentation
When publishing a react-ui-pack-* or react-ui-theme-* package,
augment ContributedTranslationKeys instead — that interface is
namespaced for downstream contributions so consumer-app augmentations
do not collide with library-defined keys.
declare module "@suaveplan/react-i18n" {
interface ContributedTranslationKeys {
"react-ui.dialog.close": true;
"react-ui.dialog.confirm": true;
}
}
The downstream package side-effect-imports the augmentation file from
its index.ts. Once a consumer adds the package to their tree, the
keys become assignable to TranslationKey without any per-consumer
configuration.
tDynamic runtime escape hatch
For the rare case where a key is computed at runtime — most often
mapping an error code like error.code to errors:<code> — use
tDynamic. It performs a runtime catalog lookup and throws
ReactI18nError(REACT_I18N.UNKNOWN_KEY) when the key is missing.
Every call also emits a warn-level log so observability surfaces
each use site (the typed t(literalKey) path is preferred 99% of
the time).
import { ReactI18nError, tDynamic, useI18n } from "@suaveplan/react-i18n";
function ErrorMessage({ error }: { error: { code: string } }): string {
const i18n = useI18n();
if (i18n === null) return "";
try {
return tDynamic(i18n, `errors:${error.code}`);
} catch (cause) {
if (
cause instanceof ReactI18nError &&
cause.code === "REACT_I18N.UNKNOWN_KEY"
) {
return error.code;
}
throw cause;
}
}
No-backwards-deps gate
@suaveplan/react-i18n is the leaf-most React package .
Every downstream React package — @suaveplan/react-ui,
@suaveplan/react-forms, every @suaveplan/react-ui-theme-*, every
@suaveplan/react-ui-pack-* — augments
ContributedTranslationKeys via TypeScript module augmentation. For
that pattern to stay sound, react-i18n MUST NOT depend on any of those
packages in any of its dep buckets (dependencies,
peerDependencies, devDependencies).
The CI gate at
scripts/lint/check-no-backwards-deps.ts
enforces the rule on every commit:
bun scripts/lint/check-no-backwards-deps.ts \
--package-json packages/react/react-i18n/package.json
The script is wired into package.json's lint step so
bunx turbo lint --filter=@suaveplan/react-i18n fails fast if a banned
dep ever lands.
Telemetry
OTel instruments published via getMeter("suaveplan.react-i18n"):
- Counter
react_i18n.missing_key.total— labels:locale,namespace,componentName?. Incremented from<Trans>(andtDynamic) when a key is absent from the catalog. - Counter
react_i18n.suspense.fallback.total— labels:namespace,locale. Incremented every time<I18nSuspense>renders its default fallback. - Histogram
react_i18n.render.duration_ms— labels:locale,namespace,componentName?. Recorded around the<Trans>formatChunksrender. - Counter
i18n.detector.match_total{detector}— recorded by every detector when it returns a non-null supported locale. - Histogram
i18n.detector.duration_ms{detector}— recorded for eachdetectAsynccall. - Span
i18n.detect— wraps the entire chain run; carrieswinning_detectorandresolved_localeattributes.
The tDynamic call's per-use warn log is NOT OTel — it goes to the
@suaveplan/logger sink and surfaces in centralised log aggregation.
E2E sweep
The Playwright suite at
e2e/react-i18n.spec.ts runs against a
tiny Vite + React test app at
e2e/test-app/ and covers the five user-visible
behaviours:
- Live language switch through all five locales (
en-GB,ar-AE,zh-CN,cy-GB,ru-RU) with localStorage persistence. - RTL flip on Arabic —
<html dir="rtl">updates and the layout reflows. - Lazy-namespace Suspense — the
<I18nSuspense>fallback resolves to translated content via the async test backend. - Missing-key overlay (dev mode) — caps at 50 entries with
aria-live="polite". - SSR hydration round-trip — the server-rendered HTML already contains the translated string (no flash) and the client hydrates without a React warning.
Per CLAUDE.md "Web App & Page Verification": every scenario also
asserts (a) zero browser-console errors, (b) zero failed network
requests (4xx/5xx), and (c) the rendered DOM contains the expected
content (not merely a 200 response).
bunx turbo lint typecheck test:coverage build --filter=@suaveplan/react-i18n
bun --cwd packages/react/react-i18n test:e2e
Testing
bunx turbo lint typecheck test --filter=@suaveplan/react-i18n
bunx turbo test:coverage --filter=@suaveplan/react-i18n
Coverage targets: 100/100/100/100 (statements / branches / functions /
lines) per CLAUDE.md §"Quality". The thresholds are wired into
vitest.config.ts. The pre-publish gate also runs the full §13
verification sequence — see AGENTS.md for the canonical
checklist.
License
MIT.