@intlayer/docs 7.0.4-canary.0 → 7.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/blog/ar/intlayer_with_i18next.md +68 -106
- package/blog/ar/intlayer_with_next-i18next.md +84 -288
- package/blog/ar/intlayer_with_next-intl.md +58 -337
- package/blog/ar/intlayer_with_react-i18next.md +68 -290
- package/blog/ar/intlayer_with_react-intl.md +63 -266
- package/blog/de/intlayer_with_i18next.md +77 -97
- package/blog/de/intlayer_with_next-i18next.md +69 -296
- package/blog/de/intlayer_with_next-intl.md +59 -340
- package/blog/de/intlayer_with_react-i18next.md +68 -290
- package/blog/de/intlayer_with_react-intl.md +62 -264
- package/blog/en/intlayer_with_i18next.md +36 -1638
- package/blog/en/intlayer_with_next-i18next.md +22 -847
- package/blog/en/intlayer_with_next-intl.md +32 -1053
- package/blog/en/intlayer_with_react-i18next.md +38 -764
- package/blog/en/intlayer_with_react-intl.md +42 -1018
- package/blog/en-GB/intlayer_with_i18next.md +67 -103
- package/blog/en-GB/intlayer_with_next-i18next.md +71 -292
- package/blog/en-GB/intlayer_with_next-intl.md +58 -337
- package/blog/en-GB/intlayer_with_react-i18next.md +67 -289
- package/blog/en-GB/intlayer_with_react-intl.md +61 -264
- package/blog/es/intlayer_with_i18next.md +67 -103
- package/blog/es/intlayer_with_next-i18next.md +71 -296
- package/blog/es/intlayer_with_next-intl.md +57 -338
- package/blog/es/intlayer_with_react-i18next.md +68 -290
- package/blog/es/intlayer_with_react-intl.md +62 -265
- package/blog/fr/intlayer_with_i18next.md +66 -104
- package/blog/fr/intlayer_with_next-i18next.md +82 -285
- package/blog/fr/intlayer_with_next-intl.md +57 -338
- package/blog/fr/intlayer_with_react-i18next.md +67 -289
- package/blog/fr/intlayer_with_react-intl.md +61 -264
- package/blog/hi/intlayer_with_i18next.md +68 -104
- package/blog/hi/intlayer_with_next-i18next.md +74 -299
- package/blog/hi/intlayer_with_next-intl.md +57 -239
- package/blog/hi/intlayer_with_react-i18next.md +69 -291
- package/blog/hi/intlayer_with_react-intl.md +65 -268
- package/blog/id/intlayer_with_i18next.md +126 -0
- package/blog/id/intlayer_with_next-i18next.md +142 -0
- package/blog/id/intlayer_with_next-intl.md +113 -0
- package/blog/id/intlayer_with_react-i18next.md +124 -0
- package/blog/id/intlayer_with_react-intl.md +122 -0
- package/blog/it/intlayer_with_i18next.md +67 -103
- package/blog/it/intlayer_with_next-i18next.md +71 -296
- package/blog/it/intlayer_with_next-intl.md +57 -338
- package/blog/it/intlayer_with_react-i18next.md +68 -290
- package/blog/it/intlayer_with_react-intl.md +62 -265
- package/blog/ja/intlayer_with_i18next.md +68 -103
- package/blog/ja/intlayer_with_next-i18next.md +85 -283
- package/blog/ja/intlayer_with_next-intl.md +58 -336
- package/blog/ja/intlayer_with_react-i18next.md +68 -290
- package/blog/ja/intlayer_with_react-intl.md +62 -264
- package/blog/ko/intlayer_with_i18next.md +80 -96
- package/blog/ko/intlayer_with_next-i18next.md +85 -287
- package/blog/ko/intlayer_with_next-intl.md +68 -327
- package/blog/ko/intlayer_with_react-i18next.md +68 -290
- package/blog/ko/intlayer_with_react-intl.md +64 -266
- package/blog/pl/intlayer_with_i18next.md +126 -0
- package/blog/pl/intlayer_with_next-i18next.md +142 -0
- package/blog/pl/intlayer_with_next-intl.md +111 -0
- package/blog/pl/intlayer_with_react-i18next.md +124 -0
- package/blog/pl/intlayer_with_react-intl.md +122 -0
- package/blog/pt/intlayer_with_i18next.md +67 -103
- package/blog/pt/intlayer_with_next-i18next.md +72 -293
- package/blog/pt/intlayer_with_next-intl.md +57 -256
- package/blog/pt/intlayer_with_react-i18next.md +104 -78
- package/blog/pt/intlayer_with_react-intl.md +62 -266
- package/blog/ru/intlayer_with_i18next.md +66 -104
- package/blog/ru/intlayer_with_next-i18next.md +71 -296
- package/blog/ru/intlayer_with_next-intl.md +58 -337
- package/blog/ru/intlayer_with_react-i18next.md +68 -290
- package/blog/ru/intlayer_with_react-intl.md +62 -265
- package/blog/tr/intlayer_with_i18next.md +71 -107
- package/blog/tr/intlayer_with_next-i18next.md +72 -297
- package/blog/tr/intlayer_with_next-intl.md +58 -339
- package/blog/tr/intlayer_with_react-i18next.md +69 -291
- package/blog/tr/intlayer_with_react-intl.md +63 -285
- package/blog/vi/intlayer_with_i18next.md +126 -0
- package/blog/vi/intlayer_with_next-i18next.md +142 -0
- package/blog/vi/intlayer_with_next-intl.md +111 -0
- package/blog/vi/intlayer_with_react-i18next.md +124 -0
- package/blog/vi/intlayer_with_react-intl.md +122 -0
- package/blog/zh/intlayer_with_i18next.md +67 -102
- package/blog/zh/intlayer_with_next-i18next.md +72 -296
- package/blog/zh/intlayer_with_next-intl.md +58 -336
- package/blog/zh/intlayer_with_react-i18next.md +68 -290
- package/blog/zh/intlayer_with_react-intl.md +63 -106
- package/docs/ar/plugins/sync-json.md +244 -0
- package/docs/de/plugins/sync-json.md +244 -0
- package/docs/en/intlayer_cli.md +25 -0
- package/docs/en/intlayer_with_nextjs_14.md +2 -0
- package/docs/en/intlayer_with_nextjs_15.md +2 -0
- package/docs/en/intlayer_with_nextjs_16.md +2 -0
- package/docs/en/plugins/sync-json.md +1 -1
- package/docs/en-GB/plugins/sync-json.md +244 -0
- package/docs/es/plugins/sync-json.md +244 -0
- package/docs/fr/plugins/sync-json.md +244 -0
- package/docs/hi/plugins/sync-json.md +244 -0
- package/docs/id/plugins/sync-json.md +244 -0
- package/docs/it/plugins/sync-json.md +244 -0
- package/docs/ja/plugins/sync-json.md +244 -0
- package/docs/ko/plugins/sync-json.md +244 -0
- package/docs/pl/plugins/sync-json.md +244 -0
- package/docs/pt/plugins/sync-json.md +244 -0
- package/docs/ru/plugins/sync-json.md +244 -0
- package/docs/tr/plugins/sync-json.md +245 -0
- package/docs/vi/plugins/sync-json.md +244 -0
- package/docs/zh/plugins/sync-json.md +244 -0
- package/package.json +14 -14
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
createdAt: 2024-12-24
|
|
3
3
|
updatedAt: 2025-10-29
|
|
4
|
-
title:
|
|
5
|
-
description:
|
|
4
|
+
title: How to automate your i18next JSON translations using Intlayer
|
|
5
|
+
description: Automate your JSON translations with Intlayer and i18next for enhanced internationalization in JavaScript applications.
|
|
6
6
|
keywords:
|
|
7
7
|
- Intlayer
|
|
8
8
|
- i18next
|
|
@@ -22,342 +22,63 @@ slugs:
|
|
|
22
22
|
history:
|
|
23
23
|
- version: 7.0.0
|
|
24
24
|
date: 2025-10-29
|
|
25
|
-
changes:
|
|
25
|
+
changes: Change to syncJSON plugin
|
|
26
26
|
---
|
|
27
27
|
|
|
28
|
-
#
|
|
29
|
-
|
|
30
|
-
## Table of Contents
|
|
31
|
-
|
|
32
|
-
<TOC/>
|
|
33
|
-
|
|
34
|
-
## What is i18next?
|
|
35
|
-
|
|
36
|
-
**i18next** is an open-source internationalization (i18n) framework designed for JavaScript applications. It has been widely used for managing translations, localization, and language switching in software projects for many years. While powerful and mature, i18next has some limitations that can complicate scalability and modern development workflows.
|
|
28
|
+
# How to automate your i18next JSON translations using Intlayer
|
|
37
29
|
|
|
38
30
|
## What is Intlayer?
|
|
39
31
|
|
|
40
|
-
**Intlayer** is
|
|
41
|
-
|
|
42
|
-
## Intlayer vs. i18next: Key Differences
|
|
43
|
-
|
|
44
|
-
Before diving into the integration, let's understand the key differences between these two frameworks:
|
|
45
|
-
|
|
46
|
-
### 1. Content Declaration & Dictionary Management
|
|
47
|
-
|
|
48
|
-
**i18next:**
|
|
49
|
-
|
|
50
|
-
- Requires translation dictionaries to be declared in specific folders (typically `public/locales/` or `locales/`)
|
|
51
|
-
- Separate JSON files for each namespace and locale
|
|
52
|
-
- Difficult to track which translations belong to which components
|
|
53
|
-
- Can complicate application scalability as the project grows
|
|
54
|
-
|
|
55
|
-
```plaintext
|
|
56
|
-
# i18next typical structure
|
|
57
|
-
locales/
|
|
58
|
-
├── en/
|
|
59
|
-
│ ├── common.json
|
|
60
|
-
│ ├── home.json
|
|
61
|
-
│ └── about.json
|
|
62
|
-
└── fr/
|
|
63
|
-
├── common.json
|
|
64
|
-
├── home.json
|
|
65
|
-
└── about.json
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
**Intlayer:**
|
|
69
|
-
|
|
70
|
-
- Allows content to be declared right next to your components
|
|
71
|
-
- Co-location of content with components
|
|
72
|
-
- Automatic detection and adaptation when components move or are removed
|
|
73
|
-
- Better maintainability and developer experience
|
|
74
|
-
|
|
75
|
-
```plaintext
|
|
76
|
-
# Intlayer structure
|
|
77
|
-
components/
|
|
78
|
-
├── Header/
|
|
79
|
-
│ ├── Header.tsx
|
|
80
|
-
│ └── Header.content.ts # Translations live with the component
|
|
81
|
-
└── Footer/
|
|
82
|
-
├── Footer.tsx
|
|
83
|
-
└── Footer.content.ts
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
**Advantages:**
|
|
87
|
-
|
|
88
|
-
- **Simplified Content Editing**: Developers don't have to search through multiple folders to find the correct dictionary
|
|
89
|
-
- **Automatic Adaptation**: If a component changes location or is removed, Intlayer detects and adapts automatically
|
|
90
|
-
- **Better Code Organization**: Keeps related code together, improving maintainability
|
|
91
|
-
|
|
92
|
-
### 2. Configuration Complexity
|
|
93
|
-
|
|
94
|
-
**i18next:**
|
|
95
|
-
|
|
96
|
-
- Complex configuration, especially with server-side rendering
|
|
97
|
-
- Requires manual setup for Next.js middleware
|
|
98
|
-
- Separate configuration for client and server
|
|
99
|
-
- Multiple plugins needed for full functionality
|
|
100
|
-
|
|
101
|
-
```typescript
|
|
102
|
-
// i18next configuration can get complex
|
|
103
|
-
import i18n from "i18next";
|
|
104
|
-
import { initReactI18next } from "react-i18next";
|
|
105
|
-
import Backend from "i18next-http-backend";
|
|
106
|
-
import LanguageDetector from "i18next-browser-languagedetector";
|
|
107
|
-
|
|
108
|
-
i18n
|
|
109
|
-
.use(Backend)
|
|
110
|
-
.use(LanguageDetector)
|
|
111
|
-
.use(initReactI18next)
|
|
112
|
-
.init({
|
|
113
|
-
fallbackLng: "en",
|
|
114
|
-
debug: true,
|
|
115
|
-
interpolation: {
|
|
116
|
-
escapeValue: false,
|
|
117
|
-
},
|
|
118
|
-
// ... many more options
|
|
119
|
-
});
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
**Intlayer:**
|
|
123
|
-
|
|
124
|
-
- Streamlined configuration process
|
|
125
|
-
- Built-in support for Next.js with simple setup
|
|
126
|
-
- Unified configuration for client and server
|
|
127
|
-
- Minimal configuration required to get started
|
|
128
|
-
|
|
129
|
-
```typescript
|
|
130
|
-
// Intlayer configuration is straightforward
|
|
131
|
-
import { Locales, type IntlayerConfig } from "intlayer";
|
|
132
|
-
|
|
133
|
-
const config: IntlayerConfig = {
|
|
134
|
-
internationalization: {
|
|
135
|
-
locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
|
|
136
|
-
defaultLocale: Locales.ENGLISH,
|
|
137
|
-
},
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
export default config;
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
### 3. TypeScript Integration
|
|
144
|
-
|
|
145
|
-
**i18next:**
|
|
146
|
-
|
|
147
|
-
- TypeScript support requires additional setup
|
|
148
|
-
- Type safety for translation keys is limited
|
|
149
|
-
- Requires manual type definitions
|
|
150
|
-
- Auto-completion is basic
|
|
151
|
-
|
|
152
|
-
**Intlayer:**
|
|
153
|
-
|
|
154
|
-
- First-class TypeScript support out of the box
|
|
155
|
-
- Fully typed content declarations
|
|
156
|
-
- Auto-generated types for all translations
|
|
157
|
-
- Excellent IDE auto-completion and error detection
|
|
158
|
-
|
|
159
|
-
```typescript
|
|
160
|
-
// Intlayer provides full type safety
|
|
161
|
-
import { t, type Dictionary } from "intlayer";
|
|
162
|
-
|
|
163
|
-
const content = {
|
|
164
|
-
key: "homepage",
|
|
165
|
-
content: {
|
|
166
|
-
title: t({
|
|
167
|
-
en: "Welcome to our site",
|
|
168
|
-
fr: "Bienvenue sur notre site",
|
|
169
|
-
es: "Bienvenido a nuestro sitio",
|
|
170
|
-
}),
|
|
171
|
-
description: t({
|
|
172
|
-
en: "Get started by exploring our features",
|
|
173
|
-
fr: "Commencez par explorer nos fonctionnalités",
|
|
174
|
-
es: "Comience explorando nuestras características",
|
|
175
|
-
}),
|
|
176
|
-
},
|
|
177
|
-
} satisfies Dictionary;
|
|
178
|
-
|
|
179
|
-
// TypeScript knows exactly what keys and values are available
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
### 4. Consistency & Validation
|
|
183
|
-
|
|
184
|
-
**i18next:**
|
|
185
|
-
|
|
186
|
-
- Easy to miss translations for specific locales
|
|
187
|
-
- No built-in validation for translation completeness
|
|
188
|
-
- Can lead to runtime errors with missing keys
|
|
189
|
-
- Requires manual checking for consistency
|
|
190
|
-
|
|
191
|
-
**Intlayer:**
|
|
192
|
-
|
|
193
|
-
- Enforces translation completeness at build time
|
|
194
|
-
- Ensures all locales have all required keys
|
|
195
|
-
- TypeScript errors for missing translations
|
|
196
|
-
- Prevents runtime errors from missing content
|
|
197
|
-
|
|
198
|
-
### 5. Content Sharing Across Applications
|
|
199
|
-
|
|
200
|
-
**i18next:**
|
|
201
|
-
|
|
202
|
-
- Difficult to share translations across multiple apps
|
|
203
|
-
- Requires custom solutions for shared translations
|
|
204
|
-
- No standard approach for monorepo setups
|
|
205
|
-
|
|
206
|
-
**Intlayer:**
|
|
207
|
-
|
|
208
|
-
- Built-in support for sharing content declarations
|
|
209
|
-
- Works seamlessly in monorepo architectures
|
|
210
|
-
- Easy to share translations across multiple applications and libraries
|
|
211
|
-
- Promotes consistency across your entire codebase
|
|
212
|
-
|
|
213
|
-
### 6. Server Components & Next.js App Router
|
|
214
|
-
|
|
215
|
-
**i18next:**
|
|
32
|
+
**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 JavaScript applications.
|
|
216
33
|
|
|
217
|
-
-
|
|
218
|
-
- Complex setup for Server Components
|
|
219
|
-
- Requires workarounds for async components
|
|
220
|
-
- Documentation not always up-to-date with latest Next.js features
|
|
34
|
+
See a concrete comparison with i18next 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.
|
|
221
35
|
|
|
222
|
-
|
|
36
|
+
## Why Combine Intlayer with i18next?
|
|
223
37
|
|
|
224
|
-
|
|
225
|
-
- First-class support for Server Components
|
|
226
|
-
- Optimized for Next.js 14, 15, and 16
|
|
227
|
-
- Works seamlessly with Turbopack and React Server Components
|
|
38
|
+
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 i18next for several reasons:
|
|
228
39
|
|
|
229
|
-
|
|
40
|
+
1. **Existing codebase**: You have an established i18next implementation and want to gradually migrate to Intlayer's improved developer experience.
|
|
41
|
+
2. **Legacy requirements**: Your project requires compatibility with existing i18next plugins or workflows.
|
|
42
|
+
3. **Team familiarity**: Your team is comfortable with i18next but wants better content management.
|
|
230
43
|
|
|
231
|
-
|
|
44
|
+
**For that, Intlayer can be implemented as an adapter for i18next to help automating your JSON translations in CLI or CI/CD pipelines, testing your translations, and more.**
|
|
232
45
|
|
|
233
|
-
|
|
234
|
-
- You need specific i18next plugins or ecosystem tools
|
|
235
|
-
- You're working on a non-React application
|
|
236
|
-
- You need interpolation patterns that i18next handles uniquely
|
|
46
|
+
This guide shows you how to leverage Intlayer's superior content declaration system while maintaining compatibility with i18next.
|
|
237
47
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
- You're starting a new project
|
|
241
|
-
- You want better TypeScript integration
|
|
242
|
-
- You're building a modern Next.js application
|
|
243
|
-
- You want co-located translations with components
|
|
244
|
-
- You want better developer experience and maintainability
|
|
245
|
-
- You're working in a monorepo or multi-app architecture
|
|
246
|
-
|
|
247
|
-
### Use Both Together if:
|
|
248
|
-
|
|
249
|
-
- You're migrating a large existing codebase gradually
|
|
250
|
-
- You want to leverage Intlayer's content management while maintaining i18next compatibility
|
|
251
|
-
- You need to support legacy code while adopting modern patterns
|
|
252
|
-
|
|
253
|
-
---
|
|
48
|
+
## Table of Contents
|
|
254
49
|
|
|
255
|
-
|
|
50
|
+
<TOC/>
|
|
256
51
|
|
|
257
|
-
|
|
52
|
+
## Step-by-Step Guide to Set Up Intlayer with i18next
|
|
258
53
|
|
|
259
54
|
### Step 1: Install Dependencies
|
|
260
55
|
|
|
261
|
-
Install the necessary packages
|
|
56
|
+
Install the necessary packages:
|
|
262
57
|
|
|
263
58
|
```bash packageManager="npm"
|
|
264
|
-
npm install intlayer
|
|
59
|
+
npm install intlayer @intlayer/sync-json-plugin
|
|
265
60
|
```
|
|
266
61
|
|
|
267
62
|
```bash packageManager="pnpm"
|
|
268
|
-
pnpm add intlayer
|
|
63
|
+
pnpm add intlayer @intlayer/sync-json-plugin
|
|
269
64
|
```
|
|
270
65
|
|
|
271
66
|
```bash packageManager="yarn"
|
|
272
|
-
yarn add intlayer
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
If you plan to use i18next alongside Intlayer (for migration or compatibility):
|
|
276
|
-
|
|
277
|
-
```bash packageManager="npm"
|
|
278
|
-
npm install i18next react-i18next i18next-resources-to-backend @intlayer/sync-json-plugin
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
```bash packageManager="pnpm"
|
|
282
|
-
pnpm add i18next react-i18next i18next-resources-to-backend @intlayer/sync-json-plugin
|
|
67
|
+
yarn add intlayer @intlayer/sync-json-plugin
|
|
283
68
|
```
|
|
284
69
|
|
|
285
|
-
|
|
286
|
-
yarn add i18next react-i18next i18next-resources-to-backend @intlayer/sync-json-plugin
|
|
287
|
-
```
|
|
70
|
+
**Package descriptions:**
|
|
288
71
|
|
|
289
|
-
**
|
|
72
|
+
- **intlayer**: Core library for internationalization management, content declaration, and building
|
|
73
|
+
- **@intlayer/sync-json-plugin**: Plugin to export Intlayer content declarations to i18next compatible JSON format
|
|
290
74
|
|
|
291
|
-
|
|
292
|
-
- **next-intlayer**: Integration package for Next.js with context providers, hooks, and plugins for Webpack/Turbopack.
|
|
293
|
-
- **i18next** (optional): The core i18next internationalization framework.
|
|
294
|
-
- **react-i18next** (optional): React bindings for i18next.
|
|
295
|
-
- **i18next-resources-to-backend** (optional): Dynamically imports i18next resources.
|
|
296
|
-
- **@intlayer/sync-json-plugin** (optional): Plugin to export Intlayer dictionaries as JSON files compatible with i18next.
|
|
297
|
-
|
|
298
|
-
### Step 2: Configure Your Project
|
|
75
|
+
### Step 2: Implement the Intlayer plugin to wrap the JSON
|
|
299
76
|
|
|
300
77
|
Create an Intlayer configuration file to define your supported locales:
|
|
301
78
|
|
|
302
|
-
```typescript fileName="intlayer.config.ts" codeFormat="typescript"
|
|
303
|
-
import { Locales, type IntlayerConfig } from "intlayer";
|
|
304
|
-
|
|
305
|
-
const config: IntlayerConfig = {
|
|
306
|
-
internationalization: {
|
|
307
|
-
locales: [
|
|
308
|
-
Locales.ENGLISH,
|
|
309
|
-
Locales.FRENCH,
|
|
310
|
-
Locales.SPANISH,
|
|
311
|
-
// Add your other locales
|
|
312
|
-
],
|
|
313
|
-
defaultLocale: Locales.ENGLISH,
|
|
314
|
-
},
|
|
315
|
-
};
|
|
316
|
-
|
|
317
|
-
export default config;
|
|
318
|
-
```
|
|
319
|
-
|
|
320
|
-
```javascript fileName="intlayer.config.mjs" codeFormat="esm"
|
|
321
|
-
import { Locales } from "intlayer";
|
|
322
|
-
|
|
323
|
-
/** @type {import('intlayer').IntlayerConfig} */
|
|
324
|
-
const config = {
|
|
325
|
-
internationalization: {
|
|
326
|
-
locales: [
|
|
327
|
-
Locales.ENGLISH,
|
|
328
|
-
Locales.FRENCH,
|
|
329
|
-
Locales.SPANISH,
|
|
330
|
-
// Add your other locales
|
|
331
|
-
],
|
|
332
|
-
defaultLocale: Locales.ENGLISH,
|
|
333
|
-
},
|
|
334
|
-
};
|
|
335
|
-
|
|
336
|
-
export default config;
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
```javascript fileName="intlayer.config.cjs" codeFormat="commonjs"
|
|
340
|
-
const { Locales } = require("intlayer");
|
|
341
|
-
|
|
342
|
-
/** @type {import('intlayer').IntlayerConfig} */
|
|
343
|
-
const config = {
|
|
344
|
-
internationalization: {
|
|
345
|
-
locales: [
|
|
346
|
-
Locales.ENGLISH,
|
|
347
|
-
Locales.FRENCH,
|
|
348
|
-
Locales.SPANISH,
|
|
349
|
-
// Add your other locales
|
|
350
|
-
],
|
|
351
|
-
defaultLocale: Locales.ENGLISH,
|
|
352
|
-
},
|
|
353
|
-
};
|
|
354
|
-
|
|
355
|
-
module.exports = config;
|
|
356
|
-
```
|
|
357
|
-
|
|
358
79
|
**If you want to also export JSON dictionaries for i18next**, add the `syncJSON` plugin:
|
|
359
80
|
|
|
360
|
-
```typescript fileName="intlayer.config.ts"
|
|
81
|
+
```typescript fileName="intlayer.config.ts"
|
|
361
82
|
import { Locales, type IntlayerConfig } from "intlayer";
|
|
362
83
|
import { syncJSON } from "@intlayer/sync-json-plugin";
|
|
363
84
|
|
|
@@ -376,1353 +97,30 @@ const config: IntlayerConfig = {
|
|
|
376
97
|
export default config;
|
|
377
98
|
```
|
|
378
99
|
|
|
379
|
-
|
|
380
|
-
import { Locales } from "intlayer";
|
|
381
|
-
import { syncJSON } from "@intlayer/sync-json-plugin";
|
|
382
|
-
|
|
383
|
-
/** @type {import('intlayer').IntlayerConfig} */
|
|
384
|
-
const config = {
|
|
385
|
-
internationalization: {
|
|
386
|
-
locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
|
|
387
|
-
defaultLocale: Locales.ENGLISH,
|
|
388
|
-
},
|
|
389
|
-
plugins: [
|
|
390
|
-
syncJSON({
|
|
391
|
-
source: ({ key, locale }) => `./intl/messages/${locale}/${key}.json`,
|
|
392
|
-
}),
|
|
393
|
-
],
|
|
394
|
-
};
|
|
395
|
-
|
|
396
|
-
export default config;
|
|
397
|
-
```
|
|
398
|
-
|
|
399
|
-
```javascript fileName="intlayer.config.cjs" codeFormat="commonjs"
|
|
400
|
-
const { Locales } = require("intlayer");
|
|
401
|
-
const { syncJSON } = require("@intlayer/sync-json-plugin");
|
|
402
|
-
|
|
403
|
-
/** @type {import('intlayer').IntlayerConfig} */
|
|
404
|
-
const config = {
|
|
405
|
-
internationalization: {
|
|
406
|
-
locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
|
|
407
|
-
defaultLocale: Locales.ENGLISH,
|
|
408
|
-
},
|
|
409
|
-
plugins: [
|
|
410
|
-
syncJSON({
|
|
411
|
-
source: ({ key, locale }) => `./intl/messages/${locale}/${key}.json`,
|
|
412
|
-
}),
|
|
413
|
-
],
|
|
414
|
-
};
|
|
415
|
-
|
|
416
|
-
module.exports = config;
|
|
417
|
-
```
|
|
418
|
-
|
|
419
|
-
> The `syncJSON` plugin will automatically generate JSON files compatible with i18next whenever you build your Intlayer dictionaries.
|
|
420
|
-
|
|
421
|
-
> **Important Note**: The exportation of i18next dictionaries is currently in beta and does not ensure a 1:1 compatibility with all i18next features. It is recommended to use Intlayer natively for the best experience, using i18next export only for gradual migration scenarios.
|
|
422
|
-
|
|
423
|
-
For a complete list of available configuration parameters, refer to the [configuration documentation](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/configuration.md).
|
|
424
|
-
|
|
425
|
-
### Step 3: Integrate Intlayer in Your Next.js Configuration
|
|
426
|
-
|
|
427
|
-
Configure your Next.js setup to use Intlayer:
|
|
428
|
-
|
|
429
|
-
```typescript fileName="next.config.ts" codeFormat="typescript"
|
|
430
|
-
import type { NextConfig } from "next";
|
|
431
|
-
import { withIntlayer } from "next-intlayer/server";
|
|
432
|
-
|
|
433
|
-
const nextConfig: NextConfig = {
|
|
434
|
-
/* your config options here */
|
|
435
|
-
};
|
|
436
|
-
|
|
437
|
-
export default withIntlayer(nextConfig);
|
|
438
|
-
```
|
|
439
|
-
|
|
440
|
-
```typescript fileName="next.config.mjs" codeFormat="esm"
|
|
441
|
-
import { withIntlayer } from "next-intlayer/server";
|
|
442
|
-
|
|
443
|
-
/** @type {import('next').NextConfig} */
|
|
444
|
-
const nextConfig = {
|
|
445
|
-
/* your config options here */
|
|
446
|
-
};
|
|
447
|
-
|
|
448
|
-
export default withIntlayer(nextConfig);
|
|
449
|
-
```
|
|
450
|
-
|
|
451
|
-
```typescript fileName="next.config.cjs" codeFormat="commonjs"
|
|
452
|
-
const { withIntlayer } = require("next-intlayer/server");
|
|
453
|
-
|
|
454
|
-
/** @type {import('next').NextConfig} */
|
|
455
|
-
const nextConfig = {
|
|
456
|
-
/* your config options here */
|
|
457
|
-
};
|
|
458
|
-
|
|
459
|
-
module.exports = withIntlayer(nextConfig);
|
|
460
|
-
```
|
|
461
|
-
|
|
462
|
-
> The `withIntlayer()` plugin integrates Intlayer with Next.js, building content declaration files, monitoring them in development mode, and providing optimizations for Webpack or Turbopack. It's compatible with both Server and Client Components.
|
|
463
|
-
|
|
464
|
-
### Step 4: Define Dynamic Locale Routes
|
|
465
|
-
|
|
466
|
-
Set up your Next.js application to handle dynamic locale routing.
|
|
467
|
-
|
|
468
|
-
First, update your root layout to remove the `<html>` and `<body>` tags:
|
|
469
|
-
|
|
470
|
-
```tsx fileName="src/app/layout.tsx" codeFormat="typescript"
|
|
471
|
-
import type { PropsWithChildren, FC } from "react";
|
|
472
|
-
import "./globals.css";
|
|
473
|
-
|
|
474
|
-
const RootLayout: FC<PropsWithChildren> = ({ children }) => (
|
|
475
|
-
// You can still wrap the children with other providers
|
|
476
|
-
<>{children}</>
|
|
477
|
-
);
|
|
478
|
-
|
|
479
|
-
export default RootLayout;
|
|
480
|
-
```
|
|
481
|
-
|
|
482
|
-
```jsx fileName="src/app/layout.mjx" codeFormat="esm"
|
|
483
|
-
import "./globals.css";
|
|
484
|
-
|
|
485
|
-
const RootLayout = ({ children }) => (
|
|
486
|
-
// You can still wrap the children with other providers
|
|
487
|
-
<>{children}</>
|
|
488
|
-
);
|
|
489
|
-
|
|
490
|
-
export default RootLayout;
|
|
491
|
-
```
|
|
492
|
-
|
|
493
|
-
```jsx fileName="src/app/layout.csx" codeFormat="commonjs"
|
|
494
|
-
require("./globals.css");
|
|
495
|
-
|
|
496
|
-
const RootLayout = ({ children }) => (
|
|
497
|
-
// You can still wrap the children with other providers
|
|
498
|
-
<>{children}</>
|
|
499
|
-
);
|
|
500
|
-
|
|
501
|
-
module.exports = {
|
|
502
|
-
default: RootLayout,
|
|
503
|
-
};
|
|
504
|
-
```
|
|
505
|
-
|
|
506
|
-
> Keeping the `RootLayout` component empty allows you to set the [`lang`](https://developer.mozilla.org/fr/docs/Web/HTML/Global_attributes/lang) and [`dir`](https://developer.mozilla.org/fr/docs/Web/HTML/Global_attributes/dir) attributes on the `<html>` tag in the locale-specific layout.
|
|
507
|
-
|
|
508
|
-
Next, create a locale-specific layout in `[locale]` directory:
|
|
509
|
-
|
|
510
|
-
```tsx fileName="src/app/[locale]/layout.tsx" codeFormat="typescript"
|
|
511
|
-
import type { NextLayoutIntlayer } from "next-intlayer";
|
|
512
|
-
import { Inter } from "next/font/google";
|
|
513
|
-
import { getHTMLTextDir } from "intlayer";
|
|
514
|
-
|
|
515
|
-
const inter = Inter({ subsets: ["latin"] });
|
|
516
|
-
|
|
517
|
-
const LocaleLayout: NextLayoutIntlayer = async ({ children, params }) => {
|
|
518
|
-
const { locale } = await params;
|
|
519
|
-
return (
|
|
520
|
-
<html lang={locale} dir={getHTMLTextDir(locale)}>
|
|
521
|
-
<body className={inter.className}>{children}</body>
|
|
522
|
-
</html>
|
|
523
|
-
);
|
|
524
|
-
};
|
|
525
|
-
|
|
526
|
-
export default LocaleLayout;
|
|
527
|
-
```
|
|
528
|
-
|
|
529
|
-
```jsx fileName="src/app/[locale]/layout.mjx" codeFormat="esm"
|
|
530
|
-
import { Inter } from "next/font/google";
|
|
531
|
-
import { getHTMLTextDir } from "intlayer";
|
|
532
|
-
|
|
533
|
-
const inter = Inter({ subsets: ["latin"] });
|
|
534
|
-
|
|
535
|
-
const LocaleLayout = async ({ children, params }) => {
|
|
536
|
-
const { locale } = await params;
|
|
537
|
-
return (
|
|
538
|
-
<html lang={locale} dir={getHTMLTextDir(locale)}>
|
|
539
|
-
<body className={inter.className}>{children}</body>
|
|
540
|
-
</html>
|
|
541
|
-
);
|
|
542
|
-
};
|
|
543
|
-
|
|
544
|
-
export default LocaleLayout;
|
|
545
|
-
```
|
|
546
|
-
|
|
547
|
-
```jsx fileName="src/app/[locale]/layout.csx" codeFormat="commonjs"
|
|
548
|
-
const { Inter } = require("next/font/google");
|
|
549
|
-
const { getHTMLTextDir } = require("intlayer");
|
|
550
|
-
|
|
551
|
-
const inter = Inter({ subsets: ["latin"] });
|
|
552
|
-
|
|
553
|
-
const LocaleLayout = async ({ children, params }) => {
|
|
554
|
-
const { locale } = await params;
|
|
555
|
-
return (
|
|
556
|
-
<html lang={locale} dir={getHTMLTextDir(locale)}>
|
|
557
|
-
<body className={inter.className}>{children}</body>
|
|
558
|
-
</html>
|
|
559
|
-
);
|
|
560
|
-
};
|
|
561
|
-
|
|
562
|
-
module.exports = LocaleLayout;
|
|
563
|
-
```
|
|
564
|
-
|
|
565
|
-
Then, add the `generateStaticParams` function to pre-generate pages for all locales:
|
|
566
|
-
|
|
567
|
-
```tsx fileName="src/app/[locale]/layout.tsx" codeFormat="typescript"
|
|
568
|
-
export { generateStaticParams } from "next-intlayer"; // Line to insert
|
|
569
|
-
|
|
570
|
-
const LocaleLayout: NextLayoutIntlayer = async ({ children, params }) => {
|
|
571
|
-
/* ... Rest of the code */
|
|
572
|
-
};
|
|
573
|
-
|
|
574
|
-
export default LocaleLayout;
|
|
575
|
-
```
|
|
576
|
-
|
|
577
|
-
```jsx fileName="src/app/[locale]/layout.mjx" codeFormat="esm"
|
|
578
|
-
export { generateStaticParams } from "next-intlayer"; // Line to insert
|
|
579
|
-
|
|
580
|
-
const LocaleLayout = async ({ children, params }) => {
|
|
581
|
-
/* ... Rest of the code */
|
|
582
|
-
};
|
|
583
|
-
|
|
584
|
-
export default LocaleLayout;
|
|
585
|
-
```
|
|
586
|
-
|
|
587
|
-
```jsx fileName="src/app/[locale]/layout.csx" codeFormat="commonjs"
|
|
588
|
-
const { generateStaticParams } = require("next-intlayer"); // Line to insert
|
|
589
|
-
|
|
590
|
-
const LocaleLayout = async ({ children, params }) => {
|
|
591
|
-
/* ... Rest of the code */
|
|
592
|
-
};
|
|
593
|
-
|
|
594
|
-
module.exports = { default: LocaleLayout, generateStaticParams };
|
|
595
|
-
```
|
|
596
|
-
|
|
597
|
-
> The `[locale]` path segment is used to define the locale. Example: `/en/about` refers to `en` and `/fr/about` refers to `fr`.
|
|
598
|
-
|
|
599
|
-
> The `generateStaticParams` function ensures your application pre-builds the necessary pages for all locales, reducing runtime computation and improving user experience.
|
|
600
|
-
|
|
601
|
-
### Step 5: Declare Your Content
|
|
602
|
-
|
|
603
|
-
Create and manage your content declarations. This is where Intlayer truly shines compared to i18next.
|
|
604
|
-
|
|
605
|
-
Create content files next to your components:
|
|
606
|
-
|
|
607
|
-
```tsx fileName="src/app/[locale]/page.content.ts" contentDeclarationFormat="typescript"
|
|
608
|
-
import { t, type Dictionary } from "intlayer";
|
|
609
|
-
|
|
610
|
-
const pageContent = {
|
|
611
|
-
key: "page",
|
|
612
|
-
content: {
|
|
613
|
-
getStarted: {
|
|
614
|
-
main: t({
|
|
615
|
-
en: "Get started by editing",
|
|
616
|
-
fr: "Commencez par éditer",
|
|
617
|
-
es: "Comience por editar",
|
|
618
|
-
}),
|
|
619
|
-
pageLink: "src/app/page.tsx",
|
|
620
|
-
},
|
|
621
|
-
docs: {
|
|
622
|
-
title: t({
|
|
623
|
-
en: "Documentation",
|
|
624
|
-
fr: "Documentation",
|
|
625
|
-
es: "Documentación",
|
|
626
|
-
}),
|
|
627
|
-
description: t({
|
|
628
|
-
en: "Find in-depth information about Next.js features and API.",
|
|
629
|
-
fr: "Trouvez des informations détaillées sur les fonctionnalités et l'API de Next.js.",
|
|
630
|
-
es: "Encuentre información detallada sobre las características y la API de Next.js.",
|
|
631
|
-
}),
|
|
632
|
-
},
|
|
633
|
-
},
|
|
634
|
-
} satisfies Dictionary;
|
|
635
|
-
|
|
636
|
-
export default pageContent;
|
|
637
|
-
```
|
|
638
|
-
|
|
639
|
-
```javascript fileName="src/app/[locale]/page.content.mjs" contentDeclarationFormat="esm"
|
|
640
|
-
import { t } from "intlayer";
|
|
641
|
-
|
|
642
|
-
/** @type {import('intlayer').Dictionary} */
|
|
643
|
-
const pageContent = {
|
|
644
|
-
key: "page",
|
|
645
|
-
content: {
|
|
646
|
-
getStarted: {
|
|
647
|
-
main: t({
|
|
648
|
-
en: "Get started by editing",
|
|
649
|
-
fr: "Commencez par éditer",
|
|
650
|
-
es: "Comience por editar",
|
|
651
|
-
}),
|
|
652
|
-
pageLink: "src/app/page.tsx",
|
|
653
|
-
},
|
|
654
|
-
docs: {
|
|
655
|
-
title: t({
|
|
656
|
-
en: "Documentation",
|
|
657
|
-
fr: "Documentation",
|
|
658
|
-
es: "Documentación",
|
|
659
|
-
}),
|
|
660
|
-
description: t({
|
|
661
|
-
en: "Find in-depth information about Next.js features and API.",
|
|
662
|
-
fr: "Trouvez des informations détaillées sur les fonctionnalités et l'API de Next.js.",
|
|
663
|
-
es: "Encuentre información detallada sobre las características y la API de Next.js.",
|
|
664
|
-
}),
|
|
665
|
-
},
|
|
666
|
-
},
|
|
667
|
-
};
|
|
668
|
-
|
|
669
|
-
export default pageContent;
|
|
670
|
-
```
|
|
671
|
-
|
|
672
|
-
```javascript fileName="src/app/[locale]/page.content.cjs" contentDeclarationFormat="commonjs"
|
|
673
|
-
const { t } = require("intlayer");
|
|
674
|
-
|
|
675
|
-
/** @type {import('intlayer').Dictionary} */
|
|
676
|
-
const pageContent = {
|
|
677
|
-
key: "page",
|
|
678
|
-
content: {
|
|
679
|
-
getStarted: {
|
|
680
|
-
main: t({
|
|
681
|
-
en: "Get started by editing",
|
|
682
|
-
fr: "Commencez par éditer",
|
|
683
|
-
es: "Comience por editar",
|
|
684
|
-
}),
|
|
685
|
-
pageLink: "src/app/page.tsx",
|
|
686
|
-
},
|
|
687
|
-
docs: {
|
|
688
|
-
title: t({
|
|
689
|
-
en: "Documentation",
|
|
690
|
-
fr: "Documentation",
|
|
691
|
-
es: "Documentación",
|
|
692
|
-
}),
|
|
693
|
-
description: t({
|
|
694
|
-
en: "Find in-depth information about Next.js features and API.",
|
|
695
|
-
fr: "Trouvez des informations détaillées sur les fonctionnalités et l'API de Next.js.",
|
|
696
|
-
es: "Encuentre información detallada sobre las características y la API de Next.js.",
|
|
697
|
-
}),
|
|
698
|
-
},
|
|
699
|
-
},
|
|
700
|
-
};
|
|
701
|
-
|
|
702
|
-
module.exports = pageContent;
|
|
703
|
-
```
|
|
704
|
-
|
|
705
|
-
```json fileName="src/app/[locale]/page.content.json" contentDeclarationFormat="json"
|
|
706
|
-
{
|
|
707
|
-
"$schema": "https://intlayer.org/schema.json",
|
|
708
|
-
"key": "page",
|
|
709
|
-
"content": {
|
|
710
|
-
"getStarted": {
|
|
711
|
-
"main": {
|
|
712
|
-
"nodeType": "translation",
|
|
713
|
-
"translation": {
|
|
714
|
-
"en": "Get started by editing",
|
|
715
|
-
"fr": "Commencez par éditer",
|
|
716
|
-
"es": "Comience por editar"
|
|
717
|
-
}
|
|
718
|
-
},
|
|
719
|
-
"pageLink": "src/app/page.tsx"
|
|
720
|
-
},
|
|
721
|
-
"docs": {
|
|
722
|
-
"title": {
|
|
723
|
-
"nodeType": "translation",
|
|
724
|
-
"translation": {
|
|
725
|
-
"en": "Documentation",
|
|
726
|
-
"fr": "Documentation",
|
|
727
|
-
"es": "Documentación"
|
|
728
|
-
}
|
|
729
|
-
},
|
|
730
|
-
"description": {
|
|
731
|
-
"nodeType": "translation",
|
|
732
|
-
"translation": {
|
|
733
|
-
"en": "Find in-depth information about Next.js features and API.",
|
|
734
|
-
"fr": "Trouvez des informations détaillées sur les fonctionnalités et l'API de Next.js.",
|
|
735
|
-
"es": "Encuentre información detallada sobre las características y la API de Next.js."
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
```
|
|
742
|
-
|
|
743
|
-
> **Intlayer vs. i18next**: With i18next, you would need to create separate JSON files in a `locales/` folder. With Intlayer, content lives right next to your component, making it easier to maintain and refactor.
|
|
744
|
-
|
|
745
|
-
> 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.{json,ts,tsx,js,jsx,mjs,mjx,cjs,cjx}`).
|
|
746
|
-
|
|
747
|
-
For more details, refer to the [content declaration documentation](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/dictionary/content_file.md).
|
|
748
|
-
|
|
749
|
-
### Step 6: Utilize Content in Your Code
|
|
750
|
-
|
|
751
|
-
Access your content dictionaries throughout your application using the `useIntlayer` hook:
|
|
752
|
-
|
|
753
|
-
```tsx fileName="src/app/[locale]/page.tsx" codeFormat="typescript"
|
|
754
|
-
import type { FC } from "react";
|
|
755
|
-
import { ClientComponentExample } from "@components/ClientComponentExample";
|
|
756
|
-
import { ServerComponentExample } from "@components/ServerComponentExample";
|
|
757
|
-
import { type NextPageIntlayer, IntlayerClientProvider } from "next-intlayer";
|
|
758
|
-
import { IntlayerServerProvider, useIntlayer } from "next-intlayer/server";
|
|
759
|
-
|
|
760
|
-
const PageContent: FC = () => {
|
|
761
|
-
const content = useIntlayer("page");
|
|
762
|
-
|
|
763
|
-
return (
|
|
764
|
-
<>
|
|
765
|
-
<h1>{content.getStarted.main}</h1>
|
|
766
|
-
<code>{content.getStarted.pageLink}</code>
|
|
767
|
-
|
|
768
|
-
<h2>{content.docs.title}</h2>
|
|
769
|
-
<p>{content.docs.description}</p>
|
|
770
|
-
</>
|
|
771
|
-
);
|
|
772
|
-
};
|
|
773
|
-
|
|
774
|
-
const Page: NextPageIntlayer = async ({ params }) => {
|
|
775
|
-
const { locale } = await params;
|
|
776
|
-
|
|
777
|
-
return (
|
|
778
|
-
<IntlayerServerProvider locale={locale}>
|
|
779
|
-
<PageContent />
|
|
780
|
-
<ServerComponentExample />
|
|
781
|
-
|
|
782
|
-
<IntlayerClientProvider locale={locale}>
|
|
783
|
-
<ClientComponentExample />
|
|
784
|
-
</IntlayerClientProvider>
|
|
785
|
-
</IntlayerServerProvider>
|
|
786
|
-
);
|
|
787
|
-
};
|
|
788
|
-
|
|
789
|
-
export default Page;
|
|
790
|
-
```
|
|
791
|
-
|
|
792
|
-
```jsx fileName="src/app/[locale]/page.mjx" codeFormat="esm"
|
|
793
|
-
import { ClientComponentExample } from "@components/ClientComponentExample";
|
|
794
|
-
import { ServerComponentExample } from "@components/ServerComponentExample";
|
|
795
|
-
import { IntlayerClientProvider } from "next-intlayer";
|
|
796
|
-
import { IntlayerServerProvider, useIntlayer } from "next-intlayer/server";
|
|
797
|
-
|
|
798
|
-
const PageContent = () => {
|
|
799
|
-
const content = useIntlayer("page");
|
|
800
|
-
|
|
801
|
-
return (
|
|
802
|
-
<>
|
|
803
|
-
<h1>{content.getStarted.main}</h1>
|
|
804
|
-
<code>{content.getStarted.pageLink}</code>
|
|
805
|
-
|
|
806
|
-
<h2>{content.docs.title}</h2>
|
|
807
|
-
<p>{content.docs.description}</p>
|
|
808
|
-
</>
|
|
809
|
-
);
|
|
810
|
-
};
|
|
811
|
-
|
|
812
|
-
const Page = async ({ params }) => {
|
|
813
|
-
const { locale } = await params;
|
|
814
|
-
|
|
815
|
-
return (
|
|
816
|
-
<IntlayerServerProvider locale={locale}>
|
|
817
|
-
<PageContent />
|
|
818
|
-
<ServerComponentExample />
|
|
819
|
-
|
|
820
|
-
<IntlayerClientProvider locale={locale}>
|
|
821
|
-
<ClientComponentExample />
|
|
822
|
-
</IntlayerClientProvider>
|
|
823
|
-
</IntlayerServerProvider>
|
|
824
|
-
);
|
|
825
|
-
};
|
|
826
|
-
|
|
827
|
-
export default Page;
|
|
828
|
-
```
|
|
829
|
-
|
|
830
|
-
```jsx fileName="src/app/[locale]/page.csx" codeFormat="commonjs"
|
|
831
|
-
const {
|
|
832
|
-
ClientComponentExample,
|
|
833
|
-
} = require("@components/ClientComponentExample");
|
|
834
|
-
const {
|
|
835
|
-
ServerComponentExample,
|
|
836
|
-
} = require("@components/ServerComponentExample");
|
|
837
|
-
const { IntlayerClientProvider } = require("next-intlayer");
|
|
838
|
-
const { IntlayerServerProvider, useIntlayer } = require("next-intlayer/server");
|
|
839
|
-
|
|
840
|
-
const PageContent = () => {
|
|
841
|
-
const content = useIntlayer("page");
|
|
842
|
-
|
|
843
|
-
return (
|
|
844
|
-
<>
|
|
845
|
-
<h1>{content.getStarted.main}</h1>
|
|
846
|
-
<code>{content.getStarted.pageLink}</code>
|
|
847
|
-
|
|
848
|
-
<h2>{content.docs.title}</h2>
|
|
849
|
-
<p>{content.docs.description}</p>
|
|
850
|
-
</>
|
|
851
|
-
);
|
|
852
|
-
};
|
|
853
|
-
|
|
854
|
-
const Page = async ({ params }) => {
|
|
855
|
-
const { locale } = await params;
|
|
856
|
-
|
|
857
|
-
return (
|
|
858
|
-
<IntlayerServerProvider locale={locale}>
|
|
859
|
-
<PageContent />
|
|
860
|
-
<ServerComponentExample />
|
|
861
|
-
|
|
862
|
-
<IntlayerClientProvider locale={locale}>
|
|
863
|
-
<ClientComponentExample />
|
|
864
|
-
</IntlayerClientProvider>
|
|
865
|
-
</IntlayerServerProvider>
|
|
866
|
-
);
|
|
867
|
-
};
|
|
868
|
-
|
|
869
|
-
module.exports = Page;
|
|
870
|
-
```
|
|
871
|
-
|
|
872
|
-
**Key points:**
|
|
873
|
-
|
|
874
|
-
- **`IntlayerClientProvider`**: Provides the locale to client-side components. Can be placed in any parent component, but recommended in layouts for efficiency.
|
|
875
|
-
- **`IntlayerServerProvider`**: Provides the locale to server children. Cannot be set in the layout due to React's cache mechanism.
|
|
876
|
-
- **`useIntlayer("page")`**: Retrieves the content for the `page` dictionary key with full TypeScript support.
|
|
877
|
-
|
|
878
|
-
**Client Component Example:**
|
|
879
|
-
|
|
880
|
-
```tsx fileName="src/components/ClientComponentExample.tsx" codeFormat="typescript"
|
|
881
|
-
"use client";
|
|
882
|
-
|
|
883
|
-
import type { FC } from "react";
|
|
884
|
-
import { useIntlayer } from "next-intlayer";
|
|
885
|
-
|
|
886
|
-
export const ClientComponentExample: FC = () => {
|
|
887
|
-
const content = useIntlayer("client-component-example");
|
|
888
|
-
|
|
889
|
-
return (
|
|
890
|
-
<div>
|
|
891
|
-
<h2>{content.title}</h2>
|
|
892
|
-
<p>{content.description}</p>
|
|
893
|
-
</div>
|
|
894
|
-
);
|
|
895
|
-
};
|
|
896
|
-
```
|
|
897
|
-
|
|
898
|
-
```jsx fileName="src/components/ClientComponentExample.mjx" codeFormat="esm"
|
|
899
|
-
"use client";
|
|
900
|
-
|
|
901
|
-
import { useIntlayer } from "next-intlayer";
|
|
902
|
-
|
|
903
|
-
export const ClientComponentExample = () => {
|
|
904
|
-
const content = useIntlayer("client-component-example");
|
|
905
|
-
|
|
906
|
-
return (
|
|
907
|
-
<div>
|
|
908
|
-
<h2>{content.title}</h2>
|
|
909
|
-
<p>{content.description}</p>
|
|
910
|
-
</div>
|
|
911
|
-
);
|
|
912
|
-
};
|
|
913
|
-
```
|
|
914
|
-
|
|
915
|
-
```jsx fileName="src/components/ClientComponentExample.csx" codeFormat="commonjs"
|
|
916
|
-
"use client";
|
|
917
|
-
|
|
918
|
-
const { useIntlayer } = require("next-intlayer");
|
|
919
|
-
|
|
920
|
-
const ClientComponentExample = () => {
|
|
921
|
-
const content = useIntlayer("client-component-example");
|
|
922
|
-
|
|
923
|
-
return (
|
|
924
|
-
<div>
|
|
925
|
-
<h2>{content.title}</h2>
|
|
926
|
-
<p>{content.description}</p>
|
|
927
|
-
</div>
|
|
928
|
-
);
|
|
929
|
-
};
|
|
930
|
-
|
|
931
|
-
exports.ClientComponentExample = ClientComponentExample;
|
|
932
|
-
```
|
|
933
|
-
|
|
934
|
-
**Server Component Example:**
|
|
935
|
-
|
|
936
|
-
```tsx fileName="src/components/ServerComponentExample.tsx" codeFormat="typescript"
|
|
937
|
-
import type { FC } from "react";
|
|
938
|
-
import { useIntlayer } from "next-intlayer/server";
|
|
939
|
-
|
|
940
|
-
export const ServerComponentExample: FC = () => {
|
|
941
|
-
const content = useIntlayer("server-component-example");
|
|
942
|
-
|
|
943
|
-
return (
|
|
944
|
-
<div>
|
|
945
|
-
<h2>{content.title}</h2>
|
|
946
|
-
<p>{content.description}</p>
|
|
947
|
-
</div>
|
|
948
|
-
);
|
|
949
|
-
};
|
|
950
|
-
```
|
|
951
|
-
|
|
952
|
-
```jsx fileName="src/components/ServerComponentExample.mjx" codeFormat="esm"
|
|
953
|
-
import { useIntlayer } from "next-intlayer/server";
|
|
954
|
-
|
|
955
|
-
export const ServerComponentExample = () => {
|
|
956
|
-
const content = useIntlayer("server-component-example");
|
|
957
|
-
|
|
958
|
-
return (
|
|
959
|
-
<div>
|
|
960
|
-
<h2>{content.title}</h2>
|
|
961
|
-
<p>{content.description}</p>
|
|
962
|
-
</div>
|
|
963
|
-
);
|
|
964
|
-
};
|
|
965
|
-
```
|
|
966
|
-
|
|
967
|
-
```jsx fileName="src/components/ServerComponentExample.csx" codeFormat="commonjs"
|
|
968
|
-
const { useIntlayer } = require("next-intlayer/server");
|
|
969
|
-
|
|
970
|
-
const ServerComponentExample = () => {
|
|
971
|
-
const content = useIntlayer("server-component-example");
|
|
972
|
-
|
|
973
|
-
return (
|
|
974
|
-
<div>
|
|
975
|
-
<h2>{content.title}</h2>
|
|
976
|
-
<p>{content.description}</p>
|
|
977
|
-
</div>
|
|
978
|
-
);
|
|
979
|
-
};
|
|
980
|
-
|
|
981
|
-
exports.ServerComponentExample = ServerComponentExample;
|
|
982
|
-
```
|
|
983
|
-
|
|
984
|
-
> If you want to use your content in a `string` attribute, such as `alt`, `title`, `href`, `aria-label`, etc., you must call the value of the function:
|
|
985
|
-
>
|
|
986
|
-
> ```jsx
|
|
987
|
-
> <img src={content.image.src.value} alt={content.image.alt.value} />
|
|
988
|
-
> ```
|
|
989
|
-
|
|
990
|
-
> To learn more about the `useIntlayer` hook, refer to the [documentation](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/packages/next-intlayer/useIntlayer.md).
|
|
991
|
-
|
|
992
|
-
### (Optional) Step 7: Configure Proxy for Locale Detection
|
|
993
|
-
|
|
994
|
-
Set up proxy to automatically detect and redirect users to their preferred locale:
|
|
995
|
-
|
|
996
|
-
```typescript fileName="src/proxy.ts" codeFormat="typescript"
|
|
997
|
-
export { intlayerProxy as proxy } from "next-intlayer/proxy";
|
|
998
|
-
|
|
999
|
-
export const config = {
|
|
1000
|
-
matcher:
|
|
1001
|
-
"/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\\..*|_next).*)",
|
|
1002
|
-
};
|
|
1003
|
-
```
|
|
1004
|
-
|
|
1005
|
-
```javascript fileName="src/proxy.mjs" codeFormat="esm"
|
|
1006
|
-
export { intlayerProxy as proxy } from "next-intlayer/proxy";
|
|
1007
|
-
|
|
1008
|
-
export const config = {
|
|
1009
|
-
matcher:
|
|
1010
|
-
"/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\\..*|_next).*)",
|
|
1011
|
-
};
|
|
1012
|
-
```
|
|
1013
|
-
|
|
1014
|
-
```javascript fileName="src/proxy.cjs" codeFormat="commonjs"
|
|
1015
|
-
const { intlayerProxy } = require("next-intlayer/proxy");
|
|
1016
|
-
|
|
1017
|
-
const config = {
|
|
1018
|
-
matcher:
|
|
1019
|
-
"/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\\..*|_next).*)",
|
|
1020
|
-
};
|
|
1021
|
-
|
|
1022
|
-
module.exports = { proxy: intlayerProxy, config };
|
|
1023
|
-
```
|
|
1024
|
-
|
|
1025
|
-
> The `intlayerProxy` detects the user's preferred locale from browser headers and cookies, then redirects them to the appropriate URL. It also saves the preference in a cookie for future visits.
|
|
1026
|
-
|
|
1027
|
-
### (Optional) Step 8: Internationalization of Your Metadata
|
|
1028
|
-
|
|
1029
|
-
To internationalize metadata (page titles, descriptions, etc.), use the `generateMetadata` function with `getIntlayer`:
|
|
1030
|
-
|
|
1031
|
-
```typescript fileName="src/app/[locale]/metadata.content.ts" contentDeclarationFormat="typescript"
|
|
1032
|
-
import { type Dictionary, t } from "intlayer";
|
|
1033
|
-
import type { Metadata } from "next";
|
|
1034
|
-
|
|
1035
|
-
const metadataContent = {
|
|
1036
|
-
key: "page-metadata",
|
|
1037
|
-
content: {
|
|
1038
|
-
title: t({
|
|
1039
|
-
en: "My Website - Home",
|
|
1040
|
-
fr: "Mon Site Web - Accueil",
|
|
1041
|
-
es: "Mi Sitio Web - Inicio",
|
|
1042
|
-
}),
|
|
1043
|
-
description: t({
|
|
1044
|
-
en: "Welcome to my multilingual website built with Intlayer and Next.js",
|
|
1045
|
-
fr: "Bienvenue sur mon site web multilingue construit avec Intlayer et Next.js",
|
|
1046
|
-
es: "Bienvenido a mi sitio web multilingüe construido con Intlayer y Next.js",
|
|
1047
|
-
}),
|
|
1048
|
-
},
|
|
1049
|
-
} satisfies Dictionary<Metadata>;
|
|
1050
|
-
|
|
1051
|
-
export default metadataContent;
|
|
1052
|
-
```
|
|
1053
|
-
|
|
1054
|
-
```javascript fileName="src/app/[locale]/metadata.content.mjs" contentDeclarationFormat="esm"
|
|
1055
|
-
import { t } from "intlayer";
|
|
1056
|
-
|
|
1057
|
-
/** @type {import('intlayer').Dictionary<import('next').Metadata>} */
|
|
1058
|
-
const metadataContent = {
|
|
1059
|
-
key: "page-metadata",
|
|
1060
|
-
content: {
|
|
1061
|
-
title: t({
|
|
1062
|
-
en: "My Website - Home",
|
|
1063
|
-
fr: "Mon Site Web - Accueil",
|
|
1064
|
-
es: "Mi Sitio Web - Inicio",
|
|
1065
|
-
}),
|
|
1066
|
-
description: t({
|
|
1067
|
-
en: "Welcome to my multilingual website built with Intlayer and Next.js",
|
|
1068
|
-
fr: "Bienvenue sur mon site web multilingue construit avec Intlayer et Next.js",
|
|
1069
|
-
es: "Bienvenido a mi sitio web multilingüe construido con Intlayer y Next.js",
|
|
1070
|
-
}),
|
|
1071
|
-
},
|
|
1072
|
-
};
|
|
1073
|
-
|
|
1074
|
-
export default metadataContent;
|
|
1075
|
-
```
|
|
1076
|
-
|
|
1077
|
-
```javascript fileName="src/app/[locale]/metadata.content.cjs" contentDeclarationFormat="commonjs"
|
|
1078
|
-
const { t } = require("intlayer");
|
|
1079
|
-
|
|
1080
|
-
/** @type {import('intlayer').Dictionary<import('next').Metadata>} */
|
|
1081
|
-
const metadataContent = {
|
|
1082
|
-
key: "page-metadata",
|
|
1083
|
-
content: {
|
|
1084
|
-
title: t({
|
|
1085
|
-
en: "My Website - Home",
|
|
1086
|
-
fr: "Mon Site Web - Accueil",
|
|
1087
|
-
es: "Mi Sitio Web - Inicio",
|
|
1088
|
-
}),
|
|
1089
|
-
description: t({
|
|
1090
|
-
en: "Welcome to my multilingual website built with Intlayer and Next.js",
|
|
1091
|
-
fr: "Bienvenue sur mon site web multilingue construit avec Intlayer et Next.js",
|
|
1092
|
-
es: "Bienvenido a mi sitio web multilingüe construido con Intlayer y Next.js",
|
|
1093
|
-
}),
|
|
1094
|
-
},
|
|
1095
|
-
};
|
|
1096
|
-
|
|
1097
|
-
module.exports = metadataContent;
|
|
1098
|
-
```
|
|
1099
|
-
|
|
1100
|
-
```json fileName="src/app/[locale]/metadata.content.json" contentDeclarationFormat="json"
|
|
1101
|
-
{
|
|
1102
|
-
"$schema": "https://intlayer.org/schema.json",
|
|
1103
|
-
"key": "page-metadata",
|
|
1104
|
-
"content": {
|
|
1105
|
-
"title": {
|
|
1106
|
-
"nodeType": "translation",
|
|
1107
|
-
"translation": {
|
|
1108
|
-
"en": "My Website - Home",
|
|
1109
|
-
"fr": "Mon Site Web - Accueil",
|
|
1110
|
-
"es": "Mi Sitio Web - Inicio"
|
|
1111
|
-
}
|
|
1112
|
-
},
|
|
1113
|
-
"description": {
|
|
1114
|
-
"nodeType": "translation",
|
|
1115
|
-
"translation": {
|
|
1116
|
-
"en": "Welcome to my multilingual website built with Intlayer and Next.js",
|
|
1117
|
-
"fr": "Bienvenue sur mon site web multilingue construit avec Intlayer et Next.js",
|
|
1118
|
-
"es": "Bienvenido a mi sitio web multilingüe construido con Intlayer y Next.js"
|
|
1119
|
-
}
|
|
1120
|
-
}
|
|
1121
|
-
}
|
|
1122
|
-
}
|
|
1123
|
-
```
|
|
1124
|
-
|
|
1125
|
-
Then use it in your layout or page:
|
|
1126
|
-
|
|
1127
|
-
```typescript fileName="src/app/[locale]/layout.tsx or src/app/[locale]/page.tsx" codeFormat="typescript"
|
|
1128
|
-
import { getIntlayer, getMultilingualUrls } from "intlayer";
|
|
1129
|
-
import type { Metadata } from "next";
|
|
1130
|
-
import type { LocalPromiseParams } from "next-intlayer";
|
|
1131
|
-
|
|
1132
|
-
export const generateMetadata = async ({
|
|
1133
|
-
params,
|
|
1134
|
-
}: LocalPromiseParams): Promise<Metadata> => {
|
|
1135
|
-
const { locale } = await params;
|
|
1136
|
-
const metadata = getIntlayer("page-metadata", locale);
|
|
1137
|
-
|
|
1138
|
-
const multilingualUrls = getMultilingualUrls("/");
|
|
1139
|
-
const localizedUrl =
|
|
1140
|
-
multilingualUrls[locale as keyof typeof multilingualUrls];
|
|
1141
|
-
|
|
1142
|
-
return {
|
|
1143
|
-
...metadata,
|
|
1144
|
-
alternates: {
|
|
1145
|
-
canonical: localizedUrl,
|
|
1146
|
-
languages: { ...multilingualUrls, "x-default": "/" },
|
|
1147
|
-
},
|
|
1148
|
-
openGraph: {
|
|
1149
|
-
url: localizedUrl,
|
|
1150
|
-
},
|
|
1151
|
-
};
|
|
1152
|
-
};
|
|
1153
|
-
|
|
1154
|
-
// ... Rest of your layout or page code
|
|
1155
|
-
```
|
|
1156
|
-
|
|
1157
|
-
```javascript fileName="src/app/[locale]/layout.mjs or src/app/[locale]/page.mjs" codeFormat="esm"
|
|
1158
|
-
import { getIntlayer, getMultilingualUrls } from "intlayer";
|
|
1159
|
-
|
|
1160
|
-
export const generateMetadata = async ({ params }) => {
|
|
1161
|
-
const { locale } = await params;
|
|
1162
|
-
const metadata = getIntlayer("page-metadata", locale);
|
|
1163
|
-
|
|
1164
|
-
const multilingualUrls = getMultilingualUrls("/");
|
|
1165
|
-
const localizedUrl = multilingualUrls[locale];
|
|
1166
|
-
|
|
1167
|
-
return {
|
|
1168
|
-
...metadata,
|
|
1169
|
-
alternates: {
|
|
1170
|
-
canonical: localizedUrl,
|
|
1171
|
-
languages: { ...multilingualUrls, "x-default": "/" },
|
|
1172
|
-
},
|
|
1173
|
-
openGraph: {
|
|
1174
|
-
url: localizedUrl,
|
|
1175
|
-
},
|
|
1176
|
-
};
|
|
1177
|
-
};
|
|
1178
|
-
|
|
1179
|
-
// ... Rest of your layout or page code
|
|
1180
|
-
```
|
|
1181
|
-
|
|
1182
|
-
```javascript fileName="src/app/[locale]/layout.cjs or src/app/[locale]/page.cjs" codeFormat="commonjs"
|
|
1183
|
-
const { getIntlayer, getMultilingualUrls } = require("intlayer");
|
|
1184
|
-
|
|
1185
|
-
const generateMetadata = async ({ params }) => {
|
|
1186
|
-
const { locale } = await params;
|
|
1187
|
-
const metadata = getIntlayer("page-metadata", locale);
|
|
1188
|
-
|
|
1189
|
-
const multilingualUrls = getMultilingualUrls("/");
|
|
1190
|
-
const localizedUrl = multilingualUrls[locale];
|
|
1191
|
-
|
|
1192
|
-
return {
|
|
1193
|
-
...metadata,
|
|
1194
|
-
alternates: {
|
|
1195
|
-
canonical: localizedUrl,
|
|
1196
|
-
languages: { ...multilingualUrls, "x-default": "/" },
|
|
1197
|
-
},
|
|
1198
|
-
openGraph: {
|
|
1199
|
-
url: localizedUrl,
|
|
1200
|
-
},
|
|
1201
|
-
};
|
|
1202
|
-
};
|
|
1203
|
-
|
|
1204
|
-
module.exports = { generateMetadata };
|
|
1205
|
-
|
|
1206
|
-
// ... Rest of your layout or page code
|
|
1207
|
-
```
|
|
1208
|
-
|
|
1209
|
-
> Learn more about Next.js metadata optimization in the [official Next.js documentation](https://nextjs.org/docs/app/building-your-application/optimizing/metadata).
|
|
1210
|
-
|
|
1211
|
-
### (Optional) Step 9: Internationalization of sitemap.xml and robots.txt
|
|
1212
|
-
|
|
1213
|
-
Internationalize your `sitemap.xml` and `robots.txt` files:
|
|
1214
|
-
|
|
1215
|
-
```tsx fileName="src/app/sitemap.ts" codeFormat="typescript"
|
|
1216
|
-
import { getMultilingualUrls } from "intlayer";
|
|
1217
|
-
import type { MetadataRoute } from "next";
|
|
1218
|
-
|
|
1219
|
-
const sitemap = (): MetadataRoute.Sitemap => [
|
|
1220
|
-
{
|
|
1221
|
-
url: "https://example.com",
|
|
1222
|
-
alternates: {
|
|
1223
|
-
languages: { ...getMultilingualUrls("https://example.com") },
|
|
1224
|
-
},
|
|
1225
|
-
},
|
|
1226
|
-
{
|
|
1227
|
-
url: "https://example.com/about",
|
|
1228
|
-
alternates: {
|
|
1229
|
-
languages: { ...getMultilingualUrls("https://example.com/about") },
|
|
1230
|
-
},
|
|
1231
|
-
},
|
|
1232
|
-
{
|
|
1233
|
-
url: "https://example.com/contact",
|
|
1234
|
-
alternates: {
|
|
1235
|
-
languages: { ...getMultilingualUrls("https://example.com/contact") },
|
|
1236
|
-
},
|
|
1237
|
-
},
|
|
1238
|
-
];
|
|
1239
|
-
|
|
1240
|
-
export default sitemap;
|
|
1241
|
-
```
|
|
1242
|
-
|
|
1243
|
-
```jsx fileName="src/app/sitemap.mjx" codeFormat="esm"
|
|
1244
|
-
import { getMultilingualUrls } from "intlayer";
|
|
1245
|
-
|
|
1246
|
-
const sitemap = () => [
|
|
1247
|
-
{
|
|
1248
|
-
url: "https://example.com",
|
|
1249
|
-
alternates: {
|
|
1250
|
-
languages: { ...getMultilingualUrls("https://example.com") },
|
|
1251
|
-
},
|
|
1252
|
-
},
|
|
1253
|
-
{
|
|
1254
|
-
url: "https://example.com/about",
|
|
1255
|
-
alternates: {
|
|
1256
|
-
languages: { ...getMultilingualUrls("https://example.com/about") },
|
|
1257
|
-
},
|
|
1258
|
-
},
|
|
1259
|
-
{
|
|
1260
|
-
url: "https://example.com/contact",
|
|
1261
|
-
alternates: {
|
|
1262
|
-
languages: { ...getMultilingualUrls("https://example.com/contact") },
|
|
1263
|
-
},
|
|
1264
|
-
},
|
|
1265
|
-
];
|
|
1266
|
-
|
|
1267
|
-
export default sitemap;
|
|
1268
|
-
```
|
|
1269
|
-
|
|
1270
|
-
```jsx fileName="src/app/sitemap.csx" codeFormat="commonjs"
|
|
1271
|
-
const { getMultilingualUrls } = require("intlayer");
|
|
1272
|
-
|
|
1273
|
-
const sitemap = () => [
|
|
1274
|
-
{
|
|
1275
|
-
url: "https://example.com",
|
|
1276
|
-
alternates: {
|
|
1277
|
-
languages: { ...getMultilingualUrls("https://example.com") },
|
|
1278
|
-
},
|
|
1279
|
-
},
|
|
1280
|
-
{
|
|
1281
|
-
url: "https://example.com/about",
|
|
1282
|
-
alternates: {
|
|
1283
|
-
languages: { ...getMultilingualUrls("https://example.com/about") },
|
|
1284
|
-
},
|
|
1285
|
-
},
|
|
1286
|
-
{
|
|
1287
|
-
url: "https://example.com/contact",
|
|
1288
|
-
alternates: {
|
|
1289
|
-
languages: { ...getMultilingualUrls("https://example.com/contact") },
|
|
1290
|
-
},
|
|
1291
|
-
},
|
|
1292
|
-
];
|
|
1293
|
-
|
|
1294
|
-
module.exports = sitemap;
|
|
1295
|
-
```
|
|
1296
|
-
|
|
1297
|
-
```tsx fileName="src/app/robots.ts" codeFormat="typescript"
|
|
1298
|
-
import type { MetadataRoute } from "next";
|
|
1299
|
-
import { getMultilingualUrls } from "intlayer";
|
|
1300
|
-
|
|
1301
|
-
const getAllMultilingualUrls = (urls: string[]) =>
|
|
1302
|
-
urls.flatMap((url) => Object.values(getMultilingualUrls(url)) as string[]);
|
|
100
|
+
The `syncJSON` plugin will automatically wrap the JSON. It will read and write the JSON files without changing the content architecture.
|
|
1303
101
|
|
|
1304
|
-
|
|
1305
|
-
rules: {
|
|
1306
|
-
userAgent: "*",
|
|
1307
|
-
allow: ["/"],
|
|
1308
|
-
disallow: getAllMultilingualUrls(["/admin", "/private"]),
|
|
1309
|
-
},
|
|
1310
|
-
host: "https://example.com",
|
|
1311
|
-
sitemap: `https://example.com/sitemap.xml`,
|
|
1312
|
-
});
|
|
102
|
+
If you want to make coexist that JSON with intlayer content declaration files (`.content` files), Intlayer will proceed this way:
|
|
1313
103
|
|
|
1314
|
-
|
|
1315
|
-
|
|
104
|
+
1. load both JSON and content declaration files and transform them into a intlayer dictionary.
|
|
105
|
+
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).
|
|
1316
106
|
|
|
1317
|
-
|
|
1318
|
-
import { getMultilingualUrls } from "intlayer";
|
|
107
|
+
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.
|
|
1319
108
|
|
|
1320
|
-
|
|
1321
|
-
urls.flatMap((url) => Object.values(getMultilingualUrls(url)));
|
|
109
|
+
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).
|
|
1322
110
|
|
|
1323
|
-
|
|
1324
|
-
rules: {
|
|
1325
|
-
userAgent: "*",
|
|
1326
|
-
allow: ["/"],
|
|
1327
|
-
disallow: getAllMultilingualUrls(["/admin", "/private"]),
|
|
1328
|
-
},
|
|
1329
|
-
host: "https://example.com",
|
|
1330
|
-
sitemap: `https://example.com/sitemap.xml`,
|
|
1331
|
-
});
|
|
1332
|
-
|
|
1333
|
-
export default robots;
|
|
1334
|
-
```
|
|
1335
|
-
|
|
1336
|
-
```jsx fileName="src/app/robots.csx" codeFormat="commonjs"
|
|
1337
|
-
const { getMultilingualUrls } = require("intlayer");
|
|
1338
|
-
|
|
1339
|
-
const getAllMultilingualUrls = (urls) =>
|
|
1340
|
-
urls.flatMap((url) => Object.values(getMultilingualUrls(url)));
|
|
1341
|
-
|
|
1342
|
-
const robots = () => ({
|
|
1343
|
-
rules: {
|
|
1344
|
-
userAgent: "*",
|
|
1345
|
-
allow: ["/"],
|
|
1346
|
-
disallow: getAllMultilingualUrls(["/admin", "/private"]),
|
|
1347
|
-
},
|
|
1348
|
-
host: "https://example.com",
|
|
1349
|
-
sitemap: `https://example.com/sitemap.xml`,
|
|
1350
|
-
});
|
|
1351
|
-
|
|
1352
|
-
module.exports = robots;
|
|
1353
|
-
```
|
|
1354
|
-
|
|
1355
|
-
> Learn more about sitemap optimization in the [Next.js sitemap documentation](https://nextjs.org/docs/app/api-reference/file-conventions/metadata/sitemap) and about robots.txt in the [Next.js robots.txt documentation](https://nextjs.org/docs/app/api-reference/file-conventions/metadata/robots).
|
|
1356
|
-
|
|
1357
|
-
### (Optional) Step 10: Change the Language of Your Content
|
|
1358
|
-
|
|
1359
|
-
To allow users to switch languages, use the `useLocale` hook and Next.js's `Link` component:
|
|
1360
|
-
|
|
1361
|
-
```tsx fileName="src/components/LocaleSwitcher.tsx" codeFormat="typescript"
|
|
1362
|
-
"use client";
|
|
1363
|
-
|
|
1364
|
-
import type { FC } from "react";
|
|
1365
|
-
import {
|
|
1366
|
-
Locales,
|
|
1367
|
-
getHTMLTextDir,
|
|
1368
|
-
getLocaleName,
|
|
1369
|
-
getLocalizedUrl,
|
|
1370
|
-
} from "intlayer";
|
|
1371
|
-
import { useLocale } from "next-intlayer";
|
|
1372
|
-
import Link from "next/link";
|
|
1373
|
-
|
|
1374
|
-
export const LocaleSwitcher: FC = () => {
|
|
1375
|
-
const { locale, pathWithoutLocale, availableLocales, setLocale } =
|
|
1376
|
-
useLocale();
|
|
1377
|
-
|
|
1378
|
-
return (
|
|
1379
|
-
<div>
|
|
1380
|
-
<button popoverTarget="localePopover">{getLocaleName(locale)}</button>
|
|
1381
|
-
<div id="localePopover" popover="auto">
|
|
1382
|
-
{availableLocales.map((localeItem) => (
|
|
1383
|
-
<Link
|
|
1384
|
-
href={getLocalizedUrl(pathWithoutLocale, localeItem)}
|
|
1385
|
-
key={localeItem}
|
|
1386
|
-
aria-current={locale === localeItem ? "page" : undefined}
|
|
1387
|
-
onClick={() => setLocale(localeItem)}
|
|
1388
|
-
replace // Ensures that "go back" browser button redirects to previous page
|
|
1389
|
-
>
|
|
1390
|
-
<span>
|
|
1391
|
-
{/* Locale - e.g. FR */}
|
|
1392
|
-
{localeItem}
|
|
1393
|
-
</span>
|
|
1394
|
-
<span>
|
|
1395
|
-
{/* Language in its own Locale - e.g. Français */}
|
|
1396
|
-
{getLocaleName(localeItem, locale)}
|
|
1397
|
-
</span>
|
|
1398
|
-
<span dir={getHTMLTextDir(localeItem)} lang={localeItem}>
|
|
1399
|
-
{/* Language in current Locale - e.g. Francés with current locale set to Locales.SPANISH */}
|
|
1400
|
-
{getLocaleName(localeItem)}
|
|
1401
|
-
</span>
|
|
1402
|
-
<span dir="ltr" lang={Locales.ENGLISH}>
|
|
1403
|
-
{/* Language in English - e.g. French */}
|
|
1404
|
-
{getLocaleName(localeItem, Locales.ENGLISH)}
|
|
1405
|
-
</span>
|
|
1406
|
-
</Link>
|
|
1407
|
-
))}
|
|
1408
|
-
</div>
|
|
1409
|
-
</div>
|
|
1410
|
-
);
|
|
1411
|
-
};
|
|
1412
|
-
```
|
|
1413
|
-
|
|
1414
|
-
```jsx fileName="src/components/LocaleSwitcher.mjx" codeFormat="esm"
|
|
1415
|
-
"use client";
|
|
1416
|
-
|
|
1417
|
-
import {
|
|
1418
|
-
Locales,
|
|
1419
|
-
getHTMLTextDir,
|
|
1420
|
-
getLocaleName,
|
|
1421
|
-
getLocalizedUrl,
|
|
1422
|
-
} from "intlayer";
|
|
1423
|
-
import { useLocale } from "next-intlayer";
|
|
1424
|
-
import Link from "next/link";
|
|
1425
|
-
|
|
1426
|
-
export const LocaleSwitcher = () => {
|
|
1427
|
-
const { locale, pathWithoutLocale, availableLocales, setLocale } =
|
|
1428
|
-
useLocale();
|
|
1429
|
-
|
|
1430
|
-
return (
|
|
1431
|
-
<div>
|
|
1432
|
-
<button popoverTarget="localePopover">{getLocaleName(locale)}</button>
|
|
1433
|
-
<div id="localePopover" popover="auto">
|
|
1434
|
-
{availableLocales.map((localeItem) => (
|
|
1435
|
-
<Link
|
|
1436
|
-
href={getLocalizedUrl(pathWithoutLocale, localeItem)}
|
|
1437
|
-
key={localeItem}
|
|
1438
|
-
aria-current={locale === localeItem ? "page" : undefined}
|
|
1439
|
-
onClick={() => setLocale(localeItem)}
|
|
1440
|
-
replace
|
|
1441
|
-
>
|
|
1442
|
-
<span>{localeItem}</span>
|
|
1443
|
-
<span>{getLocaleName(localeItem, locale)}</span>
|
|
1444
|
-
<span dir={getHTMLTextDir(localeItem)} lang={localeItem}>
|
|
1445
|
-
{getLocaleName(localeItem)}
|
|
1446
|
-
</span>
|
|
1447
|
-
<span dir="ltr" lang={Locales.ENGLISH}>
|
|
1448
|
-
{getLocaleName(localeItem, Locales.ENGLISH)}
|
|
1449
|
-
</span>
|
|
1450
|
-
</Link>
|
|
1451
|
-
))}
|
|
1452
|
-
</div>
|
|
1453
|
-
</div>
|
|
1454
|
-
);
|
|
1455
|
-
};
|
|
1456
|
-
```
|
|
1457
|
-
|
|
1458
|
-
```jsx fileName="src/components/LocaleSwitcher.csx" codeFormat="commonjs"
|
|
1459
|
-
"use client";
|
|
1460
|
-
|
|
1461
|
-
const {
|
|
1462
|
-
Locales,
|
|
1463
|
-
getHTMLTextDir,
|
|
1464
|
-
getLocaleName,
|
|
1465
|
-
getLocalizedUrl,
|
|
1466
|
-
} = require("intlayer");
|
|
1467
|
-
const { useLocale } = require("next-intlayer");
|
|
1468
|
-
const Link = require("next/link");
|
|
1469
|
-
|
|
1470
|
-
const LocaleSwitcher = () => {
|
|
1471
|
-
const { locale, pathWithoutLocale, availableLocales, setLocale } =
|
|
1472
|
-
useLocale();
|
|
1473
|
-
|
|
1474
|
-
return (
|
|
1475
|
-
<div>
|
|
1476
|
-
<button popoverTarget="localePopover">{getLocaleName(locale)}</button>
|
|
1477
|
-
<div id="localePopover" popover="auto">
|
|
1478
|
-
{availableLocales.map((localeItem) => (
|
|
1479
|
-
<Link
|
|
1480
|
-
href={getLocalizedUrl(pathWithoutLocale, localeItem)}
|
|
1481
|
-
key={localeItem}
|
|
1482
|
-
aria-current={locale === localeItem ? "page" : undefined}
|
|
1483
|
-
onClick={() => setLocale(localeItem)}
|
|
1484
|
-
replace
|
|
1485
|
-
>
|
|
1486
|
-
<span>{localeItem}</span>
|
|
1487
|
-
<span>{getLocaleName(localeItem, locale)}</span>
|
|
1488
|
-
<span dir={getHTMLTextDir(localeItem)} lang={localeItem}>
|
|
1489
|
-
{getLocaleName(localeItem)}
|
|
1490
|
-
</span>
|
|
1491
|
-
<span dir="ltr" lang={Locales.ENGLISH}>
|
|
1492
|
-
{getLocaleName(localeItem, Locales.ENGLISH)}
|
|
1493
|
-
</span>
|
|
1494
|
-
</Link>
|
|
1495
|
-
))}
|
|
1496
|
-
</div>
|
|
1497
|
-
</div>
|
|
1498
|
-
);
|
|
1499
|
-
};
|
|
1500
|
-
|
|
1501
|
-
exports.LocaleSwitcher = LocaleSwitcher;
|
|
1502
|
-
```
|
|
111
|
+
## Git Configuration
|
|
1503
112
|
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
> Documentation references:
|
|
1507
|
-
>
|
|
1508
|
-
> - [`useLocale` hook](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/packages/next-intlayer/useLocale.md)
|
|
1509
|
-
> - [`getLocaleName` hook](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/packages/intlayer/getLocaleName.md)
|
|
1510
|
-
> - [`getLocalizedUrl` hook](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/packages/intlayer/getLocalizedUrl.md)
|
|
1511
|
-
> - [`getHTMLTextDir` hook](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/packages/intlayer/getHTMLTextDir.md)
|
|
1512
|
-
|
|
1513
|
-
### (Optional) Step 11: Using i18next Alongside Intlayer
|
|
1514
|
-
|
|
1515
|
-
If you need to maintain i18next compatibility during a gradual migration, you can configure i18next to load dictionaries exported by Intlayer:
|
|
1516
|
-
|
|
1517
|
-
```typescript fileName="i18n/client.ts" codeFormat="typescript"
|
|
1518
|
-
import i18next from "i18next";
|
|
1519
|
-
import { initReactI18next } from "react-i18next";
|
|
1520
|
-
import resourcesToBackend from "i18next-resources-to-backend";
|
|
1521
|
-
|
|
1522
|
-
i18next
|
|
1523
|
-
.use(initReactI18next)
|
|
1524
|
-
.use(
|
|
1525
|
-
resourcesToBackend(
|
|
1526
|
-
(language: string, namespace: string) =>
|
|
1527
|
-
import(`../intl/messages/${language}/${namespace}.json`)
|
|
1528
|
-
)
|
|
1529
|
-
)
|
|
1530
|
-
.init({
|
|
1531
|
-
fallbackLng: "en",
|
|
1532
|
-
debug: process.env.NODE_ENV === "development",
|
|
1533
|
-
interpolation: {
|
|
1534
|
-
escapeValue: false,
|
|
1535
|
-
},
|
|
1536
|
-
});
|
|
1537
|
-
|
|
1538
|
-
export default i18next;
|
|
1539
|
-
```
|
|
1540
|
-
|
|
1541
|
-
```javascript fileName="i18n/client.mjs" codeFormat="esm"
|
|
1542
|
-
import i18next from "i18next";
|
|
1543
|
-
import { initReactI18next } from "react-i18next";
|
|
1544
|
-
import resourcesToBackend from "i18next-resources-to-backend";
|
|
1545
|
-
|
|
1546
|
-
i18next
|
|
1547
|
-
.use(initReactI18next)
|
|
1548
|
-
.use(
|
|
1549
|
-
resourcesToBackend(
|
|
1550
|
-
(language, namespace) =>
|
|
1551
|
-
import(`../intl/messages/${language}/${namespace}.json`)
|
|
1552
|
-
)
|
|
1553
|
-
)
|
|
1554
|
-
.init({
|
|
1555
|
-
fallbackLng: "en",
|
|
1556
|
-
debug: process.env.NODE_ENV === "development",
|
|
1557
|
-
interpolation: {
|
|
1558
|
-
escapeValue: false,
|
|
1559
|
-
},
|
|
1560
|
-
});
|
|
1561
|
-
|
|
1562
|
-
export default i18next;
|
|
1563
|
-
```
|
|
1564
|
-
|
|
1565
|
-
```javascript fileName="i18n/client.cjs" codeFormat="commonjs"
|
|
1566
|
-
const i18next = require("i18next");
|
|
1567
|
-
const { initReactI18next } = require("react-i18next");
|
|
1568
|
-
const resourcesToBackend = require("i18next-resources-to-backend");
|
|
1569
|
-
|
|
1570
|
-
i18next
|
|
1571
|
-
.use(initReactI18next)
|
|
1572
|
-
.use(
|
|
1573
|
-
resourcesToBackend(
|
|
1574
|
-
(language, namespace) =>
|
|
1575
|
-
import(`../intl/messages/${language}/${namespace}.json`)
|
|
1576
|
-
)
|
|
1577
|
-
)
|
|
1578
|
-
.init({
|
|
1579
|
-
fallbackLng: "en",
|
|
1580
|
-
debug: process.env.NODE_ENV === "development",
|
|
1581
|
-
interpolation: {
|
|
1582
|
-
escapeValue: false,
|
|
1583
|
-
},
|
|
1584
|
-
});
|
|
1585
|
-
|
|
1586
|
-
module.exports = i18next;
|
|
1587
|
-
```
|
|
1588
|
-
|
|
1589
|
-
**Usage in a component:**
|
|
1590
|
-
|
|
1591
|
-
```tsx fileName="src/components/LegacyComponent.tsx"
|
|
1592
|
-
"use client";
|
|
1593
|
-
|
|
1594
|
-
import { useTranslation } from "react-i18next";
|
|
1595
|
-
|
|
1596
|
-
export const LegacyComponent = () => {
|
|
1597
|
-
const { t } = useTranslation();
|
|
1598
|
-
|
|
1599
|
-
return (
|
|
1600
|
-
<div>
|
|
1601
|
-
<h2>{t("page:docs.title")}</h2>
|
|
1602
|
-
<p>{t("page:docs.description")}</p>
|
|
1603
|
-
</div>
|
|
1604
|
-
);
|
|
1605
|
-
};
|
|
1606
|
-
```
|
|
1607
|
-
|
|
1608
|
-
This approach allows you to:
|
|
1609
|
-
|
|
1610
|
-
- Use Intlayer's modern content declaration system
|
|
1611
|
-
- Gradually migrate components from i18next to Intlayer
|
|
1612
|
-
- Maintain compatibility with existing i18next code
|
|
1613
|
-
- Export dictionaries in both formats during the transition
|
|
1614
|
-
|
|
1615
|
-
---
|
|
1616
|
-
|
|
1617
|
-
## Migration Strategy: From i18next to Intlayer
|
|
1618
|
-
|
|
1619
|
-
If you're migrating an existing application from i18next to Intlayer, follow this strategy:
|
|
1620
|
-
|
|
1621
|
-
### Phase 1: Setup (Week 1)
|
|
1622
|
-
|
|
1623
|
-
1. **Install Intlayer** alongside your existing i18next setup
|
|
1624
|
-
2. **Configure Intlayer** with the `syncJSON` plugin to export dictionaries
|
|
1625
|
-
3. **Test** that both systems work in parallel
|
|
1626
|
-
|
|
1627
|
-
### Phase 2: Gradual Migration (Weeks 2-N)
|
|
1628
|
-
|
|
1629
|
-
1. **Create Intlayer content files** for new features
|
|
1630
|
-
2. **Migrate existing components** one at a time:
|
|
1631
|
-
- Create corresponding `.content.ts` files
|
|
1632
|
-
- Replace `useTranslation()` with `useIntlayer()`
|
|
1633
|
-
- Test thoroughly before moving to the next component
|
|
1634
|
-
3. **Focus on high-value components** first (frequently edited, complex translations)
|
|
1635
|
-
|
|
1636
|
-
### Phase 3: Cleanup (Final Week)
|
|
1637
|
-
|
|
1638
|
-
1. **Remove i18next dependencies** once all components are migrated
|
|
1639
|
-
2. **Delete old locale JSON files**
|
|
1640
|
-
3. **Remove the `syncJSON` plugin** from your Intlayer config
|
|
1641
|
-
4. **Update documentation** for your team
|
|
1642
|
-
|
|
1643
|
-
### Migration Checklist
|
|
1644
|
-
|
|
1645
|
-
- [ ] Intlayer installed and configured
|
|
1646
|
-
- [ ] `syncJSON` plugin configured for i18next compatibility
|
|
1647
|
-
- [ ] First component migrated and tested
|
|
1648
|
-
- [ ] Team trained on Intlayer patterns
|
|
1649
|
-
- [ ] 25% of components migrated
|
|
1650
|
-
- [ ] 50% of components migrated
|
|
1651
|
-
- [ ] 75% of components migrated
|
|
1652
|
-
- [ ] 100% of components migrated
|
|
1653
|
-
- [ ] i18next removed
|
|
1654
|
-
- [ ] Documentation updated
|
|
1655
|
-
|
|
1656
|
-
---
|
|
1657
|
-
|
|
1658
|
-
## Advanced Configuration
|
|
1659
|
-
|
|
1660
|
-
### TypeScript Configuration
|
|
1661
|
-
|
|
1662
|
-
Ensure your TypeScript configuration includes the auto-generated types:
|
|
1663
|
-
|
|
1664
|
-
```json5 fileName="tsconfig.json"
|
|
1665
|
-
{
|
|
1666
|
-
// ... Your existing TypeScript configurations
|
|
1667
|
-
"include": [
|
|
1668
|
-
// ... Your existing TypeScript configurations
|
|
1669
|
-
".intlayer/**/*.ts", // Include the auto-generated types
|
|
1670
|
-
],
|
|
1671
|
-
}
|
|
1672
|
-
```
|
|
1673
|
-
|
|
1674
|
-
### Git Configuration
|
|
1675
|
-
|
|
1676
|
-
Ignore the generated files:
|
|
113
|
+
It's recommended to ignore auto-generated Intlayer files:
|
|
1677
114
|
|
|
1678
115
|
```plaintext fileName=".gitignore"
|
|
1679
|
-
# Ignore
|
|
116
|
+
# Ignore files generated by Intlayer
|
|
1680
117
|
.intlayer
|
|
1681
118
|
```
|
|
1682
119
|
|
|
120
|
+
These files can be regenerated during your build process and don't need to be committed to version control.
|
|
121
|
+
|
|
1683
122
|
### VS Code Extension
|
|
1684
123
|
|
|
1685
124
|
For improved developer experience, install the official **Intlayer VS Code Extension**:
|
|
1686
125
|
|
|
1687
126
|
[Install from the VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=intlayer.intlayer-vs-code-extension)
|
|
1688
|
-
|
|
1689
|
-
This extension provides:
|
|
1690
|
-
|
|
1691
|
-
- **Autocompletion** for translation keys
|
|
1692
|
-
- **Real-time error detection** for missing translations
|
|
1693
|
-
- **Inline previews** of translated content
|
|
1694
|
-
- **Quick actions** for creating and updating translations
|
|
1695
|
-
|
|
1696
|
-
> **Comparison with i18next**: While i18next has some IDE extensions, Intlayer's extension provides deeper integration with TypeScript and better auto-completion thanks to co-located content declarations.
|
|
1697
|
-
|
|
1698
|
-
---
|
|
1699
|
-
|
|
1700
|
-
## Conclusion
|
|
1701
|
-
|
|
1702
|
-
By following this comprehensive guide, you now have a fully internationalized Next.js application using Intlayer, with optional i18next compatibility for gradual migration.
|
|
1703
|
-
|
|
1704
|
-
**Key Takeaways:**
|
|
1705
|
-
|
|
1706
|
-
1. **Intlayer** offers a modern, developer-friendly approach to i18n that addresses many limitations of traditional solutions like i18next
|
|
1707
|
-
2. **Co-located content** declarations improve maintainability and reduce errors
|
|
1708
|
-
3. **First-class TypeScript support** provides excellent IDE integration and catches errors at build time
|
|
1709
|
-
4. **Flexible migration** path allows gradual adoption without disrupting existing applications
|
|
1710
|
-
5. **Built-in Next.js support** makes it the ideal choice for modern React applications
|
|
1711
|
-
|
|
1712
|
-
Whether you're starting a new project or migrating from i18next, Intlayer provides the tools and flexibility you need for a scalable, maintainable internationalization solution.
|
|
1713
|
-
|
|
1714
|
-
---
|
|
1715
|
-
|
|
1716
|
-
## Further Resources
|
|
1717
|
-
|
|
1718
|
-
- [Intlayer Documentation](https://intlayer.org)
|
|
1719
|
-
- [Intlayer GitHub Repository](https://github.com/aymericzip/intlayer)
|
|
1720
|
-
- [Next.js Internationalization Guide](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/intlayer_with_nextjs_14.md)
|
|
1721
|
-
- [Intlayer Visual Editor](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/intlayer_visual_editor.md)
|
|
1722
|
-
- [Intlayer CMS](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/intlayer_CMS.md)
|
|
1723
|
-
- [i18next Documentation](https://www.i18next.com/)
|
|
1724
|
-
- [react-i18next Documentation](https://react.i18next.com/)
|
|
1725
|
-
|
|
1726
|
-
<function_calls>
|
|
1727
|
-
<invoke name="read_file">
|
|
1728
|
-
<parameter name="target_file">docs/docs/en/intlayer_with_nextjs_16.md
|