Posez votre question et obtenez un résumé du document en referencant cette page et le Provider AI de votre choix
Le contenu de cette page a été traduit à l'aide d'une IA.
Voir la dernière version du contenu original en anglaisSi vous avez une idée d’amélioration pour améliorer cette documentation, n’hésitez pas à contribuer en submitant une pull request sur GitHub.
Lien GitHub de la documentationCopier le Markdown du doc dans le presse-papiers
i18next VS Intlayer | Benchmark d'internationalisation (i18n) React & Next.js
i18next est le framework d'i18n le plus utilisé de l'écosystème JavaScript. À travers react-i18next et next-i18next, il alimente une grande partie des applications React et Next.js. Intlayer est une alternative basée sur un compilateur, découpée par composant.
Cet article les compare sur des mesures concrètes plutôt que sur des listes de fonctionnalités. Les chiffres proviennent de Benchmark Bloom, une suite open source qui compile la même application avec chaque bibliothèque et enregistre ce que le navigateur télécharge réellement.
tl;dr:i18nextest le runtime le plus lourd du benchmark: +77 KB gzip par page sur Next.js dans la configuration naïve, +22 KB après l'optimisation complète par namespaces + lazy loading. Intlayer n'ajoute que +0.3 KB. Chaque configuration d'i18next en dehors de celle entièrement compartimentée (scoped) envoie ~90% des chaînes des pages distantes; Intlayer n'envoie 0% par défaut. Changer de langue avec un backend chargé à la demande prend 123 à 185 ms avecreact-i18nextcontre 3 à 4 ms avec Intlayer. L'adaptateur@intlayer/next-i18nextconserve l'API d'i18next et atteint 150.7 KB par page contre 218.5 KB pour l'original.
En résumé
- i18next / react-i18next / next-i18next - Mature, riche en plugins, agnostique du framework. Namespaces, détecteurs de langue, backends, ICU via plugin,
<Trans>pour le contenu enrichi. Le contenu est centralisé danslocales/{lng}/{ns}.json. Puissant, mais chaque optimisation (découpage des namespaces, chargement par page, sécurité des types) représente une configuration que vous devez maintenir. - Intlayer - Modèle centré sur les composants. Les dictionnaires
.content.tsse trouvent à côté du composant qu'ils desservent, un compilateur au build effectue le tree-shaking et les charge à la demande par composant et par locale, des types TypeScript stricts sont générés à partir de votre contenu, et les traductions manquantes provoquent des erreurs au build. Fournit middleware, helpers SEO, un Éditeur Visuel / CMS et la traduction assistée par IA.
Ouvrir le tableau dans une fenêtre modale pour voir tout le contenu clairement
Les badges se mettent à jour automatiquement. Les données évoluent au fil du temps.
Comparaison des fonctionnalités
Ouvrir le tableau dans une fenêtre modale pour voir tout le contenu clairement
| Fonctionnalité | Intlayer (react-intlayer / next-intlayer) | i18next (react-i18next / next-i18next) |
|---|---|---|
| Traductions proches des composants | ✅ Oui, .content.ts colocalisé avec chaque composant | ❌ Non, centralisé dans locales/{lng}/{ns}.json |
| Intégration TypeScript | ✅ Types stricts générés automatiquement à partir du contenu | ⚠️ Basique; clés strictes via extension CustomTypeOptions et typage ressources |
| Détection des traductions manquantes | ✅ Erreur TypeScript + erreur/avertissement au build | ⚠️ Fallback au runtime (saveMissing, écho de clé) |
| Contenu riche (JSX / Markdown / composants) | ✅ Support direct | ⚠️ <Trans> avec placeholders indexés |
| Support ICU | ⚠️ En cours | ⚠️ Via plugin (i18next-icu) |
| Pluralisation | ✅ Motifs basés sur les énumérations | ✅ Suffixes _one / _other (Intl.PluralRules) |
| Formatage (dates, nombres, devises) | ✅ useNumber, useDate, ... (Intl sous le capot) | ⚠️ Formateurs d'interpolation ou Intl.* manuel |
| Routage localisé et middleware | ✅ Proxy/middleware intégré, getMultilingualUrls | ⚠️ Non natif; middleware personnalisé ou tiers |
| Helpers SEO (hreflang, sitemap, robots) | ✅ Helpers intégrés | ❌ Manuel |
| Composants serveur synchrones | ✅ useIntlayer de next-intlayer/server utilisable dans tout composant serveur | ⚠️ getFixedT sur la page, puis t transmis en props |
| Tree-shaking (n'embarque que le contenu utile) | ✅ Par composant, par locale, automatisé par le compilateur | ⚠️ Manuel: namespaces + liste ns par page + backend |
| Lazy loading | ✅ importMode: 'dynamic' (une ligne de config) | ✅ Via plugins backend (i18next-resources-to-backend, i18next-http-backend) |
| Purger le contenu inutilisé | ✅ Dictionnaires orphelins supprimés au build | ❌ Non intégré |
| Test des traductions manquantes (CLI / CI) | ✅ npx intlayer content test | ⚠️ i18next-parser / outil tiers |
| Traduction assistée par IA | ✅ Intégrée, utilise vos propres clés d'API | ❌ Non (Locize est un service tiers payant) |
| Éditeur Visuel / CMS | ✅ Éditeur Visuel gratuit + CMS optionnel | ❌ Non (Locize / plateformes externes) |
| Serveur MCP et compétences d'agent (Skills) | ✅ Oui | ❌ Non |
| Écosystème / communauté | ⚠️ Plus récent mais en forte croissance | ✅ Le plus étendu et le plus mature |
Le benchmark
Ce qui a été mesuré
La suite Benchmark Bloom construit la même application avec chaque bibliothèque: 10 pages (accueil, à propos, blog, carrières, contact, FAQ, tarifs, produits, paramètres, équipe), 10 locales (en, fr, es, de, it, pt, zh, ja, ko, ru), des composants et un contenu identiques. Les pages sont mesurées en en et en fr. Chaque bibliothèque est testée sous un maximum de quatre stratégies de chargement, de la configuration naïve à l'optimale:
Ouvrir le tableau dans une fenêtre modale pour voir tout le contenu clairement
| Stratégie | Description | Qui fait cela |
|---|---|---|
| static | Toutes les locales et pages regroupées (resources inlinées dans init()) | Prototypes rapides, code généré par IA |
| dynamic | Seule la locale active est chargée via un backend, mais tous les namespaces à la fois | La majorité des projets |
| scoped-static | Un namespace par route, tous chargés en amont | Rare |
| scoped-dynamic | Un namespace par route + chargement dynamique via backend. Seulement la page et locale active | Applications avec un budget de perf strict |
Intlayer n'a pas de variante "scoped": le compilateur isole le contenu par composant automatiquement, ses lignes static et dynamic sont donc déjà découpées.
Pour chaque build, la suite enregistre:
- Lib size: taille gzip d'un composant vide qui n'importe que la bibliothèque i18n. Le coût fixe du runtime.
- Page JS: JavaScript gzip téléchargé par page, en moyenne sur l'ensemble des pages et locales.
- Locale leak %: part des chaînes traduites dans le JS téléchargé appartenant à une locale que l'utilisateur ne consulte pas (évalué sur
enetfr, donc 50% signifie "l'autre locale mesurée est entièrement présente"; avec 10 locales compilées, le gaspillage réel est bien supérieur). - Page leak %: part des chaînes traduites dans le JS téléchargé appartenant à une page sur laquelle l'utilisateur n'est pas.
- Component avg: taille gzip moyenne de chaque composant compilé isolément. Indique le poids du runtime i18n injecté dans chaque composant.
- E2E reactivity: temps réel entre la sélection d'une nouvelle locale et la mise à jour de
html[lang]dans le DOM (Playwright, 5 itérations). - Hydration: durée de la phase d'hydratation de React.
Les données ci-dessous proviennent de l'exécution du 2026-09-12 avecnext-i18next16.3.0,react-i18next17.0.13 etintlayer9.5.1. L'application de test est volontairement modeste (quelques dizaines de chaînes par locale), les pourcentages de fuite décrivent donc un comportement type: ils augmentent à mesure que votre contenu grandit tandis que le coût du runtime reste fixe.
Résultats sur Next.js (next-i18next)
Ouvrir le tableau dans une fenêtre modale pour voir tout le contenu clairement
| Bibliothèque | Stratégie | Lib size (gz) | Page JS avg (gz) | Locale leak | Page leak | Component avg (gz) | Réactivité E2E | Hydratation |
|---|---|---|---|---|---|---|---|---|
| base (sans i18n) | - | 0.0 KB | 141.0 KB | 0.0% | 0.0% | 0.9 KB | 13.4 ms | 11.8 ms |
next-i18next | static | 19.7 KB | 218.5 KB | 0.0% | 89.8% | 78.5 KB | 16.4 ms | 15.6 ms |
next-i18next | dynamic | 19.7 KB | 169.5 KB | 50.0% | 89.8% | 26.1 KB | 15.4 ms | 27.7 ms |
next-i18next | scoped-static | 19.7 KB | 220.1 KB | 0.0% | 89.8% | 78.9 KB | 16.4 ms | 14.7 ms |
next-i18next | scoped-dynamic | 19.7 KB | 163.4 KB | 0.0% | 0.0% | 27.1 KB | 15.9 ms | 15.1 ms |
next-intlayer | static | 5.5 KB | 141.3 KB | 0.0% | 0.0% | 8.5 KB | 15.5 ms | 16.9 ms |
next-intlayer | dynamic | 5.5 KB | 141.3 KB | 0.0% | 0.0% | 6.9 KB | 15.3 ms | 15.9 ms |
@intlayer/next-i18next (compat) | static | 9.4 KB | 150.7 KB | 0.0% | 0.0% | 9.7 KB | 10.7 ms | 11.3 ms |
@intlayer/next-i18next (compat) | dynamic | 9.4 KB | 150.7 KB | 0.0% | 0.0% | 9.7 KB | 11.9 ms | 10.6 ms |
Comment interpréter ces données
- Coût du runtime. Le cœur d'i18next plus
react-i18nextreprésente le runtime le plus lourd: 19.7 KB gzip pour un composant vide, contre 5.5 KB pournext-intlayer. - La configuration naïve coûte cher. Inliner
resourcesdansinit()génère 218.5 KB par page, soit +77.5 KB par rapport à l'application de base. Chaque page embarque chaque namespace. - Optimiser demande beaucoup d'efforts. Passer à un backend (
dynamic) fait gagner 49 KB mais laisse fuiter 90% des chaînes des pages distantes et, dans cette configuration, la moitié des chaînes appartiennent à la mauvaise langue. Ajouter un découpage par namespace sur chaque route (scoped-dynamic) permet enfin d'atteindre 0% de fuite à 163.4 KB, ce qui reste +22.4 KB par page de plus qu'Intlayer à 141.3 KB, qui n'a nécessité aucune configuration spécifique. - Taille des composants. Un composant utilisant
useTranslation()pèse entre 26 et 79 KB selon la configuration; le même composant avecuseIntlayer()ne pèse que 6.9 KB. - L'hydratation monte à 27.7 ms dans la configuration
dynamic: l'instance i18next s'initialise et résout son backend côté client avant que React ne puisse hydrater la page.
Résultats sur TanStack Start (react-i18next)
La même application de test sur TanStack Start avec react-i18next pur, ce qui isole les dépendances propres à Next.js.
Ouvrir le tableau dans une fenêtre modale pour voir tout le contenu clairement
| Bibliothèque | Stratégie | Lib size (gz) | Page JS avg (gz) | Locale leak | Page leak | Component avg (gz) | Réactivité E2E | Hydratation |
|---|---|---|---|---|---|---|---|---|
| base (pas i18n) | - | 0.0 KB | 111.0 KB | 0.0% | 0.0% | 0.7 KB | 8.1 ms | 21.6 ms |
react-i18next | static | 18.4 KB | 180.3 KB | 50.0% | 89.8% | 24.3 KB | 12.9 ms | 85.1 ms |
react-i18next | dynamic | 18.4 KB | 136.4 KB | 23.1% | 89.8% | 24.8 KB | 123.1 ms | 32.9 ms |
react-i18next | scoped-static | 18.4 KB | 184.2 KB | 50.7% | 89.8% | 25.3 KB | 185.1 ms | 25.2 ms |
react-i18next | scoped-dynamic | 18.4 KB | 127.2 KB | 0.0% | 0.0% | 26.7 KB | 17.6 ms | 11.3 ms |
intlayer | static | 5.0 KB | 125.8 KB | 50.0% | 0.0% | 8.1 KB | 3.2 ms | 11.5 ms |
intlayer | dynamic | 5.0 KB | 118.6 KB | 0.0% | 0.0% | 6.3 KB | 3.6 ms | 14.1 ms |
Comment interpréter ces données
- L'application
react-i18nextnaïve envoie +69 KB par page de plus que l'application de base, et l'hydratation prend 85 ms (4 fois la base) parce que l'arbre complet des ressources est parsé et enregistré côté client avant le premier rendu. - Le changement de langue met en lumière la latence du lazy loading. Lorsque les ressources sont chargées à la demande via un backend, changer de langue nécessite un aller-retour réseau avant que
html[lang]ne se mette à jour: 123 ms endynamic, 185 ms enscoped-static. Intlayer met à jour le DOM en 3 à 4 ms dans les deux cas: le changement de langue est immédiat et ne dépend pas d'une requête réseau. - La configuration pleinement optimisée
scoped-dynamicatteint 0% de fuite à 127.2 KB, restant +8.6 KB au-dessus de la lignedynamicd'Intlayer, et a nécessité une correspondance routes-namespaces, un backend de ressources et une frontière Suspense par route pour y parvenir. - La ligne
staticd'Intlayer présente déjà 0% de fuite de page car seuls les dictionnaires importés par les composants de la page sont inclus dans le bundle. ActiverimportMode: 'dynamic'supprime également la fuite de locale. - Taille des composants: 24 à 27 KB par composant avec
react-i18nextcontre 6 à 8 KB avec Intlayer.useTranslation()lie chaque composant à l'instance globale d'i18next.
Pourquoi un tel écart? Instance globale vs dictionnaires compilés
i18next a été pensé en 2012 comme un runtime: une instance globale conserve un dépôt de ressources, des plugins l'enrichissent, et t() résout les clés au rendu. C'est ce qui le rend si flexible (n'importe quel framework, backend ou format) et également ce qui le rend lourd:
Copier le code dans le presse-papiers
L'instance ne peut pas anticiper les clés qu'un composant demandera, elle conserve donc tous les namespaces que vous lui demandez de charger. Optimiser signifie que vous devez découper les catalogues en namespaces, vous devez lister les namespaces nécessaires à chaque page, et vous devez maintenir cette liste à jour quand les composants se déplacent. Comme le soulignent les notes du benchmark: "maintenir la sécurité des types et savoir exactement quel namespace inclure sur quelle page est un cauchemar".
Intlayer élimine l'instance. Le contenu est déclaré à côté du composant, et le compilateur résout le graphe de dépendances au build:
Copier le code dans le presse-papiers
@intlayer/swc / @intlayer/babel identifie quel composant importe quel dictionnaire, n'embarque que ceux-ci, uniquement pour la locale active, et exclut ceux qui ne sont pas référencés. Le modèle "scoped-dynamic" devient le résultat direct du build plutôt qu'une discipline manuelle imposée à l'équipe.
Pour obtenir les chiffres de la lignedynamic, définissezdictionary.importMode: 'dynamic'dansintlayer.config.ts. Consultez la documentation sur l'optimisation de bundle.
Expérience développeur
Configuration
next-i18next (App Router)
Copier le code dans le presse-papiers
Il faut y ajouter un I18nProvider côté client qui recrée l'instance avec les mêmes options, un generateStaticParams, et une liste de namespaces sur chaque page.
Intlayer
Copier le code dans le presse-papiers
Copier le code dans le presse-papiers
Composant client
react-i18next
Copier le code dans le presse-papiers
Copier le code dans le presse-papiers
La page affichant ce composant doit charger le namespaceabout, ett("counter.label")reste une simple chaîne de caractères non typée à moins d'étendreCustomTypeOptions.
Intlayer
Copier le code dans le presse-papiers
Copier le code dans le presse-papiers
label et increment sont strictement typés; une faute de frappe déclenche une erreur TypeScript, et une valeur française manquante génère une erreur au build.
Composant serveur synchrone
next-i18next
Copier le code dans le presse-papiers
La page appelle i18n.getFixedT(locale, "about") et fait descendre t et locale en tant que props.
Intlayer
Copier le code dans le presse-papiers
Gardez l'API d'i18next, profitez des performances d'Intlayer
Il n'est pas nécessaire de réécrire vos composants pour bénéficier des résultats du benchmark ci-dessus. @intlayer/i18next, @intlayer/react-i18next et @intlayer/next-i18next sont des adaptateurs prêts à l'emploi: useTranslation, t(), <Trans>, {{interpolation}}, pluriels _one / _other, suffixes de contexte et returnObjects continuent de fonctionner, alimentés par les dictionnaires Intlayer compilés.
Copier le code dans le presse-papiers
Copier le code dans le presse-papiers
Dans le benchmark, la version adaptée de la même application Next.js est passée de 218.5 KB à 150.7 KB par page, de 78.5 KB à 9.7 KB par composant, de ~90% de fuite de page à 0%, et d'une hydratation de 15.6 ms à 11.3 ms, sans modifier le code de l'application. Vos fichiers existants locales/{lng}/{ns}.json peuvent rester la source de vérité grâce au plugin de synchronisation JSON.
Consultez les guides de migration: i18next, react-i18next, next-i18next.
Quand choisir quelle solution?
- Choisissez i18next si vous avez besoin de son écosystème de plugins (détecteurs, backends, ICU, Locize), si vous traduisez aussi en dehors de React (services Node, vanilla JS, autres frameworks), si votre équipe le maîtrise déjà, ou si une plateforme de traduction externe exige
locales/{lng}/{ns}.json. Prévoyez le temps de découper vos catalogues en namespaces, de configurer un backend et de maintenir la table routes-namespaces si la performance compte. - Choisissez Intlayer si vous voulez du contenu découpé par composant, un typage TypeScript strict, des erreurs au build en cas de clé manquante, du tree-shaking et lazy loading sans effort, un changement de langue instantané, des composants serveur synchrones et des outils éditoriaux intégrés (Éditeur Visuel, CMS, traduction IA, serveur MCP). Particulièrement pertinent pour les codebases modulaires et les design systems.
- Choisissez les adaptateurs
@intlayer/*-i18nextsi vous utilisez déjà i18next et souhaitez obtenir les gains de bundle et de réactivité sans refactoriser vos composants.
Comparaisons associées
- next-intl vs Intlayer (même benchmark)
- Lingui vs Intlayer (même benchmark)
- vue-i18n vs Intlayer benchmark (même benchmark)
- next-i18next vs next-intl vs Intlayer
- react-i18next vs react-intl vs Intlayer
- i18next est-il dépassé?
Étoiles GitHub
Les étoiles GitHub reflètent la popularité d'un projet, la confiance de la communauté et sa pérennité. Bien qu'elles ne mesurent pas directement la qualité technique, elles montrent combien de développeurs trouvent le projet utile, suivent ses avancées et sont susceptibles de l'adopter.
Conclusion
i18next a gagné sa place: il s'exécute partout, propose un plugin pour chaque besoin et bénéficie de plus d'une décennie de maintenance. Le benchmark met toutefois en lumière le coût d'une architecture centrée sur le runtime. La configuration déployée par la majorité des équipes ajoute +70 à 77 KB gzip par page, laisse fuiter ~90% du contenu des autres pages, et un changement de langue avec chargement dynamique prend plus de 100 ms. Atteindre 0% de fuite reste envisageable, mais impose un backend, un namespace par route et une table de correspondance maintenue manuellement, tout en restant +9 à 22 KB plus lourd qu'Intlayer.
Intlayer délègue ce travail au compilateur. Dictionnaires par composant, lazy loading par locale et purge du contenu inutile deviennent des artéfacts de build plutôt que des conventions complexes. Sur la même application: +0.3 KB par page, 0% de fuite, des composants 3 à 10 fois plus légers et un basculement de langue en 3 à 4 ms.
Toutes les données brutes, applications de test et scripts sont disponibles dans le dépôt Benchmark Bloom. Testez-le par vous-même.
Consultez la documentation 'Pourquoi Intlayer?' pour en savoir plus.
Commentaires
Aucun commentaire pour le moment. Soyez le premier à partager vos pensées.
