i18n: Translator adapter to reuse an existing i18n runtime for astryx strings
- Dominant language
- TypeScript
- Stars
- 13k
- Forks
- 1.1k
- Avg merge
- 1d 15h
- Merged PRs (30d)
- 690
Description
## Motivation
The original RFC (#3641) proposed a `Translator` adapter interface so consumers could plug their existing i18n runtime (react-intl, i18next, next-intl, LinguiJS, etc.) into `` as the resolver for astryx-shipped strings. Consumers with an existing i18n stack could then merge astryx's catalog into their own and get one-provider / one-catalog / one-runtime — no double bookkeeping.
The v1 implementation (PRs #3765 → #4016) shipped everything except this adapter. The current story is the two-provider coexistence pattern documented in [`npx astryx docs internationalization`](https://astryx.atmeta.com/docs/internationalization#using-astryx-with-your-own-i18n-library):
```tsx
```
That works and matches how MUI + react-intl users compose today, but it means two providers, two catalogs (`appFr` + `astryxFr`), and manual locale-sync. The adapter would collapse all of that into:
```tsx
```
## Design sketch
The adapter is a small interface (RFC #3641 already sketched it):
```ts
export interface Translator {
format(message: string, values?: Record, locale?: string): string;
}
```
`` would use the passed translator instead of the built-in `intl-messageformat` runtime. astryx keys (`@astryx.*`) still live in astryx's shipped `en.json` catalog; the caller merges those into whatever catalog store their runtime uses (react-intl `messages`, i18next resources, etc.).
Open design questions:
1. **Catalog merging** — does astryx export a helper to fold `@astryxdesign/core/locales/en.json` into a react-intl / i18next-shaped store, or do consumers do it manually?
2. **Regional fallback** — astryx's built-in resolver walks `pt-BR → pt → en`. Do we keep that in the runtime and only delegate the *final message.format(values)* to the adapter, or do we punt fallback to the consumer's runtime entirely?
3. **ICU parity** — react-intl handles ICU MessageFormat natively (both use `intl-messageformat` under the hood, actually). i18next uses `{{mustache}}` interpolation by default and needs a plugin for ICU — do we ship an example adapter with the required config?
4. **RSC/SSR compatibility** — the adapter needs to work in server components too; the built-in runtime already has RSC support via the `react-server` export condition. Adapter design should keep that door open.
## Prior art
- `react-intl` itself: `` with an injected `formatMessage` fn.
- `next-intl`: has a first-class integration with MUI's `LocalizationProvider` via a documented pattern.
- MUI DataGrid's `localeText` prop is the sibling of what we're proposing — same idea (delegate string resolution to the consumer), lower ambition (one component, one catalog).
## Scope
- New PR building on the current i18n stack (post #4016 merge).
- Adds the `Translator` type to `@astryxdesign/core/i18n`.
- Extends `` with an optional `translator` prop that overrides the built-in runtime when provided.
- Ships an example adapter for `react-intl` (smallest surface, most-used) as an integration guide in `packages/cli/docs/internationalization.doc.mjs`.
- Ships a codemod (`npx astryx upgrade`) is nice-to-have but not required — the migration is opt-in.
## Non-goals
- **Not** replacing `intl-messageformat` as the built-in runtime. Consumers who don't opt in continue to get today's behavior.
- **Not** shipping first-party astryx translations (tracked separately in the roadmap).
- **Not** breaking the two-provider coexistence pattern documented today — it stays as a valid alternative for consumers who prefer runtime isolation.
## Related
- #3641 — parent RFC
- #4016 — docs page that mentions the adapter as a roadmap item
- PRs #3765 → #4016 — v1 implementation the adapter builds on
Contributor guide
Assessment
This issue has not been assessed yet.