Спросите свой вопрос и получите сводку документа, используя эту страницу и выбранного вами поставщика AI
Содержимое этой страницы было переведено с помощью ИИ.
Смотреть последнюю версию оригинального контента на английскомЕсли у вас есть идея по улучшению этой документации, не стесняйтесь внести свой вклад, подав запрос на вытягивание на GitHub.
Ссылка на документацию GitHubКопировать Markdown документа в буфер обмена
Lingui против @intlayer/lingui | Те же макросы, другой рантайм
@intlayer/lingui - это адаптер совместимости для @lingui/core и @lingui/react. Ваши вызовы t`...` , <Trans>, useLingui() и i18n._() остаются в первозданном виде; макросы компилируются как обычно; меняется лишь источник сообщений во время выполнения. Вместо одного общего скомпилированного каталога на каждую локаль каждый вызов связывается со словарем Intlayer, скомпилированным индивидуально для него.
В этой статье оценивается данная замена на одном и том же приложении TanStack Start, собранном сначала с чистым Lingui, а затем с адаптером. Данные получены из Benchmark Bloom. Чтобы сравнить две библиотеки напрямую, прочтите статью Lingui против Intlayer. Здесь же речь идет о том, что дает адаптер и в каких сценариях он не дает преимуществ.
Кратко (tl;dr): На одном и том же приложении TanStack Start@intlayer/linguiснизил средний вес компонента с 85,5 КБ до 12,8 КБ gzip, ускорил гидратацию с 28 мс до 19,7 мс, а переключение языка - с 5,9 мс до 2,9 мс при полностью неизменных макросах. В базовой конфигурации (все каталоги загружаются сразу) он также устранил 90% утечки страниц и сэкономил 12 КБ на страницу. Однако при ленивой загрузке адаптер отдает 137 КБ на страницу против 115 КБ у чистого Lingui: адаптер выполняет парсинг ICU в рантайме, тогда как Lingui поставляет предварительно скомпилированные массивы токенов. Утечка исходной локали (~9-10%) одинакова с обеих сторон, так как она обусловлена встроенным в компоненты запасным текстомmessage, а не рантаймом. Адаптер оформлен как плагин для Vite; измерения проводились на TanStack Start.
Что представляет собой @intlayer/lingui
Lingui состоит из компилятора и рантайма. Макросы из вашего исходного кода извлекаются в каталог .po (или JSON) для каждой локали, компилируются в JS-модуль на локаль и загружаются в глобальный экземпляр I18n с помощью i18n.load() + i18n.activate(). Каждый вызов useLingui() подписывается на этот экземпляр; каждый вызов _() ищет идентификатор в активном каталоге.
@intlayer/lingui сохраняет макросы и привычный API, полностью преобразуя логику поиска сообщений:
- Алиасы импортов. Плагин
lingui()из пакета@intlayer/lingui/pluginоборачиваетvite-intlayerи настраиваетresolve.alias, направляя@lingui/coreи@lingui/reactна@intlayer/lingui. Ваши пути импорта остаются прежними. - Каталоги как единый источник правды. Плагин
syncJSON(илиsyncPOдля файлов.po) читает существующие каталоги и конвертирует их в словари Intlayer, записывая переводы обратно при обновлении через CLI или CMS. С опциейsplitKeys: "key-prefix"плоский каталог с составными идентификаторами (footer.github,hero.title) превращается в набор компактных словарей по префиксам вместо одного тяжелого файла на 244 КБ. - Привязка к месту вызова. Этап оптимизации Intlayer собирает идентификаторы, переданные в
_,tи<Trans>в каждом файле, и передает компоненту только релевантные словари. Элемент<Trans id="hero.title">связывается автономно; хукuseLingui()подключает все префиксы, используемые в текущем файле. Идентификаторы без точек (хешированные id,mockBanner) обращаются к общему запасному словарюmessagesLingui.
Копировать код в буфер обмена
Копировать код в буфер обмена
Компонент больше не держит ссылку на глобальный экземпляр и связанный с ним монолитный каталог. Он обращается только к словарю hero. Именно поэтому размер компонентов в таблице ниже снижается в 7 раз.
Что адаптер сохраняет, игнорирует и не заменяет
Открыть таблицу в модальном окне для четкого просмотра всех данных
| API Lingui | С @intlayer/lingui |
|---|---|
Макросы t`...` , msg, plural, select, <Trans> | ✅ Сохраняются. Оставьте @lingui/babel-plugin-lingui-macro или @lingui/swc-plugin перед этапом Intlayer |
useLingui() → { i18n, _, t } | ✅ Сохраняется. Работает и вне контекст-провайдера (локаль берется из react-intlayer) |
i18n._(id, values), i18n.t() | ✅ Сохраняется. Поддерживает как явные, так и хешированные идентификаторы |
Формы множественного числа ICU, select, selectordinal, # | ✅ Сохраняются через резолвер ICU в Intlayer |
i18n.date(), i18n.number(), formats | ✅ Сохраняются на базе нативного API Intl |
I18nProvider | ✅ Сохраняется. Оборачивает IntlayerProvider; слушает i18n.on("change"), поэтому activate() рендерит заново |
i18n.activate(locale) | ✅ Сохраняется |
i18n.load(locale, messages) / loadAndActivate() | ⚠️ Допускается как рантайм-фолбек. Скомпилированные словари в приоритете; предупреждение в dev-режиме |
setupI18n({ messages, missing }) | ⚠️ messages объединяются как фолбек; параметр missing игнорируется |
lingui extract / lingui compile | ✅ Ваш процесс не меняется. Укажите syncPO / syncJSON на извлеченные каталоги |
defaultComponent в I18nProvider | ⚠️ Сохраняется в контексте, но не применяется при рендере |
| Next.js | ❌ Плагин оборачивает vite-intlayer. Поддерживаются только Vite, TanStack Start и React Router |
Бенчмарк
Что измерялось
Тестовый набор 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.
Lingui компилировался в четырех стратегиях загрузки: от статического импорта всех каталогов заранее (static) до ленивой загрузки каталога под каждый конкретный маршрут (scoped-dynamic). Адаптер тестировался на тех же компонентах, где менялись только vite.config.ts и intlayer.config.ts. Конфигурация static упаковывает все языки сразу; конфигурация dynamic (importMode: 'dynamic') подгружает нужный язык по запросу. Вариант "scoped" отсутствует, поскольку оптимизатор изолирует код на уровне мест вызовов.
Для каждой сборки фиксируются параметры:
- 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/react6.6.0 и@intlayer/lingui9.5.1. Тестовое приложение компактно (несколько десятков строк на язык), поэтому процент утечки отражает системную тенденцию: он масштабируется с ростом контента, тогда как накладные расходы рантайма остаются постоянными.
Результаты на TanStack Start
Открыть таблицу в модальном окне для четкого просмотра всех данных
| Конфигурация | Стратегия | Lib size (gz) | Page JS ср. (gz) | Утечка языка | Утечка страниц | Компонент ср. (gz) | E2E-реактивность | Гидратация |
|---|---|---|---|---|---|---|---|---|
| base (без i18n) | - | 0,0 КБ | 111,0 КБ | 0,0% | 0,0% | 0,7 КБ | 8,1 мс | 21,6 мс |
| Lingui | static | 11,2 КБ | 152,2 КБ | 50,0% | 90,0% | 58,0 КБ | 3,9 мс | 19,9 мс |
| Lingui | dynamic | 11,2 КБ | 115,2 КБ | 9,3% | 0,0% | 85,5 КБ | 5,9 мс | 28,0 мс |
| Lingui | scoped-static | 11,2 КБ | 120,8 КБ | 4,0% | 0,0% | 147,9 КБ | 7,1 мс | 33,9 мс |
| Lingui | scoped-dynamic | 11,2 КБ | 120,2 КБ | 8,6% | 0,0% | 83,7 КБ | 42,1 мс | 32,9 мс |
@intlayer/lingui | static | 10,3 КБ | 140,5 КБ | 50,0% | 0,0% | 14,9 КБ | 3,3 мс | 11,3 мс |
@intlayer/lingui | dynamic | 10,3 КБ | 137,0 КБ | 9,9% | 0,0% | 12,8 КБ | 2,9 мс | 19,7 мс |
intlayer (нативный) | static | 5,0 КБ | 125,8 КБ | 50,0% | 0,0% | 8,1 КБ | 3,2 мс | 11,5 мс |
intlayer (нативный) | dynamic | 5,0 КБ | 118,6 КБ | 0,0% | 0,0% | 6,3 КБ | 3,6 мс | 14,1 мс |
Анализ результатов
- Компоненты: в 7 раз компактнее. Это главное преимущество адаптера. Изолированный компонент Lingui весит в среднем от 58 до 148 КБ в зависимости от схемы загрузки, так как
useLingui()тянет глобальный экземпляр и весь загруженный в него каталог. Тот же компонент с адаптером весит всего 12,8-14,9 КБ: он импортирует только собственные словари и ICU-резолвер. - Гидратация: на 8-14 мс быстрее. Методы
i18n.load()+i18n.activate()выполняются на клиенте до того, как React начнет гидратацию. Чем сильнее разбит Lingui, тем дольше идет инициализация (28-34 мс). С адаптером словари приходят как стандартные импорты, уже включенные бандлером в чанк страницы: 11,3 мс в режимеstatic, 19,7 мс вdynamic. - Переключение языка: в 2 раза быстрее и без просадок. Оптимизированная конфигурация
scoped-dynamicв Lingui тратит 42 мс на обновлениеhtml[lang], поскольку каталог маршрута должен быть запрошен, загружен и активирован. Адаптер стабильно укладывается в 2,9-3,3 мс в обоих режимах. - Простая конфигурация исправляется автоматически. Статический Lingui отдает все каталоги на каждой странице: 152,2 КБ и 90% утечки страниц. Статический адаптер: 140,5 КБ и 0% утечки страниц при тех же самых компонентах.
- Объем на страницу: Lingui выигрывает в
dynamicна 22 КБ. Это важный нюанс. Lingui транслирует сообщения в массивы токенов еще на этапе сборки и использует рантайм весом 11 КБ, который просто обходит эти массивы. Адаптер поставляет резолвер ICU от Intlayer (примерно +15 КБ к коду@intlayer/coreпо сравнению с нативной сборкой), слой адаптера (~10 КБ) иreact-intlayer(~6 КБ). В данном приложении это дает 137,0 КБ против 115,2 КБ. Если ваш единственный ориентир - минимальный вес страницы, и у вас уже настроен ленивый Lingui, адаптер не уменьшит эту цифру. - Утечка локали сопоставима с обеих сторон. 9,3% у Lingui и 9,9% у адаптера в
dynamic. Причина кроется в коде компонентов: вызовi18n._({ id: "careers-benefits.pay", message: "Top-of-market compensation" })содержит английский оригинал в качестве фолбека, как и сгенерированный макросами код, если поле message не удалено. Этот английский текст попадает в чанкfrвне зависимости от используемого рантайма. Нативный Intlayer (.content.ts, без встроенного в код исходного текста) дает ровно 0%.
Почему показатели меняются и почему один остается неизменным
На итоговые значения влияют два фактора: к чему привязан компонент и в каком формате передаются сообщения.
Привязка. В Lingui базовой единицей служит локаль целиком. Файл messages.mjs для fr представляет собой единый модуль; любой компонент, подключающий экземпляр, получает доступ ко всему содержимому, не позволяя сборщику разделить его тоньше. В адаптере единицей является конкретное место вызова: секции hero и footer - это отдельные импорты, которые бандлер делит и подгружает для каждого компонента индивидуально. Отсюда экономия в размере компонентов, гидратации и утечке страниц.
Копировать код в буфер обмена
Копировать код в буфер обмена
Формат. Компилятор Lingui преобразует конструкцию {count, plural, one {# item} other {# items}} в массив токенов, поэтому рантайму не требуется парсить ICU. Адаптер хранит сообщение в виде строки и разбирает его через ICU-резолвер Intlayer. Это создает фиксированные накладные расходы около 15 КБ один раз на страницу. По этой причине строка dynamic уступает по объему байтов, выигрывая по всем остальным метрикам. Нативный Intlayer избегает этого, поскольку в файлах .content.ts узлы enu() / insert() рассчитываются компилятором заранее.
Миграция в три шага
Установка
bashКопировать кодКопировать код в буфер обмена
Команда обнаружит Lingui, изучит
lingui.config.ts, чтобы подключитьsyncPO(для каталогов.po) илиsyncJSON(для каталогов JSON), установит пакетыintlayer,react-intlayer,@intlayer/linguiи требуемый плагин синхронизации, а также заменит@lingui/vite-pluginна плагин адаптера вvite.config.ts. Пакеты@lingui/core,@lingui/reactи плагин макросов удалять не нужно: макросы компилируются штатно, а адаптер опирается на типы Lingui.Настройка связи Intlayer с каталогами
Для каталогов JSON (
format: "minimal"вlingui.config.ts):intlayer.config.tsКопировать кодКопировать код в буфер обмена
Для каталогов
.poзаменитеsyncJSONнаsyncPOиз пакета@intlayer/sync-po-pluginс аналогичным шаблоном в свойствеsourceи расширением.po. Подробнее в документации плагина Sync PO.Параметр
splitKeys: "key-prefix"как раз обеспечивает уменьшение размера компонентов. Сам файл каталога сохраняет линейную структуру, разбивка применяется только к сгенерированным словарям, а обратная синхронизация автоматически объединяет ключи.Подключение плагина
vite.config.tsКопировать кодКопировать код в буфер обмена
Плагин
lingui()оборачиваетvite-intlayer(отслеживание изменений контента, сборка словарей, фаза оптимизации) и настраивает алиасы для перенаправления@lingui/coreи@lingui/reactна адаптер. Запустите сборку, и зафиксированные выше преимущества станут доступны вашему приложению.
Что можно удалить после перехода
Открыть таблицу в модальном окне для четкого просмотра всех данных
| Файл / паттерн | Причина |
|---|---|
await import(`./locales/${locale}/messages.mjs`) | Словари импортируются компонентами напрямую. Метод i18n.load() становится фолбеком | |
i18n.load() / i18n.loadAndActivate() | Оставьте i18n.activate(locale); удалите ручную загрузку каталогов |
lingui compile в сценариях сборки | Только если вы перешли на JSON / .po как источник и больше не импортируете скомпилированные модули |
Что вы получаете помимо экономии байтов
- Поиск пропущенных переводов. Команда
npx intlayer testпрерывает пайплайн CI, если в каком-либо языке отсутствует ключ;lingui extractотображает лишь сводную статистику. npx intlayer fillпереводит недостающие строки с помощью выбранного вами ИИ-провайдера (OpenAI, Anthropic, Mistral, Gemini...) и сохраняет их прямо в исходные каталоги.- Визуальный редактор и CMS работают с теми же словарями, позволяя контент-менеджерам редактировать файлы
.poи JSON через удобный интерфейс. - Плавная миграция на
.content.ts. В любой момент отдельный компонент можно перевести сuseLingui()наuseIntlayer("hero")с выделенным файлом декларации контента. Оба формата словарей прекрасно сосуществуют.
Ограничения, о которых важно знать заранее
- Накладные расходы на страницу в режиме
dynamic. Как отмечалось выше: рассчитывайте примерно на +20 КБ на страницу относительно лениво загружаемого Lingui на небольшом приложении. Эта разница не растет вместе с текстами (она задается резолвером, а не каталогами), но и не сокращается. - Утечка исходной локали сохраняется. Дескрипторы сообщений и сгенерированный макросами код содержат английский оригинал для подстраховки. Для ее устранения потребуется очистить поле
messageили переписать компонент на.content.ts. i18n.load()- это фолбек, а не целевой путь. Если продолжать импортировать скомпилированные каталоги и вызыватьload(), в итоговый бандл попадут и старая, и новая схемы. Удалите эти импорты.- Поддерживается только Vite. Плагин для Next.js в рамках
@intlayer/linguiотсутствует. Проектам на Next.js с Lingui имеет смысл сразу рассмотретьnext-intlayer. - Свойство
defaultComponentне задействуется. Если вы использовали его для оборачивания каждого<Trans>, добавляйте обертку явно в разметку компонентов.
Что выбрать в вашей ситуации?
- Оставайтесь на Lingui, если в проекте уже реализована схема
scoped-dynamic, вашей приоритетной метрикой является минимальный вес страницы в КБ, а переключение языка за 42 мс и гидратация за 30 мс полностью приемлемы для задач проекта. - Выбирайте
@intlayer/lingui, если проект использует Lingui и вам требуются легковесные компоненты, быстрая гидратация и отзывчивая смена локали, отсутствие утечек страниц в простых схемах, типизированные идентификаторы, валидация в CI и автоперевод через ИИ без изменения макросов. Это оптимальный мост для существующей кодовой базы. - Переходите на нативный Intlayer (
react-intlayer), когда начнется плановый рефакторинг компонентов. Это единственное решение в тесте с 0% утечки локали, рантаймом 5 КБ и приростом всего +7,6 КБ на страницу по сравнению с базовым приложением.
Похожие сравнения
- Lingui против Intlayer (сравнение библиотек в рамках того же бенчмарка)
- next-intl против @intlayer/next-intl (материал из серии об адаптерах)
- i18next против @intlayer/i18next (материал из серии об адаптерах)
- vue-i18n против @intlayer/vue-i18n (материал из серии об адаптерах)
- Справочник по адаптеру совместимости: Lingui
- Компилятор против декларативной i18n
Заключение
@intlayer/lingui меняет то, к чему привязываются места вызова Lingui: вместо глобального экземпляра и монолитного каталога локали каждый компонент получает персонально скомпилированный словарь. На приложении TanStack Start это дает уменьшение компонентов в 7 раз, ускорение гидратации на 8-14 мс, в 2 раза более быстрое переключение языка без пауз в 42 мс без необходимости переписывать макросы. Решение не удаляет встроенные запасные строки (утечка исходного языка сохраняется) и парсит ICU на лету (динамическая схема загружает примерно на 20 КБ больше на страницу, чем чистый Lingui). Оцените ваши цели по производительности перед выбором подходящего пути.
Все исходные данные, тестовые стенды и скрипты опубликованы в репозитории Benchmark Bloom. Вы можете повторить эти тесты самостоятельно.
Ознакомьтесь с материалом 'Почему Intlayer?' для получения дополнительной информации.
Комментарии
Пока нет комментариев. Будьте первым, кто поделится своими мыслями.
