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

3.7 KiB

registry.schemas

Purpose

Strict-object Zod schemas for every plugin kind accepted by ReactI18nRegistry. The schemas are the boundary at which a malformed plugin shape is rejected at registration time — before any callsite has a chance to consume the bad plugin and throw a less-actionable runtime error elsewhere. Each schema is .strict()-locked so extra fields on a plugin (typos, stale fields from an older registry version) trigger validation failure rather than silently passing through.

Features

  • One schema per plugin kind — DetectorPluginSchema, FormatterPluginSchema, PostProcessorPluginSchema, LocaleAdapterPluginSchema.
  • Non-empty id enforced (z.string().min(1)) so every plugin has a stable lookup key.
  • Function-shape fields validated structurally; their semantic correctness is exercised by the tests of each consumer (e.g. useFormatter test calls the registered plugin's format and asserts the output).
  • Schemas surface failure as a Zod ZodError, which the registry wraps in a ReactI18nError(REACT_I18N.PLUGIN_INVALID) with the issues attached to cause.

Usage: Basic Example

import { FormatterPluginSchema } from "@suaveplan/react-i18n/registry";

const result = FormatterPluginSchema.safeParse({
    id: "duration",
    format: (locale, args) => String(args.ms),
});

result.success; // true

Usage: Advanced Example

The schemas accept generic format / process / resolve / detect functions because their signatures cannot be expressed as a closed Zod shape. The schema verifies the function is present and is a function; the actual (locale, args) => result contract is enforced at the type-system level via FormatterPlugin<TArgs, TResult> and at the test level by exercising the plugin's behavior.

import { DetectorPluginSchema } from "@suaveplan/react-i18n/registry";

const malformed = {
    id: "ab",
    priority: 0,
    detect: { name: "ab" /* missing environment */ },
};

const result = DetectorPluginSchema.safeParse(malformed);
result.success; // false
result.error.issues; // [{ path: ["detect", "environment"], message: "Required" }]

API Reference

DetectorPluginSchema

Validates { id: string; priority: number; detect: { name; environment; detect?; detectAsync?; cache? } }. The inner detect object is passthrough because LocaleDetector from @suaveplan/i18n-detector may carry forward-compatible fields the registry does not own.

FormatterPluginSchema

Validates { id: string; schema?: unknown; defaultOptions?: unknown; format: function }. The schema field is opaque (z.unknown()) because it is a Zod schema reference and Zod cannot meaningfully validate "is this a Zod schema."

PostProcessorPluginSchema

Validates { id: string; priority: number; process: function }.

LocaleAdapterPluginSchema

Validates { id: string; resolve: function }.

Implementation Notes

  • All schemas are .strict() — extra keys cause validation failure. This catches stale fields from older registry versions and typos in plugin objects.
  • The schemas are exported from a single file rather than co-located with their plugin types so a consumer importing @suaveplan/react-i18n/registry for runtime validation does not pay the bundle cost of the plugin function code.
  • Validation errors are passed through to the registry which decorates them with stable error codes; consumers should never safeParse directly against these schemas — register through ReactI18nRegistry.register* to get the decorated error.
  • The schemas live in this package rather than in @suaveplan/types because they validate React-specific plugin shapes; the ReactI18nRegistry is itself React-bound (consumed via context).