@intlayer/docs 6.1.4 → 6.1.6-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/blog/ar/next-i18next_vs_next-intl_vs_intlayer.md +1366 -75
  2. package/blog/ar/nextjs-multilingual-seo-comparison.md +364 -0
  3. package/blog/de/next-i18next_vs_next-intl_vs_intlayer.md +1288 -72
  4. package/blog/de/nextjs-multilingual-seo-comparison.md +362 -0
  5. package/blog/en/intlayer_with_next-i18next.mdx +431 -0
  6. package/blog/en/intlayer_with_next-intl.mdx +335 -0
  7. package/blog/en/next-i18next_vs_next-intl_vs_intlayer.md +583 -336
  8. package/blog/en/nextjs-multilingual-seo-comparison.md +360 -0
  9. package/blog/en-GB/next-i18next_vs_next-intl_vs_intlayer.md +1144 -37
  10. package/blog/en-GB/nextjs-multilingual-seo-comparison.md +360 -0
  11. package/blog/es/next-i18next_vs_next-intl_vs_intlayer.md +1236 -64
  12. package/blog/es/nextjs-multilingual-seo-comparison.md +363 -0
  13. package/blog/fr/next-i18next_vs_next-intl_vs_intlayer.md +1142 -75
  14. package/blog/fr/nextjs-multilingual-seo-comparison.md +362 -0
  15. package/blog/hi/nextjs-multilingual-seo-comparison.md +363 -0
  16. package/blog/it/next-i18next_vs_next-intl_vs_intlayer.md +1130 -55
  17. package/blog/it/nextjs-multilingual-seo-comparison.md +363 -0
  18. package/blog/ja/next-i18next_vs_next-intl_vs_intlayer.md +1150 -76
  19. package/blog/ja/nextjs-multilingual-seo-comparison.md +362 -0
  20. package/blog/ko/next-i18next_vs_next-intl_vs_intlayer.md +1139 -73
  21. package/blog/ko/nextjs-multilingual-seo-comparison.md +362 -0
  22. package/blog/pt/next-i18next_vs_next-intl_vs_intlayer.md +1143 -76
  23. package/blog/pt/nextjs-multilingual-seo-comparison.md +362 -0
  24. package/blog/ru/next-i18next_vs_next-intl_vs_intlayer.md +1150 -74
  25. package/blog/ru/nextjs-multilingual-seo-comparison.md +370 -0
  26. package/blog/tr/next-i18next_vs_next-intl_vs_intlayer.md +2 -0
  27. package/blog/tr/nextjs-multilingual-seo-comparison.md +362 -0
  28. package/blog/zh/next-i18next_vs_next-intl_vs_intlayer.md +1152 -75
  29. package/blog/zh/nextjs-multilingual-seo-comparison.md +394 -0
  30. package/dist/cjs/generated/blog.entry.cjs +16 -0
  31. package/dist/cjs/generated/blog.entry.cjs.map +1 -1
  32. package/dist/cjs/generated/docs.entry.cjs +16 -0
  33. package/dist/cjs/generated/docs.entry.cjs.map +1 -1
  34. package/dist/esm/generated/blog.entry.mjs +16 -0
  35. package/dist/esm/generated/blog.entry.mjs.map +1 -1
  36. package/dist/esm/generated/docs.entry.mjs +16 -0
  37. package/dist/esm/generated/docs.entry.mjs.map +1 -1
  38. package/dist/types/generated/blog.entry.d.ts +1 -0
  39. package/dist/types/generated/blog.entry.d.ts.map +1 -1
  40. package/dist/types/generated/docs.entry.d.ts +1 -0
  41. package/dist/types/generated/docs.entry.d.ts.map +1 -1
  42. package/docs/ar/component_i18n.md +186 -0
  43. package/docs/ar/vs_code_extension.md +48 -109
  44. package/docs/de/component_i18n.md +186 -0
  45. package/docs/de/vs_code_extension.md +46 -107
  46. package/docs/en/component_i18n.md +186 -0
  47. package/docs/en/interest_of_intlayer.md +2 -2
  48. package/docs/en/intlayer_with_nextjs_14.md +18 -1
  49. package/docs/en/intlayer_with_nextjs_15.md +18 -1
  50. package/docs/en/vs_code_extension.md +24 -114
  51. package/docs/en-GB/component_i18n.md +186 -0
  52. package/docs/en-GB/vs_code_extension.md +42 -103
  53. package/docs/es/component_i18n.md +182 -0
  54. package/docs/es/vs_code_extension.md +53 -114
  55. package/docs/fr/component_i18n.md +186 -0
  56. package/docs/fr/vs_code_extension.md +50 -111
  57. package/docs/hi/component_i18n.md +186 -0
  58. package/docs/hi/vs_code_extension.md +49 -110
  59. package/docs/it/component_i18n.md +186 -0
  60. package/docs/it/vs_code_extension.md +50 -111
  61. package/docs/ja/component_i18n.md +186 -0
  62. package/docs/ja/vs_code_extension.md +50 -111
  63. package/docs/ko/component_i18n.md +186 -0
  64. package/docs/ko/vs_code_extension.md +48 -109
  65. package/docs/pt/component_i18n.md +186 -0
  66. package/docs/pt/vs_code_extension.md +46 -107
  67. package/docs/ru/component_i18n.md +186 -0
  68. package/docs/ru/vs_code_extension.md +48 -109
  69. package/docs/tr/component_i18n.md +186 -0
  70. package/docs/tr/vs_code_extension.md +54 -115
  71. package/docs/zh/component_i18n.md +186 -0
  72. package/docs/zh/vs_code_extension.md +51 -105
  73. package/package.json +11 -11
  74. package/src/generated/blog.entry.ts +16 -0
  75. package/src/generated/docs.entry.ts +16 -0
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  createdAt: 2025-08-23
3
- updatedAt: 2025-08-23
3
+ updatedAt: 2025-09-29
4
4
  title: next-i18next vs next-intl vs Intlayer
5
5
  description: Vergleich von next-i18next mit next-intl und Intlayer für die Internationalisierung (i18n) einer Next.js-App
6
6
  keywords:
@@ -19,144 +19,1360 @@ slugs:
19
19
 
20
20
  # next-i18next VS next-intl VS intlayer | Next.js Internationalisierung (i18n)
21
21
 
22
- Dieser Leitfaden vergleicht drei weit verbreitete i18n-Optionen für **Next.js**: **next-intl**, **next-i18next** und **Intlayer**.
22
+ ![next-i18next VS next-intl VS intlayer](https://github.com/aymericzip/intlayer/blob/main/docs/assets/i18next-next-intl-intlayer.png?raw=true)
23
+
24
+ Werfen wir einen Blick auf die Gemeinsamkeiten und Unterschiede zwischen drei i18n-Optionen für Next.js: next-i18next, next-intl und Intlayer.
25
+
26
+ Dies ist kein vollständiges Tutorial. Es ist ein Vergleich, der Ihnen bei der Auswahl helfen soll.
27
+
23
28
  Wir konzentrieren uns auf den **Next.js 13+ App Router** (mit **React Server Components**) und bewerten:
24
29
 
25
30
  1. **Architektur & Inhaltsorganisation**
26
31
  2. **TypeScript & Sicherheit**
27
32
  3. **Umgang mit fehlenden Übersetzungen**
28
33
  4. **Routing & Middleware**
29
- 5. **Leistung & Ladeverhalten**
30
- 6. **Entwicklererfahrung (DX), Werkzeuge & Wartung**
34
+ 5. **Performance & Ladeverhalten**
35
+ 6. **Entwicklererfahrung (DX), Tools & Wartung**
31
36
  7. **SEO & Skalierbarkeit bei großen Projekten**
32
37
 
33
38
  > **Kurzfassung**: Alle drei können eine Next.js-App lokalisieren. Wenn Sie **komponentenbezogenen Inhalt**, **strenge TypeScript-Typen**, **Build-Zeit-Prüfungen fehlender Schlüssel**, **tree-shakbare Wörterbücher** und **erstklassige App Router- und SEO-Hilfen** wünschen, ist **Intlayer** die vollständigste und modernste Wahl.
34
39
 
40
+ > Eine häufige Verwirrung bei Entwicklern ist die Annahme, dass `next-intl` die Next.js-Version von `react-intl` sei. Das ist nicht der Fall – `next-intl` wird von [Amann](https://github.com/amannn) gepflegt, während `react-intl` von [FormatJS](https://github.com/formatjs/formatjs) betreut wird.
41
+
42
+ ---
43
+
44
+ ## Kurz gesagt
45
+
46
+ - **next-intl** – Leichtgewichtiges, unkompliziertes Nachrichtenformat mit solider Next.js-Unterstützung. Zentralisierte Kataloge sind üblich; die Entwicklererfahrung (DX) ist einfach, aber Sicherheit und großflächige Wartung bleiben größtenteils Ihre Verantwortung.
47
+ - **next-i18next** – i18next im Next.js-Gewand. Ausgereiftes Ökosystem und Funktionen über Plugins (z. B. ICU), aber die Konfiguration kann umfangreich sein und Kataloge neigen dazu, mit wachsendem Projekt zentralisiert zu werden.
48
+ - **Intlayer** – Komponentenorientiertes Inhaltsmodell für Next.js, **strikte TS-Typisierung**, **Build-Zeit-Prüfungen**, **Tree-Shaking**, **eingebaute Middleware- & SEO-Hilfen**, optionaler **Visueller Editor/CMS** und **KI-unterstützte Übersetzungen**.
49
+
50
+ ---
51
+
52
+ | Library | GitHub Stars | Total Commits | Last Commit | First Version | NPM Version | NPM Downloads |
53
+ | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
54
+ | `aymericzip/intlayer` | [![GitHub Repo stars](https://img.shields.io/github/stars/aymericzip/intlayer?style=for-the-badge&label=%E2%AD%90%20stars)](https://github.com/aymericzip/intlayer/stargazers) | [![GitHub commit activity](https://img.shields.io/github/commit-activity/t/aymericzip/intlayer?style=for-the-badge&label=commits)](https://github.com/aymericzip/intlayer/commits) | [![Last Commit](https://img.shields.io/github/last-commit/aymericzip/intlayer?style=for-the-badge)](https://github.com/aymericzip/intlayer/commits) | April 2024 | [![npm](https://img.shields.io/npm/v/intlayer?style=for-the-badge)](https://www.npmjs.com/package/intlayer) | [![npm downloads](https://img.shields.io/npm/dm/intlayer?style=for-the-badge)](https://www.npmjs.com/package/intlayer) |
55
+ | `amannn/next-intl` | [![GitHub Repo stars](https://img.shields.io/github/stars/amannn/next-intl?style=for-the-badge&label=%E2%AD%90%20stars)](https://github.com/amannn/next-intl/stargazers) | [![GitHub commit activity](https://img.shields.io/github/commit-activity/t/amannn/next-intl?style=for-the-badge&label=commits)](https://github.com/amannn/next-intl/commits) | [![Last Commit](https://img.shields.io/github/last-commit/amannn/next-intl?style=for-the-badge)](https://github.com/amannn/next-intl/commits) | Nov 2020 | [![npm](https://img.shields.io/npm/v/next-intl?style=for-the-badge)](https://www.npmjs.com/package/next-intl) | [![npm downloads](https://img.shields.io/npm/dm/next-intl?style=for-the-badge)](https://www.npmjs.com/package/next-intl) |
56
+ | `i18next/i18next` | [![GitHub Repo stars](https://img.shields.io/github/stars/i18next/i18next?style=for-the-badge&label=%E2%AD%90%20stars)](https://github.com/i18next/i18next/stargazers) | [![GitHub commit activity](https://img.shields.io/github/commit-activity/t/i18next/i18next?style=for-the-badge&label=commits)](https://github.com/i18next/i18next/commits) | [![Last Commit](https://img.shields.io/github/last-commit/i18next/i18next?style=for-the-badge)](https://github.com/i18next/i18next/commits) | Jan 2012 | [![npm](https://img.shields.io/npm/v/i18next?style=for-the-badge)](https://www.npmjs.com/package/i18next) | [![npm downloads](https://img.shields.io/npm/dm/i18next?style=for-the-badge)](https://www.npmjs.com/package/i18next) |
57
+ | `i18next/next-i18next` | [![GitHub Repo stars](https://img.shields.io/github/stars/i18next/next-i18next?style=for-the-badge&label=%E2%AD%90%20stars)](https://github.com/i18next/next-i18next/stargazers) | [![GitHub commit activity](https://img.shields.io/github/commit-activity/t/i18next/next-i18next?style=for-the-badge&label=commits)](https://github.com/i18next/next-i18next/commits) | [![Last Commit](https://img.shields.io/github/last-commit/i18next/next-i18next?style=for-the-badge)](https://github.com/i18next/next-i18next/commits) | Nov 2018 | [![npm](https://img.shields.io/npm/v/next-i18next?style=for-the-badge)](https://www.npmjs.com/package/next-i18next) | [![npm downloads](https://img.shields.io/npm/dm/next-i18next?style=for-the-badge)](https://www.npmjs.com/package/next-i18next) |
58
+
59
+ > Abzeichen werden automatisch aktualisiert. Schnappschüsse können sich im Laufe der Zeit ändern.
60
+
35
61
  ---
36
62
 
37
- ## Übergeordnete Positionierung
63
+ ## Gegenüberstellung der Funktionen (Fokus auf Next.js)
64
+
65
+ | Funktion | `next-intlayer` (Intlayer) | `next-intl` | `next-i18next` |
38
66
 
39
- - **next-intl** - Leichtgewichtiges, unkompliziertes Nachrichtenformat mit solider Next.js-Unterstützung. Zentralisierte Kataloge sind üblich; die Entwicklererfahrung ist einfach, aber Sicherheit und großflächige Wartung liegen größtenteils in Ihrer Verantwortung.
40
- - **next-i18next** - i18next im Next.js-Gewand. Ausgereiftes Ökosystem und Funktionen über Plugins (z. B. ICU), aber die Konfiguration kann umfangreich sein und Kataloge neigen dazu, mit wachsendem Projektumfang zentralisiert zu werden.
41
- - **Intlayer** - Komponentenorientiertes Inhaltsmodell für Next.js, **strenge TS-Typisierung**, **Build-Zeit-Prüfungen**, **Tree-Shaking**, **eingebaute Middleware- und SEO-Hilfen**, optionaler **Visueller Editor/CMS** und **KI-unterstützte Übersetzungen**.
67
+ > Abzeichen werden automatisch aktualisiert. Screenshots können sich im Laufe der Zeit ändern.
42
68
 
43
69
  ---
44
70
 
45
- ## Vergleich der Funktionen im Überblick (Fokus auf Next.js)
71
+ ## Gegenüberstellung der Funktionen (Fokus auf Next.js)
46
72
 
47
- | Funktion | `next-intlayer` (Intlayer) | `next-intl` | `next-i18next` |
48
- | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
49
- | **Übersetzungen in der Nähe der Komponenten** | ✅ Ja, Inhalt ist mit jeder Komponente zusammengefasst | ❌ Nein | ❌ Nein |
50
- | **TypeScript-Integration** | ✅ Fortgeschritten, automatisch generierte strenge Typen | ✅ Gut | ⚠️ Grundlegend |
51
- | **Fehlende Übersetzungserkennung** | ✅ TypeScript-Fehlerhervorhebung und Fehler-/Warnmeldung zur Build-Zeit | ⚠️ Laufzeit-Fallback | ⚠️ Laufzeit-Fallback |
52
- | **Reicher Inhalt (JSX/Markdown/Komponenten)** | ✅ Direkte Unterstützung | ❌ Nicht für komplexe Knoten ausgelegt | ⚠️ Eingeschränkt |
53
- | **KI-gestützte Übersetzung** | ✅ Ja, unterstützt mehrere KI-Anbieter. Nutzbar mit eigenen API-Schlüsseln. Berücksichtigt den Kontext Ihrer Anwendung und den Umfang des Inhalts | ❌ Nein | ❌ Nein |
54
- | **Visueller Editor** | ✅ Ja, lokaler visueller Editor + optionales CMS; kann Codebasis-Inhalte auslagern; einbettbar | ❌ Nein / verfügbar über externe Lokalisierungsplattformen | ❌ Nein / verfügbar über externe Lokalisierungsplattformen |
55
- | **Lokalisierte Routenführung** | ✅ Ja, unterstützt lokalisierte Pfade direkt (funktioniert mit Next.js & Vite) | ✅ Integriert, App Router unterstützt `[locale]` Segment | ✅ Integriert |
56
- | **Dynamische Routen-Generierung** | ✅ Ja | ✅ Ja | ✅ Ja |
57
- | **Pluralisierung** | ✅ Aufzählungsbasierte Muster | ✅ Gut | ✅ Gut |
58
- | **Formatierung (Daten, Zahlen, Währungen)** | ✅ Optimierte Formatierer (Intl im Hintergrund) | ✅ Gut (Intl-Hilfsmittel) | ✅ Gut (Intl-Hilfsmittel) |
59
- | **Inhaltsformat** | ✅ .tsx, .ts, .js, .json, .md, .txt, (.yaml in Arbeit) | ✅ .json, .js, .ts | ⚠️ .json |
60
- | **ICU-Unterstützung** | ⚠️ In Arbeit | ✅ Ja | ⚠️ Über Plugin (`i18next-icu`) |
61
- | **SEO-Helfer (hreflang, Sitemap)** | ✅ Eingebaute Werkzeuge: Helfer für Sitemap, robots.txt, Metadaten | ✅ Gut | ✅ Gut |
62
- | **Ökosystem / Community** | ⚠️ Kleinere, aber schnell wachsende und reaktive Community | ✅ Mittelgroß, Next.js-fokussiert | ✅ Mittelgroß, Next.js-fokussiert |
63
- | **Server-seitiges Rendering & Server-Komponenten** | ✅ Ja, optimiert für SSR / React Server-Komponenten | ⚠️ Unterstützt auf Seitenebene, aber t-Funktionen müssen im Komponentenbaum für untergeordnete Server-Komponenten übergeben werden | ⚠️ Unterstützt auf Seitenebene, aber t-Funktionen müssen im Komponentenbaum für untergeordnete Server-Komponenten übergeben werden |
64
- | **Tree-shaking (nur genutzte Inhalte laden)** | ✅ Ja, pro Komponente zur Build-Zeit über Babel/SWC-Plugins | ⚠️ Teilweise | ⚠️ Teilweise |
65
- | **Lazy Loading** | ✅ Ja, pro Locale / pro Wörterbuch | ✅ Ja (pro Route/pro Locale), benötigt Namespace-Verwaltung | ✅ Ja (pro Route/pro Locale), benötigt Namespace-Verwaltung |
66
- | **Bereinigung ungenutzter Inhalte** | ✅ Ja, pro Wörterbuch zur Build-Zeit | ❌ Nein, kann manuell mit Namespace-Verwaltung gehandhabt werden | ❌ Nein, kann manuell mit Namespace-Verwaltung gehandhabt werden |
67
- | **Management großer Projekte** | ✅ Fördert Modularität, geeignet für Design-Systeme | ✅ Modular mit Setup | ✅ Modular mit Setup |
73
+ | Funktion | `next-intlayer` (Intlayer) | `next-intl` | `next-i18next` |
74
+ | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
75
+ | **Übersetzungen in der Nähe der Komponenten** | ✅ Ja, Inhalt ist mit jeder Komponente zusammengefasst | ❌ Nein | ❌ Nein |
76
+ | **TypeScript-Integration** | ✅ Fortgeschritten, automatisch generierte strenge Typen | ✅ Gut | ⚠️ Grundlegend |
77
+ | **Erkennung fehlender Übersetzungen** | ✅ TypeScript-Fehlerhervorhebung und Fehler-/Warnmeldung zur Build-Zeit | ⚠️ Laufzeit-Fallback | ⚠️ Laufzeit-Fallback |
78
+ | **Reicher Inhalt (JSX/Markdown/Komponenten)** | ✅ Direkte Unterstützung | ❌ Nicht für komplexe Knoten ausgelegt | ⚠️ Eingeschränkt |
79
+ | **KI-gestützte Übersetzung** | ✅ Ja, unterstützt mehrere KI-Anbieter. Nutzbar mit eigenen API-Schlüsseln. Berücksichtigt den Kontext Ihrer Anwendung und den Umfang des Inhalts | ❌ Nein | ❌ Nein |
80
+ | **Visueller Editor** | ✅ Ja, lokaler visueller Editor + optionales CMS; kann Codebasis-Inhalte auslagern; einbettbar | ❌ Nein / verfügbar über externe Lokalisierungsplattformen | ❌ Nein / verfügbar über externe Lokalisierungsplattformen |
81
+ | **Lokalisierte Routenführung** | ✅ Ja, unterstützt lokalisierte Pfade direkt (funktioniert mit Next.js & Vite) | ✅ Eingebaut, App Router unterstützt `[locale]` Segment | ✅ Eingebaut |
82
+ | **Dynamische Routen-Generierung** | ✅ Ja | ✅ Ja | ✅ Ja |
83
+ | **Pluralisierung** | ✅ Aufzählungsbasierte Muster | ✅ Gut | ✅ Gut |
84
+ | **Formatierung (Daten, Zahlen, Währungen)** | ✅ Optimierte Formatierer (Intl im Hintergrund) | ✅ Gut (Intl-Helfer) | ✅ Gut (Intl-Helfer) |
85
+ | **Inhaltsformat** | ✅ .tsx, .ts, .js, .json, .md, .txt, (.yaml in Arbeit) | ✅ .json, .js, .ts | ⚠️ .json |
86
+ | **ICU-Unterstützung** | ⚠️ In Arbeit | ✅ Ja | ⚠️ Über Plugin (`i18next-icu`) |
87
+ | **SEO-Helfer (hreflang, Sitemap)** | ✅ Eingebaute Werkzeuge: Helfer für Sitemap, robots.txt, Metadaten | ✅ Gut | ✅ Gut |
88
+ | **Ökosystem / Community** | ⚠️ Kleiner, wächst aber schnell und ist reaktiv | ✅ Gut | ✅ Gut |
89
+ | **Server-seitiges Rendering & Server-Komponenten** | ✅ Ja, optimiert für SSR / React Server-Komponenten | ⚠️ Auf Seitenebene unterstützt, aber t-Funktionen müssen im Komponentenbaum an untergeordnete Server-Komponenten weitergegeben werden | ⚠️ Auf Seitenebene unterstützt, aber t-Funktionen müssen im Komponentenbaum an untergeordnete Server-Komponenten weitergegeben werden |
90
+ | **Tree-shaking (nur verwendeten Inhalt laden)** | ✅ Ja, pro Komponente zur Build-Zeit über Babel/SWC-Plugins | ⚠️ Teilweise | ⚠️ Teilweise |
91
+ | **Lazy Loading** | ✅ Ja, pro Sprache / pro Wörterbuch | ✅ Ja (pro Route/pro Sprache), benötigt Namespace-Verwaltung | ✅ Ja (pro Route/pro Sprache), benötigt Namespace-Verwaltung |
92
+ | **Bereinigung ungenutzter Inhalte** | ✅ Ja, pro Wörterbuch zur Build-Zeit | ❌ Nein, kann manuell mit Namespace-Verwaltung gehandhabt werden | ❌ Nein, kann manuell mit Namespace-Verwaltung gehandhabt werden |
93
+ | **Verwaltung großer Projekte** | ✅ Fördert Modularität, geeignet für Design-Systeme | ✅ Modular mit Setup | ✅ Modular mit Setup |
94
+ | **Testen fehlender Übersetzungen (CLI/CI)** | ✅ CLI: `npx intlayer content test` (CI-freundliches Audit) | ⚠️ Nicht eingebaut; Dokumentation empfiehlt `npx @lingual/i18n-check` | ⚠️ Nicht eingebaut; verlässt sich auf i18next-Tools / Laufzeit `saveMissing` |
68
95
 
69
96
  ---
70
97
 
71
- ## Tiefgehender Vergleich
98
+ ## Einführung
99
+
100
+ Next.js bietet integrierte Unterstützung für internationalisierte Routen (z.B. Lokalisierungssegmente). Diese Funktion übernimmt jedoch nicht automatisch die Übersetzungen. Sie benötigen weiterhin eine Bibliothek, um lokalisierten Inhalt für Ihre Nutzer darzustellen.
101
+
102
+ Es gibt viele i18n-Bibliotheken, aber in der Next.js-Welt gewinnen heute drei an Bedeutung: next-i18next, next-intl und Intlayer.
72
103
 
73
- ### 1) Architektur & Skalierbarkeit
104
+ ---
105
+
106
+ ## Architektur & Skalierbarkeit
74
107
 
75
- - **next-intl / next-i18next**: Standardmäßig **zentralisierte Kataloge** pro Locale (plus **Namespaces** in i18next). Funktioniert anfangs gut, wird aber oft zu einer großen gemeinsamen Oberfläche mit zunehmender Kopplung und Schlüsseländerungen.
76
- - **Intlayer**: Fördert **pro Komponente** (oder pro Funktion) **lokalisierte Wörterbücher**, die **direkt am Code** liegen, den sie bedienen. Dies reduziert die kognitive Belastung, erleichtert die Duplizierung/Migration von UI-Elementen und verringert Konflikte zwischen Teams. Unbenutzte Inhalte sind so leichter zu erkennen und zu entfernen.
108
+ - **next-intl / next-i18next**: Standardmäßig **zentralisierte Kataloge** pro Locale (plus **Namespaces** in i18next). Funktioniert anfangs gut, wird aber oft zu einer großen gemeinsamen Oberfläche mit zunehmender Kopplung und Schlüsselwechsel.
109
+ - **Intlayer**: Fördert **pro-Komponenten** (oder pro-Feature) Wörterbücher, die **am selben Ort** wie der zugehörige Code liegen. Dies reduziert die kognitive Belastung, erleichtert die Duplizierung/Migration von UI-Teilen und verringert Konflikte zwischen Teams. Unbenutzter Inhalt ist so natürlicherweise leichter zu erkennen und zu entfernen.
77
110
 
78
111
  **Warum das wichtig ist:** In großen Codebasen oder Design-System-Setups skaliert **modularer Inhalt** besser als monolithische Kataloge.
79
112
 
80
113
  ---
81
114
 
82
- ### 2) TypeScript & Sicherheit
115
+ ## Bundle-Größen & Abhängigkeiten
83
116
 
84
- - **next-intl**: Solide TypeScript-Unterstützung, aber **Schlüssel sind standardmäßig nicht strikt typisiert**; Sicherheitsmuster müssen manuell gepflegt werden.
85
- - **next-i18next**: Basis-Typen für Hooks; **strikte Schlüsseltypisierung erfordert zusätzliche Tools/Konfiguration**.
86
- - **Intlayer**: **Erzeugt strenge Typen** aus Ihren Inhalten. **IDE-Autovervollständigung** und **Kompilierzeitfehler** erkennen Tippfehler und fehlende Schlüssel vor der Bereitstellung.
117
+ Nach dem Erstellen der Anwendung ist das Bundle das JavaScript, das der Browser zum Rendern der Seite lädt. Die Bundle-Größe ist daher wichtig für die Anwendungsleistung.
87
118
 
88
- **Warum das wichtig ist:** Starke Typisierung verschiebt Fehler **nach links** (CI/Build) statt **nach rechts** (Laufzeit).
119
+ Zwei Komponenten sind im Kontext eines mehrsprachigen Anwendungs-Bundles wichtig:
120
+
121
+ - Der Anwendungscode
122
+ - Der vom Browser geladene Inhalt
123
+
124
+ ## Anwendungscode
125
+
126
+ Die Bedeutung des Anwendungscodes ist in diesem Fall minimal. Alle drei Lösungen sind tree-shakable, was bedeutet, dass ungenutzte Teile des Codes nicht im Bundle enthalten sind.
127
+
128
+ Hier ist ein Vergleich der JavaScript-Bundle-Größe, die der Browser für eine mehrsprachige Anwendung mit den drei Lösungen lädt.
129
+
130
+ Wenn wir keinen Formatter in der Anwendung benötigen, ist die Liste der exportierten Funktionen nach dem Tree-Shaking wie folgt:
131
+
132
+ - **next-intlayer**: `useIntlayer`, `useLocale`, `NextIntlClientProvider`, (Paketgröße ist 180,6 kB -> 78,6 kB (gzip))
133
+ - **next-intl**: `useTranslations`, `useLocale`, `NextIntlClientProvider`, (Paketgröße ist 101,3 kB -> 31,4 kB (gzip))
134
+ - **next-i18next**: `useTranslation`, `useI18n`, `I18nextProvider`, (Paketgröße ist 80,7 kB -> 25,5 kB (gzip))
135
+
136
+ Diese Funktionen sind nur Wrapper um den React-Kontext/-Status, daher ist der Gesamteinfluss der i18n-Bibliothek auf die Paketgröße minimal.
137
+
138
+ > Intlayer ist etwas größer als `next-intl` und `next-i18next`, da es mehr Logik in der Funktion `useIntlayer` enthält. Dies hängt mit der Integration von Markdown und `intlayer-editor` zusammen.
139
+
140
+ ## Inhalt und Übersetzungen
141
+
142
+ Dieser Teil wird von Entwicklern oft ignoriert, aber betrachten wir den Fall einer Anwendung, die aus 10 Seiten in 10 Sprachen besteht. Nehmen wir zur Vereinfachung der Berechnung an, dass jede Seite zu 100 % einzigartigen Inhalt enthält (in Wirklichkeit ist viel Inhalt zwischen den Seiten redundant, z. B. Seitentitel, Kopfzeile, Fußzeile usw.).
143
+
144
+ Ein Benutzer, der die Seite `/fr/about` besuchen möchte, lädt den Inhalt einer Seite in einer bestimmten Sprache. Das Ignorieren der Inhaltsoptimierung würde bedeuten, dass unnötigerweise 8.200 % `((1 + (((10 Seiten - 1) × (10 Sprachen - 1)))) × 100)` des Anwendungsinhalts geladen werden. Sehen Sie das Problem? Selbst wenn dieser Inhalt nur Text ist und Sie wahrscheinlich eher daran denken, die Bilder Ihrer Website zu optimieren, senden Sie unnötigen Inhalt über den Globus und lassen die Computer der Benutzer ihn umsonst verarbeiten.
145
+
146
+ Zwei wichtige Probleme:
147
+
148
+ - **Aufteilung nach Route:**
149
+
150
+ > Wenn ich mich auf der Seite `/about` befinde, möchte ich nicht den Inhalt der Seite `/home` laden.
151
+
152
+ - **Aufteilung nach Sprache:**
153
+
154
+ > Wenn ich mich auf der Seite `/fr/about` befinde, möchte ich nicht den Inhalt der Seite `/en/about` laden.
155
+
156
+ Alle drei Lösungen sind sich dieser Probleme bewusst und ermöglichen die Verwaltung dieser Optimierungen. Der Unterschied zwischen den drei Lösungen liegt in der DX (Developer Experience).
157
+
158
+ `next-intl` und `next-i18next` verwenden einen zentralisierten Ansatz zur Verwaltung von Übersetzungen, der es erlaubt, JSON-Dateien nach Sprache und Unterdateien aufzuteilen. In `next-i18next` nennen wir die JSON-Dateien „Namespaces“; `next-intl` erlaubt das Deklarieren von Nachrichten. In `intlayer` nennen wir die JSON-Dateien „Dictionaries“.
159
+
160
+ - Im Fall von `next-intl`, ähnlich wie bei `next-i18next`, wird der Inhalt auf Seiten-/Layout-Ebene geladen, und dieser Inhalt wird dann in einen Context-Provider geladen. Das bedeutet, dass der Entwickler die JSON-Dateien, die für jede Seite geladen werden sollen, manuell verwalten muss.
161
+
162
+ > In der Praxis bedeutet dies, dass Entwickler diese Optimierung oft überspringen und es vorziehen, aus Einfachheitsgründen den gesamten Inhalt im Context-Provider der Seite zu laden.
163
+
164
+ - Im Fall von `intlayer` wird der gesamte Inhalt in der Anwendung geladen. Anschließend kümmert sich ein Plugin (`@intlayer/babel` / `@intlayer/swc`) darum, das Bundle zu optimieren, indem nur der auf der Seite verwendete Inhalt geladen wird. Der Entwickler muss daher die zu ladenden Wörterbücher nicht manuell verwalten. Dies ermöglicht eine bessere Optimierung, bessere Wartbarkeit und reduziert die Entwicklungszeit.
165
+
166
+ Da die Anwendung wächst (insbesondere wenn mehrere Entwickler an der Anwendung arbeiten), ist es üblich, dass vergessen wird, nicht mehr verwendete Inhalte aus den JSON-Dateien zu entfernen.
167
+
168
+ > Beachten Sie, dass in allen Fällen (next-intl, next-i18next, intlayer) alle JSON-Dateien geladen werden.
169
+
170
+ Deshalb ist der Ansatz von Intlayer leistungsfähiger: Wenn eine Komponente nicht mehr verwendet wird, wird ihr Wörterbuch nicht im Bundle geladen.
171
+
172
+ Wie die Bibliothek mit Fallbacks umgeht, ist ebenfalls wichtig. Nehmen wir an, die Anwendung ist standardmäßig auf Englisch eingestellt und der Benutzer besucht die Seite `/fr/about`. Wenn Übersetzungen auf Französisch fehlen, wird der englische Fallback berücksichtigt.
173
+
174
+ Im Fall von `next-intl` und `next-i18next` erfordert die Bibliothek das Laden der JSON-Dateien, die sowohl zur aktuellen Locale als auch zur Fallback-Locale gehören. Somit lädt jede Seite, vorausgesetzt alle Inhalte sind übersetzt, 100 % unnötigen Inhalt. **Im Vergleich dazu verarbeitet `intlayer` das Fallback bereits zur Build-Zeit des Wörterbuchs. Dadurch lädt jede Seite nur den tatsächlich genutzten Inhalt.**
175
+
176
+ Hier ein Beispiel für die Auswirkung der Bundle-Größenoptimierung mit `intlayer` in einer vite + react Anwendung:
177
+
178
+ | Optimiertes Bundle | Nicht optimiertes Bundle |
179
+ | ------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
180
+ | ![optimiertes Bundle](https://github.com/aymericzip/intlayer/blob/main/docs/assets/bundle.png?raw=true) | ![nicht optimiertes Bundle](https://github.com/aymericzip/intlayer/blob/main/docs/assets/bundle_no_optimization.png?raw=true) |
89
181
 
90
182
  ---
91
183
 
92
- ### 3) Umgang mit fehlenden Übersetzungen
184
+ ## TypeScript & Sicherheit
185
+
186
+ <Columns>
187
+ <Column>
188
+
189
+ **next-intl**
190
+
191
+ - Solide TypeScript-Unterstützung, aber **Schlüssel sind standardmäßig nicht strikt typisiert**; Sicherheitsmuster müssen manuell gepflegt werden.
93
192
 
94
- - **next-intl / next-i18next**: Verlassen sich auf **Laufzeit-Fallbacks** (z.B. Anzeige des Schlüssels oder der Standardsprache). Der Build schlägt nicht fehl.
95
- - **Intlayer**: **Erkennung zur Build-Zeit** mit **Warnungen/Fehlern** bei fehlenden Sprachen oder Schlüsseln.
193
+ </Column>
194
+ <Column>
96
195
 
97
- **Warum das wichtig ist:** Das Erkennen von Lücken während des Builds verhindert „mysteriöse Strings“ in der Produktion und entspricht strengen Release-Richtlinien.
196
+ **next-i18next**
197
+
198
+ - Basis-Typen für Hooks; **strikte Schlüsseltypisierung erfordert zusätzliche Werkzeuge/Konfiguration**.
199
+
200
+ </Column>
201
+ <Column>
202
+
203
+ **intlayer**
204
+
205
+ - **Erzeugt strenge Typen** aus Ihrem Inhalt. **IDE-Autovervollständigung** und **Kompilierzeit-Fehler** erkennen Tippfehler und fehlende Schlüssel vor der Bereitstellung.
206
+
207
+ </Column>
208
+ </Columns>
209
+
210
+ **Warum das wichtig ist:** Starke Typisierung verschiebt Fehler **nach links** (CI/Build) statt **nach rechts** (Laufzeit).
98
211
 
99
212
  ---
100
213
 
101
- ### 4) Routing, Middleware & URL-Strategie
214
+ ## Umgang mit fehlenden Übersetzungen
215
+
216
+ **next-intl**
217
+
218
+ - Verwendet **Fallbacks zur Laufzeit** (z. B. Anzeige des Schlüssels oder der Standardsprache). Der Build schlägt nicht fehl.
102
219
 
103
- - Alle drei arbeiten mit **Next.js lokalisiertem Routing** im App Router.
104
- - **Intlayer** geht noch weiter mit **i18n-Middleware** (Locale-Erkennung über Header/Cookies) und **Hilfsfunktionen**, um lokalisierte URLs und `<link rel="alternate" hreflang="…">`-Tags zu generieren.
220
+ **next-i18next**
105
221
 
106
- **Warum das wichtig ist:** Weniger individuelle Verbindungs-Schichten; **konsistente Benutzererfahrung** und **sauberes SEO** über alle Sprachen hinweg.
222
+ - Verwendet **Fallbacks zur Laufzeit** (z. B. Anzeige des Schlüssels oder der Standardsprache). Der Build schlägt nicht fehl.
223
+
224
+ **intlayer**
225
+
226
+ - **Erkennung zur Build-Zeit** mit **Warnungen/Fehlern** bei fehlenden Sprachen oder Schlüsseln.
227
+
228
+ **Warum das wichtig ist:** Das Erkennen von Lücken während des Builds verhindert „mysteriöse Strings“ in der Produktion und entspricht strengen Release-Gates.
107
229
 
108
230
  ---
109
231
 
110
- ### 5) Ausrichtung auf Server-Komponenten (RSC)
232
+ ## Routing, Middleware & URL-Strategie
233
+
234
+ <Columns>
235
+ <Column>
236
+
237
+ **next-intl**
238
+
239
+ - Funktioniert mit **Next.js lokalisiertem Routing** im App Router.
240
+
241
+ </Column>
242
+ <Column>
243
+
244
+ **next-i18next**
245
+
246
+ - Funktioniert mit **Next.js lokalisiertem Routing** im App Router.
247
+
248
+ </Column>
249
+ <Column>
111
250
 
112
- - **Alle** unterstützen Next.js 13+.
113
- - **Intlayer** glättet die **Server/Client-Grenze** mit einer konsistenten API und Providern, die für RSC entwickelt wurden, sodass Sie Formatierer oder t-Funktionen nicht durch Komponentenbäume schleusen müssen.
251
+ **intlayer**
114
252
 
115
- **Warum das wichtig ist:** Klareres mentales Modell und weniger Randfälle in hybriden Bäumen.
253
+ - Alles Genannte, plus **i18n Middleware** (Locale-Erkennung über Header/Cookies) und **Hilfsmittel** zur Generierung lokalisierter URLs und `<link rel="alternate" hreflang="…">` Tags.
254
+
255
+ </Column>
256
+ </Columns>
257
+
258
+ **Warum es wichtig ist:** Weniger benutzerdefinierte Verbindungs-Schichten; **konsistente Benutzererfahrung (UX)** und **sauberes SEO** über alle Sprachen hinweg.
116
259
 
117
260
  ---
118
261
 
119
- ### 6) Leistung & Ladeverhalten
262
+ ## Ausrichtung auf Server Components (RSC)
263
+
264
+ <Columns>
265
+ <Column>
266
+
267
+ **next-intl**
268
+
269
+ - Unterstützt Next.js 13+. Erfordert oft das Weiterreichen von t-Funktionen/Formatierern durch Komponentenbäume in hybriden Setups.
270
+
271
+ </Column>
272
+ <Column>
273
+
274
+ **next-i18next**
275
+
276
+ - Unterstützt Next.js 13+. Ähnliche Einschränkungen beim Weitergeben von Übersetzungswerkzeugen über Grenzen hinweg.
277
+
278
+ </Column>
279
+ <Column>
120
280
 
121
- - **next-intl / next-i18next**: Teilweise Kontrolle über **Namespaces** und **Routen-spezifische Aufteilungen**; Risiko, ungenutzte Strings mitzuliefern, wenn die Disziplin nachlässt.
122
- - **Intlayer**: **Tree-Shaking** beim Build und **Lazy-Loading pro Wörterbuch/Locale**. Unbenutzter Inhalt wird nicht ausgeliefert.
281
+ **intlayer**
123
282
 
124
- **Warum das wichtig ist:** Kleinere Bundles und schnellerer Start, besonders bei mehrsprachigen Websites.
283
+ - Unterstützt Next.js 13+ und erleichtert die **Server/Client-Grenze** mit einer konsistenten API und RSC-orientierten Providern, wodurch das Hin- und Herschicken von Formatierern oder t-Funktionen vermieden wird.
284
+
285
+ </Column>
286
+ </Columns>
287
+
288
+ **Warum es wichtig ist:** Klareres mentales Modell und weniger Randfälle in hybriden Bäumen.
125
289
 
126
290
  ---
127
291
 
128
- ### 7) DX, Tools & Wartung
292
+ ## DX, Tools & Wartung
293
+
294
+ <Columns>
295
+ <Column>
296
+
297
+ **next-intl**
298
+
299
+ - Wird häufig mit externen Lokalisierungsplattformen und redaktionellen Workflows kombiniert.
300
+
301
+ </Column>
302
+ <Column>
303
+
304
+ **next-i18next**
305
+
306
+ - Wird häufig mit externen Lokalisierungsplattformen und redaktionellen Workflows kombiniert.
307
+
308
+ </Column>
309
+ <Column>
310
+
311
+ **intlayer**
312
+
313
+ - Bietet einen **kostenlosen Visual Editor** und ein **optionales CMS** (Git-freundlich oder externalisiert) sowie eine **VSCode-Erweiterung** und **KI-unterstützte Übersetzungen** mit Ihren eigenen Anbieter-Schlüsseln.
314
+
315
+ </Column>
316
+ </Columns>
317
+
318
+ **Warum das wichtig ist:** Senkt die Betriebskosten und verkürzt den Kommunikationszyklus zwischen Entwicklern und Inhaltserstellern.
319
+
320
+ ## Integration mit Lokalisierungsplattformen (TMS)
321
+
322
+ Große Organisationen verlassen sich oft auf Translation Management Systeme (TMS) wie **Crowdin**, **Phrase**, **Lokalise**, **Localizely** oder **Localazy**.
323
+
324
+ - **Warum Unternehmen das wichtig finden**
325
+ - **Zusammenarbeit & Rollen**: Mehrere Akteure sind beteiligt: Entwickler, Produktmanager, Übersetzer, Prüfer, Marketingteams.
326
+ - **Skalierung & Effizienz**: kontinuierliche Lokalisierung, kontextbezogene Überprüfung.
327
+
328
+ - **next-intl / next-i18next**
329
+ - Verwenden typischerweise **zentralisierte JSON-Kataloge**, sodass der Export/Import mit TMS unkompliziert ist.
330
+ - Ausgereifte Ökosysteme und Beispiele/Integrationen für die oben genannten Plattformen.
331
+
332
+ - **Intlayer**
333
+ - Fördert **dezentralisierte, komponentenbezogene Wörterbücher** und unterstützt **TypeScript/TSX/JS/JSON/MD** Inhalte.
334
+ - Dies verbessert die Modularität im Code, kann jedoch die Plug-and-Play-Integration von TMS erschweren, wenn ein Tool zentralisierte, flache JSON-Dateien erwartet.
335
+ - Intlayer bietet Alternativen: **KI-unterstützte Übersetzungen** (unter Verwendung eigener Anbieter-Schlüssel), einen **Visuellen Editor/CMS** und **CLI/CI** Workflows, um Lücken zu erkennen und vorab zu füllen.
336
+
337
+ > Hinweis: `next-intl` und `i18next` akzeptieren ebenfalls TypeScript-Kataloge. Wenn Ihr Team Nachrichten in `.ts`-Dateien speichert oder diese nach Features dezentralisiert, können ähnliche Schwierigkeiten mit TMS auftreten. Viele `next-intl`-Setups bleiben jedoch zentralisiert in einem `locales/`-Ordner, was die Umwandlung in JSON für TMS etwas erleichtert.
338
+
339
+ ## Entwicklererfahrung
340
+
341
+ Dieser Abschnitt bietet einen tiefgehenden Vergleich der drei Lösungen. Anstatt einfache Fälle zu betrachten, wie sie in der „Erste Schritte“-Dokumentation jeder Lösung beschrieben sind, betrachten wir einen realen Anwendungsfall, der einem echten Projekt ähnlicher ist.
342
+
343
+ ### App-Struktur
344
+
345
+ Die App-Struktur ist wichtig, um eine gute Wartbarkeit Ihres Codes sicherzustellen.
346
+
347
+ <Tab defaultTab="next-intl" group='techno'>
348
+
349
+ <TabItem label="next-i18next" value="next-i18next">
350
+
351
+ ```bash
352
+ .
353
+ ├── i18n.config.ts
354
+ └── src
355
+ ├── locales
356
+ │ ├── en
357
+ │ │ ├── common.json
358
+ │ │ └── about.json
359
+ │ └── fr
360
+ │ ├── common.json
361
+ │ └── about.json
362
+ ├── app
363
+ │ ├── i18n
364
+ │ │ └── server.ts
365
+ │ └── [locale]
366
+ │ ├── layout.tsx
367
+ │ └── about.tsx
368
+ └── components
369
+ ├── I18nProvider.tsx
370
+ ├── ClientComponent.tsx
371
+ └── ServerComponent.tsx
372
+ ```
373
+
374
+ </TabItem>
375
+ <TabItem label="next-intl" value="next-intl">
376
+
377
+ ```bash
378
+ .
379
+ ├── i18n.ts
380
+ ├── locales
381
+ │ ├── en
382
+ │ │ ├── home.json
383
+ │ │ └── navbar.json
384
+ │ ├── fr
385
+ │ │ ├── home.json
386
+ │ │ └── navbar.json
387
+ │ └── es
388
+ │ ├── home.json
389
+ │ └── navbar.json
390
+ └── src
391
+ ├── middleware.ts
392
+ ├── app
393
+ │ ├── i18n
394
+ │ │ └── server.ts
395
+ │ └── [locale]
396
+ │ └── home.tsx
397
+ └── components
398
+ └── Navbar
399
+ └── index.tsx
400
+ ```
401
+
402
+ </TabItem>
403
+ <TabItem label="intlayer" value="intlayer">
404
+
405
+ ```bash
406
+ .
407
+ ├── intlayer.config.ts
408
+ └── src
409
+ ├── middleware.ts
410
+ ├── app
411
+ │ └── [locale]
412
+ │ ├── layout.tsx
413
+ │ └── home
414
+ │ ├── index.tsx
415
+ │ └── index.content.ts
416
+ └── components
417
+ └── Navbar
418
+ ├── index.tsx
419
+ └── index.content.ts
420
+ ```
421
+
422
+ </TabItem>
423
+ </Tab>
424
+
425
+ #### Vergleich
426
+
427
+ - **next-intl / next-i18next**: Zentralisierte Kataloge (JSON; Namespaces/Nachrichten). Klare Struktur, gute Integration mit Übersetzungsplattformen, kann jedoch bei wachsender App zu mehr Datei-übergreifenden Änderungen führen.
428
+ - **Intlayer**: Pro-Komponenten `.content.{ts|js|json}` Wörterbücher, die direkt bei den Komponenten liegen. Einfachere Wiederverwendung von Komponenten und lokale Nachvollziehbarkeit; fügt Dateien hinzu und setzt auf Build-Zeit-Tools.
429
+
430
+ #### Einrichtung und Laden von Inhalten
431
+
432
+ Wie bereits erwähnt, müssen Sie optimieren, wie jede JSON-Datei in Ihren Code importiert wird.
433
+ Wie die Bibliothek das Laden von Inhalten handhabt, ist wichtig.
434
+
435
+ <Tab defaultTab="next-intl" group='techno'>
436
+ <TabItem label="next-i18next" value="next-i18next">
437
+
438
+ ```ts fileName="i18n.config.ts"
439
+ export const locales = ["en", "fr"] as const;
440
+ export type Locale = (typeof locales)[number];
441
+
442
+ export const defaultLocale: Locale = "en";
443
+
444
+ export const rtlLocales = ["ar", "he", "fa", "ur"] as const;
445
+ export const isRtl = (locale: string) =>
446
+ (rtlLocales as readonly string[]).includes(locale);
447
+
448
+ export function localizedPath(locale: string, path: string) {
449
+ return locale === defaultLocale ? path : "/" + locale + path;
450
+ }
451
+
452
+ const ORIGIN = "https://example.com";
453
+ export function abs(locale: string, path: string) {
454
+ return ORIGIN + localizedPath(locale, path);
455
+ }
456
+ ```
457
+
458
+ ```ts fileName="src/app/i18n/server.ts"
459
+ import { createInstance } from "i18next";
460
+ import { initReactI18next } from "react-i18next/initReactI18next";
461
+ import resourcesToBackend from "i18next-resources-to-backend";
462
+ import { defaultLocale } from "@/i18n.config";
463
+
464
+ // Load JSON resources from src/locales/<locale>/<namespace>.json
465
+ const backend = resourcesToBackend(
466
+ (locale: string, namespace: string) =>
467
+ import(`../../locales/${locale}/${namespace}.json`)
468
+ );
469
+
470
+ export async function initI18next(
471
+ locale: string,
472
+ namespaces: string[] = ["common"]
473
+ ) {
474
+ const i18n = createInstance();
475
+ await i18n
476
+ .use(initReactI18next)
477
+ .use(backend)
478
+ .init({
479
+ lng: locale,
480
+ fallbackLng: defaultLocale,
481
+ ns: namespaces,
482
+ defaultNS: "common",
483
+ interpolation: { escapeValue: false },
484
+ react: { useSuspense: false },
485
+ });
486
+ return i18n;
487
+ }
488
+ ```
489
+
490
+ ```tsx fileName="src/components/I18nProvider.tsx"
491
+ "use client";
492
+
493
+ import * as React from "react";
494
+ import { I18nextProvider } from "react-i18next";
495
+ import { createInstance } from "i18next";
496
+ import { initReactI18next } from "react-i18next/initReactI18next";
497
+ import resourcesToBackend from "i18next-resources-to-backend";
498
+ import { defaultLocale } from "@/i18n.config";
499
+
500
+ const backend = resourcesToBackend(
501
+ (locale: string, namespace: string) =>
502
+ import(`../../locales/${locale}/${namespace}.json`)
503
+ );
504
+
505
+ type Props = {
506
+ locale: string;
507
+ namespaces?: string[];
508
+ resources?: Record<string, any>; // { ns: bundle }
509
+ children: React.ReactNode;
510
+ };
511
+
512
+ export default function I18nProvider({
513
+ locale,
514
+ namespaces = ["common"],
515
+ resources,
516
+ children,
517
+ }: Props) {
518
+ const [i18n] = React.useState(() => {
519
+ const i = createInstance();
520
+
521
+ i.use(initReactI18next)
522
+ .use(backend)
523
+ .init({
524
+ lng: locale,
525
+ fallbackLng: defaultLocale,
526
+ ns: namespaces,
527
+ resources: resources ? { [locale]: resources } : undefined,
528
+ defaultNS: "common",
529
+ interpolation: { escapeValue: false },
530
+ react: { useSuspense: false },
531
+ });
532
+
533
+ return i;
534
+ });
535
+
536
+ return <I18nextProvider i18n={i18n}>{children}</I18nextProvider>;
537
+ }
538
+ ```
539
+
540
+ ```tsx fileName="src/app/[locale]/layout.tsx"
541
+ import type { ReactNode } from "react";
542
+ import { locales, defaultLocale, isRtl, type Locale } from "@/i18n.config";
543
+
544
+ export const dynamicParams = false;
545
+
546
+ export function generateStaticParams() {
547
+ return locales.map((locale) => ({ locale }));
548
+ }
549
+
550
+ export default function LocaleLayout({
551
+ children,
552
+ params,
553
+ }: {
554
+ children: ReactNode;
555
+ params: { locale: string };
556
+ }) {
557
+ const locale: Locale = (locales as readonly string[]).includes(params.locale)
558
+ ? (params.locale as any)
559
+ : defaultLocale;
560
+
561
+ const dir = isRtl(locale) ? "rtl" : "ltr";
562
+
563
+ return (
564
+ <html lang={locale} dir={dir}>
565
+ <body>{children}</body>
566
+ </html>
567
+ );
568
+ }
569
+ ```
570
+
571
+ ```tsx fileName="src/app/[locale]/about.tsx"
572
+ import I18nProvider from "@/components/I18nProvider";
573
+ import { initI18next } from "@/app/i18n/server";
574
+ import type { Locale } from "@/i18n.config";
575
+ import ClientComponent from "@/components/ClientComponent";
576
+ import ServerComponent from "@/components/ServerComponent";
577
+
578
+ // Force static rendering for the page
579
+ export const dynamic = "force-static";
580
+
581
+ export default async function AboutPage({
582
+ params: { locale },
583
+ }: {
584
+ params: { locale: Locale };
585
+ }) {
586
+ const namespaces = ["common", "about"] as const;
587
+
588
+ const i18n = await initI18next(locale, [...namespaces]);
589
+ const tAbout = i18n.getFixedT(locale, "about");
590
+
591
+ return (
592
+ <I18nProvider locale={locale} namespaces={[...namespaces]}>
593
+ <main>
594
+ <h1>{tAbout("title")}</h1>
595
+
596
+ <ClientComponent />
597
+ <ServerComponent t={tAbout} locale={locale} count={0} />
598
+ </main>
599
+ </I18nProvider>
600
+ );
601
+ }
602
+ ```
603
+
604
+ </TabItem>
605
+ <TabItem label="next-intl" value="next-intl">
606
+
607
+ ```tsx fileName="src/i18n.ts"
608
+ import { getRequestConfig } from "next-intl/server";
609
+ import { notFound } from "next/navigation";
610
+
611
+ export const locales = ["en", "fr", "es"] as const;
612
+ export const defaultLocale = "en" as const;
613
+
614
+ async function loadMessages(locale: string) {
615
+ // Load only the namespaces your layout/pages need
616
+ const [common, about] = await Promise.all([
617
+ import(`../locales/${locale}/common.json`).then((m) => m.default),
618
+ import(`../locales/${locale}/about.json`).then((m) => m.default),
619
+ ]);
620
+
621
+ return { common, about } as const;
622
+ }
623
+
624
+ export default getRequestConfig(async ({ locale }) => {
625
+ if (!locales.includes(locale as any)) notFound();
626
+
627
+ return {
628
+ messages: await loadMessages(locale),
629
+ };
630
+ });
631
+ ```
632
+
633
+ ```tsx fileName="src/app/[locale]/layout.tsx"
634
+ import type { ReactNode } from "react";
635
+ import { locales } from "@/i18n";
636
+ import {
637
+ getLocaleDirection,
638
+ unstable_setRequestLocale,
639
+ } from "next-intl/server";
640
+
641
+ export const dynamic = "force-static";
642
+
643
+ export function generateStaticParams() {
644
+ return locales.map((locale) => ({ locale }));
645
+ }
646
+
647
+ export default async function LocaleLayout({
648
+ children,
649
+ params,
650
+ }: {
651
+ children: ReactNode;
652
+ params: Promise<{ locale: string }>;
653
+ }) {
654
+ const { locale } = await params;
655
+
656
+ // Set the active request locale for this server render (RSC)
657
+ unstable_setRequestLocale(locale);
129
658
 
130
- - **next-intl / next-i18next**: In der Regel binden Sie externe Plattformen für Übersetzungen und redaktionelle Workflows ein.
131
- - **Intlayer**: Bietet einen **kostenlosen Visual Editor** und ein **optionales CMS** (Git-freundlich oder extern). Außerdem eine **VSCode-Erweiterung** für die Inhaltserstellung und **KI-unterstützte Übersetzungen** mit Ihren eigenen Provider-Schlüsseln.
659
+ const dir = getLocaleDirection(locale);
132
660
 
133
- **Warum es wichtig ist:** Senkt die Betriebskosten und verkürzt die Schleife zwischen Entwicklern und Inhaltserstellern.
661
+ return (
662
+ <html lang={locale} dir={dir}>
663
+ <body>{children}</body>
664
+ </html>
665
+ );
666
+ }
667
+ ```
668
+
669
+ ```tsx fileName="src/app/[locale]/about/page.tsx"
670
+ import { getTranslations, getMessages, getFormatter } from "next-intl/server";
671
+ import { NextIntlClientProvider } from "next-intl";
672
+ import pick from "lodash/pick";
673
+ import ServerComponent from "@/components/ServerComponent";
674
+ import ClientComponentExample from "@/components/ClientComponentExample";
675
+
676
+ export const dynamic = "force-static";
677
+
678
+ export default async function AboutPage({
679
+ params,
680
+ }: {
681
+ params: Promise<{ locale: string }>;
682
+ }) {
683
+ const { locale } = await params;
684
+
685
+ // Messages are loaded server-side. Push only what's needed to the client.
686
+ const messages = await getMessages();
687
+ const clientMessages = pick(messages, ["common", "about"]);
688
+
689
+ // Strictly server-side translations/formatting
690
+ const tAbout = await getTranslations("about");
691
+ const tCounter = await getTranslations("about.counter");
692
+ const format = await getFormatter();
693
+
694
+ const initialFormattedCount = format.number(0);
695
+
696
+ return (
697
+ <NextIntlClientProvider locale={locale} messages={clientMessages}>
698
+ <main>
699
+ <h1>{tAbout("title")}</h1>
700
+ <ClientComponentExample />
701
+ <ServerComponent
702
+ formattedCount={initialFormattedCount}
703
+ label={tCounter("label")}
704
+ increment={tCounter("increment")}
705
+ />
706
+ </main>
707
+ </NextIntlClientProvider>
708
+ );
709
+ }
710
+ ```
711
+
712
+ </TabItem>
713
+ <TabItem label="intlayer" value="intlayer">
714
+
715
+ ```tsx fileName="intlayer.config.ts"
716
+ import { type IntlayerConfig, Locales } from "intlayer";
717
+
718
+ const config: IntlayerConfig = {
719
+ internationalization: {
720
+ locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
721
+ defaultLocale: Locales.ENGLISH,
722
+ },
723
+ };
724
+
725
+ export default config;
726
+ ```
727
+
728
+ ```tsx fileName="src/app/[locale]/layout.tsx"
729
+ import { getHTMLTextDir } from "intlayer";
730
+ import {
731
+ IntlayerClientProvider,
732
+ generateStaticParams,
733
+ type NextLayoutIntlayer,
734
+ } from "next-intlayer";
735
+
736
+ export const dynamic = "force-static";
737
+
738
+ const LocaleLayout: NextLayoutIntlayer = async ({ children, params }) => {
739
+ const { locale } = await params;
740
+
741
+ return (
742
+ <html lang={locale} dir={getHTMLTextDir(locale)}>
743
+ <body>
744
+ <IntlayerClientProvider locale={locale}>
745
+ {children}
746
+ </IntlayerClientProvider>
747
+ </body>
748
+ </html>
749
+ );
750
+ };
751
+
752
+ export default LandingLayout;
753
+ ```
754
+
755
+ ```tsx fileName="src/app/[locale]/about/page.tsx"
756
+ import { PageContent } from "@components/PageContent";
757
+ import type { NextPageIntlayer } from "next-intlayer";
758
+ import { IntlayerServerProvider, useIntlayer } from "next-intlayer/server";
759
+ import { ClientComponent, ServerComponent } from "@components";
760
+
761
+ const LandingPage: NextPageIntlayer = async ({ params }) => {
762
+ const { locale } = await params;
763
+ const { title } = useIntlayer("about", locale);
764
+
765
+ return (
766
+ <IntlayerServerProvider locale={locale}>
767
+ <main>
768
+ <h1>{title}</h1>
769
+ <ClientComponent />
770
+ <ServerComponent />
771
+ </main>
772
+ </IntlayerServerProvider>
773
+ );
774
+ };
775
+
776
+ export default LandingPage;
777
+ ```
778
+
779
+ </TabItem>
780
+ </Tab>
781
+
782
+ #### Vergleich
783
+
784
+ Alle drei unterstützen das Laden von Inhalten und Providern pro Locale.
785
+
786
+ - Mit **next-intl/next-i18next** laden Sie typischerweise ausgewählte Nachrichten/Namensräume pro Route und platzieren Provider dort, wo sie benötigt werden.
787
+
788
+ - Mit **Intlayer** wird eine Build-Zeit-Analyse hinzugefügt, um die Nutzung abzuleiten, was manuelle Verkabelung reduzieren und möglicherweise einen einzigen Root-Provider ermöglichen kann.
789
+
790
+ Wählen Sie je nach Teampräferenz zwischen expliziter Kontrolle und Automatisierung.
791
+
792
+ ### Verwendung in einer Client-Komponente
793
+
794
+ Nehmen wir ein Beispiel einer Client-Komponente, die einen Zähler rendert.
795
+
796
+ <Tab defaultTab="next-intl" group='techno'>
797
+ <TabItem label="next-i18next" value="next-i18next">
798
+
799
+ **Übersetzungen (müssen echtes JSON in `public/locales/...` sein)**
800
+
801
+ ```json fileName="public/locales/en/about.json"
802
+ {
803
+ "counter": {
804
+ "label": "Counter",
805
+ "increment": "Increment"
806
+ }
807
+ }
808
+ ```
809
+
810
+ ```json fileName="public/locales/fr/about.json"
811
+ {
812
+ "counter": {
813
+ "label": "Compteur",
814
+ "increment": "Incrémenter"
815
+ }
816
+ }
817
+ ```
818
+
819
+ **Client-Komponente**
820
+
821
+ ```tsx fileName="src/components/ClientComponentExample.tsx"
822
+ "use client";
823
+
824
+ import React, { useMemo, useState } from "react";
825
+ import { useTranslation } from "next-i18next";
826
+
827
+ const ClientComponentExample = () => {
828
+ const { t, i18n } = useTranslation("about");
829
+ const [count, setCount] = useState(0);
830
+
831
+ // next-i18next stellt useNumber nicht bereit; benutze Intl.NumberFormat
832
+ const numberFormat = new Intl.NumberFormat(i18n.language);
833
+
834
+ return (
835
+ <div>
836
+ <p>{numberFormat.format(count)}</p>
837
+ <button
838
+ aria-label={t("counter.label")}
839
+ onClick={() => setCount((count) => count + 1)}
840
+ >
841
+ {t("counter.increment")}
842
+ </button>
843
+ </div>
844
+ );
845
+ };
846
+ ```
847
+
848
+ > Vergessen Sie nicht, den Namespace "about" bei den serverSideTranslations der Seite hinzuzufügen
849
+ > Hier verwenden wir die Version React 19.x.x, aber bei niedrigeren Versionen müssen Sie useMemo verwenden, um die Instanz des Formatierers zu speichern, da dies eine ressourcenintensive Funktion ist
850
+
851
+ </TabItem>
852
+ <TabItem label="next-intl" value="next-intl">
853
+
854
+ **Übersetzungen (Struktur wiederverwendet; laden Sie sie in die next-intl-Nachrichten, wie Sie möchten)**
855
+
856
+ ```json fileName="locales/en/about.json"
857
+ {
858
+ "counter": {
859
+ "label": "Counter",
860
+ "increment": "Increment"
861
+ }
862
+ }
863
+ ```
864
+
865
+ ```json fileName="locales/fr/about.json"
866
+ {
867
+ "counter": {
868
+ "label": "Compteur",
869
+ "increment": "Incrémenter"
870
+ }
871
+ }
872
+ ```
873
+
874
+ **Client-Komponente**
875
+
876
+ ```tsx fileName="src/components/ClientComponentExample.tsx"
877
+ "use client";
878
+
879
+ import React, { useState } from "react";
880
+ import { useTranslations, useFormatter } from "next-intl";
881
+
882
+ const ClientComponentExample = () => {
883
+ // Direkt auf das verschachtelte Objekt zugreifen
884
+ const t = useTranslations("about.counter");
885
+ const format = useFormatter();
886
+ const [count, setCount] = useState(0);
887
+
888
+ return (
889
+ <div>
890
+ <p>{format.number(count)}</p>
891
+ <button
892
+ aria-label={t("label")}
893
+ onClick={() => setCount((count) => count + 1)}
894
+ >
895
+ {t("increment")}
896
+ </button>
897
+ </div>
898
+ );
899
+ };
900
+ ```
901
+
902
+ > Vergessen Sie nicht, die "about"-Nachricht auf der Client-Seite der Seite hinzuzufügen
903
+
904
+ </TabItem>
905
+ <TabItem label="intlayer" value="intlayer">
906
+
907
+ **Inhalt**
908
+
909
+ ```ts fileName="src/components/ClientComponentExample/index.content.ts"
910
+ import { t, type Dictionary } from "intlayer";
911
+
912
+ const counterContent = {
913
+ key: "counter",
914
+ content: {
915
+ label: t({ de: "Zähler", en: "Counter", fr: "Compteur" }),
916
+ increment: t({ de: "Erhöhen", en: "Increment", fr: "Incrémenter" }),
917
+ },
918
+ } satisfies Dictionary;
919
+
920
+ export default counterContent;
921
+ ```
922
+
923
+ **Client-Komponente**
924
+
925
+ ```tsx fileName="src/components/ClientComponentExample/index.tsx"
926
+ "use client";
927
+
928
+ import React, { useState } from "react";
929
+ import { useNumber, useIntlayer } from "next-intlayer";
930
+
931
+ const ClientComponentExample = () => {
932
+ const [count, setCount] = useState(0);
933
+ const { label, increment } = useIntlayer("counter"); // gibt Strings zurück
934
+ const { number } = useNumber();
935
+
936
+ return (
937
+ <div>
938
+ <p>{number(count)}</p>
939
+ <button aria-label={label} onClick={() => setCount((count) => count + 1)}>
940
+ {increment}
941
+ </button>
942
+ </div>
943
+ );
944
+ };
945
+ ```
946
+
947
+ </TabItem>
948
+ </Tab>
949
+
950
+ #### Vergleich
951
+
952
+ - **Zahlenformatierung**
953
+ - **next-i18next**: kein `useNumber`; verwendet `Intl.NumberFormat` (oder i18next-icu).
954
+ - **next-intl**: `useFormatter().number(value)`.
955
+ - **Intlayer**: integriertes `useNumber()`.
956
+
957
+ - **Schlüssel**
958
+ - Behalte eine verschachtelte Struktur bei (`about.counter.label`) und passe deinen Hook entsprechend an (`useTranslation("about")` + `t("counter.label")` oder `useTranslations("about.counter")` + `t("label")`).
959
+
960
+ - **Dateiablagen**
961
+ - **next-i18next** erwartet JSON in `public/locales/{lng}/{ns}.json`.
962
+ - **next-intl** ist flexibel; lade Nachrichten wie du es konfigurierst.
963
+ - **Intlayer** speichert Inhalte in TS/JS Wörterbüchern und löst sie über Schlüssel auf.
134
964
 
135
965
  ---
136
966
 
137
- ## Wann welche Lösung wählen?
967
+ ### Verwendung in einer Server-Komponente
968
+
969
+ Wir betrachten den Fall einer UI-Komponente. Diese Komponente ist eine Server-Komponente und sollte als Kind einer Client-Komponente eingefügt werden können. (Seite (Server-Komponente) -> Client-Komponente -> Server-Komponente). Da diese Komponente als Kind einer Client-Komponente eingefügt werden kann, darf sie nicht asynchron sein.
970
+
971
+ <Tab defaultTab="next-intl" group='techno'>
972
+ <TabItem label="next-i18next" value="next-i18next">
973
+
974
+ ```tsx fileName="src/pages/about.tsx"
975
+ import type { GetStaticProps } from "next";
976
+ import { useTranslation } from "next-i18next";
977
+
978
+ type ServerComponentProps = {
979
+ count: number;
980
+ };
981
+
982
+ const ServerComponent = ({ count }: ServerComponentProps) => {
983
+ const { t, i18n } = useTranslation("about");
984
+ const formatted = new Intl.NumberFormat(i18n.language).format(count);
985
+
986
+ return (
987
+ <div>
988
+ <p>{formatted}</p>
989
+ <button aria-label={t("counter.label")}>{t("counter.increment")}</button>
990
+ </div>
991
+ );
992
+ };
993
+ ```
994
+
995
+ > Da die Server-Komponente nicht asynchron sein kann, müssen die Übersetzungen und die Formatierungsfunktion als Props übergeben werden.
996
+
997
+ </TabItem>
998
+ <TabItem label="next-intl" value="next-intl">
999
+
1000
+ ```tsx fileName="src/components/ServerComponent.tsx"
1001
+ type ServerComponentProps = {
1002
+ count: number;
1003
+ t: (key: string) => string;
1004
+ formatter: Intl.NumberFormat;
1005
+ };
1006
+
1007
+ const ServerComponent = ({ t, count, formatter }: ServerComponentProps) => {
1008
+ const formatted = formatter.format(count);
1009
+
1010
+ return (
1011
+ <div>
1012
+ <p>{formatted}</p>
1013
+ <button aria-label={t("label")}>{t("increment")}</button>
1014
+ </div>
1015
+ );
1016
+ };
1017
+ ```
1018
+
1019
+ > Da die Server-Komponente nicht asynchron sein kann, müssen Sie die Übersetzungen und die Formatierungsfunktion als Props übergeben.
1020
+ >
1021
+ > - `const t = await getTranslations("about.counter");`
1022
+ > - `const formatter = await getFormatter().then((formatter) => formatter.number());`
138
1023
 
139
- - **Wählen Sie next-intl**, wenn Sie eine **minimale** Lösung wünschen, mit zentralisierten Katalogen vertraut sind und Ihre App **klein bis mittelgroß** ist.
140
- - **Wählen Sie next-i18next**, wenn Sie das **Plugin-Ökosystem von i18next** benötigen (z. B. erweiterte ICU-Regeln über Plugins) und Ihr Team i18next bereits kennt, wobei Sie **mehr Konfiguration** für Flexibilität akzeptieren.
141
- - **Wählen Sie Intlayer**, wenn Sie **komponentenbezogenen Inhalt**, **striktes TypeScript**, **Build-Zeit-Garantien**, **Tree-Shaking** und **umfangreiche Routing/SEO/Editor-Tools** schätzen – insbesondere für den **Next.js App Router** und **große, modulare Codebasen**.
1024
+ </TabItem>
1025
+ <TabItem label="intlayer" value="intlayer">
1026
+
1027
+ ```tsx fileName="src/components/ServerComponent.tsx"
1028
+ import { useIntlayer, useNumber } from "next-intlayer/server";
1029
+
1030
+ const ServerComponent = ({ count }: { count: number }) => {
1031
+ const { label, increment } = useIntlayer("counter");
1032
+ const { number } = useNumber();
1033
+
1034
+ return (
1035
+ <div>
1036
+ <p>{number(count)}</p>
1037
+ <button aria-label={label}>{increment}</button>
1038
+ </div>
1039
+ );
1040
+ };
1041
+ ```
1042
+
1043
+ </TabItem>
1044
+ </Tab>
1045
+
1046
+ > Intlayer stellt **server-sichere** Hooks über `next-intlayer/server` bereit. Damit sie funktionieren, verwenden `useIntlayer` und `useNumber` eine hook-ähnliche Syntax, ähnlich wie die Client-Hooks, basieren aber im Hintergrund auf dem Server-Kontext (`IntlayerServerProvider`).
1047
+
1048
+ ### Metadaten / Sitemap / Robots
1049
+
1050
+ Inhalte zu übersetzen ist großartig. Aber oft wird vergessen, dass das Hauptziel der Internationalisierung darin besteht, Ihre Website für die Welt sichtbarer zu machen. I18n ist ein unglaublicher Hebel, um die Sichtbarkeit Ihrer Website zu verbessern.
1051
+
1052
+ Hier ist eine Liste von Best Practices bezüglich mehrsprachigem SEO.
1053
+
1054
+ - Setzen Sie hreflang-Meta-Tags im `<head>`-Tag
1055
+ > Dies hilft Suchmaschinen zu verstehen, welche Sprachen auf der Seite verfügbar sind
1056
+ - Listen Sie alle Seitenübersetzungen in der sitemap.xml unter Verwendung des XML-Schemas `http://www.w3.org/1999/xhtml` auf
1057
+ >
1058
+ - Vergessen Sie nicht, vorangestellte Seiten in der robots.txt auszuschließen (z. B. `/dashboard` sowie `/fr/dashboard`, `/es/dashboard`)
1059
+ >
1060
+ - Verwenden Sie eine benutzerdefinierte Link-Komponente, um auf die am besten lokalisierte Seite weiterzuleiten (z. B. auf Französisch `<a href="/fr/about">A propos</a>`)
1061
+ >
1062
+
1063
+ Entwickler vergessen oft, ihre Seiten über verschiedene Sprachversionen hinweg korrekt zu referenzieren.
1064
+
1065
+ <Tab defaultTab="next-intl" group='techno'>
1066
+
1067
+ <TabItem label="next-i18next" value="next-i18next">
1068
+
1069
+ ```ts fileName="i18n.config.ts"
1070
+ export const locales = ["en", "fr"] as const;
1071
+ export type Locale = (typeof locales)[number];
1072
+ export const defaultLocale: Locale = "en";
1073
+
1074
+ export function localizedPath(locale: string, path: string) {
1075
+ return locale === defaultLocale ? path : "/" + locale + path;
1076
+ }
1077
+
1078
+ const ORIGIN = "https://example.com";
1079
+ export function abs(locale: string, path: string) {
1080
+ return ORIGIN + localizedPath(locale, path);
1081
+ }
1082
+ ```
1083
+
1084
+ ```tsx fileName="src/app/[locale]/about/layout.tsx"
1085
+ import type { Metadata } from "next";
1086
+ import { locales, defaultLocale, localizedPath } from "@/i18n.config";
1087
+
1088
+ export async function generateMetadata({
1089
+ params,
1090
+ }: {
1091
+ params: { locale: string };
1092
+ }): Promise<Metadata> {
1093
+ const { locale } = params;
1094
+
1095
+ // Dynamisch die korrekte JSON-Datei importieren
1096
+ const messages = (
1097
+ await import("@/../public/locales/" + locale + "/about.json")
1098
+ ).default;
1099
+
1100
+ const languages = Object.fromEntries(
1101
+ locales.map((locale) => [locale, localizedPath(locale, "/about")])
1102
+ );
1103
+
1104
+ return {
1105
+ title: messages.title,
1106
+ description: messages.description,
1107
+ alternates: {
1108
+ canonical: localizedPath(locale, "/about"),
1109
+ languages: { ...languages, "x-default": "/about" },
1110
+ },
1111
+ };
1112
+ }
1113
+
1114
+ export default async function AboutPage() {
1115
+ return <h1>Über</h1>; // Überschrift für die "Über"-Seite
1116
+ }
1117
+ ```
1118
+
1119
+ ```ts fileName="src/app/sitemap.ts"
1120
+ import type { MetadataRoute } from "next";
1121
+ import { locales, defaultLocale, abs } from "@/i18n.config";
1122
+
1123
+ export default function sitemap(): MetadataRoute.Sitemap {
1124
+ const languages = Object.fromEntries(
1125
+ locales.map((locale) => [locale, abs(locale, "/about")])
1126
+ );
1127
+ return [
1128
+ {
1129
+ url: abs(defaultLocale, "/about"),
1130
+ lastModified: new Date(), // Datum der letzten Änderung
1131
+ changeFrequency: "monthly", // Änderungsfrequenz der Seite
1132
+ priority: 0.7, // Priorität der Seite im Sitemap
1133
+ alternates: { languages }, // Alternative Sprachversionen
1134
+ },
1135
+ ];
1136
+ }
1137
+ ```
1138
+
1139
+ ```ts fileName="src/app/robots.ts"
1140
+ import type { MetadataRoute } from "next";
1141
+ import { locales, defaultLocale, localizedPath } from "@/i18n.config";
1142
+
1143
+ const ORIGIN = "https://example.com";
1144
+
1145
+ const expandAllLocales = (path: string) => [
1146
+ localizedPath(defaultLocale, path),
1147
+ ...locales
1148
+ .filter((locale) => locale !== defaultLocale)
1149
+ .map((locale) => localizedPath(locale, path)),
1150
+ ];
1151
+
1152
+ export default function robots(): MetadataRoute.Robots {
1153
+ const disallow = [
1154
+ ...expandAllLocales("/dashboard"),
1155
+ ...expandAllLocales("/admin"),
1156
+ ];
1157
+
1158
+ return {
1159
+ rules: { userAgent: "*", allow: ["/"], disallow },
1160
+ host: ORIGIN,
1161
+ sitemap: ORIGIN + "/sitemap.xml",
1162
+ };
1163
+ }
1164
+ ```
1165
+
1166
+ </TabItem>
1167
+ <TabItem label="next-intl" value="next-intl">
1168
+
1169
+ ```tsx fileName="src/app/[locale]/about/layout.tsx"
1170
+ import type { Metadata } from "next";
1171
+ import { locales, defaultLocale } from "@/i18n";
1172
+ import { getTranslations } from "next-intl/server";
1173
+
1174
+ function localizedPath(locale: string, path: string) {
1175
+ return locale === defaultLocale ? path : "/" + locale + path;
1176
+ }
1177
+
1178
+ export async function generateMetadata({
1179
+ params,
1180
+ }: {
1181
+ params: { locale: string };
1182
+ }): Promise<Metadata> {
1183
+ const { locale } = params;
1184
+ const t = await getTranslations({ locale, namespace: "about" });
1185
+
1186
+ const url = "/about";
1187
+ const languages = Object.fromEntries(
1188
+ locales.map((locale) => [locale, localizedPath(locale, url)])
1189
+ );
1190
+
1191
+ return {
1192
+ title: t("title"),
1193
+ description: t("description"),
1194
+ alternates: {
1195
+ canonical: localizedPath(locale, url),
1196
+ languages: { ...languages, "x-default": url },
1197
+ },
1198
+ };
1199
+ }
1200
+
1201
+ // ... Restlicher Seiten-Code
1202
+ ```
1203
+
1204
+ ```tsx fileName="src/app/sitemap.ts"
1205
+ import type { MetadataRoute } from "next";
1206
+ import { locales, defaultLocale } from "@/i18n";
1207
+
1208
+ const origin = "https://example.com";
1209
+
1210
+ const formatterLocalizedPath = (locale: string, path: string) =>
1211
+ locale === defaultLocale ? origin + path : origin + "/" + locale + path;
1212
+
1213
+ export default function sitemap(): MetadataRoute.Sitemap {
1214
+ const aboutLanguages = Object.fromEntries(
1215
+ locales.map((l) => [l, formatterLocalizedPath(l, "/about")])
1216
+ );
1217
+
1218
+ return [
1219
+ {
1220
+ url: formatterLocalizedPath(defaultLocale, "/about"),
1221
+ lastModified: new Date(),
1222
+ changeFrequency: "monatlich",
1223
+ priority: 0.7,
1224
+ alternates: { languages: aboutLanguages },
1225
+ },
1226
+ ];
1227
+ }
1228
+ ```
1229
+
1230
+ ```tsx fileName="src/app/robots.ts"
1231
+ import type { MetadataRoute } from "next";
1232
+ import { locales, defaultLocale } from "@/i18n";
1233
+
1234
+ const origin = "https://example.com";
1235
+ const withAllLocales = (path: string) => [
1236
+ path,
1237
+ ...locales
1238
+ .filter((locale) => locale !== defaultLocale)
1239
+ .map((locale) => "/" + locale + path),
1240
+ ];
1241
+
1242
+ export default function robots(): MetadataRoute.Robots {
1243
+ const disallow = [
1244
+ ...withAllLocales("/dashboard"),
1245
+ ...withAllLocales("/admin"),
1246
+ ];
1247
+
1248
+ return {
1249
+ rules: { userAgent: "*", allow: ["/"], disallow },
1250
+ host: origin,
1251
+ sitemap: origin + "/sitemap.xml",
1252
+ };
1253
+ }
1254
+ ```
1255
+
1256
+ </TabItem>
1257
+ <TabItem label="intlayer" value="intlayer">
1258
+
1259
+ ```typescript fileName="src/app/[locale]/about/layout.tsx"
1260
+ import { getIntlayer, getMultilingualUrls } from "intlayer";
1261
+ import type { Metadata } from "next";
1262
+ import type { LocalPromiseParams } from "next-intlayer";
1263
+
1264
+ export const generateMetadata = async ({
1265
+ params,
1266
+ }: LocalPromiseParams): Promise<Metadata> => {
1267
+ const { locale } = await params;
1268
+
1269
+ const metadata = getIntlayer("page-metadata", locale);
1270
+
1271
+ const multilingualUrls = getMultilingualUrls("/about");
1272
+
1273
+ return {
1274
+ ...metadata,
1275
+ alternates: {
1276
+ canonical: multilingualUrls[locale as keyof typeof multilingualUrls],
1277
+ languages: { ...multilingualUrls, "x-default": "/about" },
1278
+ },
1279
+ };
1280
+ };
1281
+
1282
+ // ... Restlicher Seiten-Code
1283
+ ```
1284
+
1285
+ ```tsx fileName="src/app/sitemap.ts"
1286
+ import { getMultilingualUrls } from "intlayer";
1287
+ import type { MetadataRoute } from "next";
1288
+
1289
+ const sitemap = (): MetadataRoute.Sitemap => [
1290
+ {
1291
+ url: "https://example.com/about",
1292
+ alternates: {
1293
+ languages: { ...getMultilingualUrls("https://example.com/about") },
1294
+ },
1295
+ },
1296
+ ];
1297
+ ```
1298
+
1299
+ ```tsx fileName="src/app/robots.ts"
1300
+ import { getMultilingualUrls } from "intlayer";
1301
+ import type { MetadataRoute } from "next";
1302
+
1303
+ // Funktion, die alle mehrsprachigen URLs aus einer Liste von URLs extrahiert
1304
+ const getAllMultilingualUrls = (urls: string[]) =>
1305
+ urls.flatMap((url) => Object.values(getMultilingualUrls(url)) as string[]);
1306
+
1307
+ const robots = (): MetadataRoute.Robots => ({
1308
+ rules: {
1309
+ userAgent: "*",
1310
+ allow: ["/"],
1311
+ disallow: getAllMultilingualUrls(["/dashboard"]), // Verbotene Pfade für alle Sprachversionen
1312
+ },
1313
+ host: "https://example.com",
1314
+ sitemap: "https://example.com/sitemap.xml",
1315
+ });
1316
+
1317
+ export default robots;
1318
+ ```
1319
+
1320
+ </TabItem>
1321
+ </Tab>
1322
+
1323
+ > Intlayer stellt eine `getMultilingualUrls`-Funktion bereit, um mehrsprachige URLs für Ihre Sitemap zu generieren.
1324
+
1325
+ ---
142
1326
 
143
1327
  ---
144
1328
 
145
- ## Praktische Migrationshinweise (next-intl / next-i18next → Intlayer)
1329
+ ## Und der Gewinner ist…
1330
+
1331
+ Es ist nicht einfach. Jede Option hat ihre Vor- und Nachteile. So sehe ich das:
1332
+
1333
+ <Columns>
1334
+ <Column>
1335
+
1336
+ **next-intl**
1337
+
1338
+ - am einfachsten, leichtgewichtig, weniger Entscheidungen, die dir aufgezwungen werden. Wenn du eine **minimale** Lösung möchtest, mit zentralisierten Katalogen zurechtkommst und deine App **klein bis mittelgroß** ist.
1339
+
1340
+ </Column>
1341
+ <Column>
1342
+
1343
+ **next-i18next**
1344
+
1345
+ - ausgereift, vollgepackt mit Funktionen, viele Community-Plugins, aber höherer Einrichtungsaufwand. Wenn du das **Plugin-Ökosystem von i18next** benötigst (z. B. erweiterte ICU-Regeln über Plugins) und dein Team i18next bereits kennt, und du **mehr Konfiguration** für Flexibilität akzeptierst.
1346
+
1347
+ </Column>
1348
+ <Column>
1349
+
1350
+ **Intlayer**
1351
+
1352
+ - entwickelt für modernes Next.js, mit modularen Inhalten, Typsicherheit, Werkzeugen und weniger Boilerplate. Wenn Sie **komponentenbezogene Inhalte**, **striktes TypeScript**, **Build-Zeit-Garantien**, **Tree-Shaking** und **batteriebetriebene** Routing/SEO/Editor-Werkzeuge schätzen – besonders für den **Next.js App Router**, Design-Systeme und **große, modulare Codebasen**.
1353
+
1354
+ </Column>
1355
+ </Columns>
1356
+
1357
+ Wenn Sie eine minimale Einrichtung bevorzugen und etwas manuelle Verkabelung akzeptieren, ist next-intl eine gute Wahl. Wenn Sie alle Funktionen benötigen und Komplexität kein Problem ist, funktioniert next-i18next. Aber wenn Sie eine moderne, skalierbare, modulare Lösung mit integrierten Werkzeugen wollen, zielt Intlayer darauf ab, Ihnen genau das sofort bereitzustellen.
1358
+
1359
+ > **Alternative für Enterprise-Teams**: Wenn Sie eine bewährte Lösung benötigen, die perfekt mit etablierten Lokalisierungsplattformen wie **Crowdin**, **Phrase** oder anderen professionellen Übersetzungsmanagementsystemen zusammenarbeitet, sollten Sie **next-intl** oder **next-i18next** wegen ihres ausgereiften Ökosystems und bewährten Integrationen in Betracht ziehen.
1360
+
1361
+ > **Zukünftige Roadmap**: Intlayer plant außerdem die Entwicklung von Plugins, die auf den **i18next**- und **next-intl**-Lösungen aufbauen. Dies bietet Ihnen die Vorteile von Intlayer für Automatisierung, Syntax und Inhaltsverwaltung, während die Sicherheit und Stabilität dieser etablierten Lösungen in Ihrem Anwendungscode erhalten bleibt.
1362
+
1363
+ ## GitHub-STARS
1364
+
1365
+ GitHub-Sterne sind ein starkes Indiz für die Popularität eines Projekts, das Vertrauen der Community und die langfristige Relevanz. Obwohl sie kein direktes Maß für die technische Qualität sind, spiegeln sie wider, wie viele Entwickler das Projekt nützlich finden, seinen Fortschritt verfolgen und es wahrscheinlich übernehmen werden. Zur Einschätzung des Werts eines Projekts helfen Sterne dabei, die Resonanz im Vergleich zu Alternativen zu vergleichen und Einblicke in das Wachstum des Ökosystems zu geben.
146
1366
 
147
- - **Pro Funktion starten**: Verschieben Sie eine Route oder Komponente nach der anderen zu **lokalen Wörterbüchern**.
148
- - **Alte Kataloge parallel behalten**: Überbrücken Sie während der Migration; vermeiden Sie einen Big Bang.
149
- - **Strenge Prüfungen aktivieren**: Lassen Sie Lücken frühzeitig durch Build-Zeit-Erkennung sichtbar werden.
150
- - **Middleware & Helfer übernehmen**: Standardisieren Sie die Lokalerkennung und SEO-Tags auf der gesamten Website.
151
- - **Bundles messen**: Erwarten Sie **Reduzierungen der Bundle-Größe**, da ungenutzte Inhalte entfernt werden.
1367
+ [![Stern-Historien-Diagramm](https://api.star-history.com/svg?repos=i18next/next-i18next&repos=amannn/next-intl&repos=aymericzip/intlayer&type=Date)](https://www.star-history.com/#i18next/next-i18next&amannn/next-intl&aymericzip/intlayer)
152
1368
 
153
1369
  ---
154
1370
 
155
1371
  ## Fazit
156
1372
 
157
- Alle drei Bibliotheken sind bei der Kernlokalisierung erfolgreich. Der Unterschied liegt darin, **wie viel Arbeit Sie investieren müssen**, um eine robuste, skalierbare Einrichtung in **modernem Next.js** zu erreichen:
1373
+ Alle drei Bibliotheken sind im Kern der Lokalisierung erfolgreich. Der Unterschied liegt darin, **wie viel Arbeit Sie investieren müssen**, um eine robuste, skalierbare Einrichtung in **modernem Next.js** zu erreichen:
158
1374
 
159
- - Mit **Intlayer** sind **modularer Inhalt**, **striktes TypeScript**, **Build-Zeit-Sicherheit**, **tree-shaken Bundles** sowie **erstklassiger App Router und SEO-Tools** **Standard**, keine lästige Pflicht.
1375
+ - Mit **Intlayer** sind **modularer Inhalt**, **striktes TypeScript**, **Build-Zeit-Sicherheit**, **tree-shaken Bundles** und **erstklassiger App Router + SEO-Tools** **Standard**, nicht lästige Pflicht.
160
1376
  - Wenn Ihr Team **Wartbarkeit und Geschwindigkeit** in einer mehrsprachigen, komponentenbasierten Anwendung schätzt, bietet Intlayer heute die **vollständigste** Erfahrung.
161
1377
 
162
- Siehe das Dokument ['Warum Intlayer?'](https://intlayer.org/doc/why) für weitere Details.
1378
+ Weitere Details finden Sie im Dokument ['Warum Intlayer?'](https://intlayer.org/doc/why).