# `` _(no submodules)_ ## Purpose `` is a thin convenience wrapper around React's built-in `` boundary. It gives a translation-aware default fallback that announces loading state to assistive technology via `aria-live="polite"`, increments the `react_i18n.suspense.fallback.total` OTel counter every time the fallback renders, and resolves the fallback label through the active catalog so consumers can localise the loading announcement without forking the component. The wrapper exists so consumers do not need to re-implement the same a11y + telemetry boilerplate around every namespace boundary. ## Features - Default fallback with `role="status"`, `aria-live="polite"`, and `aria-busy="true"` for screen-reader-friendly loading state - Visually hidden loading label so the announcement does not push DOM layout around - Counter `react_i18n.suspense.fallback.total{ namespace, locale }` records every fallback render - Loading label resolves from the active catalog with the bundled English string (`react-i18n.suspense.loading`) as the fallback - Custom `fallback` prop bypasses the default entirely (consumer-owned spinner, skeleton, or loading screen) - Zero hardcoded user-facing strings — every visible string flows through a translation key ## Usage: Basic Example Wrap a subtree that may suspend during a backend-driven namespace load: ```tsx import { I18nSuspense } from "@suaveplan/react-i18n/components/i18n-suspense"; ; ``` When `LoginForm` reads from a not-yet-loaded namespace and `useTranslation` throws the preload promise, the wrapper renders the default fallback, records the metric sample, and re-renders the children once the load resolves. ## Usage: Advanced Example Custom fallback + per-namespace telemetry segmentation: ```tsx import { I18nSuspense } from "@suaveplan/react-i18n/components/i18n-suspense"; {t("checkout.loading")} } > ; ``` Consumers can override the bundled loading label by adding the `react-i18n.suspense.loading` key to their catalog: ```ts const i18n = createI18n({ defaultLocale: "de-DE", messages: { "de-DE": { "react-i18n": { suspense: { loading: "Übersetzungen werden geladen…" }, }, }, }, }); ``` The default fallback will pick up the override automatically. ## API Reference ### `I18nSuspense(props: I18nSuspenseProps): ReactElement` Props: - `fallback?: ReactNode` — replaces the default screen-reader-friendly fallback element. When omitted, the wrapper renders its built-in `aria-live="polite"` div. - `namespace?: string` — namespace label attached to the `react_i18n.suspense.fallback.total` counter sample. Default: `""`. - `children: ReactNode` — subtree wrapped by the underlying `` boundary. ## Implementation Notes - The default-fallback element is a child component (`DefaultFallback`) rather than an inline `
` so the `useEffect` that records the metric runs only when the fallback is actually rendered (Suspense defers child rendering, but the fallback always renders fresh on each suspend cycle). - A per-instance `useRef` guards against React's Strict-Mode double-invoke so the counter records exactly once per logical fallback render. - The visually-hidden style block uses the canonical `clip: rect(0,0,0,0)` pattern so the label is exposed to assistive tech but invisible to sighted users. - `resolveLoadingLabel` checks `i18n.catalog.has` before `t()` so a missing key does not also fire a `missingKey` event for the fallback bundle path — the bundled string is the silent default. - `aria-busy` on the fallback container hints to assistive tech that the region is updating; combined with `role="status"` + `aria-live="polite"` this is the WAI-ARIA-recommended pattern for transient loading indicators.