# `` _(no submodules)_ ## Purpose `` is a headless render-prop component that exposes the language-switching surface to consumer-controlled UI. It renders no DOM markup of its own — the caller controls the visual treatment via the render-prop child. The component bundles the bookkeeping that every language switcher needs: it normalises the supplied locale list into `LocaleDescriptor` objects with derived text-direction, subscribes to `languageChanged` / `loaded` events through the active Provider's `I18nInstance`, wraps the locale-change call in `useTransition` so the namespace load triggered by the switch never blocks urgent updates, and exposes `isPending` so the caller can render a busy state. ## Features - Headless: zero DOM, zero CSS — render any UI in the render prop - Locale normalisation: accepts string codes or `LocaleDescriptor` objects - Auto-derives `direction` (`ltr` / `rtl`) via `getTextDirection` from `@suaveplan/i18n` - `change(locale): Promise` returns when the language change resolves so callers can chain `await change(...)` - `isPending` aggregates the `useTransition` flag and the awaitable in-flight flag so both render-time pendingness and async resolution are surfaced - Throws `ReactI18nError(REACT_I18N.NO_PROVIDER)` outside a Provider ## Usage: Basic Example ```tsx import { TranslationProvider } from "@suaveplan/react-i18n"; import { LanguageSwitcher } from "@suaveplan/react-i18n/components/language-switcher"; {({ locales, current, change, isPending }) => ( )} ; ``` ## Usage: Advanced Example A typed dropdown with custom labels, RTL-aware ordering, and loading spinner: ```tsx import { LanguageSwitcher } from "@suaveplan/react-i18n/components/language-switcher"; {({ locales, current, change, isPending }) => (
{locales.map((l) => ( ))}
)}
; ``` ## API Reference ### `LanguageSwitcher(props: LanguageSwitcherProps): ReactElement | null` Props: - `locales: ReadonlyArray` — locales to surface to the render-prop. Strings are upgraded to `{ code, direction }` descriptors via `getTextDirection`. - `children: (state: LanguageSwitcherState) => ReactNode` — render-prop child. Receives the resolved state object. ### `LanguageSwitcherState` - `locales: readonly LocaleDescriptor[]` - `current: string` - `change: (locale: string) => Promise` — wrapped in `useTransition`; returns when the underlying `i18n.changeLanguage` resolves - `isPending: boolean` — true while a transition or awaitable change is in flight ### `LocaleDescriptor` - `code: string` - `name?: string` — Latin-script display name - `nativeName?: string` — native-script display name - `direction?: "ltr" | "rtl"` — derived when not supplied Throws `ReactI18nError(REACT_I18N.NO_PROVIDER)` outside a `TranslationProvider`. ## Implementation Notes - The component uses `useSyncExternalStore` so `current` reflects the active locale across concurrent renders. - `useTransition` alone surfaces pendingness only during a render. The component layers an explicit awaiting flag so an `await change(...)` in user code observes the entire async lifecycle (start → load → end) rather than only the React-scheduled portion. - `getTextDirection` is invoked once per render via `useMemo` keyed on `props.locales`. Stable input arrays therefore avoid re-deriving direction on every render. - The render-prop is invoked as a function, not as JSX (`children(state)`), so React does not treat the function as a component instance — no `displayName` warnings, no Strict Mode double-invocation surprise.