4.1 KiB
<I18nSuspense>
(no submodules)
Purpose
<I18nSuspense> is a thin convenience wrapper around React's built-in
<Suspense> 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", andaria-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
fallbackprop 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:
import { I18nSuspense } from "@suaveplan/react-i18n/components/i18n-suspense";
<I18nSuspense namespace="auth">
<LoginForm />
</I18nSuspense>;
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:
import { I18nSuspense } from "@suaveplan/react-i18n/components/i18n-suspense";
<I18nSuspense
namespace="checkout"
fallback={
<div role="status" aria-live="polite" aria-busy="true">
<Spinner />
<span>{t("checkout.loading")}</span>
</div>
}
>
<CheckoutFlow />
</I18nSuspense>;
Consumers can override the bundled loading label by adding the
react-i18n.suspense.loading key to their catalog:
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-inaria-live="polite"div.namespace?: string— namespace label attached to thereact_i18n.suspense.fallback.totalcounter sample. Default:"".children: ReactNode— subtree wrapped by the underlying<Suspense>boundary.
Implementation Notes
- The default-fallback element is a child component (
DefaultFallback) rather than an inline<div>so theuseEffectthat 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<boolean>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. resolveLoadingLabelchecksi18n.catalog.hasbeforet()so a missing key does not also fire amissingKeyevent for the fallback bundle path — the bundled string is the silent default.aria-busyon the fallback container hints to assistive tech that the region is updating; combined withrole="status"+aria-live="polite"this is the WAI-ARIA-recommended pattern for transient loading indicators.