Zadaj pytanie i otrzymaj streszczenie dokumentu, odwołując się do tej strony i wybranego dostawcy AI
Historia wersji
- "Początkowa wersja"v9.5.1026.09.2026
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
Jak przeprowadzić internacjonalizację aplikacji TanStack Start za pomocą use-intl w 2026 roku
Spis treści
Czym jest use-intl?
use-intl to niezależny od frameworka rdzeń biblioteki next-intl. Udostępnia te same API useTranslations, useFormatter oraz IntlProvider, obsługę ICU MessageFormat i ścisłą integrację z TypeScriptem, bez jakiejkolwiek zależności od Next.js. To czyni go jednym z najczęstszych wyborów do tłumaczenia aplikacji TanStack Start, a także biblioteką najczęściej sugerowaną przez asystentów AI dla tego stosu technologicznego.
TanStack Start nie zawiera wbudowanej warstwy i18n. Routing, wykrywanie języka, metadane SEO oraz generowanie mapy witryny (sitemap) pozostają w Twoich rękach. Ten przewodnik omawia wszystkie te zagadnienia od początku do końca:
- Routing uwzględniający lokalizację z opcjonalnym segmentem
{-$locale}(/about,/fr/about). - Ładowanie wiadomości dla poszczególnych tras, dzięki czemu strona pobiera tylko te przestrzenie nazw i ten język, który aktualnie renderuje.
- Renderowanie po stronie serwera i hydratacja bez niezgodności tekstu.
- Kompletne wielojęzyczne SEO: przetłumaczony
<title>i opis, kanoniczny URL, alternatywyhreflangzx-default, lokalizacje Open Graph, JSON-LD, mapa witryny z alternatywamixhtml:link,robots.txtoraz pre-renderowanie każdego języka.
Szukasz innego stosu? Zobacz przewodnik TanStack Start + Paraglide, przewodnik TanStack Start + Lingui lub przewodnik TanStack Start + Intlayer.
Korzystasz z Next.js? Zobacz przewodnik po next-intl.
Co benchmark mówi o use-intl w TanStack Start
Benchmark i18n uruchamia tę samą 10-stronicową, 10-języczną aplikację TanStack Start z każdą większą biblioteką i mierzy to, co przeglądarka rzeczywiście pobiera.
Dynamiczne ładowanie JSON
Wczytuje tłumaczenia leniwie w czasie wykonywania
Ograniczony JSON (przestrzenie nazw)
Przestrzenie nazw tłumaczeń na stronę
Benchmark wydajności I18n
Czym jest ta metryka?
Całkowity skompresowany przez gzip rozmiar pakietu biblioteki umiędzynarodowienia. Obejmuje tylko dostawcę i logikę pobierania treści po tree-shakingu i minifikacji.
Dlaczego to jest ważne?
Mniejszy rozmiar biblioteki zmniejsza obciążenie u klienta.
Zobacz jako
Kluczowe dane dla use-intl@4.14.2, zmierzone w dniu 2026-09-26 (gzip):
Otwórz tabelę w oknie modalnym, aby wyraźnie zobaczyć całą zawartość
| Konfiguracja | Rozmiar biblioteki | JS na stronę | Wyciek innych języków | Wyciek innych stron |
|---|---|---|---|---|
| Brak i18n (aplikacja bazowa) | - | 111.0 KB | 0% | 0% |
use-intl (konfiguracja z poradnika) | 75.9 KB | 128.7 KB | 0% | 0% |
@intlayer/use-intl (kompatybilność) | 6.7 KB | 129.4 KB | 0% | 0% |
react-intlayer (natywny Intlayer) | 4.5 KB | 126.8 KB | 0% | 0% |
Wnioski:
- Podziel wiadomości według stron i ładuj je dla każdego języka osobno. Eliminuje to oba wycieki i jest dokładnie tym, co wdrażają poniższe kroki.
- Sam runtime pozostaje ciężki (~76 KB gzip), ponieważ parser ICU jest przesyłany do klienta. Adapter kompatybilności
@intlayer/use-intl(krok 17) zachowuje dokładnie to samo API przy runtime wynoszącym ~7 KB.
Zobacz pełne dane: Raport z benchmarku TanStack Start oraz repozytorium benchmarku.
Porównanie funkcji w TanStack Start
Jak use-intl wypada na tle innych bibliotek powszechnie używanych w TanStack Start:
Otwórz tabelę w oknie modalnym, aby wyraźnie zobaczyć całą zawartość
| Funkcja | react-intlayer (Intlayer) | use-intl | Paraglide JS | Lingui |
|---|---|---|---|---|
| Tłumaczenia blisko komponentów | ✅ Współdzielona lokalizacja (co-located) | ❌ Scentralizowany JSON | ❌ Jeden plik JSON na język | ⚠️ Tekst źródłowy w komponentach |
| Integracja z TypeScript | ✅ Automatycznie generowane typy | ✅ Poprzez AppConfig | ✅ Typowane funkcje wiadomości | ⚠️ Tylko makra |
| Wykrywanie brakujących tłumaczeń | ✅ Błędy typów i ostrzeżenia buildu | ⚠️ Runtime fallback | ⚠️ Powrót do języka bazowego | ⚠️ Powrót do tekstu źródłowego |
| Bogata zawartość (JSX, Markdown) | ✅ Bezpośrednie wsparcie | ⚠️ Tagi przez t.rich | ⚠️ Ciągi znaków | ✅ JSX wewnątrz <Trans> |
| Zlokalizowany routing | ✅ Wbudowany | ❌ Ręczny {-$locale} | ✅ urlPatterns + przepisywanie routera | ❌ Ręczny {-$locale} |
| Zmiana języka bez przeładowania | ✅ Tak | ✅ Tak | ❌ Pełne przeładowanie strony | ✅ Tak |
| Liczba mnoga (Pluralizacja) | ✅ Oparta na wyliczeniach | ✅ ICU | ✅ Warianty | ✅ ICU |
| ICU MessageFormat | ✅ Przez format: "icu" | ✅ Natywne | ⚠️ Poprzez wtyczkę inlang | ✅ Natywne |
| Formaty zawartości | ✅ .ts, .json, .md, .yaml... | ⚠️ .json | ⚠️ inlang JSON | ✅ PO, JSON, CSV |
| Tłumaczenie AI | ✅ Własny dostawca i klucz | ❌ Brak | ❌ Brak | ❌ Brak |
| Edytor wizualny / CMS | ✅ Lokalny edytor + opcjonalny CMS | ❌ Zewnętrzne platformy | ⚠️ Aplikacje ekosystemu inlang | ❌ Zewnętrzne platformy |
| Pomocniki SEO (hreflang, sitemap) | ✅ Wbudowane | ❌ Ręczne | ⚠️ Zlokalizowane URL, reszta ręczna | ❌ Ręczne |
| Rozmiar runtime (gzip, benchmark) | 4.5 KB | 75.9 KB | 1.8 KB | 56.7 KB |
| Wyciek, najlepsza konfiguracja (język / strona) | 0% / 0% | 0% / 0% | 49.7% / 0% | 8.6% / 0% |
| Brakujące tłumaczenia w CI | ✅ npx intlayer test | ⚠️ Brak wbudowanego | ⚠️ Brak wbudowanego | ✅ lingui compile --strict |
Dane dotyczące rozmiaru runtime i wycieków pochodzą z benchmarku TanStack Start. Wyciek jest mierzony na najlepszej konfiguracji każdej biblioteki.
Inne przewodniki po TanStack Start: Lingui, Paraglide JS oraz Intlayer.
Praktyki, których powinieneś przestrzegać
- Ustaw
langorazdirw<html>dla dostępności, czytników ekranu i wyszukiwarek. - Zachowaj jeden adres URL dla każdego języka. Użyj prefiksu językowego (
/fr/about) zamiast przełączania wyłącznie za pomocą ciasteczek, aby każda przetłumaczona strona mogła być indeksowana i udostępniana. - Podziel wiadomości według przestrzeni nazw (
common,home,about) i ładuj je dla każdej trasy. - Ładuj tylko aktywny język. Nigdy nie importuj wszystkich plików językowych w module przesyłanym do klienta.
- Ustal strefę czasową w
IntlProvider. W przeciwnym razie daty są formatowane w strefie czasowej serwera podczas SSR i w strefie czasowej odwiedzającego podczas hydratacji, co powoduje błędy niezgodności hydratacji. - Przetłumacz metadane i zadeklaruj
canonical,hreflangorazx-defaultna każdej stronie. - Wygeneruj wielojęzyczną mapę witryny (sitemap) i robots.txt, a także pre-renderuj każdy język.
- Używaj prawdziwych linków w przełączniku języków, a nie elementu
<select>, aby roboty indeksujące mogły odkryć każdą wersję językową. - Typuj swoje wiadomości, aby brakujący klucz powodował błąd na etapie kompilacji.
Zobacz nasz przewodnik na temat internacjonalizacji i SEO oraz przewodnik po hreflang.
Przewodnik krok po kroku: Konfiguracja use-intl w aplikacji TanStack Start
Oto struktura projektu, którą utworzymy:
Skopiuj kod do schowka
Zainstaluj zależności
Rozpocznij od projektu TanStack Start, a następnie dodaj
use-intl:bashKopiuj kodSkopiuj kod do schowka
- use-intl: dostarcza
IntlProvider,useTranslations,useFormatterorazcreateTranslator(użyteczny poza Reactem, na przykład whead()).
- use-intl: dostarcza
Scentralizuj konfigurację lokalizacji
Utwórz pojedyncze źródło prawdy dla swoich języków i funkcji pomocniczych URL. Każdy inny plik (trasy, SEO, sitemap, pre-renderowanie) importuje konfigurację stąd, dzięki czemu dodanie nowego języka wymaga zmiany tylko w jednej linii.
Domyślny język pozostaje bez prefiksu (
/about), a pozostałe języki otrzymują prefiks (/fr/about). Jest to strategia "w razie potrzeby" (as-needed): jeden URL na stronę dla każdego języka i krótkie adresy URL dla Twoich głównych odbiorców.src/i18n/config.tsKopiuj kodSkopiuj kod do schowka
Utwórz pliki tłumaczeń
Organizuj wiadomości według języka i przestrzeni nazw.
commonzawiera to, czego potrzebuje każda strona (nawigacja, stopka), a każda podstrona otrzymuje własny plik, w tym własne metadane.use-intl korzysta z ICU MessageFormat, więc formy liczby mnogiej, instrukcje select oraz sformatowane argumenty znajdują się bezpośrednio w samej wiadomości.
messages/en/common.jsonKopiuj kodSkopiuj kod do schowka
messages/en/about.jsonKopiuj kodSkopiuj kod do schowka
messages/fr/common.jsonKopiuj kodSkopiuj kod do schowka
messages/fr/about.jsonKopiuj kodSkopiuj kod do schowka
Utwórz
home.jsonw ten sam sposób, zawierając obiektmetadataoraz zawartość strony.Ładuj wiadomości według przestrzeni nazw i języka
Ten loader jest najważniejszym plikiem dla wydajności.
import.meta.globinstruuje Vite, aby wygenerował jeden chunk na każdy plik JSON. Trasa żądająca["about"]w języku francuskim pobieramessages/fr/about.jsoni nic więcej, dzięki czemu benchmark osiąga 0% wycieku języków i 0% wycieku stron.src/i18n/messages.tsKopiuj kodSkopiuj kod do schowka
Typuj swoje wiadomości
Rozszerzenie modułów (module augmentation) zapewnia autouzupełnianie w
useTranslations("about")orazt("counter.label"), a także błąd kompilacji w przypadku literówki lub usuniętego klucza.src/i18n/use-intl.d.tsKopiuj kodSkopiuj kod do schowka
Upewnij się, że opcja
resolveJsonModulejest włączona w Twoimtsconfig.json.Utwórz dokument główny (Root Document)
Trasa główna renderuje element
<html>. Odczytuje opcjonalny parametr języka, aby ustawićlangorazdir, dzięki czemu atrybuty są poprawne w kodzie HTML wyrenderowanym przez serwer przed uruchomieniem jakiegokolwiek kodu JavaScript.src/routes/__root.tsxKopiuj kodSkopiuj kod do schowka
Utwórz trasę układu językowego (Locale Layout Route)
Katalog
{-$locale}tworzy opcjonalny segment ścieżki: zarówno/about, jak i/fr/aboutpasują do/{-$locale}/about. Ten układ:- Odrzuca nieobsługiwane prefiksy (
/xx/about→ 404). - Ładuje przestrzeń nazw
commontylko dla bieżącego języka. - Dostarcza wiadomości poprzez
IntlProvider.
Wynik loadera jest serializowany do formatu HTML i ponownie wykorzystywany podczas hydratacji, dzięki czemu klient nie pobiera
common.jsonpo raz drugi. OpcjastaleTime: Infinityutrzymuje dane w pamięci podręcznej podczas nawigacji po stronie klienta.src/routes/{-$locale}/route.tsxKopiuj kodSkopiuj kod do schowka
IntlProvidernie scala automatycznie wiadomości z nadrzędnego providera. Następny krok dodaje mały komponent, który to robi, dzięki czemu każda strona może dodać własną przestrzeń nazw docommon.- Odrzuca nieobsługiwane prefiksy (
Ogranicz zasięg wiadomości strony (Scope Page Messages)
Każda strona ładuje własną przestrzeń nazw w swoim loaderze, a następnie owija swoją zawartość za pomocą
ScopedMessages, który scala przestrzeń nazw strony z wiadomościami nadrzędnymi.src/components/ScopedMessages.tsxKopiuj kodSkopiuj kod do schowka
Wykorzystaj tłumaczenia na swoich stronach
Loader strony pobiera przestrzeń nazw
aboutdla bieżącego języka, funkcjahead()buduje na jej podstawie przetłumaczone, kompletne pod kątem SEO metadane (zobacz krok 13), a komponent renderuje zawartość.src/routes/{-$locale}/about.tsxKopiuj kodSkopiuj kod do schowka
Używaj tłumaczeń i formaterów w komponentach
Każdy komponent znajdujący się wewnątrz providerów może wywoływać
useTranslationsorazuseFormatter. Formy liczby mnogiej są obsługiwane przez ICU, a liczby są formatowane zgodnie z aktywnym językiem.src/components/Counter.tsxKopiuj kodSkopiuj kod do schowka
Zbuduj komponent zlokalizowanego linku
OpcjonalneKażda trasa znajduje się pod
{-$locale}, więc link musi przekazywać bieżący parametr języka. Ten wrapper zachowuje typowanetoz TanStack Router i automatycznie wstrzykuje język.src/components/LocalizedLink.tsxKopiuj kodSkopiuj kod do schowka
src/components/Header.tsxKopiuj kodSkopiuj kod do schowka
Zmień język swojej zawartości
OpcjonalneRenderuj przełącznik jako linki, a nie
<select>. Linki mogą być indeksowane przez roboty sieciowe, dzięki czemu wyszukiwarki znajdują każdą wersję językową, a ponadto działają one bez włączonego JavaScriptu.to="."zachowuje bieżącą stronę i podmienia jedynie parametr języka. Ciasteczko zapamiętuje jednoznaczny wybór dla oprogramowania pośredniczącego (middleware) przekierowań z kroku 16.src/components/LocaleSwitcher.tsxKopiuj kodSkopiuj kod do schowka
Zinternacjonalizuj swoje metadane
OpcjonalneTo tutaj i18n przynosi największe korzyści: każda wersja językowa może pozycjonować się niezależnie. Każda strona musi udostępniać:
- przetłumaczony
<title>oraz opis (description); - kanoniczny URL wskazujący na samą siebie (nie na domyślny język);
- jeden odpowiednik
hreflangna każdy język, plusx-defaultdla niedopasowanych języków; - Open Graph
og:locale,og:locale:alternateorazog:url, używane przez podglądy w mediach społecznościowych; - JSON-LD z
inLanguage, co pomaga wyszukiwarkom i asystentom AI poprawnie przypisać język strony.
Jeden pomocnik buduje to wszystko, dzięki czemu pliki stron pozostają zwięzłe:
src/i18n/seo.tsKopiuj kodSkopiuj kod do schowka
Użyj go w
head()każdej strony, jak pokazano w kroku 9. W przypadku strony głównej przekażpath: "/".- przetłumaczony
Zinternacjonalizuj swoją mapę witryny (Sitemap)
OpcjonalneWielojęzyczna mapa witryny zawiera każdy URL w każdym języku, a każdy wpis deklaruje wszystkie swoje alternatywy za pomocą
xhtml:link. Google używa tych adnotacji dokładnie tak samo jak tagówhreflangna stronie, co czyni je niezawodnym zabezpieczeniem, gdy strona jest rzadziej indeksowana.Trasy serwerowe TanStack Start pozwalają na serwowanie mapy witryny bezpośrednio z trasy plikowej:
src/routes/sitemap[.]xml.tsKopiuj kodSkopiuj kod do schowka
Zinternacjonalizuj swój plik robots.txt
OpcjonalnePrywatne trasy istnieją w każdym języku, więc reguły
Disallowmuszą obejmować każdy prefiks. Usuńpublic/robots.txt, jeśli szablon startowy go utworzył, a następnie serwuj go z trasy:src/routes/robots[.]txt.tsKopiuj kodSkopiuj kod do schowka
Przekierowuj użytkowników odwiedzających stronę po raz pierwszy do ich języka
OpcjonalneOprogramowanie pośredniczące (middleware) żądań kieruje odwiedzającego wchodzącego na
/do jego preferowanego języka, sprawdzając w pierwszej kolejności ciasteczko lokalizacji, a następnie nagłówekAccept-Language. Przekierowywany jest wyłącznie adres/: bezpośrednie linki (deep links) nigdy nie są modyfikowane, więc udostępniane adresy URL i roboty indeksujące zawsze otrzymują dokładnie tę stronę, o którą prosiły.src/i18n/negotiateLocale.tsKopiuj kodSkopiuj kod do schowka
src/start.tsKopiuj kodSkopiuj kod do schowka
Odwiedzający, który wyraźnie wybierze język angielski w przełączniku, otrzymuje
locale=enw ciasteczku, dzięki czemu nie zostanie przekierowany ponownie. W przypadku wdrożenia w pełni statycznego (krok 18),/jest serwowany jako plik i to oprogramowanie pośredniczące nie jest uruchamiane, co jest w porządku: strona pozostaje dostępna, a przełącznik załatwia resztę.Zachowaj API use-intl, zredukuj rozmiar runtime dzięki Intlayer
OpcjonalneBenchmark pokazuje, że najcięższą częścią konfiguracji use-intl jest sam runtime (~76 KB gzip). Adapter kompatybilności
@intlayer/use-intludostępnia to samo API (useTranslations,useFormatter,IntlProvider,createTranslator, formy mnogie ICU,t.rich), ale serwuje je ze skompilowanych słowników Intlayer: ~6.7 KB zamiast ~75.9 KB, 0% wycieku języków i 0% wycieku stron, bez konieczności wprowadzania jakichkolwiek zmian w komponentach.bashKopiuj kodSkopiuj kod do schowka
Wtyczka Vite tworzy alias
use-intlna adapter, dzięki czemu dotychczasowe importy działają bez zmian:vite.config.tsKopiuj kodSkopiuj kod do schowka
Twoje pliki JSON pozostają źródłem prawdy dzięki wtyczce sync JSON:
intlayer.config.tsKopiuj kodSkopiuj kod do schowka
Adapter stanowi również płynną ścieżkę migracji: po jego uruchomieniu możesz stopniowo przenosić poszczególne komponenty na natywne API
useIntlayer. Zobacz przewodnik po Intlayer dla TanStack Start.Pre-renderuj każdy język
OpcjonalneStatyczny HTML to najszybsza strona, jaką możesz zaserwować, i najłatwiejsza do zaindeksowania. Wypisz każdą zlokalizowaną ścieżkę, aby TanStack Start pre-renderował wszystkie wersje językowe w czasie budowania, a także pliki sitemap i robots:
vite.config.tsKopiuj kodSkopiuj kod do schowka
Ponieważ przełącznik języków renderuje prawdziwe linki, opcja
crawlLinks: trueodkryje również podstrony, o których zapomniałeś na liście.Obsługuj zlokalizowane strony 404
OpcjonalneUkład z kroku 7 już zgłasza
notFound()dla nieznanych prefiksów językowych. Dodaj trasę uniwersalną (catch-all), aby nieznane ścieżki w ramach danego języka również renderowały zlokalizowany błąd 404 i oznacz ją jakonoindex: React 19 automatycznie przenosi tag<meta>do<head>.src/components/NotFound.tsxKopiuj kodSkopiuj kod do schowka
src/routes/{-$locale}/$.tsxKopiuj kodSkopiuj kod do schowka
Uzyskaj dostęp do języka w funkcjach serwerowych (Server Functions)
OpcjonalneFunkcje serwerowe nie otrzymują parametrów trasy. Odczytaj ciasteczko lokalizacji i w razie potrzeby użyj nagłówka
Accept-Language, aby wysłać zlokalizowaną wiadomość e-mail lub zapisać preferencje językowe:src/server/getServerLocale.tsKopiuj kodSkopiuj kod do schowka
Aby przetłumaczyć treść wewnątrz funkcji serwerowej, połącz powyższe rozwiązanie z
loadMessagesorazcreateTranslatorz bibliotekiuse-intl.Zautomatyzuj swoje tłumaczenia za pomocą Intlayer
Opcjonalneuse-intl renderuje tłumaczenia, ale nie pomaga w ich tworzeniu. Intlayer jest darmowy i open-source, wypełniając tę lukę nawet jeśli pozostaniesz przy use-intl:
- Testuj brakujące tłumaczenia w CI lub testach jednostkowych. Zobacz testowanie tłumaczeń.
- Tłumacz za pomocą AI, korzystając z własnego klucza API i dostawcy: polecenie
npx intlayer filltłumaczy brakujące klucze z uwzględnieniem kontekstu Twojej aplikacji. Zobacz automatyczne uzupełnianie (auto fill) oraz CLI. - Zachowaj pliki JSON jako źródło prawdy dzięki wtyczce sync JSON.
- Edytuj zawartość wizualnie za pomocą edytora wizualnego oraz CMS, umożliwiając osobom nietechnicznym aktualizację tłumaczeń.
- Zapewnij kontekst swojemu agentowi AI dzięki serwerowi MCP i umiejętnościom agenta (agent skills).
- Skanuj wdrożoną stronę pod kątem brakujących tagów
hreflang, nieprawidłowych adresów kanonicznych i wycieków języków za pomocą polecenia scan.
Aby odkryć wszystkie funkcje, sprawdź dlaczego warto wybrać Intlayer.
Często zadawane pytania
Tak, jeśli chcesz używać API next-intl poza Next.js. Otrzymujesz obsługę wiadomości ICU, formatery i dobre wsparcie TypeScriptu, unikając ograniczeń specyficznych dla Next.js, takich jak setRequestLocale. Kompromisem jest waga: benchmark wskazuje ~76 KB gzip dla runtime, a naiwna konfiguracja przesyła wszystkie języki i wszystkie strony do przeglądarki. Ładuj przestrzenie nazw dla każdej trasy i dla każdego języka osobno, jak opisano w tym poradniku, aby uniknąć wycieków.
use-intl to rdzeń biblioteki next-intl. next-intl dodaje integracje specyficzne dla Next.js: middleware, pomocniki nawigacji, getTranslations dla Server Components oraz konfigurację żądań. W TanStack Start używasz use-intl bezpośrednio i implementujesz routing za pomocą TanStack Router, jak pokazano powyżej.
Użyj prefiksu w adresie URL. Dzięki temu każda wersja językowa posiada własny adres URL, który wyszukiwarki mogą zaindeksować, a użytkownicy udostępniać. Ciasteczko jest nadal przydatne do zapamiętania jednoznacznego wyboru użytkownika, co robi middleware przekierowań z kroku 16.
Serwer i przeglądarka formatują daty w różnych strefach czasowych. Przekaż jednoznaczną wartość timeZone do IntlProvider (lub strefę czasową odwiedzającego zapisaną w ciasteczku), aby obie strony wygenerowały identyczny tekst.
Po pierwsze, podziel wiadomości według przestrzeni nazw i ładuj je dla każdej trasy i języka za pomocą import.meta.glob, co eliminuje wycieki języków i stron. Następnie, jeśli rozmiar runtime ma znaczenie, przełącz się na adapter @intlayer/use-intl: to samo API, ~6.7 KB zamiast ~75.9 KB w benchmarku.
Wywołaj createTranslator wewnątrz funkcji head() trasy z wiadomościami zwróconymi przez loader trasy, a następnie zwróć title, description, link kanoniczny oraz linki hreflang. Krok 13 zawiera gotowy do ponownego użycia pomocnik.
Komentarze
Nie ma jeszcze komentarzy. Bądź pierwszą osobą, która podzieli się swoimi przemyśleniami.
