first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View 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.

View 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.

View 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.

View 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.