Files
skills/docs/localization/i18n/react-i18n/src/config/to-i18n-instance-options.md
2026-05-29 08:20:56 +01:00

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 sentinel undefineds on the output.
  • Renames initialMessages → messages so 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 sets exactOptionalPropertyTypes: true — explicit guards keep the output shape free of sentinel undefined properties that would otherwise conflict with optional fields on I18nInstanceOptions.
  • Backend and I18nPlugin casts 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 (useMemo on the config, useState/useRef on the instance).