@intlayer/docs 8.7.5-canary.0 → 8.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/blog/de/next-i18next_vs_next-intl_vs_intlayer.md +0 -2
- package/blog/en-GB/next-i18next_vs_next-intl_vs_intlayer.md +0 -2
- package/blog/es/next-i18next_vs_next-intl_vs_intlayer.md +0 -2
- package/blog/fr/next-i18next_vs_next-intl_vs_intlayer.md +0 -2
- package/blog/id/list_i18n_technologies/frameworks/svelte.md +0 -2
- package/blog/it/next-i18next_vs_next-intl_vs_intlayer.md +0 -2
- package/blog/ja/next-i18next_vs_next-intl_vs_intlayer.md +0 -2
- package/blog/ko/next-i18next_vs_next-intl_vs_intlayer.md +0 -2
- package/blog/pl/list_i18n_technologies/frameworks/svelte.md +0 -2
- package/blog/pt/next-i18next_vs_next-intl_vs_intlayer.md +0 -2
- package/blog/ru/next-i18next_vs_next-intl_vs_intlayer.md +0 -2
- package/blog/vi/list_i18n_technologies/frameworks/svelte.md +0 -2
- package/blog/zh/next-i18next_vs_next-intl_vs_intlayer.md +0 -2
- package/dist/cjs/generated/docs.entry.cjs +60 -0
- package/dist/cjs/generated/docs.entry.cjs.map +1 -1
- package/dist/esm/generated/docs.entry.mjs +60 -0
- package/dist/esm/generated/docs.entry.mjs.map +1 -1
- package/dist/types/generated/docs.entry.d.ts +3 -0
- package/dist/types/generated/docs.entry.d.ts.map +1 -1
- package/docs/ar/benchmark/index.md +29 -0
- package/docs/ar/benchmark/nextjs.md +227 -0
- package/docs/ar/benchmark/tanstack.md +193 -0
- package/docs/ar/intlayer_with_tanstack.md +0 -2
- package/docs/de/benchmark/index.md +29 -0
- package/docs/de/benchmark/nextjs.md +227 -0
- package/docs/de/benchmark/tanstack.md +193 -0
- package/docs/en/benchmark/___NOTE.md +82 -0
- package/docs/en/benchmark/___nextjs.md +195 -0
- package/docs/en/benchmark/___tanstack.md +187 -0
- package/docs/en/benchmark/index.md +29 -0
- package/docs/en/benchmark/nextjs.md +228 -0
- package/docs/en/benchmark/tanstack.md +217 -0
- package/docs/en-GB/benchmark/index.md +29 -0
- package/docs/en-GB/benchmark/nextjs.md +228 -0
- package/docs/en-GB/benchmark/tanstack.md +193 -0
- package/docs/es/benchmark/index.md +29 -0
- package/docs/es/benchmark/nextjs.md +226 -0
- package/docs/es/benchmark/tanstack.md +193 -0
- package/docs/fr/benchmark/index.md +29 -0
- package/docs/fr/benchmark/nextjs.md +227 -0
- package/docs/fr/benchmark/tanstack.md +193 -0
- package/docs/hi/benchmark/index.md +29 -0
- package/docs/hi/benchmark/nextjs.md +227 -0
- package/docs/hi/benchmark/tanstack.md +193 -0
- package/docs/id/benchmark/index.md +29 -0
- package/docs/id/benchmark/nextjs.md +227 -0
- package/docs/id/benchmark/tanstack.md +193 -0
- package/docs/id/intlayer_with_react_native+expo.md +0 -2
- package/docs/it/benchmark/index.md +29 -0
- package/docs/it/benchmark/nextjs.md +227 -0
- package/docs/it/benchmark/tanstack.md +193 -0
- package/docs/ja/benchmark/index.md +29 -0
- package/docs/ja/benchmark/nextjs.md +227 -0
- package/docs/ja/benchmark/tanstack.md +193 -0
- package/docs/ko/benchmark/index.md +29 -0
- package/docs/ko/benchmark/nextjs.md +227 -0
- package/docs/ko/benchmark/tanstack.md +193 -0
- package/docs/ko/intlayer_with_tanstack.md +0 -2
- package/docs/pl/benchmark/index.md +29 -0
- package/docs/pl/benchmark/nextjs.md +227 -0
- package/docs/pl/benchmark/tanstack.md +193 -0
- package/docs/pt/benchmark/index.md +29 -0
- package/docs/pt/benchmark/nextjs.md +227 -0
- package/docs/pt/benchmark/tanstack.md +193 -0
- package/docs/ru/benchmark/index.md +29 -0
- package/docs/ru/benchmark/nextjs.md +227 -0
- package/docs/ru/benchmark/tanstack.md +193 -0
- package/docs/tr/benchmark/index.md +29 -0
- package/docs/tr/benchmark/nextjs.md +227 -0
- package/docs/tr/benchmark/tanstack.md +193 -0
- package/docs/uk/benchmark/index.md +29 -0
- package/docs/uk/benchmark/nextjs.md +227 -0
- package/docs/uk/benchmark/tanstack.md +193 -0
- package/docs/vi/benchmark/index.md +29 -0
- package/docs/vi/benchmark/nextjs.md +227 -0
- package/docs/vi/benchmark/tanstack.md +193 -0
- package/docs/zh/benchmark/index.md +29 -0
- package/docs/zh/benchmark/nextjs.md +227 -0
- package/docs/zh/benchmark/tanstack.md +193 -0
- package/package.json +6 -6
- package/src/generated/docs.entry.ts +60 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
createdAt: 2026-04-20
|
|
3
|
+
updatedAt: 2026-04-20
|
|
4
|
+
title: Benchmark des bibliothèques i18n
|
|
5
|
+
description: Découvrez comment Intlayer se compare aux autres bibliothèques i18n en termes de performance et de taille de bundle.
|
|
6
|
+
keywords:
|
|
7
|
+
- benchmark
|
|
8
|
+
- i18n
|
|
9
|
+
- intl
|
|
10
|
+
- nextjs
|
|
11
|
+
- tanstack
|
|
12
|
+
- intlayer
|
|
13
|
+
slugs:
|
|
14
|
+
- doc
|
|
15
|
+
- benchmark
|
|
16
|
+
history:
|
|
17
|
+
- version: 8.7.5
|
|
18
|
+
date: 2026-01-06
|
|
19
|
+
changes: "Initialisation du benchmark"
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
# Benchmark - Rapport
|
|
23
|
+
|
|
24
|
+
Benchmark Bloom est une suite de benchmarks de performance qui mesure l'impact réel des bibliothèques i18n (internationalisation) sur plusieurs frameworks React et stratégies de chargement.
|
|
25
|
+
|
|
26
|
+
Les rapports détaillés et la documentation technique pour chaque framework se trouvent ci-dessous :
|
|
27
|
+
|
|
28
|
+
- [**Rapport de benchmark Next.js**](https://github.com/aymericzip/intlayer/blob/main/docs/docs/fr/benchmark/nextjs.md)
|
|
29
|
+
- [**Rapport de benchmark TanStack Start**](https://github.com/aymericzip/intlayer/blob/main/docs/docs/fr/benchmark/tanstack.md)
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
---
|
|
2
|
+
createdAt: 2026-04-20
|
|
3
|
+
updatedAt: 2026-04-21
|
|
4
|
+
title: La meilleure solution i18n pour Next.js en 2026 - Rapport de Benchmark
|
|
5
|
+
description: Comparez les bibliothèques d'internationalisation (i18n) pour Next.js comme next-intl, next-i18next et Intlayer. Rapport de performance détaillé sur la taille du bundle, les fuites et la réactivité.
|
|
6
|
+
keywords:
|
|
7
|
+
- benchmark
|
|
8
|
+
- i18n
|
|
9
|
+
- intl
|
|
10
|
+
- nextjs
|
|
11
|
+
- performance
|
|
12
|
+
- intlayer
|
|
13
|
+
slugs:
|
|
14
|
+
- doc
|
|
15
|
+
- benchmark
|
|
16
|
+
- nextjs
|
|
17
|
+
author: Aymeric PINEAU
|
|
18
|
+
applicationTemplate: https://github.com/intlayer-org/benchmark-i18n
|
|
19
|
+
history:
|
|
20
|
+
- version: 8.7.5
|
|
21
|
+
date: 2026-01-06
|
|
22
|
+
changes: "Initialisation du benchmark"
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
# Bibliothèques i18n Next.js — Rapport de Benchmark 2026
|
|
26
|
+
|
|
27
|
+
Cette page est un rapport de benchmark pour les solutions i18n sur Next.js.
|
|
28
|
+
|
|
29
|
+
## Table des Matières
|
|
30
|
+
|
|
31
|
+
<Toc/>
|
|
32
|
+
|
|
33
|
+
## Benchmark Interactif
|
|
34
|
+
|
|
35
|
+
<I18nBenchmark framework="nextjs" vertical/>
|
|
36
|
+
|
|
37
|
+
## Référence des résultats :
|
|
38
|
+
|
|
39
|
+
<iframe
|
|
40
|
+
src="https://intlayer.org/markdown?url=https%3A%2F%2Fraw.githubusercontent.com%2Fintlayer-org%2Fbenchmark-i18n%2Fmain%2Freport%2Fscripts%2Fsummarize-nextjs.md"
|
|
41
|
+
width="100%"
|
|
42
|
+
height="600px"
|
|
43
|
+
style="border:none;">
|
|
44
|
+
</iframe>
|
|
45
|
+
|
|
46
|
+
> https://intlayer.org/markdown?url=https%3A%2F%2Fraw.githubusercontent.com%2Fintlayer-org%2Fbenchmark-i18n%2Fmain%2Freport%2Fscripts%2Fsummarize-nextjs.md
|
|
47
|
+
|
|
48
|
+
Voir le dépôt complet du benchmark [ici](https://github.com/intlayer-org/benchmark-i18n).
|
|
49
|
+
|
|
50
|
+
## Introduction
|
|
51
|
+
|
|
52
|
+
Les bibliothèques d'internationalisation ont un impact lourd sur votre application. Le risque principal est de charger du contenu pour chaque page et chaque langue alors que l'utilisateur ne visite qu'une seule page.
|
|
53
|
+
|
|
54
|
+
À mesure que votre application grandit, la taille du bundle peut augmenter de manière exponentielle, ce qui peut nuire considérablement aux performances.
|
|
55
|
+
|
|
56
|
+
Par exemple, pour les cas les plus critiques, une fois internationalisée, votre page peut finir par être près de 4 fois plus volumineuse.
|
|
57
|
+
|
|
58
|
+
Un autre impact des bibliothèques i18n est le ralentissement du développement. Transformer des composants en contenu multilingue à travers plusieurs langues prend du temps.
|
|
59
|
+
|
|
60
|
+
Parce que le problème est complexe, de nombreuses solutions existent — certaines axées sur la DX (expérience développeur), d'autres sur la performance ou la scalabilité, etc.
|
|
61
|
+
|
|
62
|
+
Intlayer tente d'optimiser l'ensemble de ces dimensions.
|
|
63
|
+
|
|
64
|
+
## Testez votre application
|
|
65
|
+
|
|
66
|
+
Pour mettre en lumière ces problèmes, j'ai construit un scanner gratuit que vous pouvez essayer [ici](https://intlayer.org/i18n-seo-scanner).
|
|
67
|
+
|
|
68
|
+
<iframe src="https://intlayer.org/i18n-seo-scanner" width="100%" height="600px" style="border:none;"/>
|
|
69
|
+
|
|
70
|
+
## Le problème
|
|
71
|
+
|
|
72
|
+
Il existe deux manières majeures de limiter l'impact d'une application multilingue sur votre bundle :
|
|
73
|
+
|
|
74
|
+
- Diviser votre JSON (ou contenu) par fichiers / variables / namespaces afin que le bundler puisse "tree-shaker" le contenu inutilisé pour une page donnée
|
|
75
|
+
- Charger dynamiquement le contenu de votre page uniquement dans la langue de l'utilisateur
|
|
76
|
+
|
|
77
|
+
Limitations techniques pour ces approches :
|
|
78
|
+
|
|
79
|
+
**Chargement dynamique**
|
|
80
|
+
|
|
81
|
+
Même lorsque vous déclarez des routes comme `[locale]/page.tsx`, avec Webpack ou Turbopack, et même si `generateStaticParams` est défini, le bundler ne traite pas `locale` comme une constante statique. Cela signifie qu'il peut inclure le contenu de toutes les langues dans chaque page. Le principal moyen de limiter cela est de charger le contenu via un import dynamique (ex: `import('./locales/${locale}.json')`).
|
|
82
|
+
|
|
83
|
+
Ce qui se passe au moment du build, c'est que Next.js émet un bundle JS par locale (ex: `./locales_fr_12345.js`). Une fois le site envoyé au client, lors de l'exécution de la page, le navigateur effectue une requête HTTP supplémentaire pour le fichier JS nécessaire (ex: `./locales_fr_12345.js`).
|
|
84
|
+
|
|
85
|
+
> Une autre façon de traiter le même problème est d'utiliser `fetch()` pour charger le JSON dynamiquement. C'est ainsi que `Tolgee` fonctionne lorsque le JSON se trouve sous `/public`, ou `next-translate`, qui s'appuie sur `getStaticProps` pour charger le contenu. Le flux est le même : le navigateur fait une requête HTTP supplémentaire pour charger l'asset.
|
|
86
|
+
|
|
87
|
+
**Découpage du contenu (Splitting)**
|
|
88
|
+
|
|
89
|
+
Si vous utilisez une syntaxe comme `const t = useTranslation()` + `t('mon-objet.mon-sous-objet.ma-cle')`, l'intégralité du JSON doit généralement être présent dans le bundle pour que la bibliothèque puisse le parser et résoudre la clé. Une grande partie de ce contenu est donc expédiée même lorsqu'il est inutilisé sur la page.
|
|
90
|
+
|
|
91
|
+
Pour atténuer cela, certaines bibliothèques vous demandent de déclarer par page quels namespaces charger — ex: `next-i18next`, `next-intl`, `lingui`, `next-translate`, `next-international`.
|
|
92
|
+
|
|
93
|
+
En revanche, `Paraglide` ajoute une étape supplémentaire avant le build pour transformer le JSON en symboles plats comme `const en_my_var = () => 'ma valeur'`. En théorie, cela permet au tree-shaking de supprimer le contenu inutilisé sur la page. Comme nous le verrons, cette méthode présente tout de même des compromis.
|
|
94
|
+
|
|
95
|
+
Enfin, `Intlayer` applique une optimisation au moment du build afin que `useIntlayer('ma-cle')` soit remplacé directement par le contenu correspondant.
|
|
96
|
+
|
|
97
|
+
## Méthodologie
|
|
98
|
+
|
|
99
|
+
Pour ce benchmark, nous avons comparé les bibliothèques suivantes :
|
|
100
|
+
|
|
101
|
+
- `Base App` (Pas de bibliothèque i18n)
|
|
102
|
+
- `next-intlayer` (v8.7.5)
|
|
103
|
+
- `next-i18next` (v16.0.5)
|
|
104
|
+
- `next-intl` (v4.9.1)
|
|
105
|
+
- `@lingui/core` (v5.3.0)
|
|
106
|
+
- `next-translate` (v3.1.2)
|
|
107
|
+
- `next-international` (v1.3.1)
|
|
108
|
+
- `@inlang/paraglide-js` (v2.15.1)
|
|
109
|
+
- `tolgee` (v7.0.0)
|
|
110
|
+
- `@lingo.dev/compiler` (v0.4.0)
|
|
111
|
+
- `wuchale` (v0.22.11)
|
|
112
|
+
- `gt-next` (v6.16.5)
|
|
113
|
+
|
|
114
|
+
J'ai utilisé `Next.js` en version `16.2.4` avec l'App Router.
|
|
115
|
+
|
|
116
|
+
J'ai construit une application multilingue avec **10 pages** et **10 langues**.
|
|
117
|
+
|
|
118
|
+
J'ai comparé **quatre stratégies de chargement** :
|
|
119
|
+
|
|
120
|
+
| Stratégie | Sans namespaces (global) | Avec namespaces (scoped) |
|
|
121
|
+
| :----------------------- | :------------------------------------------------ | :------------------------------------------------------------------- |
|
|
122
|
+
| **Chargement statique** | **Static** : Tout en mémoire au démarrage. | **Scoped static** : Divisé par namespace ; tout chargé au démarrage. |
|
|
123
|
+
| **Chargement dynamique** | **Dynamic** : Chargement à la demande par locale. | **Scoped dynamic** : Chargement granulaire par namespace et locale. |
|
|
124
|
+
|
|
125
|
+
## Résumé des stratégies
|
|
126
|
+
|
|
127
|
+
- **Static** : Simple ; pas de latence réseau après le chargement initial. Inconvénient : taille de bundle importante.
|
|
128
|
+
- **Dynamic** : Réduit le poids initial (lazy-loading). Idéal lorsque vous avez de nombreuses locales.
|
|
129
|
+
- **Scoped static** : Organise bien le code (séparation logique) sans requêtes réseau supplémentaires complexes.
|
|
130
|
+
- **Scoped dynamic** : Meilleure approche pour le _code splitting_ et la performance. Minimise la mémoire en ne chargeant que ce dont la vue actuelle et la locale active ont besoin.
|
|
131
|
+
|
|
132
|
+
### Ce que j'ai mesuré :
|
|
133
|
+
|
|
134
|
+
J'ai fait tourner la même application multilingue dans un navigateur réel pour chaque stack, puis j'ai noté ce qui passait réellement sur le réseau et combien de temps les choses prenaient. Les tailles sont reportées **après compression web normale**, car c'est ce qui est le plus proche de ce que les gens téléchargent réellement.
|
|
135
|
+
|
|
136
|
+
- **Taille de la bibliothèque d'internationalisation** : Après bundling, tree-shaking et minification, la taille de la bibliothèque i18n est la taille des providers (ex: `NextIntlClientProvider`) + le code des hooks (ex: `useTranslations`) dans un composant vide. Cela n'inclut pas le chargement des fichiers de traduction. Cela répond à la question du coût de la bibliothèque avant même que votre contenu n'entre en jeu.
|
|
137
|
+
|
|
138
|
+
- **JavaScript par page** : Pour chaque route du benchmark, quelle quantité de script le navigateur récupère pour cette visite, moyennée sur l'ensemble des pages de la suite (et sur l'ensemble des locales lorsque le rapport les regroupe). Des pages lourdes sont des pages lentes.
|
|
139
|
+
|
|
140
|
+
- **Fuite depuis d'autres locales** : C'est le contenu de la même page mais dans une autre langue qui serait chargé par erreur dans la page auditée. Ce contenu est inutile et devrait être évité (ex: contenu de la page `/fr/about` dans le bundle de la page `/en/about`).
|
|
141
|
+
|
|
142
|
+
- **Fuite depuis d'autres routes** : La même idée pour les **autres écrans** de l'application : si leur texte accompagne le chargement alors que vous n'avez ouvert qu'une seule page (ex: contenu de la page `/en/about` dans le bundle de la page `/en/contact`). Un score élevé suggère un mauvais découpage ou des bundles trop larges.
|
|
143
|
+
|
|
144
|
+
- **Taille moyenne de bundle par composant** : Les éléments d'interface communs sont mesurés **un par un** au lieu de se cacher derrière un gros chiffre global de l'application. Cela montre si l'internationalisation gonfle silencieusement les composants quotidiens. Par exemple, si votre composant re-rend, il chargera toutes ces données depuis la mémoire. Attacher un JSON géant à n'importe quel composant revient à connecter un grand réservoir de données inutilisées qui ralentira la performance de vos composants.
|
|
145
|
+
|
|
146
|
+
- **Réactivité du changement de langue** : Je change la langue en utilisant le propre contrôle de l'application et je mesure le temps nécessaire jusqu'à ce que la page ait clairement basculé — ce qu'un visiteur remarquerait, et non une micro-étape de laboratoire.
|
|
147
|
+
|
|
148
|
+
- **Travail de rendu après un changement de langue** : Un suivi plus précis : quel effort l'interface a fourni pour se redessiner dans la nouvelle langue une fois le basculement lancé. Utile lorsque le ressenti utilisateur et le coût du framework divergent.
|
|
149
|
+
|
|
150
|
+
- **Temps de chargement initial de la page** : De la navigation jusqu'à ce que le navigateur considère la page comme entièrement chargée pour les scénarios testés. Idéal pour comparer les démarrages à froid (cold starts).
|
|
151
|
+
|
|
152
|
+
- **Temps d'hydratation** : Lorsque l'application le permet, combien de temps le client passe à transformer le HTML serveur en quelque chose sur lequel on peut réellement cliquer. Un tiret dans les tableaux signifie que cette implémentation n'a pas fourni de valeur d'hydratation fiable dans ce benchmark.
|
|
153
|
+
|
|
154
|
+
## Résultats détaillés
|
|
155
|
+
|
|
156
|
+
### 1 — Solutions à éviter
|
|
157
|
+
|
|
158
|
+
Certaines solutions, telles que `gt-next` ou `lingo.dev`, sont clairement à éviter. Elles combinent un verrouillage propriétaire (vendor lock-in) avec une pollution de votre base de code. Malgré de nombreuses heures passées à essayer de les implémenter, je n'ai jamais réussi à les faire fonctionner correctement — ni sur TanStack Start, ni sur Next.js.
|
|
159
|
+
|
|
160
|
+
Problèmes rencontrés :
|
|
161
|
+
|
|
162
|
+
**(General Translation)** (`gt-next@6.16.5`) :
|
|
163
|
+
|
|
164
|
+
- Pour une application de 110 Ko, `gt-react` ajoute plus de 440 Ko supplémentaires.
|
|
165
|
+
- `Quota Exceeded, please upgrade your plan` dès le tout premier build avec General Translation.
|
|
166
|
+
- Les traductions ne sont pas rendues ; j'obtiens l'erreur `Error: <T> used on the client-side outside of <GTProvider>`, ce qui semble être un bug de la bibliothèque.
|
|
167
|
+
- Lors de l'implémentation de **gt-tanstack-start-react**, je suis également tombé sur un [problème](https://github.com/generaltranslation/gt/issues/1210#event-24510646961) avec la bibliothèque : `does not provide an export named 'printAST' - @formatjs/icu-messageformat-parser`, ce qui cassait l'application. Après avoir signalé ce problème, le mainteneur l'a corrigé sous 24 heures.
|
|
168
|
+
- La bibliothèque bloque le rendu statique des pages Next.js.
|
|
169
|
+
|
|
170
|
+
**(Lingo.dev)** (`@lingo.dev/compiler@0.4.0`) :
|
|
171
|
+
|
|
172
|
+
- Quota AI dépassé, bloquant entièrement le build — vous ne pouvez donc pas mettre en production sans payer.
|
|
173
|
+
- Le compilateur ratait presque 40 % du contenu traduit. J'ai dû réécrire tous les `.map` en blocs de composants plats pour que cela fonctionne.
|
|
174
|
+
- Leur CLI est buggée et réinitialisait régulièrement le fichier de configuration sans raison.
|
|
175
|
+
- Au build, il effaçait totalement les JSONs générés lorsque du nouveau contenu était ajouté. Résultat : une poignée de clés pouvait rayer de la carte plus de 300 clés existantes.
|
|
176
|
+
|
|
177
|
+
### 2 — Solutions expérimentales
|
|
178
|
+
|
|
179
|
+
**(Wuchale)** (`wuchale@0.22.11`) :
|
|
180
|
+
|
|
181
|
+
L'idée derrière `Wuchale` est intéressante mais pas encore viable. J'ai rencontré des problèmes de réactivité et j'ai dû forcer le re-rendu du provider pour faire fonctionner l'application. La documentation est également assez floue, ce qui rend l'adoption difficile.
|
|
182
|
+
|
|
183
|
+
**(Paraglide)** (`@inlang/paraglide-js@2.15.1`) :
|
|
184
|
+
|
|
185
|
+
`Paraglide` propose une approche innovante et bien pensée. Pourtant, dans ce benchmark, le tree-shaking annoncé n'a pas fonctionné pour mes configurations Next.js ou TanStack Start. Le workflow et la DX sont plus complexes que d'autres options.
|
|
186
|
+
Personnellement, je n'aime pas devoir régénérer des fichiers JS avant chaque push, ce qui crée un risque constant de conflit de fusion via les PRs. L'outil semble également plus axé sur Vite que sur Next.js.
|
|
187
|
+
Enfin, par rapport aux autres solutions, Paraglide n'utilise pas de "store" (ex: contexte React) pour récupérer la langue actuelle afin de rendre le contenu. Pour chaque nœud analysé, il demandera la langue au localStorage / cookie etc. Cela conduit à l'exécution d'une logique inutile qui impacte la réactivité des composants.
|
|
188
|
+
|
|
189
|
+
### 3 — Solutions acceptables
|
|
190
|
+
|
|
191
|
+
**(Tolgee)** (`tolgee@7.0.0`) :
|
|
192
|
+
|
|
193
|
+
`Tolgee` traite bon nombre des problèmes mentionnés plus haut. Je l'ai trouvé plus difficile à adopter que des outils similaires. Il n'offre pas de sécurité de type (type safety), ce qui rend également plus difficile la détection des clés manquantes à la compilation. J'ai dû wrapper les fonctions de Tolgee avec les miennes pour ajouter la détection des clés manquantes.
|
|
194
|
+
|
|
195
|
+
**(Next Intl)** (`next-intl@4.9.1`) :
|
|
196
|
+
|
|
197
|
+
`next-intl` est l'option la plus à la mode et celle que les agents IA poussent le plus, mais à mon avis à tort. La mise en route est facile. En pratique, l'optimisation pour limiter les fuites est complexe. Combiner chargement dynamique + namespacing + types TypeScript ralentit beaucoup le développement. Le package est également assez lourd (env. 13 Ko pour `NextIntlClientProvider` + `useTranslations`, soit plus de 2 fois `next-intlayer`). **next-intl** bloquait auparavant le rendu statique des pages Next.js. Il fournit un helper nommé `setRequestLocale()`. Cela semble partiellement résolu pour les fichiers centralisés comme `en.json` / `fr.json`, mais le rendu statique casse toujours lorsque le contenu est divisé en namespaces tels que `en/shared.json` / `fr/shared.json` / `es/shared.json`.
|
|
198
|
+
|
|
199
|
+
**(Next I18next)** (`next-i18next@16.0.5`) :
|
|
200
|
+
|
|
201
|
+
`next-i18next` est probablement l'option la plus populaire car elle fut l'une des premières solutions i18n pour les applications JavaScript. Elle dispose de nombreux plugins communautaires. Elle partage les mêmes inconvénients majeurs que `next-intl`. Le package est particulièrement lourd (env. 18 Ko pour `I18nProvider` + `useTranslation`, environ 3 fois `next-intlayer`).
|
|
202
|
+
|
|
203
|
+
Les formats de messages diffèrent également : `next-intl` utilise ICU MessageFormat, tandis qu'i18next utilise son propre format.
|
|
204
|
+
|
|
205
|
+
**(Next International)** (`next-international@1.3.1`) :
|
|
206
|
+
|
|
207
|
+
`next-international` s'attaque également aux problèmes ci-dessus mais ne diffère pas beaucoup de `next-intl` ou `next-i18next`. Il inclut `scopedT()` pour les traductions spécifiques à un namespace, mais son utilisation n'a pratiquement aucun impact sur la taille du bundle.
|
|
208
|
+
|
|
209
|
+
**(Lingui)** (`@lingui/core@5.3.0`) :
|
|
210
|
+
|
|
211
|
+
`Lingui` est souvent vanté. Personnellement, j'ai trouvé le workflow `lingui extract` / `lingui compile` plus complexe que les alternatives, sans avantage clair. J'ai également remarqué des syntaxes inconsistantes qui perturbent les IAs (ex: `t()`, `t''`, `i18n.t()`, `<Trans>`).
|
|
212
|
+
|
|
213
|
+
### 4 — Recommandations
|
|
214
|
+
|
|
215
|
+
**(Next Translate)** (`next-translate@3.1.2`) :
|
|
216
|
+
|
|
217
|
+
`next-translate` est ma recommandation principale si vous aimez une API de style `t()`. C'est élégant via `next-translate-plugin`, chargeant les namespaces via `getStaticProps` avec un loader Webpack / Turbopack. C'est aussi l'option la plus légère ici (env. 2,5 Ko). Pour le découpage en namespaces, la définition par page ou par route dans la config est bien pensée et plus facile à maintenir que les alternatives principales comme **next-intl** ou **next-i18next**. Dans la version `3.1.2`, j'ai noté que le rendu statique ne fonctionnait pas ; Next.js se repliait sur le rendu dynamique.
|
|
218
|
+
|
|
219
|
+
**(Intlayer)** (`next-intlayer@8.7.5`) :
|
|
220
|
+
|
|
221
|
+
Je ne jugerai pas personnellement `next-intlayer` par souci d'objectivité, puisqu'il s'agit de ma propre solution.
|
|
222
|
+
|
|
223
|
+
### Note personnelle
|
|
224
|
+
|
|
225
|
+
Cette note est personnelle et n'affecte pas les résultats du benchmark. Dans le monde de l'i18n, on voit souvent un consensus autour de `const t = useTranslation('xx')` + `<>{t('xx.xx')}</>`.
|
|
226
|
+
|
|
227
|
+
Dans les applications React, injecter une fonction en tant que `ReactNode` est, à mon avis, un anti-pattern. Cela ajoute également une complexité évitable et un surcoût d'exécution JavaScript (même s'il est à peine perceptible).
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
---
|
|
2
|
+
createdAt: 2026-04-20
|
|
3
|
+
updatedAt: 2026-04-21
|
|
4
|
+
title: La meilleure solution i18n pour TanStack Start en 2026 - Rapport de Benchmark
|
|
5
|
+
description: Comparez les bibliothèques d'internationalisation pour TanStack Start comme react-i18next, use-intl et Intlayer. Rapport de performance détaillé sur la taille du bundle, les fuites et la réactivité.
|
|
6
|
+
keywords:
|
|
7
|
+
- benchmark
|
|
8
|
+
- i18n
|
|
9
|
+
- intl
|
|
10
|
+
- tanstack
|
|
11
|
+
- performance
|
|
12
|
+
- intlayer
|
|
13
|
+
slugs:
|
|
14
|
+
- doc
|
|
15
|
+
- benchmark
|
|
16
|
+
- tanstack
|
|
17
|
+
author: Aymeric PINEAU
|
|
18
|
+
applicationTemplate: https://github.com/intlayer-org/benchmark-i18n-tanstack-start-template
|
|
19
|
+
history:
|
|
20
|
+
- version: 8.7.5
|
|
21
|
+
date: 2026-01-06
|
|
22
|
+
changes: "Initialisation du benchmark"
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
# Bibliothèques i18n TanStack Start — Rapport de Benchmark 2026
|
|
26
|
+
|
|
27
|
+
Cette page est un rapport de benchmark pour les solutions i18n sur TanStack Start.
|
|
28
|
+
|
|
29
|
+
## Table des Matières
|
|
30
|
+
|
|
31
|
+
<Toc/>
|
|
32
|
+
|
|
33
|
+
## Benchmark Interactif
|
|
34
|
+
|
|
35
|
+
<I18nBenchmark framework="tanstack" vertical/>
|
|
36
|
+
|
|
37
|
+
## Référence des résultats :
|
|
38
|
+
|
|
39
|
+
<iframe
|
|
40
|
+
src="https://intlayer.org/markdown?url=https%3A%2F%2Fraw.githubusercontent.com%2Fintlayer-org%2Fbenchmark-i18n%2Fmain%2Freport%2Fscripts%2Fsummarize-tanstack.md"
|
|
41
|
+
width="100%"
|
|
42
|
+
height="600px"
|
|
43
|
+
style="border:none;">
|
|
44
|
+
</iframe>
|
|
45
|
+
|
|
46
|
+
> https://intlayer.org/markdown?url=https%3A%2F%2Fraw.githubusercontent.com%2Fintlayer-org%2Fbenchmark-i18n%2Fmain%2Freport%2Fscripts%2Fsummarize-tanstack.md
|
|
47
|
+
|
|
48
|
+
Voir le dépôt complet du benchmark [ici](https://github.com/intlayer-org/benchmark-i18n/tree/main).
|
|
49
|
+
|
|
50
|
+
## Introduction
|
|
51
|
+
|
|
52
|
+
Les solutions d'internationalisation figurent parmi les dépendances les plus lourdes d'une application React. Sur TanStack Start, le risque principal est d'embarquer du contenu inutile : les traductions d'autres pages et d'autres locales dans le bundle d'une seule route.
|
|
53
|
+
|
|
54
|
+
À mesure que votre application grandit, ce problème peut rapidement faire exploser la quantité de JavaScript envoyée au client et ralentir la navigation.
|
|
55
|
+
|
|
56
|
+
En pratique, pour les implémentations les moins optimisées, une page internationalisée peut finir par être plusieurs fois plus lourde que la version sans i18n.
|
|
57
|
+
|
|
58
|
+
L'autre impact concerne l'expérience développeur (DX) : la façon dont vous déclarez le contenu, les types, l'organisation des namespaces, le chargement dynamique et la réactivité lors du changement de langue.
|
|
59
|
+
|
|
60
|
+
## Testez votre application
|
|
61
|
+
|
|
62
|
+
Pour repérer rapidement les problèmes de fuite i18n, j'ai mis en place un scanner gratuit disponible [ici](https://intlayer.org/i18n-seo-scanner).
|
|
63
|
+
|
|
64
|
+
<iframe src="https://intlayer.org/i18n-seo-scanner" width="100%" height="600px" style="border:none;"/>
|
|
65
|
+
|
|
66
|
+
## Le problème
|
|
67
|
+
|
|
68
|
+
Deux leviers sont essentiels pour limiter le coût d'une application multilingue :
|
|
69
|
+
|
|
70
|
+
- Découper le contenu par page / namespace afin de ne pas charger des dictionnaires entiers quand on n'en a pas besoin
|
|
71
|
+
- Charger la bonne locale dynamiquement, uniquement quand nécessaire
|
|
72
|
+
|
|
73
|
+
Comprendre les limitations techniques de ces approches :
|
|
74
|
+
|
|
75
|
+
**Chargement dynamique**
|
|
76
|
+
|
|
77
|
+
Sans chargement dynamique, la plupart des solutions gardent les messages en mémoire dès le premier rendu, ce qui ajoute un surcoût important pour les applications ayant beaucoup de routes et de langues.
|
|
78
|
+
|
|
79
|
+
Avec le chargement dynamique, vous acceptez un compromis : moins de JS initial, mais parfois une requête supplémentaire lors du changement de langue.
|
|
80
|
+
|
|
81
|
+
**Découpage du contenu (Splitting)**
|
|
82
|
+
|
|
83
|
+
Les syntaxes basées sur `const t = useTranslation()` + `t('a.b.c')` sont très pratiques mais encouragent souvent la conservation de gros objets JSON au runtime. Ce modèle rend le tree-shaking difficile à moins que la bibliothèque ne propose une réelle stratégie de découpage par page.
|
|
84
|
+
|
|
85
|
+
## Méthodologie
|
|
86
|
+
|
|
87
|
+
Pour ce benchmark, nous avons comparé les bibliothèques suivantes :
|
|
88
|
+
|
|
89
|
+
- `Base App` (Pas de bibliothèque i18n)
|
|
90
|
+
- `react-intlayer` (v8.7.5-canary.0)
|
|
91
|
+
- `react-i18next` (v17.0.2)
|
|
92
|
+
- `use-intl` (v4.9.1)
|
|
93
|
+
- `@lingui/core` (v5.3.0)
|
|
94
|
+
- `@inlang/paraglide-js` (v2.15.1)
|
|
95
|
+
- `tolgee` (v7.0.0)
|
|
96
|
+
- `react-intl` (v10.1.1)
|
|
97
|
+
- `wuchale` (v0.22.11)
|
|
98
|
+
- `gt-react` (vlatest)
|
|
99
|
+
- `lingo.dev` (v0.133.9)
|
|
100
|
+
|
|
101
|
+
Le framework utilisé est `TanStack Start` avec une application multilingue de **10 pages** et **10 langues**.
|
|
102
|
+
|
|
103
|
+
Nous avons comparé **quatre stratégies de chargement** :
|
|
104
|
+
|
|
105
|
+
| Stratégie | Sans namespaces (global) | Avec namespaces (scoped) |
|
|
106
|
+
| :----------------------- | :------------------------------------------------ | :------------------------------------------------------------------- |
|
|
107
|
+
| **Chargement statique** | **Static** : Tout en mémoire au démarrage. | **Scoped static** : Divisé par namespace ; tout chargé au démarrage. |
|
|
108
|
+
| **Chargement dynamique** | **Dynamic** : Chargement à la demande par locale. | **Scoped dynamic** : Chargement granulaire par namespace et locale. |
|
|
109
|
+
|
|
110
|
+
## Résumé des stratégies
|
|
111
|
+
|
|
112
|
+
- **Static** : Simple ; pas de latence réseau après le chargement initial. Inconvénient : taille de bundle importante.
|
|
113
|
+
- **Dynamic** : Réduit le poids initial (lazy-loading). Idéal lorsque vous avez de nombreuses locales.
|
|
114
|
+
- **Scoped static** : Organise bien le code (séparation logique) sans requêtes réseau supplémentaires complexes.
|
|
115
|
+
- **Scoped dynamic** : Meilleure approche pour le _code splitting_ et la performance. Minimise la mémoire en ne chargeant que ce dont la vue actuelle et la locale active ont besoin.
|
|
116
|
+
|
|
117
|
+
## Résultats détaillés
|
|
118
|
+
|
|
119
|
+
### 1 — Solutions à éviter
|
|
120
|
+
|
|
121
|
+
Certaines solutions, telles que `gt-react` ou `lingo.dev`, sont clairement à fuir. Elles combinent un verrouillage propriétaire avec une pollution de votre base de code. Pire : malgré de nombreuses heures passées à essayer de les implémenter, je n'ai jamais réussi à les faire fonctionner correctement sur TanStack Start (comme pour Next.js avec `gt-next`).
|
|
122
|
+
|
|
123
|
+
Problèmes rencontrés :
|
|
124
|
+
|
|
125
|
+
**(General Translation)** (`gt-react@latest`) :
|
|
126
|
+
|
|
127
|
+
- Pour une application d'environ 110 Ko, `gt-react` peut ajouter plus de 440 Ko supplémentaires (ordre de grandeur observé sur l'implémentation Next.js du même benchmark).
|
|
128
|
+
- `Quota Exceeded, please upgrade your plan` dès le tout premier build avec General Translation.
|
|
129
|
+
- Les traductions ne sont pas rendues ; j'obtiens l'erreur `Error: <T> used on the client-side outside of <GTProvider>`, ce qui semble être un bug de la bibliothèque.
|
|
130
|
+
- Lors de l'implémentation de **gt-tanstack-start-react**, je suis également tombé sur un [problème](https://github.com/generaltranslation/gt/issues/1210#event-24510646961) avec la bibliothèque : `does not provide an export named 'printAST' - @formatjs/icu-messageformat-parser`, ce qui cassait l'application. Après avoir signalé ce problème, le mainteneur l'a corrigé sous 24 heures.
|
|
131
|
+
- Ces bibliothèques utilisent un anti-pattern via la fonction `initializeGT()`, empêchant le bundle d'être tree-shaké proprement.
|
|
132
|
+
|
|
133
|
+
**(Lingo.dev)** (`lingo.dev@0.133.9`) :
|
|
134
|
+
|
|
135
|
+
- Quota AI dépassé (ou dépendance serveur bloquante), rendant le build / la production risqués sans payer.
|
|
136
|
+
- Le compilateur ratait presque 40 % du contenu traduit. J'ai dû réécrire tous les `.map` en blocs de composants plats pour que cela fonctionne.
|
|
137
|
+
- Leur CLI est buggée et réinitialisait le fichier de config sans raison.
|
|
138
|
+
- Au build, il effaçait totalement les JSONs générés quand il y avait du nouveau contenu. Résultat : vous pouviez vous retrouver avec seulement quelques clés effaçant des centaines de clés existantes.
|
|
139
|
+
- J'ai rencontré des problèmes de réactivité avec la bibliothèque sur TanStack Start : au changement de langue, je devais forcer le re-rendu du provider pour que cela fonctionne.
|
|
140
|
+
|
|
141
|
+
### 2 — Solutions expérimentales
|
|
142
|
+
|
|
143
|
+
**(Wuchale)** (`wuchale@0.22.11`) :
|
|
144
|
+
|
|
145
|
+
L'idée derrière `Wuchale` est intéressante mais ce n'est pas encore une solution viable. J'ai rencontré des problèmes de réactivité avec la bibliothèque et j'ai dû forcer le re-rendu du provider pour faire fonctionner l'application sur TanStack Start. La documentation est également assez floue, ce qui rend l'adoption difficile.
|
|
146
|
+
|
|
147
|
+
### 3 — Solutions acceptables
|
|
148
|
+
|
|
149
|
+
**(Paraglide)** (`@inlang/paraglide-js@2.15.1`) :
|
|
150
|
+
|
|
151
|
+
`Paraglide` propose une approche innovante et bien pensée. Pourtant, dans ce benchmark, le tree-shaking dont leur entreprise fait la publicité n'a pas fonctionné pour mon implémentation Next.js ou pour TanStack Start. Le workflow et la DX sont également plus complexes d'autres options. Personnellement, je ne suis pas fan de devoir régénérer des fichiers JS avant chaque push, ce qui crée un risque constant de conflit de fusion pour les développeurs via les PRs.
|
|
152
|
+
|
|
153
|
+
**(Tolgee)** (`tolgee@7.0.0`) :
|
|
154
|
+
|
|
155
|
+
`Tolgee` traite bon nombre des problèmes mentionnés plus haut. Je l'ai trouvé plus difficile à prendre en main que d'autres outils aux approches similaires. Il n'offre pas de sécurité de type, ce qui rend également beaucoup plus difficile la détection des clés manquantes à la compilation. J'ai dû wrapper les APIs de Tolgee avec les miennes pour ajouter la détection des clés manquantes.
|
|
156
|
+
|
|
157
|
+
Sur TanStack Start, j'ai également eu des problèmes de réactivité : au changement de locale, je devais forcer le provider à se re-rendre et souscrire aux événements de changement de locale pour que le chargement dans une autre langue se comporte correctement.
|
|
158
|
+
|
|
159
|
+
**(use-intl)** (`use-intl@4.9.1`) :
|
|
160
|
+
|
|
161
|
+
`use-intl` est la pièce "intl" la plus à la mode dans l'écosystème React (même famille que `next-intl`) et est souvent poussée par les agents IA, mais à mon avis à tort dans un contexte privilégiant la performance. La mise en route est assez simple. En pratique, le processus pour optimiser et limiter les fuites est assez complexe. De même, combiner chargement dynamique + namespacing + types TypeScript ralentit beaucoup le développement.
|
|
162
|
+
|
|
163
|
+
Sur TanStack Start, vous évitez les pièges spécifiques à Next.js (`setRequestLocale`, rendu statique), mais le problème de fond est le même : sans une discipline stricte, le bundle transporte rapidement trop de messages et la maintenance des namespaces par route devient pénible.
|
|
164
|
+
|
|
165
|
+
**(react-i18next)** (`react-i18next@17.0.2`) :
|
|
166
|
+
|
|
167
|
+
`react-i18next` est probablement l'option la plus populaire car elle fut l'une des premières à servir les besoins i18n des applications JS. Elle dispose également d'un large éventail de plugins communautaires pour des problèmes spécifiques.
|
|
168
|
+
|
|
169
|
+
Pourtant, elle partage les mêmes inconvénients majeurs que les stacks basées sur `t('a.b.c')` : les optimisations sont possibles mais très gourmandes en temps, et les gros projets risquent de mauvaises pratiques (namespaces + chargement dynamique + types).
|
|
170
|
+
|
|
171
|
+
Les formats de messages divergent également : `use-intl` utilise ICU MessageFormat, tandis qu'i18next utilise son propre format — ce qui complique l'outillage ou les migrations si vous les mélangez.
|
|
172
|
+
|
|
173
|
+
**(Lingui)** (`@lingui/core@5.3.0`) :
|
|
174
|
+
|
|
175
|
+
`Lingui` est souvent loué. Personnellement, j'ai trouvé le workflow autour de `lingui extract` / `lingui compile` plus complexe que d'autres approches, sans avantage clair dans ce benchmark TanStack Start. J'ai également remarqué des syntaxes inconsistantes qui perturbent les IAs (ex: `t()`, `t''`, `i18n.t()`, `<Trans>`).
|
|
176
|
+
|
|
177
|
+
**(react-intl)** (`react-intl@10.1.1`) :
|
|
178
|
+
|
|
179
|
+
`react-intl` est une implémentation performante de l'équipe Format.js. La DX reste verbeuse : `const intl = useIntl()` + `intl.formatMessage({ id: "xx.xx" })` ajoute de la complexité, du travail JavaScript supplémentaire et lie l'instance globale i18n à de nombreux nœuds dans l'arbre React.
|
|
180
|
+
|
|
181
|
+
### 4 — Recommandations
|
|
182
|
+
|
|
183
|
+
Ce benchmark TanStack Start n'a pas d'équivalent direct à `next-translate` (plugin Next.js + `getStaticProps`). Pour les équipes qui veulent vraiment une API `t()` avec un écosystème mature, `react-i18next` et `use-intl` restent des choix "raisonnables", mais attendez-vous à investir beaucoup de temps dans l'optimisation pour éviter les fuites.
|
|
184
|
+
|
|
185
|
+
**(Intlayer)** (`react-intlayer@8.7.5-canary.0`) :
|
|
186
|
+
|
|
187
|
+
Je ne jugerai pas personnellement `react-intlayer` par souci d'objectivité, puisqu'il s'agit de ma propre solution.
|
|
188
|
+
|
|
189
|
+
### Note personnelle
|
|
190
|
+
|
|
191
|
+
Cette note est personnelle et n'affecte pas les résultats du benchmark. Pourtant, dans le monde de l'i18n, on voit souvent un consensus autour d'un pattern comme `const t = useTranslation('xx')` + `<>{t('xx.xx')}</>` pour le contenu traduit.
|
|
192
|
+
|
|
193
|
+
Dans les applications React, injecter une fonction en tant que `ReactNode` est, à mon avis, un anti-pattern. Cela ajoute également une complexité évitable et un surcoût d'exécution JavaScript (même s'il est à peine perceptible).
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
createdAt: 2026-04-20
|
|
3
|
+
updatedAt: 2026-04-20
|
|
4
|
+
title: i18n लाइब्रेरी बेंचमार्क
|
|
5
|
+
description: जानें कि परफॉरमेंस और बंडल आकार के मामले में Intlayer अन्य i18n लाइब्रेरीज़ की तुलना में कैसा है।
|
|
6
|
+
keywords:
|
|
7
|
+
- benchmark
|
|
8
|
+
- i18n
|
|
9
|
+
- intl
|
|
10
|
+
- nextjs
|
|
11
|
+
- tanstack
|
|
12
|
+
- intlayer
|
|
13
|
+
slugs:
|
|
14
|
+
- doc
|
|
15
|
+
- benchmark
|
|
16
|
+
history:
|
|
17
|
+
- version: 8.7.5
|
|
18
|
+
date: 2026-01-06
|
|
19
|
+
changes: "बेंचमार्क इनिशियलाइज़ेशन"
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
# Benchmark - रिपोर्ट
|
|
23
|
+
|
|
24
|
+
Benchmark Bloom एक परफॉरमेंस बेंचमार्किंग सुइट है जो कई React फ़्रेमवर्क और लोडिंग रणनीतियों में i18n (अंतर्राष्ट्रीयकरण) लाइब्रेरीज़ के वास्तविक प्रभाव को मापता है।
|
|
25
|
+
|
|
26
|
+
प्रत्येक फ़्रेमवर्क के लिए विस्तृत रिपोर्ट और तकनीकी दस्तावेज़ नीचे दिए गए हैं:
|
|
27
|
+
|
|
28
|
+
- [**Next.js बेंचमार्क रिपोर्ट**](https://github.com/aymericzip/intlayer/blob/main/docs/docs/hi/benchmark/nextjs.md)
|
|
29
|
+
- [**TanStack Start बेंचमार्क रिपोर्ट**](https://github.com/aymericzip/intlayer/blob/main/docs/docs/hi/benchmark/tanstack.md)
|