Zadaj pytanie i otrzymaj streszczenie dokumentu, odwołując się do tej strony i wybranego dostawcy AI
Treść tej strony została przetłumaczona przy użyciu sztucznej inteligencji.
Zobacz ostatnią wersję oryginalnej treści w języku angielskimJeśli masz pomysł na ulepszenie tej dokumentacji, zachęcamy do przesłania pull requesta na GitHubie.
Link do dokumentacji na GitHubieKopiuj dokument Markdown do schowka
next-intl VS @intlayer/next-intl | Ten sam API, Inny Bundle
@intlayer/next-intl jest adapterem kompatybilności: ekspozuje API next-intl (useTranslations, getTranslations, useLocale, t.rich(), plurale ICU, NextIntlClientProvider...) i serwuje go ze słowników skompilowanych przez Intlayer. Kod aplikacji nie zmienia się. Bundle się zmienia.
Artykuł porównuje oba na tej samej aplikacji Next.js, zbudowanej raz z next-intl i raz z adapterem. Liczby pochodzą z Benchmark Bloom, pakietu open-source'owego, który rejestruje to, co przeglądarka faktycznie pobiera. Jeśli chcesz porównania next-intl vs Intlayer jako bibliotek, przeczytaj next-intl vs Intlayer. Ten artykuł dotyczy tego, co adapter zmienia, gdy zachowujesz komponenty takimi, jakimi są.
tl;dr: Na tej samej aplikacji Next.js zamiananext-intlna@intlayer/next-intlzmniejszyła JavaScript na stronę z 153,6 KB do 147,5 KB gzip, średni komponent z 21,8 KB do 8,1 KB, wyciek stringów obcych stron z ~90% do 0% i hydratację z 14,7 ms do 12,8 ms, bez edycji żadnego komponentu. Na TanStack Start, równoważnikuse-intl(@intlayer/use-intl) zmniejszył komponenty z 76-87 KB do 9-11 KB i przełączanie lokalizacji z 7-21 ms do 4-9 ms. Adapter kosztuje 8,0 KB runtime w porównaniu do 14,7 KB dlanext-intli 5,5 KB dla natywnegonext-intlayer. Nawigacja i middleware są ponownie implementowane na konfiguracji routingu Intlayer; zlokalizowanepathnamesto jedyna funkcja, która nie została przeniesiona.
Co to jest @intlayer/next-intl
next-intl to runtime: getRequestConfig ładuje messages/{locale}.json dla każdego request'u, NextIntlClientProvider wysyła go na klienta, a useTranslations("about") czyta klucze z tego obiektu w momencie renderowania. Każda optymalizacja (namespaces, pick(messages, [...]) na stronę, lazy loading) to Twoja odpowiedzialność.
@intlayer/next-intl zachowuje pierwszą i ostatnią część tego łańcucha i zastępuje środkową. Twoje komponenty nadal wywołują useTranslations("about"); to, co otrzymują, pochodzi ze słownika Intlayer skompilowanego w czasie budowy, ograniczonego do tego komponentu, tylko w aktywnym języku.
Trzy mechanizmy sprawiają, że to działa:
- Import aliasing.
createNextIntlPlugin()z@intlayer/next-intl/pluginowijawithIntlayeri dodaje aliasy Webpack / Turbopack, abynext-intl,next-intl/server,next-intl/navigationinext-intl/middlewarerezolwowały się do@intlayer/next-intl. Żaden import w Twojej codebase nie zostaje przemianowany. - JSON jako źródło prawdy. Plugin
syncJSONczyta istniejący plikmessages/{locale}.json, dzieli jego klucze najwyższego poziomu na jeden słownik na namespace i zapisuje tłumaczenia z powrotem do tych samych plików, gdy CLI lub CMS je aktualizuje. Przepływ pracy tłumaczy pozostaje niezmieniony. - Call-site binding. Intlayer optimize pass (Babel lub SWC) przepisuje
useTranslations("about")na wywołanie, które otrzymuje słownikaboutbezpośrednio. Komponent nie sięga już globalnego drzewa wiadomości; sięga własnej zawartości.
Skopiuj kod do schowka
Skopiuj kod do schowka
To przepisanie jest powodem, dla którego kolumny rozmiaru komponentu i page-leakage poniżej się przesuwają: strona pobiera tylko słowniki komponentów, które renderuje, i tylko w locale'u, który jest serwowany.
Co adapter zachowuje, ignoruje i nie zastępuje
Otwórz tabelę w oknie modalnym, aby wyraźnie zobaczyć całą zawartość
next-intl API | Z @intlayer/next-intl |
|---|---|
useTranslations("ns") / getTranslations("ns") | ✅ Zachowane. Powiązane ze słownikiem ns w czasie budowania. Klucze są typowane względem Twojej zawartości. |
getTranslations({ locale, namespace }) | ✅ Zachowane |
t("key", { name }), t.rich(), t.markup(), t.raw() | ✅ Zachowane. ICU plurals, select, selectordinal, #, {ts, date, long} przechodzą przez resolver ICU Intlayera |
useLocale() / getLocale() / setRequestLocale() / setLocale | ✅ Zachowane |
useFormatter() | ✅ Zachowane. dateTime, number, relativeTime, list, dateTimeRange łączą się z natywnym Intl |
NextIntlClientProvider | ✅ Zachowane. Właściwości messages, timeZone i now są akceptowane, ale ignorowane (ostrzeżenie dev Cię o tym powiadomi) |
getMessages() | ✅ Zachowane dla zgodności; już nie potrzebne |
getRequestConfig() w src/i18n.ts | ⚠️ Nie potrzebne. Słowniki są kompilowane w czasie budowania; nie ma ładowania wiadomości per-request |
defineRouting() | ✅ Zachowane. Pominięte pola (locales, defaultLocale, localePrefix) są odczytywane z intlayer.config.ts |
createNavigation(), Link, redirect, usePathname, useRouter | ✅ Zachowane. Ponownie zaimplementowane na konfiguracji routingu Intlayera; argument routing jest akceptowany, ale ignorowany |
pathnames (zlokalizowane nazwy tras) | ❌ Akceptowane do typowania, nie interpolowane. Zachowaj zwykłe ścieżki lub przenieś to mapowanie do rewrite Intlayera |
createMiddleware() | ✅ Zachowane. Zwraca proxy Intlayera; ustawia cookie NEXT_LOCALE, dzięki czemu useLocale() i twój przełącznik działają prawidłowo |
NEXT_LOCALE cookie | ✅ Odczytywane domyślnie (chyba że sam skonfigurujesz routing.storage) |
Bare useTranslations() z bez namespace'u | ⚠️ Działa, ale call site nie jest powiązany: rozwiązuje się przez runtime registry. Przekaż namespace, aby uzyskać bundle gains |
Benchmark
Co było mierzone
Suite Benchmark Bloom buduje tę samą aplikację z każdą konfiguracją: 10 stron (home, about, blog, careers, contact, FAQ, pricing, products, settings, team), 10 locale'i (en, fr, es, de, it, pt, zh, ja, ko, ru), identyczne komponenty i identyczną zawartość. Strony są mierzone w en i fr.
next-intl został zbudowany w czterech strategiach ładowania, od naiwnego ustawienia (całe messages/{locale}.json ładowane) do optymalnego (jedna namespace per route + per-page pick()). Adapter został zbudowany na tych samych komponentach co naiwne ustawienie, ze zmienionymi tylko next.config.ts i intlayer.config.ts. Nie ma wariantu "scoped": kompilator scopes content per komponent, więc jego rzędy static i dynamic są już scoped.
Dla każdego buildu suite rejestruje:
- Lib size: gzip rozmiar pustego komponentu, który tylko importuje bibliotekę i18n. Stały koszt runtime.
- Page JS: gzip JavaScript pobrany per page, uśredniony na wszystkich stronach i locale'ach.
- Locale leak %: udział przetłumaczonych ciągów znalezionych w pobranym JS, które należą do locale'a, które użytkownik nie przegląda.
- Page leak %: udział przetłumaczonych ciągów znalezionych w pobranym JS, które należą do strony, na której użytkownik nie jest.
- Component avg: średni rozmiar gzip każdego komponentu skompilowanego w izolacji. Pokazuje, ile i18n runtime i katalogu jeden komponent pociąga za sobą.
- E2E reactivity: czas rzeczywisty między wybraniem nowego locale a aktualizacją
html[lang]w DOM (Playwright, 5 iteracji). - Hydration: czas trwania fazy hydratacji React.
Liczby poniżej pochodzą z uruchomienia z daty 2026-09-12 znext-intl/use-intl4.14.2 i@intlayer/*9.5.1. Aplikacja testowa jest celowo mała (kilkadziesiąt stringów na locale), więc procenty wycieków opisują wzorzec: rosną wraz z twoją zawartością, podczas gdy koszt runtime pozostaje stały.
Wyniki na Next.js
Otwórz tabelę w oknie modalnym, aby wyraźnie zobaczyć całą zawartość
| 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 |
Jak to czytać
- Te same komponenty, 6 KB mniej na stronę. Adapter build naiwnej aplikacji wynosi 147.5 KB, poniżej każdej konfiguracji
next-intl, włącznie w pełni zoptymalizowaną (153.6 KB). Samo runtime stanowi różnicę: 8.0 KB versus 14.7 KB, płacone na każdej stronie. - Wyciek spada do 0% bez dotykania komponentu. Naiwna konfiguracja
next-intlwysyła ~90% stringów obcych stron na każdej stronie. Osiągnięcie 0% znext-intloznacza konfiguracjescoped-*: jeden namespace na trasę ipick(messages, [...])na każdej stronie. Adapter osiąga 0% z naiwnego kodu, ponieważ pass optymalizacyjny wiąże każdyuseTranslations("ns")z jego własnym słownikiem. - Komponenty się zmniejszają 2,7x. Komponent skompilowany w izolacji średnio 21,8 KB z
next-intl(osiąga dostawcę i drzewo wiadomości) i 8,1 KB z adapterem. W konfiguracjiscoped-staticnext-intlta liczba wzrasta do 80 KB, ponieważ plik namespace każdej trasy staje się osiągalny ze strony, która go wybiera. - Hydration jest o 2 ms szybsza (12.8 vs 14.7 ms): nie ma obiektu komunikatów do deserializacji z payloadu RSC, zanim React będzie mógł się nawodnić.
- Adapter nie jest natywnym runtime'em.
next-intlayersiedzi na 141.3 KB, +0.3 KB ponad aplikację bazową, z runtime'em 5.5 KB. Adapter nosi API surfacenext-intl(useFormatter,t.rich, ICU resolver) na szczycie Intlayer'owego core'u, stąd 8.0 KB i +6 KB na stronę. To most, a nie miejsce docelowe.
Wyniki na TanStack Start (use-intl)
use-intl to framework-agnostyczne jądro next-intl. Jego adapter, @intlayer/use-intl, podąża za tym samym projektem z pluginem Vite (@intlayer/use-intl/plugin).
Otwórz tabelę w oknie modalnym, aby wyraźnie zobaczyć całą zawartość
| Konfiguracja | Strategia | Rozmiar lib (gz) | Średni JS strony (gz) | Wyciek locale | Wyciek strony | Średni komponent (gz) | Reaktywność E2E | Hydration |
|---|---|---|---|---|---|---|---|---|
| base (bez 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 |
Jak to czytać
- Bajty na stronę są porównywalne z optymalizowanym
use-intl.@intlayer/use-intlw trybiedynamic(129.7 KB) mieści się w 1 KBuse-intl'sscoped-dynamic(128.7 KB) i 10 KB powyżej zwykłegouse-intl'sdynamic(119.4 KB). Ten zwykły wierszdynamicnadal powoduje przeciek 90% ciągów ze stron obcych; liczba bajtów jest niska, ponieważ aplikacja testowa ma małą zawartość. Adapter's 0% to to, co pozostaje stałe wraz ze wzrostem zawartości. - Komponenty są 7-9 razy mniejsze. Komponenty
use-intlśrednio wynoszą 76-87 KB w każdej strategii, ponieważuseTranslationsjest powiązany z całym obiektem wiadomości providera. Adapter średnio wynosi 9-11 KB. - Przełączanie lokalizacji jest szybsze. Zoptymalizowane konfiguracje
use-intlzajmują 13-21 ms na aktualizacjęhtml[lang]; adapter zajmuje 4-9 ms. Mniej komponentów się re-renderuje i nic nie jest pobierane ponownie z drzewa wiadomości. staticzachowuje każdą lokalizację. Wierszstaticadaptera pokazuje 49,7% wycieków lokalizacji, tak samo jak natywny Intlayer w trybiestatic: wszystkie lokalizacje są pakowane, tylko słowniki strony. Jedna linia konfiguracji (importMode: 'dynamic') ją usuwa.
Dlaczego liczby się zmieniają
Nic w komponencie się nie zmieniło, więc zyski pochodzą całkowicie z tego, do czego useTranslations jest powiązany.
Z next-intl, binding jest dostawcą. NextIntlClientProvider otrzymuje cały obiekt messages dla locale'a; każdy useTranslations("about") czyta z niego. Bundler widzi jeden komponent importujący jeden hook, który czyta jeden context, i nie może wiedzieć, że używana jest tylko gałąź about. Trasy poniżej dzielą się tym samym obiektem message'a, więc kolumna page-leak czyta ~90% aż do czasu, gdy samodzielnie podzielisz plik.
Skopiuj kod do schowka
Z @intlayer/next-intl, wiązanie to słownik. syncJSON zamienia messages/en.json w jeden słownik na każdy klucz najwyższego poziomu; kompilator rozpoznaje, który komponent wywołuje useTranslations("about") i bezpośrednio przekazuje mu about w aktywnym locale, jako import, który bundler może śledzić i dzielić.
Skopiuj kod do schowka
src/i18n.ts i props messages znikają. Wszystko inne pozostaje identyczne.
Migracja w trzech krokach
Instalacja
bashKopiuj kodSkopiuj kod do schowka
Polecenie wykrywa
next-intli instalujeintlayer,next-intlayer,@intlayer/next-intli@intlayer/sync-json-plugin. Zachowajnext-intlzainstalowany: jest to peer dependency adaptera i dostarcza typy.Wskaż Intlayer na twoje wiadomości
intlayer.config.tsKopiuj kodSkopiuj kod do schowka
Plik
messages/{locale}.jsonpozostaje w swoim miejscu. Każdy klucz na najwyższym poziomie staje się słownikiem;useTranslations("about")mapuje do słownikaabout.Otocz next.config.ts
next.config.tsKopiuj kodSkopiuj kod do schowka
createNextIntlPlugin()komponujewithIntlayer(śledzenie zawartości, kompilacja słownika, przebieg optymalizacji) oraz aliasynext-intl→@intlayer/next-intldla Webpack i Turbopack. Zbuduj, a liczby w tabelach powyżej będą Twoje.
Co możesz usunąć później
Otwórz tabelę w oknie modalnym, aby wyraźnie zobaczyć całą zawartość
| Plik / wzorzec | Dlaczego |
|---|---|
getRequestConfig w src/i18n.ts | Brak ładowania wiadomości dla poszczególnych żądań. Zachowaj plik tylko, jeśli eksportuje również pomocniki createNavigation |
messages={...} na NextIntlClientProvider | Adapter odczytuje skompilowane wyjście; właściwość jest ignorowana i loguje ostrzeżenie w trakcie developmentu |
await getMessages() w layoutach | Z tego samego powodu |
Per-page pick(messages, [...]) | Compiler dokonuje wyboru, per component |
Co zyskujesz poza bajtami
- Typed keys.
useTranslations("about")jest typizowany względem skompilowanego słownikaabout.t("does.not.exist")to błąd TypeScript, nie fallback w runtime. npx intlayer testnie powiedzie się w CI, gdy w lokalizacji brakuje klucza.npx intlayer filltłumaczy brakujące klucze za pomocą wybranego przez Ciebie dostawcy (OpenAI, Anthropic, Mistral, Gemini...) używając Twojego własnego klucza i zapisuje wynik z powrotem domessages/{locale}.json.- Visual Editor i CMS pracują na tych samych słownikach, dzięki czemu osoby niebędące developerami mogą edytować
messages/fr.jsonprzez UI i plik się aktualizuje. - Stopniowe przejście do
.content.ts. Każdy komponent może przejść zuseTranslations("about")nauseIntlayer("about")z towarzyszącym plikiem content, jeden po jednym. Słowniki JSON i.content.tswspółistnieją i się łączą.
Ograniczenia, które warto znać przed rozpoczęciem
- Routing config przenieśli się do
intlayer.config.ts.createNavigation(routing)icreateMiddleware(routing)zachowują swoją sygnaturę, ale ignorują argument: locales, domyślny locale i strategia prefiksu pochodzą z konfiguracjiroutingIntlayera. Jeśli używasz zlokalizowanychpathnames(/about→/a-propos) znext-intl, adapter ich nie interpoluje;routing.rewriteIntlayera obejmuje ten przypadek, ale jest to oddzielna zmiana. useTranslations()bez namespace nie jest powiązany. Przebieg optymalizacji wymaga statycznego namespace, aby wiedzieć, który słownik zaimportować. Bare call nadal działa, za pośrednictwem runtime registry, który odwołuje się do każdego słownika, co jest dokładnie wyciekiem, który chciałeś usunąć. Przekaż namespace.- Adapter nie jest darmowy. 8.0 KB runtime w porównaniu do 5.5 KB dla
next-intlayer, i +6-7 KB na stronę w stosunku do natywnego buildu. To pokrywa koszt powierzchni APInext-intl. Jeśli osiągniesz punkt, w którym każdy component został przeniesiony douseIntlayer, porzuć adapter. messages,timeZone,nowna providerze są ignorowane. Formaterzy są wspierani przez natywnyIntli tylko locale wpływa na ich output; jeśli polegasz na wymuszonym time zone lub ustalonymnowdla hydration-stabilnych dat, obsługuj to w miejscu wywołania.
Kiedy używać którego?
- Zostań przy
next-intljeśli twoja aplikacja jest mała, bundle nie jest problemem, i twój zespół jest komfortowy z posiadaniem namespaces ipick()na stronę. - Użyj
@intlayer/next-intljeśli aktualnie używasznext-intli chcesz uzyskać korzyści w bundle, wyciekach i hydratacji, typed keys oraz narzędziach CLI / CMS bez pełnego przepisania. To jest rekomendowany punkt wejścia dla każdego istniejącego codebase'anext-intl. - Przejdź na native (
next-intlayer) dla nowych projektów lub gdy adapter spełnił swoją rolę. Jest to najlżejsza z trzech opcji (5.5 KB, +0.3 KB na stronę) i odblokowuje synchroniczne server components, pliki.content.tsper-component i pełny zestaw funkcji.
Powiązane porównania
- next-intl vs Intlayer (biblioteki, ten sam benchmark)
- i18next vs @intlayer/i18next (ta sama seria adapterów)
- Lingui vs @intlayer/lingui (seria tego samego adaptera)
- vue-i18n vs @intlayer/vue-i18n (seria tego samego adaptera)
- Przewodnik migracji: next-intl do Intlayer
- Dokumentacja adaptera kompatybilności: next-intl
Podsumowanie
@intlayer/next-intl robi jedną rzecz: zmienia to, do czego useTranslations jest powiązany, z providera zawierającego każdą wiadomość na słownik skompilowany dla tego komponentu. Na tej samej aplikacji Next.js, która wynosi 6 KB na stronę, komponenty 2,7x mniejsze, 0% wycieków i 2 ms hydratacji, zanim ktokolwiek otworzy plik komponentu. Nawigacja i middleware zachowują swój API na podstawie konfiguracji routingu Intlayer, a natywny runtime next-intlayer pozostaje jeszcze lżejszy.
Wszystkie surowe dane, aplikacje testowe i skrypty znajdują się w repozytorium Benchmark Bloom. Uruchom je sam.
Zapoznaj się z dokumentem 'Why Intlayer?' aby uzyskać więcej szczegółów.
Komentarze
Nie ma jeszcze komentarzy. Bądź pierwszą osobą, która podzieli się swoimi przemyśleniami.
