Internationalization (i18n) w Next.js
next-intl dla App Router, locale routing, ICU message format, Intl API, hreflang SEO i workflow zarządzania tłumaczeniami.
6 bibliotek i18n dla React i Next.js
next-intl, next-i18next, react-i18next, Paraglide, Tolgee i Intl API — podejścia do internacjonalizacji od App Router po bundle-size-critical.
| Biblioteka | Framework | Podejście | Format | Kiedy |
|---|---|---|---|---|
| next-intl | Next.js App Router | Server + Client components | ICU messages | Next.js 13+ — domyślny wybór |
| next-i18next | Next.js Pages Router | getServerSideProps + useTranslation | JSON namespaces | Legacy Next.js Pages Router |
| react-i18next | React (Vite, CRA) | Context + hooks | JSON namespaces | Dowolna React app bez Next.js routing |
| Paraglide JS | Framework-agnostic | Compile-time type-safe | Message functions | Type safety critical, bundle size |
| Tolgee | React / Vue / Angular | In-context editing + SDK | JSON + TMS | Non-dev tłumacze, in-context workflow |
| Intl API (native) | Wszystkie | Browser native | CLDR data | Formatowanie liczb, dat, walut |
Często zadawane pytania
Co to jest i18n (internationalization) i jak wdrożyć w Next.js?
i18n (internationalization): przygotowanie aplikacji na wiele języków i regionów. i18n = 18 liter między 'i' a 'n'. L10n (localization): tłumaczenie dla konkretnego języka/regionu. g11n (globalization): połączenie obu. Next.js App Router i18n: brak wbudowanego i18n (w przeciwieństwie do Pages Router). Rekomendowane: next-intl, next-i18next (tylko Pages Router), Paraglide. next-intl (rekomendowany 2024): middleware do routing. app/[locale]/page.tsx — locale w URL. getTranslations('HomePage') w Server Components. useTranslations('HomePage') w Client Components. Plik wiadomości: messages/pl.json i messages/en.json. Struktura: {HomePage: {title: 'Witaj', description: 'Opis'}}. t('title') w komponentach. Pages Router next-i18next: i18n config w next.config.js. useTranslation('common'). serverSideTranslations(locale, ['common']). getStaticPaths z locale. Locale detection: middleware — Accept-Language header. Cookie (user preference). URL prefix /pl, /en. Subdomain pl.example.com. ICU message format: t('greeting', {name: 'Adam'}) + 'Witaj {name}'. Pluralization: {count, plural, one {# produkt} other {# produktów}}. Date/number formatting: Intl.DateTimeFormat, Intl.NumberFormat — native JS. t.rich() — formatowanie z JSX (linki, bold).
next-intl — jak skonfigurować i używać w Next.js App Router?
next-intl instalacja: npm install next-intl. Struktura plików: messages/ (pl.json, en.json). i18n.ts (routing config). middleware.ts (locale detection). app/[locale]/layout.tsx (locale provider). app/[locale]/page.tsx. i18n.ts konfiguracja: import {defineRouting} from 'next-intl/routing'. export const routing = defineRouting({locales: ['pl', 'en'], defaultLocale: 'pl'}). middleware.ts: import {createMiddleware} from 'next-intl/middleware'. export default createMiddleware(routing). export const config = {matcher: ['/', '/(pl|en)/:path*']}. layout.tsx: import {NextIntlClientProvider} from 'next-intl'. import {getMessages} from 'next-intl/server'. const messages = await getMessages(). NextIntlClientProvider locale={locale} messages={messages}. Server Component: import {getTranslations} from 'next-intl/server'. const t = await getTranslations('HomePage'). t('title'). Client Component: 'use client'. import {useTranslations} from 'next-intl'. const t = useTranslations('HomePage'). t('title'). Link i navigation: import {Link} from '@/i18n/navigation'. href='/about' — automatycznie dodaje locale prefix. import {usePathname, useRouter} from '@/i18n/navigation'. Locale switcher: router.replace(pathname, {locale: 'en'}). Statyczne generowanie: generateStaticParams: routing.locales.map(locale => ({locale})). SEO: alternates.languages w generateMetadata.
Formatowanie liczb, dat, walut i plural rules — Intl API?
Intl API (native JS): pełne wsparcie formatowania bez zewnętrznych bibliotek. Intl.NumberFormat: new Intl.NumberFormat('pl-PL', {style: 'currency', currency: 'PLN'}).format(1234.56) = '1 234,56 zł'. style: 'decimal', 'percent', 'currency', 'unit'. unit: 'kilometer', 'liter', 'kilogram'. Intl.DateTimeFormat: new Intl.DateTimeFormat('pl-PL', {dateStyle: 'full', timeStyle: 'short'}).format(new Date()) = 'sobota, 13 kwietnia 2024, 12:00'. Options: year, month, day, weekday, hour, minute. timeZone: 'Europe/Warsaw'. Intl.RelativeTimeFormat: new Intl.RelativeTimeFormat('pl', {numeric: 'auto'}).format(-1, 'day') = 'wczoraj'. format(-3, 'month') = '3 miesiące temu'. Intl.PluralRules: new Intl.PluralRules('pl').select(1) = 'one'. select(2) = 'few'. select(5) = 'many'. Języki mają złożone reguły (PL: one/few/many). next-intl integracja: t.number(1234, {style: 'currency', currency: 'PLN'}). t.dateTime(new Date(), {dateStyle: 'medium'}). t.relativeTime(-1, 'day'). Pluralizacja next-intl: messages: {products: '{count, plural, =0 {Brak} one {# produkt} few {# produkty} many {# produktów} other {# produktu}}'} t('products', {count: 5}). Waluty i regiony: różne formaty liczb (. vs , separator). Kierunek tekstu: LTR/RTL (arabski, hebrajski). dir='rtl' na html. Tailwind RTL: rtl: prefix. date-fns/locale: locale-aware date parsing.
SEO dla wielojęzycznych stron — hreflang, canonical i sitemap?
hreflang: informuje Google o alternatywnych wersjach językowych. link rel='alternate' hreflang='pl' href='https://example.pl/pl/strona'. hreflang='en' href='https://example.pl/en/page'. hreflang='x-default' href='https://example.pl'. Musi być w każdej wersji językowej (wzajemne oznaczenia). Next.js generateMetadata: alternates: {canonical: '/pl/strona', languages: {'pl': '/pl/strona', 'en': '/en/page', 'x-default': '/'}}. Canonical URL: każda strona ma swój canonical w swoim języku. Nie wskazuj EN jako canonical dla PL strony. Sitemap: osobne sitemapy per język lub jedno z hreflang. next-sitemap: sitemapSize, robotsTxt, alternateRefs. app/sitemap.ts: return routing.locales.flatMap(locale => pages.map(page => ({url: ..., alternates: {languages: {...}}})). URL struktura: /pl/o-nas vs /en/about — przełożone URL (lepszy UX i SEO). /pl/o-nas vs /en/o-nas — łatwiejsze technicznie. Subdomain: pl.example.com (osobne crawl budget). Domain: example.pl vs example.com/pl. Google Search Console: osobne właściwości per język/kraj. Target country w GSC dla każdej wersji. Performance tracking: per locale metrics. Tłumaczenie meta: title i description w każdym języku. Unikaj auto-translate dla SEO — manualnie lub profesjonalnie. Open Graph per locale.
CMS i zarządzanie tłumaczeniami — Lokalise, Crowdin i workflow?
Wyzwania i18n: kto tłumaczy? jak synchronizować zmiany? jak unikać missing keys? Translation Management System (TMS): Lokalise: popularny TMS dla deweloperów. CLI, GitHub integration, AI translation. Crowdin: open-source friendly. podobny do Lokalise. Phrase (Memsource): enterprise. Weblate: open-source TMS. Tolgee: open-source, in-context editing, React SDK. Workflow: Developer dodaje nowy klucz w messages/en.json (primary language). CI/CD push do TMS (Lokalise CLI push). Tłumacz tłumaczy w TMS. CI/CD pull przetłumaczone (Lokalise CLI pull). PR z tłumaczeniami. Missing translations: next-intl: fallback na defaultLocale dla brakujących. Kompilacja: Paraglide-JS generuje type-safe funkcje (nie strings). Błąd przy missing kluczu w TS. Pluralization edge cases: polski ma 4 formy plural (zero/one/few/many). Arabski — 6 form. ICU message format obsługuje wszystko. i18n dla React Native: react-i18next działa też w RN. expo-localization — device locale. React i18next: useTranslation hook. Trans component dla JSX. i18nInstance.changeLanguage('pl'). Astro i18n: Astro 4+ wbudowane i18n routing. getRelativeLocaleUrl(). astro:i18n virtual module. Vue: vue-i18n (Options API, Composition API). Nuxt: @nuxtjs/i18n.
Powiązane artykuły
Skontaktuj się z nami
Porozmawiajmy o Twoim projekcie. Bezpłatna wycena w ciągu 24 godzin.
Wyślij zapytanie
Telefon
+48 790 814 814
Pon-Pt: 9:00 - 18:00
adam@fotz.pl
Odpowiadamy w ciągu 24h
Adres
Plac Wolności 16
61-739 Poznań