Автор:
    Создание:2026-09-13Последнее обновление:2026-09-27

    Lingui против Intlayer: Бенчмарк интернационализации (i18n) для React и Next.js

    Lingui и Intlayer - единственные две библиотеки в этом сравнительном исследовании, которые опираются на компилятор, а не на обычный рантайм. Lingui извлекает сообщения из макросов во время сборки и компилирует каталоги под каждую локаль. Intlayer компилирует словари на уровне компонентов и применяет tree-shaking отдельно для каждого языка. В теории их показатели должны быть близки. Цифры показывают, в чем они существенно расходятся.

    Данные получены с помощью Benchmark Bloom - открытого набора тестов, который собирает одно и то же приложение с каждой библиотекой и замеряет то, что браузер реально скачивает и исполняет.

    Кратко (tl;dr): Lingui ближе всех подобрался к Intlayer по объему чистого JavaScript на страницу: 115-120 КБ против 118,6 КБ на TanStack Start при настроенной ленивой загрузке, и 148,6 КБ против 141,3 КБ на Next.js. Однако в остальных аспектах образуется значительный разрыв: изолированно скомпилированный компонент Lingui весит 58-153 КБ против 6-8 КБ у Intlayer, гидратация занимает 28-34 мс против 11-14 мс, запасной текст исходной локали дает утечку 3-15% английских строк на французские страницы даже в оптимизированных сборках, а достижение такой оптимизации требует ручного извлечения, сборки и маршрутного разделения каталогов. Intlayer обеспечивает наилучшие показатели без какой-либо ручной настройки.

    Коротко о главном

    • Lingui - работает на макросах ( t`...` , <Trans>, msg), поддерживает синтаксис ICU MessageFormat, каталоги в форматах .po / JSON, пайплайн lingui extract + lingui compile. Компилирует идентификаторы в компактные хеши, поддерживает динамическую подгрузку каталогов по языкам. Зрелое, не привязанное к фреймворку решение с развитой инфраструктурой переводческих утилит вокруг формата .po.
    • Intlayer - компонентно-ориентированная модель контента. Словари .content.ts располагаются прямо рядом с целевым компонентом; компилятор на этапе сборки выполняет tree-shaking и загружает переводы по требованию для каждого компонента и языка; генерирует строгие типы TypeScript на основе контента, а пропущенные переводы вызывают ошибку сборки. Содержит встроенные middleware, SEO-хелперы, Визуальный редактор / CMS и модуль автоперевода через ИИ.
    БиблиотекаЗвезды на GitHubВсего коммитовПоследний коммитПервый релизВерсия в NPMЗагрузки в NPM
    aymericzip/intlayerGitHub Repo starsGitHub commit activityLast CommitАпрель 2024npmnpm downloads
    lingui/js-linguiGitHub Repo starsGitHub commit activityLast CommitДекабрь 2016npmnpm downloads
    Значки обновляются автоматически. Актуальные значения меняются со временем.

    Сопоставление функциональности

    ВозможностьIntlayer (react-intlayer / next-intlayer)Lingui (@lingui/core / @lingui/react)
    Переводы рядом с компонентами✅ Да, файл .content.ts хранится в одной папке с компонентом⚠️ Исходные строки внутри JSX через макросы; переводы в централизованных .po каталогах
    Интеграция с TypeScript✅ Строгая типизация автоматически генерируется из содержимого⚠️ Макросы типизированы; идентификаторы без типов, недостающие ключи не подсвечиваются
    Поиск отсутствующих переводов✅ Ошибка TypeScript + предупреждение/ошибка во время сборки⚠️ lingui extract показывает статистику; в рантайме тихо откатывается на английский текст
    Форматированный контент (JSX/Markdown/комп.)✅ Прямая встроенная поддержка✅ Компонент <Trans> со вложенными элементами
    Поддержка ICU⚠️ В разработке✅ Да (макросы plural, select, selectOrdinal)
    Форматирование (даты, числа, валюты)✅ useNumber, useDate, ... (на базе нативного Intl)✅ i18n.date(), i18n.number()
    Локализованный роутинг и middleware✅ Встроенный прокси/middleware, метод getMultilingualUrls❌ Отсутствует в базовом пакете
    Инструменты SEO (hreflang, sitemap...)✅ Встроенные хелперы❌ Требуется ручная реализация
    Синхронные серверные компоненты (RSC)✅ useIntlayer из next-intlayer/server работает в любых дочерних серверных компонентах⚠️ Требуется экземпляр I18n на запрос, передаваемый вручную или через setI18n
    Tree-shaking (поставка только нужного)✅ На уровне компонентов и языков, автоматически компилятором⚠️ По языкам через lingui compile; по маршрутам требует ручного разделения файлов каталога
    Ленивая загрузка (Lazy loading)✅ importMode: 'dynamic' (одна строка в конфигурационном файле)⚠️ Ручной вызов import() скомпилированных каталогов + i18n.load() / i18n.activate()
    Очистка неиспользуемого контента✅ Устаревшие словари удаляются во время сборки✅ lingui extract --clean удаляет неактуальные сообщения
    Проверка отсутствующих строк (CLI / CI)✅ npx intlayer content test⚠️ Отчеты lingui extract (по умолчанию сборка не падает с ошибкой)
    Сборочный процесс✅ Один плагин (@intlayer/swc / @intlayer/babel / vite-intlayer)⚠️ Плагин макросов (Babel или SWC) + раздельные шаги extract и compile
    Перевод с помощью искусственного интеллекта✅ Встроен, использует ваши собственные ключи API❌ Нет
    Визуальный редактор / CMS✅ Бесплатный Visual Editor + подключаемая CMS❌ Нет (формат .po интегрируется со сторонними TMS)
    Сервер MCP и Agent Skills✅ Да❌ Нет
    Экосистема и сообщество⚠️ Моложе, но стремительно развивается✅ Зрелое, независимое от конкретного фреймворка решение

    Сравнительное тестирование

    Что исследовалось

    Пакет Benchmark Bloom собирает абсолютно идентичное приложение на каждой библиотеке: 10 страниц (home, about, blog, careers, contact, FAQ, pricing, products, settings, team), 10 локалей (en, fr, es, de, it, pt, zh, ja, ko, ru), одинаковая верстка и контент. Замеры проводились на страницах en и fr. Каждая библиотека исследовалась в четырех стратегиях загрузки:

    СтратегияОписаниеГде применяется
    staticКаталоги всех языков импортируются и загружаются целиком на стартеБыстрые прототипы, код от нейросетей
    dynamicЧерез import() подгружается каталог только активного языка, но для всего сайта сразуПодавляющее большинство проектов
    scoped-staticПо каталогу на каждый маршрут, все каталоги упакованы в общий стартовый бандлВстречается редко
    scoped-dynamicПо каталогу на маршрут + динамический import(). Только активная страница и нужный языкПроекты со строгим лимитом по весу

    У Intlayer отсутствует вариант "scoped": компилятор изолирует контент на уровне каждого компонента автоматически, поэтому режимы static и dynamic изначально работают максимально изолированно.

    В ходе каждого теста замеряются:

    • Lib size: вес gzip пустого компонента, подключающего исключительно библиотеку i18n (базовый оверхед рантайма).
    • Page JS: средний вес gzip JavaScript, загружаемого на страницу (усреднено по всем страницам и локалям).
    • Locale leak %: доля переведенных строк в загруженном JS, относящихся к языкам, которые пользователь не просматривает.
    • Page leak %: доля строк в загруженном JS, относящихся к страницам, на которых пользователь не находится.
    • Component avg: средний вес gzip отдельного компонента при изолированной сборке.
    • E2E reactivity: чистое время от момента выбора нового языка до фактического обновления атрибута html[lang] в DOM (Playwright, среднее по 5 прогонам).
    • Hydration: время завершения фазы гидратации React.
    Значения зафиксированы в тестировании от 2026-09-12 на базе @lingui/react 6.6.0 и intlayer 9.5.1. Тестовое приложение намеренно сделано компактным (несколько десятков фраз на язык), поэтому утечки наглядно отражают системную проблему: по мере роста проекта они пропорционально увеличиваются.

    Результаты на Next.js

    Выберите интересующие вас метрики и библиотеки:

    Метрика

    Динамическая загрузка JSON

    Ленивая загрузка переводов во время выполнения

    Ограниченный JSON (пространства имен)

    Пространства имен перевода для каждой страницы

    Что это за метрика?

    Общий размер пакета библиотеки интернационализации в формате gzip. Он включает в себя только провайдер и логику извлечения контента после tree-shaking и минификации.

    Почему это важно?

    Меньший размер библиотеки снижает начальную загрузку JavaScript, что ускоряет загрузку и выполнение кода на клиенте.

    Вид

    БиблиотекаСтратегияLib size (gz)Page JS ср. (gz)Утечка языкаУтечка страницКомпонент ср. (gz)E2E-реактивностьГидратация
    база (без i18n)-0,0 КБ141,0 КБ0,0%0,0%0,9 КБ13,4 мс11,8 мс
    Linguistatic11,9 КБ207,4 КБ50,0%90,0%73,3 КБ15,3 мс15,2 мс
    Linguidynamic11,9 КБ145,4 КБ2,8%89,9%19,9 КБ15,7 мс12,7 мс
    Linguiscoped-static11,9 КБ148,2 КБ2,7%89,1%20,4 КБ15,1 мс13,1 мс
    Linguiscoped-dynamic11,9 КБ148,6 КБ14,8%0,0%152,6 КБ16,1 мс14,8 мс
    next-intlayerstatic5,5 КБ141,3 КБ0,0%0,0%8,5 КБ15,5 мс16,9 мс
    next-intlayerdynamic5,5 КБ141,3 КБ0,0%0,0%6,9 КБ15,3 мс15,9 мс

    Анализ данных

    • Базовый вес рантайма. Пустой компонент с Lingui весит 11,9 КБ gzip против 5,5 КБ с Intlayer. В масштабе страницы лучшая сборка Lingui оказывается на +7,3 КБ тяжелее Intlayer (148,6 против 141,3 КБ); Intlayer добавляет всего +0,3 КБ к весу приложения без интернационализации.
    • Простая сборка создает колоссальный оверхед. Загрузка всех каталогов сразу дает 207,4 КБ на страницу (+66 КБ к базовому весу). Половина строк относится к чужому языку, а 90% - к посторонним страницам сайта.
    • Динамическая загрузка исправляет язык, но не маршруты. При наличии одного каталога на язык утечка страниц сохраняется на уровне ~90%: весь французский каталог отдается на каждой странице. Чтобы добиться 0% утечки страниц в Lingui, приходится настраивать scoped-dynamic: собирать отдельный каталог на каждый маршрут и вручную подключать его в страницы.
    • Утечка оригинального текста. Даже в наиболее продуманных схемах от 3% до 15% английских строк попадают на страницы на французском языке. Макросы Lingui встраивают исходную строку для подстраховки в итоговый бандл. Intlayer разрешает фолбеки на этапе сборки и доставляет клиенту только целевой язык.
    • Взрывной рост размера компонентов в scoped-dynamic. Каждый изолированный компонент весит в среднем 152,6 КБ, поскольку через цепочку импортов в него попадают каталоги других маршрутов. Тот же компонент на useIntlayer() занимает всего 6,9 КБ.
    Полная таблица, каждая библиотека и стратегия в отчете о бенчмарке Next.js.

    Результаты на TanStack Start

    БиблиотекаСтратегияLib size (gz)Page JS ср. (gz)Утечка языкаУтечка страницКомпонент ср. (gz)E2E-реактивностьГидратация
    база (без i18n)-0,0 КБ111,0 КБ0,0%0,0%0,7 КБ8,1 мс21,6 мс
    Linguistatic11,2 КБ152,2 КБ50,0%90,0%58,0 КБ3,9 мс19,9 мс
    Linguidynamic11,2 КБ115,2 КБ9,3%0,0%85,5 КБ5,9 мс28,0 мс
    Linguiscoped-static11,2 КБ120,8 КБ4,0%0,0%147,9 КБ7,1 мс33,9 мс
    Linguiscoped-dynamic11,2 КБ120,2 КБ8,6%0,0%83,7 КБ42,1 мс32,9 мс
    intlayerstatic5,0 КБ125,8 КБ50,0%0,0%8,1 КБ3,2 мс11,5 мс
    intlayerdynamic5,0 КБ118,6 КБ0,0%0,0%6,3 КБ3,6 мс14,1 мс
    @intlayer/lingui (адаптер)dynamic10,3 КБ137,0 КБ9,9%0,0%12,8 КБ2,9 мс19,7 мс

    Анализ данных

    • По объему JS на страницу Lingui незначительно выигрывает. Вариант Lingui dynamic фиксирует 115,2 КБ, что на 3,4 КБ меньше, чем 118,6 КБ у Intlayer. Скомпилированные каталоги Lingui с хешированными ключами очень компактны, а роутер TanStack Start отлично разделяет код маршрутов, поэтому утечка страниц исчезает уже на шаге dynamic.
    • Все остальные метрики на стороне Intlayer. Гидратация с Lingui длится 28-34 мс против 11-14 мс с Intlayer: методы i18n.load() + i18n.activate() обязаны отработать на клиенте до старта гидратации React. Отдельные компоненты весят 58-148 КБ вместо 6-8 КБ. Утечка исходного языка не обнуляется никогда (составляя 4-9%).
    • Смена языка в оптимизированном режиме заметно замедлена. Lingui в схеме scoped-dynamic тратит 42 мс на смену html[lang], поскольку браузер должен сначала запросить, получить и активировать новый каталог маршрута. Intlayer переключает язык за 3-4 мс в обоих сценариях.
    • Строка static у Intlayer сразу обеспечивает 0% утечки страниц, так как в бандл включаются исключительно словари, импортированные задействованными на странице компонентами. А параметр importMode: 'dynamic' устраняет и языковую утечку.
    • @intlayer/lingui сохраняет синтаксис макросов Lingui, связывая их со словарями Intlayer. Это решение жертвует небольшим объемом на страницу (137 КБ из-за присутствия макро-рантайма), взамен давая облегченные компоненты (12,8 КБ) и быструю гидратацию. Прекрасный вариант для плавного перехода.
    Полная таблица в отчете о бенчмарке TanStack Start.

    В чем фундаментальная причина? Два компилятора, две единицы деления

    The Intlayer compiler extracts content from components

    Обе библиотеки компилируют код, но принципиально отличается объект компиляции.

    Lingui компилирует каталоги. Макросы в коде извлекаются в единый файл .po на язык, а затем компилируются в модуль JS на язык. Главная единица деления - локаль. Чтобы разделить данные глубже (по маршрутам или компонентам), вам придется заводить отдельные каталоги, прописывать правила в lingui.config.ts и вручную настраивать их загрузку. Экземпляр I18n глобален, и каждый useLingui() подключает компонент ко всему его содержимому.

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

    Intlayer компилирует словари. Каждый файл .content.ts - это изолированный словарь под определенным ключом; компилятор выясняет, какой компонент использует какой ключ, и формирует компактный JSON строго для этого компонента и языка. Главная единица деления - компонент. Привязка к маршрутам происходит автоматически: страница скачивает словари только тех компонентов, которые выводятся на экран.

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

    Именно поэтому архитектура scoped-dynamic формируется в Intlayer автоматически на этапе сборки, тогда как в Lingui это требует масштабной ручной настройки. Разрыв увеличивается сразу по двум осям, страницы и локали:

    Theoretical content leakage by architecture

    Чтобы повторить показатели строки dynamic, активируйте dictionary.importMode: 'dynamic' в intlayer.config.ts. Подробнее в документации по оптимизации бандла.

    Опыт разработки (Developer Experience)

    Настройка

    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;
    };
    

    Затем в сборщик добавляется плагин @lingui/babel-plugin-lingui-macro (или @lingui/swc-plugin), после правок выполняется lingui extract, перед сборкой - lingui compile, а приложение оборачивается в <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;
    

    Добавьте плагин intlayer() в vite.config.ts (или withIntlayer() в next.config.ts) и оберните дерево компонентов в <IntlayerProvider>. Никаких ручных команд извлечения или сборки: словари компилируются автоматически бандлером.

    Компоненты

    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>
      );
    };
    

    Английский текст пишется прямо в компоненте; перевод на французский хранится в src/locales/fr/messages.po под сгенерированным хеш-идентификатором после выполнения lingui extract. Если забыть запустить команду извлечения или сборки, на экране незаметно останется английский текст.

    src/components/Counter/index.content.ts
    import { t, type Dictionary } from "intlayer";
    
    const counterContent = {
      key: "counter",
      content: {
        label: t({ en: "Counter", fr: "Compteur" }),
        increment: t({ en: "Increment", fr: "Incrémenter" }),
      },
    } 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>
      );
    };
    

    Оба языка находятся в одном файле рядом с компонентом. Отсутствие перевода для fr приведет к ошибке сборщика, а опечатка в имени ключа будет немедленно подсвечена TypeScript.

    Вне компонентов React

    Метаданные, функции загрузки данных (loaders), серверные функции: в любом контексте без дерева React.

    src/routes/$locale/about.tsx
    import { setupI18n } from "@lingui/core";
    import { msg } from "@lingui/core/macro";
    
    const title = msg`About us`;
    
    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) };
    };
    

    Создание нового экземпляра I18n на каждый вызов, ручной импорт конкретного каталога и использование msg + i18n._() вместо привычного t. Как отмечается в примечаниях к бенчмарку, постоянный выбор между t, t` ` , i18n.t(), msg или <Trans> часто сбивает с толку.

    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 };
    };
    

    Использование макросов Lingui со словарями Intlayer

    @intlayer/lingui работает как прозрачный адаптер для @lingui/core и @lingui/react. Макросы компилируются в прежнем режиме; генерируемые вызовы i18n._() берут данные из словарей Intlayer, а плагины синхронизации сохраняют файлы .po в качестве источника данных. Формы множественного числа и условия ICU отображаются абсолютно идентично.

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

    Оставьте @lingui/babel-plugin-lingui-macro или @lingui/swc-plugin в конфигурации сборщика перед компилятором Intlayer. См. документацию по совместимости с Lingui.

    Что выбрать для вашего проекта?

    Вам нужен ICU MessageFormat с типизированными макросами, ваши переводчики работают в .po с существующим пайплайном TMS, вы предпочитаете исходные строки инлайн в JSX, и вашей команде удобно управлять извлечением, компиляцией и разделением каталогов. Его JS на страницу вполне конкурентоспособен после настройки lazy loading.

    Вам нужен контент с областью видимости на уровне компонентов, строгий TypeScript, ошибки отсутствующих ключей во время сборки, tree-shaking и lazy loading без усилий, компактные компоненты, быстрая гидратация, мгновенное переключение локалей и встроенные инструменты редактирования (Визуальный редактор, CMS, ИИ-перевод, MCP-сервер). Особенно актуально для крупных модульных кодовых баз и дизайн-систем.

    Вы уже используете Lingui и хотите постепенно перейти на словари Intlayer, не трогая макросы. Ваши каталоги .po остаются источником истины благодаря плагину синхронизации PO. Измерено бок о бок в Lingui vs @intlayer/lingui.

    FAQ

    Потому что единица компиляции различается. Lingui компилирует один каталог на локаль: всё, что ниже этого (каталоги по маршрутам, lazy loading, исключение fallback из бандла), требует ручной конфигурации. Intlayer компилирует один словарь на компонент, поэтому разделение по маршрутам происходит автоматически во время сборки. Вот почему изолированно скомпилированный компонент Lingui весит 58-153 KB против 6-8 KB у Intlayer.

    Макросы сохраняют исходное сообщение доступным в рантайме в качестве фоллбэка, поэтому английская строка отправляется вместе с переводом. Бенчмарк фиксирует 3-15% строк en внутри страниц fr в каждой оптимизированной конфигурации. Intlayer разрешает фоллбэки во время сборки и отправляет только активную локаль.

    Да, а на TanStack Start он даже чуть впереди: 115.2 KB в режиме dynamic против 118.6 KB у Intlayer. Скомпилированные каталоги с хэшированными ID компактны. Однако цена проявляется в других аспектах: гидратация за 28-34 мс против 11-14 мс и переключение локали за 42 мс в конфигурации scoped-dynamic.

    Нет. @intlayer/lingui сохраняет компиляцию t`...` , <Trans>, msg, plural, select и selectOrdinal как раньше; меняется только то, относительно чего резолвит i18n._(). Сохраните @lingui/babel-plugin-lingui-macro или @lingui/swc-plugin в сборке. См. документацию по совместимости с Lingui.

    Они остаются для макросов и исчезают для собственного контента Intlayer. Словари .content.ts собираются при запуске бандлера без отдельного шага CLI, а intlayer test прерывает CI при отсутствии ключа вместо тихого отката к исходной строке.

    Похожие сравнения

    JavaScript i18n library ecosystem

    Тот же бенчмарк, другие библиотеки:

    Подробнее:

    Справочная документация:

    Чтобы понять, откуда взялись эти библиотеки, прочитайте историю i18n в JavaScript.

    Звезды на GitHub

    Количество звезд на GitHub отражает интерес индустрии, уровень доверия сообщества и перспективность развития проекта. Это наглядно показывает, сколько разработчиков находят библиотеку полезной в реальной работе.

    График динамики звезд

    Активность коммитов

    Звёзды показывают популярность. Коммиты показывают, сколько работы вкладывается в проект. На момент написания у Intlayer около 7 500 коммитов: больше, чем у большинства сравниваемых здесь библиотек, и примерно в 5 раз больше, чем у next-intl или next-i18next.

    • lingui/js-lingui
    • aymericzip/intlayer

    Коммиты в основной ветке, источник: GitHub API.

    Intlayer это монорепозиторий, поэтому в счёт входят все пакеты для фреймворков, CLI и документация. Считайте коммиты показателем активности, а не качества.

    Загрузки npm

    • @lingui/core
    • intlayer

    Источник: API загрузок реестра npm.

    Число загрузок вознаграждает самые старые решения, а не лучшие. Библиотеку, выпущенную много лет назад, до сих пор устанавливает каждый проект, который выбрал её тогда, каждый запуск CI и каждый зависящий от неё пакет. Эта цифра измеряет инерцию, а не осознанный выбор.

    ИИ-ассистенты усиливают этот эффект. next-intl, i18next и vue-i18n повсюду встречаются в коде, на котором они обучались, поэтому ассистенты предлагают их по умолчанию, не сравнивая альтернативы. Каждая подсказка добавляет загрузки, которые подпитывают следующую. Сравнивайте по бенчмарку, а не по числу загрузок.

    Заключение

    Lingui - сильнейшая гибридная библиотека в данном бенчмарке. Ее скомпилированные и хешированные каталоги дают размер JavaScript на страницу, практически равный показателям Intlayer, а на TanStack Start даже слегка превосходящий его. Если бы единственным критерием были килобайты на страницу, результатом стала бы боевая ничья.

    Но это не единственный показатель. Компилятор Lingui ограничен границами локали; все, что лежит ниже (каталоги маршрутов, ленивая загрузка, очистка запасного текста), требует ручного труда разработчика. Бенчмарк демонстрирует цену этого ограничения: компоненты в 10-20 раз тяжелее, гидратация в 2-3 раза медленнее, постоянная утечка 3-15% исходных текстов и пауза в 42 мс при переключении языка в оптимизированной сборке. Компилятор Intlayer работает на уровне каждого компонента, обеспечивая 6-8 КБ, 11-14 мс, 0% и 3-4 мс из коробки без лишней настройки.

    Все первичные данные, тестовые приложения и скрипты опубликованы в репозитории Benchmark Bloom. Вы можете повторить эти тесты самостоятельно.

    Подробнее читайте в материале 'Почему Intlayer?'.

    Комментарии

    Пока нет комментариев. Будьте первым, кто поделится своими мыслями.

    Похожие сообщения

    Последние сообщения