Задайте питання та отримайте підсумок документа, вказавши цю сторінку та обраного вами постачальника штучного інтелекту
Історія версій
- "Додати порівняння зірок GitHub"v8.9.818.05.2026
- "Ініціалізація бенчмарку"v8.7.506.01.2026
Вміст цієї сторінки перекладено за допомогою штучного інтелекту.
Переглянути останню версію оригінального вмісту англійськоюIf you have an idea for improving this documentation, please feel free to contribute by submitting a pull request on GitHub.
GitHub link to the documentationCopy doc Markdown to clipboard
Бібліотеки i18n для TanStack Start - Звіт бенчмарку 2026
Ця сторінка є звітом бенчмарку i18n рішень на TanStack Start.
Зміст
Інтерактивний бенчмарк
Довідка за результатами:
Переглянути повні дані бенчмарку
Дивіться повний репозиторій бенчмарку тут.
Вступ
Рішення для інтернаціоналізації є одними з найважчих залежностей у React-додатках. У TanStack Start основний ризик полягає в передачі зайвого контенту: перекладів для інших сторінок та інших локалей у бандлі одного маршруту.
У міру зростання додатку ця проблема може швидко роздути обсяг JavaScript, що надсилається клієнту, і сповільнити навігацію.
На практиці для найменш оптимізованих реалізацій інтернаціоналізована сторінка може стати в кілька разів важчою за версію без i18n.
Інший вплив стосується досвіду розробника (DX): як ви оголошуєте контент, типи, організація просторів імен, динамічне завантаження та реактивність при зміні мови.
TL;DR
- Intlayer: Забезпечує найкращу продуктивність і найменший розмір бандла (v8.7.12) для TanStack Start.
- react-i18next та use-intl: Зрілі альтернативи з великими екосистемами, але значно важчі та складніші в оптимізації.
- Paraglide: Інноваційна ідея tree-shaking, яка не працює на практиці. Складний DX та накладні витрати реактивності в TanStack Start.
- Уникайте: General Translation (GT) та Lingo.dev через серйозні проблеми з продуктивністю, ліміти ШІ та прив'язку до вендора (vendor lock-in).
Протестуйте свій додаток
Щоб швидко виявити проблеми з витоком i18n, я налаштував безкоштовний сканер, доступний тут.
Проблема
Два важелі є важливими для обмеження вартості багатомовного додатку:
- Розділяйте контент за сторінками / просторами імен, щоб не завантажувати цілі словники, коли вони не потрібні.
- Динамічно завантажуйте потрібну локаль лише тоді, коли це необхідно.
Розуміння технічних обмежень цих підходів:
Динамічне завантаження
Без динамічного завантаження більшість рішень зберігають повідомлення в пам'яті з першого рендерингу, що додає значні накладні витрати для додатків з великою кількістю маршрутів та локалей.
З динамічним завантаженням ви приймаєте компроміс: менше початкового JS, але іноді додатковий запит при зміні мови.
Розділення контенту (Content splitting)
Синтаксиси навколо const t = useTranslation() + t('a.b.c') дуже зручні, але часто заохочують зберігання великих JSON-об'єктів під час виконання (runtime). Така модель ускладнює видалення невикористаного коду (tree-shaking), якщо бібліотека не пропонує реальної стратегії розділення сторінок.
Методологія
Для цього бенчмарку ми порівняли наступні бібліотеки:
Base App(Без бібліотеки i18n)react-intlayer(v8.7.12)react-i18next(v17.0.2)use-intl(v4.9.1)@lingui/core(v5.3.0)@inlang/paraglide-js(v2.15.1)@tolgee/react(v7.0.0)react-intl(v10.1.1)wuchale(v0.22.11)gt-react(vlatest)lingo.dev(v0.133.9)
Фреймворк - TanStack Start з багатомовним додатком на 10 сторінок та 10 мов.
Ми порівняли чотири стратегії завантаження:
Відкрийте таблицю в модальному вікні, щоб чітко переглянути всі дані
| Стратегія | Без просторів імен (global) | З просторами імен (scoped) |
|---|---|---|
| Статичне завантаження | Static: Все в пам'яті при запуску. | Scoped static: Розділено за простором імен; завантаження при запуску. |
| Динамічне завантаження | Dynamic: Завантаження за запитом для кожної локалі. | Scoped dynamic: Гранулярне завантаження за простором імен та локаллю. |
Резюме стратегій
- Static: Просто; відсутня мережева затримка після початкового завантаження. Мінус: великий розмір бандла.
- Dynamic: Зменшує початкову вагу (lazy-loading). Ідеально, якщо у вас багато локалей.
- Scoped static: Зберігає код структурованим (логічний поділ) без складних мережевих запитів.
- Scoped dynamic: Найкращий підхід для розділення коду (code splitting) та продуктивності. Мінімізує обсяг пам'яті, завантажуючи лише те, що потрібно поточному перегляду та активній локалі.
Зірки на GitHub
Зірки на GitHub є потужним індикатором популярності проекту, довіри спільноти та довгострокової актуальності. Хоча вони не є прямим показником технічної якості, вони відображають, скільки розробників вважають проект корисним, стежать за його розвитком і, ймовірно, впровадять його. Для оцінки цінності проекту зірки допомагають порівняти інтерес до альтернатив і дають уявлення про зростання екосистеми.
Детальні результати
1 - Рішення, яких слід уникати
Деяких рішень, таких як gt-react або lingo.dev, явно варто триматися подалі. Вони поєднують прив'язку до вендора із засміченням вашої кодової бази. Гірше того: попри багато годин спроб їх впровадження, я так і не зміг забезпечити їх коректну роботу на TanStack Start (аналогічно до gt-next на Next.js).
Виявлені проблеми:
(General Translation) (gt-react@latest):
- Для додатку розміром близько 110 КБ
gt-reactможе додати понад 440 КБ зайвих даних (порядок величини зафіксовано в реалізації Next.js у тому ж бенчмарку). Quota Exceeded, please upgrade your planвже під час першої збірки.- Переклади не рендериться; отримую помилку
Error: <T> used on the client-side outside of <GTProvider>, що схоже на баг бібліотеки. - При впровадженні gt-tanstack-start-react я також стикнувся з проблемою бібліотеки: помилка
does not provide an export named 'printAST' - @formatjs/icu-messageformat-parser, через яку додаток падав. Після звіту про помилку розробник виправив її протягом 24 годин. - Ці бібліотеки використовують антипатерн через функцію
initializeGT(), що заважає чистому видаленню невикористаного коду (tree-shaking) бандла.
(Lingo.dev) ([email protected]):
- Перевищено квоту AI (або блокування серверної залежності), що робить збірку / продакшн ризикованим без оплати.
- Компілятор пропустив майже 40% перекладеного контенту. Мені довелося переписувати всі
.mapу плоскі блоки компонентів, щоб це запрацювало. - Їхній CLI працює нестабільно і часто без причини скидає конфігураційний файл.
- Під час збірки він повністю стирав згенеровані JSON-файли при додаванні нового контенту. У результаті лише кілька ключів могли знищити сотні наявних.
- Я стикнувся з проблемами реактивності бібліотеки в TanStack Start: при зміні локалі довелося примусово перерендерити провайдер, щоб це запрацювало.
2 - Експериментальні рішення
(Wuchale) ([email protected]):
Ідея Wuchale цікава, але поки що не є життєздатним рішенням. Я зіткнувся з проблемами реактивності бібліотеки і був змушений примусово перерендерити провайдер, щоб додаток запрацював на TanStack Start. Документація також досить нечітка, що ускладнює освоєння.
3 - Прийнятні рішення
(Paraglide) (@inlang/[email protected]):
Paraglide пропонує інноваційний, добре продуманий підхід. Проте в цьому тесті рекламований компанією tree-shaking не спрацював ні для моєї реалізації на Next.js, ні для TanStack Start. Робочий процес і DX також складніші за інші варіанти. Особисто я не прихильник необхідності регенерувати JS-файли перед кожним пушем, що створює постійний ризик конфліктів під час злиття для розробників через PR.
Примітка щодо paraglide: це рішення впорскує код у вашу кодову базу для імпорту; як наслідок, метрика 'lib size' у звіті бенчмарку становить майже 0. Генерація коду - це добре, оскільки використовувана функція включатиме лише необхідну логіку (префікс усюди проти відсутності префікса, куки проти сховища тощо). Для порівняння, Intlayer виконує це фільтрування за допомогою впорскування змінних оточення в збірку, щоб змусити бандлер виконати tree-shake контенту залежно від логіки. Завдяки цьому paraglide та intlayer врешті-решт стають у 6–10 разів легшими рішеннями, ніж i18next або next-intl.
(Tolgee) (@tolgee/[email protected]):
Tolgee вирішує багато зі згаданих проблем. Проте почати роботу з ним здалося мені важче, ніж з іншими інструментами з подібними підходами. Він не забезпечує типізацію, що значно ускладнює виявлення відсутніх ключів під час компіляції. Мені довелося обертати Tolgee API своїми рішеннями, щоб додати перевірку відсутніх ключів.
На TanStack Start я також мав проблеми з реактивністю: при зміні локалі довелося примусово перерендерити провайдер і підписуватися на події зміни локалі, щоб завантаження іншою мовою працювало коректно.
(use-intl) ([email protected]):
use-intl - це зараз наймодніша частина "intl" в екосистемі React (та ж родина, що й next-intl), яку часто просувають ШІ-агенти, але на мій погляд - помилково в контексті продуктивності. Почати роботу досить просто. На практиці ж процес оптимізації та обмеження витоків є досить складним. Аналогічно, поєднання динамічного завантаження + просторів імен + типів TypeScript сильно сповільнює розробку.
У TanStack Start ви уникаєте специфічних пасток Next.js (setRequestLocale, статичний рендеринг), але основна проблема та ж сама: без суворої дисципліни бандл швидко перевантажується повідомленнями, а підтримка просторів імен для кожного маршруту стає виснажливою.
(react-i18next) ([email protected]):
react-i18next - це, мабуть, найпопулярніший варіант, оскільки він був одним із перших, хто задовольнив потреби i18n у JS-додатках. Він також має великий набір плагінів від спільноти для конкретних проблем.
Проте він має ті ж недоліки, що й стеки на базі t('a.b.c'): оптимізація можлива, але забирає багато часу, а великі проекти ризикують скотитися до поганих практик (простори імен + динамічне завантаження + типи).
Формати повідомлень також відрізняються: use-intl використовує ICU MessageFormat, тоді як i18next має власний формат - це ускладнює інструментарій або міграцію, якщо ви їх змішуєте.
(Lingui) (@lingui/[email protected]):
Lingui часто хвалять. Особисто мені робочий процес із lingui extract / lingui compile здався складнішим за інші підходи, без явних переваг у цьому тесті TanStack Start. Також помічені непослідовні синтаксиси, які плутають ШІ (наприклад, t(), t'', i18n.t(), <Trans>).
(react-intl) ([email protected]):
react-intl - це продуктивна реалізація від команди Format.js. DX залишається багатослівним: const intl = useIntl() + intl.formatMessage({ id: "xx.xx" }) додає складності, додаткової роботи JavaScript та прив'язує глобальний екземпляр i18n до багатьох вузлів у React-дереві.
4 - Рекомендації
Цей бенчмарк TanStack Start не має прямого еквівалента next-translate (Next.js плагін + getStaticProps). Командам, які справді хочуть API у стилі t() зі зрілою екосистемою, react-i18next та use-intl залишаються "розумним" вибором, але будьте готові витратити багато часу на оптимізацію для уникнення витоків.
(Intlayer) ([email protected]):
Я не буду особисто оцінювати react-intlayer заради об’єктивності, оскільки це моє власне рішення.
Особиста замітка
Ця замітка є особистою і не впливає на результати бенчмарку. Тим не менш, у світі i18n часто спостерігається консенсус навколо використання const t = useTranslation('xx') + <>{t('xx.xx')}</> для перекладеного контенту.
У React-додатках передача функції як ReactNode, на мою думку, є антипатерном. Це також додає зайвої складності та накладних витрат на виконання JavaScript (навіть якщо це ледь помітно).