Files
suaveplan/packages/ui/react/react-ui/tests/setup/test-i18n.ts
2026-05-27 23:47:53 +01:00

69 lines
2.5 KiB
TypeScript

/**
* Helper for spinning up a real `@suaveplan/i18n` `I18nInstance` plus
* its bundled translations under a fresh `I18nInstance` per test. No
* mocks — every consumer of an i18n instance in the test suite uses
* this helper so the wiring matches production exactly.
*
* The bundled `react-ui` JSON files ship FLAT keys
* (`"react-ui.button.iconOnly.fallback": "Action"`). The catalog's `t()`
* walks dot-separated paths, so we nest the flat bag into a deep
* record (`{ react-ui: { button: { iconOnly: { fallback: "Action" } } } }`)
* before merging into the catalog. This mirrors the chassis
* `ReactUiProvider`'s `nestFlatKeys` step exactly so production and
* tests resolve keys via the same pathway.
*/
import { createI18n, type I18nInstance } from "@suaveplan/i18n/plugin";
import { reactUiBundles } from "../../src/locales/index.js";
export interface CreateTestI18nOptions {
readonly locale?: "en-GB" | "zh-CN" | "ar-AE" | "cy-GB" | "ru-RU";
}
type NestedTranslationRecord = {
[key: string]: string | NestedTranslationRecord;
};
function nestFlatKeys(bag: Record<string, string>): NestedTranslationRecord {
const out: NestedTranslationRecord = {};
for (const [flatKey, value] of Object.entries(bag)) {
const parts = flatKey.split(".");
let cursor: NestedTranslationRecord = out;
for (let i = 0; i < parts.length - 1; i += 1) {
const seg = parts[i] as string;
const existing = cursor[seg];
if (
typeof existing !== "object" ||
existing === null ||
Array.isArray(existing)
) {
cursor[seg] = {};
}
cursor = cursor[seg] as NestedTranslationRecord;
}
cursor[parts[parts.length - 1] as string] = value;
}
return out;
}
/**
* Build an `I18nInstance` seeded with the chassis locale bundles.
* Useful for tests that need a Provider but want deterministic
* translations.
*/
export async function createTestI18n(
options: CreateTestI18nOptions = {}
): Promise<I18nInstance> {
const locale = options.locale ?? "en-GB";
const i18n = createI18n({
defaultLocale: locale,
fallbackLocale: "en-GB",
});
for (const code of ["en-GB", "zh-CN", "ar-AE", "cy-GB", "ru-RU"] as const) {
const bundle = await reactUiBundles[code]();
const nested = nestFlatKeys(bundle as Record<string, string>);
i18n.catalog.mergeMessages(code, nested as Record<string, unknown>);
}
return i18n;
}