Mówienie wszystkimi językami: jak działa lokalizacja w tym starterze
Lokalizacja nie jest funkcją, którą dodaje się po wdrożeniu. To decyzja podejmowana na poziomie architektury — w routingu, modelu treści i narzędziach Studio. Ten starter podejmuje tę decyzję za Ciebie, łącząc next-intl oraz @tinloof/sanity-document-i18n, tak aby cały stack od pierwszego dnia „mówił” wszystkimi językami.

1. Dwie warstwy i18n
Przy budowie wielojęzycznej strony trzeba rozwiązać dwa różne problemy.
- Tłumaczenia UI — etykiety, nawigacja, formaty dat, liczby mnogie. Żyją w kodzie.
- Tłumaczenia treści — wpisy blogowe, strony, copy marketingowe. Żyją w CMS.
Większość starterów rozwiązuje tylko jeden z tych problemów i zostawia drugi jako „zadanie dla użytkownika”. Ten rozwiązuje oba.
2. Warstwa UI: next-intl
next-intl obsługuje wszystko, co należy do kodu:
- Routing z prefiksem locale —
/en/about,/pt/sobre,/pl/o-nas - Typowane katalogi wiadomości w plikach JSON per locale
- Obsługę liczby mnogiej, formatowania dat i liczb
- Wsparcie dla React Server Components
- Jeden middleware, który negocjuje locale, obsługuje przekierowania i spina całość
Konfiguracja routingu znajduje się w jednym pliku i jest współdzielona między middleware a API nawigacji. Dodaj nowy locale — routing, przekierowania i linki alternatywne aktualizują się automatycznie.
Negocjacja locale działa według jasnej kolejności: najpierw prefiks w URL, potem zapisany cookie, następnie nagłówek Accept-Language, a na końcu domyślny język. Użytkownik trafia od razu na właściwy język i pozostaje w nim przy kolejnych wizytach.
W Server Components tłumaczenia są ładowane per request i w pełni typowane względem katalogów. W Client Components dostęp do nich jest synchroniczny. Provider w głównym layoutcie łączy stronę serwerową i kliencką, dzięki czemu wiadomości nie są duplikowane w bundle.
3. Warstwa treści: @tinloof/sanity-document-i18n
UI to tylko połowa problemu. Treści edytowane w Sanity Studio również muszą być tłumaczone, recenzowane i publikowane per locale.
@tinloof/sanity-document-i18n to community fork oficjalnego pluginu @sanity/document-internationalization, rozszerzony o lepsze zarządzanie template’ami i bardziej opiniowany setup tłumaczeń na poziomie dokumentu.
Plugin dodaje:
- Zakładkę Translations w edytorze — jeden dokument na locale, powiązany metadanymi tłumaczeń
- Oznaczenie języka na każdym dokumencie, aby edytor zawsze wiedział, w jakim locale pracuje
- Mechanizm fallback — brakujące tłumaczenia wracają do domyślnego języka
- Preview per locale w Sanity Studio — dokładny podgląd strony dla każdego języka przed publikacją
- Szablony inicjalne per locale, aby nowe dokumenty były od razu poprawnie skonfigurowane
Każdy typ dokumentu obsługujący tłumaczenia jest zarejestrowany w konfiguracji pluginu wraz z listą wspieranych języków. Każdy dokument zawiera ukryte pole języka, które GROQ wykorzystuje do filtrowania treści — jasno, explicite i przewidywalnie.
4. Jak te dwie warstwy się łączą
Segment locale w URL jest jedynym źródłem prawdy.
Middleware next-intl odczytuje request, negocjuje locale i udostępnia go wszystkim Server Components w drzewie.
Warstwa danych używa tego samego locale do zapytań do Sanity, aby pobrać treści w odpowiednim języku. Locale przepływa z góry na dół — od URL, przez layout, katalog tłumaczeń UI, aż po zapytanie GROQ. Bez ukrytej magii i bez niespójności między routerem a CMS.
5. Dodanie nowego języka
Rozszerzenie startera o nowy język to trzy kroki:
- Dodanie do konfiguracji routingu — nowy prefiks URL działa natychmiast
- Dodanie pliku wiadomości — np.
messages/de.json— dla UI - Dodanie języka do pluginu Sanity — edytorzy mogą tworzyć dokumenty w nowym języku
Bez zmian w layoutach, komponentach czy zapytaniach. Architektura naturalnie przyjmuje nowe locale.
Dlaczego to ma znaczenie
Większość systemów lokalizacji jest podzielona między frontend i CMS bez wspólnego kontraktu — frontend obsługuje routing, CMS treści, a oba systemy nie zawsze zgadzają się co do locale i fallbacków.
Ten starter sprawia, że zgadzają się z definicji:
- Lista locale jest definiowana raz w routingu i odwzorowana w konfiguracji Sanity
- next-intl obsługuje warstwę UI i routingu bez dodatkowego kodu
- @tinloof/sanity-document-i18n obsługuje warstwę treści z preview i fallbackiem
- Zapytania GROQ są świadome locale od samego początku — bez retrofittingu
Efekt to stack, w którym dodanie nowego języka jest operacją addytywną, a nie inwazyjną. Edytorzy pracują w Studio, które pokazuje właściwy język. Developerzy wykonują zapytania do CMS, który zwraca właściwe dokumenty. Użytkownicy trafiają na URL-e w swoim języku.
To jest kontrakt, który ten starter egzekwuje — i działa on w każdej skali.