Lingui czy i18next w 2026: która biblioteka i18n do React i React Native
Lingui i i18next porównane na podstawie dokumentacji i danych z npm: format komunikatów, makra, kompilacja, rozmiar, liczba mnoga i React Native.
Wybierz i18next, jeśli zależy Ci na największym ekosystemie, kluczach nazwanych przez siebie i tłumaczeniach ładowanych jako JSON w czasie działania aplikacji. Wybierz Lingui, jeśli wolisz pisać tekst źródłowy tam, gdzie się wyświetla, trzymać liczbę mnogą w jednym komunikacie ICU i kompilować katalogi przy buildzie, z rdzeniem ważącym około 2 kB po gzipie. Używam Lingui w Linkoglocie (30 języków, Expo) i na tej stronie.
TL;DR
- i18next ma zasięg: około 15 razy więcej pobrań z npm tygodniowo niż Lingui, długą listę wtyczek i typowane klucze w TypeScripcie.
- Lingui przenosi pracę na czas builda: makra tworzą ID, katalogi kompilują się do JavaScriptu, a wtyczka bundlera potrafi wywrócić build przy brakującym tłumaczeniu.
- Obie działają w React Native na Hermesie z polyfillami
Intl. Zanim wybierzesz, przetestuj liczbę mnogą w najtrudniejszym ze swoich języków na prawdziwym urządzeniu.
Nazywasz klucze czy piszesz tekst źródłowy?
Różnicę widać już w pierwszej linijce kodu. W i18next wymyślasz klucz, wpisujesz tekst do pliku JSON i w czasie działania szukasz go po kluczu:
{ "greeting": "Hello {{name}}" }
const { t } = useTranslation();
return <p>{t("greeting", { name })}</p>;
W Lingui piszesz angielski tekst w miejscu, gdzie się wyświetla. Makro zamienia go przy buildzie na komunikat z krótkim, wygenerowanym ID:
import { t } from "@lingui/core/macro";
t`Hello ${name}`;
// po przejściu makra (przykład z dokumentacji Lingui):
// i18n._({ id: "mVmaLu", message: "Hello {name}", values: { name } })
Żaden z tych modeli nie jest błędny. Nazwane klucze nie zmieniają się, gdy poprawiasz angielski tekst, a z TypeScriptem podpowiadają się w edytorze. Tekst źródłowy oznacza, że nie musisz niczego nazywać, a identyczne teksty łączą się w jeden komunikat. Druga strona medalu: poprawione angielskie zdanie dostaje nowe ID, więc tłumacz widzi je jako nowe.
Łączenie też ma haczyk. W moim katalogu „Stop” występuje dwa razy: raz jako przycisk, raz jako etap pętli z sekcji Jak pracuję, gdzie po polsku potrzebuję słowa „Etap”. Rozdziela je context:
const stopLabel = t({ message: "Stop", context: "a stop on the product loop, as in a bus stop" });
msgid "Stop"
msgstr "Stop"
msgctxt "a stop on the product loop, as in a bus stop"
msgid "Stop"
msgstr "Etap"
en
pl Czy obie biblioteki używają formatu ICU?
Domyślnie nie. Lingui używa ICU MessageFormat wszędzie: zmienne, liczba mnoga i select siedzą w jednym komunikacie, na przykład {count, plural, one {# file} other {# files}}. W i18next {{name}} służy do interpolacji, a każda forma liczby mnogiej to osobny klucz z sufiksem. Wstawiane wartości są domyślnie escapowane, żeby ograniczyć ryzyko XSS.
ICU w i18next da się włączyć wtyczką i18next-icu, opartą na intl-messageformat od formatjs. Jej README ostrzega, że z wtyczką działa wyłącznie format ICU, więc {{name}} przestaje działać. Wybierz jedną składnię na projekt.
Jak działają makra, ekstrakcja i kompilacja?
- 01 Piszesz Makro obejmuje angielski tekst tam, gdzie się wyświetla.
- 02 Ekstrakcja lingui extract zbiera komunikaty do jednego katalogu .po na język.
- 03 Tłumaczenie Człowiek, skrypt albo LLM wypełnia katalog, razem z komentarzami i kontekstem.
- 04 Kompilacja lingui compile, wtyczka Vite albo transformer Metro zamienia .po na JavaScript.
- 05 Wysyłka Do bundla trafiają skompilowane komunikaty, bez parsera.
Makra działają przy buildzie (Babel, SWC albo natywna transformacja w @lingui/vite-plugin). Według dokumentacji generują krótkie ID, pozwalają TypeScriptowi sprawdzać komponenty jak zwykły kod i usuwają z buildu produkcyjnego komentarze dla tłumaczy oraz domyślne teksty. Ekstraktor znajduje makra, jawne wywołania i18n._() i wyrażenia oznaczone komentarzem /** i18n */; komunikatu zapisanego w zmiennej nie wyciągnie.
i18next pomija kompilację. JSON trafia do bundla albo jest ładowany przez wtyczki backendowe, a przestrzenie nazw (namespaces) dzielą tłumaczenia, żeby doładowywać je na żądanie. Ekstrakcją zajmuje się i18next-cli, zestaw narzędzi oparty na SWC: wyciąga klucze, generuje typy TypeScript, wykrywa zahardkodowane teksty, synchronizuje pliki językowe i raportuje stan tłumaczeń.
Ile ważą paczki?
Dane z bundlephobii z 2026-10-05, po minifikacji oraz po minifikacji i gzipie (1 kB = 1000 bajtów):
| Paczka | Wersja | Min | Gzip |
|---|---|---|---|
i18next |
26.4.2 | 43,6 kB | 13,7 kB |
react-i18next |
17.0.15 | 25,9 kB | 10,2 kB |
@lingui/core |
6.9.0 | 5,0 kB | 2,1 kB |
@lingui/react |
6.9.0 | 3,8 kB | 1,8 kB |
Wiersze z rdzeniami zgadzają się z porównaniem na stronie Lingui. react-i18next deklaruje i18next jako peer dependency, więc jego wiersz nie zawiera samego i18next. Twoje katalogi dokładają bajty w obu przypadkach, a tree-shaking zmienia sumy, więc zanim podasz komuś liczbę, zmierz własny build.
Jak obie radzą sobie z liczbą mnogą?
Obie trzymają się kategorii z CLDR (zero, one, two, few, many, other) i obie opierają się na Intl.PluralRules. Różni się kształt danych. Angielski potrzebuje dwóch form, polski czterech. W i18next to cztery klucze, a zmienna musi nazywać się count:
{
"files_one": "{{count}} plik",
"files_few": "{{count}} pliki",
"files_many": "{{count}} plików",
"files_other": "{{count}} pliku"
}
W Lingui programista pisze tylko angielskie formy makrem plural, a polski tłumacz wypełnia jeden komunikat:
msgid "{count, plural, one {# file} other {# files}}"
msgstr "{count, plural, one {# plik} few {# pliki} many {# plików} other {# pliku}}"
Kod się nie zmienia, gdy dochodzi język z większą liczbą form. To praktyczny argument za ICU: gramatyka każdego języka zostaje w jego własnym pliku.
A co z TypeScriptem?
i18next daje typowane klucze i typy zwracanych wartości. Deklarujesz zasoby w rozszerzeniu CustomTypeOptions, a t sprawdza i podpowiada klucze. Najnowsze wersje wymagają TypeScriptu 5, a dokumentacja uprzedza, że typowanie może wydłużyć kompilację w dużych projektach (na ten przypadek jest zoptymalizowane API selektorów).
W Lingui nie ma kluczy do typowania. Makra to zwykłe template literals i JSX, więc TypeScript sprawdza wstawiane wartości jak każdy inny kod. Tracisz podpowiadanie kluczy, ale nie masz listy kluczy, którą trzeba utrzymywać.
Czy działają w React Native i Expo?
Obie działają i obie potrzebują tych samych polyfilli na Hermesie. Dokumentacja liczby mnogiej i18next mówi, że Hermes wciąż nie implementuje Intl.PluralRules. Od wersji 24 nie ma fallbacku: bez polyfilla działają tylko angielskie formy _one i _other. Samouczek React Native od Lingui (Lingui 5 lub nowsze, React Native 0.76 albo Expo 52, Hermes) każe dodać polyfille Intl.Locale i Intl.PluralRules z paczek formatjs.
react-i18next opisuje się jako framework dla React i React Native. Lingui używa tej samej paczki @lingui/react w obu środowiskach, dokłada transformer Metro dla plików .po i opcję defaultComponent, dzięki której przetłumaczony tekst ląduje w Text. Niezależnie od wyboru przetestuj liczbę mnogą na prawdziwym urządzeniu: polska forma utknięta w other łatwo umyka, gdy testy robi się po angielsku.
Jak duży jest każdy ekosystem?
i18next jest dużo większy. W tygodniu od 2026-09-28 do 2026-10-04 npm odnotował około 27,8 mln pobrań i18next i około 1,9 mln pobrań @lingui/core. Poza Reactem i18next ma integracje z Angularem i Vue, działa w Node.js i Deno i ma długą listę wtyczek (backendy, wykrywanie języka, konwertery z gettext, CSV i RESX). Jego twórcy rozwijają też Locize, serwis do zarządzania tłumaczeniami zrobiony pod i18next.
Lingui obsługuje React, React Native, Vue, SolidJS, Astro, Svelte i Node.js, a pod spodem jest niezależny od frameworka @lingui/core. Domyślny format katalogów to PO, czyli gettext: czytelny przy długich komunikatach, z komentarzami, kontekstami i metadanymi, obsługiwany przez wiele narzędzi do lokalizacji. comment i context podane w makrze trafiają do katalogu, a to pomaga zarówno tłumaczowi, jak i modelowi językowemu.
Jak używam Lingui?
Linkoglot to moja aplikacja do nauki języków, zbudowana w Expo i React Native. Przy 30 katalogach format pliku liczy się tak samo jak API. Pliki PO z komunikatami ICU trzymają reguły liczby mnogiej każdego języka w jego pliku, a zwykły tekst łatwo przetworzyć skryptem. Ten skrypt stał się później lingui-translate-ai, narzędziem CLI, które wypełnia katalogi Lingui przy pomocy LLM; opisuję je w tekście Tłumaczenie katalogów Lingui przez LLM.
Ta strona działa na Astro 7 z lingui-for-astro, integracją społecznościową (nie od zespołu Lingui), która przetwarza makra w plikach .astro i wyciąga z nich komunikaty. Wtyczka Vite kompiluje każdy plik .po przy imporcie, więc w repozytorium nie ma skompilowanych katalogów, a build produkcyjny ustawia failOnMissing:
lingui({
macroTransform: true,
failOnMissing: BUILD ? "catalog" : false,
failOnCompileError: true,
});
Przy wartości "catalog" build się wywraca, jeśli w polskim albo hiszpańskim katalogu brakuje komunikatu, i sprawdza to, zanim zadziałają języki zapasowe. Brakujący polski tekst nie trafi na produkcję jako ciche angielskie zdanie. Adresy, hreflang i przełącznik języka wokół tego opisuję w tekście wielojęzyczna strona w Astro z Lingui.
Którą bibliotekę wybrać?
| Jeśli potrzebujesz | Skłaniaj się ku |
|---|---|
| Największego ekosystemu albo serwisu do tłumaczeń od tych samych twórców (Locize) | i18next |
| Tłumaczeń ładowanych z serwera w czasie działania | i18next |
| Nazwanych kluczy z podpowiedziami w TypeScripcie | i18next |
| Tekstu źródłowego w kodzie, bez nazywania kluczy | Lingui |
Liczby mnogiej i select w jednym komunikacie ICU |
Lingui (albo i18next z i18next-icu) |
| Najmniejszego rdzenia | Lingui |
| Buildu, który pada przy brakującym tłumaczeniu | Lingui |
| Katalogów PO dla tłumaczy i skryptów | Lingui |
| React Native na Hermesie | Obie, z polyfillami Intl |
Ograniczenia
- To nie jest benchmark. Każde zachowanie pochodzi z oficjalnej dokumentacji; nie budowałem tej samej aplikacji w obu bibliotekach.
- Liczby to migawka. Rozmiary z bundlephobii i pobrania z API npm sprawdziłem 5 października 2026; zanim je zacytujesz, sprawdź aktualne.
Jeśli wybierasz dla zespołu, sprawdź obie biblioteki na jednym prawdziwym ekranie z liczbą mnogą, w tym ze swoich języków, który ma najwięcej form. Projekty, w których używam Lingui, znajdziesz na stronie z moimi pracami.
Źródła
- Lingui: Introduction (otwiera się w nowej karcie) (dostęp: )
- Lingui vs i18next (otwiera się w nowej karcie) (dostęp: )
- Lingui: Macros reference (otwiera się w nowej karcie) (dostęp: )
- Lingui: Message extraction (otwiera się w nowej karcie) (dostęp: )
- Lingui: Pluralization (otwiera się w nowej karcie) (dostęp: )
- Lingui: Catalog formats (otwiera się w nowej karcie) (dostęp: )
- Lingui: React Native tutorial (otwiera się w nowej karcie) (dostęp: )
- Lingui: Vite plugin (otwiera się w nowej karcie) (dostęp: )
- i18next documentation (otwiera się w nowej karcie) (dostęp: )
- i18next: Plurals (otwiera się w nowej karcie) (dostęp: )
- i18next: Interpolation (otwiera się w nowej karcie) (dostęp: )
- i18next: TypeScript (otwiera się w nowej karcie) (dostęp: )
- i18next-icu (GitHub) (otwiera się w nowej karcie) (dostęp: )
- i18next-cli (GitHub) (otwiera się w nowej karcie) (dostęp: )
- react-i18next documentation (otwiera się w nowej karcie) (dostęp: )
- @lingui/core on npm (otwiera się w nowej karcie) (dostęp: )
- i18next on npm (otwiera się w nowej karcie) (dostęp: )
- lingui-for-astro on npm (otwiera się w nowej karcie) (dostęp: )
- Bundlephobia: i18next 26.4.2 (otwiera się w nowej karcie) (dostęp: )
- Bundlephobia: react-i18next 17.0.15 (otwiera się w nowej karcie) (dostęp: )
- Bundlephobia: @lingui/core 6.9.0 (otwiera się w nowej karcie) (dostęp: )
- Bundlephobia: @lingui/react 6.9.0 (otwiera się w nowej karcie) (dostęp: )
- npm downloads API: i18next, last week (otwiera się w nowej karcie) (dostęp: )
- npm downloads API: @lingui/core, last week (otwiera się w nowej karcie) (dostęp: )