Zadaj pytanie i otrzymaj streszczenie dokumentu, odwołując się do tej strony i wybranego dostawcy AI
Dzięki integracji serwera Intlayer MCP z ulubionym asystentem AI możesz uzyskać dostęp do całej dokumentacji bezpośrednio z ChatGPT, DeepSeek, Cursor, VSCode itp.
Zobacz dokumentację serwera MCPTreść 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-i18next VS next-intl VS intlayer | Internacjonalizacja (i18n) Next.js

Przyjrzyjmy się podobieństwom i różnicom między trzema opcjami i18n dla Next.js: next-i18next, next-intl oraz Intlayer.
To nie jest pełny poradnik. To porównanie, które ma pomóc Ci w wyborze.
Skupiamy się na Next.js 13+ App Router (z React Server Components) i oceniamy:
w skrócie: Wszystkie trzy rozwiązania mogą lokalizować aplikację Next.js. Jeśli chcesz zawartość ograniczoną do komponentu, ścisłe typy TypeScript, sprawdzanie brakujących kluczy podczas kompilacji, słowniki poddane tree-shakingowi oraz pierwszorzędne wsparcie App Router + SEO, Intlayer jest najbardziej kompletnym i nowoczesnym wyborem.
Częstym nieporozumieniem wśród deweloperów jest myślenie, że next-intl to wersja Next.js react-intl. Tak nie jest, next-intl jest utrzymywany przez Amann, podczas gdy react-intl jest utrzymywany przez FormatJS.
W skrócie
- next-intl - Lekki, prosty formatowanie wiadomości z solidnym wsparciem Next.js. Centralizowane katalogi są powszechne; DX jest prosty, ale bezpieczeństwo i utrzymanie na dużą skalę pozostają głównie Twoją odpowiedzialnością.
- next-i18next - i18next w oprawie Next.js. Dojrzały ekosystem i funkcje dostępne przez wtyczki (np. ICU), ale konfiguracja może być rozbudowana, a katalogi mają tendencję do centralizacji wraz z rozwojem projektów.
- Intlayer - Model zawartości skoncentrowany na komponentach dla Next.js, ścisłe typowanie TS, sprawdzanie podczas kompilacji, tree-shaking, wbudowane middleware i pomocniki SEO, opcjonalny Visual Editor/CMS oraz tłumaczenia wspomagane przez AI.
Odznaki aktualizują się automatycznie. Zrzuty ekranu mogą się zmieniać w czasie.
Porównanie funkcji obok siebie (skoncentrowane na Next.js)
| Funkcja | next-intlayer (Intlayer) | next-intl | next-i18next |
|---|---|---|---|
| Tłumaczenia blisko komponentów | ✅ Tak, zawartość zlokalizowana razem z każdym komponentem | ❌ Nie | ❌ Nie |
| Integracja z TypeScript | ✅ Zaawansowana, automatycznie generowane ścisłe typy | ✅ Dobra | ⚠️ Podstawowa |
| Wykrywanie brakujących tłumaczeń | ✅ Podświetlanie błędów TypeScript oraz błędy/ostrzeżenia podczas kompilacji | ⚠️ Obsługa awaryjna w czasie wykonywania | ⚠️ Obsługa awaryjna w czasie wykonywania |
| Bogata zawartość (JSX/Markdown/komponenty) | ✅ Bezpośrednie wsparcie | ❌ Nie zaprojektowane dla bogatych węzłów | ⚠️ Ograniczone |
| Tłumaczenie wspomagane AI | ✅ Tak, obsługuje wielu dostawców AI. Można używać własnych kluczy API. Uwzględnia kontekst Twojej aplikacji i zakres treści | ❌ Nie | ❌ Nie |
| Edytor wizualny | ✅ Tak, lokalny Edytor wizualny + opcjonalny CMS; może eksternalizować zawartość codebase; osadzalny | ❌ Nie / dostępny przez zewnętrzne platformy lokalizacyjne | ❌ Nie / dostępny przez zewnętrzne platformy lokalizacyjne |
| Lokalizowane routingi | ✅ Tak, obsługuje lokalizowane ścieżki od razu (działa z Next.js i Vite) | ✅ Wbudowany, App Router obsługuje segment [locale] | ✅ Wbudowany |
| Generowanie dynamicznych tras | ✅ Tak | ✅ Tak | ✅ Tak |
| Pluralizacja | ✅ Wzorce oparte na enumeracji | ✅ Dobre | ✅ Dobre |
| Formatowanie (daty, liczby, waluty) | ✅ Zoptymalizowane formatery (Intl w tle) | ✅ Dobre (pomocniki Intl) | ✅ Dobre (pomocniki Intl) |
| Format treści | ✅ .tsx, .ts, .js, .json, .md, .txt, (.yaml WIP) | ✅ .json, .js, .ts | ⚠️ .json |
| Wsparcie ICU | ⚠️ W trakcie realizacji | ✅ Tak | ⚠️ Za pomocą wtyczki (i18next-icu) |
| Narzędzia SEO (hreflang, sitemap) | ✅ Wbudowane narzędzia: pomocniki do sitemap, robots.txt, metadanych | ✅ Dobre | ✅ Dobre |
| Ekosystem / Społeczność | ⚠️ Mniejsza, ale szybko rosnąca i reagująca | ✅ Dobra | ✅ Dobra |
| Renderowanie po stronie serwera i komponenty serwerowe | ✅ Tak, zoptymalizowane pod SSR / React Server Components | ⚠️ Obsługiwane na poziomie strony, ale wymaga przekazywania funkcji t w drzewie komponentów dla dzieci komponentów serwerowych | ⚠️ Obsługiwane na poziomie strony, ale wymaga przekazywania funkcji t w drzewie komponentów dla dzieci komponentów serwerowych |
| Tree-shaking (ładowanie tylko używanej zawartości) | ✅ Tak, per-komponent podczas budowania za pomocą wtyczek Babel/SWC | ⚠️ Częściowo | ⚠️ Częściowo |
| Lazy loading (leniwe ładowanie) | ✅ Tak, per-lokalizacja / per-słownik | ✅ Tak (per-trasa/per-lokalizacja), wymaga zarządzania przestrzeniami nazw | ✅ Tak (per-trasa/per-lokalizacja), wymaga zarządzania przestrzeniami nazw |
| Usuwanie nieużywanych treści | ✅ Tak, na poziomie słownika podczas budowania | ❌ Nie, można zarządzać ręcznie za pomocą zarządzania przestrzenią nazw | ❌ Nie, można zarządzać ręcznie za pomocą zarządzania przestrzenią nazw |
| Zarządzanie dużymi projektami | ✅ Zachęca do modularności, odpowiednie dla systemów projektowych | ✅ Modularne z konfiguracją | ✅ Modularne z konfiguracją |
| Testowanie brakujących tłumaczeń (CLI/CI) | ✅ CLI: npx intlayer content test (audyt przyjazny CI) | ⚠️ Nie wbudowane; dokumentacja sugeruje npx @lingual/i18n-check | ⚠️ Nie wbudowane; opiera się na narzędziach i18next / runtime saveMissing |
Wprowadzenie
Next.js oferuje wbudowane wsparcie dla internacjonalizowanego routingu (np. segmenty lokalizacji). Jednak ta funkcja sama w sobie nie wykonuje tłumaczeń. Nadal potrzebujesz biblioteki do renderowania zlokalizowanych treści dla użytkowników.
Istnieje wiele bibliotek i18n, ale w świecie Next.js obecnie trzy zyskują na popularności: next-i18next, next-intl oraz Intlayer.
Architektura i skalowalność
- next-intl / next-i18next: Domyślnie korzystają z centralizowanych katalogów dla każdego locale (oraz namespaces w i18next). Działa dobrze na początku, ale często staje się dużą wspólną powierzchnią z rosnącym sprzężeniem i zmianami kluczy.
- Intlayer: Zachęca do stosowania słowników per-komponent (lub per-funkcjonalność) współlokowanych z kodem, który obsługują. Zmniejsza to obciążenie poznawcze, ułatwia duplikację/migrację elementów UI oraz redukuje konflikty między zespołami. Nieużywana zawartość jest naturalnie łatwiejsza do wykrycia i usunięcia.
Dlaczego to ważne: W dużych bazach kodu lub systemach designu, modułowa zawartość skalują się lepiej niż monolityczne katalogi.
Rozmiary pakietów i zależności
Po zbudowaniu aplikacji, bundle to JavaScript, który przeglądarka załaduje, aby wyrenderować stronę. Rozmiar bundle jest więc istotny dla wydajności aplikacji.
Dwa komponenty są ważne w kontekście bundle aplikacji wielojęzycznej:
- Kod aplikacji
- Zawartość ładowana przez przeglądarkę
Kod aplikacji
Znaczenie kodu aplikacji jest w tym przypadku minimalne. Wszystkie trzy rozwiązania są tree-shakable, co oznacza, że nieużywane części kodu nie są dołączane do bundle.
Oto porównanie rozmiaru bundle JavaScript ładowanego przez przeglądarkę dla aplikacji wielojęzycznej z trzema rozwiązaniami.
Jeśli w aplikacji nie potrzebujemy żadnego formatera, lista eksportowanych funkcji po tree-shakingu będzie następująca:
- next-intlayer: useIntlayer, useLocale, NextIntlClientProvider, (Rozmiar bundla to 180,6 kB -> 78,6 kB (gzip))
- next-intl: useTranslations, useLocale, NextIntlClientProvider, (Rozmiar bundla to 101,3 kB -> 31,4 kB (gzip))
- next-i18next: useTranslation, useI18n, I18nextProvider, (Rozmiar bundla to 80,7 kB -> 25,5 kB (gzip))
Te funkcje są jedynie wrapperami wokół kontekstu/stanu React, więc całkowity wpływ biblioteki i18n na rozmiar bundla jest minimalny.
Intlayer jest nieco większy niż next-intl i next-i18next, ponieważ zawiera więcej logiki w funkcji useIntlayer. Jest to związane z integracją markdown i intlayer-editor.
Zawartość i tłumaczenia
Ta część jest często ignorowana przez deweloperów, ale rozważmy przypadek aplikacji składającej się z 10 stron w 10 językach. Załóżmy, że każda strona zawiera w 100% unikalną treść, aby uprościć obliczenia (w rzeczywistości wiele treści jest powtarzalnych między stronami, np. tytuł strony, nagłówek, stopka itp.).
Użytkownik chcący odwiedzić stronę /fr/about załaduje zawartość jednej strony w danym języku. Ignorowanie optymalizacji treści oznaczałoby niepotrzebne załadowanie 8 200% ((1 + (((10 stron - 1) × (10 języków - 1)))) × 100) zawartości aplikacji. Widzisz problem? Nawet jeśli ta zawartość pozostaje tekstem, a Ty prawdopodobnie wolisz myśleć o optymalizacji obrazów na swojej stronie, wysyłasz niepotrzebną treść na cały świat i zmuszasz komputery użytkowników do jej przetwarzania bez powodu.
Dwa ważne zagadnienia:
Podział według ścieżki:
Jeśli jestem na stronie /about, nie chcę ładować zawartości strony /home
Podział według lokalizacji:
Jeśli jestem na stronie /fr/about, nie chcę ładować zawartości strony /en/about
Ponownie, wszystkie trzy rozwiązania zdają sobie sprawę z tych problemów i pozwalają zarządzać tymi optymalizacjami. Różnica między tymi trzema rozwiązaniami to DX (Developer Experience).
next-intl i next-i18next używają scentralizowanego podejścia do zarządzania tłumaczeniami, pozwalając na podział plików JSON według lokalizacji i podplików. W next-i18next nazywamy pliki JSON 'namespaces'; next-intl pozwala deklarować wiadomości. W intlayer nazywamy pliki JSON 'dictionaries'.
- W przypadku next-intl, podobnie jak w next-i18next, zawartość jest ładowana na poziomie strony/layoutu, a następnie ta zawartość jest ładowana do providera kontekstu. Oznacza to, że deweloper musi ręcznie zarządzać plikami JSON, które będą ładowane dla każdej strony.
W praktyce oznacza to, że deweloperzy często pomijają tę optymalizację, woląc dla uproszczenia załadować całą zawartość w providerze kontekstu strony.
- W przypadku intlayer cała zawartość jest ładowana w aplikacji. Następnie wtyczka (@intlayer/babel / @intlayer/swc) zajmuje się optymalizacją bundla, ładując tylko zawartość używaną na stronie. Deweloper nie musi więc ręcznie zarządzać słownikami, które będą ładowane. Pozwala to na lepszą optymalizację, lepszą utrzymywalność oraz skraca czas developmentu.
W miarę rozwoju aplikacji (szczególnie gdy nad aplikacją pracuje wielu programistów), często zdarza się zapomnieć o usunięciu treści, które nie są już używane, z plików JSON.
Należy zauważyć, że wszystkie pliki JSON są ładowane we wszystkich przypadkach (next-intl, next-i18next, intlayer).
Dlatego podejście Intlayer jest bardziej wydajne: jeśli komponent nie jest już używany, jego słownik nie jest ładowany do bundla.
Równie ważne jest, jak biblioteka obsługuje mechanizm fallbacków. Załóżmy, że aplikacja domyślnie jest w języku angielskim, a użytkownik odwiedza stronę /fr/about. Jeśli tłumaczenia we francuskim są niekompletne, zostanie zastosowany fallback na angielski.
W przypadku next-intl i next-i18next biblioteka wymaga załadowania pliku JSON związanego z bieżącym locale, ale także z locale zapasowym (fallback). W związku z tym, zakładając, że cały content został przetłumaczony, każda strona załaduje 100% niepotrzebnej zawartości. Dla porównania, intlayer przetwarza fallback podczas budowania słownika. W ten sposób każda strona załaduje tylko używaną zawartość.
Uwaga: Aby zoptymalizować bundle za pomocą intlayer, musisz ustawić opcję importMode: 'dynamic' w pliku intlayer.config.ts. I upewnić się, że wtyczka @intlayer/babel / @intlayer/swc jest zainstalowana (domyślnie instalowana przy użyciu vite-intlayer).
Oto przykład wpływu optymalizacji rozmiaru bundle za pomocą intlayer w aplikacji vite + react:
| Zoptymalizowany pakiet | Pakiet niezoptymalizowany |
|---|---|
| |
TypeScript i bezpieczeństwo
next-i18next
- Podstawowe typy dla hooków. ścisłe typowanie kluczy wymaga dodatkowych narzędzi/konfiguracji.
next-intl
- Solidne wsparcie TypeScript, ale klucze nie są domyślnie ściśle typowane. Bezpieczeństwo utrzymasz ręcznie.
intlayer
- Generuje ścisłe typy na podstawie twoich treści. Autouzupełnianie w IDE oraz błędy podczas kompilacji wychwytują literówki i brakujące klucze przed wdrożeniem.
Dlaczego to ważne: Silne typowanie przesuwa błędy na wcześniejszy etap (CI/build), zamiast na późniejszy (czas działania).
Obsługa brakujących tłumaczeń
next-i18next
- Polega na fallbackach w czasie działania. Budowanie nie kończy się błędem.
next-intl
- Polega na fallbackach w czasie działania. Budowanie nie kończy się błędem.
intlayer
- Wykrywanie podczas kompilacji z ostrzeżeniami/błędami dla brakujących lokalizacji lub kluczy.
Dlaczego to ważne: Wykrywanie braków podczas kompilacji zapobiega pojawianiu się 'undefined' w produkcji.
Routing, middleware i strategia URL
next-i18next
- Umożliwia lokalizowany routing. Jednak middleware nie jest wbudowane.
next-intl
- Umożliwia lokalizowany routing.
- Dostarcza middleware.
intlayer
- Umożliwia lokalizowany routing.
- Dostarcza middleware.
Dlaczego to ważne: Pomaga w SEO i odkrywalności, a także poprawia doświadczenie użytkownika.
Wsparcie dla Server Components (RSC)
next-i18next
- Wspiera komponenty serwerowe stron i layoutów.
- Nie zapewnia synchronicznego API dla dziecięcych komponentów serwerowych.
next-intl
- Obsługuje komponenty serwerowe stron i układów.
- Nie zapewnia synchronicznego API dla dziecięcych komponentów serwerowych.
intlayer
- Obsługuje komponenty serwerowe stron i układów.
- Zapewnia synchroniczne API dla dziecięcych komponentów serwerowych.
Dlaczego to ważne: Wsparcie komponentów serwerowych jest kluczową funkcją Next.js 13+, wspomagającą wydajność. Przekazywanie propsów takich jak locale lub funkcji t z komponentu nadrzędnego do dziecięcych komponentów serwerowych sprawia, że Twoje komponenty są mniej wielokrotnego użytku.
Integracja z platformami lokalizacyjnymi (TMS)
Duże organizacje często polegają na Systemach Zarządzania Tłumaczeniami (TMS), takich jak Crowdin, Phrase, Lokalise, Localizely czy Localazy.
Dlaczego firmy zwracają na to uwagę
- Współpraca i role: Zaangażowanych jest wielu uczestników: deweloperzy, menedżerowie produktu, tłumacze, recenzenci, zespoły marketingowe.
- Skala i efektywność: ciągła lokalizacja, przegląd w kontekście.
next-intl / next-i18next
- Zazwyczaj używają scentralizowanych katalogów JSON, więc eksport/import z TMS jest prosty.
- Dojrzałe ekosystemy oraz przykłady/integracje dla wymienionych platform.
Intlayer
- Zachęca do zdecentralizowanych słowników per komponent i obsługuje zawartość w formatach TypeScript/TSX/JS/JSON/MD.
- Poprawia to modularność w kodzie, ale może utrudniać integrację typu plug-and-play z TMS, gdy narzędzie oczekuje scentralizowanych, płaskich plików JSON.
- Intlayer oferuje alternatywy: tłumaczenia wspomagane AI (z użyciem własnych kluczy dostawcy), Edytor Wizualny/CMS oraz workflowy CLI/CI do wykrywania i wypełniania luk.
Uwaga: next-intl i i18next również akceptują katalogi TypeScript. Jeśli Twój zespół przechowuje wiadomości w plikach .ts lub decentralizuje je według funkcji, możesz napotkać podobne problemy z TMS. Jednak wiele konfiguracji next-intl pozostaje scentralizowanych w folderze locales/, co jest nieco łatwiejsze do przekształcenia na JSON dla TMS.
Doświadczenie dewelopera
Ta część dokonuje dogłębnego porównania trzech rozwiązań. Zamiast rozważać proste przypadki, opisane w dokumentacji „getting started” dla każdego rozwiązania, rozważymy rzeczywisty przypadek użycia, bardziej zbliżony do prawdziwego projektu.
Struktura aplikacji
Struktura aplikacji jest ważna, aby zapewnić dobrą utrzymywalność Twojej bazy kodu.
.├── i18n.ts├── locales│ ├── en│ │ ├── home.json│ │ └── navbar.json│ ├── fr│ │ ├── home.json│ │ └── navbar.json│ └── es│ ├── home.json│ └── navbar.json└── src ├── middleware.ts ├── app │ ├── i18n │ │ └── server.ts │ └── [locale] │ └── home.tsx └── components └── Navbar └── index.tsxPorównanie
- next-intl / next-i18next: Centralizowane katalogi (JSON; przestrzenie nazw/wiadomości). Jasna struktura, dobrze integruje się z platformami tłumaczeniowymi, ale może prowadzić do większej liczby edycji między plikami w miarę rozwoju aplikacji.
- Intlayer: Słowniki .content.{ts|js|json} przypisane do komponentów i umieszczone razem z nimi. Ułatwia ponowne użycie komponentów i lokalne rozumowanie; dodaje pliki i opiera się na narzędziach działających podczas budowania.
Konfiguracja i ładowanie zawartości
Jak wspomniano wcześniej, musisz zoptymalizować sposób importowania każdego pliku JSON do swojego kodu. Sposób, w jaki biblioteka obsługuje ładowanie zawartości, jest ważny.
Skopiuj kod do schowka
import { getRequestConfig } from "next-intl/server";import { notFound } from "next/navigation";export const locales = ["en", "fr", "es"] as const;export const defaultLocale = "en" as const;async function loadMessages(locale: string) { // Załaduj tylko przestrzenie nazw potrzebne dla twojego layoutu/stron const [common, about] = await Promise.all([ import(`../locales/${locale}/common.json`).then((m) => m.default), import(`../locales/${locale}/about.json`).then((m) => m.default), ]); return { common, about } as const;}export default getRequestConfig(async ({ locale }) => { if (!locales.includes(locale as any)) notFound(); return { messages: await loadMessages(locale), };});Skopiuj kod do schowka
import type { ReactNode } from "react";import { locales } from "@/i18n";import { getLocaleDirection, unstable_setRequestLocale,} from "next-intl/server";export const dynamic = "force-static";export function generateStaticParams() { return locales.map((locale) => ({ locale }));}export default async function LocaleLayout({ children, params,}: { children: ReactNode; params: Promise<{ locale: string }>;}) { const { locale } = await params; // Ustaw aktywny język żądania dla tego renderowania po stronie serwera (RSC) unstable_setRequestLocale(locale); const dir = getLocaleDirection(locale); return ( <html lang={locale} dir={dir}> <body>{children}</body> </html> );}Skopiuj kod do schowka
import { getTranslations, getMessages, getFormatter } from "next-intl/server";import { NextIntlClientProvider } from "next-intl";import pick from "lodash/pick";import ServerComponent from "@/components/ServerComponent";import ClientComponentExample from "@/components/ClientComponentExample";export const dynamic = "force-static";export default async function AboutPage({ params,}: { params: Promise<{ locale: string }>;}) { const { locale } = await params; // Wiadomości są ładowane po stronie serwera. Przekaż tylko to, co jest potrzebne klientowi. const messages = await getMessages(); const clientMessages = pick(messages, ["common", "about"]); // Tłumaczenia/formatowanie wyłącznie po stronie serwera const tAbout = await getTranslations("about"); const tCounter = await getTranslations("about.counter"); const format = await getFormatter(); const initialFormattedCount = format.number(0); return ( <NextIntlClientProvider locale={locale} messages={clientMessages}> <main> <h1>{tAbout("title")}</h1> <ClientComponentExample /> <ServerComponent formattedCount={initialFormattedCount} label={tCounter("label")} increment={tCounter("increment")} /> </main> </NextIntlClientProvider> );}Porównanie
Wszystkie trzy wspierają ładowanie treści i providerów dla poszczególnych lokalizacji.
W przypadku next-intl/next-i18next zazwyczaj ładujesz wybrane wiadomości/przestrzenie nazw na trasę i umieszczasz providery tam, gdzie są potrzebne.
W przypadku Intlayer dodaje analizę w czasie budowania, aby wywnioskować użycie, co może zmniejszyć ręczne łączenie i pozwolić na pojedynczego providera na poziomie root.
Wybierz między kontrolą eksplicytną a automatyzacją w zależności od preferencji zespołu.
Użycie w komponencie klienckim
Weźmy przykład komponentu klienckiego renderującego licznik.
Tłumaczenia (zachowano strukturę; załaduj je do wiadomości next-intl według własnego uznania)
Skopiuj kod do schowka
{ "counter": { "label": "Counter", "increment": "Increment" }}Skopiuj kod do schowka
{ "counter": { "label": "Compteur", "increment": "Incrémenter" }}Komponent klienta
Skopiuj kod do schowka
"use client";import React, { useState } from "react";import { useTranslations, useFormatter } from "next-intl";const ClientComponentExample = () => { // Bezpośredni zakres do zagnieżdżonego obiektu const t = useTranslations("about.counter"); const format = useFormatter(); const [count, setCount] = useState(0); return ( <div> <p>{format.number(count)}</p> <button aria-label={t("label")} onClick={() => setCount((count) => count + 1)} > {t("increment")} </button> </div> );};Nie zapomnij dodać komunikatu "about" na stronie klienta
Porównanie
Formatowanie liczb
- next-i18next: brak useNumber; używa Intl.NumberFormat (lub i18next-icu).
- next-intl: useFormatter().number(value).
- Intlayer: wbudowane useNumber().
Klucze
- Zachowaj zagnieżdżoną strukturę (about.counter.label) i odpowiednio ustaw zakres hooka (useTranslation("about") + t("counter.label") lub useTranslations("about.counter") + t("label")).
Lokalizacje plików
- next-i18next oczekuje plików JSON w public/locales/{lng}/{ns}.json.
- next-intl jest elastyczny; ładuj wiadomości według własnej konfiguracji.
- Intlayer przechowuje zawartość w słownikach TS/JS i rozwiązuje je za pomocą klucza.
Użycie w komponencie serwerowym
Weźmiemy pod uwagę przypadek komponentu UI. Ten komponent jest komponentem serwerowym i powinien mieć możliwość bycia wstawionym jako dziecko komponentu klienta. (strona (komponent serwerowy) -> komponent klienta -> komponent serwerowy). Ponieważ ten komponent może być wstawiony jako dziecko komponentu klienta, nie może być asynchroniczny.
Skopiuj kod do schowka
type ServerComponentProps = { t: (key: string) => string; locale: string; count: number; formatter: Intl.NumberFormat;};const ServerComponent = ({ t, locale, count, formatter,}: ServerComponentProps) => { const formatted = formatter.format(count); return ( <div> <p>{formatted}</p> <button aria-label={t("counter.label")}>{t("counter.increment")}</button> </div> );};export default ServerComponent;Ponieważ komponent serwera nie może być asynchroniczny, musisz przekazać tłumaczenia i funkcję formatującą jako propsy.
W twojej stronie / układzie:
- import { getTranslations, getFormatter } from "next-intl/server";
- const t = await getTranslations("about.counter");
- const formatter = await getFormatter().then((formatter) => formatter.number());
Intlayer udostępnia bezpieczne dla serwera hooki za pośrednictwem next-intlayer/server. Aby działały, useIntlayer i useNumber używają składni podobnej do hooków klienta, ale pod spodem zależą od kontekstu serwera (IntlayerServerProvider).
Metadane / Sitemap / Robots
Tłumaczenie treści jest świetne. Jednak ludzie często zapominają, że głównym celem internacjonalizacji jest uczynienie Twojej strony bardziej widoczną dla świata. I18n to niesamowita dźwignia do poprawy widoczności Twojej strony.
Oto lista dobrych praktyk dotyczących wielojęzycznego SEO.
- ustaw metatagi hreflang w tagu <head> > Pomaga to wyszukiwarkom zrozumieć, jakie języki są dostępne na stronie
- wymień wszystkie tłumaczenia stron w pliku sitemap.xml, używając schematu XML http://www.w3.org/1999/xhtml >
- nie zapomnij wykluczyć stron z prefiksami w pliku robots.txt (np. /dashboard oraz /fr/dashboard, /es/dashboard) >
- używaj niestandardowego komponentu Link, aby przekierować do najbardziej zlokalizowanej strony (np. po francusku <a href="/fr/about">A propos</a>) >
Programiści często zapominają o prawidłowym referencjonowaniu swoich stron w różnych lokalizacjach.
Skopiuj kod do schowka
import type { Metadata } from "next";import { locales, defaultLocale } from "@/i18n";import { getTranslations } from "next-intl/server";const localizedPath = (locale: string, path: string) => { // Zwraca ścieżkę lokalizowaną, jeśli locale jest domyślne, zwraca oryginalną ścieżkę return locale === defaultLocale ? path : "/" + locale + path;};type GenerateMetadataParams = { params: Promise<{ locale: string; }>;};export const generateMetadata = async ({ params,}: GenerateMetadataParams): Promise<Metadata> => { const { locale } = await params; const t = await getTranslations({ locale, namespace: "about" }); const url = "/about"; const languages = Object.fromEntries( locales.map((locale) => [locale, localizedPath(locale, url)]) ); return { title: t("title"), description: t("description"), alternates: { canonical: localizedPath(locale, url), languages: { ...languages, "x-default": url }, }, };};// ... Reszta kodu stronySkopiuj kod do schowka
import type { MetadataRoute } from "next";import { locales, defaultLocale } from "@/i18n";const origin = "https://example.com";const formatterLocalizedPath = (locale: string, path: string) => locale === defaultLocale ? origin + path : origin + "/" + locale + path;export const sitemap = (): MetadataRoute.Sitemap => { const aboutLanguages = Object.fromEntries( locales.map((l) => [l, formatterLocalizedPath(l, "/about")]) ); return [ { url: formatterLocalizedPath(defaultLocale, "/about"), lastModified: new Date(), changeFrequency: "monthly", // częstotliwość zmian: miesięcznie priority: 0.7, // priorytet strony alternates: { languages: aboutLanguages }, // alternatywne wersje językowe }, ];};Skopiuj kod do schowka
import type { MetadataRoute } from "next";import { locales, defaultLocale } from "@/i18n";const origin = "https://example.com";const withAllLocales = (path: string) => [ path, ...locales .filter((locale) => locale !== defaultLocale) .map((locale) => "/" + locale + path),];export const robots = (): MetadataRoute.Robots => { const disallow = [ ...withAllLocales("/dashboard"), ...withAllLocales("/admin"), ]; return { rules: { userAgent: "*", allow: ["/"], disallow }, host: origin, sitemap: origin + "/sitemap.xml", };};Intlayer dostarcza funkcję getMultilingualUrls do generowania wielojęzycznych adresów URL dla Twojej mapy witryny.
Middleware do routingu lokalizacji
Dodaj middleware do obsługi wykrywania i routingu lokalizacji:
Skopiuj kod do schowka
import createMiddleware from "next-intl/middleware";import { locales, defaultLocale } from "@/i18n";export default createMiddleware({ locales: [...locales], defaultLocale, localeDetection: true,});export const config = { // Pomijaj API, wewnętrzne elementy Next oraz zasoby statyczne matcher: ["/((?!api|_next|.*\\..*).*)"],};Lista kontrolna konfiguracji i dobre praktyki
- Ustaw atrybuty html lang i dir: W pliku src/app/[locale]/layout.tsx oblicz dir za pomocą getLocaleDirection(locale) i ustaw <html lang={locale} dir={dir}>.
- Podziel wiadomości na przestrzenie nazw: Organizuj pliki JSON według lokalizacji i przestrzeni nazw (np. common.json, about.json).
- Minimalizuj obciążenie klienta: Na stronach wysyłaj do NextIntlClientProvider tylko wymagane przestrzenie nazw (np. pick(messages, ['common', 'about'])).
- Preferuj strony statyczne: Eksportuj export const dynamic = 'force-static' i generuj statyczne parametry dla wszystkich locales.
- Synchroniczne komponenty serwerowe: Utrzymuj komponenty serwerowe synchroniczne, przekazując wcześniej obliczone ciągi znaków (przetłumaczone etykiety, sformatowane liczby) zamiast wywołań asynchronicznych lub funkcji nieserializowalnych.
A zwycięzca to…
To nie jest proste. Każda opcja ma swoje kompromisy. Oto jak to widzę:
next-i18next
- dojrzały, pełen funkcji, wiele wtyczek społecznościowych, ale wyższy koszt konfiguracji. Jeśli potrzebujesz ekosystemu wtyczek i18next (np. zaawansowane reguły ICU przez wtyczki) i Twój zespół już zna i18next, akceptując więcej konfiguracji dla elastyczności.
next-intl
- najprostszy, lekki, mniej narzuconych decyzji. Jeśli chcesz minimalne rozwiązanie, czujesz się komfortowo z centralizowanymi katalogami, a Twoja aplikacja jest mała do średniej wielkości.
Intlayer
- zbudowany dla nowoczesnego Next.js, z modularną zawartością, bezpieczeństwem typów, narzędziami i mniejszą ilością boilerplate. Jeśli cenisz sobie zawartość ograniczoną do komponentu, ścisły TypeScript, gwarancje w czasie kompilacji, tree-shaking oraz wbudowane narzędzia do routingu/SEO/edytora – szczególnie dla Next.js App Router, systemów projektowych i dużych, modularnych baz kodu.
Jeśli wolisz minimalną konfigurację i akceptujesz trochę ręcznego łączenia, next-intl jest dobrym wyborem. Jeśli potrzebujesz wszystkich funkcji i nie przeszkadza Ci złożoność, next-i18next sprawdzi się. Ale jeśli chcesz nowoczesne, skalowalne, modularne rozwiązanie z wbudowanymi narzędziami, Intlayer ma na celu dostarczyć to od razu po wyjęciu z pudełka.
Alternatywa dla zespołów korporacyjnych: Jeśli potrzebujesz sprawdzonego rozwiązania, które działa doskonale z uznanymi platformami lokalizacyjnymi, takimi jak Crowdin, Phrase lub innymi profesjonalnymi systemami zarządzania tłumaczeniami, rozważ next-intl lub next-i18next ze względu na ich dojrzały ekosystem i sprawdzone integracje.
Przyszła mapa drogowa: Intlayer planuje również rozwijać wtyczki działające na bazie rozwiązań i18next i next-intl. Dzięki temu zyskasz zalety Intlayer w zakresie automatyzacji, składni i zarządzania treścią, zachowując jednocześnie bezpieczeństwo i stabilność zapewnianą przez te sprawdzone rozwiązania w kodzie aplikacji.
Gwiazdki GitHub (GitHub STARs)
Gwiazdy na GitHubie są silnym wskaźnikiem popularności projektu, zaufania społeczności oraz jego długoterminowej istotności. Choć nie są bezpośrednią miarą jakości technicznej, odzwierciedlają, ilu deweloperów uważa projekt za przydatny, śledzi jego rozwój i prawdopodobnie zdecyduje się go używać. Przy szacowaniu wartości projektu, gwiazdy pomagają porównać zainteresowanie różnymi alternatywami oraz dostarczają wglądu w rozwój ekosystemu.
Podsumowanie
Wszystkie trzy biblioteki odnoszą sukces w podstawowej lokalizacji. Różnica polega na tym, ile pracy musisz włożyć, aby osiągnąć solidną, skalowalną konfigurację w nowoczesnym Next.js:
- W przypadku Intlayer modularna zawartość, ścisły TS, bezpieczeństwo w czasie kompilacji, tree-shaken bundles oraz pierwszorzędny App Router i narzędzia SEO są domyślne, a nie obowiązkowe.
- Jeśli Twój zespół ceni łatwość utrzymania i szybkość w aplikacji wielojęzycznej, opartej na komponentach, Intlayer oferuje dziś najpełniejsze doświadczenie.
Zapoznaj się z dokumentem 'Dlaczego Intlayer?' po więcej szczegółów.