3.7 KiB
3.7 KiB
to-i18n-instance-options.ts
Purpose
Translates a Zod-parsed I18nConfig (the React-tier shape) into the
universal-core I18nInstanceOptions accepted by createI18n(...) from
@suaveplan/i18n. This is the bridge between the React Provider's
config surface (where field names favour SSR semantics, e.g.
initialMessages) and the universal core's option bag (where field
names favour catalog semantics, e.g. messages).
The function is pure and stable across calls so the Provider can wrap it
in useMemo without churning the universal core's instance lifecycle on
every render.
Features
- Single-pass field mapping with explicit conditional forwarding so
exactOptionalPropertyTypes(the tsconfig setting) does not require sentinelundefineds on the output. - Renames
initialMessages→messagesso the React-tier name ("initial" matches the SSR-hydration story) and the core-tier name ("messages" matches the catalog story) can each be ergonomic in their own context. - Forwards every other field as-is (
fallbackLocale,backend,backends,saveMissing,plugins).
Usage: Basic Example
import {
createI18nConfig,
toI18nInstanceOptions,
} from "@suaveplan/react-i18n/config";
import { createI18n } from "@suaveplan/i18n";
const cfg = createI18nConfig({
defaultLocale: "en-GB",
initialMessages: { "en-GB": { hello: "Hi" } },
});
const i18n = createI18n(toI18nInstanceOptions(cfg));
Usage: Advanced Example
import { useMemo } from "react";
import { createI18n, type I18nInstance } from "@suaveplan/i18n";
import {
type I18nConfig,
toI18nInstanceOptions,
} from "@suaveplan/react-i18n/config";
function useStableInstance(config: I18nConfig): I18nInstance {
return useMemo(
() => createI18n(toI18nInstanceOptions(config)),
[config]
);
}
API Reference
toI18nInstanceOptions(config: I18nConfig): I18nInstanceOptions
Pure mapping. Returns a new I18nInstanceOptions object on every call;
fields not present on the input config are omitted from the output (so
the universal-core defaults take over).
| Input field | Output field | Notes |
|---|---|---|
defaultLocale |
defaultLocale |
Always present (required). |
fallbackLocale |
fallbackLocale |
Forwarded when defined. |
initialMessages |
messages |
Renamed; structurally identical. |
backend |
backend |
Forwarded as-is (structural). |
backends |
backends |
Forwarded as-is (structural). |
saveMissing |
saveMissing |
Forwarded when defined. |
plugins |
plugins |
Forwarded as-is (structural). |
Implementation Notes
- Field forwarding uses explicit
if (value !== undefined)guards rather than spread because the tsconfig setsexactOptionalPropertyTypes: true— explicit guards keep the output shape free of sentinelundefinedproperties that would otherwise conflict with optional fields onI18nInstanceOptions. BackendandI18nPlugincasts at the conversion boundary are unavoidable: the React-tier Zod schema validates only the structural fields (name,capabilities) of these types because their full surface includes function fields Zod cannot meaningfully validate. The Provider's runtime guard is the universal core itself, which exercises the function fields when it boots the instance.- The function is pure — calling it twice with the same input yields equivalent options (deep-equal but not necessarily reference-equal).
- The function does NOT call
createI18n. The Provider does that separately so config building and instance construction can be wired to different React lifecycles (useMemoon the config,useState/useRefon the instance).