← Powrót do wpisów

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.

Pedro Duque
Pedro Duque
localization

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.