@intlayer/docs 7.0.4-canary.0 → 7.0.5

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,18 +1,8 @@
1
1
  ---
2
2
  createdAt: 2025-01-02
3
3
  updatedAt: 2025-10-29
4
- title: Next.js Internationalization (i18n) with next-intl and Intlayer
5
- description: Integrate Intlayer with next-intl for enhanced internationalization in Next.js applications. Learn how to structure translations, manage content, and leverage the best of both libraries.
6
- keywords:
7
- - next-intl
8
- - Intlayer
9
- - Internationalization
10
- - i18n
11
- - Blog
12
- - Next.js
13
- - JavaScript
14
- - React
15
- - TypeScript
4
+ title: How to automate your next-intl JSON translations using Intlayer
5
+ description: Automate your JSON translations with Intlayer and next-intl for enhanced internationalization in Next.js applications.
16
6
  slugs:
17
7
  - blog
18
8
  - intlayer-with-next-intl
@@ -22,86 +12,30 @@ history:
22
12
  changes: Change to syncJSON plugin
23
13
  ---
24
14
 
25
- # Next.js Internationalization (i18n) with next-intl and Intlayer
15
+ # How to automate your next-intl JSON translations using Intlayer
26
16
 
27
- ## What is next-intl?
17
+ ## What is Intlayer?
28
18
 
29
- **[next-intl](https://github.com/amannn/next-intl)** is a popular open-source internationalization (i18n) library designed specifically for Next.js applications. It provides a comprehensive solution for managing translations, formatting dates and numbers, and handling locale routing.
19
+ **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 Next.js applications.
30
20
 
31
- Key features of next-intl include:
21
+ See a concrete comparison with next-intl in our [next-i18next vs. next-intl vs. Intlayer](https://github.com/aymericzip/intlayer/blob/main/docs/blog/en/next-i18next_vs_next-intl_vs_intlayer.md) blog post.
32
22
 
33
- - **Server-side rendering support** for both App Router and Pages Router
34
- - **Flexible message structure** using JSON files
35
- - **Type-safe translations** with TypeScript support
36
- - **Middleware for automatic locale detection** and routing
37
- - **Rich formatting capabilities** for dates, times, numbers, and relative time
23
+ ## Why Combine Intlayer with next-intl?
38
24
 
39
- ## What is Intlayer?
25
+ While Intlayer provides an excellent standalone i18n solution (see our [Next.js integration guide](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/intlayer_with_nextjs_16.md)), you might want to combine it with next-intl for several reasons:
40
26
 
41
- **[Intlayer](https://intlayer.org)** is an innovative, open-source internationalization (i18n) library that takes a different approach to content management. Instead of separating translations into JSON files, Intlayer allows you to declare your multilingual content directly alongside your components.
27
+ 1. **Existing codebase**: You have an established next-intl implementation and want to gradually migrate to Intlayer's improved developer experience.
28
+ 2. **Legacy requirements**: Your project requires compatibility with existing next-intl plugins or workflows.
29
+ 3. **Team familiarity**: Your team is comfortable with next-intl but wants better content management.
42
30
 
43
- Key advantages of Intlayer include:
31
+ **For that, Intlayer can be implemented as an adapter for next-intl to help automating your JSON translations in CLI or CI/CD pipelines, testing your translations, and more.**
44
32
 
45
- - **Flexible content placement**: Declare translations right next to the components that use them
46
- - **Type-safe translations**: Automatic TypeScript type generation with full autocompletion
47
- - **Rich content support**: Handle complex content structures including React components, dates, and more
48
- - **Visual editor integration**: Manage translations through a visual interface
49
- - **Advanced features**: Dynamic locale detection, SEO optimization, and more
33
+ This guide shows you how to leverage Intlayer's superior content declaration system while maintaining compatibility with next-intl.
50
34
 
51
35
  ## Table of Contents
52
36
 
53
37
  <TOC/>
54
38
 
55
- ## Intlayer vs. next-intl: Key Differences
56
-
57
- Both next-intl and Intlayer are excellent i18n solutions for Next.js, but they take different approaches:
58
-
59
- ### Content Organization
60
-
61
- **next-intl**: Uses centralized JSON files for each locale, typically organized like:
62
-
63
- ```
64
- messages/
65
- ├── en.json
66
- ├── fr.json
67
- └── es.json
68
- ```
69
-
70
- **Intlayer**: Allows you to declare content alongside your components:
71
-
72
- ```
73
- components/
74
- └── MyComponent/
75
- ├── index.tsx
76
- └── index.content.ts # Translations for this component
77
- ```
78
-
79
- ### Flexibility
80
-
81
- **next-intl**: Great for traditional key-value translation patterns with JSON files.
82
-
83
- **Intlayer**: Offers more flexibility with support for complex content structures, React components in translations, and powerful content management features.
84
-
85
- ### TypeScript Support
86
-
87
- **next-intl**: Provides type safety through TypeScript integration.
88
-
89
- **Intlayer**: Offers automatic type generation from your content declarations, ensuring complete type safety and better developer experience with autocompletion.
90
-
91
- For a detailed comparison of Intlayer with other i18n libraries including next-intl, check out the [next-i18next vs. next-intl vs. Intlayer blog post](https://github.com/aymericzip/intlayer/blob/main/docs/blog/en/next-i18next_vs_next-intl_vs_intlayer.md).
92
-
93
- ## Why Use Intlayer with next-intl?
94
-
95
- While you can choose to use either library exclusively, combining them can be beneficial in certain scenarios:
96
-
97
- 1. **Gradual Migration**: If you have an existing next-intl project and want to leverage Intlayer's features
98
- 2. **Component-Centric Workflow**: Use Intlayer's flexible content declaration alongside next-intl's infrastructure
99
- 3. **Best of Both Worlds**: Leverage next-intl's proven routing and formatting with Intlayer's developer experience
100
-
101
- This guide shows you how to use Intlayer to generate next-intl compatible messages, allowing you to benefit from Intlayer's content declaration approach while maintaining compatibility with next-intl.
102
-
103
- ---
104
-
105
39
  ## Step-by-Step Guide to Set Up Intlayer with next-intl
106
40
 
107
41
  ### Step 1: Install Dependencies
@@ -109,45 +43,40 @@ This guide shows you how to use Intlayer to generate next-intl compatible messag
109
43
  Install the necessary packages:
110
44
 
111
45
  ```bash packageManager="npm"
112
- npm install intlayer next-intl @intlayer/sync-json-plugin
46
+ npm install intlayer @intlayer/sync-json-plugin
113
47
  ```
114
48
 
115
49
  ```bash packageManager="pnpm"
116
- pnpm add intlayer next-intl @intlayer/sync-json-plugin
50
+ pnpm add intlayer @intlayer/sync-json-plugin
117
51
  ```
118
52
 
119
53
  ```bash packageManager="yarn"
120
- yarn add intlayer next-intl @intlayer/sync-json-plugin
54
+ yarn add intlayer @intlayer/sync-json-plugin
121
55
  ```
122
56
 
123
57
  **Package descriptions:**
124
58
 
125
59
  - **intlayer**: Core library for internationalization management, content declaration, and building
126
- - **next-intl**: The next-intl library for Next.js internationalization
127
60
  - **@intlayer/sync-json-plugin**: Plugin to export Intlayer content declarations to next-intl compatible JSON format
128
61
 
129
- ### Step 2: Configure Your Project
62
+ ### Step 2: Implement the Intlayer plugin to wrap the JSON
63
+
64
+ Create an Intlayer configuration file to define your supported locales:
130
65
 
131
- Create a configuration file to set up the locales and integration:
66
+ **If you want to also export JSON dictionaries for next-intl**, add the `syncJSON` plugin:
132
67
 
133
- ```typescript fileName="intlayer.config.ts" codeFormat="typescript"
68
+ ```typescript fileName="intlayer.config.ts"
134
69
  import { Locales, type IntlayerConfig } from "intlayer";
135
70
  import { syncJSON } from "@intlayer/sync-json-plugin";
136
71
 
137
72
  const config: IntlayerConfig = {
138
73
  internationalization: {
139
- locales: [
140
- Locales.ENGLISH,
141
- Locales.FRENCH,
142
- Locales.SPANISH,
143
- // Add your other locales
144
- ],
74
+ locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
145
75
  defaultLocale: Locales.ENGLISH,
146
76
  },
147
77
  plugins: [
148
78
  syncJSON({
149
- // Configure where next-intl message files should be generated
150
- source: ({ key, locale }) => `./intl/messages/${locale}/${key}.json`,
79
+ source: ({ key, locale }) => `./messages/${locale}/${key}.json`,
151
80
  }),
152
81
  ],
153
82
  };
@@ -155,960 +84,16 @@ const config: IntlayerConfig = {
155
84
  export default config;
156
85
  ```
157
86
 
158
- ```javascript fileName="intlayer.config.mjs" codeFormat="esm"
159
- import { Locales } from "intlayer";
160
- import { syncJSON } from "@intlayer/sync-json-plugin";
161
-
162
- /** @type {import('intlayer').IntlayerConfig} */
163
- const config = {
164
- internationalization: {
165
- locales: [
166
- Locales.ENGLISH,
167
- Locales.FRENCH,
168
- Locales.SPANISH,
169
- // Add your other locales
170
- ],
171
- defaultLocale: Locales.ENGLISH,
172
- },
173
- plugins: [
174
- syncJSON({
175
- source: ({ key, locale }) => `./intl/messages/${locale}/${key}.json`,
176
- }),
177
- ],
178
- };
179
-
180
- export default config;
181
- ```
182
-
183
- ```javascript fileName="intlayer.config.cjs" codeFormat="commonjs"
184
- const { Locales } = require("intlayer");
185
- const { syncJSON } = require("@intlayer/sync-json-plugin");
186
-
187
- /** @type {import('intlayer').IntlayerConfig} */
188
- const config = {
189
- internationalization: {
190
- locales: [
191
- Locales.ENGLISH,
192
- Locales.FRENCH,
193
- Locales.SPANISH,
194
- // Add your other locales
195
- ],
196
- defaultLocale: Locales.ENGLISH,
197
- },
198
- plugins: [
199
- syncJSON({
200
- source: ({ key, locale }) => `./intl/messages/${locale}/${key}.json`,
201
- }),
202
- ],
203
- };
204
-
205
- module.exports = config;
206
- ```
207
-
208
- > The `syncJSON` plugin tells Intlayer to export your content declarations to next-intl compatible JSON files. This allows you to write content using Intlayer's flexible format while consuming it through next-intl.
209
-
210
- ### Step 3: Integrate Intlayer in Your Next.js Configuration
211
-
212
- If you plan to use additional Intlayer features beyond just generating next-intl messages, you can integrate Intlayer into your Next.js configuration:
213
-
214
- ```typescript fileName="next.config.ts" codeFormat="typescript"
215
- import type { NextConfig } from "next";
216
- import { withIntlayer } from "next-intlayer/server";
217
-
218
- const nextConfig: NextConfig = {
219
- /* your config options here */
220
- };
221
-
222
- export default withIntlayer(nextConfig);
223
- ```
224
-
225
- ```javascript fileName="next.config.mjs" codeFormat="esm"
226
- import { withIntlayer } from "next-intlayer/server";
227
-
228
- /** @type {import('next').NextConfig} */
229
- const nextConfig = {
230
- /* your config options here */
231
- };
232
-
233
- export default withIntlayer(nextConfig);
234
- ```
235
-
236
- ```javascript fileName="next.config.cjs" codeFormat="commonjs"
237
- const { withIntlayer } = require("next-intlayer/server");
238
-
239
- /** @type {import('next').NextConfig} */
240
- const nextConfig = {
241
- /* your config options here */
242
- };
243
-
244
- module.exports = withIntlayer(nextConfig);
245
- ```
246
-
247
- > **Note**: This step is optional if you're only using Intlayer to generate next-intl messages. If you want to use Intlayer's hooks and components directly in your application, this integration is required.
248
-
249
- ### Step 4: Configure next-intl Middleware
250
-
251
- Set up next-intl's middleware for automatic locale detection and routing:
252
-
253
- ```typescript fileName="src/middleware.ts" codeFormat="typescript"
254
- import createMiddleware from "next-intl/middleware";
255
-
256
- export default createMiddleware({
257
- // List of all supported locales
258
- locales: ["en", "fr", "es"],
259
-
260
- // Default locale when no preferred locale can be detected
261
- defaultLocale: "en",
262
-
263
- // Whether to redirect to the default locale
264
- localePrefix: "as-needed",
265
- });
266
-
267
- export const config = {
268
- // Matcher configuration to exclude specific paths
269
- matcher: ["/((?!api|_next|_vercel|.*\\..*).*)"],
270
- };
271
- ```
272
-
273
- ```javascript fileName="src/middleware.mjs" codeFormat="esm"
274
- import createMiddleware from "next-intl/middleware";
275
-
276
- export default createMiddleware({
277
- locales: ["en", "fr", "es"],
278
- defaultLocale: "en",
279
- localePrefix: "as-needed",
280
- });
281
-
282
- export const config = {
283
- matcher: ["/((?!api|_next|_vercel|.*\\..*).*)"],
284
- };
285
- ```
286
-
287
- ```javascript fileName="src/middleware.cjs" codeFormat="commonjs"
288
- const createMiddleware = require("next-intl/middleware");
289
-
290
- module.exports = createMiddleware({
291
- locales: ["en", "fr", "es"],
292
- defaultLocale: "en",
293
- localePrefix: "as-needed",
294
- });
295
-
296
- module.exports.config = {
297
- matcher: ["/((?!api|_next|_vercel|.*\\..*).*)"],
298
- };
299
- ```
300
-
301
- > This middleware handles automatic locale detection from the user's browser settings and manages locale-based routing. For more details, see the [next-intl middleware documentation](https://next-intl-docs.vercel.app/docs/routing/middleware).
302
-
303
- ### Step 5: Define Dynamic Locale Routes
304
-
305
- Set up your application structure to support dynamic locale routing. For the App Router (Next.js 13+):
306
-
307
- Remove everything from your root `RootLayout` and replace it with:
308
-
309
- ```tsx fileName="src/app/layout.tsx" codeFormat="typescript"
310
- import type { PropsWithChildren, FC } from "react";
311
- import "./globals.css";
312
-
313
- const RootLayout: FC<PropsWithChildren> = ({ children }) => children;
314
-
315
- export default RootLayout;
316
- ```
317
-
318
- ```jsx fileName="src/app/layout.mjx" codeFormat="esm"
319
- import "./globals.css";
320
-
321
- const RootLayout = ({ children }) => children;
322
-
323
- export default RootLayout;
324
- ```
325
-
326
- ```jsx fileName="src/app/layout.csx" codeFormat="commonjs"
327
- require("./globals.css");
328
-
329
- const RootLayout = ({ children }) => children;
330
-
331
- module.exports = RootLayout;
332
- ```
333
-
334
- Then create a locale-specific layout in `[locale]` directory:
335
-
336
- ```tsx fileName="src/app/[locale]/layout.tsx" codeFormat="typescript"
337
- import { NextIntlClientProvider } from "next-intl";
338
- import { getMessages } from "next-intl/server";
339
- import { notFound } from "next/navigation";
340
- import { routing } from "@/i18n/routing";
341
- import type { ReactNode } from "react";
342
-
343
- export default async function LocaleLayout({
344
- children,
345
- params: { locale },
346
- }: {
347
- children: ReactNode;
348
- params: { locale: string };
349
- }) {
350
- // Ensure the incoming locale is valid
351
- if (!routing.locales.includes(locale as any)) {
352
- notFound();
353
- }
354
-
355
- // Providing all messages to the client side is the easiest way to get started
356
- const messages = await getMessages();
357
-
358
- return (
359
- <html lang={locale}>
360
- <body>
361
- <NextIntlClientProvider messages={messages}>
362
- {children}
363
- </NextIntlClientProvider>
364
- </body>
365
- </html>
366
- );
367
- }
368
- ```
369
-
370
- ```jsx fileName="src/app/[locale]/layout.mjx" codeFormat="esm"
371
- import { NextIntlClientProvider } from "next-intl";
372
- import { getMessages } from "next-intl/server";
373
- import { notFound } from "next/navigation";
374
- import { routing } from "@/i18n/routing";
375
-
376
- export default async function LocaleLayout({ children, params: { locale } }) {
377
- if (!routing.locales.includes(locale)) {
378
- notFound();
379
- }
380
-
381
- const messages = await getMessages();
382
-
383
- return (
384
- <html lang={locale}>
385
- <body>
386
- <NextIntlClientProvider messages={messages}>
387
- {children}
388
- </NextIntlClientProvider>
389
- </body>
390
- </html>
391
- );
392
- }
393
- ```
394
-
395
- ```jsx fileName="src/app/[locale]/layout.csx" codeFormat="commonjs"
396
- const { NextIntlClientProvider } = require("next-intl");
397
- const { getMessages } = require("next-intl/server");
398
- const { notFound } = require("next/navigation");
399
- const { routing } = require("@/i18n/routing");
400
-
401
- async function LocaleLayout({ children, params: { locale } }) {
402
- if (!routing.locales.includes(locale)) {
403
- notFound();
404
- }
405
-
406
- const messages = await getMessages();
407
-
408
- return (
409
- <html lang={locale}>
410
- <body>
411
- <NextIntlClientProvider messages={messages}>
412
- {children}
413
- </NextIntlClientProvider>
414
- </body>
415
- </html>
416
- );
417
- }
418
-
419
- module.exports = LocaleLayout;
420
- ```
421
-
422
- Create the routing configuration file:
423
-
424
- ```typescript fileName="src/i18n/routing.ts" codeFormat="typescript"
425
- import { defineRouting } from "next-intl/routing";
426
- import { createSharedPathnamesNavigation } from "next-intl/navigation";
427
-
428
- export const routing = defineRouting({
429
- locales: ["en", "fr", "es"],
430
- defaultLocale: "en",
431
- });
432
-
433
- export const { Link, redirect, usePathname, useRouter } =
434
- createSharedPathnamesNavigation(routing);
435
- ```
436
-
437
- ```javascript fileName="src/i18n/routing.mjs" codeFormat="esm"
438
- import { defineRouting } from "next-intl/routing";
439
- import { createSharedPathnamesNavigation } from "next-intl/navigation";
440
-
441
- export const routing = defineRouting({
442
- locales: ["en", "fr", "es"],
443
- defaultLocale: "en",
444
- });
445
-
446
- export const { Link, redirect, usePathname, useRouter } =
447
- createSharedPathnamesNavigation(routing);
448
- ```
449
-
450
- ```javascript fileName="src/i18n/routing.cjs" codeFormat="commonjs"
451
- const { defineRouting } = require("next-intl/routing");
452
- const { createSharedPathnamesNavigation } = require("next-intl/navigation");
453
-
454
- const routing = defineRouting({
455
- locales: ["en", "fr", "es"],
456
- defaultLocale: "en",
457
- });
458
-
459
- const { Link, redirect, usePathname, useRouter } =
460
- createSharedPathnamesNavigation(routing);
461
-
462
- module.exports = { routing, Link, redirect, usePathname, useRouter };
463
- ```
464
-
465
- Create the next-intl configuration file:
466
-
467
- ```typescript fileName="src/i18n/request.ts" codeFormat="typescript"
468
- import { getRequestConfig } from "next-intl/server";
469
- import { routing } from "./routing";
470
-
471
- export default getRequestConfig(async ({ requestLocale }) => {
472
- // This typically corresponds to the `[locale]` segment
473
- let locale = await requestLocale;
474
-
475
- // Ensure a valid locale is used
476
- if (!locale || !routing.locales.includes(locale as any)) {
477
- locale = routing.defaultLocale;
478
- }
479
-
480
- return {
481
- locale,
482
- messages: (await import(`../../intl/messages/${locale}.json`)).default,
483
- };
484
- });
485
- ```
486
-
487
- ```javascript fileName="src/i18n/request.mjs" codeFormat="esm"
488
- import { getRequestConfig } from "next-intl/server";
489
- import { routing } from "./routing";
490
-
491
- export default getRequestConfig(async ({ requestLocale }) => {
492
- let locale = await requestLocale;
493
-
494
- if (!locale || !routing.locales.includes(locale)) {
495
- locale = routing.defaultLocale;
496
- }
497
-
498
- return {
499
- locale,
500
- messages: (await import(`../../intl/messages/${locale}.json`)).default,
501
- };
502
- });
503
- ```
504
-
505
- ```javascript fileName="src/i18n/request.cjs" codeFormat="commonjs"
506
- const { getRequestConfig } = require("next-intl/server");
507
- const { routing } = require("./routing");
508
-
509
- module.exports = getRequestConfig(async ({ requestLocale }) => {
510
- let locale = await requestLocale;
511
-
512
- if (!locale || !routing.locales.includes(locale)) {
513
- locale = routing.defaultLocale;
514
- }
515
-
516
- return {
517
- locale,
518
- messages: (await import(`../../intl/messages/${locale}.json`)).default,
519
- };
520
- });
521
- ```
522
-
523
- Update your Next.js config to use the next-intl plugin:
524
-
525
- ```typescript fileName="next.config.ts" codeFormat="typescript"
526
- import type { NextConfig } from "next";
527
- import createNextIntlPlugin from "next-intl/plugin";
528
-
529
- const withNextIntl = createNextIntlPlugin("./src/i18n/request.ts");
530
-
531
- const nextConfig: NextConfig = {
532
- /* your config options here */
533
- };
534
-
535
- export default withNextIntl(nextConfig);
536
- ```
537
-
538
- ```javascript fileName="next.config.mjs" codeFormat="esm"
539
- import createNextIntlPlugin from "next-intl/plugin";
540
-
541
- const withNextIntl = createNextIntlPlugin("./src/i18n/request.ts");
542
-
543
- /** @type {import('next').NextConfig} */
544
- const nextConfig = {
545
- /* your config options here */
546
- };
547
-
548
- export default withNextIntl(nextConfig);
549
- ```
550
-
551
- ```javascript fileName="next.config.cjs" codeFormat="commonjs"
552
- const createNextIntlPlugin = require("next-intl/plugin");
553
-
554
- const withNextIntl = createNextIntlPlugin("./src/i18n/request.ts");
555
-
556
- /** @type {import('next').NextConfig} */
557
- const nextConfig = {
558
- /* your config options here */
559
- };
560
-
561
- module.exports = withNextIntl(nextConfig);
562
- ```
563
-
564
- ### Step 6: Declare Your Content
565
-
566
- Create content declarations using Intlayer's flexible format:
567
-
568
- ```typescript fileName="src/app/[locale]/page.content.ts" contentDeclarationFormat="typescript"
569
- import { t, type Dictionary } from "intlayer";
570
-
571
- const pageContent = {
572
- key: "page",
573
- content: {
574
- getStarted: {
575
- main: t({
576
- en: "Get started by editing",
577
- fr: "Commencez par éditer",
578
- es: "Comience por editar",
579
- }),
580
- pageLink: "src/app/page.tsx",
581
- },
582
- documentationLink: {
583
- text: t({
584
- en: "Read our documentation",
585
- fr: "Lisez notre documentation",
586
- es: "Lea nuestra documentación",
587
- }),
588
- href: "https://intlayer.org",
589
- },
590
- },
591
- } satisfies Dictionary;
592
-
593
- export default pageContent;
594
- ```
595
-
596
- ```javascript fileName="src/app/[locale]/page.content.mjs" contentDeclarationFormat="esm"
597
- import { t } from "intlayer";
598
-
599
- /** @type {import('intlayer').Dictionary} */
600
- const pageContent = {
601
- key: "page",
602
- content: {
603
- getStarted: {
604
- main: t({
605
- en: "Get started by editing",
606
- fr: "Commencez par éditer",
607
- es: "Comience por editar",
608
- }),
609
- pageLink: "src/app/page.tsx",
610
- },
611
- documentationLink: {
612
- text: t({
613
- en: "Read our documentation",
614
- fr: "Lisez notre documentation",
615
- es: "Lea nuestra documentación",
616
- }),
617
- href: "https://intlayer.org",
618
- },
619
- },
620
- };
621
-
622
- export default pageContent;
623
- ```
624
-
625
- ```javascript fileName="src/app/[locale]/page.content.cjs" contentDeclarationFormat="commonjs"
626
- const { t } = require("intlayer");
87
+ The `syncJSON` plugin will automatically wrap the JSON. It will read and write the JSON files without changing the content architecture.
627
88
 
628
- /** @type {import('intlayer').Dictionary} */
629
- const pageContent = {
630
- key: "page",
631
- content: {
632
- getStarted: {
633
- main: t({
634
- en: "Get started by editing",
635
- fr: "Commencez par éditer",
636
- es: "Comience por editar",
637
- }),
638
- pageLink: "src/app/page.tsx",
639
- },
640
- documentationLink: {
641
- text: t({
642
- en: "Read our documentation",
643
- fr: "Lisez notre documentation",
644
- es: "Lea nuestra documentación",
645
- }),
646
- href: "https://intlayer.org",
647
- },
648
- },
649
- };
650
-
651
- module.exports = pageContent;
652
- ```
653
-
654
- ```json fileName="src/app/[locale]/page.content.json" contentDeclarationFormat="json"
655
- {
656
- "$schema": "https://intlayer.org/schema.json",
657
- "key": "page",
658
- "content": {
659
- "getStarted": {
660
- "main": {
661
- "nodeType": "translation",
662
- "translation": {
663
- "en": "Get started by editing",
664
- "fr": "Commencez par éditer",
665
- "es": "Comience por editar"
666
- }
667
- },
668
- "pageLink": "src/app/page.tsx"
669
- },
670
- "documentationLink": {
671
- "text": {
672
- "nodeType": "translation",
673
- "translation": {
674
- "en": "Read our documentation",
675
- "fr": "Lisez notre documentation",
676
- "es": "Lea nuestra documentación"
677
- }
678
- },
679
- "href": "https://intlayer.org"
680
- }
681
- }
682
- }
683
- ```
89
+ If you want to make coexist that JSON with intlayer content declaration files (`.content` files), Intlayer will proceed this way:
684
90
 
685
- > 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 (by default, `.content.{ts,tsx,js,jsx,mjs,cjs,json}`).
686
-
687
- > For more details about content declaration, refer to the [content declaration documentation](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/dictionary/content_file.md).
688
-
689
- ### Step 7: Build and Generate next-intl Messages
690
-
691
- Build your content declarations to generate next-intl compatible message files:
692
-
693
- ```bash packageManager="npm"
694
- npx intlayer build
695
- ```
696
-
697
- ```bash packageManager="yarn"
698
- yarn intlayer build
699
- ```
700
-
701
- ```bash packageManager="pnpm"
702
- pnpm intlayer build
703
- ```
704
-
705
- This will generate resources in the `./intl/messages` directory. The expected output:
706
-
707
- ```bash
708
- .
709
- └── intl
710
- └── messages
711
- ├── en.json
712
- ├── fr.json
713
- └── es.json
714
- ```
91
+ 1. load both JSON and content declaration files and transform them into a intlayer dictionary.
92
+ 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).
715
93
 
716
- Each JSON file contains compiled messages from all Intlayer content declarations, structured for next-intl consumption.
94
+ 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.
717
95
 
718
- ### Step 8: Utilize Content in Your Code
719
-
720
- Access your translations in your components using next-intl's hooks:
721
-
722
- ```tsx fileName="src/app/[locale]/page.tsx" codeFormat="typescript"
723
- import type { FC } from "react";
724
- import { useTranslations } from "next-intl";
725
-
726
- const Page: FC = () => {
727
- const t = useTranslations("page");
728
-
729
- return (
730
- <div>
731
- <h1>{t("getStarted.main")}</h1>
732
- <code>{t("getStarted.pageLink")}</code>
733
-
734
- <a href={t("documentationLink.href")}>{t("documentationLink.text")}</a>
735
- </div>
736
- );
737
- };
738
-
739
- export default Page;
740
- ```
741
-
742
- ```jsx fileName="src/app/[locale]/page.mjx" codeFormat="esm"
743
- import { useTranslations } from "next-intl";
744
-
745
- export default function Page() {
746
- const t = useTranslations("page");
747
-
748
- return (
749
- <div>
750
- <h1>{t("getStarted.main")}</h1>
751
- <code>{t("getStarted.pageLink")}</code>
752
-
753
- <a href={t("documentationLink.href")}>{t("documentationLink.text")}</a>
754
- </div>
755
- );
756
- }
757
- ```
758
-
759
- ```jsx fileName="src/app/[locale]/page.csx" codeFormat="commonjs"
760
- const { useTranslations } = require("next-intl");
761
-
762
- function Page() {
763
- const t = useTranslations("page");
764
-
765
- return (
766
- <div>
767
- <h1>{t("getStarted.main")}</h1>
768
- <code>{t("getStarted.pageLink")}</code>
769
-
770
- <a href={t("documentationLink.href")}>{t("documentationLink.text")}</a>
771
- </div>
772
- );
773
- }
774
-
775
- module.exports = Page;
776
- ```
777
-
778
- > The first parameter of `useTranslations` corresponds to the `key` in your Intlayer content declaration. The nested properties are accessed using dot notation.
779
-
780
- ### (Optional) Step 9: Internationalization of Your Metadata
781
-
782
- For metadata like page titles and descriptions:
783
-
784
- ```typescript fileName="src/app/[locale]/metadata.content.ts" contentDeclarationFormat="typescript"
785
- import { t, type Dictionary } from "intlayer";
786
- import type { Metadata } from "next";
787
-
788
- const metadataContent = {
789
- key: "metadata",
790
- content: {
791
- title: t({
792
- en: "My Application",
793
- fr: "Mon Application",
794
- es: "Mi Aplicación",
795
- }),
796
- description: t({
797
- en: "Welcome to my Next.js application",
798
- fr: "Bienvenue dans mon application Next.js",
799
- es: "Bienvenido a mi aplicación Next.js",
800
- }),
801
- },
802
- } satisfies Dictionary<Metadata>;
803
-
804
- export default metadataContent;
805
- ```
806
-
807
- ```javascript fileName="src/app/[locale]/metadata.content.mjs" contentDeclarationFormat="esm"
808
- import { t } from "intlayer";
809
-
810
- /** @type {import('intlayer').Dictionary<import('next').Metadata>} */
811
- const metadataContent = {
812
- key: "metadata",
813
- content: {
814
- title: t({
815
- en: "My Application",
816
- fr: "Mon Application",
817
- es: "Mi Aplicación",
818
- }),
819
- description: t({
820
- en: "Welcome to my Next.js application",
821
- fr: "Bienvenue dans mon application Next.js",
822
- es: "Bienvenido a mi aplicación Next.js",
823
- }),
824
- },
825
- };
826
-
827
- export default metadataContent;
828
- ```
829
-
830
- ```javascript fileName="src/app/[locale]/metadata.content.cjs" contentDeclarationFormat="commonjs"
831
- const { t } = require("intlayer");
832
-
833
- /** @type {import('intlayer').Dictionary<import('next').Metadata>} */
834
- const metadataContent = {
835
- key: "metadata",
836
- content: {
837
- title: t({
838
- en: "My Application",
839
- fr: "Mon Application",
840
- es: "Mi Aplicación",
841
- }),
842
- description: t({
843
- en: "Welcome to my Next.js application",
844
- fr: "Bienvenue dans mon application Next.js",
845
- es: "Bienvenido a mi aplicación Next.js",
846
- }),
847
- },
848
- };
849
-
850
- module.exports = metadataContent;
851
- ```
852
-
853
- Use in your layout or page:
854
-
855
- ```tsx fileName="src/app/[locale]/layout.tsx" codeFormat="typescript"
856
- import { getTranslations } from "next-intl/server";
857
- import type { Metadata } from "next";
858
-
859
- export async function generateMetadata({
860
- params: { locale },
861
- }: {
862
- params: { locale: string };
863
- }): Promise<Metadata> {
864
- const t = await getTranslations({ locale, namespace: "metadata" });
865
-
866
- return {
867
- title: t("title"),
868
- description: t("description"),
869
- };
870
- }
871
-
872
- // ... rest of your layout
873
- ```
874
-
875
- ### (Optional) Step 10: Internationalization of sitemap.xml and robots.txt
876
-
877
- For SEO optimization, internationalize your sitemap and robots files:
878
-
879
- ```typescript fileName="src/app/sitemap.ts" codeFormat="typescript"
880
- import { MetadataRoute } from "next";
881
- import { routing } from "@/i18n/routing";
882
-
883
- export default function sitemap(): MetadataRoute.Sitemap {
884
- return [
885
- {
886
- url: "https://example.com",
887
- lastModified: new Date(),
888
- alternates: {
889
- languages: Object.fromEntries(
890
- routing.locales.map((locale) => [
891
- locale,
892
- `https://example.com/${locale}`,
893
- ])
894
- ),
895
- },
896
- },
897
- ];
898
- }
899
- ```
900
-
901
- ```javascript fileName="src/app/sitemap.mjs" codeFormat="esm"
902
- import { routing } from "@/i18n/routing";
903
-
904
- export default function sitemap() {
905
- return [
906
- {
907
- url: "https://example.com",
908
- lastModified: new Date(),
909
- alternates: {
910
- languages: Object.fromEntries(
911
- routing.locales.map((locale) => [
912
- locale,
913
- `https://example.com/${locale}`,
914
- ])
915
- ),
916
- },
917
- },
918
- ];
919
- }
920
- ```
921
-
922
- ```javascript fileName="src/app/sitemap.cjs" codeFormat="commonjs"
923
- const { routing } = require("@/i18n/routing");
924
-
925
- function sitemap() {
926
- return [
927
- {
928
- url: "https://example.com",
929
- lastModified: new Date(),
930
- alternates: {
931
- languages: Object.fromEntries(
932
- routing.locales.map((locale) => [
933
- locale,
934
- `https://example.com/${locale}`,
935
- ])
936
- ),
937
- },
938
- },
939
- ];
940
- }
941
-
942
- module.exports = sitemap;
943
- ```
944
-
945
- ### (Optional) Step 11: Change the Language of Your Content
946
-
947
- Create a language switcher component:
948
-
949
- ```tsx fileName="src/components/LanguageSwitcher.tsx" codeFormat="typescript"
950
- "use client";
951
-
952
- import { useLocale } from "next-intl";
953
- import { useRouter, usePathname } from "@/i18n/routing";
954
- import { routing } from "@/i18n/routing";
955
- import type { FC } from "react";
956
-
957
- export const LanguageSwitcher: FC = () => {
958
- const locale = useLocale();
959
- const router = useRouter();
960
- const pathname = usePathname();
961
-
962
- const handleLocaleChange = (newLocale: string) => {
963
- router.replace(pathname, { locale: newLocale });
964
- };
965
-
966
- return (
967
- <select value={locale} onChange={(e) => handleLocaleChange(e.target.value)}>
968
- {routing.locales.map((loc) => (
969
- <option key={loc} value={loc}>
970
- {loc.toUpperCase()}
971
- </option>
972
- ))}
973
- </select>
974
- );
975
- };
976
- ```
977
-
978
- ```jsx fileName="src/components/LanguageSwitcher.mjx" codeFormat="esm"
979
- "use client";
980
-
981
- import { useLocale } from "next-intl";
982
- import { useRouter, usePathname } from "@/i18n/routing";
983
- import { routing } from "@/i18n/routing";
984
-
985
- export const LanguageSwitcher = () => {
986
- const locale = useLocale();
987
- const router = useRouter();
988
- const pathname = usePathname();
989
-
990
- const handleLocaleChange = (newLocale) => {
991
- router.replace(pathname, { locale: newLocale });
992
- };
993
-
994
- return (
995
- <select value={locale} onChange={(e) => handleLocaleChange(e.target.value)}>
996
- {routing.locales.map((loc) => (
997
- <option key={loc} value={loc}>
998
- {loc.toUpperCase()}
999
- </option>
1000
- ))}
1001
- </select>
1002
- );
1003
- };
1004
- ```
1005
-
1006
- ```jsx fileName="src/components/LanguageSwitcher.csx" codeFormat="commonjs"
1007
- "use client";
1008
-
1009
- const { useLocale } = require("next-intl");
1010
- const { useRouter, usePathname } = require("@/i18n/routing");
1011
- const { routing } = require("@/i18n/routing");
1012
-
1013
- const LanguageSwitcher = () => {
1014
- const locale = useLocale();
1015
- const router = useRouter();
1016
- const pathname = usePathname();
1017
-
1018
- const handleLocaleChange = (newLocale) => {
1019
- router.replace(pathname, { locale: newLocale });
1020
- };
1021
-
1022
- return (
1023
- <select value={locale} onChange={(e) => handleLocaleChange(e.target.value)}>
1024
- {routing.locales.map((loc) => (
1025
- <option key={loc} value={loc}>
1026
- {loc.toUpperCase()}
1027
- </option>
1028
- ))}
1029
- </select>
1030
- );
1031
- };
1032
-
1033
- module.exports = { LanguageSwitcher };
1034
- ```
1035
-
1036
- ### (Optional) Step 12: Creating a Localized Link Component
1037
-
1038
- For locale-aware navigation, use the `Link` component from your routing configuration:
1039
-
1040
- ```tsx fileName="src/components/LocalizedLink.tsx" codeFormat="typescript"
1041
- import { Link } from "@/i18n/routing";
1042
- import type { ComponentProps, FC } from "react";
1043
-
1044
- export const LocalizedLink: FC<ComponentProps<typeof Link>> = (props) => {
1045
- return <Link {...props} />;
1046
- };
1047
- ```
1048
-
1049
- ```jsx fileName="src/components/LocalizedLink.mjx" codeFormat="esm"
1050
- import { Link } from "@/i18n/routing";
1051
-
1052
- export const LocalizedLink = (props) => {
1053
- return <Link {...props} />;
1054
- };
1055
- ```
1056
-
1057
- ```jsx fileName="src/components/LocalizedLink.csx" codeFormat="commonjs"
1058
- const { Link } = require("@/i18n/routing");
1059
-
1060
- const LocalizedLink = (props) => {
1061
- return <Link {...props} />;
1062
- };
1063
-
1064
- module.exports = { LocalizedLink };
1065
- ```
1066
-
1067
- Use it in your application:
1068
-
1069
- ```tsx
1070
- import { LocalizedLink } from "@/components/LocalizedLink";
1071
-
1072
- export default function Navigation() {
1073
- return (
1074
- <nav>
1075
- <LocalizedLink href="/">Home</LocalizedLink>
1076
- <LocalizedLink href="/about">About</LocalizedLink>
1077
- <LocalizedLink href="/contact">Contact</LocalizedLink>
1078
- </nav>
1079
- );
1080
- }
1081
- ```
1082
-
1083
- ---
1084
-
1085
- ## Updating or Adding New Translations
1086
-
1087
- 1. **Add or modify** content in any `*.content.*` file
1088
- 2. Run `intlayer build` to regenerate the JSON files
1089
- 3. Next.js (and next-intl) will automatically pick up the changes on the next rebuild or in development mode
1090
-
1091
- ---
1092
-
1093
- ## TypeScript Configuration
1094
-
1095
- Intlayer provides **autogenerated type definitions** for your content. Ensure TypeScript picks them up:
1096
-
1097
- ```json5 fileName="tsconfig.json"
1098
- {
1099
- "compilerOptions": {
1100
- // ... your other compiler options
1101
- },
1102
- "include": [
1103
- "src",
1104
- ".intlayer/**/*.ts", // Include Intlayer auto-generated types
1105
- ],
1106
- }
1107
- ```
1108
-
1109
- This provides autocompletion and type checking for your translation keys.
1110
-
1111
- ---
96
+ 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).
1112
97
 
1113
98
  ## Git Configuration
1114
99
 
@@ -1117,18 +102,12 @@ It's recommended to ignore auto-generated Intlayer files:
1117
102
  ```plaintext fileName=".gitignore"
1118
103
  # Ignore files generated by Intlayer
1119
104
  .intlayer
1120
- intl
1121
105
  ```
1122
106
 
1123
107
  These files can be regenerated during your build process and don't need to be committed to version control.
1124
108
 
1125
- ---
1126
-
1127
- ## Further Resources and Learning
109
+ ### VS Code Extension
1128
110
 
1129
- - **[Intlayer Documentation](https://intlayer.org)**: Comprehensive guides and API reference
1130
- - **[next-intl Documentation](https://next-intl-docs.vercel.app)**: Official next-intl documentation
1131
- - **[Intlayer Visual Editor](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/intlayer_visual_editor.md)**: Manage translations through a visual interface
1132
- - **[Intlayer CLI Documentation](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/intlayer_cli.md)**: Command-line tools for managing translations
111
+ For improved developer experience, install the official **Intlayer VS Code Extension**:
1133
112
 
1134
- By combining Intlayer's flexible content declaration approach with next-intl's proven routing and middleware capabilities, you can create a powerful internationalization solution for your Next.js application. This hybrid approach allows you to leverage the best features of both libraries while maintaining a clean, maintainable codebase.
113
+ [Install from the VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=intlayer.intlayer-vs-code-extension)