@intlayer/docs 8.7.5-canary.0 → 8.7.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/blog/de/next-i18next_vs_next-intl_vs_intlayer.md +0 -2
  2. package/blog/en-GB/next-i18next_vs_next-intl_vs_intlayer.md +0 -2
  3. package/blog/es/next-i18next_vs_next-intl_vs_intlayer.md +0 -2
  4. package/blog/fr/next-i18next_vs_next-intl_vs_intlayer.md +0 -2
  5. package/blog/id/list_i18n_technologies/frameworks/svelte.md +0 -2
  6. package/blog/it/next-i18next_vs_next-intl_vs_intlayer.md +0 -2
  7. package/blog/ja/next-i18next_vs_next-intl_vs_intlayer.md +0 -2
  8. package/blog/ko/next-i18next_vs_next-intl_vs_intlayer.md +0 -2
  9. package/blog/pl/list_i18n_technologies/frameworks/svelte.md +0 -2
  10. package/blog/pt/next-i18next_vs_next-intl_vs_intlayer.md +0 -2
  11. package/blog/ru/next-i18next_vs_next-intl_vs_intlayer.md +0 -2
  12. package/blog/vi/list_i18n_technologies/frameworks/svelte.md +0 -2
  13. package/blog/zh/next-i18next_vs_next-intl_vs_intlayer.md +0 -2
  14. package/dist/cjs/generated/docs.entry.cjs +60 -0
  15. package/dist/cjs/generated/docs.entry.cjs.map +1 -1
  16. package/dist/esm/generated/docs.entry.mjs +60 -0
  17. package/dist/esm/generated/docs.entry.mjs.map +1 -1
  18. package/dist/types/generated/docs.entry.d.ts +3 -0
  19. package/dist/types/generated/docs.entry.d.ts.map +1 -1
  20. package/docs/ar/benchmark/index.md +29 -0
  21. package/docs/ar/benchmark/nextjs.md +227 -0
  22. package/docs/ar/benchmark/tanstack.md +193 -0
  23. package/docs/ar/intlayer_with_tanstack.md +0 -2
  24. package/docs/de/benchmark/index.md +29 -0
  25. package/docs/de/benchmark/nextjs.md +227 -0
  26. package/docs/de/benchmark/tanstack.md +193 -0
  27. package/docs/en/benchmark/___NOTE.md +82 -0
  28. package/docs/en/benchmark/___nextjs.md +195 -0
  29. package/docs/en/benchmark/___tanstack.md +187 -0
  30. package/docs/en/benchmark/index.md +29 -0
  31. package/docs/en/benchmark/nextjs.md +228 -0
  32. package/docs/en/benchmark/tanstack.md +217 -0
  33. package/docs/en-GB/benchmark/index.md +29 -0
  34. package/docs/en-GB/benchmark/nextjs.md +228 -0
  35. package/docs/en-GB/benchmark/tanstack.md +193 -0
  36. package/docs/es/benchmark/index.md +29 -0
  37. package/docs/es/benchmark/nextjs.md +226 -0
  38. package/docs/es/benchmark/tanstack.md +193 -0
  39. package/docs/fr/benchmark/index.md +29 -0
  40. package/docs/fr/benchmark/nextjs.md +227 -0
  41. package/docs/fr/benchmark/tanstack.md +193 -0
  42. package/docs/hi/benchmark/index.md +29 -0
  43. package/docs/hi/benchmark/nextjs.md +227 -0
  44. package/docs/hi/benchmark/tanstack.md +193 -0
  45. package/docs/id/benchmark/index.md +29 -0
  46. package/docs/id/benchmark/nextjs.md +227 -0
  47. package/docs/id/benchmark/tanstack.md +193 -0
  48. package/docs/id/intlayer_with_react_native+expo.md +0 -2
  49. package/docs/it/benchmark/index.md +29 -0
  50. package/docs/it/benchmark/nextjs.md +227 -0
  51. package/docs/it/benchmark/tanstack.md +193 -0
  52. package/docs/ja/benchmark/index.md +29 -0
  53. package/docs/ja/benchmark/nextjs.md +227 -0
  54. package/docs/ja/benchmark/tanstack.md +193 -0
  55. package/docs/ko/benchmark/index.md +29 -0
  56. package/docs/ko/benchmark/nextjs.md +227 -0
  57. package/docs/ko/benchmark/tanstack.md +193 -0
  58. package/docs/ko/intlayer_with_tanstack.md +0 -2
  59. package/docs/pl/benchmark/index.md +29 -0
  60. package/docs/pl/benchmark/nextjs.md +227 -0
  61. package/docs/pl/benchmark/tanstack.md +193 -0
  62. package/docs/pt/benchmark/index.md +29 -0
  63. package/docs/pt/benchmark/nextjs.md +227 -0
  64. package/docs/pt/benchmark/tanstack.md +193 -0
  65. package/docs/ru/benchmark/index.md +29 -0
  66. package/docs/ru/benchmark/nextjs.md +227 -0
  67. package/docs/ru/benchmark/tanstack.md +193 -0
  68. package/docs/tr/benchmark/index.md +29 -0
  69. package/docs/tr/benchmark/nextjs.md +227 -0
  70. package/docs/tr/benchmark/tanstack.md +193 -0
  71. package/docs/uk/benchmark/index.md +29 -0
  72. package/docs/uk/benchmark/nextjs.md +227 -0
  73. package/docs/uk/benchmark/tanstack.md +193 -0
  74. package/docs/vi/benchmark/index.md +29 -0
  75. package/docs/vi/benchmark/nextjs.md +227 -0
  76. package/docs/vi/benchmark/tanstack.md +193 -0
  77. package/docs/zh/benchmark/index.md +29 -0
  78. package/docs/zh/benchmark/nextjs.md +227 -0
  79. package/docs/zh/benchmark/tanstack.md +193 -0
  80. package/package.json +6 -6
  81. package/src/generated/docs.entry.ts +60 -0
@@ -0,0 +1,227 @@
1
+ ---
2
+ createdAt: 2026-04-20
3
+ updatedAt: 2026-04-21
4
+ title: Лучшее решение i18n для Next.js в 2026 году - Отчет о бенчмарке
5
+ description: Сравните библиотеки интернационализации (i18n) для Next.js, такие как next-intl, next-i18next и Intlayer. Подробный отчет о производительности, размере бандла, утечках и реактивности.
6
+ keywords:
7
+ - benchmark
8
+ - i18n
9
+ - intl
10
+ - nextjs
11
+ - производительность
12
+ - intlayer
13
+ slugs:
14
+ - doc
15
+ - benchmark
16
+ - nextjs
17
+ author: Aymeric PINEAU
18
+ applicationTemplate: https://github.com/intlayer-org/benchmark-i18n
19
+ history:
20
+ - version: 8.7.5
21
+ date: 2026-01-06
22
+ changes: "Инициализация бенчмарка"
23
+ ---
24
+
25
+ # Библиотеки i18n для Next.js — Отчет о бенчмарке 2026
26
+
27
+ Эта страница представляет собой отчет о бенчмарке i18n-решений для Next.js.
28
+
29
+ ## Содержание
30
+
31
+ <Toc/>
32
+
33
+ ## Интерактивный бенчмарк
34
+
35
+ <I18nBenchmark framework="nextjs" vertical/>
36
+
37
+ ## Ссылка на результаты:
38
+
39
+ <iframe
40
+ src="https://intlayer.org/markdown?url=https%3A%2F%2Fraw.githubusercontent.com%2Fintlayer-org%2Fbenchmark-i18n%2Fmain%2Freport%2Fscripts%2Fsummarize-nextjs.md"
41
+ width="100%"
42
+ height="600px"
43
+ style="border:none;">
44
+ </iframe>
45
+
46
+ > https://intlayer.org/markdown?url=https%3A%2F%2Fraw.githubusercontent.com%2Fintlayer-org%2Fbenchmark-i18n%2Fmain%2Freport%2Fscripts%2Fsummarize-nextjs.md
47
+
48
+ Полный репозиторий бенчмарка можно найти [здесь](https://github.com/intlayer-org/benchmark-i18n).
49
+
50
+ ## Введение
51
+
52
+ Библиотеки интернационализации оказывают серьезное влияние на ваше приложение. Основной риск заключается в загрузке контента для каждой страницы и каждого языка, когда пользователь посещает только одну страницу.
53
+
54
+ По мере роста вашего приложения размер бандла может расти экспоненциально, что может заметно снизить производительность.
55
+
56
+ Например, в худших случаях после интернационализации ваша страница может стать почти в 4 раза больше.
57
+
58
+ Еще одним последствием использования библиотек i18n является замедление разработки. Превращение компонентов в мультиязычный контент на разных языках отнимает много времени.
59
+
60
+ Поскольку проблема сложна, существует множество решений — одни ориентированы на DX, другие на производительность или масштабируемость и так далее.
61
+
62
+ Intlayer пытается оптимизировать все эти аспекты.
63
+
64
+ ## Проверьте свое приложение
65
+
66
+ Чтобы выявить эти проблемы, я создал бесплатный сканер, который вы можете попробовать [здесь](https://intlayer.org/i18n-seo-scanner).
67
+
68
+ <iframe src="https://intlayer.org/i18n-seo-scanner" width="100%" height="600px" style="border:none;"/>
69
+
70
+ ## Проблема
71
+
72
+ Существует два основных способа ограничить влияние мультиязычного приложения на ваш бандл:
73
+
74
+ - Разделение вашего JSON (или контента) по файлам / переменным / пространствам имен (namespaces), чтобы сборщик мог исключить (tree-shake) неиспользуемый контент для конкретной страницы.
75
+ - Динамическая загрузка контента страницы только на языке пользователя.
76
+
77
+ Технические ограничения этих подходов:
78
+
79
+ **Динамическая загрузка**
80
+
81
+ Даже если вы объявляете маршруты типа `[locale]/page.tsx`, с Webpack или Turbopack, и даже если определен `generateStaticParams`, сборщик не рассматривает `locale` как статическую константу. Это означает, что он может включить контент для всех языков в каждую страницу. Основной способ ограничить это — загружать контент через динамический импорт (например, `import('./locales/${locale}.json')`).
82
+
83
+ На этапе сборки Next.js создает один JS-бандл на локаль (например, `./locales_fr_12345.js`). Когда сайт отправляется клиенту и страница запускается, браузер выполняет дополнительный HTTP-запрос для необходимого JS-файла (например, `./locales_fr_12345.js`).
84
+
85
+ > Другой способ решения той же проблемы — использование `fetch()` для динамической загрузки JSON. Так работает `Tolgee`, когда JSON находится в `/public`, или `next-translate`, который полагается на `getStaticProps` для загрузки контента. Процесс тот же: браузер делает дополнительный HTTP-запрос для загрузки ресурса.
86
+
87
+ **Разделение контента**
88
+
89
+ Если вы используете синтаксис типа `const t = useTranslation()` + `t('my-object.my-sub-object.my-key')`, весь JSON обычно должен быть в бандле, чтобы библиотека могла его проанализировать и разрешить ключ. Большая часть этого контента попадает в сборку, даже если он не используется на странице.
90
+
91
+ Чтобы смягчить это, некоторые библиотеки просят вас указывать для каждой страницы, какие пространства имен загружать — например, `next-i18next`, `next-intl`, `lingui`, `next-translate`, `next-international`.
92
+
93
+ Напротив, `Paraglide` добавляет дополнительный шаг перед сборкой, чтобы превратить JSON в плоские символы, такие как `const en_my_var = () => 'my value'`. Теоретически это позволяет исключать неиспользуемый контент на странице. Как мы увидим, у этого метода все же есть свои компромиссы.
94
+
95
+ Наконец, `Intlayer` применяет оптимизацию на этапе сборки, поэтому `useIntlayer('my-key')` заменяется на соответствующий контент напрямую.
96
+
97
+ ## Методология
98
+
99
+ Для этого бенчмарка мы сравнили следующие библиотеки:
100
+
101
+ - `Base App` (Без библиотеки i18n)
102
+ - `next-intlayer` (v8.7.5)
103
+ - `next-i18next` (v16.0.5)
104
+ - `next-intl` (v4.9.1)
105
+ - `@lingui/core` (v5.3.0)
106
+ - `next-translate` (v3.1.2)
107
+ - `next-international` (v1.3.1)
108
+ - `@inlang/paraglide-js` (v2.15.1)
109
+ - `tolgee` (v7.0.0)
110
+ - `@lingo.dev/compiler` (v0.4.0)
111
+ - `wuchale` (v0.22.11)
112
+ - `gt-next` (v6.16.5)
113
+
114
+ Я использовал `Next.js` версии `16.2.4` с App Router.
115
+
116
+ Я построил мультиязычное приложение с **10 страницами** и **10 языками**.
117
+
118
+ Я сравнил **четыре стратегии загрузки**:
119
+
120
+ | Стратегия | Без пространств имен (глобальная) | С пространствами имен (локальная/scoped) |
121
+ | :------------------------ | :---------------------------------------------- | :------------------------------------------------------------------------------- |
122
+ | **Статическая загрузка** | **Static**: Все в памяти при запуске. | **Scoped static**: Разделено по пространствам имен; все загружается при запуске. |
123
+ | **Динамическая загрузка** | **Dynamic**: Загрузка по требованию для локали. | **Scoped dynamic**: Гранулярная загрузка по пространствам имен и локалям. |
124
+
125
+ ## Резюме стратегий
126
+
127
+ - **Статическая (Static)**: Простота; отсутствие задержек сети после начальной загрузки. Минус: большой размер бандла.
128
+ - **Динамическая (Dynamic)**: Уменьшает начальный вес (ленивая загрузка). Идеально при наличии множества локалей.
129
+ - **Локальная статическая (Scoped static)**: Позволяет организовать код (логическое разделение) без сложных дополнительных сетевых запросов.
130
+ - **Локальная динамическая (Scoped dynamic)**: Лучший подход для _разделения кода_ и производительности. Минимизирует использование памяти, загружая только то, что нужно для текущего представления и активной локали.
131
+
132
+ ### Что я измерял:
133
+
134
+ Я запускал одно и то же мультиязычное приложение в реальном браузере для каждого стека, а затем записывал, что на самом деле передавалось по сети и сколько времени это занимало. Размеры указаны **после обычного веб-сжатия**, так как это ближе к тому, что скачивают пользователи, чем количество исходного кода.
135
+
136
+ - **Размер библиотеки интернационализации**: После сборки, tree-shaking и минимизации, размер i18n-библиотеки — это размер провайдеров (например, `NextIntlClientProvider`) + код хуков (например, `useTranslations`) в пустом компоненте. Это не включает загрузку файлов перевода. Это ответ на вопрос, насколько "дорогая" библиотека сама по себе до добавления вашего контента.
137
+
138
+ - **JavaScript на страницу**: Для каждого маршрута бенчмарка — сколько скриптов браузер загружает при посещении, усредненное по страницам в наборе (и по локалям, где отчет их объединяет). Тяжелые страницы — это медленные страницы.
139
+
140
+ - **Утечка из других локалей**: Это контент той же страницы, но на другом языке, который ошибочно загружается на проверяемую страницу. Этого контента следует избегать (например, контент страницы `/fr/about` в бандле страницы `/en/about`).
141
+
142
+ - **Утечка из других маршрутов**: Та же идея для **других экранов** в приложении: попадает ли их текст в сборку, когда вы открыли только одну страницу (например, контент страницы `/en/about` в бандле страницы `/en/contact`). Высокий показатель указывает на плохое разделение или слишком широкие бандлы.
143
+
144
+ - **Средний размер бандла компонента**: Общие элементы пользовательского интерфейса измеряются **по отдельности**, а не прячутся внутри одного гигантского числа приложения. Это показывает, не раздувает ли интернационализация обычные компоненты. Например, если ваш компонент перерендеривается, он будет загружать все эти данные из памяти. Привязка гигантского JSON-файла к любому компоненту подобна подключению большого хранилища неиспользуемых данных, которое замедлит работу ваших компонентов.
145
+
146
+ - **Скорость переключения языка**: Я переключаю язык с помощью собственного элемента управления приложения и засекаю время, пока страница явно не переключится — то, что заметит посетитель, а не лабораторный микро-шаг.
147
+
148
+ - **Работа по рендерингу после смены языка**: Более узкое наблюдение: сколько усилий потребовалось интерфейсу для перерисовки на новом языке после начала переключения. Полезно, когда "ощущаемое" время и стоимость фреймворка расходятся.
149
+
150
+ - **Время начальной загрузки страницы**: От перехода до того момента, когда браузер считает страницу полностью загруженной для протестированных сценариев. Хорошо для сравнения "холодных стартов".
151
+
152
+ - **Время гидратации**: Когда приложение предоставляет такие данные — сколько времени клиент тратит на превращение серверного HTML в нечто, на что можно нажать. Прочерк в таблицах означает, что данная реализация не предоставила надежных данных по гидратации в этом бенчмарке.
153
+
154
+ ## Результаты в деталях
155
+
156
+ ### 1 — Решения, которых следует избегать
157
+
158
+ Некоторых решений, таких как `gt-next` или `lingo.dev`, явно лучше избегать. Они сочетают в себе привязку к вендору с загрязнением вашей кодовой базы. Несмотря на многие часы попыток их внедрить, мне так и не удалось заставить их работать — ни на TanStack Start, ни на Next.js.
159
+
160
+ Встреченные проблемы:
161
+
162
+ **(General Translation)** (`gt-next@6.16.5`):
163
+
164
+ - Для приложения весом 110 КБ `gt-react` добавляет более 440 КБ сверху.
165
+ - `Quota Exceeded, please upgrade your plan` (Квота превышена, пожалуйста, обновите тарифный план) при самой первой сборке с General Translation.
166
+ - Переводы не отображаются; я получаю ошибку `Error: <T> used on the client-side outside of <GTProvider>`, что кажется багом библиотеки.
167
+ - При внедрении **gt-tanstack-start-react** я также столкнулся с [проблемой](https://github.com/generaltranslation/gt/issues/1210#event-24510646961) в библиотеке: `does not provide an export named 'printAST' - @formatjs/icu-messageformat-parser`, которая приводила к поломке приложения. После сообщения об этой проблеме сопровождающий исправил ее в течение 24 часов.
168
+ - Библиотека блокирует статический рендеринг страниц Next.js.
169
+
170
+ **(Lingo.dev)** (`@lingo.dev/compiler@0.4.0`):
171
+
172
+ - Квота AI превышена, что полностью блокирует сборку — вы не можете отправить приложение в продакшн без оплаты.
173
+ - Компилятор пропустил почти 40% переведенного контента. Мне пришлось переписать все вызовы `.map` в плоские блоки компонентов, чтобы это заработало.
174
+ - Их CLI работает с ошибками и периодически сбрасывает конфигурационный файл без причины.
175
+ - При сборке он полностью удалял сгенерированные JSON-файлы при добавлении нового контента. В результате несколько ключей могли стереть более 300 существующих ключей.
176
+
177
+ ### 2 — Экспериментальные решения
178
+
179
+ **(Wuchale)** (`wuchale@0.22.11`):
180
+
181
+ Идея `Wuchale` интересна, но проект пока не жизнеспособен. Я столкнулся с проблемами реактивности и был вынужден принудительно перерендеривать провайдер, чтобы приложение заработало. Документация также довольно неясная, что затрудняет начало работы.
182
+
183
+ **(Paraglide)** (`@inlang/paraglide-js@2.15.1`):
184
+
185
+ `Paraglide` предлагает инновационный, хорошо продуманный подход. Тем не менее, в этом бенчмарке разрекламированный tree-shaking не сработал для моих настроек Next.js или TanStack Start. Процесс работы и DX сложнее, чем у других вариантов.
186
+ Лично мне не нравится необходимость перегенерировать JS-файлы перед каждым пушем, что создает постоянный риск конфликтов слияния в PR. Инструмент также кажется более ориентированным на Vite, чем на Next.js.
187
+ Наконец, по сравнению с другими решениями, Paraglide не использует хранилище (например, React context) для получения текущей локали для рендеринга контента. Для каждого обрабатываемого узла он запрашивает локаль из localStorage / куки и т.д. Это приводит к выполнению ненужной логики, которая влияет на реактивность компонентов.
188
+
189
+ ### 3 — Приемлемые решения
190
+
191
+ **(Tolgee)** (`tolgee@7.0.0`):
192
+
193
+ `Tolgee` решает многие из упомянутых ранее проблем. Мне показалось, что его сложнее внедрить, чем аналогичные инструменты. Он не обеспечивает типобезопасность, что также затрудняет отлов отсутствующих ключей на этапе компиляции. Мне пришлось обернуть функции Tolgee своими собственными, чтобы добавить обнаружение отсутствующих ключей.
194
+
195
+ **(Next Intl)** (`next-intl@4.9.1`):
196
+
197
+ `next-intl` — самый модный вариант, который чаще всего продвигают AI-агенты, но, на мой взгляд, ошибочно. Начать работу легко. На практике оптимизация для ограничения утечек сложна. Сочетание динамической загрузки + пространств имен + типов TypeScript сильно замедляет разработку. Пакет также довольно тяжелый (~13 КБ для `NextIntlClientProvider` + `useTranslations`, что более чем в 2 раза больше `next-intlayer`). **next-intl** раньше блокировал статический рендеринг страниц Next.js. Он предоставляет помощник под названием `setRequestLocale()`. Кажется, это частично решено для централизованных файлов типа `en.json` / `fr.json`, но статический рендеринг все равно ломается, когда контент разделен на пространства имен, такие как `en/shared.json` / `fr/shared.json` / `es/shared.json`.
198
+
199
+ **(Next I18next)** (`next-i18next@16.0.5`):
200
+
201
+ `next-i18next`, вероятно, является самым популярным вариантом, так как был одним из первых i18n-решений для JavaScript-приложений. У него много плагинов от сообщества. У него те же основные недостатки, что и у `next-intl`. Пакет особенно тяжелый (~18 КБ для `I18nProvider` + `useTranslation`, примерно в 3 раза тяжелее `next-intlayer`).
202
+
203
+ Форматы сообщений также различаются: `next-intl` использует ICU MessageFormat, тогда как `i18next` использует свой собственный формат.
204
+
205
+ **(Next International)** (`next-international@1.3.1`):
206
+
207
+ `next-international` также решает вышеуказанные проблемы, но не сильно отличается от `next-intl` или `next-i18next`. Он включает `scopedT()` для переводов внутри пространств имен, но его использование практически не влияет на размер бандла.
208
+
209
+ **(Lingui)** (`@lingui/core@5.3.0`):
210
+
211
+ `Lingui` часто хвалят. Лично мне рабочий процесс с `lingui extract` / `lingui compile` показался более сложным, чем у альтернатив, без явных преимуществ. Я также заметил непоследовательный синтаксис, который путает AI (например, `t()`, `t''`, `i18n.t()`, `<Trans>`).
212
+
213
+ ### 4 — Рекомендации
214
+
215
+ **(Next Translate)** (`next-translate@3.1.2`):
216
+
217
+ `next-translate` — моя основная рекомендация, если вам нравится API в стиле `t()`. Он элегантен благодаря `next-translate-plugin`, загружая пространства имен через `getStaticProps` с помощью загрузчика Webpack / Turbopack. Это также самый легкий вариант (~2.5 КБ). Что касается пространств имен, их определение для каждой страницы или маршрута в конфиге хорошо продумано и проще в обслуживании, чем основные альтернативы, такие как **next-intl** или **next-i18next**. В версии `3.1.2` я заметил, что статический рендеринг не работал; Next.js откатывался к динамическому рендерингу.
218
+
219
+ **(Intlayer)** (`next-intlayer@8.7.5`):
220
+
221
+ Я не буду лично судить о `next-intlayer` ради объективности, так как это мое собственное решение.
222
+
223
+ ### Личное примечание
224
+
225
+ Это примечание является личным и не влияет на результаты бенчмарка. В мире i18n часто можно увидеть консенсус вокруг паттерна `const t = useTranslation('xx')` + `<>{t('xx.xx')}</>`.
226
+
227
+ В React-приложениях внедрение функции в качестве `ReactNode`, на мой взгляд, является антипаттерном. Это также добавляет лишнюю сложность и накладные расходы на выполнение JavaScript (хотя они почти незаметны).
@@ -0,0 +1,193 @@
1
+ ---
2
+ createdAt: 2026-04-20
3
+ updatedAt: 2026-04-21
4
+ title: Лучшее решение i18n для TanStack Start в 2026 году - Отчет о бенчмарке
5
+ description: Сравните библиотеки интернационализации для TanStack Start, такие как react-i18next, use-intl и Intlayer. Подробный отчет о производительности, размере бандла, утечках и реактивности.
6
+ keywords:
7
+ - benchmark
8
+ - i18n
9
+ - intl
10
+ - tanstack
11
+ - производительность
12
+ - intlayer
13
+ slugs:
14
+ - doc
15
+ - benchmark
16
+ - tanstack
17
+ author: Aymeric PINEAU
18
+ applicationTemplate: https://github.com/intlayer-org/benchmark-i18n-tanstack-start-template
19
+ history:
20
+ - version: 8.7.5
21
+ date: 2026-01-06
22
+ changes: "Инициализация бенчмарка"
23
+ ---
24
+
25
+ # Библиотеки i18n для TanStack Start — Отчет о бенчмарке 2026
26
+
27
+ Эта страница представляет собой отчет о бенчмарке i18n-решений для TanStack Start.
28
+
29
+ ## Содержание
30
+
31
+ <Toc/>
32
+
33
+ ## Интерактивный бенчмарк
34
+
35
+ <I18nBenchmark framework="tanstack" vertical/>
36
+
37
+ ## Ссылка на результаты:
38
+
39
+ <iframe
40
+ src="https://intlayer.org/markdown?url=https%3A%2F%2Fraw.githubusercontent.com%2Fintlayer-org%2Fbenchmark-i18n%2Fmain%2Freport%2Fscripts%2Fsummarize-tanstack.md"
41
+ width="100%"
42
+ height="600px"
43
+ style="border:none;">
44
+ </iframe>
45
+
46
+ > https://intlayer.org/markdown?url=https%3A%2F%2Fraw.githubusercontent.com%2Fintlayer-org%2Fbenchmark-i18n%2Fmain%2Freport%2Fscripts%2Fsummarize-tanstack.md
47
+
48
+ Полный репозиторий бенчмарка можно найти [здесь](https://github.com/intlayer-org/benchmark-i18n/tree/main).
49
+
50
+ ## Введение
51
+
52
+ Решения для интернационализации являются одними из самых тяжелых зависимостей в React-приложении. В случае с TanStack Start основной риск заключается в передаче ненужного контента: переводов для других страниц и других локалей в бандле одного маршрута.
53
+
54
+ По мере роста вашего приложения эта проблема может быстро привести к раздуванию JavaScript, отправляемого клиенту, и замедлению навигации.
55
+
56
+ На практике в наименее оптимизированных реализациях интернационализированная страница может оказаться в несколько раз тяжелее версии без i18n.
57
+
58
+ Другой аспект — это опыт разработчика: то, как вы объявляете контент, типы, организацию пространств имен, динамическую загрузку и реактивность при смене локали.
59
+
60
+ ## Проверьте свое приложение
61
+
62
+ Чтобы быстро выявить проблемы с утечкой i18n, я настроил бесплатный сканер, доступный [здесь](https://intlayer.org/i18n-seo-scanner).
63
+
64
+ <iframe src="https://intlayer.org/i18n-seo-scanner" width="100%" height="600px" style="border:none;"/>
65
+
66
+ ## Проблема
67
+
68
+ Два рычага необходимы для ограничения стоимости мультиязычного приложения:
69
+
70
+ - Разделение контента по страницам / пространствам имен, чтобы не загружать целые словари, когда они вам не нужны.
71
+ - Динамическая загрузка нужной локали только тогда, когда она требуется.
72
+
73
+ Понимание технических ограничений этих подходов:
74
+
75
+ **Динамическая загрузка**
76
+
77
+ Без динамической загрузки большинство решений хранят сообщения в памяти с первого рендеринга, что создает значительные накладные расходы для приложений с множеством маршрутов и локалей.
78
+
79
+ При использовании динамической загрузки вы идете на компромисс: меньше начального JS, но иногда дополнительный запрос при переключении языка.
80
+
81
+ **Разделение контента**
82
+
83
+ Синтаксис, построенный вокруг `const t = useTranslation()` + `t('a.b.c')`, очень удобен, но часто способствует хранению больших JSON-объектов во время выполнения. Эта модель затрудняет tree-shaking, если библиотека не предлагает реальную стратегию разделения контента для каждой страницы.
84
+
85
+ ## Методология
86
+
87
+ Для этого бенчмарка мы сравнили следующие библиотеки:
88
+
89
+ - `Base App` (Без библиотеки i18n)
90
+ - `react-intlayer` (v8.7.5-canary.0)
91
+ - `react-i18next` (v17.0.2)
92
+ - `use-intl` (v4.9.1)
93
+ - `@lingui/core` (v5.3.0)
94
+ - `@inlang/paraglide-js` (v2.15.1)
95
+ - `tolgee` (v7.0.0)
96
+ - `react-intl` (v10.1.1)
97
+ - `wuchale` (v0.22.11)
98
+ - `gt-react` (vlatest)
99
+ - `lingo.dev` (v0.133.9)
100
+
101
+ Фреймворк — `TanStack Start` с мультиязычным приложением из **10 страниц** и **10 языков**.
102
+
103
+ Мы сравнили **четыре стратегии загрузки**:
104
+
105
+ | Стратегия | Без пространств имен (глобальная) | С пространствами имен (локальная/scoped) |
106
+ | :------------------------ | :---------------------------------------------- | :------------------------------------------------------------------------------- |
107
+ | **Статическая загрузка** | **Static**: Все в памяти при запуске. | **Scoped static**: Разделено по пространствам имен; все загружается при запуске. |
108
+ | **Динамическая загрузка** | **Dynamic**: Загрузка по требованию для локали. | **Scoped dynamic**: Гранулярная загрузка по пространствам имен и локалям. |
109
+
110
+ ## Резюме стратегий
111
+
112
+ - **Статическая (Static)**: Простота; отсутствие задержек сети после начальной загрузки. Минус: большой размер бандла.
113
+ - **Динамическая (Dynamic)**: Уменьшает начальный вес (ленивая загрузка). Идеально при наличии множества локалей.
114
+ - **Локальная статическая (Scoped static)**: Позволяет организовать код (логическое разделение) без сложных дополнительных сетевых запросов.
115
+ - **Локальная динамическая (Scoped dynamic)**: Лучший подход для _разделения кода_ и производительности. Минимизирует использование памяти, загружая только то, что нужно для текущего представления и активной локали.
116
+
117
+ ## Результаты в деталях
118
+
119
+ ### 1 — Решения, которых следует избегать
120
+
121
+ Некоторых решений, таких как `gt-react` или `lingo.dev`, явно стоит остерегаться. Они сочетают в себе привязку к вендору с загрязнением вашей кодовой базы. Хуже того: несмотря на многие часы попыток внедрить их, мне так и не удалось заставить их работать должным образом на TanStack Start (аналогично Next.js с `gt-next`).
122
+
123
+ Встреченные проблемы:
124
+
125
+ **(General Translation)** (`gt-react@latest`):
126
+
127
+ - Для приложения весом около 110 КБ `gt-react` может добавить более 440 КБ сверху (порядок величины, наблюдаемый в реализации Next.js в том же бенчмарке).
128
+ - `Quota Exceeded, please upgrade your plan` (Квота превышена, пожалуйста, обновите тарифный план) при самой первой сборке с General Translation.
129
+ - Переводы не отображаются; я получаю ошибку `Error: <T> used on the client-side outside of <GTProvider>`, что кажется багом библиотеки.
130
+ - При внедрении **gt-tanstack-start-react** я также столкнулся с [проблемой](https://github.com/generaltranslation/gt/issues/1210#event-24510646961) в библиотеке: `does not provide an export named 'printAST' - @formatjs/icu-messageformat-parser`, которая приводила к поломке приложения. После сообщения об этой проблеме сопровождающий исправил ее в течение 24 часов.
131
+ - Эти библиотеки используют антипаттерн через функцию `initializeGT()`, что мешает сборке чисто исключать неиспользуемый код (tree-shaking).
132
+
133
+ **(Lingo.dev)** (`lingo.dev@0.133.9`):
134
+
135
+ - Квота AI превышена (или блокирующая зависимость от сервера), что делает сборку / продакшн рискованными без оплаты.
136
+ - Компилятор пропустил почти 40% переведенного контента. Мне пришлось переписать все вызовы `.map` в плоские блоки компонентов, чтобы это заработало.
137
+ - Их CLI работает с ошибками и периодически сбрасывает конфигурационный файл без причины.
138
+ - При сборке он полностью удалял сгенерированные JSON-файлы, когда добавлялся новый контент. В результате всего несколько ключей могли стереть сотни существующих.
139
+ - Я столкнулся с проблемами реактивности в этой библиотеке на TanStack Start: при смене локали мне приходилось принудительно перерендеривать провайдер.
140
+
141
+ ### 2 — Экспериментальные решения
142
+
143
+ **(Wuchale)** (`wuchale@0.22.11`):
144
+
145
+ Идея `Wuchale` интересна, но это пока не жизнеспособное решение. Я столкнулся с проблемами реактивности в этой библиотеке и был вынужден принудительно перерендеривать провайдер, чтобы приложение заработало на TanStack Start. Документация также довольно неясная, что затрудняет начало работы.
146
+
147
+ ### 3 — Приемлемые решения
148
+
149
+ **(Paraglide)** (`@inlang/paraglide-js@2.15.1`):
150
+
151
+ `Paraglide` предлагает инновационный, хорошо продуманный подход. Тем не менее, в этом бенчмарке обещанный tree-shaking не сработал ни для моей реализации на Next.js, ни для TanStack Start. Процесс работы и DX также сложнее, чем у других вариантов. Лично я не в восторге от необходимости перегенерировать JS-файлы перед каждым пушем, что создает постоянный риск конфликтов при слиянии в PR.
152
+
153
+ **(Tolgee)** (`tolgee@7.0.0`):
154
+
155
+ `Tolgee` решает многие из упомянутых ранее проблем. Мне показалось, что начать работу с ним сложнее, чем с другими инструментами с похожими подходами. Он не обеспечивает типобезопасность, что значительно затрудняет отлов отсутствующих ключей на этапе компиляции. Мне пришлось обернуть API Tolgee своими собственными, чтобы добавить обнаружение отсутствующих ключей.
156
+
157
+ На TanStack Start у меня также были проблемы с реактивностью: при смене локали мне приходилось принудительно перерендеривать провайдер и подписываться на события смены локали, чтобы загрузка на другом языке вела себя корректно.
158
+
159
+ **(use-intl)** (`use-intl@4.9.1`):
160
+
161
+ `use-intl` — самый модный представитель "intl" в экосистеме React (из того же семейства, что и `next-intl`), который часто продвигают AI-агенты, но, на мой взгляд, ошибочно в контексте производительности. Начать работу довольно просто. На практике процесс оптимизации и ограничения утечек довольно сложен. Аналогично, сочетание динамической загрузки + пространств имен + типов TypeScript сильно замедляет разработку.
162
+
163
+ На TanStack Start вы избегаете ловушек, специфичных для Next.js (`setRequestLocale`, статический рендеринг), но основная проблема та же: без строгой дисциплины бандл быстро переполняется сообщениями, а обслуживание пространств имен для каждого маршрута становится мучительным.
164
+
165
+ **(react-i18next)** (`react-i18next@17.0.2`):
166
+
167
+ `react-i18next`, вероятно, является самым популярным вариантом, так как был одним из первых решений для нужд i18n в JavaScript-приложениях. У него также есть широкий набор плагинов от сообщества для конкретных задач.
168
+
169
+ Тем не менее, у него те же основные недостатки, что и у стеков, построенных на `t('a.b.c')`: оптимизация возможна, но отнимает очень много времени, а большие проекты рискуют столкнуться с плохими практиками.
170
+
171
+ Форматы сообщений также расходятся: `use-intl` использует ICU MessageFormat, в то время как `i18next` использует свой собственный формат, что усложняет инструментарий или миграцию при их смешивании.
172
+
173
+ **(Lingui)** (`@lingui/core@5.3.0`):
174
+
175
+ `Lingui` часто хвалят. Лично мне рабочий процесс вокруг `lingui extract` / `lingui compile` показался более сложным, чем другие подходы, без явных преимуществ в этом бенчмарке TanStack Start. Я также заметил непоследовательный синтаксис, который путает AI (например, `t()`, `t''`, `i18n.t()`, `<Trans>`).
176
+
177
+ **(react-intl)** (`react-intl@10.1.1`):
178
+
179
+ `react-intl` — это производительная реализация от команды Format.js. DX остается многословным: `const intl = useIntl()` + `intl.formatMessage({ id: "xx.xx" })` добавляет сложности, лишней работы для JavaScript и привязывает глобальный экземппляр i18n ко многим узлам в дереве React.
180
+
181
+ ### 4 — Рекомендации
182
+
183
+ В этом бенчмарке TanStack Start нет прямого эквивалента `next-translate` (плагин Next.js + `getStaticProps`). Для команд, которые действительно хотят API `t()` со зрелой экосистемой, `react-i18next` и `use-intl` остаются "разумным" выбором, но будьте готовы потратить много времени на оптимизацию, чтобы избежать утечек.
184
+
185
+ **(Intlayer)** (`react-intlayer@8.7.5-canary.0`):
186
+
187
+ Я не буду лично судить о `react-intlayer` ради объективности, так как это мое собственное решение.
188
+
189
+ ### Личное примечание
190
+
191
+ Это примечание является личным и не влияет на результаты бенчмарка. Тем не менее, в мире i18n часто можно встретить консенсус вокруг паттерна типа `const t = useTranslation('xx')` + `<>{t('xx.xx')}</>` для переведенного контента.
192
+
193
+ В React-приложениях внедрение функции в качестве `ReactNode`, на мой взгляд, является антипаттерном. Это также добавляет лишнюю сложность и накладные расходы на выполнение JavaScript (даже если это почти незаметно).
@@ -0,0 +1,29 @@
1
+ ---
2
+ createdAt: 2026-04-20
3
+ updatedAt: 2026-04-20
4
+ title: i18n Kütüphaneleri Karşılaştırması (Benchmark)
5
+ description: Intlayer'ın performans ve paket boyutu açısından diğer i18n kütüphaneleriyle nasıl karşılaştırıldığını öğrenin.
6
+ keywords:
7
+ - benchmark
8
+ - i18n
9
+ - intl
10
+ - nextjs
11
+ - tanstack
12
+ - intlayer
13
+ slugs:
14
+ - doc
15
+ - benchmark
16
+ history:
17
+ - version: 8.7.5
18
+ date: 2026-01-06
19
+ changes: "Benchmark başlatıldı"
20
+ ---
21
+
22
+ # Benchmark - Rapor
23
+
24
+ Benchmark Bloom, birden fazla React çatısı ve yükleme stratejisinde i18n (uluslararasılaştırma) kütüphanelerinin gerçek dünyadaki etkisini ölçen bir performans kıyaslama paketidir.
25
+
26
+ Her çatı için ayrıntılı raporlar ve teknik belgeleri aşağıda bulabilirsiniz:
27
+
28
+ - [**Next.js benchmark raporu**](https://github.com/aymericzip/intlayer/blob/main/docs/docs/tr/benchmark/nextjs.md)
29
+ - [**TanStack Start benchmark raporu**](https://github.com/aymericzip/intlayer/blob/main/docs/docs/tr/benchmark/tanstack.md)