Initial Commit
This commit is contained in:
67
apps/demo/src/client/i18n/i18n-instance.ts
Normal file
67
apps/demo/src/client/i18n/i18n-instance.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Build the demo's `I18nInstance`.
|
||||
*
|
||||
* The instance is consumed by `<ReactUiProvider>` and any
|
||||
* `@suaveplan/react-ui` primitive that calls `useTranslation()` (e.g.
|
||||
* `<Button>` for icon-only-button fallback, `<Input>` for placeholder
|
||||
* resolution).
|
||||
*
|
||||
* Locale strategy: the URL carries the language subtag only (`/en/home`,
|
||||
* `/zh/home`, etc.). The chassis bundles in `@suaveplan/react-ui` are
|
||||
* keyed by full BCP47 — `en-GB`, `zh-CN`, `ar-AE`, `cy-GB`, `ru-RU`. We
|
||||
* therefore canonicalise each demo base locale to its closest BCP47
|
||||
* variant and use THAT as the i18n locale. The demo's typed `Messages`
|
||||
* catalog continues to be consumed via `useLocale()` keyed by base, so
|
||||
* the demo never depends on react-i18n's `t(key)` for its own copy.
|
||||
*
|
||||
* The chassis bundles themselves are auto-registered by
|
||||
* `<ReactUiProvider>` (its internal `ReactUiBundleLoader` effect imports
|
||||
* the matching JSON and merges it into the catalog on mount / language
|
||||
* change), so this module deliberately does NOT preload them.
|
||||
*/
|
||||
import { createI18n, type I18nInstance } from "@suaveplan/i18n/plugin";
|
||||
import type { BaseLocale } from "./locales.js";
|
||||
|
||||
export type DemoBcp47 = "en-GB" | "zh-CN" | "ar-AE" | "cy-GB" | "ru-RU";
|
||||
|
||||
/** Map URL base locale -> canonical BCP47 variant fed to the i18n core. */
|
||||
export const BASE_TO_BCP47: Readonly<Record<BaseLocale, DemoBcp47>> = {
|
||||
en: "en-GB",
|
||||
zh: "zh-CN",
|
||||
ar: "ar-AE",
|
||||
cy: "cy-GB",
|
||||
ru: "ru-RU",
|
||||
};
|
||||
|
||||
/**
|
||||
* Singleton i18n instance for the demo. Created lazily so the i18n core
|
||||
* (which is module-side-effect free) does not run during static module
|
||||
* graph evaluation in tests or SSR snapshots.
|
||||
*/
|
||||
let cached: I18nInstance | undefined;
|
||||
|
||||
export function getDemoI18n(): I18nInstance {
|
||||
if (cached !== undefined) return cached;
|
||||
// Use the default `string` generic so the cached singleton matches
|
||||
// `<ReactUiProvider>`'s `i18n: I18nInstance` (without the
|
||||
// narrowed locale generic).
|
||||
const instance: I18nInstance = createI18n({
|
||||
defaultLocale: BASE_TO_BCP47.en as string,
|
||||
fallbackLocale: BASE_TO_BCP47.en as string,
|
||||
});
|
||||
cached = instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drive the i18n instance to the BCP47 variant matching the URL base.
|
||||
* Safe to call from a `useEffect` — `changeLanguage(...)` short-circuits
|
||||
* when the locale already matches.
|
||||
*/
|
||||
export async function syncDemoLocale(base: BaseLocale): Promise<void> {
|
||||
const i18n = getDemoI18n();
|
||||
const target = BASE_TO_BCP47[base];
|
||||
if (i18n.locale !== target) {
|
||||
await i18n.changeLanguage(target);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user