Спросите свой вопрос и получите сводку документа, используя эту страницу и выбранного вами поставщика AI
Содержимое этой страницы было переведено с помощью ИИ.
Смотреть последнюю версию оригинального контента на английскомЕсли у вас есть идея по улучшению этой документации, не стесняйтесь внести свой вклад, подав запрос на вытягивание на GitHub.
Ссылка на документацию GitHubКопировать Markdown документа в буфер обмена
next-intl VS @intlayer/next-intl | Один и тот же API, разный Bundle
@intlayer/next-intl - это адаптер совместимости: он предоставляет API next-intl (useTranslations, getTranslations, useLocale, t.rich(), ICU plurals, NextIntlClientProvider...) и обслуживает его из словарей, скомпилированных Intlayer. Код приложения не меняется. Меняется bundle.
Эта статья сравнивает оба варианта на одном Next.js приложении, собранном один раз с next-intl и один раз с адаптером. Цифры взяты из Benchmark Bloom, open-source набора инструментов, который записывает, что на самом деле загружает браузер. Если вам нужно сравнение next-intl и Intlayer как библиотек, прочитайте next-intl vs Intlayer. Здесь речь идёт о том, что меняется в адаптере, когда вы оставляете компоненты как они есть.
tl;dr: На одном и том же приложении Next.js переход сnext-intlна@intlayer/next-intlснизил размер JavaScript на страницу с 153.6 KB до 147.5 KB (gzip), средний компонент с 21.8 KB до 8.1 KB, утечку строк на чужих страницах с ~90% до 0%, и гидратацию с 14.7 ms до 12.8 ms, при этом ни один компонент не был отредактирован. На TanStack Start эквивалентuse-intl(@intlayer/use-intl) сократил компоненты с 76-87 KB до 9-11 KB и переключение локали с 7-21 ms до 4-9 ms. Адаптер занимает 8.0 KB runtime против 14.7 KB дляnext-intlи 5.5 KB для нативногоnext-intlayer. Навигация и middleware переработаны на основе конфига маршрутизации Intlayer; локализованныеpathnames- единственная функция, которая не перенесена.
Что такое @intlayer/next-intl
next-intl - это runtime: getRequestConfig загружает messages/{locale}.json на каждый запрос, NextIntlClientProvider отправляет его на клиент, и useTranslations("about") читает ключи из этого объекта во время рендеринга. Каждая оптимизация (namespaces, pick(messages, [...]) на страницу, ленивая загрузка) - ваша ответственность.
@intlayer/next-intl сохраняет первую и последнюю часть этой цепи и заменяет середину. Ваши компоненты по-прежнему вызывают useTranslations("about"); то, что они получают, берется из словаря Intlayer, скомпилированного во время сборки, ограниченного этим компонентом и активной локалью только.
Три механизма делают это возможным:
- Import aliasing.
createNextIntlPlugin()из@intlayer/next-intl/pluginоборачиваетwithIntlayerи добавляет aliases Webpack / Turbopack так, чтобыnext-intl,next-intl/server,next-intl/navigationиnext-intl/middlewareразрешались в@intlayer/next-intl. Никакие импорты в вашей codebase не переименовываются. - JSON as source of truth. The
syncJSONplugin читает ваши существующиеmessages/{locale}.json, разделяет его top-level ключи на один dictionary per namespace и записывает переводы обратно в те же файлы, когда CLI или CMS их обновляют. Рабочий процесс ваших переводчиков остается неизменным. - Привязка на месте вызова. Оптимизирующий проход Intlayer (Babel или SWC) переписывает
useTranslations("about")в вызов, который получает словарьaboutнапрямую. Компонент больше не обращается к глобальному дереву сообщений; он обращается к своему собственному контенту.
Копировать код в буфер обмена
Копировать код в буфер обмена
Это переписывание объясняет, почему столбцы размера компонента и утечки страницы ниже смещаются: страница загружает только словари компонентов, которые она отображает, и только в обслуживаемой локали.
Что адаптер сохраняет, игнорирует и не заменяет
Открыть таблицу в модальном окне для четкого просмотра всех данных
next-intl API | С @intlayer/next-intl |
|---|---|
useTranslations("ns") / getTranslations("ns") | ✅ Сохранено. Привязано к словарю ns на этапе сборки. Ключи типизированы в соответствии с вашим контентом. |
getTranslations({ locale, namespace }) | ✅ Сохранено |
t("key", { name }), t.rich(), t.markup(), t.raw() | ✅ Сохранено. ICU множественные числа, select, selectordinal, #, {ts, date, long} выполняются через Intlayer's ICU resolver |
useLocale() / getLocale() / setRequestLocale() / setLocale | ✅ Сохранено |
useFormatter() | ✅ Сохранено. dateTime, number, relativeTime, list, dateTimeRange подключаются к нативному Intl |
NextIntlClientProvider | ✅ Сохранено. Props messages, timeZone и now принимаются, но игнорируются (dev warning уведомит вас об этом) |
getMessages() | ✅ Сохранено для совместимости; больше не требуется |
getRequestConfig() в src/i18n.ts | ⚠️ Не требуется. Словари компилируются во время сборки; загрузки сообщений для каждого запроса нет |
defineRouting() | ✅ Сохранено. Опущенные поля (locales, defaultLocale, localePrefix) читаются из intlayer.config.ts |
createNavigation(), Link, redirect, usePathname, useRouter | ✅ Сохранено. Переимплементировано на основе конфигурации маршрутизации Intlayer; аргумент routing принимается, но игнорируется |
pathnames (локализованные имена маршрутов) | ❌ Принимается для типизации, не интерполируется. Сохраняйте простые имена маршрутов или переместите это сопоставление в rewrite Intlayer |
createMiddleware() | ✅ Сохранено. Возвращает прокси Intlayer; устанавливает куку NEXT_LOCALE так, чтобы useLocale() и ваш переключатель продолжали работать |
NEXT_LOCALE кука | ✅ Читается по умолчанию (если вы не настроили routing.storage самостоятельно) |
Bare useTranslations() без namespace | ⚠️ Работает, но сайт вызова не привязан: разрешается через runtime registry. Передайте namespace для получения выигрыша в bundle |
The benchmark
What was measured
Набор тестов 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.
next-intl был создан с четырьмя стратегиями загрузки, от наивного подхода (загрузка целого файла messages/{locale}.json) до оптимального (один namespace на маршрут + per-page pick()). Адаптер был построен на тех же компонентах, что и наивный подход, с измененными только next.config.ts и intlayer.config.ts. У него нет варианта "scoped": компилятор определяет содержимое на уровне компонента, поэтому его строки static и dynamic уже являются scoped.
Для каждой сборки suite записывает:
- Lib size: gzip размер пустого компонента, который только импортирует i18n библиотеку. Фиксированная стоимость runtime.
- Page JS: gzip JavaScript, загруженный на одну страницу, усредненный по всем страницам и локалям.
- Locale leak %: доля переведённых строк, найденных в загруженном JS, которые принадлежат локали, которую пользователь не просматривает.
- Page leak %: доля переведённых строк, найденных в загруженном JS, которые принадлежат странице, на которой пользователь не находится.
- Component avg: средний размер gzip каждого компонента, скомпилированного отдельно. Показывает, сколько i18n runtime и каталога тащит за собой один компонент.
- E2E reactivity: время, прошедшее между выбором новой локали и обновлением
html[lang]в DOM (Playwright, 5 итераций). - Hydration: длительность фазы гидратации React.
Приведённые ниже числа получены из прогона от 2026-09-12 сnext-intl/use-intl4.14.2 и@intlayer/*9.5.1. Тестовое приложение намеренно небольшое (несколько десятков строк на локаль), поэтому процентные показатели утечки описывают закономерность: они растут с расширением вашего контента, а стоимость runtime остаётся неизменной.
Результаты на Next.js
Открыть таблицу в модальном окне для четкого просмотра всех данных
| Setup | Strategy | Lib size (gz) | Page JS avg (gz) | Locale leak | Page leak | Component avg (gz) | E2E reactivity | Hydration |
|---|---|---|---|---|---|---|---|---|
| base (no i18n) | - | 0.0 KB | 141.0 KB | 0.0% | 0.0% | 0.9 KB | 13.4 ms | 11.8 ms |
next-intl | static | 14.7 KB | 153.6 KB | 4.2% | 89.8% | 21.8 KB | 16.0 ms | 14.7 ms |
next-intl | dynamic | 14.7 KB | 153.6 KB | 9.7% | 89.9% | 21.8 KB | 15.6 ms | 14.8 ms |
next-intl | scoped-static | 14.7 KB | 153.6 KB | 0.0% | 0.0% | 80.1 KB | 17.9 ms | 17.4 ms |
next-intl | scoped-dynamic | 14.7 KB | 153.6 KB | 0.0% | 0.0% | 22.9 KB | 17.8 ms | 16.8 ms |
@intlayer/next-intl | static | 8.0 KB | 147.5 KB | 0.0% | 0.0% | 8.1 KB | 14.5 ms | 12.8 ms |
@intlayer/next-intl | dynamic | 8.0 KB | 148.7 KB | 0.0% | 0.0% | 8.1 KB | 11.7 ms | 12.8 ms |
next-intlayer (native) | static | 5.5 KB | 141.3 KB | 0.0% | 0.0% | 8.5 KB | 15.5 ms | 16.9 ms |
next-intlayer (native) | dynamic | 5.5 KB | 141.3 KB | 0.0% | 0.0% | 6.9 KB | 15.3 ms | 15.9 ms |
Как это читать
- Те же компоненты, 6 KB меньше на каждую страницу. Сборка адаптера наивного приложения приземляется на 147.5 KB, ниже каждой конфигурации
next-intl, включая полностью оптимизированную (153.6 KB). Сам runtime - это разница: 8.0 KB против 14.7 KB, оплачиваемая на каждой странице. - Утечка данных составляет 0% без изменения компонентов. Наивная конфигурация
next-intlдоставляет ~90% строк иностранных страниц на каждую страницу. Достижение 0% сnext-intlозначает использование конфигурацийscoped-*: одно пространство имён на маршрут иpick(messages, [...])на каждой странице. Адаптер достигает 0% из наивного кода, потому что проход оптимизации привязывает каждыйuseTranslations("ns")к своему собственному словарю. - Компоненты сжимаются в 2,7 раза. Компонент, скомпилированный изолированно, в среднем весит 21,8 КБ с
next-intl(он достигает провайдера и дерева сообщений) и 8,1 КБ с адаптером. В конфигурацииscoped-staticnext-intlэто число увеличивается до 80 КБ, потому что файл пространства имён каждого маршрута становится доступным со страницы, которая его выбирает. - Гидратация на 2 мс быстрее (12.8 против 14.7 мс): нет необходимости десериализовать объект сообщения из RSC payload перед тем, как React может выполнить гидратацию.
- Адаптер не является нативным runtime.
next-intlayerзанимает 141.3 KB, +0.3 KB относительно базового приложения, с runtime 5.5 KB. Адаптер предоставляет API поверхностьnext-intl(useFormatter,t.rich, ICU resolver) поверх ядра Intlayer, отсюда 8.0 KB и +6 KB на страницу. Это мост, а не пункт назначения.
Результаты на TanStack Start (use-intl)
use-intl - это framework-агностическое ядро next-intl. Его адаптер, @intlayer/use-intl, следует той же архитектуре с Vite плагином (@intlayer/use-intl/plugin).
Открыть таблицу в модальном окне для четкого просмотра всех данных
| Настройка | Стратегия | Размер lib (gz) | Средний JS страницы (gz) | Утечка локали | Утечка страницы | Средний компонент (gz) | E2E отзывчивость | Гидратация |
|---|---|---|---|---|---|---|---|---|
| base (без i18n) | - | 0.0 KB | 111.0 KB | 0.0% | 0.0% | 0.7 KB | 8.1 ms | 21.6 ms |
use-intl | static | 14.1 KB | 179.8 KB | 50.0% | 89.8% | 76.0 KB | 6.7 ms | 15.3 ms |
use-intl | dynamic | 14.1 KB | 119.4 KB | 0.0% | 89.8% | 75.9 KB | 7.0 ms | 15.4 ms |
use-intl | scoped-static | 14.1 KB | 128.7 KB | 0.0% | 0.0% | 87.1 KB | 20.9 ms | 24.8 ms |
use-intl | scoped-dynamic | 14.1 KB | 128.7 KB | 0.0% | 0.0% | 87.1 KB | 13.3 ms | 25.9 ms |
@intlayer/use-intl | static | 7.3 KB | 135.8 KB | 49.7% | 0.0% | 10.9 KB | 4.2 ms | 10.5 ms |
@intlayer/use-intl | dynamic | 7.3 KB | 129.7 KB | 0.0% | 0.0% | 9.3 KB | 8.7 ms | 16.1 ms |
intlayer (native) | static | 5.0 KB | 125.8 KB | 50.0% | 0.0% | 8.1 KB | 3.2 ms | 11.5 ms |
intlayer (native) | dynamic | 5.0 KB | 118.6 KB | 0.0% | 0.0% | 6.3 KB | 3.6 ms | 14.1 ms |
Как читать таблицу
- Байты на странице примерно равны оптимизированному
use-intl.@intlayer/use-intlв режимеdynamic(129.7 KB) находится в пределах 1 KB отuse-intl'sscoped-dynamic(128.7 KB), и на 10 KB выше простогоdynamicuse-intl(119.4 KB). Эта простая строкаdynamicпо-прежнему утекает 90% строк со страниц на других языках; количество байт низко, потому что содержимое тестового приложения небольшое. Адаптер 0% остается неизменным при увеличении содержимого. - Компоненты на 7-9x меньше. Компоненты
use-intlв среднем занимают 76-87 KB при любой стратегии, потому чтоuseTranslationsпривязан к полному объекту сообщений провайдера. Адаптер в среднем занимает 9-11 KB. - Переключение локалей работает быстрее. Оптимизированные настройки
use-intlзанимают 13-21 ms для обновленияhtml[lang]; адаптер занимает 4-9 ms. Меньше компонентов перерисовывается, и ничего не переходит из дерева сообщений. staticсохраняет каждую локаль. Строкаstaticадаптера показывает утечку локалей на 49.7%, как и native Intlayer в режимеstatic: все локали объединены, но используются только словари страницы. Одна строка конфига (importMode: 'dynamic') это убирает.
Почему цифры меняются
Ничего в компоненте не изменилось, поэтому прирост производительности полностью зависит от того, к чему привязана useTranslations.
With next-intl, the binding is the provider. NextIntlClientProvider receives the whole messages object for the locale; every useTranslations("about") reads from it. The bundler sees one component importing one hook that reads one context, and cannot know that only the about branch is used. The routes below all share the same message object, so the page-leak column reads ~90% until you split the file yourself.
Копировать код в буфер обмена
С @intlayer/next-intl привязка осуществляется к словарю. syncJSON преобразует messages/en.json в один словарь на каждый ключ верхнего уровня; компилятор разрешает, какой компонент вызывает useTranslations("about"), и передает ему about напрямую на активном языке как импорт, который bundler может отследить и разделить.
Копировать код в буфер обмена
src/i18n.ts и свойство messages удаляются. Всё остальное остаётся идентичным.
Миграция в три шага
Установка
bashКопировать кодКопировать код в буфер обмена
Команда обнаруживает
next-intlи устанавливаетintlayer,next-intlayer,@intlayer/next-intlи@intlayer/sync-json-plugin. Сохранитеnext-intlустановленным: это peer dependency адаптера и предоставляет типы.Укажите Intlayer на ваши сообщения
intlayer.config.tsКопировать кодКопировать код в буфер обмена
messages/{locale}.jsonостаётся на месте. Каждый ключ верхнего уровня становится словарём;useTranslations("about")соответствует словарюabout.Обёртка next.config.ts
next.config.tsКопировать кодКопировать код в буфер обмена
createNextIntlPlugin()компонуетwithIntlayer(отслеживание контента, компиляция словарей, оптимизирующий проход) и aliasesnext-intl→@intlayer/next-intlдля Webpack и Turbopack. Постройте проект, и числа в таблицах выше будут вашими.
Что вы можете удалить впоследствии
Открыть таблицу в модальном окне для четкого просмотра всех данных
| Файл / шаблон | Причина |
|---|---|
getRequestConfig в src/i18n.ts | Нет загрузки сообщений на каждый запрос. Оставьте файл только если он также экспортирует helpers createNavigation |
messages={...} на NextIntlClientProvider | Адаптер читает скомпилированный output; свойство игнорируется и выводит предупреждение в разработке |
await getMessages() в layouts | По той же причине |
Per-page pick(messages, [...]) | Компилятор выполняет выборку, для каждого компонента |
Что вы получаете сверх оптимизации размера
- Типизированные ключи.
useTranslations("about")типизирована против скомпилированного словаряabout.t("does.not.exist")— это ошибка TypeScript, а не fallback во время выполнения. npx intlayer testблокирует CI, когда в какой-то локали отсутствует ключ.npx intlayer fillпереводит отсутствующие ключи с помощью выбранного провайдера (OpenAI, Anthropic, Mistral, Gemini...) используя ваш собственный ключ и записывает результат обратно вmessages/{locale}.json.- Visual Editor и CMS работают с одними и теми же словарями, поэтому не-разработчики могут редактировать
messages/fr.jsonчерез UI, и файл обновляется. - Постепенный переход на
.content.ts. Любой компонент может переключиться сuseTranslations("about")наuseIntlayer("about")с co-located content файлом, по одному за раз. JSON и.content.tsсловари сосуществуют и объединяются.
Ограничения, которые нужно знать перед началом
- Routing config переходит в
intlayer.config.ts.createNavigation(routing)иcreateMiddleware(routing)сохраняют свою сигнатуру, но игнорируют аргумент: локали, локаль по умолчанию и стратегия префикса поступают из конфига Intlayer'srouting. Если вы используете локализованныеpathnamesотnext-intl(/about→/a-propos), адаптер их не интерполирует;intlayer.routing.rewriteпокрывает этот случай, но это отдельное изменение. useTranslations()без namespace не привязан. Этап оптимизации требует статический namespace, чтобы знать, какой dictionary импортировать. Прямой вызов все еще работает через registry во время выполнения, который ссылается на каждый dictionary, что точно утечка, которую вы пытались избежать. Передайте namespace.- Адаптер не бесплатен. 8.0 KB runtime против 5.5 KB для
next-intlayerи +6-7 KB на страницу по сравнению с native build. Это цена за API surfacenext-intl. Если вы дошли до точки, где каждый компонент был перемещен наuseIntlayer, отказывайтесь от адаптера. messages,timeZone,nowна провайдере игнорируются. Форматеры поддерживаются нативнымIntlи только locale влияет на их output; если вы полагаетесь на forced time zone или fixednowдля hydration-stable дат, обработайте это на call site.
Когда использовать что?
- Оставайтесь на
next-intlесли ваше приложение небольшое, bundle не вызывает беспокойства, и ваша команда комфортно владеет namespaces иpick()на каждой странице. - Используйте
@intlayer/next-intl, если вы сейчас работаете сnext-intlи хотите получить преимущества в размере bundle, устранение утечек и гидрации, типизированные ключи и инструменты CLI / CMS без переписывания. Это рекомендуемая точка входа для любого существующегоnext-intlcodebase. - Перейдите на native (
next-intlayer) для новых проектов или после того, как adapter выполнит свою работу. Это самый легкий из трех (5.5 KB, +0.3 KB за страницу) и разблокирует синхронные server components,.content.tsфайлы per-component и полный набор функций.
Связанные сравнения
- next-intl vs Intlayer (библиотеки, одинаковый benchmark)
- i18next vs @intlayer/i18next (одна серия adapter)
- Lingui vs @intlayer/lingui (одна серия адаптеров)
- vue-i18n vs @intlayer/vue-i18n (одна серия адаптеров)
- Руководство по миграции: next-intl на Intlayer
- Справочник адаптера совместимости: next-intl
Заключение
@intlayer/next-intl делает одно: изменяет, к чему привязан useTranslations, от провайдера, содержащего все сообщения, к словарю, скомпилированному для этого компонента. На том же Next.js приложении, которое стоит 6 KB на страницу, компоненты в 2,7 раза меньше, 0% утечек и 2 мс гидратации, прежде чем кто-либо откроет файл компонента. Навигация и middleware сохраняют свой API поверх конфига маршрутизации Intlayer, а нативный runtime next-intlayer остается еще более легким.
Все исходные данные, тестовые приложения и скрипты находятся в репозитории Benchmark Bloom. Запустите это самостоятельно.
Дополнительные сведения см. в документации 'Why Intlayer?'.
Комментарии
Пока нет комментариев. Будьте первым, кто поделится своими мыслями.
