Files
skills/docs/localization/i18n/react-i18n/src/components/i18n-suspense/i18n-suspense.md
2026-05-29 08:20:56 +01:00

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", 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:

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-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 <Suspense> boundary.

Implementation Notes

  • The default-fallback element is a child component (DefaultFallback) rather than an inline <div> 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<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.
  • 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.