first commit
This commit is contained in:
74
docs/ui/react/react-ui/src/locales/augmentation.md
Normal file
74
docs/ui/react/react-ui/src/locales/augmentation.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# `augmentation` — `ContributedTranslationKeys` registration
|
||||
|
||||
## Purpose
|
||||
|
||||
Side-effect-only TypeScript module that augments
|
||||
`@suaveplan/react-i18n`'s `ContributedTranslationKeys` interface with
|
||||
every key shipped by the `@suaveplan/react-ui` chassis. The
|
||||
augmentation runs purely in TypeScript's type space — there is no
|
||||
runtime value to import — but the file is included as a side-effect
|
||||
import from `src/locales/index.ts` so consumers' `tsconfig` picks it
|
||||
up automatically when they `import "@suaveplan/react-ui"`.
|
||||
|
||||
## Features
|
||||
|
||||
- Module augmentation that registers every chassis translation key
|
||||
into the branded `TranslationKey` union
|
||||
- Side-effect-only — zero runtime cost
|
||||
- Mirror of the canonical `en-GB.json` source-of-truth bundle so
|
||||
consumer autocomplete in IDEs lists the chassis keys without
|
||||
manually importing or copying them
|
||||
|
||||
## Usage: Basic example
|
||||
|
||||
```ts
|
||||
// In a consumer app:
|
||||
import "@suaveplan/react-ui";
|
||||
import { useTranslation } from "@suaveplan/react-i18n";
|
||||
|
||||
function Toolbar() {
|
||||
const { t } = useTranslation();
|
||||
return <button>{t("react-ui.themeToggle.label")}</button>; // typechecks
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced example
|
||||
|
||||
```ts
|
||||
// Compile-time error path:
|
||||
import "@suaveplan/react-ui";
|
||||
|
||||
const key = "react-ui.dialog.cloze"; // typo; not in ContributedTranslationKeys
|
||||
t(key); // Type error: argument of type '"react-ui.dialog.cloze"' is not
|
||||
// assignable to parameter of type 'TranslationKey'
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
This file exposes no runtime values. Its sole effect is to extend the
|
||||
TypeScript type space:
|
||||
|
||||
```ts
|
||||
declare module "@suaveplan/react-i18n" {
|
||||
interface ContributedTranslationKeys {
|
||||
readonly "react-ui.themeToggle.label": true;
|
||||
// … one entry per key in en-GB.json
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
After the augmentation lands, `TranslationKey` (defined in
|
||||
`@suaveplan/react-i18n` as `string & { ... } & (keyof AppTranslationKeys
|
||||
| keyof ContributedTranslationKeys)`) accepts the chassis keys as legal
|
||||
literal values.
|
||||
|
||||
## Implementation notes
|
||||
|
||||
The file is hand-edited in this version to mirror the canonical
|
||||
`en-GB.json` exactly. A future generator
|
||||
(`scripts/generate-translation-types.ts` from `@suaveplan/react-i18n`)
|
||||
will rewrite this file from the JSON whenever keys change so the type
|
||||
augmentation never drifts from runtime catalog contents. The empty
|
||||
`export {}` at the end is required so TypeScript treats the file as a
|
||||
module (not a script) — without it, the `declare module` block would
|
||||
be parsed in script mode and silently merged into the global scope.
|
||||
62
docs/ui/react/react-ui/src/locales/locales.types.md
Normal file
62
docs/ui/react/react-ui/src/locales/locales.types.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# `locales.types` — Locale registry and supported-locale brand
|
||||
|
||||
## Purpose
|
||||
|
||||
Declares the canonical `SUPPORTED_LOCALES` tuple and the derived
|
||||
`SupportedLocale` union for `@suaveplan/react-ui`. The chassis ships
|
||||
five locales chosen to exercise the harder paths of the universal i18n
|
||||
core: `en-GB` (canonical), `zh-CN` (CJK), `ar-AE` (RTL), `cy-GB` (Welsh
|
||||
six-form plurals), `ru-RU` (Cyrillic, Slavic plurals).
|
||||
|
||||
## Features
|
||||
|
||||
- Single source of truth for the locale set the chassis recognises
|
||||
- `SUPPORTED_LOCALES` is a `readonly`-typed tuple (`as const`) so
|
||||
consumers can iterate it without losing literal-type narrowing
|
||||
- `SupportedLocale` derived as `(typeof SUPPORTED_LOCALES)[number]` —
|
||||
adding a locale to the tuple automatically extends the union
|
||||
- Pairs with `reactUiBundles` (in `index.ts`) to provide the
|
||||
`Record<SupportedLocale, () => Promise<LocaleBundle>>` lazy-loader
|
||||
surface
|
||||
|
||||
## Usage: Basic example
|
||||
|
||||
```ts
|
||||
import { SUPPORTED_LOCALES, type SupportedLocale } from "@suaveplan/react-ui/locales";
|
||||
|
||||
function isSupported(locale: string): locale is SupportedLocale {
|
||||
return (SUPPORTED_LOCALES as readonly string[]).includes(locale);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced example
|
||||
|
||||
```ts
|
||||
import { SUPPORTED_LOCALES, reactUiBundles } from "@suaveplan/react-ui/locales";
|
||||
|
||||
// Eagerly preload every locale at app startup (rare; the default is lazy):
|
||||
async function preloadAll(): Promise<void> {
|
||||
await Promise.all(
|
||||
SUPPORTED_LOCALES.map(async (locale) => {
|
||||
await reactUiBundles[locale]();
|
||||
})
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `SUPPORTED_LOCALES: readonly ['en-GB', 'zh-CN', 'ar-AE', 'cy-GB', 'ru-RU']`
|
||||
- `type SupportedLocale = 'en-GB' | 'zh-CN' | 'ar-AE' | 'cy-GB' | 'ru-RU'`
|
||||
|
||||
## Implementation notes
|
||||
|
||||
Adding a sixth locale is a deliberate decision tracked through OpenSpec
|
||||
because every chassis component's `WithI18n` story matrix in Wave 3
|
||||
expands by one cell per locale. Translators contribute via PRs that
|
||||
add the locale to this tuple, the canonical `en-GB.json` mirror file
|
||||
under `src/locales/<locale>.json`, and the corresponding entry in
|
||||
`reactUiBundles`. The CI gate
|
||||
`bun scripts/check-locale-completeness.ts packages/react/react-ui` then
|
||||
ensures every key in `en-GB.json` is mirrored in the new bundle before
|
||||
the change can land.
|
||||
78
docs/ui/react/react-ui/src/locales/schema.md
Normal file
78
docs/ui/react/react-ui/src/locales/schema.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# `LocaleBundleSchema` — Zod schema for `@suaveplan/react-ui` locale bundles
|
||||
|
||||
## Purpose
|
||||
|
||||
Zod schema asserting that every locale bundle declares exactly the keys
|
||||
present in the canonical `en-GB.json` source-of-truth. The schema is
|
||||
built dynamically from `en-GB.json` itself so adding a key in English
|
||||
automatically tightens the contract for every other locale shipped by
|
||||
the package — there is no place for a translator to add a key that the
|
||||
English bundle has not blessed first.
|
||||
|
||||
## Features
|
||||
|
||||
- Strict mode (`.strict()`) rejects extra keys so a stray entry in
|
||||
`cy-GB.json` cannot silently leak into a consumer's `t()` call surface
|
||||
- Every key is required and typed as `z.string()`
|
||||
- `REACT_UI_BUNDLE_KEYS` exposes the exhaustive key list as a const
|
||||
array so other parts of the chassis (the `<ReactUiProvider>` mount
|
||||
hook, the registry-completeness CI gate, the augmentation generator)
|
||||
can iterate the contracted keys without re-importing the JSON file
|
||||
|
||||
## Usage: Basic example
|
||||
|
||||
```ts
|
||||
import { LocaleBundleSchema } from "@suaveplan/react-ui/locales";
|
||||
import frFR from "./fr-FR.json";
|
||||
|
||||
const parsed = LocaleBundleSchema.safeParse(frFR);
|
||||
if (!parsed.success) {
|
||||
throw new Error(`fr-FR is missing keys: ${parsed.error.message}`);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced example
|
||||
|
||||
```ts
|
||||
import {
|
||||
LocaleBundleSchema,
|
||||
REACT_UI_BUNDLE_KEYS,
|
||||
reactUiBundles,
|
||||
SUPPORTED_LOCALES,
|
||||
} from "@suaveplan/react-ui/locales";
|
||||
|
||||
// Fail-fast key parity check at app startup
|
||||
async function assertKeyParity(): Promise<void> {
|
||||
for (const locale of SUPPORTED_LOCALES) {
|
||||
const bundle = await reactUiBundles[locale]();
|
||||
const result = LocaleBundleSchema.safeParse(bundle);
|
||||
if (!result.success) {
|
||||
throw new Error(`Locale ${locale} failed schema: ${result.error.message}`);
|
||||
}
|
||||
for (const key of REACT_UI_BUNDLE_KEYS) {
|
||||
if (!(key in bundle)) {
|
||||
throw new Error(`Locale ${locale} missing key ${key}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `LocaleBundleSchema: z.ZodObject<...>` — Zod schema with one
|
||||
`z.string()` field per key in `en-GB.json`. Strict.
|
||||
- `type LocaleBundle = z.infer<typeof LocaleBundleSchema>` — typed
|
||||
bundle shape. Equivalent to a `Record` over the contracted key set.
|
||||
- `REACT_UI_BUNDLE_KEYS: readonly string[]` — exhaustive key list.
|
||||
|
||||
## Implementation notes
|
||||
|
||||
The schema is built at module-load time by walking
|
||||
`Object.keys(enGB)` rather than at type-only level. That means adding
|
||||
a key to `en-GB.json` does not require regenerating any TypeScript file
|
||||
to extend the schema — the runtime build picks up the new key on the
|
||||
next module evaluation. The accompanying `augmentation.ts` file IS
|
||||
generated and DOES need to be regenerated when keys change; the
|
||||
`scripts/check-locale-completeness.ts` CI gate fails the build if a key
|
||||
appears in `en-GB.json` but is absent from any other locale bundle.
|
||||
46
docs/ui/react/react-ui/src/locales/translation-keys.md
Normal file
46
docs/ui/react/react-ui/src/locales/translation-keys.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# `translation-keys` — codegen union of contributed keys
|
||||
|
||||
## Purpose
|
||||
|
||||
`translation-keys.ts` is the codegen-emitted string-literal union of every translation key contributed by `@suaveplan/react-ui`. It is regenerated from `src/locales/en-GB.json` by `bun packages/react/react-i18n/scripts/generate-translation-types.ts packages/react/react-ui --package-name @suaveplan/react-ui` and committed verbatim so consumers and CI can statically reference the union without re-running the codegen.
|
||||
|
||||
The union is useful for non-React consumers (universal helpers, server-side validators) that want a typed reference to the chassis-contributed keys without taking on the full `@suaveplan/react-i18n` `ContributedTranslationKeys` augmentation pathway. React consumers should prefer the branded `TranslationKey` type from `@suaveplan/react-i18n` instead — that type widens automatically as more packages augment the interface and is the type chassis component props use.
|
||||
|
||||
## Features
|
||||
|
||||
- Single-purpose: re-declares every key in `en-GB.json` as a string-literal union.
|
||||
- Sorted alphabetically via `localeCompare("en")` for diff stability.
|
||||
- Generated comment header documents the regeneration command so the file does not drift on hand-edit.
|
||||
- Side-effect-free — no runtime code, no imports from runtime packages.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import type { ReactUiTranslationKey } from "@suaveplan/react-ui";
|
||||
|
||||
declare const key: ReactUiTranslationKey;
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import type { ReactUiTranslationKey } from "@suaveplan/react-ui";
|
||||
|
||||
const TRANSLATABLE_FALLBACKS: Record<string, ReactUiTranslationKey> = {
|
||||
button: "react-ui.button.iconOnly.fallback",
|
||||
empty: "react-ui.empty.noResults",
|
||||
loading: "react-ui.loading",
|
||||
};
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `ReactUiTranslationKey`
|
||||
|
||||
String-literal union of every (flattened) key in `src/locales/en-GB.json`. Sorted alphabetically; deterministic across regeneration runs.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The file is regenerated by codegen — never hand-edit. Re-run `bun packages/react/react-i18n/scripts/generate-translation-types.ts packages/react/react-ui --package-name @suaveplan/react-ui` after every change to `en-GB.json` and commit the regenerated file.
|
||||
- The sibling `augmentation.d.ts` (also generated by the same codegen) extends the runtime-typed `ContributedTranslationKeys` interface that `TranslationKey` reads. The two files always carry the same set of keys; the union here is a convenience export for non-React consumers, the augmentation is the chassis-default type integration.
|
||||
- The output ordering uses `localeCompare("en")` so the union is byte-identical for identical input regardless of `en-GB.json`'s in-file key insertion order.
|
||||
Reference in New Issue
Block a user