@intlayer/docs 6.1.4 → 6.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/blog/ar/next-i18next_vs_next-intl_vs_intlayer.md +1135 -75
- package/blog/ar/nextjs-multilingual-seo-comparison.md +364 -0
- package/blog/de/next-i18next_vs_next-intl_vs_intlayer.md +1139 -72
- package/blog/de/nextjs-multilingual-seo-comparison.md +362 -0
- package/blog/en/next-i18next_vs_next-intl_vs_intlayer.md +224 -240
- package/blog/en/nextjs-multilingual-seo-comparison.md +360 -0
- package/blog/en-GB/next-i18next_vs_next-intl_vs_intlayer.md +1134 -37
- package/blog/en-GB/nextjs-multilingual-seo-comparison.md +360 -0
- package/blog/es/next-i18next_vs_next-intl_vs_intlayer.md +1122 -64
- package/blog/es/nextjs-multilingual-seo-comparison.md +363 -0
- package/blog/fr/next-i18next_vs_next-intl_vs_intlayer.md +1132 -75
- package/blog/fr/nextjs-multilingual-seo-comparison.md +362 -0
- package/blog/hi/nextjs-multilingual-seo-comparison.md +363 -0
- package/blog/it/next-i18next_vs_next-intl_vs_intlayer.md +1120 -55
- package/blog/it/nextjs-multilingual-seo-comparison.md +363 -0
- package/blog/ja/next-i18next_vs_next-intl_vs_intlayer.md +1140 -76
- package/blog/ja/nextjs-multilingual-seo-comparison.md +362 -0
- package/blog/ko/next-i18next_vs_next-intl_vs_intlayer.md +1129 -73
- package/blog/ko/nextjs-multilingual-seo-comparison.md +362 -0
- package/blog/pt/next-i18next_vs_next-intl_vs_intlayer.md +1133 -76
- package/blog/pt/nextjs-multilingual-seo-comparison.md +362 -0
- package/blog/ru/next-i18next_vs_next-intl_vs_intlayer.md +1142 -74
- package/blog/ru/nextjs-multilingual-seo-comparison.md +370 -0
- package/blog/tr/nextjs-multilingual-seo-comparison.md +362 -0
- package/blog/zh/next-i18next_vs_next-intl_vs_intlayer.md +1142 -75
- package/blog/zh/nextjs-multilingual-seo-comparison.md +394 -0
- package/dist/cjs/generated/blog.entry.cjs +16 -0
- package/dist/cjs/generated/blog.entry.cjs.map +1 -1
- package/dist/esm/generated/blog.entry.mjs +16 -0
- package/dist/esm/generated/blog.entry.mjs.map +1 -1
- package/dist/types/generated/blog.entry.d.ts +1 -0
- package/dist/types/generated/blog.entry.d.ts.map +1 -1
- package/docs/en/interest_of_intlayer.md +2 -2
- package/package.json +10 -10
- package/src/generated/blog.entry.ts +16 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
createdAt: 2025-08-23
|
|
3
|
-
updatedAt: 2025-09-
|
|
3
|
+
updatedAt: 2025-09-29
|
|
4
4
|
title: next-i18next vs next-intl vs Intlayer
|
|
5
5
|
description: Compare next-i18next with next-intl and Intlayer for the internationalization (i18n) of a Next.js app
|
|
6
6
|
keywords:
|
|
@@ -47,12 +47,12 @@ We focus on **Next.js 13+ App Router** (with **React Server Components**) and ev
|
|
|
47
47
|
|
|
48
48
|
---
|
|
49
49
|
|
|
50
|
-
| Library | GitHub Stars
|
|
51
|
-
| ---------------------- |
|
|
52
|
-
| `aymericzip/intlayer` | [](https://github.com/aymericzip/intlayer/stargazers) | [](https://github.com/aymericzip/intlayer/commits) | [](https://github.com/aymericzip/intlayer/commits) | April 2024 | [](https://www.npmjs.com/package/intlayer) | [](https://www.npmjs.com/package/intlayer) |
|
|
53
|
+
| `amannn/next-intl` | [](https://github.com/amannn/next-intl/stargazers) | [](https://github.com/amannn/next-intl/commits) | [](https://github.com/amannn/next-intl/commits) | Nov 2020 | [](https://www.npmjs.com/package/next-intl) | [](https://www.npmjs.com/package/next-intl) |
|
|
54
|
+
| `i18next/i18next` | [](https://github.com/i18next/i18next/stargazers) | [](https://github.com/i18next/i18next/commits) | [](https://github.com/i18next/i18next/commits) | Jan 2012 | [](https://www.npmjs.com/package/i18next) | [](https://www.npmjs.com/package/i18next) |
|
|
55
|
+
| `i18next/next-i18next` | [](https://github.com/i18next/next-i18next/stargazers) | [](https://github.com/i18next/next-i18next/commits) | [](https://github.com/i18next/next-i18next/commits) | Nov 2018 | [](https://www.npmjs.com/package/next-i18next) | [](https://www.npmjs.com/package/next-i18next) |
|
|
56
56
|
|
|
57
57
|
> Badges update automatically. Snapshots will vary over time.
|
|
58
58
|
|
|
@@ -140,6 +140,7 @@ Two important issues:
|
|
|
140
140
|
> If I'm on the `/about` page, I don't want to load the content of the `/home` page
|
|
141
141
|
|
|
142
142
|
- **Splitting by locale:**
|
|
143
|
+
|
|
143
144
|
> If I'm on the `/fr/about` page, I don't want to load the content of the `/en/about` page
|
|
144
145
|
|
|
145
146
|
Again, all three solutions are aware of these issues and allow managing these optimizations. The difference between the three solutions is the DX (Developer Experience).
|
|
@@ -170,6 +171,173 @@ Here an example of the impact of bundle size optimization using `intlayer` in a
|
|
|
170
171
|
|
|
171
172
|
---
|
|
172
173
|
|
|
174
|
+
## TypeScript & safety
|
|
175
|
+
|
|
176
|
+
<Columns>
|
|
177
|
+
<Column>
|
|
178
|
+
|
|
179
|
+
**next-intl**
|
|
180
|
+
|
|
181
|
+
- Solid TypeScript support, but **keys aren’t strictly typed by default**; you’ll maintain safety patterns manually.
|
|
182
|
+
|
|
183
|
+
</Column>
|
|
184
|
+
<Column>
|
|
185
|
+
|
|
186
|
+
**next-i18next**
|
|
187
|
+
|
|
188
|
+
- Base typings for hooks; **strict key typing requires extra tooling/config**.
|
|
189
|
+
|
|
190
|
+
</Column>
|
|
191
|
+
<Column>
|
|
192
|
+
|
|
193
|
+
**intlayer**
|
|
194
|
+
|
|
195
|
+
- **Generates strict types** from your content. **IDE autocompletion** and **compile-time errors** catch typos and missing keys before deploy.
|
|
196
|
+
|
|
197
|
+
</Column>
|
|
198
|
+
</Columns>
|
|
199
|
+
|
|
200
|
+
**Why it matters:** Strong typing shifts failures **left** (CI/build) instead of **right** (runtime).
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Missing translation handling
|
|
205
|
+
|
|
206
|
+
<Columns>
|
|
207
|
+
<Column>
|
|
208
|
+
|
|
209
|
+
**next-intl**
|
|
210
|
+
|
|
211
|
+
- Relies on **runtime fallbacks** (e.g., show the key or default locale). Build doesn’t fail.
|
|
212
|
+
|
|
213
|
+
</Column>
|
|
214
|
+
<Column>
|
|
215
|
+
|
|
216
|
+
**next-i18next**
|
|
217
|
+
|
|
218
|
+
- Relies on **runtime fallbacks** (e.g., show the key or default locale). Build doesn’t fail.
|
|
219
|
+
|
|
220
|
+
</Column>
|
|
221
|
+
<Column>
|
|
222
|
+
|
|
223
|
+
**intlayer**
|
|
224
|
+
|
|
225
|
+
- **Build-time detection** with **warnings/errors** for missing locales or keys.
|
|
226
|
+
|
|
227
|
+
</Column>
|
|
228
|
+
</Columns>
|
|
229
|
+
|
|
230
|
+
**Why it matters:** Catching gaps during build prevents “mystery strings” in production and aligns with strict release gates.
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Routing, middleware & URL strategy
|
|
235
|
+
|
|
236
|
+
<Columns>
|
|
237
|
+
<Column>
|
|
238
|
+
|
|
239
|
+
**next-intl**
|
|
240
|
+
|
|
241
|
+
- Works with **Next.js localized routing** on the App Router.
|
|
242
|
+
|
|
243
|
+
</Column>
|
|
244
|
+
<Column>
|
|
245
|
+
|
|
246
|
+
**next-i18next**
|
|
247
|
+
|
|
248
|
+
- Works with **Next.js localized routing** on the App Router.
|
|
249
|
+
|
|
250
|
+
</Column>
|
|
251
|
+
<Column>
|
|
252
|
+
|
|
253
|
+
**intlayer**
|
|
254
|
+
|
|
255
|
+
- All of the above, plus **i18n middleware** (locale detection via headers/cookies) and **helpers** to generate localized URLs and `<link rel="alternate" hreflang="…">` tags.
|
|
256
|
+
|
|
257
|
+
</Column>
|
|
258
|
+
</Columns>
|
|
259
|
+
|
|
260
|
+
**Why it matters:** Fewer custom glue layers; **consistent UX** and **clean SEO** across locales.
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Server Components (RSC) alignment
|
|
265
|
+
|
|
266
|
+
<Columns>
|
|
267
|
+
<Column>
|
|
268
|
+
|
|
269
|
+
**next-intl**
|
|
270
|
+
|
|
271
|
+
- Supports Next.js 13+. Often requires passing t-functions/formatters through component trees in hybrid setups.
|
|
272
|
+
|
|
273
|
+
</Column>
|
|
274
|
+
<Column>
|
|
275
|
+
|
|
276
|
+
**next-i18next**
|
|
277
|
+
|
|
278
|
+
- Supports Next.js 13+. Similar constraints with passing translation utilities across boundaries.
|
|
279
|
+
|
|
280
|
+
</Column>
|
|
281
|
+
<Column>
|
|
282
|
+
|
|
283
|
+
**intlayer**
|
|
284
|
+
|
|
285
|
+
- Supports Next.js 13+ and smooths the **server/client boundary** with a consistent API and RSC-oriented providers, avoiding shuttling formatters or t-functions.
|
|
286
|
+
|
|
287
|
+
</Column>
|
|
288
|
+
</Columns>
|
|
289
|
+
|
|
290
|
+
**Why it matters:** Cleaner mental model and fewer edge cases in hybrid trees.
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## DX, tooling & maintenance
|
|
295
|
+
|
|
296
|
+
<Columns>
|
|
297
|
+
<Column>
|
|
298
|
+
|
|
299
|
+
**next-intl**
|
|
300
|
+
|
|
301
|
+
- Commonly paired with external localization platforms and editorial workflows.
|
|
302
|
+
|
|
303
|
+
</Column>
|
|
304
|
+
<Column>
|
|
305
|
+
|
|
306
|
+
**next-i18next**
|
|
307
|
+
|
|
308
|
+
- Commonly paired with external localization platforms and editorial workflows.
|
|
309
|
+
|
|
310
|
+
</Column>
|
|
311
|
+
<Column>
|
|
312
|
+
|
|
313
|
+
**intlayer**
|
|
314
|
+
|
|
315
|
+
- Ships a **free Visual Editor** and **optional CMS** (Git-friendly or externalized), plus a **VSCode extension** and **AI-assisted translations** using your own provider keys.
|
|
316
|
+
|
|
317
|
+
</Column>
|
|
318
|
+
</Columns>
|
|
319
|
+
|
|
320
|
+
**Why it matters:** Lowers ops cost and shortens the loop between developers and content authors.
|
|
321
|
+
|
|
322
|
+
## Integration with localization platforms (TMS)
|
|
323
|
+
|
|
324
|
+
Large organizations often rely on Translation Management Systems (TMS) like **Crowdin**, **Phrase**, **Lokalise**, **Localizely**, or **Localazy**.
|
|
325
|
+
|
|
326
|
+
- **Why companies care**
|
|
327
|
+
- **Collaboration & roles**: Multiple actors are involved: developers, product managers, translators, reviewers, marketing teams.
|
|
328
|
+
- **Scale & efficiency**: continuous localization, in‑context review.
|
|
329
|
+
|
|
330
|
+
- **next-intl / next-i18next**
|
|
331
|
+
- Typically use **centralized JSON catalogs**, so export/import with TMS is straightforward.
|
|
332
|
+
- Mature ecosystems and examples/integrations for the platforms above.
|
|
333
|
+
|
|
334
|
+
- **Intlayer**
|
|
335
|
+
- Encourages **decentralized, per-component dictionaries** and supports **TypeScript/TSX/JS/JSON/MD** content.
|
|
336
|
+
- This improves modularity in code, but can make plug‑and‑play TMS integration harder when a tool expects centralized, flat JSON files.
|
|
337
|
+
- Intlayer provides alternatives: **AI‑assisted translations** (using your own provider keys), a **Visual Editor/CMS**, and **CLI/CI** workflows to catch and prefill gaps.
|
|
338
|
+
|
|
339
|
+
> Note: `next-intl` and `i18next` also accepts TypeScript catalogs. If your team stores messages in `.ts` files or decentralizes them by feature, you can face similar TMS friction. However, many `next-intl` setups remain centralized in a `locales/` folder, which is a bit easier to refactor to JSON for TMS.
|
|
340
|
+
|
|
173
341
|
## Developer Experience
|
|
174
342
|
|
|
175
343
|
This part makes a deep comparison between the three solutions. Rather than considering simple cases, as described in the 'getting started' documentation for each solution, we will consider a real use case, more similar to a real project.
|
|
@@ -206,7 +374,7 @@ The app structure is important to ensure good maintainability for your codebase.
|
|
|
206
374
|
```
|
|
207
375
|
|
|
208
376
|
</TabItem>
|
|
209
|
-
|
|
377
|
+
<TabItem label="next-intl" value="next-intl">
|
|
210
378
|
|
|
211
379
|
```bash
|
|
212
380
|
.
|
|
@@ -253,54 +421,8 @@ The app structure is important to ensure good maintainability for your codebase.
|
|
|
253
421
|
|
|
254
422
|
#### Comparison
|
|
255
423
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
Intlayer uses a centralized configuration file to set up your locale, middleware, build, etc.
|
|
259
|
-
|
|
260
|
-
##### Content declaration
|
|
261
|
-
|
|
262
|
-
The centralized type of architecture slows down the development process and makes the codebase more complex to maintain for several reasons:
|
|
263
|
-
|
|
264
|
-
1. **For any new component created, you should:**
|
|
265
|
-
- Create the new resource/namespace in the `locales` folder
|
|
266
|
-
- Remember to import the new namespace in your page
|
|
267
|
-
- Translate your content (often done manually by copy/paste from AI providers)
|
|
268
|
-
|
|
269
|
-
2. **For any change made on your components, you should:**
|
|
270
|
-
- Search for the related resource/namespace (far from the component)
|
|
271
|
-
- Translate your content
|
|
272
|
-
- Ensure your content is up to date for any locale
|
|
273
|
-
- Verify your namespace doesn't include unused keys/values
|
|
274
|
-
- Ensure the structure of your JSON files is the same for all locales
|
|
275
|
-
|
|
276
|
-
On professional projects using these solutions, localization platforms are often used to help manage the translation of your content. However, this can quickly become costly for large projects.
|
|
277
|
-
|
|
278
|
-
To solve this problem, Intlayer adopts an approach that scopes your content per-component and keeps your content close to your component, as we often do with CSS (`styled-components`), types, documentation (`storybook`), or unit tests (`jest`).
|
|
279
|
-
|
|
280
|
-
This approach allows you to:
|
|
281
|
-
|
|
282
|
-
1. **Increase the speed of development**
|
|
283
|
-
- `.content.{{ts|js|json}}` files can be created using a VSCode extension
|
|
284
|
-
- Autocompletion AI tools in your IDE (such as GitHub Copilot) can help you declare your content, reducing copy/paste
|
|
285
|
-
|
|
286
|
-
2. **Clean your codebase**
|
|
287
|
-
- Reduce the complexity
|
|
288
|
-
- Increase the maintainability
|
|
289
|
-
|
|
290
|
-
3. **Duplicate your components and their related content more easily (Example: login/register components, etc.)**
|
|
291
|
-
- By limiting the risk of impacting other components' content
|
|
292
|
-
- By copy/pasting your content from one application to another without external dependencies
|
|
293
|
-
|
|
294
|
-
4. **Avoid polluting your codebase with unused keys/values for unused components**
|
|
295
|
-
- If you don't use a component, Intlayer will not import its related content
|
|
296
|
-
- If you delete a component, you'll more easily remember to remove its related content as it will be present in the same folder
|
|
297
|
-
|
|
298
|
-
5. **Reduce reasoning cost for AI agents to declare your multilingual content**
|
|
299
|
-
- The AI agent won't have to scan your entire codebase to know where to implement your content
|
|
300
|
-
- Translations can easily be done by autocompletion AI tools in your IDE (such as GitHub Copilot)
|
|
301
|
-
|
|
302
|
-
6. **Optimize loading performance**
|
|
303
|
-
- If a component is lazy-loaded, its related content will be loaded at the same time
|
|
424
|
+
- **next-intl / next-i18next**: Centralized catalogs (JSON; namespaces/messages). Clear structure, integrates well with translation platforms, but can lead to more cross-file edits as apps grow.
|
|
425
|
+
- **Intlayer**: Per-component `.content.{ts|js|json}` dictionaries co-located with components. Easier component reuse and local reasoning; adds files and relies on build-time tooling.
|
|
304
426
|
|
|
305
427
|
#### Setup and Loading Content
|
|
306
428
|
|
|
@@ -335,7 +457,7 @@ import { I18nextProvider, initReactI18next } from "react-i18next";
|
|
|
335
457
|
import { createInstance } from "i18next";
|
|
336
458
|
import { ClientComponent, ServerComponent } from "@components";
|
|
337
459
|
|
|
338
|
-
export default function HomePage() {
|
|
460
|
+
export default function HomePage({ locale }: { locale: string }) {
|
|
339
461
|
// Déclarez explicitement le namespace utilisé par ce composant
|
|
340
462
|
const resources = await loadMessagesFor(locale); // your loader (JSON, etc.)
|
|
341
463
|
|
|
@@ -394,7 +516,7 @@ export default getRequestConfig(async ({ locale }) => {
|
|
|
394
516
|
|
|
395
517
|
```tsx fileName="src/app/[locale]/about/layout.tsx"
|
|
396
518
|
import { NextIntlClientProvider } from "next-intl";
|
|
397
|
-
import { getMessages } from "next-intl/server";
|
|
519
|
+
import { getMessages, unstable_setRequestLocale } from "next-intl/server";
|
|
398
520
|
import pick from "lodash/pick";
|
|
399
521
|
|
|
400
522
|
export default async function LocaleLayout({
|
|
@@ -405,9 +527,13 @@ export default async function LocaleLayout({
|
|
|
405
527
|
params: { locale: string };
|
|
406
528
|
}) {
|
|
407
529
|
const { locale } = params;
|
|
408
|
-
|
|
409
|
-
//
|
|
410
|
-
|
|
530
|
+
|
|
531
|
+
// Set the active request locale for this server render (RSC)
|
|
532
|
+
unstable_setRequestLocale(locale);
|
|
533
|
+
|
|
534
|
+
// Messages are loaded server-side via src/i18n/request.ts
|
|
535
|
+
// (see next-intl docs). Here we only push a subset to the client
|
|
536
|
+
// that's needed for client components (payload optimization).
|
|
411
537
|
const messages = await getMessages();
|
|
412
538
|
const clientMessages = pick(messages, ["common", "about"]);
|
|
413
539
|
|
|
@@ -511,13 +637,13 @@ export default LandingPage;
|
|
|
511
637
|
|
|
512
638
|
#### Comparison
|
|
513
639
|
|
|
514
|
-
|
|
640
|
+
All three support per-locale content loading and providers.
|
|
515
641
|
|
|
516
|
-
|
|
642
|
+
- With **next-intl/next-i18next**, you typically load selected messages/namespaces per route and place providers where needed.
|
|
517
643
|
|
|
518
|
-
|
|
644
|
+
- With **Intlayer**, adds build-time analysis to infer usage, which can reduce manual wiring and may allow a single root provider.
|
|
519
645
|
|
|
520
|
-
|
|
646
|
+
Choose between explicit control and automation based on team preference.
|
|
521
647
|
|
|
522
648
|
### Usage in a client component
|
|
523
649
|
|
|
@@ -554,7 +680,7 @@ Let's take an example of a client component rendering a counter.
|
|
|
554
680
|
import React, { useMemo, useState } from "react";
|
|
555
681
|
import { useTranslation } from "next-i18next";
|
|
556
682
|
|
|
557
|
-
|
|
683
|
+
const ClientComponentExample = () => {
|
|
558
684
|
const { t, i18n } = useTranslation("about");
|
|
559
685
|
const [count, setCount] = useState(0);
|
|
560
686
|
|
|
@@ -572,7 +698,7 @@ export default function ClientComponentExample() {
|
|
|
572
698
|
</button>
|
|
573
699
|
</div>
|
|
574
700
|
);
|
|
575
|
-
}
|
|
701
|
+
};
|
|
576
702
|
```
|
|
577
703
|
|
|
578
704
|
> Don't forget to add "about" namespace on the page serverSideTranslations
|
|
@@ -609,7 +735,7 @@ export default function ClientComponentExample() {
|
|
|
609
735
|
import React, { useState } from "react";
|
|
610
736
|
import { useTranslations, useFormatter } from "next-intl";
|
|
611
737
|
|
|
612
|
-
|
|
738
|
+
const ClientComponentExample = () => {
|
|
613
739
|
// Scope directly to the nested object
|
|
614
740
|
const t = useTranslations("about.counter");
|
|
615
741
|
const format = useFormatter();
|
|
@@ -626,7 +752,7 @@ export default function ClientComponentExample() {
|
|
|
626
752
|
</button>
|
|
627
753
|
</div>
|
|
628
754
|
);
|
|
629
|
-
}
|
|
755
|
+
};
|
|
630
756
|
```
|
|
631
757
|
|
|
632
758
|
> Don't forget to add "about" message on the page client message
|
|
@@ -658,7 +784,7 @@ export default counterContent;
|
|
|
658
784
|
import React, { useState } from "react";
|
|
659
785
|
import { useNumber, useIntlayer } from "next-intlayer";
|
|
660
786
|
|
|
661
|
-
|
|
787
|
+
const ClientComponentExample = () => {
|
|
662
788
|
const [count, setCount] = useState(0);
|
|
663
789
|
const { label, increment } = useIntlayer("counter"); // returns strings
|
|
664
790
|
const { number } = useNumber();
|
|
@@ -671,7 +797,7 @@ export default function ClientComponentExample() {
|
|
|
671
797
|
</button>
|
|
672
798
|
</div>
|
|
673
799
|
);
|
|
674
|
-
}
|
|
800
|
+
};
|
|
675
801
|
```
|
|
676
802
|
|
|
677
803
|
</TabItem>
|
|
@@ -702,62 +828,52 @@ We will take the case of a UI component. This component is a server component, a
|
|
|
702
828
|
<TabItem label="next-i18next" value="next-i18next">
|
|
703
829
|
|
|
704
830
|
```tsx fileName="src/pages/about.tsx"
|
|
705
|
-
import React from "react";
|
|
706
831
|
import type { GetStaticProps } from "next";
|
|
707
832
|
import { useTranslation } from "next-i18next";
|
|
708
|
-
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
|
|
709
833
|
|
|
710
834
|
type ServerComponentProps = {
|
|
711
835
|
count: number;
|
|
712
|
-
t: (key: string) => string;
|
|
713
|
-
format: (value: number) => string;
|
|
714
836
|
};
|
|
715
837
|
|
|
716
|
-
|
|
717
|
-
t,
|
|
718
|
-
format
|
|
719
|
-
|
|
720
|
-
}: ServerComponentProps) {
|
|
838
|
+
const ServerComponent = ({ count }: ServerComponentProps) => {
|
|
839
|
+
const { t, i18n } = useTranslation("about");
|
|
840
|
+
const formatted = new Intl.NumberFormat(i18n.language).format(count);
|
|
841
|
+
|
|
721
842
|
return (
|
|
722
843
|
<div>
|
|
723
|
-
<p>{
|
|
844
|
+
<p>{formatted}</p>
|
|
724
845
|
<button aria-label={t("counter.label")}>{t("counter.increment")}</button>
|
|
725
846
|
</div>
|
|
726
847
|
);
|
|
727
|
-
}
|
|
848
|
+
};
|
|
728
849
|
```
|
|
729
850
|
|
|
730
|
-
> As the server component cannot be async, you need to pass the translations and formatter function as props.
|
|
731
|
-
>
|
|
732
|
-
> - `const { t, i18n } = useTranslation("about");`
|
|
733
|
-
> - `const formatted = new Intl.NumberFormat(i18n.language).format(initialCount);`
|
|
734
|
-
|
|
735
851
|
</TabItem>
|
|
736
852
|
<TabItem label="next-intl" value="next-intl">
|
|
737
853
|
|
|
738
854
|
```tsx fileName="src/components/ServerComponent.tsx"
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
export default async function ServerComponent({
|
|
742
|
-
t,
|
|
743
|
-
format,
|
|
744
|
-
count,
|
|
745
|
-
}: {
|
|
746
|
-
t: (key: string) => string;
|
|
747
|
-
format: (value: number) => string;
|
|
855
|
+
type ServerComponentProps = {
|
|
748
856
|
count: number;
|
|
749
|
-
|
|
857
|
+
t: (key: string) => string;
|
|
858
|
+
};
|
|
859
|
+
|
|
860
|
+
const ServerComponent = ({ t, count }: ServerComponentProps) => {
|
|
861
|
+
const formatted = new Intl.NumberFormat(i18n.language).format(count);
|
|
862
|
+
|
|
750
863
|
return (
|
|
751
864
|
<div>
|
|
752
|
-
<p>{
|
|
865
|
+
<p>{formatted}</p>
|
|
753
866
|
<button aria-label={t("label")}>{t("increment")}</button>
|
|
754
867
|
</div>
|
|
755
868
|
);
|
|
756
|
-
}
|
|
869
|
+
};
|
|
757
870
|
```
|
|
758
871
|
|
|
759
872
|
> As the server component cannot be async, you need to pass the translations and formatter function as props.
|
|
760
873
|
>
|
|
874
|
+
> In your page / layout:
|
|
875
|
+
>
|
|
876
|
+
> - `import { getTranslations, getFormatter } from "next-intl/server";`
|
|
761
877
|
> - `const t = await getTranslations("about.counter");`
|
|
762
878
|
> - `const format = await getFormatter();`
|
|
763
879
|
|
|
@@ -767,7 +883,11 @@ export default async function ServerComponent({
|
|
|
767
883
|
```tsx fileName="src/components/ServerComponent.tsx"
|
|
768
884
|
import { useIntlayer, useNumber } from "next-intlayer/server";
|
|
769
885
|
|
|
770
|
-
|
|
886
|
+
type ServerComponentProps = {
|
|
887
|
+
count: number;
|
|
888
|
+
};
|
|
889
|
+
|
|
890
|
+
const ServerComponent = ({ count }: ServerComponentProps) => {
|
|
771
891
|
const { label, increment } = useIntlayer("counter");
|
|
772
892
|
const { number } = useNumber();
|
|
773
893
|
|
|
@@ -909,7 +1029,7 @@ export default function robots(): MetadataRoute.Robots {
|
|
|
909
1029
|
```tsx fileName="src/app/[locale]/about/layout.tsx"
|
|
910
1030
|
import type { Metadata } from "next";
|
|
911
1031
|
import { locales, defaultLocale } from "@/i18n";
|
|
912
|
-
import { getTranslations
|
|
1032
|
+
import { getTranslations } from "next-intl/server";
|
|
913
1033
|
|
|
914
1034
|
function localizedPath(locale: string, path: string) {
|
|
915
1035
|
return locale === defaultLocale ? path : "/" + locale + path;
|
|
@@ -1063,147 +1183,20 @@ export default robots;
|
|
|
1063
1183
|
|
|
1064
1184
|
---
|
|
1065
1185
|
|
|
1066
|
-
## TypeScript & safety
|
|
1067
|
-
|
|
1068
|
-
<Columns>
|
|
1069
|
-
<Column>
|
|
1070
|
-
|
|
1071
|
-
**next-intl**
|
|
1072
|
-
|
|
1073
|
-
- Solid TypeScript support, but **keys aren’t strictly typed by default**; you’ll maintain safety patterns manually.
|
|
1074
|
-
|
|
1075
|
-
</Column>
|
|
1076
|
-
<Column>
|
|
1077
|
-
|
|
1078
|
-
**next-i18next**
|
|
1079
|
-
|
|
1080
|
-
- Base typings for hooks; **strict key typing requires extra tooling/config**.
|
|
1081
|
-
|
|
1082
|
-
</Column>
|
|
1083
|
-
<Column>
|
|
1084
|
-
|
|
1085
|
-
**intlayer**
|
|
1086
|
-
|
|
1087
|
-
- **Generates strict types** from your content. **IDE autocompletion** and **compile-time errors** catch typos and missing keys before deploy.
|
|
1088
|
-
|
|
1089
|
-
</Column>
|
|
1090
|
-
<Columns>
|
|
1091
|
-
|
|
1092
|
-
**Why it matters:** Strong typing shifts failures **left** (CI/build) instead of **right** (runtime).
|
|
1093
|
-
|
|
1094
1186
|
---
|
|
1095
1187
|
|
|
1096
|
-
##
|
|
1097
|
-
|
|
1098
|
-
**next-intl**
|
|
1099
|
-
|
|
1100
|
-
- Relies on **runtime fallbacks** (e.g., show the key or default locale). Build doesn’t fail.
|
|
1101
|
-
|
|
1102
|
-
**next-i18next**
|
|
1103
|
-
|
|
1104
|
-
- Relies on **runtime fallbacks** (e.g., show the key or default locale). Build doesn’t fail.
|
|
1105
|
-
|
|
1106
|
-
**intlayer**
|
|
1107
|
-
|
|
1108
|
-
- **Build-time detection** with **warnings/errors** for missing locales or keys.
|
|
1109
|
-
|
|
1110
|
-
**Why it matters:** Catching gaps during build prevents “mystery strings” in production and aligns with strict release gates.
|
|
1111
|
-
|
|
1112
|
-
---
|
|
1113
|
-
|
|
1114
|
-
## Routing, middleware & URL strategy
|
|
1115
|
-
|
|
1116
|
-
<Columns>
|
|
1117
|
-
<Column>
|
|
1118
|
-
|
|
1119
|
-
**next-intl**
|
|
1120
|
-
|
|
1121
|
-
- Works with **Next.js localized routing** on the App Router.
|
|
1122
|
-
|
|
1123
|
-
</Column>
|
|
1124
|
-
<Column>
|
|
1125
|
-
|
|
1126
|
-
**next-i18next**
|
|
1127
|
-
|
|
1128
|
-
- Works with **Next.js localized routing** on the App Router.
|
|
1129
|
-
|
|
1130
|
-
</Column>
|
|
1131
|
-
<Column>
|
|
1132
|
-
|
|
1133
|
-
**intlayer**
|
|
1134
|
-
|
|
1135
|
-
- All of the above, plus **i18n middleware** (locale detection via headers/cookies) and **helpers** to generate localized URLs and `<link rel="alternate" hreflang="…">` tags.
|
|
1136
|
-
|
|
1137
|
-
</Column>
|
|
1138
|
-
</Columns>
|
|
1139
|
-
|
|
1140
|
-
**Why it matters:** Fewer custom glue layers; **consistent UX** and **clean SEO** across locales.
|
|
1141
|
-
|
|
1142
|
-
---
|
|
1143
|
-
|
|
1144
|
-
## Server Components (RSC) alignment
|
|
1145
|
-
|
|
1146
|
-
<Columns>
|
|
1147
|
-
<Column>
|
|
1148
|
-
|
|
1149
|
-
**next-intl**
|
|
1150
|
-
|
|
1151
|
-
- Supports Next.js 13+. Often requires passing t-functions/formatters through component trees in hybrid setups.
|
|
1152
|
-
|
|
1153
|
-
</Column>
|
|
1154
|
-
<Column>
|
|
1155
|
-
|
|
1156
|
-
**next-i18next**
|
|
1157
|
-
|
|
1158
|
-
- Supports Next.js 13+. Similar constraints with passing translation utilities across boundaries.
|
|
1159
|
-
|
|
1160
|
-
</Column>
|
|
1161
|
-
<Column>
|
|
1162
|
-
|
|
1163
|
-
**intlayer**
|
|
1164
|
-
|
|
1165
|
-
- Supports Next.js 13+ and smooths the **server/client boundary** with a consistent API and RSC-oriented providers, avoiding shuttling formatters or t-functions.
|
|
1166
|
-
|
|
1167
|
-
</Column>
|
|
1168
|
-
</Columns>
|
|
1169
|
-
|
|
1170
|
-
**Why it matters:** Cleaner mental model and fewer edge cases in hybrid trees.
|
|
1171
|
-
|
|
1172
|
-
---
|
|
1188
|
+
## And the winner is…
|
|
1173
1189
|
|
|
1174
|
-
|
|
1190
|
+
It’s not simple. Each option has trade-offs. Here’s how I see it:
|
|
1175
1191
|
|
|
1176
1192
|
<Columns>
|
|
1177
1193
|
<Column>
|
|
1178
1194
|
|
|
1179
|
-
**next-intl**
|
|
1180
|
-
|
|
1181
|
-
- Commonly paired with external localization platforms and editorial workflows.
|
|
1182
|
-
|
|
1183
|
-
</Column>
|
|
1184
|
-
<Column>
|
|
1185
|
-
|
|
1186
1195
|
**next-i18next**
|
|
1187
1196
|
|
|
1188
|
-
-
|
|
1189
|
-
|
|
1190
|
-
</Column>
|
|
1191
|
-
<Column>
|
|
1192
|
-
|
|
1193
|
-
**intlayer**
|
|
1194
|
-
|
|
1195
|
-
- Ships a **free Visual Editor** and **optional CMS** (Git-friendly or externalized), plus a **VSCode extension** and **AI-assisted translations** using your own provider keys.
|
|
1197
|
+
- mature, full of features, lots of community plugins, but higher setup cost. If you need **i18next’s plugin ecosystem** (e.g., advanced ICU rules via plugins) and your team already knows i18next, accepting **more configuration** for flexibility.
|
|
1196
1198
|
|
|
1197
1199
|
</Column>
|
|
1198
|
-
</Columns>
|
|
1199
|
-
|
|
1200
|
-
**Why it matters:** Lowers ops cost and shortens the loop between developers and content authors.
|
|
1201
|
-
|
|
1202
|
-
## And the winner is…
|
|
1203
|
-
|
|
1204
|
-
It’s not simple. Each option has trade-offs. Here’s how I see it:
|
|
1205
|
-
|
|
1206
|
-
<Columns>
|
|
1207
1200
|
<Column>
|
|
1208
1201
|
|
|
1209
1202
|
**next-intl**
|
|
@@ -1213,13 +1206,6 @@ It’s not simple. Each option has trade-offs. Here’s how I see it:
|
|
|
1213
1206
|
</Column>
|
|
1214
1207
|
<Column>
|
|
1215
1208
|
|
|
1216
|
-
**next-i18next**
|
|
1217
|
-
|
|
1218
|
-
- mature, full of features, lots of community plugins, but higher setup cost. If you need **i18next’s plugin ecosystem** (e.g., advanced ICU rules via plugins) and your team already knows i18next, accepting **more configuration** for flexibility.
|
|
1219
|
-
|
|
1220
|
-
</Column>
|
|
1221
|
-
<Column>
|
|
1222
|
-
|
|
1223
1209
|
**Intlayer**
|
|
1224
1210
|
|
|
1225
1211
|
- built for modern Next.js, with modular content, type safety, tooling, and less boilerplate. If you value **component-scoped content**, **strict TypeScript**, **build-time guarantees**, **tree-shaking**, and **batteries-included** routing/SEO/editor tooling - especially for **Next.js App Router**, design-systems and **large, modular codebases**.
|
|
@@ -1233,8 +1219,6 @@ If you prefer minimal setup and accept some manual wiring, next-intl is a good p
|
|
|
1233
1219
|
|
|
1234
1220
|
> **Future roadmap**: Intlayer also plans to develop plugins that work on top of **i18next** and **next-intl** solutions. This will give you the advantages of Intlayer for automation, syntax, and content management while keeping the security and stability provided by these established solutions in your application code.
|
|
1235
1221
|
|
|
1236
|
-
---
|
|
1237
|
-
|
|
1238
1222
|
## GitHub STARs
|
|
1239
1223
|
|
|
1240
1224
|
GitHub stars are a strong indicator of a project's popularity, community trust, and long-term relevance. While not a direct measure of technical quality, they reflect how many developers find the project useful, follow its progress, and are likely to adopt it. For estimating the value of a project, stars help compare traction across alternatives and provide insights into ecosystem growth.
|