@intlayer/docs 7.0.4-canary.0 → 7.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/blog/ar/intlayer_with_i18next.md +68 -106
- package/blog/ar/intlayer_with_next-i18next.md +84 -288
- package/blog/ar/intlayer_with_next-intl.md +58 -337
- package/blog/ar/intlayer_with_react-i18next.md +68 -290
- package/blog/ar/intlayer_with_react-intl.md +63 -266
- package/blog/de/intlayer_with_i18next.md +77 -97
- package/blog/de/intlayer_with_next-i18next.md +69 -296
- package/blog/de/intlayer_with_next-intl.md +59 -340
- package/blog/de/intlayer_with_react-i18next.md +68 -290
- package/blog/de/intlayer_with_react-intl.md +62 -264
- package/blog/en/intlayer_with_i18next.md +36 -1638
- package/blog/en/intlayer_with_next-i18next.md +22 -847
- package/blog/en/intlayer_with_next-intl.md +32 -1053
- package/blog/en/intlayer_with_react-i18next.md +38 -764
- package/blog/en/intlayer_with_react-intl.md +42 -1018
- package/blog/en-GB/intlayer_with_i18next.md +67 -103
- package/blog/en-GB/intlayer_with_next-i18next.md +71 -292
- package/blog/en-GB/intlayer_with_next-intl.md +58 -337
- package/blog/en-GB/intlayer_with_react-i18next.md +67 -289
- package/blog/en-GB/intlayer_with_react-intl.md +61 -264
- package/blog/es/intlayer_with_i18next.md +67 -103
- package/blog/es/intlayer_with_next-i18next.md +71 -296
- package/blog/es/intlayer_with_next-intl.md +57 -338
- package/blog/es/intlayer_with_react-i18next.md +68 -290
- package/blog/es/intlayer_with_react-intl.md +62 -265
- package/blog/fr/intlayer_with_i18next.md +66 -104
- package/blog/fr/intlayer_with_next-i18next.md +82 -285
- package/blog/fr/intlayer_with_next-intl.md +57 -338
- package/blog/fr/intlayer_with_react-i18next.md +67 -289
- package/blog/fr/intlayer_with_react-intl.md +61 -264
- package/blog/hi/intlayer_with_i18next.md +68 -104
- package/blog/hi/intlayer_with_next-i18next.md +74 -299
- package/blog/hi/intlayer_with_next-intl.md +57 -239
- package/blog/hi/intlayer_with_react-i18next.md +69 -291
- package/blog/hi/intlayer_with_react-intl.md +65 -268
- package/blog/id/intlayer_with_i18next.md +126 -0
- package/blog/id/intlayer_with_next-i18next.md +142 -0
- package/blog/id/intlayer_with_next-intl.md +113 -0
- package/blog/id/intlayer_with_react-i18next.md +124 -0
- package/blog/id/intlayer_with_react-intl.md +122 -0
- package/blog/it/intlayer_with_i18next.md +67 -103
- package/blog/it/intlayer_with_next-i18next.md +71 -296
- package/blog/it/intlayer_with_next-intl.md +57 -338
- package/blog/it/intlayer_with_react-i18next.md +68 -290
- package/blog/it/intlayer_with_react-intl.md +62 -265
- package/blog/ja/intlayer_with_i18next.md +68 -103
- package/blog/ja/intlayer_with_next-i18next.md +85 -283
- package/blog/ja/intlayer_with_next-intl.md +58 -336
- package/blog/ja/intlayer_with_react-i18next.md +68 -290
- package/blog/ja/intlayer_with_react-intl.md +62 -264
- package/blog/ko/intlayer_with_i18next.md +80 -96
- package/blog/ko/intlayer_with_next-i18next.md +85 -287
- package/blog/ko/intlayer_with_next-intl.md +68 -327
- package/blog/ko/intlayer_with_react-i18next.md +68 -290
- package/blog/ko/intlayer_with_react-intl.md +64 -266
- package/blog/pl/intlayer_with_i18next.md +126 -0
- package/blog/pl/intlayer_with_next-i18next.md +142 -0
- package/blog/pl/intlayer_with_next-intl.md +111 -0
- package/blog/pl/intlayer_with_react-i18next.md +124 -0
- package/blog/pl/intlayer_with_react-intl.md +122 -0
- package/blog/pt/intlayer_with_i18next.md +67 -103
- package/blog/pt/intlayer_with_next-i18next.md +72 -293
- package/blog/pt/intlayer_with_next-intl.md +57 -256
- package/blog/pt/intlayer_with_react-i18next.md +104 -78
- package/blog/pt/intlayer_with_react-intl.md +62 -266
- package/blog/ru/intlayer_with_i18next.md +66 -104
- package/blog/ru/intlayer_with_next-i18next.md +71 -296
- package/blog/ru/intlayer_with_next-intl.md +58 -337
- package/blog/ru/intlayer_with_react-i18next.md +68 -290
- package/blog/ru/intlayer_with_react-intl.md +62 -265
- package/blog/tr/intlayer_with_i18next.md +71 -107
- package/blog/tr/intlayer_with_next-i18next.md +72 -297
- package/blog/tr/intlayer_with_next-intl.md +58 -339
- package/blog/tr/intlayer_with_react-i18next.md +69 -291
- package/blog/tr/intlayer_with_react-intl.md +63 -285
- package/blog/vi/intlayer_with_i18next.md +126 -0
- package/blog/vi/intlayer_with_next-i18next.md +142 -0
- package/blog/vi/intlayer_with_next-intl.md +111 -0
- package/blog/vi/intlayer_with_react-i18next.md +124 -0
- package/blog/vi/intlayer_with_react-intl.md +122 -0
- package/blog/zh/intlayer_with_i18next.md +67 -102
- package/blog/zh/intlayer_with_next-i18next.md +72 -296
- package/blog/zh/intlayer_with_next-intl.md +58 -336
- package/blog/zh/intlayer_with_react-i18next.md +68 -290
- package/blog/zh/intlayer_with_react-intl.md +63 -106
- package/docs/ar/plugins/sync-json.md +244 -0
- package/docs/de/plugins/sync-json.md +244 -0
- package/docs/en/intlayer_cli.md +25 -0
- package/docs/en/intlayer_with_nextjs_14.md +2 -0
- package/docs/en/intlayer_with_nextjs_15.md +2 -0
- package/docs/en/intlayer_with_nextjs_16.md +2 -0
- package/docs/en/plugins/sync-json.md +1 -1
- package/docs/en-GB/plugins/sync-json.md +244 -0
- package/docs/es/plugins/sync-json.md +244 -0
- package/docs/fr/plugins/sync-json.md +244 -0
- package/docs/hi/plugins/sync-json.md +244 -0
- package/docs/id/plugins/sync-json.md +244 -0
- package/docs/it/plugins/sync-json.md +244 -0
- package/docs/ja/plugins/sync-json.md +244 -0
- package/docs/ko/plugins/sync-json.md +244 -0
- package/docs/pl/plugins/sync-json.md +244 -0
- package/docs/pt/plugins/sync-json.md +244 -0
- package/docs/ru/plugins/sync-json.md +244 -0
- package/docs/tr/plugins/sync-json.md +245 -0
- package/docs/vi/plugins/sync-json.md +244 -0
- package/docs/zh/plugins/sync-json.md +244 -0
- package/package.json +14 -14
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
createdAt: 2025-01-02
|
|
3
3
|
updatedAt: 2025-10-29
|
|
4
|
-
title: How to
|
|
5
|
-
description:
|
|
4
|
+
title: How to automate your react-intl JSON translations using Intlayer
|
|
5
|
+
description: Automate your JSON translations with Intlayer and react-intl for enhanced internationalization in React applications.
|
|
6
6
|
keywords:
|
|
7
7
|
- react-intl
|
|
8
8
|
- Intlayer
|
|
@@ -21,98 +21,70 @@ history:
|
|
|
21
21
|
changes: Change to syncJSON plugin
|
|
22
22
|
---
|
|
23
23
|
|
|
24
|
-
#
|
|
25
|
-
|
|
26
|
-
This comprehensive guide demonstrates how to integrate **Intlayer** with **react-intl** to manage translations in a React application efficiently. You'll learn to declare your translatable content with Intlayer and consume those messages with **react-intl**, a popular library from the [FormatJS](https://formatjs.io/docs/react-intl) ecosystem.
|
|
27
|
-
|
|
28
|
-
## Table of Contents
|
|
29
|
-
|
|
30
|
-
<TOC/>
|
|
24
|
+
# How to automate your react-intl JSON translations using Intlayer
|
|
31
25
|
|
|
32
26
|
## What is Intlayer?
|
|
33
27
|
|
|
34
|
-
**Intlayer** is an innovative, open-source internationalization
|
|
35
|
-
|
|
36
|
-
With Intlayer, you can:
|
|
37
|
-
|
|
38
|
-
- **Easily manage translations** using declarative dictionaries at the component level.
|
|
39
|
-
- **Dynamically localize content** throughout your application.
|
|
40
|
-
- **Access translations in both client-side and server-side components**.
|
|
41
|
-
- **Ensure TypeScript support** with autogenerated types, improving autocompletion and error detection.
|
|
42
|
-
- **Benefit from advanced features**, like dynamic locale detection and switching.
|
|
43
|
-
- **Maintain component-level translations** to prevent orphaned translations when components are moved or deleted.
|
|
44
|
-
|
|
45
|
-
> Intlayer also integrates with Next.js, Express, React, and other popular frameworks. Check out our documentation for framework-specific guides.
|
|
46
|
-
|
|
47
|
-
---
|
|
28
|
+
**Intlayer** is an innovative, open-source internationalization library designed to address the shortcomings of traditional i18n solutions. It offers a modern approach to content management in React applications.
|
|
48
29
|
|
|
49
|
-
|
|
30
|
+
See a concrete comparison with react-intl in our [react-i18next vs. react-intl vs. Intlayer](https://github.com/aymericzip/intlayer/blob/main/docs/blog/en/react-i18next_vs_react-intl_vs_intlayer.md) blog post.
|
|
50
31
|
|
|
51
|
-
|
|
32
|
+
## Why Combine Intlayer with react-intl?
|
|
52
33
|
|
|
53
|
-
|
|
34
|
+
While Intlayer provides an excellent standalone i18n solution (see our [React integration guide](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/intlayer_with_vite+react.md)), you might want to combine it with react-intl for several reasons:
|
|
54
35
|
|
|
55
|
-
1. **
|
|
56
|
-
|
|
36
|
+
1. **Existing codebase**: You have an established react-intl implementation and want to gradually migrate to Intlayer's improved developer experience.
|
|
37
|
+
2. **Legacy requirements**: Your project requires compatibility with existing react-intl plugins or workflows.
|
|
38
|
+
3. **Team familiarity**: Your team is comfortable with react-intl but wants better content management.
|
|
57
39
|
|
|
58
|
-
|
|
59
|
-
Each content declaration file collects all translations needed by a component. This is particularly helpful in TypeScript projects where missing translations can be caught at compile time.
|
|
40
|
+
**For that, Intlayer can be implemented as an adapter for react-intl to help automating your JSON translations in CLI or CI/CD pipelines, testing your translations, and more.**
|
|
60
41
|
|
|
61
|
-
|
|
62
|
-
Whenever you add or update translations, Intlayer regenerates message JSON files automatically.
|
|
42
|
+
This guide shows you how to leverage Intlayer's superior content declaration system while maintaining compatibility with react-intl.
|
|
63
43
|
|
|
64
|
-
|
|
65
|
-
TypeScript integration ensures that missing or incorrect translation keys are caught during development.
|
|
44
|
+
## Table of Contents
|
|
66
45
|
|
|
67
|
-
|
|
46
|
+
<TOC/>
|
|
68
47
|
|
|
69
|
-
## Step-by-Step Guide to Set Up Intlayer
|
|
48
|
+
## Step-by-Step Guide to Set Up Intlayer with react-intl
|
|
70
49
|
|
|
71
50
|
### Step 1: Install Dependencies
|
|
72
51
|
|
|
73
|
-
Install the necessary packages
|
|
52
|
+
Install the necessary packages:
|
|
74
53
|
|
|
75
54
|
```bash packageManager="npm"
|
|
76
|
-
npm install intlayer
|
|
55
|
+
npm install intlayer @intlayer/sync-json-plugin
|
|
77
56
|
```
|
|
78
57
|
|
|
79
58
|
```bash packageManager="pnpm"
|
|
80
|
-
pnpm add intlayer
|
|
59
|
+
pnpm add intlayer @intlayer/sync-json-plugin
|
|
81
60
|
```
|
|
82
61
|
|
|
83
62
|
```bash packageManager="yarn"
|
|
84
|
-
yarn add intlayer
|
|
63
|
+
yarn add intlayer @intlayer/sync-json-plugin
|
|
85
64
|
```
|
|
86
65
|
|
|
87
|
-
|
|
66
|
+
**Package descriptions:**
|
|
88
67
|
|
|
89
|
-
- **intlayer**: Core
|
|
90
|
-
-
|
|
91
|
-
- **@intlayer/sync-json-plugin**: Plugin to automatically sync Intlayer dictionaries to react-intl compatible JSON files.
|
|
68
|
+
- **intlayer**: Core library for internationalization management, content declaration, and building
|
|
69
|
+
- **@intlayer/sync-json-plugin**: Plugin to export Intlayer content declarations to react-intl compatible JSON format
|
|
92
70
|
|
|
93
|
-
|
|
71
|
+
### Step 2: Implement the Intlayer plugin to wrap the JSON
|
|
94
72
|
|
|
95
|
-
|
|
73
|
+
Create an Intlayer configuration file to define your supported locales:
|
|
96
74
|
|
|
97
|
-
|
|
75
|
+
**If you want to also export JSON dictionaries for react-intl**, add the `syncJSON` plugin:
|
|
98
76
|
|
|
99
|
-
```typescript fileName="intlayer.config.ts"
|
|
77
|
+
```typescript fileName="intlayer.config.ts"
|
|
100
78
|
import { Locales, type IntlayerConfig } from "intlayer";
|
|
101
79
|
import { syncJSON } from "@intlayer/sync-json-plugin";
|
|
102
80
|
|
|
103
81
|
const config: IntlayerConfig = {
|
|
104
82
|
internationalization: {
|
|
105
|
-
locales: [
|
|
106
|
-
Locales.ENGLISH,
|
|
107
|
-
Locales.FRENCH,
|
|
108
|
-
Locales.SPANISH,
|
|
109
|
-
// Add your other locales here
|
|
110
|
-
],
|
|
83
|
+
locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
|
|
111
84
|
defaultLocale: Locales.ENGLISH,
|
|
112
85
|
},
|
|
113
86
|
plugins: [
|
|
114
87
|
syncJSON({
|
|
115
|
-
// Define the output directory for react-intl message files
|
|
116
88
|
source: ({ key, locale }) => `./intl/messages/${locale}/${key}.json`,
|
|
117
89
|
}),
|
|
118
90
|
],
|
|
@@ -121,978 +93,30 @@ const config: IntlayerConfig = {
|
|
|
121
93
|
export default config;
|
|
122
94
|
```
|
|
123
95
|
|
|
124
|
-
|
|
125
|
-
import { Locales } from "intlayer";
|
|
126
|
-
import { syncJSON } from "@intlayer/sync-json-plugin";
|
|
127
|
-
|
|
128
|
-
/** @type {import('intlayer').IntlayerConfig} */
|
|
129
|
-
const config = {
|
|
130
|
-
internationalization: {
|
|
131
|
-
locales: [
|
|
132
|
-
Locales.ENGLISH,
|
|
133
|
-
Locales.FRENCH,
|
|
134
|
-
Locales.SPANISH,
|
|
135
|
-
// Add your other locales here
|
|
136
|
-
],
|
|
137
|
-
defaultLocale: Locales.ENGLISH,
|
|
138
|
-
},
|
|
139
|
-
plugins: [
|
|
140
|
-
syncJSON({
|
|
141
|
-
// Define the output directory for react-intl message files
|
|
142
|
-
source: ({ key, locale }) => `./intl/messages/${locale}/${key}.json`,
|
|
143
|
-
}),
|
|
144
|
-
],
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
export default config;
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
```javascript fileName="intlayer.config.cjs" codeFormat="commonjs"
|
|
151
|
-
const { Locales } = require("intlayer");
|
|
152
|
-
const { syncJSON } = require("@intlayer/sync-json-plugin");
|
|
153
|
-
|
|
154
|
-
/** @type {import('intlayer').IntlayerConfig} */
|
|
155
|
-
const config = {
|
|
156
|
-
internationalization: {
|
|
157
|
-
locales: [
|
|
158
|
-
Locales.ENGLISH,
|
|
159
|
-
Locales.FRENCH,
|
|
160
|
-
Locales.SPANISH,
|
|
161
|
-
// Add your other locales here
|
|
162
|
-
],
|
|
163
|
-
defaultLocale: Locales.ENGLISH,
|
|
164
|
-
},
|
|
165
|
-
plugins: [
|
|
166
|
-
syncJSON({
|
|
167
|
-
// Define the output directory for react-intl message files
|
|
168
|
-
source: ({ key, locale }) => `./intl/messages/${locale}/${key}.json`,
|
|
169
|
-
}),
|
|
170
|
-
],
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
module.exports = config;
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
> Through this configuration file, you can set up locales, output directories, content file patterns, and more. For a complete list of available parameters, refer to the [configuration documentation](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/configuration.md).
|
|
177
|
-
|
|
178
|
-
### Step 3: Integrate Intlayer in Your Build Process
|
|
179
|
-
|
|
180
|
-
Configure your build scripts to run Intlayer when building your application. Add the Intlayer build command to your `package.json`:
|
|
181
|
-
|
|
182
|
-
```json fileName="package.json"
|
|
183
|
-
{
|
|
184
|
-
"scripts": {
|
|
185
|
-
"build": "intlayer build && vite build",
|
|
186
|
-
"dev": "intlayer build && vite dev"
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
> The `intlayer build` command scans your content declaration files, compiles them, and generates the JSON message files that react-intl will consume.
|
|
192
|
-
|
|
193
|
-
### Step 4: Declare Your Content
|
|
194
|
-
|
|
195
|
-
Create and manage your content declarations to store translations. Intlayer scans your codebase (by default, under `./src`) for files matching `*.content.{ts,tsx,js,jsx,mjs,mjx,cjs,cjx,json}`.
|
|
196
|
-
|
|
197
|
-
Here's a **TypeScript** example:
|
|
198
|
-
|
|
199
|
-
```typescript fileName="src/components/MyComponent/index.content.ts" contentDeclarationFormat="typescript"
|
|
200
|
-
import { t, type Dictionary } from "intlayer";
|
|
201
|
-
|
|
202
|
-
const content = {
|
|
203
|
-
// "key" becomes the namespace in your react-intl JSON files
|
|
204
|
-
key: "my-component",
|
|
205
|
-
|
|
206
|
-
content: {
|
|
207
|
-
// Each call to t() declares a translatable field
|
|
208
|
-
helloWorld: t({
|
|
209
|
-
en: "Hello World",
|
|
210
|
-
fr: "Bonjour le monde",
|
|
211
|
-
es: "Hola Mundo",
|
|
212
|
-
}),
|
|
213
|
-
description: t({
|
|
214
|
-
en: "This is a description",
|
|
215
|
-
fr: "Ceci est une description",
|
|
216
|
-
es: "Esta es una descripción",
|
|
217
|
-
}),
|
|
218
|
-
welcomeMessage: t({
|
|
219
|
-
en: "Welcome to our application",
|
|
220
|
-
fr: "Bienvenue dans notre application",
|
|
221
|
-
es: "Bienvenido a nuestra aplicación",
|
|
222
|
-
}),
|
|
223
|
-
},
|
|
224
|
-
} satisfies Dictionary;
|
|
225
|
-
|
|
226
|
-
export default content;
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
```javascript fileName="src/components/MyComponent/index.content.mjs" contentDeclarationFormat="esm"
|
|
230
|
-
import { t } from "intlayer";
|
|
231
|
-
|
|
232
|
-
/** @type {import('intlayer').Dictionary} */
|
|
233
|
-
const content = {
|
|
234
|
-
key: "my-component",
|
|
235
|
-
|
|
236
|
-
content: {
|
|
237
|
-
helloWorld: t({
|
|
238
|
-
en: "Hello World",
|
|
239
|
-
fr: "Bonjour le monde",
|
|
240
|
-
es: "Hola Mundo",
|
|
241
|
-
}),
|
|
242
|
-
description: t({
|
|
243
|
-
en: "This is a description",
|
|
244
|
-
fr: "Ceci est une description",
|
|
245
|
-
es: "Esta es una descripción",
|
|
246
|
-
}),
|
|
247
|
-
welcomeMessage: t({
|
|
248
|
-
en: "Welcome to our application",
|
|
249
|
-
fr: "Bienvenue dans notre application",
|
|
250
|
-
es: "Bienvenido a nuestra aplicación",
|
|
251
|
-
}),
|
|
252
|
-
},
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
export default content;
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
```javascript fileName="src/components/MyComponent/index.content.cjs" contentDeclarationFormat="commonjs"
|
|
259
|
-
const { t } = require("intlayer");
|
|
260
|
-
|
|
261
|
-
/** @type {import('intlayer').Dictionary} */
|
|
262
|
-
const content = {
|
|
263
|
-
key: "my-component",
|
|
264
|
-
|
|
265
|
-
content: {
|
|
266
|
-
helloWorld: t({
|
|
267
|
-
en: "Hello World",
|
|
268
|
-
fr: "Bonjour le monde",
|
|
269
|
-
es: "Hola Mundo",
|
|
270
|
-
}),
|
|
271
|
-
description: t({
|
|
272
|
-
en: "This is a description",
|
|
273
|
-
fr: "Ceci est une description",
|
|
274
|
-
es: "Esta es una descripción",
|
|
275
|
-
}),
|
|
276
|
-
welcomeMessage: t({
|
|
277
|
-
en: "Welcome to our application",
|
|
278
|
-
fr: "Bienvenue dans notre application",
|
|
279
|
-
es: "Bienvenido a nuestra aplicación",
|
|
280
|
-
}),
|
|
281
|
-
},
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
module.exports = content;
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
```json fileName="src/components/MyComponent/index.content.json" contentDeclarationFormat="json"
|
|
288
|
-
{
|
|
289
|
-
"$schema": "https://intlayer.org/schema.json",
|
|
290
|
-
"key": "my-component",
|
|
291
|
-
"content": {
|
|
292
|
-
"helloWorld": {
|
|
293
|
-
"nodeType": "translation",
|
|
294
|
-
"translation": {
|
|
295
|
-
"en": "Hello World",
|
|
296
|
-
"fr": "Bonjour le monde",
|
|
297
|
-
"es": "Hola Mundo"
|
|
298
|
-
}
|
|
299
|
-
},
|
|
300
|
-
"description": {
|
|
301
|
-
"nodeType": "translation",
|
|
302
|
-
"translation": {
|
|
303
|
-
"en": "This is a description",
|
|
304
|
-
"fr": "Ceci est une description",
|
|
305
|
-
"es": "Esta es una descripción"
|
|
306
|
-
}
|
|
307
|
-
},
|
|
308
|
-
"welcomeMessage": {
|
|
309
|
-
"nodeType": "translation",
|
|
310
|
-
"translation": {
|
|
311
|
-
"en": "Welcome to our application",
|
|
312
|
-
"fr": "Bienvenue dans notre application",
|
|
313
|
-
"es": "Bienvenido a nuestra aplicación"
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
```
|
|
319
|
-
|
|
320
|
-
> Your content declarations can be defined anywhere in your application as long as they are included in the `contentDir` directory (by default, `./src`) and match the content declaration file extension pattern.
|
|
321
|
-
|
|
322
|
-
> For more details, refer to the [content declaration documentation](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/dictionary/content_file.md).
|
|
323
|
-
|
|
324
|
-
### Step 5: Build the react-intl Messages
|
|
325
|
-
|
|
326
|
-
To generate the actual message JSON files for **react-intl**, run:
|
|
327
|
-
|
|
328
|
-
```bash packageManager="npm"
|
|
329
|
-
npx intlayer build
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
```bash packageManager="pnpm"
|
|
333
|
-
pnpm intlayer build
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
```bash packageManager="yarn"
|
|
337
|
-
yarn intlayer build
|
|
338
|
-
```
|
|
339
|
-
|
|
340
|
-
This command scans all `*.content.*` files, compiles them, and writes the results to the directory specified in your **`intlayer.config.ts`**—in this example, `./intl/messages`.
|
|
341
|
-
|
|
342
|
-
A typical output structure might look like:
|
|
343
|
-
|
|
344
|
-
```bash
|
|
345
|
-
.
|
|
346
|
-
└── intl
|
|
347
|
-
└── messages
|
|
348
|
-
├── en
|
|
349
|
-
│ └── my-component.json
|
|
350
|
-
├── fr
|
|
351
|
-
│ └── my-component.json
|
|
352
|
-
└── es
|
|
353
|
-
└── my-component.json
|
|
354
|
-
```
|
|
355
|
-
|
|
356
|
-
Each file is a JSON object with keys corresponding to the `content` properties defined in your Intlayer dictionaries. For example, **en/my-component.json** might look like:
|
|
357
|
-
|
|
358
|
-
```json fileName="intl/messages/en/my-component.json"
|
|
359
|
-
{
|
|
360
|
-
"helloWorld": "Hello World",
|
|
361
|
-
"description": "This is a description",
|
|
362
|
-
"welcomeMessage": "Welcome to our application"
|
|
363
|
-
}
|
|
364
|
-
```
|
|
365
|
-
|
|
366
|
-
### Step 6: Initialize react-intl in Your React App
|
|
367
|
-
|
|
368
|
-
#### 6.1. Load the Generated Messages
|
|
369
|
-
|
|
370
|
-
In your application's entry point (e.g., `src/main.tsx` or `src/index.tsx`), you need to:
|
|
371
|
-
|
|
372
|
-
1. **Import** the generated message files (either statically or dynamically).
|
|
373
|
-
2. **Provide** them to `<IntlProvider>` from `react-intl`.
|
|
374
|
-
|
|
375
|
-
Here's an example using **Vite's** `import.meta.glob`:
|
|
376
|
-
|
|
377
|
-
```typescript fileName="src/main.tsx" codeFormat="typescript"
|
|
378
|
-
import React from "react";
|
|
379
|
-
import ReactDOM from "react-dom/client";
|
|
380
|
-
import { IntlProvider } from "react-intl";
|
|
381
|
-
import App from "./App";
|
|
382
|
-
|
|
383
|
-
// Dynamically import all JSON message files
|
|
384
|
-
const messages = import.meta.glob<Record<string, string>>(
|
|
385
|
-
"../intl/messages/**/*.json",
|
|
386
|
-
{
|
|
387
|
-
eager: true,
|
|
388
|
-
}
|
|
389
|
-
);
|
|
390
|
-
|
|
391
|
-
// Structure messages by locale
|
|
392
|
-
const messagesRecord: Record<string, Record<string, string>> = {};
|
|
393
|
-
|
|
394
|
-
Object.entries(messages).forEach(([path, module]) => {
|
|
395
|
-
// Extract locale and namespace from file path
|
|
396
|
-
// Example path: "../intl/messages/en/my-component.json"
|
|
397
|
-
const match = path.match(/messages\/(\w+)\/(.+?)\.json$/);
|
|
398
|
-
if (match) {
|
|
399
|
-
const [, locale, namespace] = match;
|
|
400
|
-
if (!messagesRecord[locale]) {
|
|
401
|
-
messagesRecord[locale] = {};
|
|
402
|
-
}
|
|
403
|
-
// Flatten all messages for the locale
|
|
404
|
-
Object.assign(messagesRecord[locale], module.default || module);
|
|
405
|
-
}
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
// Detect user locale (you can implement more sophisticated logic)
|
|
409
|
-
const userLocale = navigator.language.split("-")[0] || "en";
|
|
410
|
-
const locale = messagesRecord[userLocale] ? userLocale : "en";
|
|
411
|
-
|
|
412
|
-
ReactDOM.createRoot(document.getElementById("root")!).render(
|
|
413
|
-
<React.StrictMode>
|
|
414
|
-
<IntlProvider locale={locale} messages={messagesRecord[locale]}>
|
|
415
|
-
<App />
|
|
416
|
-
</IntlProvider>
|
|
417
|
-
</React.StrictMode>
|
|
418
|
-
);
|
|
419
|
-
```
|
|
420
|
-
|
|
421
|
-
```javascript fileName="src/main.mjs" codeFormat="esm"
|
|
422
|
-
import React from "react";
|
|
423
|
-
import ReactDOM from "react-dom/client";
|
|
424
|
-
import { IntlProvider } from "react-intl";
|
|
425
|
-
import App from "./App";
|
|
426
|
-
|
|
427
|
-
// Dynamically import all JSON message files
|
|
428
|
-
const messages = import.meta.glob("../intl/messages/**/*.json", {
|
|
429
|
-
eager: true,
|
|
430
|
-
});
|
|
431
|
-
|
|
432
|
-
// Structure messages by locale
|
|
433
|
-
const messagesRecord = {};
|
|
434
|
-
|
|
435
|
-
Object.entries(messages).forEach(([path, module]) => {
|
|
436
|
-
const match = path.match(/messages\/(\w+)\/(.+?)\.json$/);
|
|
437
|
-
if (match) {
|
|
438
|
-
const [, locale, namespace] = match;
|
|
439
|
-
if (!messagesRecord[locale]) {
|
|
440
|
-
messagesRecord[locale] = {};
|
|
441
|
-
}
|
|
442
|
-
Object.assign(messagesRecord[locale], module.default || module);
|
|
443
|
-
}
|
|
444
|
-
});
|
|
445
|
-
|
|
446
|
-
// Detect user locale
|
|
447
|
-
const userLocale = navigator.language.split("-")[0] || "en";
|
|
448
|
-
const locale = messagesRecord[userLocale] ? userLocale : "en";
|
|
449
|
-
|
|
450
|
-
ReactDOM.createRoot(document.getElementById("root")).render(
|
|
451
|
-
<React.StrictMode>
|
|
452
|
-
<IntlProvider locale={locale} messages={messagesRecord[locale]}>
|
|
453
|
-
<App />
|
|
454
|
-
</IntlProvider>
|
|
455
|
-
</React.StrictMode>
|
|
456
|
-
);
|
|
457
|
-
```
|
|
458
|
-
|
|
459
|
-
```javascript fileName="src/main.cjs" codeFormat="commonjs"
|
|
460
|
-
const React = require("react");
|
|
461
|
-
const ReactDOM = require("react-dom/client");
|
|
462
|
-
const { IntlProvider } = require("react-intl");
|
|
463
|
-
const App = require("./App");
|
|
464
|
-
|
|
465
|
-
// For CommonJS, you'll need to load messages differently
|
|
466
|
-
// This example assumes you have a way to import JSON files
|
|
467
|
-
const enMessages = require("../intl/messages/en/my-component.json");
|
|
468
|
-
const frMessages = require("../intl/messages/fr/my-component.json");
|
|
469
|
-
const esMessages = require("../intl/messages/es/my-component.json");
|
|
470
|
-
|
|
471
|
-
const messagesRecord = {
|
|
472
|
-
en: enMessages,
|
|
473
|
-
fr: frMessages,
|
|
474
|
-
es: esMessages,
|
|
475
|
-
};
|
|
476
|
-
|
|
477
|
-
// Detect user locale
|
|
478
|
-
const userLocale = navigator.language.split("-")[0] || "en";
|
|
479
|
-
const locale = messagesRecord[userLocale] ? userLocale : "en";
|
|
480
|
-
|
|
481
|
-
ReactDOM.createRoot(document.getElementById("root")).render(
|
|
482
|
-
React.createElement(
|
|
483
|
-
React.StrictMode,
|
|
484
|
-
null,
|
|
485
|
-
React.createElement(
|
|
486
|
-
IntlProvider,
|
|
487
|
-
{ locale: locale, messages: messagesRecord[locale] },
|
|
488
|
-
React.createElement(App)
|
|
489
|
-
)
|
|
490
|
-
)
|
|
491
|
-
);
|
|
492
|
-
```
|
|
493
|
-
|
|
494
|
-
> **Tip**: For production applications, consider:
|
|
495
|
-
>
|
|
496
|
-
> - Implementing proper locale detection based on user preferences, browser settings, or user accounts
|
|
497
|
-
> - Loading messages dynamically to reduce initial bundle size
|
|
498
|
-
> - Using a locale switcher component to allow users to change languages
|
|
499
|
-
|
|
500
|
-
#### 6.2. Create a Locale Context (Optional but Recommended)
|
|
501
|
-
|
|
502
|
-
For better locale management, create a context that allows components to access and change the current locale:
|
|
503
|
-
|
|
504
|
-
```typescript fileName="src/context/LocaleContext.tsx" codeFormat="typescript"
|
|
505
|
-
import React, {
|
|
506
|
-
createContext,
|
|
507
|
-
useContext,
|
|
508
|
-
useState,
|
|
509
|
-
ReactNode,
|
|
510
|
-
useEffect,
|
|
511
|
-
} from "react";
|
|
512
|
-
import { IntlProvider } from "react-intl";
|
|
513
|
-
|
|
514
|
-
interface LocaleContextType {
|
|
515
|
-
locale: string;
|
|
516
|
-
setLocale: (locale: string) => void;
|
|
517
|
-
availableLocales: string[];
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
const LocaleContext = createContext<LocaleContextType | undefined>(undefined);
|
|
521
|
-
|
|
522
|
-
// Load messages
|
|
523
|
-
const messages = import.meta.glob<Record<string, string>>(
|
|
524
|
-
"../../intl/messages/**/*.json",
|
|
525
|
-
{
|
|
526
|
-
eager: true,
|
|
527
|
-
}
|
|
528
|
-
);
|
|
529
|
-
|
|
530
|
-
const messagesRecord: Record<string, Record<string, string>> = {};
|
|
531
|
-
|
|
532
|
-
Object.entries(messages).forEach(([path, module]) => {
|
|
533
|
-
const match = path.match(/messages\/(\w+)\/(.+?)\.json$/);
|
|
534
|
-
if (match) {
|
|
535
|
-
const [, locale] = match;
|
|
536
|
-
if (!messagesRecord[locale]) {
|
|
537
|
-
messagesRecord[locale] = {};
|
|
538
|
-
}
|
|
539
|
-
Object.assign(messagesRecord[locale], module.default || module);
|
|
540
|
-
}
|
|
541
|
-
});
|
|
542
|
-
|
|
543
|
-
const availableLocales = Object.keys(messagesRecord);
|
|
544
|
-
|
|
545
|
-
export const LocaleProvider: React.FC<{ children: ReactNode }> = ({
|
|
546
|
-
children,
|
|
547
|
-
}) => {
|
|
548
|
-
const [locale, setLocaleState] = useState<string>(() => {
|
|
549
|
-
// Get locale from localStorage or browser
|
|
550
|
-
const saved = localStorage.getItem("locale");
|
|
551
|
-
if (saved && availableLocales.includes(saved)) {
|
|
552
|
-
return saved;
|
|
553
|
-
}
|
|
554
|
-
const browserLocale = navigator.language.split("-")[0];
|
|
555
|
-
return availableLocales.includes(browserLocale) ? browserLocale : "en";
|
|
556
|
-
});
|
|
557
|
-
|
|
558
|
-
const setLocale = (newLocale: string) => {
|
|
559
|
-
if (availableLocales.includes(newLocale)) {
|
|
560
|
-
setLocaleState(newLocale);
|
|
561
|
-
localStorage.setItem("locale", newLocale);
|
|
562
|
-
}
|
|
563
|
-
};
|
|
564
|
-
|
|
565
|
-
return (
|
|
566
|
-
<LocaleContext.Provider value={{ locale, setLocale, availableLocales }}>
|
|
567
|
-
<IntlProvider locale={locale} messages={messagesRecord[locale]}>
|
|
568
|
-
{children}
|
|
569
|
-
</IntlProvider>
|
|
570
|
-
</LocaleContext.Provider>
|
|
571
|
-
);
|
|
572
|
-
};
|
|
573
|
-
|
|
574
|
-
export const useLocale = () => {
|
|
575
|
-
const context = useContext(LocaleContext);
|
|
576
|
-
if (!context) {
|
|
577
|
-
throw new Error("useLocale must be used within LocaleProvider");
|
|
578
|
-
}
|
|
579
|
-
return context;
|
|
580
|
-
};
|
|
581
|
-
```
|
|
582
|
-
|
|
583
|
-
```javascript fileName="src/context/LocaleContext.mjs" codeFormat="esm"
|
|
584
|
-
import React, { createContext, useContext, useState } from "react";
|
|
585
|
-
import { IntlProvider } from "react-intl";
|
|
586
|
-
|
|
587
|
-
const LocaleContext = createContext(undefined);
|
|
588
|
-
|
|
589
|
-
// Load messages
|
|
590
|
-
const messages = import.meta.glob("../../intl/messages/**/*.json", {
|
|
591
|
-
eager: true,
|
|
592
|
-
});
|
|
593
|
-
|
|
594
|
-
const messagesRecord = {};
|
|
595
|
-
|
|
596
|
-
Object.entries(messages).forEach(([path, module]) => {
|
|
597
|
-
const match = path.match(/messages\/(\w+)\/(.+?)\.json$/);
|
|
598
|
-
if (match) {
|
|
599
|
-
const [, locale] = match;
|
|
600
|
-
if (!messagesRecord[locale]) {
|
|
601
|
-
messagesRecord[locale] = {};
|
|
602
|
-
}
|
|
603
|
-
Object.assign(messagesRecord[locale], module.default || module);
|
|
604
|
-
}
|
|
605
|
-
});
|
|
606
|
-
|
|
607
|
-
const availableLocales = Object.keys(messagesRecord);
|
|
608
|
-
|
|
609
|
-
export const LocaleProvider = ({ children }) => {
|
|
610
|
-
const [locale, setLocaleState] = useState(() => {
|
|
611
|
-
const saved = localStorage.getItem("locale");
|
|
612
|
-
if (saved && availableLocales.includes(saved)) {
|
|
613
|
-
return saved;
|
|
614
|
-
}
|
|
615
|
-
const browserLocale = navigator.language.split("-")[0];
|
|
616
|
-
return availableLocales.includes(browserLocale) ? browserLocale : "en";
|
|
617
|
-
});
|
|
618
|
-
|
|
619
|
-
const setLocale = (newLocale) => {
|
|
620
|
-
if (availableLocales.includes(newLocale)) {
|
|
621
|
-
setLocaleState(newLocale);
|
|
622
|
-
localStorage.setItem("locale", newLocale);
|
|
623
|
-
}
|
|
624
|
-
};
|
|
625
|
-
|
|
626
|
-
return (
|
|
627
|
-
<LocaleContext.Provider value={{ locale, setLocale, availableLocales }}>
|
|
628
|
-
<IntlProvider locale={locale} messages={messagesRecord[locale]}>
|
|
629
|
-
{children}
|
|
630
|
-
</IntlProvider>
|
|
631
|
-
</LocaleContext.Provider>
|
|
632
|
-
);
|
|
633
|
-
};
|
|
634
|
-
|
|
635
|
-
export const useLocale = () => {
|
|
636
|
-
const context = useContext(LocaleContext);
|
|
637
|
-
if (!context) {
|
|
638
|
-
throw new Error("useLocale must be used within LocaleProvider");
|
|
639
|
-
}
|
|
640
|
-
return context;
|
|
641
|
-
};
|
|
642
|
-
```
|
|
643
|
-
|
|
644
|
-
Now update your main entry file to use the `LocaleProvider`:
|
|
645
|
-
|
|
646
|
-
```typescript fileName="src/main.tsx" codeFormat="typescript"
|
|
647
|
-
import React from "react";
|
|
648
|
-
import ReactDOM from "react-dom/client";
|
|
649
|
-
import { LocaleProvider } from "./context/LocaleContext";
|
|
650
|
-
import App from "./App";
|
|
651
|
-
|
|
652
|
-
ReactDOM.createRoot(document.getElementById("root")!).render(
|
|
653
|
-
<React.StrictMode>
|
|
654
|
-
<LocaleProvider>
|
|
655
|
-
<App />
|
|
656
|
-
</LocaleProvider>
|
|
657
|
-
</React.StrictMode>
|
|
658
|
-
);
|
|
659
|
-
```
|
|
660
|
-
|
|
661
|
-
### Step 7: Utilize Content in Your Code
|
|
662
|
-
|
|
663
|
-
Access your translations throughout your application using react-intl's components and hooks.
|
|
664
|
-
|
|
665
|
-
#### Approach A: Using `<FormattedMessage>`
|
|
666
|
-
|
|
667
|
-
For quick inline usage:
|
|
668
|
-
|
|
669
|
-
```tsx fileName="src/components/MyComponent/index.tsx" codeFormat="typescript"
|
|
670
|
-
import React from "react";
|
|
671
|
-
import { FormattedMessage } from "react-intl";
|
|
672
|
-
|
|
673
|
-
const MyComponent: React.FC = () => {
|
|
674
|
-
return (
|
|
675
|
-
<div>
|
|
676
|
-
<h1>
|
|
677
|
-
<FormattedMessage id="my-component.helloWorld" />
|
|
678
|
-
</h1>
|
|
679
|
-
<p>
|
|
680
|
-
<FormattedMessage id="my-component.description" />
|
|
681
|
-
</p>
|
|
682
|
-
<p>
|
|
683
|
-
<FormattedMessage id="my-component.welcomeMessage" />
|
|
684
|
-
</p>
|
|
685
|
-
</div>
|
|
686
|
-
);
|
|
687
|
-
};
|
|
688
|
-
|
|
689
|
-
export default MyComponent;
|
|
690
|
-
```
|
|
691
|
-
|
|
692
|
-
```jsx fileName="src/components/MyComponent/index.mjx" codeFormat="esm"
|
|
693
|
-
import React from "react";
|
|
694
|
-
import { FormattedMessage } from "react-intl";
|
|
695
|
-
|
|
696
|
-
const MyComponent = () => {
|
|
697
|
-
return (
|
|
698
|
-
<div>
|
|
699
|
-
<h1>
|
|
700
|
-
<FormattedMessage id="my-component.helloWorld" />
|
|
701
|
-
</h1>
|
|
702
|
-
<p>
|
|
703
|
-
<FormattedMessage id="my-component.description" />
|
|
704
|
-
</p>
|
|
705
|
-
<p>
|
|
706
|
-
<FormattedMessage id="my-component.welcomeMessage" />
|
|
707
|
-
</p>
|
|
708
|
-
</div>
|
|
709
|
-
);
|
|
710
|
-
};
|
|
711
|
-
|
|
712
|
-
export default MyComponent;
|
|
713
|
-
```
|
|
714
|
-
|
|
715
|
-
```jsx fileName="src/components/MyComponent/index.csx" codeFormat="commonjs"
|
|
716
|
-
const React = require("react");
|
|
717
|
-
const { FormattedMessage } = require("react-intl");
|
|
718
|
-
|
|
719
|
-
const MyComponent = () => {
|
|
720
|
-
return (
|
|
721
|
-
<div>
|
|
722
|
-
<h1>
|
|
723
|
-
<FormattedMessage id="my-component.helloWorld" />
|
|
724
|
-
</h1>
|
|
725
|
-
<p>
|
|
726
|
-
<FormattedMessage id="my-component.description" />
|
|
727
|
-
</p>
|
|
728
|
-
<p>
|
|
729
|
-
<FormattedMessage id="my-component.welcomeMessage" />
|
|
730
|
-
</p>
|
|
731
|
-
</div>
|
|
732
|
-
);
|
|
733
|
-
};
|
|
734
|
-
|
|
735
|
-
module.exports = MyComponent;
|
|
736
|
-
```
|
|
737
|
-
|
|
738
|
-
> The **`id`** prop in `<FormattedMessage>` must match the flattened key structure: `namespace.key` (e.g., `my-component.helloWorld`).
|
|
739
|
-
|
|
740
|
-
#### Approach B: Using `useIntl()`
|
|
741
|
-
|
|
742
|
-
For more dynamic usage and to access translations in JavaScript logic:
|
|
743
|
-
|
|
744
|
-
```tsx fileName="src/components/MyComponent/MyComponent.tsx" codeFormat="typescript"
|
|
745
|
-
import React from "react";
|
|
746
|
-
import { useIntl } from "react-intl";
|
|
747
|
-
|
|
748
|
-
const MyComponent: React.FC = () => {
|
|
749
|
-
const intl = useIntl();
|
|
750
|
-
|
|
751
|
-
return (
|
|
752
|
-
<div>
|
|
753
|
-
<h1>{intl.formatMessage({ id: "my-component.helloWorld" })}</h1>
|
|
754
|
-
<p>{intl.formatMessage({ id: "my-component.description" })}</p>
|
|
755
|
-
<button
|
|
756
|
-
aria-label={intl.formatMessage({ id: "my-component.welcomeMessage" })}
|
|
757
|
-
>
|
|
758
|
-
{intl.formatMessage({ id: "my-component.welcomeMessage" })}
|
|
759
|
-
</button>
|
|
760
|
-
</div>
|
|
761
|
-
);
|
|
762
|
-
};
|
|
763
|
-
|
|
764
|
-
export default MyComponent;
|
|
765
|
-
```
|
|
766
|
-
|
|
767
|
-
```jsx fileName="src/components/MyComponent/MyComponent.mjx" codeFormat="esm"
|
|
768
|
-
import React from "react";
|
|
769
|
-
import { useIntl } from "react-intl";
|
|
770
|
-
|
|
771
|
-
const MyComponent = () => {
|
|
772
|
-
const intl = useIntl();
|
|
773
|
-
|
|
774
|
-
return (
|
|
775
|
-
<div>
|
|
776
|
-
<h1>{intl.formatMessage({ id: "my-component.helloWorld" })}</h1>
|
|
777
|
-
<p>{intl.formatMessage({ id: "my-component.description" })}</p>
|
|
778
|
-
<button
|
|
779
|
-
aria-label={intl.formatMessage({ id: "my-component.welcomeMessage" })}
|
|
780
|
-
>
|
|
781
|
-
{intl.formatMessage({ id: "my-component.welcomeMessage" })}
|
|
782
|
-
</button>
|
|
783
|
-
</div>
|
|
784
|
-
);
|
|
785
|
-
};
|
|
786
|
-
|
|
787
|
-
export default MyComponent;
|
|
788
|
-
```
|
|
789
|
-
|
|
790
|
-
```jsx fileName="src/components/MyComponent/MyComponent.csx" codeFormat="commonjs"
|
|
791
|
-
const React = require("react");
|
|
792
|
-
const { useIntl } = require("react-intl");
|
|
793
|
-
|
|
794
|
-
const MyComponent = () => {
|
|
795
|
-
const intl = useIntl();
|
|
796
|
-
|
|
797
|
-
return (
|
|
798
|
-
<div>
|
|
799
|
-
<h1>{intl.formatMessage({ id: "my-component.helloWorld" })}</h1>
|
|
800
|
-
<p>{intl.formatMessage({ id: "my-component.description" })}</p>
|
|
801
|
-
<button
|
|
802
|
-
aria-label={intl.formatMessage({ id: "my-component.welcomeMessage" })}
|
|
803
|
-
>
|
|
804
|
-
{intl.formatMessage({ id: "my-component.welcomeMessage" })}
|
|
805
|
-
</button>
|
|
806
|
-
</div>
|
|
807
|
-
);
|
|
808
|
-
};
|
|
809
|
-
|
|
810
|
-
module.exports = MyComponent;
|
|
811
|
-
```
|
|
812
|
-
|
|
813
|
-
### (Optional) Step 8: Change the Language of Your Content
|
|
814
|
-
|
|
815
|
-
To allow users to switch languages, create a locale switcher component:
|
|
816
|
-
|
|
817
|
-
```tsx fileName="src/components/LocaleSwitcher.tsx" codeFormat="typescript"
|
|
818
|
-
import React from "react";
|
|
819
|
-
import { useLocale } from "../context/LocaleContext";
|
|
820
|
-
|
|
821
|
-
const localeNames: Record<string, string> = {
|
|
822
|
-
en: "English",
|
|
823
|
-
fr: "Français",
|
|
824
|
-
es: "Español",
|
|
825
|
-
};
|
|
826
|
-
|
|
827
|
-
const LocaleSwitcher: React.FC = () => {
|
|
828
|
-
const { locale, setLocale, availableLocales } = useLocale();
|
|
829
|
-
|
|
830
|
-
return (
|
|
831
|
-
<div>
|
|
832
|
-
<label htmlFor="locale-select">Choose language: </label>
|
|
833
|
-
<select
|
|
834
|
-
id="locale-select"
|
|
835
|
-
value={locale}
|
|
836
|
-
onChange={(e) => setLocale(e.target.value)}
|
|
837
|
-
>
|
|
838
|
-
{availableLocales.map((loc) => (
|
|
839
|
-
<option key={loc} value={loc}>
|
|
840
|
-
{localeNames[loc] || loc}
|
|
841
|
-
</option>
|
|
842
|
-
))}
|
|
843
|
-
</select>
|
|
844
|
-
</div>
|
|
845
|
-
);
|
|
846
|
-
};
|
|
847
|
-
|
|
848
|
-
export default LocaleSwitcher;
|
|
849
|
-
```
|
|
850
|
-
|
|
851
|
-
```jsx fileName="src/components/LocaleSwitcher.mjx" codeFormat="esm"
|
|
852
|
-
import React from "react";
|
|
853
|
-
import { useLocale } from "../context/LocaleContext";
|
|
854
|
-
|
|
855
|
-
const localeNames = {
|
|
856
|
-
en: "English",
|
|
857
|
-
fr: "Français",
|
|
858
|
-
es: "Español",
|
|
859
|
-
};
|
|
860
|
-
|
|
861
|
-
const LocaleSwitcher = () => {
|
|
862
|
-
const { locale, setLocale, availableLocales } = useLocale();
|
|
863
|
-
|
|
864
|
-
return (
|
|
865
|
-
<div>
|
|
866
|
-
<label htmlFor="locale-select">Choose language: </label>
|
|
867
|
-
<select
|
|
868
|
-
id="locale-select"
|
|
869
|
-
value={locale}
|
|
870
|
-
onChange={(e) => setLocale(e.target.value)}
|
|
871
|
-
>
|
|
872
|
-
{availableLocales.map((loc) => (
|
|
873
|
-
<option key={loc} value={loc}>
|
|
874
|
-
{localeNames[loc] || loc}
|
|
875
|
-
</option>
|
|
876
|
-
))}
|
|
877
|
-
</select>
|
|
878
|
-
</div>
|
|
879
|
-
);
|
|
880
|
-
};
|
|
881
|
-
|
|
882
|
-
export default LocaleSwitcher;
|
|
883
|
-
```
|
|
884
|
-
|
|
885
|
-
```jsx fileName="src/components/LocaleSwitcher.csx" codeFormat="commonjs"
|
|
886
|
-
const React = require("react");
|
|
887
|
-
const { useLocale } = require("../context/LocaleContext");
|
|
96
|
+
The `syncJSON` plugin will automatically wrap the JSON. It will read and write the JSON files without changing the content architecture.
|
|
888
97
|
|
|
889
|
-
|
|
890
|
-
en: "English",
|
|
891
|
-
fr: "Français",
|
|
892
|
-
es: "Español",
|
|
893
|
-
};
|
|
98
|
+
If you want to make coexist that JSON with intlayer content declaration files (`.content` files), Intlayer will proceed this way:
|
|
894
99
|
|
|
895
|
-
|
|
896
|
-
|
|
100
|
+
1. load both JSON and content declaration files and transform them into a intlayer dictionary.
|
|
101
|
+
2. if there is conflicts between the JSON and the content declaration files, Intlayer will process to the merge of that all dictionaries. Depending of the priority of the plugins, and the one of the content declaration file (all are configurable).
|
|
897
102
|
|
|
898
|
-
|
|
899
|
-
"div",
|
|
900
|
-
null,
|
|
901
|
-
React.createElement(
|
|
902
|
-
"label",
|
|
903
|
-
{ htmlFor: "locale-select" },
|
|
904
|
-
"Choose language: "
|
|
905
|
-
),
|
|
906
|
-
React.createElement(
|
|
907
|
-
"select",
|
|
908
|
-
{
|
|
909
|
-
id: "locale-select",
|
|
910
|
-
value: locale,
|
|
911
|
-
onChange: (e) => setLocale(e.target.value),
|
|
912
|
-
},
|
|
913
|
-
availableLocales.map((loc) =>
|
|
914
|
-
React.createElement(
|
|
915
|
-
"option",
|
|
916
|
-
{ key: loc, value: loc },
|
|
917
|
-
localeNames[loc] || loc
|
|
918
|
-
)
|
|
919
|
-
)
|
|
920
|
-
)
|
|
921
|
-
);
|
|
922
|
-
};
|
|
103
|
+
If changes are made using the CLI to translate the JSON, or using the CMS, Intlayer will update the JSON file with the new translations.
|
|
923
104
|
|
|
924
|
-
|
|
925
|
-
```
|
|
926
|
-
|
|
927
|
-
Then use the `LocaleSwitcher` component in your app:
|
|
105
|
+
To see more details about the `syncJSON` plugin, please refer to the [syncJSON plugin documentation](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/plugins/sync-json.md).
|
|
928
106
|
|
|
929
|
-
|
|
930
|
-
import React from "react";
|
|
931
|
-
import MyComponent from "./components/MyComponent";
|
|
932
|
-
import LocaleSwitcher from "./components/LocaleSwitcher";
|
|
933
|
-
|
|
934
|
-
const App: React.FC = () => {
|
|
935
|
-
return (
|
|
936
|
-
<div>
|
|
937
|
-
<LocaleSwitcher />
|
|
938
|
-
<MyComponent />
|
|
939
|
-
</div>
|
|
940
|
-
);
|
|
941
|
-
};
|
|
942
|
-
|
|
943
|
-
export default App;
|
|
944
|
-
```
|
|
945
|
-
|
|
946
|
-
### (Optional) Step 9: Advanced Message Formatting
|
|
947
|
-
|
|
948
|
-
react-intl supports advanced formatting features like pluralization, date/time formatting, and number formatting. Here are some examples:
|
|
949
|
-
|
|
950
|
-
#### Pluralization
|
|
951
|
-
|
|
952
|
-
```typescript fileName="src/components/ItemCount.content.ts" codeFormat="typescript"
|
|
953
|
-
import { t, type Dictionary } from "intlayer";
|
|
954
|
-
|
|
955
|
-
const content = {
|
|
956
|
-
key: "item-count",
|
|
957
|
-
content: {
|
|
958
|
-
items: t({
|
|
959
|
-
en: "{count, plural, =0 {No items} one {One item} other {# items}}",
|
|
960
|
-
fr: "{count, plural, =0 {Aucun article} one {Un article} other {# articles}}",
|
|
961
|
-
es: "{count, plural, =0 {Sin artículos} one {Un artículo} other {# artículos}}",
|
|
962
|
-
}),
|
|
963
|
-
},
|
|
964
|
-
} satisfies Dictionary;
|
|
965
|
-
|
|
966
|
-
export default content;
|
|
967
|
-
```
|
|
968
|
-
|
|
969
|
-
```tsx fileName="src/components/ItemCount.tsx" codeFormat="typescript"
|
|
970
|
-
import React from "react";
|
|
971
|
-
import { FormattedMessage } from "react-intl";
|
|
972
|
-
|
|
973
|
-
interface ItemCountProps {
|
|
974
|
-
count: number;
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
const ItemCount: React.FC<ItemCountProps> = ({ count }) => {
|
|
978
|
-
return (
|
|
979
|
-
<p>
|
|
980
|
-
<FormattedMessage id="item-count.items" values={{ count }} />
|
|
981
|
-
</p>
|
|
982
|
-
);
|
|
983
|
-
};
|
|
984
|
-
|
|
985
|
-
export default ItemCount;
|
|
986
|
-
```
|
|
107
|
+
## Git Configuration
|
|
987
108
|
|
|
988
|
-
|
|
109
|
+
It's recommended to ignore auto-generated Intlayer files:
|
|
989
110
|
|
|
990
|
-
```
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
const FormattedData: React.FC = () => {
|
|
995
|
-
const today = new Date();
|
|
996
|
-
const price = 1234.56;
|
|
997
|
-
|
|
998
|
-
return (
|
|
999
|
-
<div>
|
|
1000
|
-
<p>
|
|
1001
|
-
Date: <FormattedDate value={today} />
|
|
1002
|
-
</p>
|
|
1003
|
-
<p>
|
|
1004
|
-
Time: <FormattedTime value={today} />
|
|
1005
|
-
</p>
|
|
1006
|
-
<p>
|
|
1007
|
-
Price: <FormattedNumber value={price} style="currency" currency="USD" />
|
|
1008
|
-
</p>
|
|
1009
|
-
</div>
|
|
1010
|
-
);
|
|
1011
|
-
};
|
|
1012
|
-
|
|
1013
|
-
export default FormattedData;
|
|
1014
|
-
```
|
|
1015
|
-
|
|
1016
|
-
### (Optional) Step 10: Handle Missing Translations
|
|
1017
|
-
|
|
1018
|
-
By default, react-intl will render the message ID if a translation is missing. You can customize this behavior:
|
|
1019
|
-
|
|
1020
|
-
```typescript fileName="src/main.tsx" codeFormat="typescript"
|
|
1021
|
-
import React from "react";
|
|
1022
|
-
import ReactDOM from "react-dom/client";
|
|
1023
|
-
import { IntlProvider } from "react-intl";
|
|
1024
|
-
import App from "./App";
|
|
1025
|
-
|
|
1026
|
-
// Custom handler for missing translations
|
|
1027
|
-
const onError = (err: any) => {
|
|
1028
|
-
if (err.code === "MISSING_TRANSLATION") {
|
|
1029
|
-
console.warn("Missing translation", err.message);
|
|
1030
|
-
return;
|
|
1031
|
-
}
|
|
1032
|
-
throw err;
|
|
1033
|
-
};
|
|
1034
|
-
|
|
1035
|
-
ReactDOM.createRoot(document.getElementById("root")!).render(
|
|
1036
|
-
<React.StrictMode>
|
|
1037
|
-
<IntlProvider locale="en" messages={{}} onError={onError}>
|
|
1038
|
-
<App />
|
|
1039
|
-
</IntlProvider>
|
|
1040
|
-
</React.StrictMode>
|
|
1041
|
-
);
|
|
1042
|
-
```
|
|
1043
|
-
|
|
1044
|
-
### (Optional) Step 11: TypeScript Integration
|
|
1045
|
-
|
|
1046
|
-
Intlayer can generate TypeScript type definitions for your translations, providing compile-time type safety.
|
|
1047
|
-
|
|
1048
|
-
Ensure your `tsconfig.json` includes the generated types:
|
|
1049
|
-
|
|
1050
|
-
```json5 fileName="tsconfig.json"
|
|
1051
|
-
{
|
|
1052
|
-
"compilerOptions": {
|
|
1053
|
-
// ... your other compiler options
|
|
1054
|
-
},
|
|
1055
|
-
"include": [
|
|
1056
|
-
"src",
|
|
1057
|
-
".intlayer/**/*.ts", // Include Intlayer generated types
|
|
1058
|
-
],
|
|
1059
|
-
}
|
|
1060
|
-
```
|
|
1061
|
-
|
|
1062
|
-
This enables autocompletion and compile-time checks for translation keys in your IDE.
|
|
1063
|
-
|
|
1064
|
-
### Configure TypeScript
|
|
1065
|
-
|
|
1066
|
-
Intlayer uses module augmentation to get benefits of TypeScript and make your codebase stronger.
|
|
1067
|
-
|
|
1068
|
-

|
|
1069
|
-
|
|
1070
|
-

|
|
1071
|
-
|
|
1072
|
-
Ensure your TypeScript configuration includes the autogenerated types.
|
|
1073
|
-
|
|
1074
|
-
```json5 fileName="tsconfig.json"
|
|
1075
|
-
{
|
|
1076
|
-
// ... Your existing TypeScript configurations
|
|
1077
|
-
"include": [
|
|
1078
|
-
// ... Your existing TypeScript configurations
|
|
1079
|
-
".intlayer/**/*.ts", // Include the auto-generated types
|
|
1080
|
-
],
|
|
1081
|
-
}
|
|
111
|
+
```plaintext fileName=".gitignore"
|
|
112
|
+
# Ignore files generated by Intlayer
|
|
113
|
+
.intlayer
|
|
1082
114
|
```
|
|
1083
115
|
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
It's recommended to ignore the files generated by Intlayer. This allows you to avoid committing them to your Git repository.
|
|
116
|
+
These files can be regenerated during your build process and don't need to be committed to version control.
|
|
1087
117
|
|
|
1088
|
-
|
|
118
|
+
### VS Code Extension
|
|
1089
119
|
|
|
1090
|
-
|
|
1091
|
-
# Ignore the files generated by Intlayer
|
|
1092
|
-
.intlayer
|
|
1093
|
-
|
|
1094
|
-
# Optionally ignore generated message files if they're rebuilt in CI/CD
|
|
1095
|
-
intl
|
|
1096
|
-
```
|
|
120
|
+
For improved developer experience, install the official **Intlayer VS Code Extension**:
|
|
1097
121
|
|
|
1098
|
-
|
|
122
|
+
[Install from the VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=intlayer.intlayer-vs-code-extension)
|