Autor:
    Data utworzenia:2025-09-09Ostatnia aktualizacja:2026-08-25

    Przetłumacz swoją stronę Tanstack Start za pomocą Intlayer | Internacjonalizacja (i18n)

    Spis treści

    Ten przewodnik pokazuje, jak zintegrować Intlayer dla płynnej internacjonalizacji w projektach Tanstack Start z routingiem uwzględniającym lokalizację, wsparciem TypeScript oraz nowoczesnymi praktykami programistycznymi.

    Dlaczego Interlayer zamiast alternatyw?

    W porównaniu do głównych rozwiązań, takich jak „react-i18next”, „use-intl” lub „paraglide”, Intlayer jest rozwiązaniem wyposażonym w zintegrowane optymalizacje, takie jak:

    Pełne pokrycie TanStack Start

    Intlayer jest w pełni zoptymalizowany pod kątem TanStack Start, zapewniając wielojęzyczny routing, zarządzanie plikami cookie, generowanie mapy witryny, dynamiczne ładowanie treści i wszystkie funkcje potrzebne do skalowania wysiłków związanych z internacjonalizacją (i18n).

    </Accordion>

    Rozmiar bundle'a

    Zamiast ładować ogromne pliki JSON na swoje strony, ładuj tylko niezbędną treść. Intlayer pomaga zmniejszyć rozmiary bundle'a i stron nawet o 50%.

    </Accordion>

    Łatwość konserwacji

    Określanie zakresu zawartości aplikacji ułatwia konserwację aplikacji na dużą skalę. Możesz powielić lub usunąć pojedynczy folder funkcji bez obciążania psychicznego koniecznością przeglądania całej bazy kodu zawartości. Dodatkowo Inlayer jest w pełni napisany, aby zapewnić dokładność treści.

    Agent AI

    Wspólna lokalizacja treści zmniejsza potrzebny kontekst dzięki modelom dużego języka (LLM). Intlayer zawiera także zestaw narzędzi, taki jak CLI do sprawdzania brakujących tłumaczeńLSP, MCP i agent skills, aby praca programisty (DX) była jeszcze płynniejsza dla agentów AI.

    Automatyzacja

    Korzystaj z automatyzacji, aby tłumaczyć w swoim potoku CI/CD przy użyciu wybranego LLM na koszt dostawcy sztucznej inteligencji. Intlayer oferuje także kompilator do automatyzacji ekstrakcji treści, a także [platformę internetową] (/pl/doc/concept/cms), która pomaga tłumaczyć w tle.

    Wydajność

    Łączenie ogromnych plików JSON z komponentami może prowadzić do problemów z wydajnością i reaktywnością. Inlayer optymalizuje ładowanie treści w czasie kompilacji.

    Skalowanie bez użycia dewelopera

    Więcej niż tylko rozwiązanie i18n, Intlayer zapewnia samodzielny edytor wizualny i pełny CMS, który pomoże Ci zarządzać wielojęzyczną treścią w w czasie rzeczywistym, dzięki czemu współpraca z tłumaczami, copywriterami i innymi członkami zespołu będzie płynna. Treść może być przechowywana lokalnie i/lub zdalnie.

    </Accordion>


    Przewodnik krok po kroku, jak skonfigurować Intlayer w aplikacji Tanstack Start

    www.youtube.com
    ide.intlayer.org
    intlayer-tanstack-start-template.vercel.app

    Zobacz Szablon aplikacji na GitHub.

    1. Utwórz projekt

      Rozpocznij od utworzenia nowego projektu TanStack Start, postępując zgodnie z przewodnikiem Start new project na stronie TanStack Start.

    2. Zainstaluj pakiety Intlayer

      Zainstaluj niezbędne pakiety, używając preferowanego menedżera pakietów:

      bash
      npx intlayer init --interactive
      
      flaga --interactive jest opcjonalna. Użyj intlayer-cli init, jeśli jesteś agentem AI.
      To polecenie wykryje Twoje środowisko i zainstaluje wymagane pakiety. Na przykład:
      bash
      npm install intlayer react-intlayer
      npm install vite-intlayer --save-dev
      
      • intlayer

        Podstawowy pakiet, który dostarcza narzędzia do internacjonalizacji dla zarządzania konfiguracją, tłumaczeń, deklaracji treści, transpiliacji oraz poleceń CLI.

      • react-intlayer Pakiet integrujący Intlayer z aplikacją React. Zapewnia dostawców kontekstu oraz hooki do internacjonalizacji w React.

      • vite-intlayer Zawiera wtyczkę Vite do integracji Intlayer z bundlerem Vite, a także middleware do wykrywania preferowanego języka użytkownika, zarządzania ciasteczkami oraz obsługi przekierowań URL.

    3. Konfiguracja projektu

      Utwórz plik konfiguracyjny, aby skonfigurować języki swojej aplikacji:

      intlayer.config.ts
      import type { IntlayerConfig } from "intlayer";
      
      import { Locales } from "intlayer";
      
      const config: IntlayerConfig = {
        internationalization: {
          defaultLocale: Locales.ENGLISH,
          locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
        },
      };
      
      export default config;
      
      Za pomocą tego pliku konfiguracyjnego możesz ustawić lokalizowane adresy URL, przekierowania w middleware, nazwy ciasteczek, lokalizację i rozszerzenie deklaracji treści, wyłączyć logi Intlayer w konsoli i wiele więcej. Pełną listę dostępnych parametrów znajdziesz w dokumentacji konfiguracji.
    4. Integracja Intlayer w konfiguracji Vite

      Dodaj wtyczkę intlayer do swojej konfiguracji:

      vite.config.ts
      import { tanstackStart } from "@tanstack/react-start/plugin/vite";
      import viteReact from "@vitejs/plugin-react";
      import { nitro } from "nitro/vite";
      import { defineConfig } from "vite";
      import { intlayer } from "vite-intlayer";
      
      const config = defineConfig({
        plugins: [
          nitro(),
          intlayer({
            proxy: {
              ignore: (req) => req.url?.startsWith("/api"),
            },
          }),
          tanstackStart({
            router: {
              routeFileIgnorePattern:
                ".content.(ts|tsx|js|mjs|cjs|jsx|json|jsonc|json5|md|mdx|yaml|yml)$",
            },
          }),
          viteReact(),
        ],
      });
      
      export default config;
      
      Wtyczka intlayer() dla Vite służy do integracji Intlayer z Vite. Zapewnia budowanie plików deklaracji treści oraz monitorowanie ich w trybie deweloperskim. Definiuje zmienne środowiskowe Intlayer w aplikacji Vite. Dodatkowo dostarcza aliasy optymalizujące wydajność.
    5. Utwórz układ główny

      Skonfiguruj swój główny układ, aby wspierać internacjonalizację, używając useParams do wykrywania aktualnej lokalizacji i ustawiając atrybuty lang i dir w tagu html.

      src/routes/__root.tsx
      import {
        createRootRouteWithContext,
        getRouteApi,
        HeadContent,
        Scripts,
      } from "@tanstack/react-router";
      import { defaultLocale, getHTMLTextDir } from "intlayer";
      import { type ReactNode } from "react";
      import { IntlayerProvider } from "react-intlayer";
      
      const localeRoute = getRouteApi("/{-$locale}");
      
      export const Route = createRootRouteWithContext<{}>()({
        head: () => ({
          meta: [
            {
              charSet: "utf-8",
            },
            {
              content: "width=device-width, initial-scale=1",
              name: "viewport",
            },
            {
              title: "TanStack Start Starter",
            },
          ],
        }),
      
        shellComponent: RootDocument,
      });
      
      function RootDocument({ children }: { children: ReactNode }) {
        const params = localeRoute.useParams();
        const locale = params?.locale ?? defaultLocale;
      
        return (
          <html dir={getHTMLTextDir(locale)} lang={locale}>
            <head>
              <HeadContent />
            </head>
            <body>
              <IntlayerProvider locale={locale}>{children}</IntlayerProvider>
              <Scripts />
            </body>
          </html>
        );
      }
      
    6. Utwórz układ lokalizacji

      Utwórz układ, który obsługuje prefiks lokalizacji i wykonuje walidację.

      src/routes/{-$locale}/route.tsx
      import { createFileRoute, Outlet, redirect } from "@tanstack/react-router";
      import { validatePrefix } from "intlayer";
      
      export const Route = createFileRoute("/{-$locale}")({
        beforeLoad: ({ params }) => {
          const localeParam = params.locale;
      
          // Walidacja prefiksu lokalizacji
          const { isValid, localePrefix } = validatePrefix(localeParam);
      
          if (!isValid) {
            throw redirect({
              to: "/{-$locale}/404",
              params: { locale: localePrefix },
            });
          }
        },
        component: Outlet,
      });
      
      Tutaj {-$locale} jest dynamicznym parametrem trasy, który zostaje zastąpiony aktualną lokalizacją. Ta notacja sprawia, że slot jest opcjonalny, co pozwala na współpracę z trybami routingu takimi jak 'prefix-no-default' itp.

      Pamiętaj, że ten slot może powodować problemy, jeśli używasz wielu dynamicznych segmentów w tej samej trasie (np. /{-$locale}/other-path/$anotherDynamicPath/...). W trybie 'prefix-all' możesz woleć zmienić slot na $locale. W trybach 'no-prefix' lub 'search-params' możesz całkowicie usunąć ten slot.

    7. Zadeklaruj swoją treść

      Twórz i zarządzaj deklaracjami treści, aby przechowywać tłumaczenia:

      src/contents/page.content.ts
      import type { Dictionary } from "intlayer";
      
      import { t } from "intlayer";
      
      const appContent = {
        content: {
          links: {
            about: t({
              en: "About",
              es: "Acerca de",
              fr: "À propos",
            }),
            home: t({
              en: "Home",
              es: "Inicio",
              fr: "Accueil",
            }),
          },
          meta: {
            title: t({
              en: "Welcome to Intlayer + TanStack Router",
              es: "Bienvenido a Intlayer + TanStack Router",
              fr: "Bienvenue à Intlayer + TanStack Router",
            }),
            description: t({
              en: "This is an example of using Intlayer with TanStack Router",
              es: "Este es un ejemplo de uso de Intlayer con TanStack Router",
              fr: "Ceci est un exemple d'utilisation d'Intlayer avec TanStack Router",
            }),
          },
        },
        key: "app",
      } satisfies Dictionary;
      
      export default appContent;
      
      Twoje deklaracje zawartości mogą być definiowane w dowolnym miejscu w Twojej aplikacji, pod warunkiem, że zostaną umieszczone w katalogu contentDir (domyślnie ./app). I będą miały rozszerzenie pliku deklaracji zawartości (domyślnie .content.{json,ts,tsx,js,jsx,mjs,cjs,md,mdx,yaml,yml}).
      Po więcej szczegółów odsyłamy do dokumentacji deklaracji zawartości.
    8. Tworzenie komponentów i hooków uwzględniających lokalizację

      Utwórz komponent LocalizedLink do nawigacji uwzględniającej lokalizację:

      src/components/localized-link.tsx
      import type { FC } from "react";
      
      import { Link, type LinkComponentProps } from "@tanstack/react-router";
      import { useLocale } from "react-intlayer";
      import { getPrefix } from "intlayer";
      
      export const LOCALE_ROUTE = "{-$locale}" as const;
      
      export type To = StripLocalePrefix<LinkComponentProps["to"]>;
      
      export type StripLocalePrefix<T extends string | undefined> = T extends
        `/${typeof LOCALE_ROUTE}/` | `/${typeof LOCALE_ROUTE}`
        ? "/"
        : T extends `/${typeof LOCALE_ROUTE}/${infer Rest}`
          ? `/${Rest}`
          : T;
      
      type LocalizedLinkProps = {
        to?: To;
      } & Omit<LinkComponentProps, "to">;
      
      export const LocalizedLink: FC<LocalizedLinkProps> = (props) => {
        const { locale } = useLocale();
        const { localePrefix } = getPrefix(locale);
      
        return (
          <Link
            {...props}
            params={{
              locale: localePrefix,
              ...(typeof props?.params === "object" ? props?.params : {}),
            }}
            to={`/${LOCALE_ROUTE}${props.to}` as LinkComponentProps["to"]}
          />
        );
      };
      

      Ten komponent ma dwa cele:

      • Usunięcie niepotrzebnego prefiksu {-$locale} z URL.
      • Wstrzyknięcie parametru locale do URL, aby zapewnić użytkownikowi bezpośrednie przekierowanie do zlokalizowanej ścieżki.

      Następnie możemy stworzyć hook useLocalizedNavigate do nawigacji programowej:

      src/hooks/useLocalizedNavigate.tsx
      import { useNavigate } from "@tanstack/react-router";
      import { getPrefix } from "intlayer";
      import { useLocale } from "react-intlayer";
      import type { StripLocalePrefix } from "@/components/localized-link";
      import type { FileRouteTypes } from "@/routeTree.gen";
      
      type NavigateFn = ReturnType<typeof useNavigate>;
      type BaseNavigateOptions = Parameters<NavigateFn>[0];
      
      type LocalizedTo = StripLocalePrefix<FileRouteTypes["to"]>;
      
      export type LocalizedNavigateOptions = Omit<
        BaseNavigateOptions,
        "to" | "params"
      > & {
        to: LocalizedTo;
        params?: Omit<NonNullable<BaseNavigateOptions["params"]>, "locale">;
      };
      
      type LocalizedNavigate = (
        options: LocalizedNavigateOptions
      ) => ReturnType<NavigateFn>;
      
      export const useLocalizedNavigate = () => {
        const navigate = useNavigate();
      
        const { locale } = useLocale();
      
        const localizedNavigate: LocalizedNavigate = (args: any) => {
          const { localePrefix } = getPrefix(locale);
      
          if (typeof args === "string") {
            return navigate({
              to: `/${LOCALE_ROUTE}${args}`,
              params: { locale: localePrefix },
            });
          }
      
          const { to, ...rest } = args;
      
          const localizedTo = `/${LOCALE_ROUTE}${to}` as any;
      
          return navigate({
            to: localizedTo,
            params: { locale: localePrefix, ...rest } as any,
          });
        };
      
        return localizedNavigate;
      };
      
    9. Wykorzystaj Intlayer na swoich stronach

      Domyślnie używaj useIntlayer: to zalecany sposób odczytu treści wewnątrz komponentów, a kompilator rozwiązuje go do renderowanej lokalizacji. Po getIntlayer / getIntlayerAsync sięgaj tylko poza drzewem React: w head tras, loaderach i funkcjach serwerowych.

      Uzyskaj dostęp do swoich słowników treści w całej aplikacji:

      Strona główna zlokalizowana

      src/routes/{-$locale}/index.tsx
      import { createFileRoute } from "@tanstack/react-router";
      import { useIntlayer } from "react-intlayer";
      
      import LocaleSwitcher from "@/components/locale-switcher";
      import { LocalizedLink } from "@/components/localized-link";
      import { useLocalizedNavigate } from "@/hooks/useLocalizedNavigate";
      
      export const Route = createFileRoute("/{-$locale}/")({
        component: RouteComponent,
      });
      
      function RouteComponent() {
        const content = useIntlayer("app");
        const navigate = useLocalizedNavigate();
      
        return (
          <div>
            <div>
              {content.title}
              <LocaleSwitcher />
              <div>
                <LocalizedLink to="/">{content.links.home}</LocalizedLink>
                <LocalizedLink to="/about">{content.links.about}</LocalizedLink>
              </div>
              <div>
                <button onClick={() => navigate({ to: "/" })}>
                  {content.links.home}
                </button>
                <button onClick={() => navigate({ to: "/about" })}>
                  {content.links.about}
                </button>
              </div>
            </div>
          </div>
        );
      }
      

      Jeśli chcesz użyć zawartości w atrybucie string, takim jak alt, title, href, aria-label itd., możesz użyć wartości funkcji, na przykład:

      html
      <img src="{content.image.src.value}" alt="{content.image.value}" />
      <img src="{content.image.src.toString()}" alt="{content.image.toString()}" />
      <img src="{String(content.image.src)}" alt="{String(content.image)}" />
      
      Aby dowiedzieć się więcej o hook'u useIntlayer, zapoznaj się z dokumentacją.
    10. Utwórz komponent przełącznika języków

      Utwórz komponent umożliwiający użytkownikom zmianę języka:

      src/components/locale-switcher.tsx
      import { useLocation } from "@tanstack/react-router";
      import {
        getHTMLTextDir,
        getLocaleName,
        getPathWithoutLocale,
        getPrefix,
        Locales,
      } from "intlayer";
      import type { FC } from "react";
      import { useLocale } from "react-intlayer";
      
      import { LocalizedLink, type To } from "./localized-link";
      
      export const LocaleSwitcher: FC = () => {
        const { pathname } = useLocation();
      
        const { availableLocales, locale, setLocale } = useLocale();
      
        const pathWithoutLocale = getPathWithoutLocale(pathname);
      
        return (
          <ol>
            {availableLocales.map((localeEl) => (
              <li key={localeEl}>
                <LocalizedLink
                  aria-current={localeEl === locale ? "page" : undefined}
                  onClick={() => setLocale(localeEl)}
                  params={{ locale: getPrefix(localeEl).localePrefix }}
                  to={pathWithoutLocale as To}
                >
                  <span>
                    {/* Locale - np. FR */}
                    {localeEl}
                  </span>
                  <span>
                    {/* Język w jego własnym locale - np. Français */}
                    {getLocaleName(localeEl, locale)}
                  </span>
                  <span dir={getHTMLTextDir(localeEl)} lang={localeEl}>
                    {/* Język w bieżącym locale - np. Francés przy bieżącym locale ustawionym na Locales.SPANISH */}
                    {getLocaleName(localeEl)}
                  </span>
                  <span dir="ltr" lang={Locales.ENGLISH}>
                    {/* Język w angielskim - np. French */}
                    {getLocaleName(localeEl, Locales.ENGLISH)}
                  </span>
                </LocalizedLink>
              </li>
            ))}
          </ol>
        );
      };
      
      Aby dowiedzieć się więcej o hook'u useLocale, zapoznaj się z dokumentacją.
    11. Zarządzanie atrybutami HTML

      Jak pokazano w kroku 5, możesz zarządzać atrybutami lang i dir tagu html za pomocą useParams w komponencie głównym. Zapewnia to, że prawidłowe atrybuty są ustawione na serwerze i kliencie.

      src/routes/__root.tsx
      const localeRoute = getRouteApi("/{-$locale}");
      
      function RootDocument({ children }: { children: ReactNode }) {
        const params = localeRoute.useParams();
        const locale = params?.locale ?? defaultLocale;
      
        return (
          <html dir={getHTMLTextDir(locale)} lang={locale}>
            {/* ... */}
          </html>
        );
      }
      

    12. Dodaj middleware

      Możesz także użyć intlayerProxy do dodania routingu po stronie serwera do aplikacji. Plugin ten automatycznie wykryje bieżący język na podstawie URL i ustawi odpowiedni plik cookie języka. Jeśli nie zostanie określony żaden język, plugin określi najbardziej odpowiedni język na podstawie preferencji języka przeglądarki użytkownika. Jeśli nie zostanie wykryty żaden język, nastąpi przekierowanie do języka domyślnego.

      Uwaga: aby użyć intlayerProxy w środowisku produkcyjnym, musisz przenieść pakiet vite-intlayer z devDependencies do dependencies.
      Od Intlayer v9, intlayerProxy() jest bezpośrednio dołączony do pluginu intlayer() i domyślnie włączony za pośrednictwem opcji routing.enableProxy (true domyślnie). Rejestrowanie go osobno, jak pokazano poniżej, jest teraz opcjonalne: jest zachowywane dla kompatybilności wstecznej i dla ustawień, które muszą kontrolować kolejność pluginów. Ustaw routing.enableProxy: false, aby zrezygnować. Zapoznaj się z notatkami do wydania v9.
      vite.config.ts
      import { tanstackStart } from "@tanstack/react-start/plugin/vite";
      import viteReact from "@vitejs/plugin-react";
      import { nitro } from "nitro/vite";
      import { defineConfig } from "vite";
      import { intlayer } from "vite-intlayer";
      
      export default defineConfig({
        plugins: [
          nitro(),
          intlayer({
            proxy: {
              ignore: (req) => req.url?.startsWith("/api"),
            },
          }),
          tanstackStart({
            router: {
              routeFileIgnorePattern:
                ".content.(ts|tsx|js|mjs|cjs|jsx|json|jsonc|json5|md|mdx|yaml|yml)$",
            },
          }),
          viteReact(),
        ],
      });
      

    13. Internationalizuj swoje metadane

      getIntlayer rozwiązuje się synchronicznie względem scalonego słownika, tego zawierającego każdy zadeklarowany język. head pozostaje synchroniczny i nic nie jest oczekiwane, ale cały wielojęzyczny słownik jest pobierany do fragmentu trasy wysyłanego do przeglądarki.

      src/routes/{-$locale}/index.tsx
      import { createFileRoute } from "@tanstack/react-router";
      import {
        defaultLocale,
        getIntlayer,
        getLocalizedUrl,
        localeMap,
      } from "intlayer";
      
      export const Route = createFileRoute("/{-$locale}/")({
        component: RouteComponent,
        head: ({ params }) => {
          const { locale = defaultLocale } = params;
          const path = "/"; // Ścieżka dla tej trasy
      
          const metaContent = getIntlayer("app", locale);
      
          return {
            links: [
              // Link kanoniczny: wskazuje na bieżącą stronę zlokalizowaną
              { rel: "canonical", href: getLocalizedUrl(path, locale) },
      
              // Hreflang: powiadomi Google o wszystkich zlokalizowanych wersjach
              ...localeMap(({ locale: mapLocale }) => ({
                rel: "alternate",
                hrefLang: mapLocale,
                href: getLocalizedUrl(path, mapLocale),
              })),
      
              // x-default: dla użytkowników w niedopasowanych językach
              // Zdefiniuj domyślny fallback locale (zwykle Twój język podstawowy)
              {
                rel: "alternate",
                hrefLang: "x-default",
                href: getLocalizedUrl(path, defaultLocale),
              },
            ],
            meta: [
              { title: metaContent.title },
              { name: "description", content: metaContent.meta.description },
            ],
          };
        },
      });
      

      Najlepsze dla małych słowników metadanych, kilku locale'i lub podczas prototypowania.

      getIntlayerAsync (dostępne od v9.4) zachowuje się jak getIntlayer, ale plugin budowania wskazuje go na fragment dla konkretnego locale'a w .intlayer/dynamic_dictionaries/ zamiast scalonego słownika. Strona zatem wysyła tylko locale, który renderuje. Ponieważ fragment jest ładowany na żądanie, head staje się async:

      src/routes/{-$locale}/index.tsx
      import { createFileRoute } from "@tanstack/react-router";
      import {
        defaultLocale,
        getIntlayerAsync,
        getLocalizedUrl,
        localeMap,
      } from "intlayer";
      
      export const Route = createFileRoute("/{-$locale}/")({
        component: RouteComponent,
        head: async ({ params }) => {
          const { locale = defaultLocale } = params;
          const path = "/"; // Ścieżka dla tej trasy
      
          const metaContent = await getIntlayerAsync("app", locale);
      
          return {
            links: [
              // Link kanoniczny: wskazuje na bieżącą stronę zlokalizowaną
              { rel: "canonical", href: getLocalizedUrl(path, locale) },
      
              // Hreflang: powiadomi Google o wszystkich zlokalizowanych wersjach
              ...localeMap(({ locale: mapLocale }) => ({
                rel: "alternate",
                hrefLang: mapLocale,
                href: getLocalizedUrl(path, mapLocale),
              })),
      
              // x-default: dla użytkowników w niedopasowanych językach
              // Zdefiniuj domyślny fallback locale (zwykle Twój język podstawowy)
              {
                rel: "alternate",
                hrefLang: "x-default",
                href: getLocalizedUrl(path, defaultLocale),
              },
            ],
            meta: [
              { title: metaContent.title },
              { name: "description", content: metaContent.meta.description },
            ],
          };
        },
      });
      
      Jeśli head odczytuje kilka słowników, rozwiąż je za pomocą Promise.all: oczekiwanie każdego getIntlayerAsync w oddzielnej linii łańcuchuje żądania zamiast uruchamiać je równolegle.

      Kompromis: import dynamiczny jest rozwiązywany podczas uruchamiania head, na krytycznej ścieżce renderowania dokumentu. Na zimnej trasie opóźnia to head o kilka milisekund i może nieco pogorszyć LCP.

      Rozwiąż słownik w loader trasy i przeczytaj go z powrotem z loaderData w head. Loadery dopasowanych tras działają równolegle, a staleTime: Infinity mówi TanStack Router, że wynik nigdy się nie starzeje, więc fragment dla konkretnego locale'a jest rozwiązywany raz i podawany z cache'a routera później, pozostawiając head synchroniczny.

      src/routes/{-$locale}/index.tsx
      import { createFileRoute } from "@tanstack/react-router";
      import {
        defaultLocale,
        getIntlayerAsync,
        getLocalizedUrl,
        localeMap,
      } from "intlayer";
      
      export const Route = createFileRoute("/{-$locale}/")({
        component: RouteComponent,
        // Rozwiązywany równolegle z innymi dopasowanymi trasami, poza krytyczną ścieżką head
        loader: async ({ params }) => {
          const { locale = defaultLocale } = params;
      
          return { metaContent: await getIntlayerAsync("app", locale) };
        },
        // Słownik nigdy się nie zmienia dla danego locale: rozwiąż fragment raz
        staleTime: Infinity,
        head: ({ params, loaderData }) => {
          const { locale = defaultLocale } = params;
          const path = "/"; // Ścieżka dla tej trasy
      
          return {
            links: [
              // Link kanoniczny: wskazuje na bieżącą stronę zlokalizowaną
              { rel: "canonical", href: getLocalizedUrl(path, locale) },
      
              // Hreflang: powiadomi Google o wszystkich zlokalizowanych wersjach
              ...localeMap(({ locale: mapLocale }) => ({
                rel: "alternate",
                hrefLang: mapLocale,
                href: getLocalizedUrl(path, mapLocale),
              })),
      
              // x-default: dla użytkowników w niedopasowanych językach
              // Zdefiniuj domyślny fallback locale (zwykle Twój język podstawowy)
              {
                rel: "alternate",
                hrefLang: "x-default",
                href: getLocalizedUrl(path, defaultLocale),
              },
            ],
            meta: [
              { title: loaderData?.metaContent.title },
              {
                name: "description",
                content: loaderData?.metaContent.meta.description,
              },
            ],
          };
        },
      });
      
      head może być wywoływany przed osadzeniem loadera, więc loaderData jest wpisywana jako możliwie undefined. Zachowaj opcjonalne łańcuchowanie lub zwróć tytuł fallback.

      Zachowujesz fragment dla konkretnego locale'a bez płacenia jego kosztu na krytycznej ścieżce head. Cena to doświadczenie deweloperskie: zawartość musi być jawnie przekazywana z loadera do head poprzez loaderData.

      Którą rozdzielczość powinienem wybrać?

      Rozdzielczość statyczna Rozdzielczość dynamiczna Rozdzielczość dynamiczna z cache'em
      API getIntlayer getIntlayerAsync (v9.4+) getIntlayerAsync w loader (v9.4+)
      Sygnatura head synchroniczna async synchroniczna, czyta loaderData
      Ustawienia regionalne każdy zadeklarowany locale tylko żądany locale tylko żądany locale
      Nawigacja na kliencie nic do rozwiązania ponownie wznawiane przy każdym dopasowaniu obsługiwane z cache'u routera
      Doświadczenie dewelopera najprostsze jedno await zawartość przesłana przez loaderData

    14. Pobierz locale w swoich server actions

      Możesz chcieć uzyskać dostęp do bieżącego locale'a z wnętrza twoich server actions lub API endpoints. Możesz to zrobić używając helpera getLocale z intlayer.

      Oto przykład używający server functions TanStack Start:

      src/routes/{-$locale}/index.tsx
      import { createServerFn } from "@tanstack/react-start";
      import {
        getRequestHeader,
        getRequestHeaders,
      } from "@tanstack/react-start/server";
      import { getCookie, getIntlayer, getLocale } from "intlayer";
      
      export const getLocaleServer = createServerFn().handler(async () => {
        const locale = await getLocale({
          // Pobierz cookie z żądania (domyślnie: 'INTLAYER_LOCALE')
          getCookie: (name) => {
            const cookieString = getRequestHeader("cookie");
      
            return getCookie(name, cookieString);
          },
          // Pobierz nagłówek z żądania (domyślnie: 'x-intlayer-locale')
          // Fallback używający negocjacji Accept-Language
          getHeader: (name) => getRequestHeader(name),
        });
      
        // Pobierz zawartość używając getIntlayerAsync()
        const content = getIntlayer("app", locale);
      
        return { locale, content };
      });
      

    15. Zarządzaj stronami not found

      Gdy użytkownik odwiedzi nieistniejącą stronę, możesz wyświetlić niestandardową stronę not found, a prefiks locale'a może wpłynąć na sposób, w jaki strona not found jest wyzwalana.

      Lokalizowana strona główna

      Jeśli chcesz użyć swojej zawartości w atrybucie string, takim jak alt, title, href, aria-label, itp., możesz użyć wartości funkcji, na przykład:

      html
      <img src="{content.image.src.value}" alt="{content.image.value}" />
      <img src="{content.image.src.toString()}" alt="{content.image.toString()}" />
      <img src="{String(content.image.src)}" alt="{String(content.image)}" />
      
      Aby dowiedzieć się więcej o hooku useIntlayer, zapoznaj się z dokumentacją.
    16. src/components/locale-switcher.tsx
      import { useLocation } from "@tanstack/react-router";
      import {
        getHTMLTextDir,
        getLocaleName,
        getPathWithoutLocale,
        getPrefix,
        Locales,
      } from "intlayer";
      import type { FC } from "react";
      import { useLocale } from "react-intlayer";
      
      import { LocalizedLink, type To } from "./localized-link";
      
      export const LocaleSwitcher: FC = () => {
        const { pathname } = useLocation();
      
        const { availableLocales, locale, setLocale } = useLocale();
      
        const pathWithoutLocale = getPathWithoutLocale(pathname);
      
        return (
          <ol>
            {availableLocales.map((localeEl) => (
              <li key={localeEl}>
                <LocalizedLink
                  aria-current={localeEl === locale ? "page" : undefined}
                  onClick={() => setLocale(localeEl)}
                  params={{ locale: getPrefix(localeEl).localePrefix }}
                  to={pathWithoutLocale as To}
                >
                  <span>
                    {/* Lokalizacja - np. FR */}
                    {localeEl}
                  </span>
                  <span>
                    {/* Język w swojej własnej lokalizacji - np. Français */}
                    {getLocaleName(localeEl, locale)}
                  </span>
                  <span dir={getHTMLTextDir(localeEl)} lang={localeEl}>
                    {/* Język w bieżącej lokalizacji - np. Francés przy ustawionej lokalizacji Locales.SPANISH */}
                    {getLocaleName(localeEl)}
                  </span>
                  <span dir="ltr" lang={Locales.ENGLISH}>
                    {/* Język po angielsku - np. French */}
                    {getLocaleName(localeEl, Locales.ENGLISH)}
                  </span>
                </LocalizedLink>
              </li>
            ))}
          </ol>
        );
      };
      
      Aby dowiedzieć się więcej o hooku useLocale, zapoznaj się z dokumentacją.

      </Step>

    17. Zarządzanie atrybutami HTML

      return ( {/* ... _/} ); } {/_ ... */} </html> ); }

      export const Route = createFileRoute("/{-$locale}/")({ component: RouteComponent, head: async ({ params }) => { const { locale = defaultLocale } = params; const path = "/"; // The path for this route

      plaintext
      const metaContent = await getIntlayerAsync("app", locale);
      plaintext
      
      > Jeśli `head` czyta kilka słowników, rozwiąż je przez `Promise.all`; oczekiwanie na każde `getIntlayerAsync` w osobnej linii łańcuchuje żądania zamiast wykonywać je równolegle.
      
      Kompromis: dynamiczny import jest rozwiązywany w trakcie działania `head`, na ścieżce krytycznej renderowania dokumentu. Na „zimnej” trasie opóźnia to `head` o kilka milisekund i może nieznacznie pogorszyć **LCP**.
      
      </Tab>
      
      <Tab label="Buforowane rozwiązywanie dynamiczne" value="cached">
      
      Rozwiąż słownik w `loaderze` trasy i odczytaj go z `loaderData` w `head`. Loadery dopasowanych tras działają równolegle, a `staleTime: Infinity` informuje TanStack Router, że wynik nigdy się nie dezaktualizuje, fragment per-lokalizacja jest więc rozwiązywany raz, a potem serwowany z cache routera, pozostawiając `head` synchronicznym.
      
      ```tsx fileName="src/routes/{-$locale}/index.tsx"
            return getCookie(name, cookieString);
          },
          // Pobierz nagłówek z żądania (domyślnie: 'x-intlayer-locale')
          // Rezerwowe rozwiązanie przy użyciu negocjacji Accept-Language
          getHeader: (name) => getRequestHeader(name),
        });
      
        // Pobierz treść za pomocą getIntlayer()
        const content = getIntlayer("app", locale);
      
      

    18. Zarządzanie stronami &quot;nie znaleziono&quot;

      Gdy użytkownik odwiedza nieistniejącą stronę, możesz wyświetlić niestandardową stronę "nie znaleziono", a prefiks lokalizacji może wpływać na sposób wyzwalania strony "nie znaleziono".

      Zrozumienie obsługi 404 w TanStack Router z prefiksami lokalizacji

      W TanStack Router obsługa stron 404 z zlokalizowanymi trasami wymaga podejścia wielowarstwowego:

      1. Dedykowana trasa 404: Konkretna trasa do wyświetlenia interfejsu 404
      2. Walidacja na poziomie trasy: Weryfikuje prefiksy lokalizacji i przekierowuje nieprawidłowe do 404
      3. Trasa catch-all: Przechwytuje wszystkie niedopasowane ścieżki w segmencie lokalizacji
      src/routes/{-$locale}/404.tsx
      
      
      src/routes/{-$locale}/route.tsx
      
      
      src/routes/{-$locale}/$.tsx
      
      
    19. Wyodrębnij zawartość swoich komponentów

      Opcjonalne
    20. Jeśli masz istniejącą bazę kodu, transformacja tysięcy plików może być czasochłonna.

      Aby ułatwić ten proces, Intlayer proponuje kompilator / ekstraktor, aby przetransformować komponenty i wyodrębnić zawartość.

      Aby go skonfigurować, możesz dodać sekcję compiler w pliku intlayer.config.ts:

      intlayer.config.ts
      import { type IntlayerConfig } from "intlayer";
      
          /**
           * Definiuje ścieżkę plików wyjściowych
           */
          output: ({ fileName, extension }) => `./${fileName}${extension}`,
      
          /**
           * Prefiks klucza słownika
           */
          dictionaryKeyPrefix: "",
        },
      };
      
      export default config;
      

      Uruchom ekstraktor, aby przetransformować komponenty i wyodrębnić zawartość

      bash
      
      

      bun x intlayer extract import { defineConfig } from "vite"; import { intlayer, intlayerCompiler } from "vite-intlayer";

      export default defineConfig({ plugins: [

      plaintext
      intlayer(),
      intlayerCompiler(), // Adds the compiler plugin

      ], });

      plaintext
      
      

      bash packageManager="npm" npm run build # Lub npm run dev

      plaintext
      
      

      bash packageManager="pnpm" pnpm run build # Or pnpm run dev

      plaintext
      
      

      bash packageManager="yarn" yarn build # Or yarn dev

      plaintext
      
      

      bash packageManager="bun"


      bun run build # Or bun run dev import { localeFlatMap } from "intlayer"; // ... inne importy

      export const pathList = ["", "/about", "/404"];

      const localizedPages = localeFlatMap(({ urlPrefix }) => pathList.map((path) => ({

      plaintext
      path: `${urlPrefix}${path}`,
      prerender: {
        enabled: true,
      },

      })) );

      export default defineConfig({ plugins: [

      plaintext
      // ... pozostałe wtyczki
      tanstackStart({
        // ... pozostała konfiguracja
        sitemap: {
          enabled: false,
        },
        prerender: {
          enabled: true,
          crawlLinks: false,
          concurrency: 10,
        },
        pages: localizedPages,
      }),

      ], });

      plaintext
      
      Następnie utwórz trasę `src/routes/sitemap[.]xml.ts`, która wykorzystuje funkcję `generateSitemap`:
      
      

      `typescript fileName="src/routes/sitemap[.]xml.ts"


      export const Route = createFileRoute("/sitemap.xml")({ server: {

      plaintext
      handlers: {
        GET: async () => {
          const sitemap = generateSitemap(
            [
              { path: "/", changefreq: "daily", priority: 1.0 },
              { path: "/about", changefreq: "monthly", priority: 0.8 },
            ],
            { siteUrl: SITE_URL }
          );
      
          return new Response(sitemap, {
            headers: { "Content-Type": "application/xml" },
          });
        },
      },

      }, }); { // ... twoje istniejące konfiguracje include: [

      plaintext
      // ... twoje istniejące includy
      ".intlayer/**/*.ts", // Dołącz auto-generowane typy

      ], }

      Konfiguracja Git

      Zaleca się ignorować pliki generowane przez Intlayer. Pozwala to uniknąć zatwierdzania ich w repozytorium Git.

      Aby to zrobić, możesz dodać następujące instrukcje do pliku .gitignore:

      .gitignore
      # Ignoruj pliki generowane przez Intlayer
      .intlayer
      

      `


      Rozszerzenie VS Code

      Aby ulepszyć doświadczenie programistyczne dzięki Intlayer, możesz zainstalować oficjalne rozszerzenie Intlayer dla VS Code.

      Zainstaluj z VS Code Marketplace

      To rozszerzenie zapewnia:

      • Autouzupełnianie dla kluczy tłumaczeń.
      • Wykrywanie błędów w czasie rzeczywistym dla brakujących tłumaczeń.
      • Podglądy inline przetłumaczonej zawartości.
      • Szybkie akcje do łatwego tworzenia i aktualizacji tłumaczeń.

      Aby uzyskać więcej szczegółów na temat korzystania z rozszerzenia, zapoznaj się z dokumentacją rozszerzenia Intlayer VS Code Extension.


      Idź dalej

      Aby pójść dalej, możesz wdrożyć edytor wizualny lub externalizować swoją zawartość przy użyciu CMS.


      Referencje Dokumentacji