Auteur:
    Création:2026-09-13Dernière mise à jour:2026-09-27

    Lingui VS Intlayer: Benchmark d'Internationalization (i18n) React & Next.js

    Lingui et Intlayer sont les deux bibliothèques de ce benchmark qui s'appuient sur un compilateur plutôt que sur un runtime pur. Lingui extrait les messages des macros au moment de la compilation et compile les catalogues par locale. Intlayer compile les dictionnaires par composant et les tree-shake par locale. Sur le papier, ils devraient être proches. Les chiffres montrent où ils divergent.

    Les données proviennent de Benchmark Bloom, une suite open-source qui construit la même application avec chaque bibliothèque et enregistre ce que le navigateur télécharge et exécute réellement.

    tl;dr: Lingui est le plus proche d'Intlayer en termes de JavaScript brut par page : 115-120 KB vs 118.6 KB sur TanStack Start une fois le chargement différé configuré, 148.6 KB vs 141.3 KB sur Next.js. L'écart s'élargit ailleurs : un composant Lingui compilé isolément pèse 58-153 KB contre 6-8 KB pour Intlayer, l'hydratation prend 28-34 ms contre 11-14 ms, le fallback pour la locale source fuit 3-15% des chaînes de caractères en dans les pages fr dans chaque setup optimisé, et atteindre ce setup optimisé signifie extraire, compiler et sélectionner manuellement les catalogs par route. Intlayer y arrive sans aucune configuration.

    En résumé

    • Lingui - Basé sur des macros ( t`...` , <Trans>, msg), ICU MessageFormat, catalogues .po / JSON, workflow lingui extract + lingui compile. Compile les IDs de message en hashes courts, supporte le chargement dynamique de catalogues par locale. Bien établi, framework-agnostique, forte histoire de tooling pour traducteurs autour de .po.
    • Intlayer - Modèle de contenu centré sur les composants. Les dictionnaires .content.ts se situent à côté du composant qu'ils servent, un compilateur au moment du build tree-shake et lazy-load par composant et par locale, les types TypeScript stricts sont générés à partir de votre contenu, et les traductions manquantes échouent au moment du build. Livré avec middleware, helpers SEO, un Éditeur Visuel / CMS et traduction assistée par IA.
    BibliothèqueÉtoiles GitHubCommits TotauxDernier CommitPremière VersionVersion NPMTéléchargements NPM
    aymericzip/intlayerGitHub Repo starsGitHub commit activityLast CommitAvril 2024npmnpm downloads
    lingui/js-linguiGitHub Repo starsGitHub commit activityLast CommitDec 2016npmnpm downloads
    Les badges se mettent à jour automatiquement. Les snapshots varieront au fil du temps.

    Comparaison des fonctionnalités côte à côte

    FonctionnalitéIntlayer (react-intlayer / next-intlayer)Lingui (@lingui/core / @lingui/react)
    Traductions près des composants✅ Oui, .content.ts colocalisé avec chaque composant⚠️ Chaînes de caractères sources inline dans JSX via macros; traductions dans les catalogues .po centralisés
    Intégration TypeScript✅ Types stricts auto-générés à partir du contenu⚠️ Les macros sont typées ; les IDs de message ne le sont pas, les entrées de catalogue manquantes ne sont pas détectées
    Détection des traductions manquantes✅ Erreur TypeScript + erreur/avertissement au moment de la compilation⚠️ lingui extract rapporte les statistiques ; le fallback à l'exécution utilise la chaîne source
    Contenu riche (JSX / Markdown / composants)✅ Support direct✅ <Trans> avec composants imbriqués
    Support ICU⚠️ WIP✅ Oui (macros plural, select, selectOrdinal)
    Formatage (dates, nombres, devises)✅ useNumber, useDate, ... (Intl sous le capot)✅ i18n.date(), i18n.number()
    Routage localisé et middleware✅ Proxy/middleware intégré, getMultilingualUrls❌ Pas dans le cœur
    SEO helpers (hreflang, sitemap, robots)✅ Built-in helpers❌ Manual
    Synchronous server components✅ useIntlayer from next-intlayer/server works in any child server component⚠️ Needs an I18n instance per request, passed down or set via setI18n
    Tree-shaking (ship only used content)✅ Per component, per locale, automated by the compiler⚠️ Per locale via lingui compile; per route needs manual catalog splitting
    Chargement lazy✅ importMode: 'dynamic' (une ligne de config)⚠️ import() manuel des catalogues compilés + i18n.load() / i18n.activate()
    Purger le contenu inutilisé✅ Les dictionnaires morts sont supprimés au moment du build✅ lingui extract --clean supprime les messages obsolètes
    Tester les traductions manquantes (CLI/CI)✅ npx intlayer content test⚠️ Statistiques de lingui extract (pas de code de sortie d'erreur par défaut)
    Build pipeline✅ Un plugin (@intlayer/swc / @intlayer/babel / vite-intlayer)⚠️ Macro plugin (Babel ou SWC) + extract + compile étapes
    AI-powered translation✅ Intégré, utilise vos propres clés de provider❌ Non
    Visual Editor / CMS✅ Visual Editor gratuit + CMS optionnel❌ Non (.po fonctionne avec un TMS externe)
    Serveur MCP & Compétences Agent✅ Oui❌ Non
    Écosystème / communauté⚠️ Plus petit mais en croissance rapide✅ Établi, indépendant du framework

    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, tarification, produits, paramètres, équipe), 10 locales (en, fr, es, de, it, pt, zh, ja, ko, ru), composants identiques et contenu identique. Les pages sont mesurées en en et fr. Chaque bibliothèque est implémentée dans jusqu'à quatre stratégies de chargement, de la configuration naïve à la configuration optimale :

    StratégieDescriptionQui fait cela
    staticTous les catalogues compilés des locales importés et chargés à l'avancePrototypes rapides, code généré par IA
    dynamicSeul le catalogue de la locale active est import()é, mais il contient toutes les pagesLa plupart des projets
    scoped-staticUn catalogue par route, tous bundlés à l'avanceRare
    scoped-dynamicUn catalogue par route + lazy import(). Seulement la page actuelle, la locale actuelleApplications avec un budget de performance strict

    Intlayer n'a pas de variante "scoped" : le compilateur scope le contenu par composant automatiquement, donc ses lignes static et dynamic sont déjà scopées.

    Pour chaque build, la suite enregistre :

    • Lib size: taille gzip d'un composant vide qui importe uniquement la bibliothèque i18n. Le coût fixe du runtime.
    • Page JS: JavaScript gzip téléchargé par page, moyenné sur toutes les pages et locales.
    • Locale leak %: part des chaînes traduites trouvées dans le JS téléchargé qui appartiennent à une locale que l'utilisateur n'est pas en train de consulter (empreinte digitale sur en et fr, donc 50% signifie "l'autre locale mesurée est entièrement présente"; avec 10 locales bundlées, le gaspillage réel est plus élevé).
    • Page leak %: part des chaînes traduites trouvées dans le JS téléchargé qui appartiennent à une page sur laquelle l'utilisateur n'est pas.
    • Component avg: taille gzip moyenne de chaque composant compilé isolément. Montre combien de runtime i18n et de catalog un seul composant entraîne.
    • E2E réactivité : temps écoulé entre la sélection d'une nouvelle locale et la mise à jour de html[lang] dans le DOM (Playwright, 5 itérations).
    • Hydratation : durée de la phase d'hydratation de React.
    Les chiffres ci-dessous proviennent de l'exécution datée du 12-09-2026 avec @lingui/react 6.6.0 et intlayer 9.5.1. L'application de test est volontairement petite (quelques dizaines de chaînes par locale), donc les pourcentages de fuite décrivent un motif : ils augmentent avec votre contenu tandis que le coût d'exécution reste fixe.

    Résultats sur Next.js

    Sélectionnez les métriques et les bibliothèques qui vous intéressent :

    Métrique

    Chargement JSON dynamique

    Charge les traductions à la volée

    JSON scopé (espaces de noms)

    Espaces de noms de traduction par page

    Quelle est cette métrique ?

    La taille totale compressée en gzip du bundle de la bibliothèque d’internationalisation. Elle n’inclut que le fournisseur et la logique de récupération de contenu après tree-shaking et minification.

    Pourquoi est-ce important ?

    Une taille de bibliothèque plus petite réduit la charge utile JavaScript initiale, ce qui accélère le téléchargement et le temps d’exécution sur le client.

    Voir comme

    LibraryStrategyLib size (gz)Page JS avg (gz)Locale leakPage leakComponent avg (gz)E2E reactivityHydration
    base (sans i18n)-0.0 KB141.0 KB0.0%0.0%0.9 KB13.4 ms11.8 ms
    Linguistatic11.9 KB207.4 KB50.0%90.0%73.3 KB15.3 ms15.2 ms
    Linguidynamic11.9 KB145.4 KB2.8%89.9%19.9 KB15.7 ms12.7 ms
    Linguiscoped-static11.9 KB148.2 KB2.7%89.1%20.4 KB15.1 ms13.1 ms
    Linguiscoped-dynamic11.9 KB148.6 KB14.8%0.0%152.6 KB16.1 ms14.8 ms
    next-intlayerstatic5.5 KB141.3 KB0.0%0.0%8.5 KB15.5 ms16.9 ms
    next-intlayerdynamic5.5 KB141.3 KB0.0%0.0%6.9 KB15.3 ms15.9 ms

    Comment le lire

    • Coût d'exécution. Un composant vide coûte 11.9 KB gzip avec Lingui, 5.5 KB avec Intlayer. Sur la page complète, la meilleure configuration de Lingui se situe à +7.3 KB par rapport à Intlayer (148.6 vs 141.3 KB) ; Intlayer se situe à +0.3 KB par rapport à l'app de base.
    • La configuration naïve est coûteuse. Charger chaque catalog compilé en avant donne 207.4 KB par page, +66 KB par rapport à l'app de base. La moitié des chaînes empreintes appartiennent à la mauvaise locale, 90% à la mauvaise page.
    • Le chargement dynamique corrige la locale, pas la page. Avec un catalog par locale, la fuite de page reste à ~90% : l'intégralité du catalog fr se charge sur chaque page française. Atteindre 0% de fuite de page requiert la configuration scoped-dynamic : un catalog par route, extrait et compilé séparément, sélectionné manuellement dans chaque page.
    • La source-locale fallback fuit. Même dans les configurations optimisées, 3-15% des chaînes en sont incluses dans les pages fr. Les macros Lingui conservent le message source disponible comme fallback, ce qui se retrouve dans le bundle à côté de la traduction. Intlayer résout les fallbacks au moment de la compilation et ne livre que la locale active.
    • La taille du composant explose en scoped-dynamic. Chaque composant compilé isolément fait en moyenne 152.6 KB, car le catalog de chaque route est accessible depuis le composant qui l'importe. Le même composant avec useIntlayer() fait en moyenne 6.9 KB.
    Tableau complet, chaque bibliothèque et chaque stratégie, dans le rapport de benchmark Next.js.

    Résultats sur TanStack Start

    LibraryStrategyLib size (gz)Page JS avg (gz)Locale leakPage leakComponent avg (gz)E2E reactivityHydration
    base (no i18n)-0.0 KB111.0 KB0.0%0.0%0.7 KB8.1 ms21.6 ms
    Linguistatic11.2 KB152.2 KB50.0%90.0%58.0 KB3.9 ms19.9 ms
    Linguidynamic11.2 KB115.2 KB9.3%0.0%85.5 KB5.9 ms28.0 ms
    Linguiscoped-static11.2 KB120.8 KB4.0%0.0%147.9 KB7.1 ms33.9 ms
    Linguiscoped-dynamic11.2 KB120.2 KB8.6%0.0%83.7 KB42.1 ms32.9 ms
    intlayerstatic5.0 KB125.8 KB50.0%0.0%8.1 KB3.2 ms11.5 ms
    intlayerdynamic5.0 KB118.6 KB0.0%0.0%6.3 KB3.6 ms14.1 ms
    @intlayer/lingui (compat)dynamic10.3 KB137.0 KB9.9%0.0%12.8 KB2.9 ms19.7 ms

    Comment le lire

    • Sur le JavaScript par page, Lingui gagne de justesse. Le dynamic de Lingui s'élève à 115.2 KB, 3.4 KB sous les 118.6 KB d'Intlayer. Les catalogues compilés de Lingui avec des IDs hachés sont compacts, et le routeur TanStack Start divise les routes suffisamment bien pour que la fuite de page soit déjà à 0% dans la ligne dynamic.
    • Tout ce qui concerne la taille de la page va dans l'autre sens. L'hydratation prend 28-34 ms avec Lingui contre 11-14 ms avec Intlayer : i18n.load() + i18n.activate() s'exécutent sur le client avant que React puisse hydrater. Les composants compilés isolément pèsent 58-148 KB contre 6-8 KB. La fuite de locale n'atteint jamais 0% (4-9%) en raison du fallback de locale source.
    • Le changement de locale dans la configuration optimisée est lent. scoped-dynamic Lingui prend 42 ms pour mettre à jour html[lang] : le nouveau catalogue de route doit être récupéré, chargé et activé avant que le changement soit visible. Intlayer bascule en 3-4 ms dans les deux modes.
    • La ligne static d'Intlayer a déjà 0% de fuite de page car seuls les dictionnaires importés par les composants de la page sont regroupés. Une ligne de config (importMode: 'dynamic') supprime aussi la fuite de locale.
    • @intlayer/lingui conserve la syntaxe des macros de Lingui et les sert à partir des dictionnaires Intlayer. Il échange une taille de page plus importante (137 KB, puisque le runtime des macros reste) pour des composants plus petits (12.8 KB) et une hydratation plus rapide que Lingui natif. C'est une étape de migration, pas la destination.
    Tableau complet dans le rapport de benchmark TanStack Start.

    Pourquoi l'écart ? Deux compilateurs, deux unités de travail

    The Intlayer compiler extracts content from components

    Les deux bibliothèques compilent. La différence est ce qu' elles compilent.

    Lingui compile des catalogues. Les macros dans votre source sont extraites dans un fichier .po par locale, puis compilées dans un module JS par locale. L'unité est la locale. Diviser davantage, par route ou par composant, signifie créer plusieurs catalogues, configurer lingui.config.ts pour extraire chacun d'un ensemble différent de fichiers, et charger le bon dans chaque route. L'instance I18n au runtime est globale ; chaque appel useLingui() souscrit le composant à celle-ci.

    bash
    .
    ├── lingui.config.ts
    └── src
        ├── i18n.ts                          # setupI18n(), load(), activate()
        ├── locales
        │   ├── en
        │   │   ├── messages.po
        │   │   └── messages.mjs             # lingui compile output
        │   └── fr
        │       ├── messages.po
        │       └── messages.mjs
        ├── components
        │   └── Counter.tsx                  # const { t } = useLingui(); t`Increment`
        └── routes
            └── $locale
                └── about.tsx                # await import(`../locales/${locale}/messages.mjs`)
    

    Intlayer compile les dictionnaires. Chaque fichier .content.ts est un dictionnaire lié à une clé ; le compilateur résout quel composant importe quelle clé et émet, par dictionnaire et par locale, exactement le JSON dont ce composant a besoin. L'unité est le composant. La portée de la route est une conséquence : une page n'importe que les dictionnaires des composants qu'elle rend.

    bash
    .
    ├── intlayer.config.ts
    └── src
        ├── components
        │   └── Counter
        │       ├── index.tsx                # useIntlayer("counter")
        │       └── index.content.ts
        └── routes
            └── $locale
                ├── about.tsx
                └── about.content.ts
    

    C'est pourquoi le pattern scoped-dynamic est une sortie de build pour Intlayer et une configuration de projet pour Lingui. L'écart se creuse sur deux axes à la fois, les pages et les locales :

    Theoretical content leakage by architecture

    Pour obtenir les numéros de la ligne dynamic, définissez dictionary.importMode: 'dynamic' dans intlayer.config.ts. Voir la documentation d'optimisation du bundle.

    Expérience développeur

    Configuration

    lingui.config.ts
    import { defineConfig } from "@lingui/cli";
    
    export default defineConfig({
      sourceLocale: "en",
      locales: ["en", "fr"],
      catalogs: [
        {
          path: "<rootDir>/src/locales/{locale}/messages",
          include: ["src"],
        },
      ],
    });
    
    src/i18n.ts
    import { setupI18n } from "@lingui/core";
    
    export const loadCatalog = async (locale: string) => {
      const { messages } = await import(`./locales/${locale}/messages.mjs`);
      const i18n = setupI18n();
      i18n.load(locale, messages);
      i18n.activate(locale);
      return i18n;
    };
    

    Puis ajoutez @lingui/babel-plugin-lingui-macro (ou @lingui/swc-plugin) au bundler, exécutez lingui extract après avoir modifié la source, lingui compile avant de construire, et enveloppez l'arborescence dans <I18nProvider i18n={i18n}>.

    intlayer.config.ts
    import { type IntlayerConfig, Locales } from "intlayer";
    
    const config: IntlayerConfig = {
      internationalization: {
        locales: [Locales.ENGLISH, Locales.FRENCH],
        defaultLocale: Locales.ENGLISH,
      },
    };
    
    export default config;
    

    Ajoutez intlayer() à vite.config.ts (ou withIntlayer() à next.config.ts) et enveloppez l'arborescence dans <IntlayerProvider>. Aucune étape d'extraction ou de compilation : les dictionnaires sont construits lors de l'exécution du bundler.

    Composant

    src/components/Counter.tsx
    import { useState } from "react";
    import { useLingui } from "@lingui/react/macro";
    import { Trans } from "@lingui/react/macro";
    
    export const Counter = () => {
      const { t, i18n } = useLingui();
      const [count, setCount] = useState(0);
    
      return (
        <div>
          <p>{i18n.number(count)}</p>
          <button aria-label={t`Counter`} onClick={() => setCount((c) => c + 1)}>
            <Trans>Increment</Trans>
          </button>
        </div>
      );
    };
    

    Le texte anglais se trouve dans le composant ; le texte français se trouve dans src/locales/fr/messages.po sous un ID haché, après l'exécution de lingui extract. Oublier de l'exécuter ou d'exécuter compile bascule silencieusement vers l'anglais.

    src/components/Counter/index.content.ts
    import { t, type Dictionary } from "intlayer";
    
    const counterContent = {
      key: "counter",
      content: {
        label: t({ fr: "Compteur", en: "Counter" }),
        increment: t({ fr: "Incrémenter", en: "Increment" }),
      },
    } satisfies Dictionary;
    
    export default counterContent;
    
    src/components/Counter/index.tsx
    import { useState } from "react";
    import { useIntlayer } from "react-intlayer";
    import { useNumber } from "react-intlayer/format";
    
    export const Counter = () => {
      const { label, increment } = useIntlayer("counter");
      const number = useNumber();
      const [count, setCount] = useState(0);
    
      return (
        <div>
          <p>{number(count)}</p>
          <button aria-label={label} onClick={() => setCount((c) => c + 1)}>
            {increment}
          </button>
        </div>
      );
    };
    

    Les deux locales se trouvent dans un seul fichier à côté du composant. Une valeur fr manquante est une erreur de build, une clé incorrecte est une erreur TypeScript.

    En dehors des composants

    Métadonnées, loaders, fonctions serveur : partout sans arborescence React.

    src/routes/$locale/about.tsx
    import { setupI18n } from "@lingui/core";
    import { msg } from "@lingui/core/macro";
    
    const title = msg`À propos de nous`;
    
    export const loader = async ({ params }: { params: { locale: string } }) => {
      const { messages } = await import(
        `../../locales/${params.locale}/messages.mjs`
      );
      const i18n = setupI18n({
        locale: params.locale,
        messages: { [params.locale]: messages },
      });
    
      return { title: i18n._(title) };
    };
    

    Une nouvelle instance I18n par appel, le bon catalogue chargé manuellement, et msg + i18n._() au lieu de t. Comme le benchmark le note, savoir quand utiliser t, t` ` , i18n.t(), msg ou <Trans> « n'est pas intuitif ».

    src/routes/$locale/about.tsx
    import { getIntlayer } from "intlayer";
    
    export const loader = async ({ params }: { params: { locale: string } }) => {
      const { title } = getIntlayer("about-metadata", params.locale);
    
      return { title };
    };
    

    Conservez les macros Lingui, obtenez les dictionnaires Intlayer

    @intlayer/lingui est un adaptateur prêt à l'emploi pour @lingui/core et @lingui/react. Les macros continuent de se compiler comme avant ; le runtime i18n._() qu'elles compilent est servi à partir des dictionnaires Intlayer, avec des plugins de synchronisation .po qui maintiennent vos catalogues existants comme source de vérité. Les pluriels et sélections ICU s'affichent de manière identique.

    vite.config.ts
    import { defineConfig } from "vite";
    import { lingui } from "@intlayer/lingui/plugin";
    
    export default defineConfig({
      plugins: [lingui()],
    });
    

    Conservez @lingui/babel-plugin-lingui-macro / @lingui/swc-plugin dans la build, exécutées avant le compilateur Intlayer. Consultez la documentation de compatibilité Lingui.

    Quand choisir quoi ?

    Vous voulez ICU MessageFormat avec des macros typées, vos traducteurs travaillent en .po avec un pipeline TMS existant, vous préférez les chaînes sources en ligne dans JSX, et votre équipe est à l'aise avec la gestion du workflow extraction / compilation / division des catalogues. Son JS par page est compétitif une fois le lazy loading configuré.

    Vous voulez du contenu scopé au composant, du TypeScript strict, des erreurs de clés manquantes au build, du tree-shaking et lazy loading sans effort, des composants légers, une hydratation rapide, un changement de locale instantané, et des outils éditoriaux intégrés (Visual Editor, CMS, traduction par IA, serveur MCP). Particulièrement pertinent pour les codebases modulaires volumineuses et les design systems.

    Vous utilisez déjà Lingui et souhaitez migrer vers les dictionnaires Intlayer progressivement sans toucher aux macros. Vos catalogues .po restent la source de vérité grâce au plugin de synchronisation PO. Mesuré côte à côte dans Lingui vs @intlayer/lingui.

    FAQ

    Parce que l'unité de compilation diffère. Lingui compile un catalogue par locale : tout ce qui est en dessous (catalogues par route, lazy loading, exclusion du fallback du bundle) relève de la configuration. Intlayer compile un dictionnaire par composant, donc le découpage par route découle directement du build. C'est pourquoi un composant Lingui compilé isolément pèse 58-153 KB contre 6-8 KB pour Intlayer.

    Les macros gardent le message source disponible comme solution de repli (fallback) au runtime, de sorte que la chaîne anglaise est envoyée à côté de sa traduction. Le benchmark mesure 3-15% de chaînes en dans les pages fr dans chaque configuration optimisée. Intlayer résout les fallbacks au build et ne livre que la locale active.

    Oui, et sur TanStack Start il l'emporte d'un cheveu : 115.2 KB en dynamic contre 118.6 KB pour Intlayer. Les catalogues compilés avec des identifiants hachés sont compacts. Le coût apparaît ailleurs : hydratation à 28-34 ms contre 11-14 ms, et un changement de locale à 42 ms dans la configuration scoped-dynamic.

    Non. @intlayer/lingui permet de compiler t`...` , <Trans>, msg, plural, select et selectOrdinal comme avant ; seul le mécanisme de résolution sous-jacent de i18n._() change. Conservez @lingui/babel-plugin-lingui-macro ou @lingui/swc-plugin dans le build. Voir la documentation de compatibilité Lingui.

    Elles restent nécessaires pour les macros, mais disparaissent pour le contenu propre à Intlayer. Les dictionnaires .content.ts sont créés lors de l'exécution du bundler, sans commande CLI distincte, et intlayer test fait échouer la CI en cas de clé manquante au lieu de basculer silencieusement sur la chaîne source.

    Comparaisons associées

    JavaScript i18n library ecosystem

    Même benchmark, autres bibliothèques :

    Pour aller plus loin :

    Documentation de référence :

    Pour comprendre d'où viennent ces bibliothèques, lisez l'histoire de l'i18n en JavaScript.

    Étoiles GitHub

    Les stars GitHub sont un indicateur fort de la popularité d'un projet, de la confiance de la communauté et de sa pertinence à long terme. Bien que ce ne soit pas une mesure directe de la qualité technique, ils reflètent le nombre de développeurs qui trouvent le projet utile, suivent sa progression et sont susceptibles de l'adopter.

    Star History Chart

    Activité des commits

    Les étoiles mesurent la popularité. Les commits mesurent le travail investi dans un projet. Au moment de l'écriture, Intlayer compte environ 7 500 commits, plus que la plupart des bibliothèques comparées ici, et environ 5 fois plus que next-intl ou next-i18next.

    • lingui/js-lingui
    • aymericzip/intlayer

    Commits sur la branche par défaut, source : API GitHub.

    Intlayer est un monorepo : ce total inclut chaque package de framework, la CLI et la documentation. Lisez les commits comme un signal d'activité, pas de qualité.

    Téléchargements npm

    • @lingui/core
    • intlayer

    Source : API de téléchargements du registre npm.

    Le nombre de téléchargements récompense les solutions les plus anciennes, pas les meilleures. Une bibliothèque publiée il y a des années est toujours installée par chaque projet qui l'a choisie à l'époque, par chaque exécution de CI et par chaque package qui en dépend. Ce chiffre mesure l'inertie plus qu'un choix réfléchi.

    Les assistants IA amplifient cet effet. next-intl, i18next et vue-i18n sont omniprésents dans le code sur lequel ils ont été entraînés : ils les proposent donc par défaut, sans faire l'effort de comparer les alternatives. Chaque suggestion ajoute des téléchargements, qui alimentent la suggestion suivante. Comparez sur le benchmark plutôt que sur le nombre de téléchargements.

    Conclusion

    Lingui est la plus forte bibliothèque runtime-plus-compiler dans ce benchmark. Ses catalogs compilés et hashés lui donnent du JavaScript par page dans quelques KB de Intlayer, et même légèrement en dessous sur TanStack Start. Si les bytes par page étaient la seule métrique, ce serait une égalité.

    Ce n'est pas le cas. Le compilateur de Lingui s'arrête à la locale ; tout ce qui est en dessous (catalogs par route, chargement différé, exclusion de la fallback du bundle) est configuration, et le benchmark montre le coût de cette limite : composants 10-20x plus volumineux, hydratation 2-3x plus lente, 3-15% de fuite de locale qui ne disparaît jamais, et un changement de locale de 42 ms dans la configuration optimisée. Le compilateur d'Intlayer fonctionne au niveau du composant, donc ces chiffres sont 6-8 KB, 11-14 ms, 0% et 3-4 ms sans aucune configuration.

    Toutes les données brutes, les applications de test et les scripts se trouvent dans le référentiel Benchmark Bloom. Exécutez-le vous-même.

    Consultez le document 'Pourquoi Intlayer ?' pour plus de détails.

    Commentaires

    Aucun commentaire pour le moment. Soyez le premier à partager vos pensées.

    Articles similaires

    Derniers articles