@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.
Files changed (107) hide show
  1. package/blog/ar/intlayer_with_i18next.md +68 -106
  2. package/blog/ar/intlayer_with_next-i18next.md +84 -288
  3. package/blog/ar/intlayer_with_next-intl.md +58 -337
  4. package/blog/ar/intlayer_with_react-i18next.md +68 -290
  5. package/blog/ar/intlayer_with_react-intl.md +63 -266
  6. package/blog/de/intlayer_with_i18next.md +77 -97
  7. package/blog/de/intlayer_with_next-i18next.md +69 -296
  8. package/blog/de/intlayer_with_next-intl.md +59 -340
  9. package/blog/de/intlayer_with_react-i18next.md +68 -290
  10. package/blog/de/intlayer_with_react-intl.md +62 -264
  11. package/blog/en/intlayer_with_i18next.md +36 -1638
  12. package/blog/en/intlayer_with_next-i18next.md +22 -847
  13. package/blog/en/intlayer_with_next-intl.md +32 -1053
  14. package/blog/en/intlayer_with_react-i18next.md +38 -764
  15. package/blog/en/intlayer_with_react-intl.md +42 -1018
  16. package/blog/en-GB/intlayer_with_i18next.md +67 -103
  17. package/blog/en-GB/intlayer_with_next-i18next.md +71 -292
  18. package/blog/en-GB/intlayer_with_next-intl.md +58 -337
  19. package/blog/en-GB/intlayer_with_react-i18next.md +67 -289
  20. package/blog/en-GB/intlayer_with_react-intl.md +61 -264
  21. package/blog/es/intlayer_with_i18next.md +67 -103
  22. package/blog/es/intlayer_with_next-i18next.md +71 -296
  23. package/blog/es/intlayer_with_next-intl.md +57 -338
  24. package/blog/es/intlayer_with_react-i18next.md +68 -290
  25. package/blog/es/intlayer_with_react-intl.md +62 -265
  26. package/blog/fr/intlayer_with_i18next.md +66 -104
  27. package/blog/fr/intlayer_with_next-i18next.md +82 -285
  28. package/blog/fr/intlayer_with_next-intl.md +57 -338
  29. package/blog/fr/intlayer_with_react-i18next.md +67 -289
  30. package/blog/fr/intlayer_with_react-intl.md +61 -264
  31. package/blog/hi/intlayer_with_i18next.md +68 -104
  32. package/blog/hi/intlayer_with_next-i18next.md +74 -299
  33. package/blog/hi/intlayer_with_next-intl.md +57 -239
  34. package/blog/hi/intlayer_with_react-i18next.md +69 -291
  35. package/blog/hi/intlayer_with_react-intl.md +65 -268
  36. package/blog/id/intlayer_with_i18next.md +126 -0
  37. package/blog/id/intlayer_with_next-i18next.md +142 -0
  38. package/blog/id/intlayer_with_next-intl.md +113 -0
  39. package/blog/id/intlayer_with_react-i18next.md +124 -0
  40. package/blog/id/intlayer_with_react-intl.md +122 -0
  41. package/blog/it/intlayer_with_i18next.md +67 -103
  42. package/blog/it/intlayer_with_next-i18next.md +71 -296
  43. package/blog/it/intlayer_with_next-intl.md +57 -338
  44. package/blog/it/intlayer_with_react-i18next.md +68 -290
  45. package/blog/it/intlayer_with_react-intl.md +62 -265
  46. package/blog/ja/intlayer_with_i18next.md +68 -103
  47. package/blog/ja/intlayer_with_next-i18next.md +85 -283
  48. package/blog/ja/intlayer_with_next-intl.md +58 -336
  49. package/blog/ja/intlayer_with_react-i18next.md +68 -290
  50. package/blog/ja/intlayer_with_react-intl.md +62 -264
  51. package/blog/ko/intlayer_with_i18next.md +80 -96
  52. package/blog/ko/intlayer_with_next-i18next.md +85 -287
  53. package/blog/ko/intlayer_with_next-intl.md +68 -327
  54. package/blog/ko/intlayer_with_react-i18next.md +68 -290
  55. package/blog/ko/intlayer_with_react-intl.md +64 -266
  56. package/blog/pl/intlayer_with_i18next.md +126 -0
  57. package/blog/pl/intlayer_with_next-i18next.md +142 -0
  58. package/blog/pl/intlayer_with_next-intl.md +111 -0
  59. package/blog/pl/intlayer_with_react-i18next.md +124 -0
  60. package/blog/pl/intlayer_with_react-intl.md +122 -0
  61. package/blog/pt/intlayer_with_i18next.md +67 -103
  62. package/blog/pt/intlayer_with_next-i18next.md +72 -293
  63. package/blog/pt/intlayer_with_next-intl.md +57 -256
  64. package/blog/pt/intlayer_with_react-i18next.md +104 -78
  65. package/blog/pt/intlayer_with_react-intl.md +62 -266
  66. package/blog/ru/intlayer_with_i18next.md +66 -104
  67. package/blog/ru/intlayer_with_next-i18next.md +71 -296
  68. package/blog/ru/intlayer_with_next-intl.md +58 -337
  69. package/blog/ru/intlayer_with_react-i18next.md +68 -290
  70. package/blog/ru/intlayer_with_react-intl.md +62 -265
  71. package/blog/tr/intlayer_with_i18next.md +71 -107
  72. package/blog/tr/intlayer_with_next-i18next.md +72 -297
  73. package/blog/tr/intlayer_with_next-intl.md +58 -339
  74. package/blog/tr/intlayer_with_react-i18next.md +69 -291
  75. package/blog/tr/intlayer_with_react-intl.md +63 -285
  76. package/blog/vi/intlayer_with_i18next.md +126 -0
  77. package/blog/vi/intlayer_with_next-i18next.md +142 -0
  78. package/blog/vi/intlayer_with_next-intl.md +111 -0
  79. package/blog/vi/intlayer_with_react-i18next.md +124 -0
  80. package/blog/vi/intlayer_with_react-intl.md +122 -0
  81. package/blog/zh/intlayer_with_i18next.md +67 -102
  82. package/blog/zh/intlayer_with_next-i18next.md +72 -296
  83. package/blog/zh/intlayer_with_next-intl.md +58 -336
  84. package/blog/zh/intlayer_with_react-i18next.md +68 -290
  85. package/blog/zh/intlayer_with_react-intl.md +63 -106
  86. package/docs/ar/plugins/sync-json.md +244 -0
  87. package/docs/de/plugins/sync-json.md +244 -0
  88. package/docs/en/intlayer_cli.md +25 -0
  89. package/docs/en/intlayer_with_nextjs_14.md +2 -0
  90. package/docs/en/intlayer_with_nextjs_15.md +2 -0
  91. package/docs/en/intlayer_with_nextjs_16.md +2 -0
  92. package/docs/en/plugins/sync-json.md +1 -1
  93. package/docs/en-GB/plugins/sync-json.md +244 -0
  94. package/docs/es/plugins/sync-json.md +244 -0
  95. package/docs/fr/plugins/sync-json.md +244 -0
  96. package/docs/hi/plugins/sync-json.md +244 -0
  97. package/docs/id/plugins/sync-json.md +244 -0
  98. package/docs/it/plugins/sync-json.md +244 -0
  99. package/docs/ja/plugins/sync-json.md +244 -0
  100. package/docs/ko/plugins/sync-json.md +244 -0
  101. package/docs/pl/plugins/sync-json.md +244 -0
  102. package/docs/pt/plugins/sync-json.md +244 -0
  103. package/docs/ru/plugins/sync-json.md +244 -0
  104. package/docs/tr/plugins/sync-json.md +245 -0
  105. package/docs/vi/plugins/sync-json.md +244 -0
  106. package/docs/zh/plugins/sync-json.md +244 -0
  107. 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 Integrate Intlayer with react-intl Complete i18n Guide
5
- description: Learn how to integrate Intlayer with react-intl for a React app. Comprehensive guide with code examples for managing translations efficiently.
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
- # React Internationalization (i18n) with **react-intl** and Intlayer
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 (i18n) library designed to simplify multilingual support in modern web applications. Intlayer seamlessly integrates with popular React frameworks, including **react-intl**.
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
- ## Intlayer vs. react-intl: Key Differences
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
- For a deeper analysis of how Intlayer compares to other i18n libraries for React (such as react-intl), check out the [react-i18next vs. react-intl vs. Intlayer blog post](https://github.com/aymericzip/intlayer/blob/main/docs/blog/en/react-i18next_vs_react-intl_vs_intlayer.md).
32
+ ## Why Combine Intlayer with react-intl?
52
33
 
53
- **Key advantages of using Intlayer with react-intl:**
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. **Component-Level Dictionaries**
56
- Intlayer content declaration files can live alongside your React components, preventing "orphaned" translations if components are moved or removed.
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
- 2. **Centralized Translations**
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
- 3. **Automatic Build and Regeneration**
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
- 4. **Type Safety**
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 in a React Application with react-intl
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 using your preferred package manager:
52
+ Install the necessary packages:
74
53
 
75
54
  ```bash packageManager="npm"
76
- npm install intlayer react-intl @intlayer/sync-json-plugin
55
+ npm install intlayer @intlayer/sync-json-plugin
77
56
  ```
78
57
 
79
58
  ```bash packageManager="pnpm"
80
- pnpm add intlayer react-intl @intlayer/sync-json-plugin
59
+ pnpm add intlayer @intlayer/sync-json-plugin
81
60
  ```
82
61
 
83
62
  ```bash packageManager="yarn"
84
- yarn add intlayer react-intl @intlayer/sync-json-plugin
63
+ yarn add intlayer @intlayer/sync-json-plugin
85
64
  ```
86
65
 
87
- #### Why These Packages?
66
+ **Package descriptions:**
88
67
 
89
- - **intlayer**: Core CLI and library that scans for content declarations, merges them, and builds dictionary outputs.
90
- - **react-intl**: The main library from FormatJS that provides `<IntlProvider>`, `<FormattedMessage>`, `useIntl()`, and other internationalization primitives.
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
- > If you don't already have React installed, you'll also need `react` and `react-dom`.
71
+ ### Step 2: Implement the Intlayer plugin to wrap the JSON
94
72
 
95
- ### Step 2: Configure Your Project
73
+ Create an Intlayer configuration file to define your supported locales:
96
74
 
97
- Create a configuration file to define the languages and output settings for your application:
75
+ **If you want to also export JSON dictionaries for react-intl**, add the `syncJSON` plugin:
98
76
 
99
- ```typescript fileName="intlayer.config.ts" codeFormat="typescript"
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
- ```javascript fileName="intlayer.config.mjs" codeFormat="esm"
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
- const localeNames = {
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
- const LocaleSwitcher = () => {
896
- const { locale, setLocale, availableLocales } = useLocale();
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
- return React.createElement(
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
- module.exports = LocaleSwitcher;
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
- ```tsx fileName="src/App.tsx" codeFormat="typescript"
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
- #### Date and Number Formatting
109
+ It's recommended to ignore auto-generated Intlayer files:
989
110
 
990
- ```tsx fileName="src/components/FormattedData.tsx" codeFormat="typescript"
991
- import React from "react";
992
- import { FormattedDate, FormattedNumber, FormattedTime } from "react-intl";
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
- ![Autocompletion](https://github.com/aymericzip/intlayer/blob/main/docs/assets/autocompletion.png?raw=true)
1069
-
1070
- ![Translation Error](https://github.com/aymericzip/intlayer/blob/main/docs/assets/translation_error.png?raw=true)
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
- ### Git Configuration
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
- Add the following to your `.gitignore` file:
118
+ ### VS Code Extension
1089
119
 
1090
- ```plaintext fileName=".gitignore"
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
- Depending on your workflow, you may want to commit the `./intl/messages` files if they're needed for production deployments. If your CI/CD pipeline regenerates them, you can safely ignore them.
122
+ [Install from the VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=intlayer.intlayer-vs-code-extension)