@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: Comparaison de next-i18next avec next-intl et Intlayer pour l'internationalisation (i18n) d'une application Next.js
6
6
  keywords:
@@ -19,144 +19,1211 @@ slugs:
19
19
 
20
20
  # next-i18next VS next-intl VS intlayer | Internationalisation (i18n) Next.js
21
21
 
22
- Ce guide compare trois options i18n largement utilisées pour **Next.js** : **next-intl**, **next-i18next** et **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
+ Examinons les similitudes et différences entre trois options i18n pour Next.js : next-i18next, next-intl, et Intlayer.
25
+
26
+ Ce n’est pas un tutoriel complet. C’est une comparaison pour vous aider à choisir.
27
+
23
28
  Nous nous concentrons sur **Next.js 13+ App Router** (avec **React Server Components**) et évaluons :
24
29
 
25
- 1. **Architecture & organisation du contenu**
26
- 2. **TypeScript & sécurité**
30
+ 1. **Architecture et organisation du contenu**
31
+ 2. **TypeScript et sécurité**
27
32
  3. **Gestion des traductions manquantes**
28
- 4. **Routage & middleware**
29
- 5. **Performance & comportement de chargement**
30
- 6. **Expérience développeur (DX), outils & maintenance**
31
- 7. **SEO & scalabilité pour les grands projets**
33
+ 4. **Routage et middleware**
34
+ 5. **Performance et comportement de chargement**
35
+ 6. **Expérience développeur (DX), outils et maintenance**
36
+ 7. **SEO et évolutivité pour les grands projets**
32
37
 
33
- > **En résumé** : Les trois solutions peuvent localiser une application Next.js. Si vous souhaitez un **contenu scoped par composant**, des **types TypeScript stricts**, des **vérifications des clés manquantes à la compilation**, des **dictionnaires optimisés par tree-shaking**, ainsi que des **helpers App Router et SEO de première classe**, **Intlayer** est le choix le plus complet et moderne.
38
+ > **En résumé** : Les trois solutions peuvent localiser une application Next.js. Si vous souhaitez un **contenu scoped par composant**, des **types TypeScript stricts**, des **vérifications des clés manquantes à la compilation**, des **dictionnaires optimisés par tree-shaking**, ainsi que des **helpers de premier ordre pour App Router et SEO**, **Intlayer** est le choix le plus complet et moderne.
39
+
40
+ > Une confusion fréquente chez les développeurs est de penser que `next-intl` est la version Next.js de `react-intl`. Ce n'est pas le cas — `next-intl` est maintenu par [Amann](https://github.com/amannn), tandis que `react-intl` est maintenu par [FormatJS](https://github.com/formatjs/formatjs).
34
41
 
35
42
  ---
36
43
 
37
- ## Positionnement général
44
+ ## En bref
38
45
 
39
- - **next-intl** - Formatage de messages léger et simple avec un bon support Next.js. Les catalogues centralisés sont courants ; lexpérience développeur est simple, mais la sécurité et la maintenance à grande échelle restent principalement de votre responsabilité.
46
+ - **next-intl** - Formatage de messages léger et simple avec un bon support Next.js. Les catalogues centralisés sont courants ; l'expérience développeur (DX) est simple, mais la sécurité et la maintenance à grande échelle restent principalement de votre responsabilité.
40
47
  - **next-i18next** - i18next habillé pour Next.js. Écosystème mature et fonctionnalités via des plugins (par exemple, ICU), mais la configuration peut être verbeuse et les catalogues ont tendance à se centraliser à mesure que les projets grandissent.
41
- - **Intlayer** - Modèle de contenu centré sur les composants pour Next.js, **typage TS strict**, **vérifications à la compilation**, **tree-shaking**, **middleware intégré et helpers SEO**, **éditeur visuel/CMS** optionnel, et **traductions assistées par IA**.
48
+ - **Intlayer** - Modèle de contenu centré sur les composants pour Next.js, **typage strict en TS**, **vérifications à la compilation**, **tree-shaking**, **middleware intégré et aides SEO**, **éditeur visuel/CMS** optionnel, et **traductions assistées par IA**.
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
+ > Les badges se mettent à jour automatiquement. Les instantanés peuvent varier dans le temps.
42
60
 
43
61
  ---
44
62
 
45
63
  ## Comparaison des fonctionnalités côte à côte (axée sur Next.js)
46
64
 
47
- | Fonctionnalité | `next-intlayer` (Intlayer) | `next-intl` | `next-i18next` |
48
- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ |
49
- | **Traductions Près des Composants** | ✅ Oui, contenu collé avec chaque composant | ❌ Non | ❌ Non |
50
- | **Intégration TypeScript** | ✅ Avancée, types stricts générés automatiquement | ✅ Bonne | ⚠️ Basique |
51
- | **Détection des Traductions Manquantes** | ✅ Mise en évidence des erreurs TypeScript et erreur/avertissement à la compilation | ⚠️ Repli à l'exécution | ⚠️ Repli à l'exécution |
52
- | **Contenu Riche (JSX/Markdown/composants)** | ✅ Support direct | ❌ Non conçu pour des nœuds riches | ⚠️ Limité |
53
- | **Traduction assistée par IA** | ✅ Oui, supporte plusieurs fournisseurs d'IA. Utilisable avec vos propres clés API. Prend en compte le contexte de votre application et la portée du contenu | ❌ Non | ❌ Non |
54
- | **Éditeur Visuel** | ✅ Oui, éditeur visuel local + CMS optionnel ; peut externaliser le contenu de la base de code ; intégrable | ❌ Non / disponible via des plateformes de localisation externes | ❌ Non / disponible via des plateformes de localisation externes |
55
- | **Routage Localisé** | ✅ Oui, supporte les chemins localisés nativement (fonctionne avec Next.js & Vite) | ✅ Intégré, App Router supporte le segment `[locale]` | ✅ Intégré |
56
- | **Génération Dynamique de Routes** | ✅ Oui | ✅ Oui | ✅ Oui |
57
- | **Pluriels** | ✅ Modèles basés sur des énumérations | ✅ Bon | ✅ Bon |
58
- | **Formatage (dates, nombres, devises)** | ✅ Formatteurs optimisés (Intl en interne) | ✅ Bon (helpers Intl) | ✅ Bon (helpers Intl) |
59
- | **Format de contenu** | ✅ .tsx, .ts, .js, .json, .md, .txt, (.yaml en cours de développement) | ✅ .json, .js, .ts | ⚠️ .json |
60
- | **Support ICU** | ⚠️ En cours de développement | ✅ Oui | ⚠️ Via plugin (`i18next-icu`) |
61
- | **Aides SEO (hreflang, sitemap)** | ✅ Outils intégrés : aides pour sitemap, robots.txt, métadonnées | ✅ Bon | ✅ Bon |
62
- | **Écosystème / Communauté** | ⚠️ Plus petite mais en croissance rapide et réactive | ✅ Taille moyenne, axée sur Next.js | ✅ Taille moyenne, axée sur Next.js |
63
- | **Rendu côté serveur & Composants Serveur** | ✅ Oui, optimisé pour SSR / Composants Serveur React | ⚠️ Pris en charge au niveau de la page mais nécessite de passer les fonctions t dans larbre des composants pour les composants serveurs enfants | ⚠️ Pris en charge au niveau de la page mais nécessite de passer les fonctions t dans larbre des composants pour les composants serveurs enfants |
64
- | **Tree-shaking (chargement uniquement du contenu utilisé)** | ✅ Oui, par composant au moment de la compilation via les plugins Babel/SWC | ⚠️ Partiel | ⚠️ Partiel |
65
- | **Chargement paresseux** | ✅ Oui, par locale / par dictionnaire | ✅ Oui (par route / par locale), nécessite une gestion des espaces de noms | ✅ Oui (par route / par locale), nécessite une gestion des espaces de noms |
66
- | **Purge du contenu inutilisé** | ✅ Oui, par dictionnaire au moment de la compilation | ❌ Non, peut être géré manuellement avec la gestion des espaces de noms | ❌ Non, peut être géré manuellement avec la gestion des espaces de noms |
67
- | **Gestion des grands projets** | ✅ Encourage la modularité, adapté aux design-systems | ✅ Modulaire avec configuration | ✅ Modulaire avec configuration |
65
+ | Fonctionnalité | `next-intlayer` (Intlayer) | `next-intl` | `next-i18next` |
66
+ | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
67
+ | **Traductions Près des Composants** | ✅ Oui, contenu collé à chaque composant | ❌ Non | ❌ Non |
68
+ | **Intégration TypeScript** | ✅ Avancée, types stricts générés automatiquement | ✅ Bonne | ⚠️ Basique |
69
+ | **Détection des traductions manquantes** | ✅ Mise en évidence des erreurs TypeScript et erreurs/avertissements à la compilation | ⚠️ Repli à l'exécution | ⚠️ Repli à l'exécution |
70
+ | **Contenu enrichi (JSX/Markdown/composants)** | ✅ Support direct | ❌ Non conçu pour des nœuds riches | ⚠️ Limité |
71
+ | **Traduction assistée par IA** | ✅ Oui, prend en charge plusieurs fournisseurs d'IA. Utilisable avec vos propres clés API. Prend en compte le contexte de votre application et la portée du contenu | ❌ Non | ❌ Non |
72
+ | **Éditeur Visuel** | ✅ Oui, éditeur visuel local + CMS optionnel ; peut externaliser le contenu de la base de code ; intégrable | ❌ Non / disponible via des plateformes de localisation externes | ❌ Non / disponible via des plateformes de localisation externes |
73
+ | **Routage Localisé** | ✅ Oui, prend en charge les chemins localisés nativement (fonctionne avec Next.js & Vite) | ✅ Intégré, App Router prend en charge le segment `[locale]` | ✅ Intégré |
74
+ | **Génération dynamique de routes** | ✅ Oui | ✅ Oui | ✅ Oui |
75
+ | **Pluriels** | ✅ Modèles basés sur des énumérations | ✅ Bon | ✅ Bon |
76
+ | **Formatage (dates, nombres, devises)** | ✅ Formatteurs optimisés (Intl en interne) | ✅ Bon (helpers Intl) | ✅ Bon (helpers Intl) |
77
+ | **Format de contenu** | ✅ .tsx, .ts, .js, .json, .md, .txt, (.yaml en cours) | ✅ .json, .js, .ts | ⚠️ .json |
78
+ | **Support ICU** | ⚠️ En cours de développement | ✅ Oui | ⚠️ Via plugin (`i18next-icu`) |
79
+ | **Aides SEO (hreflang, sitemap)** | ✅ Outils intégrés : aides pour sitemap, robots.txt, métadonnées | ✅ Bon | ✅ Bon |
80
+ | **Écosystème / Communauté** | ⚠️ Plus petite mais en croissance rapide et réactive | ✅ Bonne | ✅ Bonne |
81
+ | **Rendu côté serveur & Composants serveur** | ✅ Oui, optimisé pour SSR / Composants serveur React | ⚠️ Pris en charge au niveau de la page mais nécessite de passer les fonctions t dans l'arbre des composants pour les composants serveur enfants | ⚠️ Pris en charge au niveau de la page mais nécessite de passer les fonctions t dans l'arbre des composants pour les composants serveur enfants |
82
+ | **Élimination des codes morts (chargement uniquement du contenu utilisé)** | ✅ Oui, par composant au moment de la compilation via des plugins Babel/SWC | ⚠️ Partiel | ⚠️ Partiel |
83
+ | **Chargement paresseux** | ✅ Oui, par locale / par dictionnaire | ✅ Oui (par route / par locale), nécessite une gestion des espaces de noms | ✅ Oui (par route / par locale), nécessite une gestion des espaces de noms |
84
+ | **Purge du contenu inutilisé** | ✅ Oui, par dictionnaire au moment de la compilation | ❌ Non, peut être géré manuellement avec la gestion des espaces de noms | ❌ Non, peut être géré manuellement avec la gestion des espaces de noms |
85
+ | **Gestion des grands projets** | ✅ Encourage la modularité, adapté aux systèmes de design | ✅ Modulaire avec configuration | ✅ Modulaire avec configuration |
86
+ | **Test des traductions manquantes (CLI/CI)** | ✅ CLI : `npx intlayer content test` (audit compatible CI) | ⚠️ Non intégré ; la documentation suggère `npx @lingual/i18n-check` | ⚠️ Non intégré ; dépend des outils i18next / runtime `saveMissing` |
87
+
88
+ ---
89
+
90
+ ## Introduction
91
+
92
+ Next.js offre un support intégré pour le routage internationalisé (par exemple, les segments de locale). Mais cette fonctionnalité ne réalise pas les traductions par elle-même. Vous avez toujours besoin d'une bibliothèque pour afficher du contenu localisé à vos utilisateurs.
93
+
94
+ De nombreuses bibliothèques i18n existent, mais dans l’univers Next.js aujourd’hui, trois gagnent en popularité : next-i18next, next-intl, et Intlayer.
95
+
96
+ ---
97
+
98
+ ## Architecture & évolutivité
99
+
100
+ - **next-intl / next-i18next** : Par défaut, utilisent des **catalogues centralisés** par locale (plus des **espaces de noms** dans i18next). Cela fonctionne bien au début, mais devient souvent une grande surface partagée avec un couplage croissant et une forte rotation des clés.
101
+ - **Intlayer** : Encourage des dictionnaires **par composant** (ou par fonctionnalité) **co-localisés** avec le code qu’ils servent. Cela réduit la charge cognitive, facilite la duplication/migration des éléments d’interface utilisateur, et diminue les conflits entre équipes. Le contenu inutilisé est naturellement plus facile à repérer et à supprimer.
102
+
103
+ **Pourquoi c’est important :** Dans les grandes bases de code ou les configurations de systèmes de design, le **contenu modulaire** évolue mieux que les catalogues monolithiques.
68
104
 
69
105
  ---
70
106
 
71
- ## Comparaison approfondie
107
+ ## Tailles des bundles & dépendances
108
+
109
+ Après la compilation de l'application, le bundle correspond au JavaScript que le navigateur chargera pour afficher la page. La taille du bundle est donc importante pour la performance de l'application.
110
+
111
+ Deux composants sont importants dans le contexte d'un bundle d'application multilingue :
112
+
113
+ - Le code de l'application
114
+ - Le contenu chargé par le navigateur
115
+
116
+ ## Code de l'application
117
+
118
+ L'importance du code de l'application est minimale dans ce cas. Les trois solutions sont "tree-shakables", ce qui signifie que les parties inutilisées du code ne sont pas incluses dans le bundle.
119
+
120
+ Voici une comparaison de la taille du bundle JavaScript chargé par le navigateur pour une application multilingue avec les trois solutions.
121
+
122
+ Si aucun formateur n'est nécessaire dans l'application, la liste des fonctions exportées après tree-shaking sera :
123
+
124
+ - **next-intlayer** : `useIntlayer`, `useLocale`, `NextIntlClientProvider`, (La taille du bundle est de 180,6 kB -> 78,6 kB (gzip))
125
+ - **next-intl** : `useTranslations`, `useLocale`, `NextIntlClientProvider`, (La taille du bundle est de 101,3 kB -> 31,4 kB (gzip))
126
+ - **next-i18next** : `useTranslation`, `useI18n`, `I18nextProvider`, (La taille du bundle est de 80,7 kB -> 25,5 kB (gzip))
127
+
128
+ Ces fonctions ne sont que des wrappers autour du contexte/état React, donc l'impact total de la bibliothèque i18n sur la taille du bundle est minimal.
129
+
130
+ > Intlayer est légèrement plus volumineux que `next-intl` et `next-i18next` car il inclut plus de logique dans la fonction `useIntlayer`. Cela est lié à l'intégration de markdown et de `intlayer-editor`.
131
+
132
+ ## Contenu et traductions
133
+
134
+ Cette partie est souvent ignorée par les développeurs, mais considérons le cas d'une application composée de 10 pages en 10 langues. Supposons que chaque page intègre un contenu 100 % unique pour simplifier le calcul (en réalité, beaucoup de contenu est redondant entre les pages, par exemple, le titre de la page, l'en-tête, le pied de page, etc.).
135
+
136
+ Un utilisateur souhaitant visiter la page `/fr/about` chargera le contenu d'une page dans une langue donnée. Ignorer l'optimisation du contenu reviendrait à charger inutilement 8 200 % `((1 + (((10 pages - 1) × (10 langues - 1)))) × 100)` du contenu de l'application. Voyez-vous le problème ? Même si ce contenu reste du texte, et alors que vous préférez probablement penser à optimiser les images de votre site, vous envoyez du contenu inutile à travers le monde et faites traiter cela aux ordinateurs des utilisateurs pour rien.
137
+
138
+ Deux problèmes importants :
139
+
140
+ - **Fractionnement par route :**
141
+
142
+ > Si je suis sur la page `/about`, je ne veux pas charger le contenu de la page `/home`
72
143
 
73
- ### 1) Architecture et évolutivité
144
+ - **Fractionnement par locale :**
74
145
 
75
- - **next-intl / next-i18next** : Par défaut, utilise des **catalogues centralisés** par locale (plus les **espaces de noms** dans i18next). Fonctionne bien au début, mais devient souvent une grande surface partagée avec un couplage croissant et une forte rotation des clés.
76
- - **Intlayer** : Encourage les dictionnaires **par composant** (ou par fonctionnalité) **co-localisés** avec le code qu’ils servent. Cela réduit la charge cognitive, facilite la duplication/migration des éléments UI, et diminue les conflits entre équipes. Le contenu inutilisé est naturellement plus facile à repérer et à purger.
146
+ > Si je suis sur la page `/fr/about`, je ne veux pas charger le contenu de la page `/en/about`
77
147
 
78
- **Pourquoi c’est important :** Dans les grandes bases de code ou les configurations de design-systems, le **contenu modulaire** évolue mieux que les catalogues monolithiques.
148
+ Encore une fois, les trois solutions sont conscientes de ces problèmes et permettent de gérer ces optimisations. La différence entre les trois solutions réside dans l'expérience développeur (DX).
149
+
150
+ `next-intl` et `next-i18next` utilisent une approche centralisée pour gérer les traductions, permettant de fractionner les fichiers JSON par locale et par sous-fichiers. Dans `next-i18next`, nous appelons ces fichiers JSON des « namespaces » ; `next-intl` permet de déclarer des messages. Dans `intlayer`, nous appelons ces fichiers JSON des « dictionnaires ».
151
+
152
+ - Dans le cas de `next-intl`, comme pour `next-i18next`, le contenu est chargé au niveau de la page/layout, puis ce contenu est chargé dans un fournisseur de contexte. Cela signifie que le développeur doit gérer manuellement les fichiers JSON qui seront chargés pour chaque page.
153
+
154
+ > En pratique, cela implique que les développeurs sautent souvent cette optimisation, préférant charger tout le contenu dans le fournisseur de contexte de la page pour plus de simplicité.
155
+
156
+ - Dans le cas de `intlayer`, tout le contenu est chargé dans l'application. Ensuite, un plugin (`@intlayer/babel` / `@intlayer/swc`) s'occupe d'optimiser le bundle en ne chargeant que le contenu utilisé sur la page. Le développeur n'a donc pas besoin de gérer manuellement les dictionnaires qui seront chargés. Cela permet une meilleure optimisation, une meilleure maintenabilité, et réduit le temps de développement.
157
+
158
+ À mesure que l'application grandit (surtout lorsque plusieurs développeurs travaillent sur l'application), il est courant d'oublier de supprimer le contenu qui n'est plus utilisé dans les fichiers JSON.
159
+
160
+ > Notez que tous les JSON sont chargés dans tous les cas (next-intl, next-i18next, intlayer).
161
+
162
+ C'est pourquoi l'approche d'Intlayer est plus performante : si un composant n'est plus utilisé, son dictionnaire n'est pas chargé dans le bundle.
163
+
164
+ La manière dont la bibliothèque gère les valeurs de repli (fallbacks) est également importante. Considérons que l'application est en anglais par défaut, et que l'utilisateur visite la page `/fr/about`. Si des traductions manquent en français, nous considérerons le repli en anglais.
165
+
166
+ Dans le cas de `next-intl` et `next-i18next`, la bibliothèque nécessite de charger le JSON lié à la locale actuelle, mais aussi à la locale de secours. Ainsi, en supposant que tout le contenu a été traduit, chaque page chargera 100 % de contenu inutile. **En comparaison, `intlayer` traite la locale de secours lors de la construction du dictionnaire. Ainsi, chaque page ne chargera que le contenu utilisé.**
167
+
168
+ Voici un exemple de l'impact de l'optimisation de la taille du bundle en utilisant `intlayer` dans une application vite + react :
169
+
170
+ | Bundle optimisé | Bundle non optimisé |
171
+ | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
172
+ | ![bundle optimisé](https://github.com/aymericzip/intlayer/blob/main/docs/assets/bundle.png?raw=true) | ![bundle non optimisé](https://github.com/aymericzip/intlayer/blob/main/docs/assets/bundle_no_optimization.png?raw=true) |
79
173
 
80
174
  ---
81
175
 
82
- ### 2) TypeScript & sécurité
176
+ ## TypeScript & sécurité
177
+
178
+ <Columns>
179
+ <Column>
180
+
181
+ **next-intl**
182
+
183
+ - Support TypeScript solide, mais **les clés ne sont pas strictement typées par défaut** ; vous devrez maintenir manuellement les bonnes pratiques de sécurité.
184
+
185
+ </Column>
186
+ <Column>
187
+
188
+ **next-i18next**
189
+
190
+ - Typages de base pour les hooks ; **une typage stricte des clés nécessite des outils/configurations supplémentaires**.
83
191
 
84
- - **next-intl** : Support solide de TypeScript, mais les **clés ne sont pas strictement typées par défaut** ; vous devrez maintenir manuellement les patterns de sécurité.
85
- - **next-i18next** : Typages de base pour les hooks ; la **typage strict des clés nécessite des outils/configurations supplémentaires**.
86
- - **Intlayer** : **Génère des types stricts** à partir de votre contenu. L’**autocomplétion dans l’IDE** et les **erreurs à la compilation** détectent les fautes de frappe et les clés manquantes avant le déploiement.
192
+ </Column>
193
+ <Column>
87
194
 
88
- **Pourquoi c’est important :** Le typage fort déplace les erreurs vers la **gauche** (CI/build) au lieu de la **droite** (exécution).
195
+ **intlayer**
196
+
197
+ - **Génère des types stricts** à partir de votre contenu. **Autocomplétion IDE** et **erreurs à la compilation** détectent les fautes de frappe et les clés manquantes avant le déploiement.
198
+
199
+ </Column>
200
+ </Columns>
201
+
202
+ **Pourquoi c’est important :** Un typage fort déplace les erreurs vers la **gauche** (CI/build) au lieu de la **droite** (exécution).
89
203
 
90
204
  ---
91
205
 
92
- ### 3) Gestion des traductions manquantes
206
+ ## Gestion des traductions manquantes
207
+
208
+ **next-intl**
209
+
210
+ - S’appuie sur des **solutions de secours à l’exécution** (par exemple, afficher la clé ou la locale par défaut). La compilation ne plante pas.
93
211
 
94
- - **next-intl / next-i18next** : Dépendent des **solutions de secours à l’exécution** (par exemple, afficher la clé ou la locale par défaut). La compilation ne plante pas.
95
- - **Intlayer** : **Détection à la compilation** avec des **avertissements/erreurs** pour les locales ou clés manquantes.
212
+ **next-i18next**
213
+
214
+ - S’appuie sur des **solutions de secours à l’exécution** (par exemple, afficher la clé ou la locale par défaut). La compilation ne plante pas.
215
+
216
+ **intlayer**
217
+
218
+ - **Détection à la compilation** avec **avertissements/erreurs** pour les locales ou clés manquantes.
96
219
 
97
220
  **Pourquoi c’est important :** Détecter les lacunes lors de la compilation évite les « chaînes mystères » en production et s’aligne avec des règles strictes de publication.
98
221
 
99
222
  ---
100
223
 
101
- ### 4) Routage, middleware & stratégie d’URL
224
+ ## Routage, middleware & stratégie d’URL
225
+
226
+ <Columns>
227
+ <Column>
228
+
229
+ **next-intl**
230
+
231
+ - Fonctionne avec le **routage localisé de Next.js** sur l'App Router.
232
+
233
+ </Column>
234
+ <Column>
235
+
236
+ **next-i18next**
237
+
238
+ - Fonctionne avec le **routage localisé de Next.js** sur l'App Router.
239
+
240
+ </Column>
241
+ <Column>
242
+
243
+ **intlayer**
244
+
245
+ - Tout ce qui précède, plus un **middleware i18n** (détection de la locale via headers/cookies) et des **helpers** pour générer des URLs localisées et des balises `<link rel="alternate" hreflang="…">`.
102
246
 
103
- - Les trois fonctionnent avec le **routage localisé Next.js** sur l’App Router.
104
- - **Intlayer** va plus loin avec un **middleware i18n** (détection de la locale via les headers/cookies) et des **helpers** pour générer des URLs localisées et des balises `<link rel="alternate" hreflang="…">`.
247
+ </Column>
248
+ </Columns>
105
249
 
106
250
  **Pourquoi c’est important :** Moins de couches de liaison personnalisées ; **expérience utilisateur cohérente** et **SEO propre** à travers les locales.
107
251
 
108
252
  ---
109
253
 
110
- ### 5) Alignement avec les Server Components (RSC)
254
+ ## Alignement avec les Server Components (RSC)
111
255
 
112
- - **Tous** supportent Next.js 13+.
113
- - **Intlayer** facilite la **frontière serveur/client** avec une API cohérente et des providers conçus pour RSC, évitant ainsi de passer des formateurs ou des fonctions t à travers les arbres de composants.
256
+ <Columns>
257
+ <Column>
114
258
 
115
- **Pourquoi c’est important :** Modèle mental plus clair et moins de cas limites dans les arbres hybrides.
259
+ **next-intl**
116
260
 
117
- ---
261
+ - Supporte Next.js 13+. Nécessite souvent de passer les fonctions t/formatters à travers les arbres de composants dans des configurations hybrides.
262
+
263
+ </Column>
264
+ <Column>
265
+
266
+ **next-i18next**
267
+
268
+ - Prend en charge Next.js 13+. Contraintes similaires pour le passage des utilitaires de traduction à travers les frontières.
269
+
270
+ </Column>
271
+ <Column>
118
272
 
119
- ### 6) Performance et comportement de chargement
273
+ **intlayer**
120
274
 
121
- - **next-intl / next-i18next** : Contrôle partiel via les **espaces de noms** et les **découpages au niveau des routes** ; risque d’inclure des chaînes inutilisées si la discipline n’est pas respectée.
122
- - **Intlayer** : Effectue du **tree-shaking** à la compilation et **charge paresseusement par dictionnaire/locale**. Le contenu inutilisé n’est pas inclus.
275
+ - Prend en charge Next.js 13+ et facilite la **frontière serveur/client** avec une API cohérente et des fournisseurs orientés RSC, évitant le transfert de formateurs ou de fonctions t.
123
276
 
124
- **Pourquoi c’est important :** Des bundles plus petits et un démarrage plus rapide, surtout sur des sites multi-locales.
277
+ </Column>
278
+ </Columns>
279
+
280
+ **Pourquoi c'est important :** Modèle mental plus clair et moins de cas particuliers dans les arbres hybrides.
125
281
 
126
282
  ---
127
283
 
128
- ### 7) Expérience développeur, outils et maintenance
284
+ ## DX, outils & maintenance
285
+
286
+ <Columns>
287
+ <Column>
288
+
289
+ **next-intl**
290
+
291
+ - Souvent associé à des plateformes de localisation externes et à des flux éditoriaux.
129
292
 
130
- - **next-intl / next-i18next** : Vous connecterez généralement des plateformes externes pour les traductions et les flux éditoriaux.
131
- - **Intlayer** : Propose un **éditeur visuel gratuit** et un **CMS optionnel** (compatible Git ou externalisé). Plus une **extension VSCode** pour la rédaction de contenu et des **traductions assistées par IA** utilisant vos propres clés de fournisseur.
293
+ </Column>
294
+ <Column>
295
+
296
+ **next-i18next**
297
+
298
+ - Souvent associé à des plateformes de localisation externes et à des flux éditoriaux.
299
+
300
+ </Column>
301
+ <Column>
302
+
303
+ **intlayer**
304
+
305
+ - Propose un **éditeur visuel gratuit** et un **CMS optionnel** (compatible Git ou externalisé), ainsi qu’une **extension VSCode** et des **traductions assistées par IA** utilisant vos propres clés de fournisseur.
306
+
307
+ </Column>
308
+ </Columns>
132
309
 
133
310
  **Pourquoi c’est important :** Réduit les coûts opérationnels et raccourcit la boucle entre les développeurs et les auteurs de contenu.
134
311
 
312
+ ## Intégration avec les plateformes de localisation (TMS)
313
+
314
+ Les grandes organisations s’appuient souvent sur des systèmes de gestion de traduction (TMS) comme **Crowdin**, **Phrase**, **Lokalise**, **Localizely** ou **Localazy**.
315
+
316
+ - **Pourquoi les entreprises s’en soucient**
317
+ - **Collaboration & rôles** : Plusieurs acteurs sont impliqués : développeurs, chefs de produit, traducteurs, relecteurs, équipes marketing.
318
+ - **Échelle & efficacité** : localisation continue, relecture en contexte.
319
+
320
+ - **next-intl / next-i18next**
321
+ - Utilisent généralement des **catalogues JSON centralisés**, ce qui rend l’export/import avec un TMS simple.
322
+ - Écosystèmes matures et exemples/intégrations pour les plateformes mentionnées ci-dessus.
323
+
324
+ - **Intlayer**
325
+ - Encourage les **dictionnaires décentralisés par composant** et supporte du contenu **TypeScript/TSX/JS/JSON/MD**.
326
+ - Cela améliore la modularité dans le code, mais peut rendre l’intégration plug-and-play avec un TMS plus difficile lorsqu’un outil attend des fichiers JSON centralisés et plats.
327
+ - Intlayer propose des alternatives : **traductions assistées par IA** (en utilisant vos propres clés de fournisseur), un **éditeur visuel/CMS**, et des workflows **CLI/CI** pour détecter et préremplir les lacunes.
328
+
329
+ > Remarque : `next-intl` et `i18next` acceptent également les catalogues TypeScript. Si votre équipe stocke les messages dans des fichiers `.ts` ou les décentralise par fonctionnalité, vous pouvez rencontrer des frictions similaires avec le TMS. Cependant, de nombreuses configurations `next-intl` restent centralisées dans un dossier `locales/`, ce qui est un peu plus facile à refactoriser en JSON pour le TMS.
330
+
331
+ ## Expérience développeur
332
+
333
+ Cette partie fait une comparaison approfondie entre les trois solutions. Plutôt que de considérer des cas simples, comme décrit dans la documentation « démarrage » de chaque solution, nous allons considérer un cas d'utilisation réel, plus proche d'un vrai projet.
334
+
335
+ ### Structure de l'application
336
+
337
+ La structure de l'application est importante pour assurer une bonne maintenabilité de votre base de code.
338
+
339
+ <Tab defaultTab="next-intl" group='techno'>
340
+
341
+ <TabItem label="next-i18next" value="next-i18next">
342
+
343
+ ```bash
344
+ .
345
+ ├── i18n.config.ts
346
+ └── src
347
+ ├── locales
348
+ │ ├── en
349
+ │ │ ├── common.json
350
+ │ │ └── about.json
351
+ │ └── fr
352
+ │ ├── common.json
353
+ │ └── about.json
354
+ ├── app
355
+ │ ├── i18n
356
+ │ │ └── server.ts
357
+ │ └── [locale]
358
+ │ ├── layout.tsx
359
+ │ └── about.tsx
360
+ └── components
361
+ ├── I18nProvider.tsx
362
+ ├── ClientComponent.tsx
363
+ └── ServerComponent.tsx
364
+ ```
365
+
366
+ </TabItem>
367
+ <TabItem label="next-intl" value="next-intl">
368
+
369
+ ```bash
370
+ .
371
+ ├── i18n.ts
372
+ ├── locales
373
+ │ ├── en
374
+ │ │ ├── home.json
375
+ │ │ └── navbar.json
376
+ │ ├── fr
377
+ │ │ ├── home.json
378
+ │ │ └── navbar.json
379
+ │ └── es
380
+ │ ├── home.json
381
+ │ └── navbar.json
382
+ └── src
383
+ ├── middleware.ts
384
+ ├── app
385
+ │ ├── i18n
386
+ │ │ └── server.ts
387
+ │ └── [locale]
388
+ │ └── home.tsx
389
+ └── components
390
+ └── Navbar
391
+ └── index.tsx
392
+ ```
393
+
394
+ </TabItem>
395
+ <TabItem label="intlayer" value="intlayer">
396
+
397
+ ```bash
398
+ .
399
+ ├── intlayer.config.ts
400
+ └── src
401
+ ├── middleware.ts
402
+ ├── app
403
+ │ └── [locale]
404
+ │ ├── layout.tsx
405
+ │ └── home
406
+ │ ├── index.tsx
407
+ │ └── index.content.ts
408
+ └── components
409
+ └── Navbar
410
+ ├── index.tsx
411
+ └── index.content.ts
412
+ ```
413
+
414
+ </TabItem>
415
+ </Tab>
416
+
417
+ #### Comparaison
418
+
419
+ - **next-intl / next-i18next** : Catalogues centralisés (JSON ; espaces de noms/messages). Structure claire, s'intègre bien avec les plateformes de traduction, mais peut entraîner plus de modifications croisées entre fichiers à mesure que les applications grandissent.
420
+ - **Intlayer** : Dictionnaires `.content.{ts|js|json}` par composant, co-localisés avec les composants. Facilite la réutilisation des composants et la réflexion locale ; ajoute des fichiers et repose sur des outils au moment de la compilation.
421
+
422
+ #### Configuration et Chargement du Contenu
423
+
424
+ Comme mentionné précédemment, vous devez optimiser la manière dont chaque fichier JSON est importé dans votre code.
425
+ La façon dont la bibliothèque gère le chargement du contenu est importante.
426
+
427
+ <Tab defaultTab="next-intl" group='techno'>
428
+ <TabItem label="next-i18next" value="next-i18next">
429
+
430
+ ```tsx fileName="next-i18next.config.js"
431
+ module.exports = {
432
+ i18n: {
433
+ locales: ["en", "fr", "es"],
434
+ defaultLocale: "en",
435
+ },
436
+ };
437
+ ```
438
+
439
+ ```tsx fileName="src/app/_app.tsx"
440
+ import { appWithTranslation } from "next-i18next";
441
+
442
+ const MyApp = ({ Component, pageProps }) => <Component {...pageProps} />;
443
+
444
+ export default appWithTranslation(MyApp);
445
+ ```
446
+
447
+ ```tsx fileName="src/app/[locale]/about/page.tsx"
448
+ import type { GetStaticProps } from "next";
449
+ import { serverSideTranslations } from "next-i18next/serverSideTranslations";
450
+ import { useTranslation } from "next-i18next";
451
+ import { I18nextProvider, initReactI18next } from "react-i18next";
452
+ import { createInstance } from "i18next";
453
+ import { ClientComponent, ServerComponent } from "@components";
454
+
455
+ export default function HomePage({ locale }: { locale: string }) {
456
+ // Déclarez explicitement le namespace utilisé par ce composant
457
+ const resources = await loadMessagesFor(locale); // votre chargeur (JSON, etc.)
458
+
459
+ const i18n = createInstance();
460
+ i18n.use(initReactI18next).init({
461
+ lng: locale,
462
+ fallbackLng: "en",
463
+ resources,
464
+ ns: ["common", "about"],
465
+ defaultNS: "common",
466
+ interpolation: { escapeValue: false },
467
+ });
468
+
469
+ const { t } = useTranslation("about");
470
+
471
+ return (
472
+ <I18nextProvider i18n={i18n}>
473
+ <main>
474
+ <h1>{t("title")}</h1>
475
+ <ClientComponent />
476
+ <ServerComponent />
477
+ </main>
478
+ </I18nextProvider>
479
+ );
480
+ }
481
+
482
+ export const getStaticProps: GetStaticProps = async ({ locale }) => {
483
+ // Ne préchargez que les namespaces nécessaires à CETTE page
484
+ return {
485
+ props: {
486
+ ...(await serverSideTranslations(locale ?? "en", ["common", "about"])),
487
+ },
488
+ };
489
+ };
490
+ ```
491
+
492
+ </TabItem>
493
+ <TabItem label="next-intl" value="next-intl">
494
+
495
+ ```tsx fileName="i18n.ts"
496
+ import { getRequestConfig } from "next-intl/server";
497
+ import { notFound } from "next/navigation";
498
+
499
+ // Peut être importé depuis une configuration partagée
500
+ const locales = ["en", "fr", "es"];
501
+
502
+ export default getRequestConfig(async ({ locale }) => {
503
+ // Valider que le paramètre `locale` reçu est valide
504
+ if (!locales.includes(locale as any)) notFound();
505
+
506
+ return {
507
+ messages: (await import(`../messages/${locale}.json`)).default,
508
+ };
509
+ });
510
+ ```
511
+
512
+ ```tsx fileName="src/app/[locale]/about/layout.tsx"
513
+ import { NextIntlClientProvider } from "next-intl";
514
+ import { getMessages, unstable_setRequestLocale } from "next-intl/server";
515
+ import pick from "lodash/pick";
516
+
517
+ export default async function LocaleLayout({
518
+ children,
519
+ params,
520
+ }: {
521
+ children: React.ReactNode;
522
+ params: { locale: string };
523
+ }) {
524
+ const { locale } = params;
525
+
526
+ // Définir la locale de requête active pour ce rendu serveur (RSC)
527
+ unstable_setRequestLocale(locale);
528
+
529
+ // Les messages sont chargés côté serveur via src/i18n/request.ts
530
+ // (voir la documentation next-intl). Ici, nous ne transmettons qu'un sous-ensemble au client
531
+ // nécessaire pour les composants client (optimisation de la charge utile).
532
+ const messages = await getMessages();
533
+ const clientMessages = pick(messages, ["common", "about"]);
534
+
535
+ const rtlLocales = ["ar", "he", "fa", "ur"];
536
+
537
+ return (
538
+ <html lang={locale} dir={rtlLocales.includes(locale) ? "rtl" : "ltr"}>
539
+ <body>
540
+ <NextIntlClientProvider locale={locale} messages={clientMessages}>
541
+ {children}
542
+ </NextIntlClientProvider>
543
+ </body>
544
+ </html>
545
+ );
546
+ }
547
+ ```
548
+
549
+ ```tsx fileName="src/app/[locale]/about/page.tsx"
550
+ import { getTranslations } from "next-intl/server";
551
+ import { ClientComponent, ServerComponent } from "@components";
552
+
553
+ export default async function LandingPage({
554
+ params,
555
+ }: {
556
+ params: { locale: string };
557
+ }) {
558
+ // Chargement strictement côté serveur (pas hydraté au client)
559
+ const t = await getTranslations("about");
560
+
561
+ return (
562
+ <main>
563
+ <h1>{t("title")}</h1>
564
+ <ClientComponent />
565
+ <ServerComponent />
566
+ </main>
567
+ );
568
+ }
569
+ ```
570
+
571
+ </TabItem>
572
+ <TabItem label="intlayer" value="intlayer">
573
+
574
+ ```tsx fileName="intlayer.config.ts"
575
+ export default {
576
+ internationalization: {
577
+ locales: ["en", "fr", "es"],
578
+ defaultLocale: "en",
579
+ },
580
+ };
581
+ ```
582
+
583
+ ```tsx fileName="src/app/[locale]/layout.tsx"
584
+ import { getHTMLTextDir } from "intlayer";
585
+ import {
586
+ IntlayerClientProvider,
587
+ generateStaticParams,
588
+ type NextLayoutIntlayer,
589
+ } from "next-intlayer";
590
+
591
+ export const dynamic = "force-static";
592
+
593
+ const LandingLayout: NextLayoutIntlayer = async ({ children, params }) => {
594
+ const { locale } = await params;
595
+
596
+ return (
597
+ <html lang={locale} dir={getHTMLTextDir(locale)}>
598
+ <IntlayerClientProvider locale={locale}>
599
+ {children}
600
+ </IntlayerClientProvider>
601
+ </html>
602
+ );
603
+ };
604
+
605
+ export default LandingLayout;
606
+ ```
607
+
608
+ ```tsx fileName="src/app/[locale]/about/page.tsx"
609
+ import { PageContent } from "@components/PageContent";
610
+ import type { NextPageIntlayer } from "next-intlayer";
611
+ import { IntlayerServerProvider, useIntlayer } from "next-intlayer/server";
612
+ import { ClientComponent, ServerComponent } from "@components";
613
+
614
+ const LandingPage: NextPageIntlayer = async ({ params }) => {
615
+ const { locale } = await params;
616
+ const { title } = useIntlayer("about", locale);
617
+
618
+ return (
619
+ <IntlayerServerProvider locale={locale}>
620
+ <main>
621
+ <h1>{title}</h1>
622
+ <ClientComponent />
623
+ <ServerComponent />
624
+ </main>
625
+ </IntlayerServerProvider>
626
+ );
627
+ };
628
+
629
+ export default LandingPage;
630
+ ```
631
+
632
+ </TabItem>
633
+ </Tab>
634
+
635
+ #### Comparaison
636
+
637
+ Les trois prennent en charge le chargement de contenu par locale et les fournisseurs.
638
+
639
+ - Avec **next-intl/next-i18next**, vous chargez généralement les messages/namespaces sélectionnés par route et placez les providers là où c'est nécessaire.
640
+
641
+ - Avec **Intlayer**, une analyse au moment de la compilation est ajoutée pour déduire l'utilisation, ce qui peut réduire le câblage manuel et permettre un provider racine unique.
642
+
643
+ Choisissez entre un contrôle explicite et l'automatisation selon la préférence de l'équipe.
644
+
645
+ ### Utilisation dans un composant client
646
+
647
+ Prenons un exemple d'un composant client affichant un compteur.
648
+
649
+ <Tab defaultTab="next-intl" group='techno'>
650
+ <TabItem label="next-i18next" value="next-i18next">
651
+
652
+ **Traductions (doivent être un vrai JSON dans `public/locales/...`)**
653
+
654
+ ```json fileName="public/locales/en/about.json"
655
+ {
656
+ "counter": {
657
+ "label": "Counter",
658
+ "increment": "Increment"
659
+ }
660
+ }
661
+ ```
662
+
663
+ ```json fileName="public/locales/fr/about.json"
664
+ {
665
+ "counter": {
666
+ "label": "Compteur",
667
+ "increment": "Incrémenter"
668
+ }
669
+ }
670
+ ```
671
+
672
+ **Composant client**
673
+
674
+ ```tsx fileName="src/components/ClientComponentExample.tsx"
675
+ "use client";
676
+
677
+ import React, { useMemo, useState } from "react";
678
+ import { useTranslation } from "next-i18next";
679
+
680
+ const ClientComponentExample = () => {
681
+ const { t, i18n } = useTranslation("about");
682
+ const [count, setCount] = useState(0);
683
+
684
+ // next-i18next n'expose pas useNumber ; utilisez Intl.NumberFormat
685
+ const numberFormat = new Intl.NumberFormat(i18n.language);
686
+
687
+ return (
688
+ <div>
689
+ <p>{numberFormat.format(count)}</p>
690
+ <button
691
+ aria-label={t("counter.label")}
692
+ onClick={() => setCount((count) => count + 1)}
693
+ >
694
+ {t("counter.increment")}
695
+ </button>
696
+ </div>
697
+ );
698
+ };
699
+ ```
700
+
701
+ > N'oubliez pas d'ajouter l'espace de noms "about" dans les serverSideTranslations de la page
702
+ > Nous utilisons ici la version 19.x.x de React, mais pour les versions inférieures, vous devrez utiliser useMemo pour stocker l'instance du formateur car c'est une fonction lourde
703
+
704
+ </TabItem>
705
+ <TabItem label="next-intl" value="next-intl">
706
+
707
+ **Traductions (forme réutilisée ; chargez-les dans les messages next-intl comme vous préférez)**
708
+
709
+ ```json fileName="locales/en/about.json"
710
+ {
711
+ "counter": {
712
+ "label": "Counter",
713
+ "increment": "Increment"
714
+ }
715
+ }
716
+ ```
717
+
718
+ ```json fileName="locales/fr/about.json"
719
+ {
720
+ "counter": {
721
+ "label": "Compteur",
722
+ "increment": "Incrémenter"
723
+ }
724
+ }
725
+ ```
726
+
727
+ **Composant client**
728
+
729
+ ```tsx fileName="src/components/ClientComponentExample.tsx"
730
+ "use client";
731
+
732
+ import React, { useState } from "react";
733
+ import { useTranslations, useFormatter } from "next-intl";
734
+
735
+ const ClientComponentExample = () => {
736
+ // Portée directement sur l'objet imbriqué
737
+ const t = useTranslations("about.counter");
738
+ const format = useFormatter();
739
+ const [count, setCount] = useState(0);
740
+
741
+ return (
742
+ <div>
743
+ <p>{format.number(count)}</p>
744
+ <button
745
+ aria-label={t("label")}
746
+ onClick={() => setCount((count) => count + 1)}
747
+ >
748
+ {t("increment")}
749
+ </button>
750
+ </div>
751
+ );
752
+ };
753
+ ```
754
+
755
+ > N'oubliez pas d'ajouter le message "about" dans les messages clients de la page
756
+
757
+ </TabItem>
758
+ <TabItem label="intlayer" value="intlayer">
759
+
760
+ **Contenu**
761
+
762
+ ```ts fileName="src/components/ClientComponentExample/index.content.ts"
763
+ import { t, type Dictionary } from "intlayer";
764
+
765
+ const counterContent = {
766
+ key: "counter",
767
+ content: {
768
+ label: t({ en: "Counter", fr: "Compteur" }),
769
+ increment: t({ en: "Increment", fr: "Incrémenter" }),
770
+ },
771
+ } satisfies Dictionary;
772
+
773
+ export default counterContent;
774
+ ```
775
+
776
+ **Composant client**
777
+
778
+ ```tsx fileName="src/components/ClientComponentExample/index.tsx"
779
+ "use client";
780
+
781
+ import React, { useState } from "react";
782
+ import { useNumber, useIntlayer } from "next-intlayer";
783
+
784
+ const ClientComponentExample = () => {
785
+ const [count, setCount] = useState(0);
786
+ const { label, increment } = useIntlayer("counter"); // retourne des chaînes de caractères
787
+ const { number } = useNumber();
788
+
789
+ return (
790
+ <div>
791
+ <p>{number(count)}</p>
792
+ <button aria-label={label} onClick={() => setCount((count) => count + 1)}>
793
+ {increment}
794
+ </button>
795
+ </div>
796
+ );
797
+ };
798
+ ```
799
+
800
+ </TabItem>
801
+ </Tab>
802
+
803
+ #### Comparaison
804
+
805
+ - **Formatage des nombres**
806
+ - **next-i18next** : pas de `useNumber` ; utilise `Intl.NumberFormat` (ou i18next-icu).
807
+ - **next-intl** : `useFormatter().number(value)`.
808
+ - **Intlayer** : `useNumber()` intégré.
809
+
810
+ - **Clés**
811
+ - Gardez une structure imbriquée (`about.counter.label`) et adaptez la portée de votre hook en conséquence (`useTranslation("about")` + `t("counter.label")` ou `useTranslations("about.counter")` + `t("label")`).
812
+
813
+ - **Emplacements des fichiers**
814
+ - **next-i18next** attend des JSON dans `public/locales/{lng}/{ns}.json`.
815
+ - **next-intl** est flexible ; chargez les messages comme vous le configurez.
816
+ - **Intlayer** stocke le contenu dans des dictionnaires TS/JS et résout par clé.
817
+
135
818
  ---
136
819
 
137
- ## Quand choisir quoi ?
820
+ ### Utilisation dans un composant serveur
821
+
822
+ Nous prendrons le cas d'un composant UI. Ce composant est un composant serveur, et doit pouvoir être inséré en tant qu'enfant d'un composant client. (page (composant serveur) -> composant client -> composant serveur). Comme ce composant peut être inséré en tant qu'enfant d'un composant client, il ne peut pas être asynchrone.
823
+
824
+ <Tab defaultTab="next-intl" group='techno'>
825
+ <TabItem label="next-i18next" value="next-i18next">
826
+
827
+ ```tsx fileName="src/pages/about.tsx"
828
+ import type { GetStaticProps } from "next";
829
+ import { useTranslation } from "next-i18next";
830
+
831
+ type ServerComponentProps = {
832
+ count: number;
833
+ };
834
+
835
+ const ServerComponent = ({ count }: ServerComponentProps) => {
836
+ const { t, i18n } = useTranslation("about");
837
+ const formatted = new Intl.NumberFormat(i18n.language).format(count);
838
+
839
+ return (
840
+ <div>
841
+ <p>{formatted}</p>
842
+ <button aria-label={t("counter.label")}>{t("counter.increment")}</button>
843
+ </div>
844
+ );
845
+ };
846
+ ```
847
+
848
+ </TabItem>
849
+ <TabItem label="next-intl" value="next-intl">
850
+
851
+ ```tsx fileName="src/components/ServerComponent.tsx"
852
+ type ServerComponentProps = {
853
+ count: number;
854
+ t: (key: string) => string;
855
+ formatter: Intl.NumberFormat;
856
+ };
857
+
858
+ const ServerComponent = ({ t, count, formatter }: ServerComponentProps) => {
859
+ const formatted = formatter.format(count);
860
+
861
+ return (
862
+ <div>
863
+ <p>{formatted}</p>
864
+ <button aria-label={t("label")}>{t("increment")}</button>
865
+ </div>
866
+ );
867
+ };
868
+ ```
869
+
870
+ > Comme le composant serveur ne peut pas être asynchrone, vous devez passer les traductions et la fonction de formatage en tant que props.
871
+ >
872
+ > - `const t = await getTranslations("about.counter");`
873
+ > - `const formatter = await getFormatter().then((formatter) => formatter.number());`
874
+
875
+ </TabItem>
876
+ <TabItem label="intlayer" value="intlayer">
877
+
878
+ ```tsx fileName="src/components/ServerComponent.tsx"
879
+ import { useIntlayer, useNumber } from "next-intlayer/server";
880
+
881
+ const ServerComponent = ({ count }: { count: number }) => {
882
+ const { label, increment } = useIntlayer("counter");
883
+ const { number } = useNumber();
884
+
885
+ return (
886
+ <div>
887
+ <p>{number(count)}</p>
888
+ <button aria-label={label}>{increment}</button>
889
+ </div>
890
+ );
891
+ };
892
+ ```
893
+
894
+ </TabItem>
895
+ </Tab>
896
+
897
+ > Intlayer expose des hooks **sécurisés pour le serveur** via `next-intlayer/server`. Pour fonctionner, `useIntlayer` et `useNumber` utilisent une syntaxe de type hooks, similaire aux hooks côté client, mais dépendent en interne du contexte serveur (`IntlayerServerProvider`).
898
+
899
+ ### Métadonnées / Sitemap / Robots
900
+
901
+ Traduire le contenu est excellent. Mais les gens oublient souvent que le but principal de l'internationalisation est de rendre votre site web plus visible dans le monde. L'i18n est un levier incroyable pour améliorer la visibilité de votre site web.
138
902
 
139
- - **Choisissez next-intl** si vous souhaitez une solution **minimale**, que vous êtes à l’aise avec des catalogues centralisés, et que votre application est de taille **petite à moyenne**.
140
- - **Choisissez next-i18next** si vous avez besoin de **l’écosystème de plugins d’i18next** (par exemple, des règles ICU avancées via des plugins) et que votre équipe connaît déjà i18next, acceptant une **configuration plus importante** pour plus de flexibilité.
141
- - **Choisissez Intlayer** si vous valorisez le **contenu scoped par composant**, un **TypeScript strict**, des **garanties à la compilation**, le **tree-shaking**, et des outils de routage/SEO/édition **tout-en-un** - particulièrement pour le **Next.js App Router** et les **bases de code larges et modulaires**.
903
+ Voici une liste de bonnes pratiques concernant le SEO multilingue.
904
+
905
+ - définir les balises meta hreflang dans la balise `<head>`
906
+ > Cela aide les moteurs de recherche à comprendre quelles langues sont disponibles sur la page
907
+ - lister toutes les traductions des pages dans le sitemap.xml en utilisant le schéma XML `http://www.w3.org/1999/xhtml`
908
+ >
909
+ - ne pas oublier d'exclure les pages préfixées du robots.txt (par exemple `/dashboard`, et `/fr/dashboard`, `/es/dashboard`)
910
+ >
911
+ - utiliser un composant Link personnalisé pour rediriger vers la page la plus localisée (par exemple en français `<a href="/fr/about">À propos</a>`)
912
+ >
913
+
914
+ Les développeurs oublient souvent de référencer correctement leurs pages selon les locales.
915
+
916
+ <Tab defaultTab="next-intl" group='techno'>
917
+
918
+ <TabItem label="next-i18next" value="next-i18next">
919
+
920
+ ```ts fileName="i18n.config.ts"
921
+ export const locales = ["en", "fr"] as const;
922
+ export type Locale = (typeof locales)[number];
923
+ export const defaultLocale: Locale = "en";
924
+
925
+ export function localizedPath(locale: string, path: string) {
926
+ return locale === defaultLocale ? path : "/" + locale + path;
927
+ }
928
+
929
+ const ORIGIN = "https://example.com";
930
+ export function abs(locale: string, path: string) {
931
+ return ORIGIN + localizedPath(locale, path);
932
+ }
933
+ ```
934
+
935
+ ```tsx fileName="src/app/[locale]/about/layout.tsx"
936
+ import type { Metadata } from "next";
937
+ import { locales, defaultLocale, localizedPath } from "@/i18n.config";
938
+
939
+ export async function generateMetadata({
940
+ params,
941
+ }: {
942
+ params: { locale: string };
943
+ }): Promise<Metadata> {
944
+ const { locale } = params;
945
+
946
+ // Importer dynamiquement le fichier JSON correct
947
+ const messages = (
948
+ await import("@/../public/locales/" + locale + "/about.json")
949
+ ).default;
950
+
951
+ const languages = Object.fromEntries(
952
+ locales.map((locale) => [locale, localizedPath(locale, "/about")])
953
+ );
954
+
955
+ return {
956
+ title: messages.title,
957
+ description: messages.description,
958
+ alternates: {
959
+ canonical: localizedPath(locale, "/about"),
960
+ languages: { ...languages, "x-default": "/about" },
961
+ },
962
+ };
963
+ }
964
+
965
+ export default async function AboutPage() {
966
+ return <h1>À propos</h1>;
967
+ }
968
+ ```
969
+
970
+ ```ts fileName="src/app/sitemap.ts"
971
+ import type { MetadataRoute } from "next";
972
+ import { locales, defaultLocale, abs } from "@/i18n.config";
973
+
974
+ export default function sitemap(): MetadataRoute.Sitemap {
975
+ const languages = Object.fromEntries(
976
+ locales.map((locale) => [locale, abs(locale, "/about")])
977
+ );
978
+ return [
979
+ {
980
+ url: abs(defaultLocale, "/about"),
981
+ lastModified: new Date(),
982
+ changeFrequency: "monthly",
983
+ priority: 0.7,
984
+ alternates: { languages },
985
+ },
986
+ ];
987
+ }
988
+ ```
989
+
990
+ ```ts fileName="src/app/robots.ts"
991
+ import type { MetadataRoute } from "next";
992
+ import { locales, defaultLocale, localizedPath } from "@/i18n.config";
993
+
994
+ const ORIGIN = "https://example.com";
995
+
996
+ const expandAllLocales = (path: string) => [
997
+ localizedPath(defaultLocale, path),
998
+ ...locales
999
+ .filter((locale) => locale !== defaultLocale)
1000
+ .map((locale) => localizedPath(locale, path)),
1001
+ ];
1002
+
1003
+ export default function robots(): MetadataRoute.Robots {
1004
+ const disallow = [
1005
+ ...expandAllLocales("/dashboard"),
1006
+ ...expandAllLocales("/admin"),
1007
+ ];
1008
+
1009
+ return {
1010
+ rules: { userAgent: "*", allow: ["/"], disallow },
1011
+ host: ORIGIN,
1012
+ sitemap: ORIGIN + "/sitemap.xml",
1013
+ };
1014
+ }
1015
+ ```
1016
+
1017
+ </TabItem>
1018
+ <TabItem label="next-intl" value="next-intl">
1019
+
1020
+ ```tsx fileName="src/app/[locale]/about/layout.tsx"
1021
+ import type { Metadata } from "next";
1022
+ import { locales, defaultLocale } from "@/i18n";
1023
+ import { getTranslations } from "next-intl/server";
1024
+
1025
+ function localizedPath(locale: string, path: string) {
1026
+ return locale === defaultLocale ? path : "/" + locale + path;
1027
+ }
1028
+
1029
+ export async function generateMetadata({
1030
+ params,
1031
+ }: {
1032
+ params: { locale: string };
1033
+ }): Promise<Metadata> {
1034
+ const { locale } = params;
1035
+ const t = await getTranslations({ locale, namespace: "about" });
1036
+
1037
+ const url = "/about";
1038
+ const languages = Object.fromEntries(
1039
+ locales.map((locale) => [locale, localizedPath(locale, url)])
1040
+ );
1041
+
1042
+ return {
1043
+ title: t("title"),
1044
+ description: t("description"),
1045
+ alternates: {
1046
+ canonical: localizedPath(locale, url),
1047
+ languages: { ...languages, "x-default": url },
1048
+ },
1049
+ };
1050
+ }
1051
+
1052
+ // ... Le reste du code de la page
1053
+ ```
1054
+
1055
+ ```tsx fileName="src/app/sitemap.ts"
1056
+ import type { MetadataRoute } from "next";
1057
+ import { locales, defaultLocale } from "@/i18n";
1058
+
1059
+ const origin = "https://example.com";
1060
+
1061
+ const formatterLocalizedPath = (locale: string, path: string) =>
1062
+ locale === defaultLocale ? origin + path : origin + "/" + locale + path;
1063
+
1064
+ export default function sitemap(): MetadataRoute.Sitemap {
1065
+ const aboutLanguages = Object.fromEntries(
1066
+ locales.map((l) => [l, formatterLocalizedPath(l, "/about")])
1067
+ );
1068
+
1069
+ return [
1070
+ {
1071
+ url: formatterLocalizedPath(defaultLocale, "/about"),
1072
+ lastModified: new Date(),
1073
+ changeFrequency: "monthly", // fréquence de modification
1074
+ priority: 0.7, // priorité dans le sitemap
1075
+ alternates: { languages: aboutLanguages }, // versions alternatives par langue
1076
+ },
1077
+ ];
1078
+ }
1079
+ ```
1080
+
1081
+ ```tsx fileName="src/app/robots.ts"
1082
+ import type { MetadataRoute } from "next";
1083
+ import { locales, defaultLocale } from "@/i18n";
1084
+
1085
+ const origin = "https://example.com";
1086
+ const withAllLocales = (path: string) => [
1087
+ path,
1088
+ ...locales
1089
+ .filter((locale) => locale !== defaultLocale)
1090
+ .map((locale) => "/" + locale + path),
1091
+ ];
1092
+
1093
+ export default function robots(): MetadataRoute.Robots {
1094
+ const disallow = [
1095
+ ...withAllLocales("/dashboard"),
1096
+ ...withAllLocales("/admin"),
1097
+ ];
1098
+
1099
+ return {
1100
+ rules: { userAgent: "*", allow: ["/"], disallow },
1101
+ host: origin,
1102
+ sitemap: origin + "/sitemap.xml",
1103
+ };
1104
+ }
1105
+ ```
1106
+
1107
+ </TabItem>
1108
+ <TabItem label="intlayer" value="intlayer">
1109
+
1110
+ ```typescript fileName="src/app/[locale]/about/layout.tsx"
1111
+ import { getIntlayer, getMultilingualUrls } from "intlayer";
1112
+ import type { Metadata } from "next";
1113
+ import type { LocalPromiseParams } from "next-intlayer";
1114
+
1115
+ export const generateMetadata = async ({
1116
+ params,
1117
+ }: LocalPromiseParams): Promise<Metadata> => {
1118
+ const { locale } = await params;
1119
+
1120
+ const metadata = getIntlayer("page-metadata", locale);
1121
+
1122
+ const multilingualUrls = getMultilingualUrls("/about");
1123
+
1124
+ return {
1125
+ ...metadata,
1126
+ alternates: {
1127
+ canonical: multilingualUrls[locale as keyof typeof multilingualUrls],
1128
+ languages: { ...multilingualUrls, "x-default": "/about" },
1129
+ },
1130
+ };
1131
+ };
1132
+
1133
+ // ... Reste du code de la page
1134
+ ```
1135
+
1136
+ ```tsx fileName="src/app/sitemap.ts"
1137
+ import { getMultilingualUrls } from "intlayer";
1138
+ import type { MetadataRoute } from "next";
1139
+
1140
+ const sitemap = (): MetadataRoute.Sitemap => [
1141
+ {
1142
+ url: "https://example.com/about",
1143
+ alternates: {
1144
+ languages: { ...getMultilingualUrls("https://example.com/about") },
1145
+ },
1146
+ },
1147
+ ];
1148
+ ```
1149
+
1150
+ ```tsx fileName="src/app/robots.ts"
1151
+ import { getMultilingualUrls } from "intlayer";
1152
+ import type { MetadataRoute } from "next";
1153
+
1154
+ const getAllMultilingualUrls = (urls: string[]) =>
1155
+ urls.flatMap((url) => Object.values(getMultilingualUrls(url)) as string[]);
1156
+
1157
+ // Règles pour le fichier robots.txt
1158
+ const robots = (): MetadataRoute.Robots => ({
1159
+ rules: {
1160
+ userAgent: "*",
1161
+ allow: ["/"],
1162
+ disallow: getAllMultilingualUrls(["/dashboard"]), // Interdire l'accès à toutes les URLs multilingues du dashboard
1163
+ },
1164
+ host: "https://example.com",
1165
+ sitemap: "https://example.com/sitemap.xml",
1166
+ });
1167
+
1168
+ export default robots;
1169
+ ```
1170
+
1171
+ </TabItem>
1172
+ </Tab>
1173
+
1174
+ > Intlayer fournit une fonction `getMultilingualUrls` pour générer des URLs multilingues pour votre sitemap.
1175
+
1176
+ ---
142
1177
 
143
1178
  ---
144
1179
 
145
- ## Notes pratiques de migration (next-intl / next-i18next → Intlayer)
1180
+ ## Et le gagnant est…
1181
+
1182
+ Ce n’est pas simple. Chaque option a ses compromis. Voici comment je le vois :
1183
+
1184
+ <Columns>
1185
+ <Column>
1186
+
1187
+ **next-intl**
1188
+
1189
+ - le plus simple, léger, avec moins de décisions imposées. Si vous voulez une solution **minimale**, que vous êtes à l’aise avec des catalogues centralisés, et que votre application est de taille **petite à moyenne**.
1190
+
1191
+ </Column>
1192
+ <Column>
1193
+
1194
+ **next-i18next**
1195
+
1196
+ - mature, riche en fonctionnalités, avec beaucoup de plugins communautaires, mais un coût de configuration plus élevé. Si vous avez besoin de **l’écosystème de plugins d’i18next** (par exemple, des règles ICU avancées via des plugins) et que votre équipe connaît déjà i18next, en acceptant **plus de configuration** pour plus de flexibilité.
1197
+
1198
+ </Column>
1199
+ <Column>
1200
+
1201
+ **Intlayer**
1202
+
1203
+ - conçu pour Next.js moderne, avec un contenu modulaire, une sécurité de type, des outils, et moins de code répétitif. Si vous valorisez le **contenu limité au composant**, le **TypeScript strict**, les **garanties à la compilation**, le **tree-shaking**, et des outils de routage/SEO/éditeur **inclus d’office** - en particulier pour le **Next.js App Router**, les systèmes de design et les **bases de code larges et modulaires**.
1204
+
1205
+ </Column>
1206
+ </Columns>
1207
+
1208
+ Si vous préférez une configuration minimale et acceptez un certain câblage manuel, next-intl est un bon choix. Si vous avez besoin de toutes les fonctionnalités et que la complexité ne vous dérange pas, next-i18next fonctionne. Mais si vous voulez une solution moderne, évolutive, modulaire avec des outils intégrés, Intlayer vise à vous offrir cela prêt à l’emploi.
1209
+
1210
+ > **Alternative pour les équipes d'entreprise** : Si vous avez besoin d'une solution éprouvée qui fonctionne parfaitement avec des plateformes de localisation établies comme **Crowdin**, **Phrase**, ou d'autres systèmes professionnels de gestion de traduction, considérez **next-intl** ou **next-i18next** pour leur écosystème mature et leurs intégrations éprouvées.
1211
+
1212
+ > **Feuille de route future** : Intlayer prévoit également de développer des plugins qui fonctionneront par-dessus les solutions **i18next** et **next-intl**. Cela vous offrira les avantages d'Intlayer pour l'automatisation, la syntaxe et la gestion de contenu tout en conservant la sécurité et la stabilité fournies par ces solutions établies dans votre code applicatif.
1213
+
1214
+ ## Étoiles GitHub (GitHub STARs)
1215
+
1216
+ Les étoiles GitHub sont un indicateur fort de la popularité d’un projet, de la confiance de la communauté et de sa pertinence à long terme. Bien qu’elles ne mesurent pas directement la qualité technique, elles reflètent combien de développeurs trouvent le projet utile, suivent son évolution et sont susceptibles de l’adopter. Pour estimer la valeur d’un projet, les étoiles aident à comparer la traction entre différentes alternatives et fournissent des informations sur la croissance de l’écosystème.
146
1217
 
147
- - **Commencez par fonctionnalité** : Déplacez une route ou un composant à la fois vers des **dictionnaires locaux**.
148
- - **Conservez les anciens catalogues en parallèle** : Assurez une transition progressive ; évitez un changement brutal.
149
- - **Activez les vérifications strictes** : Laissez la détection à la compilation révéler les lacunes tôt.
150
- - **Adoptez middleware et helpers** : Standardisez la détection de la locale et les balises SEO sur tout le site.
151
- - **Mesurez les bundles** : Attendez-vous à des **réductions de taille des bundles** grâce à la suppression du contenu inutilisé.
1218
+ [![Graphique de l’historique des étoiles](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
1219
 
153
1220
  ---
154
1221
 
155
1222
  ## Conclusion
156
1223
 
157
- Les trois bibliothèques réussissent la localisation de base. La différence réside dans **la quantité de travail nécessaire** pour obtenir une configuration robuste et évolutive dans **Next.js moderne** :
1224
+ Les trois bibliothèques réussissent la localisation de base. La différence réside dans **la quantité de travail que vous devez fournir** pour obtenir une configuration robuste et évolutive dans **Next.js moderne** :
158
1225
 
159
- - Avec **Intlayer**, le **contenu modulaire**, le **TS strict**, la **sécurité à la compilation**, les **bundles optimisés par tree-shaking**, ainsi que un **App Router et des outils SEO de première classe** sont des **paramètres par défaut**, et non des corvées.
160
- - Si votre équipe valorise la **maintenabilité et la rapidité** dans une application multi-locale pilotée par composants, Intlayer offre aujourd’hui l’expérience la **plus complète**.
1226
+ - Avec **Intlayer**, le **contenu modulaire**, **TypeScript strict**, **la sécurité à la compilation**, **les bundles optimisés par tree-shaking**, et **un App Router de première classe avec des outils SEO** sont des **paramètres par défaut**, et non des corvées.
1227
+ - Si votre équipe valorise la **maintenabilité et la rapidité** dans une application multi-langues pilotée par composants, Intlayer offre aujourd’hui l’expérience la **plus complète**.
161
1228
 
162
- Consultez le document ['Pourquoi Intlayer ?'](https://intlayer.org/doc/why) pour plus de détails.
1229
+ Consultez la documentation ['Pourquoi Intlayer ?'](https://intlayer.org/doc/why) pour plus de détails.