Skip to content

Commit 481ca47

Browse files
Add docs for React MF2 integration (#60)
Co-authored-by: Luca Casonato <[email protected]>
1 parent 7ef1c36 commit 481ca47

File tree

3 files changed

+259
-0
lines changed

3 files changed

+259
-0
lines changed

docs/_data.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@ sidebar:
1919
- docs/integration/c/
2020
- docs/integration/cpp/
2121
- docs/frameworks/angular/
22+
- docs/frameworks/react/
2223
- docs/frameworks/sveltekit/
2324
- docs/lsp/

docs/frameworks/react.md

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
---
2+
title: Using MF2 with React
3+
sidebar_title: React
4+
---
5+
6+
This guide explains how to localize React applications with MessageFormat 2
7+
(MF2), using the `mf2react` package.
8+
9+
The library builds ontop of [i18next](https://www.i18next.com/) and
10+
[react-i18next](https://react.i18next.com/), popular internationalization
11+
frameworks for JavaScript and React.
12+
13+
The package contains a post-processor plugin for i18next that compiles MF2
14+
messages and converts lightweight curly-tag markup into safe HTML tags, which
15+
`react-i18next` can render as JSX. For example the message
16+
`{#bold}Hello{/bold},
17+
{$name}!` becomes `<strong>Hello</strong>, {$name}!` when
18+
rendered.
19+
20+
MF2 features such as pluralization, select, and conditional logic are fully
21+
supported. For example, the following MF2 message:
22+
23+
```mf2
24+
.match {$count: number}
25+
one {{You have {$count} message}}
26+
* {{You have {$count} messages}}
27+
```
28+
29+
Can be used in a React component like this:
30+
31+
```tsx
32+
import { Trans } from "react-i18next";
33+
export default function MessagesComponent({ count }: { count: number }) {
34+
return <Trans i18nKey="messages" values={{ count }} />;
35+
}
36+
```
37+
38+
## Introduction
39+
40+
> This guide assumes you have a basic understanding of React and i18next /
41+
> react-i18next.
42+
43+
## Installation and setup
44+
45+
In an existing React project, install the `mf2react` package, along with the
46+
`i18next`, and `react-i18next` dependencies:
47+
48+
```bash
49+
npm install mf2react i18next react-i18next
50+
```
51+
52+
You can also use a different package manager, such as `yarn`, `pnpm`, or `deno`
53+
to install the packages.
54+
55+
### Defining your catalogs (translations)
56+
57+
Create JSON files for each locale you want to support. For example, create a
58+
`locales/en/translation.json` file for English translations:
59+
60+
```json
61+
{
62+
"welcome": "Welcome to our application!",
63+
"goodbye": "Goodbye!",
64+
"greeting": "Hello, {$name}!",
65+
"apples": ".input {$value :number}\n.match $value\none {{{#bold}1{/bold} apple}}\n* {{{#bold}{$value}{/bold} apples}}"
66+
}
67+
```
68+
69+
And a `locales/no/translation.json` file for Norwegian translations:
70+
71+
```json
72+
{
73+
"welcome": "Velkommen til vår applikasjon!",
74+
"goodbye": "Ha det!",
75+
"greeting": "Hei, {$name}!",
76+
"apples": ".input {$value :number}\n.match $value\none {{{#bold}1{/bold} eple}}\n* {{{#bold}{$value}{/bold} epler}}"
77+
}
78+
```
79+
80+
### Setting up i18next
81+
82+
Create a `i18n.ts` file in your project to configure i18next.
83+
84+
```ts
85+
// i18n.ts
86+
import i18n from "i18next";
87+
import { initReactI18next } from "react-i18next";
88+
import { MF2PostProcessor, MF2ReactPreset } from "mf2react";
89+
90+
import en from "./locales/en/translation.json";
91+
import no from "./locales/no/translation.json";
92+
93+
i18n
94+
.use(MF2PostProcessor) // Enable the post-processor
95+
.use(MF2ReactPreset) // Enable curly-tag -> JSX conversion
96+
.use(initReactI18next)
97+
.init({
98+
lng: "en",
99+
postProcess: ["mf2"], // Apply MF2 to all translations
100+
resources: {
101+
// Reference the translation files
102+
en: { translation: en },
103+
no: { translation: no },
104+
},
105+
});
106+
107+
export default i18n;
108+
```
109+
110+
> Instead of defining the selected locale (`lng`) and `resources` directly in
111+
> the `init` function, you may also choose to load them dynamically, e.g. via
112+
> [i18next-http-backend](https://github.com/i18next/i18next-http-backend),
113+
> [i18next-resources-to-backend](https://github.com/i18next/i18next-resources-to-backend).
114+
> Additionally you may want to auto-detect the user's locale using
115+
> [i18next-browser-languagedetector](https://github.com/i18next/i18next-browser-languagedetector).
116+
117+
### Wrapping your application with I18nextProvider
118+
119+
To use translations in your React components, you need to wrap your application
120+
with the `I18nextProvider` from `react-i18next`. This is typically done in your
121+
main application file or layout component.
122+
123+
```ts
124+
"use client";
125+
126+
import { I18nextProvider } from "react-i18next";
127+
import { i18n } from "./i18n";
128+
129+
export default function AppLayout({ children }: { children: React.ReactNode }) {
130+
return <I18nextProvider i18n={i18n}>{children}</I18nextProvider>;
131+
}
132+
```
133+
134+
> Good to know: because `I18nextProvider` uses React context, it can only be
135+
> used in a client component.
136+
137+
#### Choosing where to place the provider
138+
139+
Where you place the provider affects what becomes client-rendered. You may
140+
either wrap your whole application in the provider, or each component that uses
141+
translations. This is because `I18nextProvider` must be used in a client
142+
component. Choose the placement based on how much of your UI should be
143+
client-rendered.
144+
145+
### Using translations in components
146+
147+
Now you can use the `<Trans>` component from `react-i18next` to render
148+
translations in your React components. For example:
149+
150+
```tsx
151+
import { Trans } from "react-i18next";
152+
export default function WelcomeComponent() {
153+
return (
154+
<div>
155+
<h1>
156+
<Trans i18nKey="welcome" />
157+
</h1>
158+
<p>
159+
<Trans i18nKey="goodbye" />
160+
</p>
161+
</div>
162+
);
163+
}
164+
```
165+
166+
> You can read more about the Trans component in the
167+
> [react-i18next documentation](https://react.i18next.com/latest/trans-component)
168+
169+
#### Passing variables to translations
170+
171+
You can also pass variables to your translations using the `values` prop of the
172+
`<Trans>` component. For example, if you have a translation that includes a
173+
variable:
174+
175+
```json
176+
{
177+
"greeting": "Hello, {$name}!"
178+
}
179+
```
180+
181+
You can use it in your component like this:
182+
183+
```tsx
184+
import { Trans } from "react-i18next";
185+
export default function GreetingComponent({ name }: { name: string }) {
186+
return <Trans i18nKey="greeting" values={{ name }} />;
187+
}
188+
```
189+
190+
This also works for MF2 messages with pluralization and formatting:
191+
192+
```tsx
193+
import { Trans } from "react-i18next";
194+
export default function ApplesComponent({ count }: { count: number }) {
195+
return <Trans i18nKey="apples" values={{ value: count }} />;
196+
}
197+
```
198+
199+
> Output when `count` is 1: **1** apple
200+
>
201+
> Output when `count` is 5: **5** apples
202+
203+
#### Markup with curly-tags
204+
205+
You can use curly-tags in your translations to add formatting. For example, in
206+
your translation file:
207+
208+
```json
209+
{
210+
"bold": "This is {#bold}bold text{/bold}."
211+
}
212+
```
213+
214+
You can render it in your component like this:
215+
216+
```tsx
217+
import { Trans } from "react-i18next";
218+
export default function BoldComponent() {
219+
return <Trans i18nKey="bold" />;
220+
}
221+
```
222+
223+
> Output: This is **bold text**.
224+
225+
This works because the `mf2react` post-processor converts the curly-tags into
226+
safe HTML tags, which `react-i18next` can render as JSX. The following tags are
227+
supported:
228+
229+
```txt
230+
{#bold}…{/bold}
231+
{#strong}…{/strong}
232+
{#i}…{/i}
233+
{#em}…{/em}
234+
{#u}…{/u}
235+
{#s}…{/s}
236+
{#small}…{/small}
237+
{#code}…{/code}
238+
```
239+
240+
## Notes and limitations
241+
242+
- i18next post-processors must return **strings**. The JSX conversion happens
243+
inside `<Trans>`.
244+
- Messages are compiled and cached per language for performance.
245+
- The curly-tag conversion is intentionally minimal and safe. It only recognizes
246+
tags defined in the alias list.
247+
- If you switch languages at runtime, the plugin automatically reuses or
248+
recompiles as needed.
249+
- Unsupported MF2 syntax will fall back gracefully to raw string + curly tag
250+
conversion.
251+
252+
## Acknowledgements
253+
254+
- [i18next](https://www.i18next.com/) - the internalization framework
255+
- [react-i18next](https://react.i18next.com/) - React bindings for i18next
256+
- [@messageformat/core](https://github.com/messageformat/messageformat) -
257+
MessageFormat2 engine used for compiling and evaluating MF2 syntax.

docs/integration/js.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ advanced uses are possible, which are documented in the API documentation.
1313
> number of higher-level framework-specific integration packages are available:
1414
>
1515
> - [Angular](../../frameworks/angular)
16+
> - [React](../../frameworks/react)
1617
> - [SvelteKit](../../frameworks/sveltekit)
1718
1819
## Installation and setup

0 commit comments

Comments
 (0)