@intlayer/docs 7.0.3-canary.1 → 7.0.4-canary.0

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 (62) hide show
  1. package/blog/en/intlayer_with_i18next.md +1620 -54
  2. package/blog/en/intlayer_with_next-i18next.md +763 -163
  3. package/blog/en/intlayer_with_next-intl.md +986 -217
  4. package/blog/en/intlayer_with_react-i18next.md +645 -147
  5. package/blog/en/intlayer_with_react-intl.md +900 -147
  6. package/blog/en/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
  7. package/dist/cjs/generated/blog.entry.cjs +13 -1
  8. package/dist/cjs/generated/blog.entry.cjs.map +1 -1
  9. package/dist/cjs/generated/docs.entry.cjs +13 -1
  10. package/dist/cjs/generated/docs.entry.cjs.map +1 -1
  11. package/dist/cjs/generated/frequentQuestions.entry.cjs +13 -1
  12. package/dist/cjs/generated/frequentQuestions.entry.cjs.map +1 -1
  13. package/dist/cjs/generated/legal.entry.cjs +13 -1
  14. package/dist/cjs/generated/legal.entry.cjs.map +1 -1
  15. package/dist/esm/generated/blog.entry.mjs +14 -3
  16. package/dist/esm/generated/blog.entry.mjs.map +1 -1
  17. package/dist/esm/generated/docs.entry.mjs +14 -3
  18. package/dist/esm/generated/docs.entry.mjs.map +1 -1
  19. package/dist/esm/generated/frequentQuestions.entry.mjs +14 -3
  20. package/dist/esm/generated/frequentQuestions.entry.mjs.map +1 -1
  21. package/dist/esm/generated/legal.entry.mjs +14 -3
  22. package/dist/esm/generated/legal.entry.mjs.map +1 -1
  23. package/dist/types/generated/blog.entry.d.ts.map +1 -1
  24. package/dist/types/generated/docs.entry.d.ts.map +1 -1
  25. package/dist/types/generated/frequentQuestions.entry.d.ts.map +1 -1
  26. package/dist/types/generated/legal.entry.d.ts.map +1 -1
  27. package/docs/de/releases/v7.md +1 -18
  28. package/docs/en/CI_CD.md +1 -1
  29. package/docs/en/configuration.md +1 -1
  30. package/docs/en/formatters.md +1 -1
  31. package/docs/en/how_works_intlayer.md +1 -1
  32. package/docs/en/intlayer_CMS.md +1 -1
  33. package/docs/en/intlayer_cli.md +1 -1
  34. package/docs/en/intlayer_with_nextjs_14.md +1 -1
  35. package/docs/en/intlayer_with_nextjs_15.md +1 -1
  36. package/docs/en/intlayer_with_nextjs_16.md +1 -1
  37. package/docs/en/intlayer_with_nextjs_page_router.md +1 -1
  38. package/docs/en/intlayer_with_nuxt.md +1 -1
  39. package/docs/en/intlayer_with_react_native+expo.md +1 -1
  40. package/docs/en/intlayer_with_react_router_v7.md +1 -1
  41. package/docs/en/intlayer_with_tanstack.md +1 -1
  42. package/docs/en/intlayer_with_vite+preact.md +1 -1
  43. package/docs/en/intlayer_with_vite+react.md +1 -1
  44. package/docs/en/intlayer_with_vite+solid.md +1 -1
  45. package/docs/en/intlayer_with_vite+svelte.md +1 -1
  46. package/docs/en/intlayer_with_vite+vue.md +1 -1
  47. package/docs/en/roadmap.md +1 -1
  48. package/docs/es/releases/v7.md +1 -18
  49. package/docs/fr/intlayer_with_nextjs_16.md +2 -51
  50. package/docs/fr/releases/v7.md +1 -18
  51. package/docs/hi/intlayer_with_nextjs_16.md +3 -2
  52. package/docs/id/releases/v7.md +1 -18
  53. package/docs/it/releases/v7.md +1 -18
  54. package/docs/ja/intlayer_with_nextjs_16.md +44 -205
  55. package/docs/ja/releases/v7.md +1 -18
  56. package/docs/ko/releases/v7.md +1 -18
  57. package/docs/pt/intlayer_with_nextjs_16.md +1 -52
  58. package/package.json +17 -17
  59. package/src/generated/blog.entry.ts +27 -4
  60. package/src/generated/docs.entry.ts +27 -4
  61. package/src/generated/frequentQuestions.entry.ts +27 -4
  62. package/src/generated/legal.entry.ts +27 -4
@@ -1,16 +1,18 @@
1
1
  ---
2
2
  createdAt: 2025-01-02
3
3
  updatedAt: 2025-10-29
4
- title: Intlayer and next-intl
5
- description: Integrate Intlayer with next-intl for the internationalization (i18n) of a React app
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
6
  keywords:
7
7
  - next-intl
8
8
  - Intlayer
9
9
  - Internationalization
10
+ - i18n
10
11
  - Blog
11
12
  - Next.js
12
13
  - JavaScript
13
14
  - React
15
+ - TypeScript
14
16
  slugs:
15
17
  - blog
16
18
  - intlayer-with-next-intl
@@ -22,106 +24,129 @@ history:
22
24
 
23
25
  # Next.js Internationalization (i18n) with next-intl and Intlayer
24
26
 
25
- Both next-intl and Intlayer are open-source internationalization (i18n) frameworks designed for Next.js applications. They are widely used for managing translations, localization, and language switching in software projects.
27
+ ## What is next-intl?
26
28
 
27
- They share three principal notions:
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.
28
30
 
29
- 1. **Dictionary**: The method for defining the translatable content of your application.
30
- - Named `content declaration file` in Intlayer, which can be a JSON, JS, or TS file exporting the structured data. See [Intlayer documentation](https://intlayer.org/fr/doc/concept/content) for more information.
31
- - Named `messages` or `locale messages` in next-intl, usually in JSON files. See [next-intl documentation](https://github.com/amannn/next-intl) for more information.
31
+ Key features of next-intl include:
32
32
 
33
- 2. **Utilities**: Tools to build and interpret content declarations in the application, such as `useIntlayer()` or `useLocale()` for Intlayer, and `useTranslations()` for next-intl.
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
34
38
 
35
- 3. **Plugins and Middlewares**: Features for managing URL redirection, bundling optimization, and more e.g., `intlayerMiddleware` for Intlayer or [`createMiddleware`](https://github.com/amannn/next-intl) for next-intl.
39
+ ## What is Intlayer?
40
+
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.
42
+
43
+ Key advantages of Intlayer include:
44
+
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
36
50
 
37
51
  ## Table of Contents
38
52
 
39
- <TOC>
53
+ <TOC/>
40
54
 
41
55
  ## Intlayer vs. next-intl: Key Differences
42
56
 
43
- For a deeper analysis of how Intlayer compares to other i18n libraries for Next.js (such as next-intl), check out the [next-i18next vs. next-intl vs. Intlayer blog post](https://github.com/aymericzip/intlayer/blob/main/docs/blog/en/i18next_vs_next-intl_vs_intlayer.md).
57
+ Both next-intl and Intlayer are excellent i18n solutions for Next.js, but they take different approaches:
44
58
 
45
- ## How to Generate next-intl Messages with Intlayer
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
+ ```
46
78
 
47
- ### Why Use Intlayer with next-intl?
79
+ ### Flexibility
48
80
 
49
- Intlayer content declaration files generally offer a better developer experience. They are more flexible and maintainable due to two main advantages:
81
+ **next-intl**: Great for traditional key-value translation patterns with JSON files.
50
82
 
51
- 1. **Flexible Placement**: You can place an Intlayer content declaration file anywhere in your application’s file tree. This makes it easy to rename or delete components without leaving unused or dangling message files.
83
+ **Intlayer**: Offers more flexibility with support for complex content structures, React components in translations, and powerful content management features.
52
84
 
53
- Example file structures:
85
+ ### TypeScript Support
54
86
 
55
- ```bash codeFormat="typescript"
56
- .
57
- └── src
58
- └── components
59
- └── MyComponent
60
- ├── index.content.ts # Content declaration file
61
- └── index.tsx
62
- ```
87
+ **next-intl**: Provides type safety through TypeScript integration.
63
88
 
64
- ```bash codeFormat="esm"
65
- .
66
- └── src
67
- └── components
68
- └── MyComponent
69
- ├── index.content.mjs # Content declaration file
70
- └── index.mjx
71
- ```
89
+ **Intlayer**: Offers automatic type generation from your content declarations, ensuring complete type safety and better developer experience with autocompletion.
72
90
 
73
- ```bash codeFormat="cjs"
74
- .
75
- └── src
76
- └── components
77
- └── MyComponent
78
- ├── index.content.cjs # Content declaration file
79
- └── index.cjx
80
- ```
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).
81
92
 
82
- ```bash codeFormat="json"
83
- .
84
- └── src
85
- └── components
86
- └── MyComponent
87
- ├── index.content.json # Content declaration file
88
- └── index.jsx
89
- ```
93
+ ## Why Use Intlayer with next-intl?
90
94
 
91
- 2. **Centralized Translations**: Intlayer stores all translations in a single content declaration, ensuring no translation is missing. In TypeScript projects, missing translations are flagged automatically as type errors, providing immediate feedback to developers.
95
+ While you can choose to use either library exclusively, combining them can be beneficial in certain scenarios:
92
96
 
93
- ### Installation
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
94
100
 
95
- To use Intlayer and next-intl together, install both libraries:
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
+ ## Step-by-Step Guide to Set Up Intlayer with next-intl
106
+
107
+ ### Step 1: Install Dependencies
108
+
109
+ Install the necessary packages:
96
110
 
97
111
  ```bash packageManager="npm"
98
112
  npm install intlayer next-intl @intlayer/sync-json-plugin
99
113
  ```
100
114
 
115
+ ```bash packageManager="pnpm"
116
+ pnpm add intlayer next-intl @intlayer/sync-json-plugin
117
+ ```
118
+
101
119
  ```bash packageManager="yarn"
102
120
  yarn add intlayer next-intl @intlayer/sync-json-plugin
103
121
  ```
104
122
 
105
- ```bash packageManager="pnpm"
106
- pnpm add intlayer next-intl @intlayer/sync-json-plugin
107
- ```
123
+ **Package descriptions:**
108
124
 
109
- ### Configuring Intlayer to Export next-intl Messages
125
+ - **intlayer**: Core library for internationalization management, content declaration, and building
126
+ - **next-intl**: The next-intl library for Next.js internationalization
127
+ - **@intlayer/sync-json-plugin**: Plugin to export Intlayer content declarations to next-intl compatible JSON format
110
128
 
111
- > **Note:** Exporting messages from Intlayer for next-intl can introduce slight differences in structure. If possible, keep an Intlayer-only or next-intl-only flow to simplify the integration. If you do need to generate next-intl messages from Intlayer, follow the steps below.
129
+ ### Step 2: Configure Your Project
112
130
 
113
- Create or update an `intlayer.config.ts` file (or `.mjs` / `.cjs`) in the root of your project:
131
+ Create a configuration file to set up the locales and integration:
114
132
 
115
- ```typescript fileName="intlayer.config.ts"
133
+ ```typescript fileName="intlayer.config.ts" codeFormat="typescript"
116
134
  import { Locales, type IntlayerConfig } from "intlayer";
135
+ import { syncJSON } from "@intlayer/sync-json-plugin";
117
136
 
118
137
  const config: IntlayerConfig = {
119
138
  internationalization: {
120
- locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
139
+ locales: [
140
+ Locales.ENGLISH,
141
+ Locales.FRENCH,
142
+ Locales.SPANISH,
143
+ // Add your other locales
144
+ ],
121
145
  defaultLocale: Locales.ENGLISH,
122
146
  },
123
147
  plugins: [
124
148
  syncJSON({
149
+ // Configure where next-intl message files should be generated
125
150
  source: ({ key, locale }) => `./intl/messages/${locale}/${key}.json`,
126
151
  }),
127
152
  ],
@@ -130,80 +155,540 @@ const config: IntlayerConfig = {
130
155
  export default config;
131
156
  ```
132
157
 
133
- ### Dictionary
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
+ };
134
547
 
135
- Below are examples of content declaration files in multiple formats. Intlayer will compile these into message files that next-intl can consume.
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
+ };
136
560
 
137
- ```typescript fileName="**/*.content.ts" contentDeclarationFormat="typescript"
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"
138
569
  import { t, type Dictionary } from "intlayer";
139
570
 
140
- const content = {
141
- key: "my-component",
571
+ const pageContent = {
572
+ key: "page",
142
573
  content: {
143
- helloWorld: t({
144
- en: "Hello World",
145
- es: "Hola Mundo",
146
- fr: "Bonjour le monde",
147
- }),
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
+ },
148
590
  },
149
591
  } satisfies Dictionary;
150
592
 
151
- export default content;
593
+ export default pageContent;
152
594
  ```
153
595
 
154
- ```javascript fileName="**/*.content.mjs" contentDeclarationFormat="esm"
596
+ ```javascript fileName="src/app/[locale]/page.content.mjs" contentDeclarationFormat="esm"
155
597
  import { t } from "intlayer";
156
598
 
157
599
  /** @type {import('intlayer').Dictionary} */
158
- const content = {
159
- key: "my-component",
600
+ const pageContent = {
601
+ key: "page",
160
602
  content: {
161
- helloWorld: t({
162
- en: "Hello World",
163
- es: "Hola Mundo",
164
- fr: "Bonjour le monde",
165
- }),
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
+ },
166
619
  },
167
620
  };
168
621
 
169
- export default content;
622
+ export default pageContent;
170
623
  ```
171
624
 
172
- ```javascript fileName="**/*.content.cjs" contentDeclarationFormat="commonjs"
625
+ ```javascript fileName="src/app/[locale]/page.content.cjs" contentDeclarationFormat="commonjs"
173
626
  const { t } = require("intlayer");
174
627
 
175
- module.exports = {
176
- key: "my-component",
628
+ /** @type {import('intlayer').Dictionary} */
629
+ const pageContent = {
630
+ key: "page",
177
631
  content: {
178
- helloWorld: t({
179
- en: "Hello World",
180
- es: "Hola Mundo",
181
- fr: "Bonjour le monde",
182
- }),
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
+ },
183
648
  },
184
649
  };
650
+
651
+ module.exports = pageContent;
185
652
  ```
186
653
 
187
- ```json fileName="**/*.content.json" contentDeclarationFormat="json"
654
+ ```json fileName="src/app/[locale]/page.content.json" contentDeclarationFormat="json"
188
655
  {
189
656
  "$schema": "https://intlayer.org/schema.json",
190
- "key": "my-component",
657
+ "key": "page",
191
658
  "content": {
192
- "helloWorld": {
193
- "nodeType": "translation",
194
- "translation": {
195
- "en": "Hello World",
196
- "fr": "Bonjour le monde",
197
- "es": "Hola Mundo"
198
- }
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"
199
680
  }
200
681
  }
201
682
  }
202
683
  ```
203
684
 
204
- ### Build the next-intl Messages
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).
205
688
 
206
- To build the message files for next-intl, run:
689
+ ### Step 7: Build and Generate next-intl Messages
690
+
691
+ Build your content declarations to generate next-intl compatible message files:
207
692
 
208
693
  ```bash packageManager="npm"
209
694
  npx intlayer build
@@ -217,149 +702,433 @@ yarn intlayer build
217
702
  pnpm intlayer build
218
703
  ```
219
704
 
220
- This will generate resources in the `./intl/messages` directory (as configured in `intlayer.config.*`). The expected output:
705
+ This will generate resources in the `./intl/messages` directory. The expected output:
221
706
 
222
707
  ```bash
223
708
  .
224
709
  └── intl
225
710
  └── messages
226
- └── en
227
- └── my-content.json
228
- └── fr
229
- └── my-content.json
230
- └── es
231
- └── my-content.json
232
- ```
233
-
234
- Each file includes compiled messages from all Intlayer content declarations. The top-level keys typically match your `content.key` fields.
235
-
236
- ### Using next-intl in Your Next.js App
237
-
238
- > For more details, see the official [next-intl usage docs](https://github.com/amannn/next-intl#readme).
239
-
240
- 1. **Create a Middleware (optional):**
241
- If you want to manage automatic locale detection or redirection, use next-intl’s [createMiddleware](https://github.com/amannn/next-intl#createMiddleware).
242
-
243
- ```typescript fileName="middleware.ts"
244
- import createMiddleware from "next-intl/middleware";
245
- import { NextResponse } from "next/server";
246
-
247
- export default createMiddleware({
248
- locales: ["en", "fr", "es"],
249
- defaultLocale: "en",
250
- });
251
-
252
- export const config = {
253
- matcher: ["/((?!api|_next|.*\\..*).*)"],
254
- };
255
- ```
256
-
257
- 2. **Create a `layout.tsx` or `_app.tsx` to Load Messages:**
258
- If you’re using the App Router (Next.js 13+), create a layout:
259
-
260
- ```typescript fileName="app/[locale]/layout.tsx"
261
- import { NextIntlClientProvider } from 'next-intl';
262
- import { notFound } from 'next/navigation';
263
- import React, { ReactNode } from 'react';
264
-
265
-
266
- export default async function RootLayout({
267
- children,
268
- params
269
- }: {
270
- children: ReactNode;
271
- params: { locale: string };
272
- }) {
273
- let messages;
274
- try {
275
- messages = (await import(`../../intl/messages/${params.locale}.json`)).default;
276
- } catch (error) {
277
- notFound();
278
- }
279
-
280
- return (
281
- <html lang={params.locale}>
282
- <body>
283
- <NextIntlClientProvider locale={params.locale} messages={messages}>
284
- {children}
285
- </NextIntlClientProvider>
286
- </body>
287
- </html>
288
- );
289
- }
290
- ```
291
-
292
- If you’re using the Pages Router (Next.js 12 or below), load messages in `_app.tsx`:
293
-
294
- ```typescript fileName="pages/_app.tsx"
295
- import type { AppProps } from 'next/app';
296
- import { NextIntlProvider } from 'next-intl';
297
-
298
- function MyApp({ Component, pageProps }: AppProps) {
299
- return (
300
- <NextIntlProvider locale={pageProps.locale} messages={pageProps.messages}>
301
- <Component {...pageProps} />
302
- </NextIntlProvider>
303
- );
304
- }
305
-
306
- export default MyApp;
307
- ```
308
-
309
- 3. **Fetch Messages Server-Side (Pages Router example):**
310
-
311
- ```typescript fileName="pages/index.tsx"
312
- import { GetServerSideProps } from "next";
313
- import HomePage from "../components/HomePage";
314
-
315
- export default HomePage;
316
-
317
- export const getServerSideProps: GetServerSideProps = async ({ locale }) => {
318
- const messages = (await import(`../intl/messages/${locale}.json`)).default;
319
-
320
- return {
321
- props: {
322
- locale,
323
- messages,
324
- },
325
- };
326
- };
327
- ```
328
-
329
- ### Using Content in Next.js Components
330
-
331
- Once the messages are loaded into next-intl, you can use them in your components via the `useTranslations()` hook:
711
+ ├── en.json
712
+ ├── fr.json
713
+ └── es.json
714
+ ```
715
+
716
+ Each JSON file contains compiled messages from all Intlayer content declarations, structured for next-intl consumption.
332
717
 
333
- ```typescript fileName="src/components/MyComponent/index.tsx" codeFormat="typescript"
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"
334
723
  import type { FC } from "react";
335
- import { useTranslations } from 'next-intl';
724
+ import { useTranslations } from "next-intl";
336
725
 
337
- const MyComponent: FC = () => {
338
- const t = useTranslations('my-component');
339
- // 'my-component' corresponds to the content key in Intlayer
726
+ const Page: FC = () => {
727
+ const t = useTranslations("page");
340
728
 
341
729
  return (
342
730
  <div>
343
- <h1>{t('helloWorld')}</h1>
731
+ <h1>{t("getStarted.main")}</h1>
732
+ <code>{t("getStarted.pageLink")}</code>
733
+
734
+ <a href={t("documentationLink.href")}>{t("documentationLink.text")}</a>
344
735
  </div>
345
736
  );
346
737
  };
347
738
 
348
- export default MyComponent;
739
+ export default Page;
349
740
  ```
350
741
 
351
- ```jsx fileName="src/components/MyComponent/index.jsx" codeFormat="esm"
742
+ ```jsx fileName="src/app/[locale]/page.mjx" codeFormat="esm"
352
743
  import { useTranslations } from "next-intl";
353
744
 
354
- export default function MyComponent() {
355
- const t = useTranslations("my-component");
745
+ export default function Page() {
746
+ const t = useTranslations("page");
356
747
 
357
748
  return (
358
749
  <div>
359
- <h1>{t("helloWorld")}</h1>
750
+ <h1>{t("getStarted.main")}</h1>
751
+ <code>{t("getStarted.pageLink")}</code>
752
+
753
+ <a href={t("documentationLink.href")}>{t("documentationLink.text")}</a>
360
754
  </div>
361
755
  );
362
756
  }
363
757
  ```
364
758
 
365
- **That’s it!** Whenever you update or add new Intlayer content declaration files, re-run the `intlayer build` command to regenerate your next-intl JSON messages. next-intl will pick up the updated content automatically.
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
+ ---
1112
+
1113
+ ## Git Configuration
1114
+
1115
+ It's recommended to ignore auto-generated Intlayer files:
1116
+
1117
+ ```plaintext fileName=".gitignore"
1118
+ # Ignore files generated by Intlayer
1119
+ .intlayer
1120
+ intl
1121
+ ```
1122
+
1123
+ These files can be regenerated during your build process and don't need to be committed to version control.
1124
+
1125
+ ---
1126
+
1127
+ ## Further Resources and Learning
1128
+
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
1133
+
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.