ГлавнаяПесочницаВитринаПриложениеДокументБлог
    • Englishанглийский
      EN
    • русскийрусский
      RU
    • 日本語японский
      JA
    • françaisфранцузский
      FR
    • 한국어корейский
      KO
    • 中文китайский
      ZH
    • españolиспанский
      ES
    • Deutschнемецкий
      DE
    • العربيةарабский
      AR
    • italianoитальянский
      IT
    • British Englishбританский английский
      EN-GB
    • portuguêsпортугальский
      PT
    • हिन्दीхинди
      HI
    • Türkçeтурецкий
      TR
    • polskiпольский
      PL
    • Indonesiaиндонезийский
      ID
    • Tiếng Việtвьетнамский
      VI
    • українськаукраинский
      UK
    /
    Фильтровать документы по фреймворку
    Alt+←
    Почему Intlayer?
    Начать
    Концепция
    • Как работает Intlayer
    • Конфигурация
    • TestFillBuildWatchExtractLoginPushPullConfigurationListVersionEditorLiveDebugDoc ReviewDoc TranslateSDK
    • Визуальный редактор
    • CMS
    • Интеграция CI/CD
    • ПереводМножественное числоПеречислениеУсловиеПолВставкаФайлВложенностьMarkdownHTMLПолучение функции
    • Файл для каждой локали
    • Компилятор
    • Автозаполнение
    • Тестирование
    • Оптимизация пакета
    Окружающая среда
    • Next.js 14 и App Router
      Next.js 15
      Next.js без locale URL
      Next.js и Page Router
      Compiler
    • Tanstack Start Solid
    • Astro и React
      Astro и Svelte
      Astro и Vue
      Astro и Solid
      Astro и Preact
      Astro и Lit
      Astro и Vanilla JS
    • React Router v7
      React Router v7 (fs-routes)
      Compiler
    • Nuxt и Vue
    • Vite и Solid
    • SvelteKit
    • Vite и Preact
    • Vite и Vanilla JS
    • Vite и Lit
    • Angular 19 (Webpack)
      Analog
    • React CRA
    • React Native и Expo
    • Express.js
      NestJS
      Fastify
      Hono
      Adonis
    • Lynx и React
    Plugins
    • JSON
    • gettext (.po)
    Расширение VS Code
    Агент
    • Сервер MCP
    • Навики агента
    Релизы
    • v8
    • v7
    • v6
    Бенчмарк
    • Next.js
    • TanStack
    • Vue
    • Solid
    • Svelte
    Блог
    Задать вопрос
    1. Documentation
    2. Окружающая среда
    3. Tanstack Start
    Создание:2025-09-09Последнее обновление:2026-05-06
    Посмотреть шаблон приложения на GitHub

    Для этой страницы доступен шаблон приложения.

    Посмотреть демонстрационное приложение

    Эта страница ведёт на живую демонстрацию шаблона.

    Посмотреть видео урок

    Для этой страницы доступен видео урок.

    Ссылайтесь на этот документ на ваш любимый ассистент AI
    ChatGPT
    Claude
    DeepSeek
    Google AI mode
    Gemini
    Perplexity
    Mistral
    Grok

    Спросите свой вопрос и получите сводку документа, используя эту страницу и выбранного вами поставщика AI

    История версий

    1. "Обновление использования API useIntlayer в Solid для прямого доступа к свойствам"
      v8.9.004.05.2026
    2. "Добавить команду init"
      v7.5.930.12.2025
    3. "Внедрена validatePrefix и добавлен шаг 14: Обработка страниц 404 с локализованными маршрутами."
      v7.4.011.12.2025
    4. "Добавлен шаг 13: Получение текущей локали в ваших server actions (опционально)"
      v7.3.905.12.2025
    5. "Добавить шаг 13: Адаптация Nitro"
      v7.2.318.11.2025
    6. "Исправлено значение префикса по умолчанию путем добавления функции getPrefix useLocalizedNavigate, LocaleSwitcher и LocalizedLink."
      v7.1.017.11.2025
    7. "Обновление документации"
      v6.5.203.10.2025
    8. "Добавлено для Tanstack Start"
      v5.8.109.09.2025

    Содержимое этой страницы было переведено с помощью ИИ.

    Смотреть последнюю версию оригинального контента на английском
    Изменить эту документацию

    Если у вас есть идея по улучшению этой документации, не стесняйтесь внести свой вклад, подав запрос на вытягивание на GitHub.

    Ссылка на документацию GitHub
    Копировать

    Копировать Markdown документа в буфер обмена

    Переведите ваш Tanstack Start с Intlayer | Интернационализация (i18n)

    Содержание

    Это руководство демонстрирует, как интегрировать Intlayer для бесшовной интернационализации в проектах Tanstack Start с маршрутизацией, учитывающей локаль, поддержкой TypeScript и современными практиками разработки.

    Что такое Intlayer?

    Intlayer, это инновационная, открытая библиотека интернационализации (i18n), созданная для упрощения поддержки многоязычности в современных веб-приложениях.

    С помощью Intlayer вы можете:

    • Легко управлять переводами с использованием декларативных словарей на уровне компонентов.
    • Динамически локализовать метаданные, маршруты и контент.
    • Обеспечить поддержку TypeScript с помощью автогенерируемых типов, улучшая автодополнение и обнаружение ошибок.
    • Воспользоваться расширенными функциями, такими как динамическое определение и переключение локали.
    • Включите маршрутизацию с учетом локали с помощью файловой системы маршрутизации Tanstack Start.

    Пошаговое руководство по настройке Intlayer в приложении Tanstack Start

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

    См. Шаблон приложения на GitHub.

    Шаг 1: Создайте проект

    Начните с создания нового проекта TanStack Start, следуя руководству Start new project на сайте TanStack Start.

    Шаг 2: Установите пакеты Intlayer

    Установите необходимые пакеты, используя предпочитаемый менеджер пакетов:

    bash
    Копировать код

    Копировать код в буфер обмена

    npm install intlayer react-intlayernpm install vite-intlayer --save-devnpx intlayer init
    • intlayer

      Основной пакет, предоставляющий инструменты интернационализации для управления конфигурацией, перевода, объявления контента, транспиляции и CLI-команд.

    • react-intlayer Пакет, который интегрирует Intlayer с приложением React. Он предоставляет провайдеры контекста и хуки для интернационализации в React.

    • vite-intlayer Включает плагин Vite для интеграции Intlayer с сборщиком Vite, а также промежуточное ПО для определения предпочтительной локали пользователя, управления куки и обработки перенаправления URL.

    Шаг 3: Конфигурация вашего проекта

    Создайте файл конфигурации для настройки языков вашего приложения:

    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;
    С помощью этого файла конфигурации вы можете настроить локализованные URL, перенаправление через middleware, имена cookie, расположение и расширение ваших объявлений контента, отключить логи Intlayer в консоли и многое другое. Для полного списка доступных параметров обратитесь к документации по конфигурации.

    Шаг 4: Интеграция Intlayer в вашу конфигурацию Vite

    Добавьте плагин intlayer в вашу конфигурацию:

    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(),    tanstackStart({      router: {        routeFileIgnorePattern:          ".content.(ts|tsx|js|mjs|cjs|jsx|json|jsonc|json5)$",      },    }),    viteReact(),  ],});export default config;
    Плагин Vite intlayer() используется для интеграции Intlayer с Vite. Он обеспечивает сборку файлов деклараций контента и отслеживает их в режиме разработки. Также он определяет переменные окружения Intlayer внутри приложения Vite. Дополнительно плагин предоставляет алиасы для оптимизации производительности.

    Шаг 5: Создайте корневой макет

    Настройте корневой макет для поддержки интернационализации, используя useParams для определения текущей локали и установив атрибуты lang и dir в теге html.

    src/routes/__root.tsx
    Копировать код

    Копировать код в буфер обмена

    import {  createRootRouteWithContext,  HeadContent,  Scripts,} from "@tanstack/react-router";import { defaultLocale, getHTMLTextDir } from "intlayer";import { type ReactNode } from "react";import { IntlayerProvider } from "react-intlayer";import { Route as LocaleRoute } from "./{-$locale}/route";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>  );}
    Если вы хотите использовать ваш контент в атрибуте типа string, таком как alt, title, href, aria-label и т.д., вы должны вызвать значение функции, например:
    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)}" />

    Шаг 6: Создайте макет локали

    Создайте макет, который обрабатывает префикс локали и выполняет валидацию.

    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;    // Валидация префикса локали    const { isValid, localePrefix } = validatePrefix(localeParam);    if (!isValid) {      throw redirect({        to: "/{-$locale}/404",        params: { locale: localePrefix },      });    }  },  component: Outlet,});
    Здесь {-$locale}, это динамический параметр маршрута, который заменяется текущей локалью. Эта нотация делает слот необязательным, позволяя ему работать с такими режимами маршрутизации, как 'prefix-no-default' и т. д.

    Имейте в виду, что этот слот может вызвать проблемы, если вы используете несколько динамических сегментов в одном маршруте (например, /{-$locale}/other-path/$anotherDynamicPath/...). Для режима 'prefix-all' вы можете предпочесть переключить слот на $locale. Для режимов 'no-prefix' или 'search-params' вы можете полностью удалить слот.

    Шаг 7: Объявите ваш контент

    Создавайте и управляйте объявлениями контента для хранения переводов:

    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;
    Ваши объявления контента могут быть определены в любом месте вашего приложения, как только они включены в директорию contentDir (по умолчанию, ./app). И соответствуют расширению файла объявления контента (по умолчанию, .content.{json,ts,tsx,js,jsx,mjs,cjs}).
    Для получения дополнительной информации обратитесь к документации по объявлениям контента.

    Шаг 8: Создание компонентов и хуков с поддержкой локализации

    Создайте компонент LocalizedLink для навигации с учетом локали:

    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"]}    />  );};

    Этот компонент выполняет две задачи:

    • Удаляет ненужный префикс {-$locale} из URL.
    • Вставляет параметр локали в URL, чтобы пользователь был напрямую перенаправлен на локализованный маршрут.

    Далее мы можем создать хук useLocalizedNavigate для программной навигации:

    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: Использование Intlayer на ваших страницах

    Получайте доступ к вашим словарям контента по всему приложению:

    Локализованная главная страница

    src/routes/{-$locale}/index.tsx
    Копировать код

    Копировать код в буфер обмена

    import { createFileRoute } from "@tanstack/react-router";import { getIntlayer } from "intlayer";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>  );}
    Чтобы узнать больше о хуке useIntlayer, обратитесь к документации.

    Шаг 10: Создание компонента переключателя локали

    Создайте компонент, который позволит пользователям менять язык:

    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>              {/* Локаль - например, FR */}              {localeEl}            </span>            <span>              {/* Язык на его собственной локали - например, Français */}              {getLocaleName(localeEl, locale)}            </span>            <span dir={getHTMLTextDir(localeEl)} lang={localeEl}>              {/* Язык на текущей локали - например, Francés при установленной локали Locales.SPANISH */}              {getLocaleName(localeEl)}            </span>            <span dir="ltr" lang={Locales.ENGLISH}>              {/* Язык на английском - например, French */}              {getLocaleName(localeEl, Locales.ENGLISH)}            </span>          </LocalizedLink>        </li>      ))}    </ol>  );};
    Чтобы узнать больше о хуке useLocale, обратитесь к документации.

    Шаг 11: Управление атрибутами HTML

    Как показано на шаге 5, вы можете управлять атрибутами lang и dir тега html, используя useParams в вашем корневом компоненте. Это гарантирует правильную установку атрибутов как на сервере, так и на клиенте.

    src/routes/__root.tsx
    Копировать код

    Копировать код в буфер обмена

    function RootDocument({ children }: { children: ReactNode }) {  const params = LocaleRoute.useParams();  const locale = params?.locale ?? defaultLocale;  return (    <html dir={getHTMLTextDir(locale)} lang={locale}>      {/* ... */}    </html>  );}

    Шаг 12: Добавление middleware (опционально)

    Вы также можете использовать intlayerProxy для добавления маршрутизации на стороне сервера в ваше приложение. Этот плагин автоматически определит текущую локаль на основе URL и установит соответствующий cookie с локалью. Если локаль не указана, плагин определит наиболее подходящую локаль на основе языковых предпочтений браузера пользователя. Если локаль не будет обнаружена, произойдет перенаправление на локаль по умолчанию.

    Обратите внимание, что для использования intlayerProxy в продакшене необходимо переместить пакет vite-intlayer из devDependencies в dependencies.
    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, intlayerProxy } from "vite-intlayer";export default defineConfig({  plugins: [    intlayerProxy(), // Прокси должен быть размещен перед сервером, если вы используете Nitro    nitro(),    intlayer(),    tanstackStart({      router: {        routeFileIgnorePattern:          ".content.(ts|tsx|js|mjs|cjs|jsx|json|jsonc|json5)$",      },    }),    viteReact(),  ],});

    Шаг 12: Интернационализация метаданных (опционально)

    Вы также можете использовать хук getIntlayer для доступа к вашим словарям контента во всем приложении:

    src/routes/{-$locale}/index.tsx
    Копировать код

    Копировать код в буфер обмена

    import { createFileRoute } from "@tanstack/react-router";import { getIntlayer } from "intlayer";export const Route = createFileRoute("/{-$locale}/")({  component: RouteComponent,  head: ({ params }) => {    const { locale } = params;    const path = "/"; // The path for this route    const metaContent = getIntlayer("app", locale);    return {      links: [        // Canonical link: Points to the current localized page        { rel: "canonical", href: getLocalizedUrl(path, locale) },        // Hreflang: Tell Google about all localized versions        ...localeMap(({ locale: mapLocale }) => ({          rel: "alternate",          hrefLang: mapLocale,          href: getLocalizedUrl(path, mapLocale),        })),        // x-default: For users in unmatched languages        // Define the default fallback locale (usually your primary language)        {          rel: "alternate",          hrefLang: "x-default",          href: getLocalizedUrl(path, defaultLocale),        },      ],      meta: [        { title: metaContent.title },        { name: "description", content: metaContent.meta.description },      ],    };  },});

    Шаг 13: Получение локали в серверных действиях (опционально)

    Вы можете захотеть получить доступ к текущей локали внутри ваших серверных действий или конечных точек API. Вы можете сделать это с помощью помощника getLocale из intlayer.

    Вот пример использования серверных функций 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({    // Получить cookie из запроса (по умолчанию: 'INTLAYER_LOCALE')    getCookie: (name) => {      const cookieString = getRequestHeader("cookie");      return getCookie(name, cookieString);    },    // Получить заголовок из запроса (по умолчанию: 'x-intlayer-locale')    // Резервный вариант с использованием согласования Accept-Language    getHeader: (name) => getRequestHeader(name),  });  // Получить контент с помощью getIntlayer()  const content = getIntlayer("app", locale);  return { locale, content };});

    Шаг 14: Управление страницами "не найдено" (опционально)

    Когда пользователь посещает несуществующую страницу, вы можете отобразить пользовательскую страницу "не найдено", и префикс локали может повлиять на способ срабатывания страницы "не найдено".

    Понимание обработки 404 в TanStack Router с префиксами локали

    В TanStack Router обработка страниц 404 с локализованными маршрутами требует многоуровневого подхода:

    1. Выделенный маршрут 404: Специфический маршрут для отображения интерфейса 404
    2. Проверка на уровне маршрута: Проверяет префиксы локали и перенаправляет недействительные на 404
    3. Маршрут catch-all: Перехватывает все несовпадающие пути в сегменте локали
    src/routes/{-$locale}/404.tsx
    Копировать код

    Копировать код в буфер обмена

    import { createFileRoute } from "@tanstack/react-router";// Это создает выделенный маршрут /[locale]/404// Он используется как прямой маршрут и импортируется как компонент в других файлахexport const Route = createFileRoute("/{-$locale}/404")({  component: NotFoundComponent,});// Экспортируется отдельно, чтобы можно было повторно использовать в notFoundComponent и catch-all маршрутахexport function NotFoundComponent() {  return (    <div>      <h1>404</h1>    </div>  );}
    src/routes/{-$locale}/route.tsx
    Копировать код

    Копировать код в буфер обмена

    import { createFileRoute, Outlet, redirect } from "@tanstack/react-router";import { validatePrefix } from "intlayer";import { NotFoundComponent } from "./404";export const Route = createFileRoute("/{-$locale}")({  // beforeLoad выполняется до рендеринга маршрута (как на сервере, так и на клиенте)  // Это идеальное место для проверки префикса локали  beforeLoad: ({ params }) => {    const localeParam = params.locale;    // validatePrefix проверяет, является ли локаль действительной согласно вашей конфигурации intlayer    const { isValid, localePrefix } = validatePrefix(localeParam);    if (!isValid) {      // Недействительный префикс локали - перенаправить на страницу 404 с действительным префиксом локали      throw redirect({        to: "/{-$locale}/404",        params: { locale: localePrefix },      });    }  },  component: Outlet,  // notFoundComponent вызывается, когда дочерний маршрут не существует  // например, /en/несуществующая-страница запускает это в макете /en  notFoundComponent: NotFoundComponent,});
    src/routes/{-$locale}/$.tsx
    Копировать код

    Копировать код в буфер обмена

    import { createFileRoute } from "@tanstack/react-router";import { NotFoundComponent } from "./404";// Маршрут $ (splat/catch-all) соответствует любому пути, который не соответствует другим маршрутам// например, /en/какой-то/глубоко/вложенный/недействительный/путь// Это гарантирует, что ВСЕ несоответствующие пути в локали показывают страницу 404// Без этого несоответствующие глубокие пути могут показать пустую страницу или ошибкуexport const Route = createFileRoute("/{-$locale}/$")({  component: NotFoundComponent,});

    (Опционально) Шаг 15: Извлечение содержимого ваших компонентов

    Если у вас есть существующая кодовая база, преобразование тысяч файлов может занять много времени.

    Чтобы упростить этот процесс, Intlayer предлагает компилятор / экстрактор для преобразования ваших компонентов и извлечения содержимого.

    Чтобы настроить его, вы можете добавить раздел compiler в ваш файл intlayer.config.ts:

    intlayer.config.ts
    Копировать код

    Копировать код в буфер обмена

    import { type IntlayerConfig } from "intlayer";
    
    const config: IntlayerConfig = {
      // ... Остальная часть вашей конфигурации
      compiler: {
        /**
         * Указывает, должен ли быть включен компилятор.
         */
        enabled: true,
    
        /**
         * Определяет путь к выходным файлам
         */
        output: ({ fileName, extension }) => `./${fileName}${extension}`,
    
        /**
         * Указывает, должны ли компоненты сохраняться после преобразования. Таким образом, компилятор можно запустить только один раз для преобразования приложения, а затем удалить.
         */
        saveComponents: false,
    
        /**
         * Префикс ключа словаря
         */
        dictionaryKeyPrefix: "",
      },
    };
    
    export default config;

    Запустите экстрактор для преобразования компонентов и извлечения содержимого

    bash
    Копировать код

    Копировать код в буфер обмена

    npx intlayer extract

    Обновите ваш vite.config.ts, чтобы включить плагин intlayerCompiler:

    vite.config.ts
    Копировать код

    Копировать код в буфер обмена

    import { defineConfig } from "vite";import { intlayer, intlayerCompiler } from "vite-intlayer";export default defineConfig({ plugins: [   intlayer(),   intlayerCompiler(), // Добавляет плагин компилятора ],});
    bash
    Копировать код

    Копировать код в буфер обмена

    npm run build # Или npm run dev

    Шаг 16: Генерация карты сайта (Sitemap) (опционально)

    Intlayer поставляется со встроенным генератором карты сайта, который поможет вам легко создать карту сайта для вашего приложения. Он учитывает локализованные маршруты и добавляет необходимые метаданные для поисковых систем.

    Создаваемая Intlayer карта сайта поддерживает пространство имен xhtml:link (Hreflang XML Extensions). В отличие от стандартных генераторов карт сайта, которые просто перечисляют прямые URL-адреса, Intlayer автоматически создает необходимые двусторонние связи между всеми языковыми версиями страницы (например, /about, /about?lang=fr и /about?lang=es). Это гарантирует, что поисковые системы будут правильно индексировать и показывать нужную языковую версию соответствующей аудитории.

    Чтобы использовать его, вам сначала нужно настроить ваш файл vite.config.ts, чтобы включить предварительный рендеринг (pre-rendering) для ваших локализованных маршрутов и отключить генерацию карты сайта по умолчанию в TanStack Start.

    vite.config.ts
    Копировать код

    Копировать код в буфер обмена

    import { localeFlatMap } from "intlayer";// ... другие импортыexport const pathList = ["", "/about", "/404"];const localizedPages = localeFlatMap(({ urlPrefix }) =>  pathList.map((path) => ({    path: `${urlPrefix}${path}`,    prerender: {      enabled: true,    },  })));export default defineConfig({  plugins: [    // ... другие плагины    tanstackStart({      // ... другие настройки      sitemap: {        enabled: false,      },      prerender: {        enabled: true,        crawlLinks: false,        concurrency: 10,      },      pages: localizedPages,    }),  ],});

    Затем создайте маршрут src/routes/sitemap[.]xml.ts, который использует функцию generateSitemap:

    src/routes/sitemap[.]xml.ts
    Копировать код

    Копировать код в буфер обмена

    import { createFileRoute } from "@tanstack/react-router";import { generateSitemap } from "intlayer";const SITE_URL = (  import.meta.env.VITE_SITE_URL ?? "http://localhost:3000").replace(/\/$/, "");export const Route = createFileRoute("/sitemap.xml")({  server: {    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" },        });      },    },  },});

    Шаг 17: Настройка TypeScript (опционально)

    Intlayer использует расширение модулей (module augmentation), чтобы использовать преимущества TypeScript и сделать вашу кодовую базу более надежной.

    Убедитесь, что ваша конфигурация TypeScript включает автоматически сгенерированные типы:

    tsconfig.json
    Копировать код

    Копировать код в буфер обмена

    {  // ... ваши существующие настройки  include: [    // ... ваши существующие включения    ".intlayer/**/*.ts", // Включить автоматически сгенерированные типы  ],}

    Конфигурация Git

    Рекомендуется игнорировать файлы, сгенерированные Intlayer. Это позволит избежать их коммита в ваш Git-репозиторий.

    Чтобы сделать это, вы можете добавить следующие инструкции в ваш файл .gitignore:

    .gitignore
    Копировать код

    Копировать код в буфер обмена

    # Игнорировать файлы, сгенерированные Intlayer.intlayer

    Расширение VS Code

    Для улучшения вашего опыта разработки с Intlayer вы можете установить официальное расширение Intlayer для VS Code.

    Установить из VS Code Marketplace

    Это расширение предоставляет:

    • Автозаполнение для ключей переводов.
    • Обнаружение ошибок в реальном времени для отсутствующих переводов.
    • Встроенный просмотр переведённого контента.
    • Быстрые действия для лёгкого создания и обновления переводов.

    Для получения дополнительной информации о том, как использовать расширение, обратитесь к документации расширения Intlayer для VS Code.


    Продвинутые возможности

    Для расширения возможностей вы можете реализовать визуальный редактор или вынести ваш контент во внешний CMS.


    Ссылки на документацию

    • Документация Intlayer
    • Документация Tanstack Start
    • Хук useIntlayer
    • useLocale hook
    • Объявление контента
    • Конфигурация
    Compiler
    Tanstack Start Solid
    Alt+→

    На этой странице

      Обсуждения анонимны и регулярно просматриваются для решения распространённых проблем. Не стесняйтесь делиться идеями функций, отзывами о документации или чем-либо, связанным с Intlayer, мы используем эту информацию для формирования нашей дорожной карты и улучшения продукта.

      npm install intlayer react-intlayernpm install vite-intlayer --save-devnpx intlayer init
      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;
      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(),    tanstackStart({      router: {        routeFileIgnorePattern:          ".content.(ts|tsx|js|mjs|cjs|jsx|json|jsonc|json5)$",      },    }),    viteReact(),  ],});export default config;
      import {  createRootRouteWithContext,  HeadContent,  Scripts,} from "@tanstack/react-router";import { defaultLocale, getHTMLTextDir } from "intlayer";import { type ReactNode } from "react";import { IntlayerProvider } from "react-intlayer";import { Route as LocaleRoute } from "./{-$locale}/route";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>  );}
      <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)}" />
      import { createFileRoute, Outlet, redirect } from "@tanstack/react-router";import { validatePrefix } from "intlayer";export const Route = createFileRoute("/{-$locale}")({  beforeLoad: ({ params }) => {    const localeParam = params.locale;    // Валидация префикса локали    const { isValid, localePrefix } = validatePrefix(localeParam);    if (!isValid) {      throw redirect({        to: "/{-$locale}/404",        params: { locale: localePrefix },      });    }  },  component: Outlet,});
      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;
      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"]}    />  );};
      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;};
      import { createFileRoute } from "@tanstack/react-router";import { getIntlayer } from "intlayer";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>  );}
      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>              {/* Локаль - например, FR */}              {localeEl}            </span>            <span>              {/* Язык на его собственной локали - например, Français */}              {getLocaleName(localeEl, locale)}            </span>            <span dir={getHTMLTextDir(localeEl)} lang={localeEl}>              {/* Язык на текущей локали - например, Francés при установленной локали Locales.SPANISH */}              {getLocaleName(localeEl)}            </span>            <span dir="ltr" lang={Locales.ENGLISH}>              {/* Язык на английском - например, French */}              {getLocaleName(localeEl, Locales.ENGLISH)}            </span>          </LocalizedLink>        </li>      ))}    </ol>  );};
      function RootDocument({ children }: { children: ReactNode }) {  const params = LocaleRoute.useParams();  const locale = params?.locale ?? defaultLocale;  return (    <html dir={getHTMLTextDir(locale)} lang={locale}>      {/* ... */}    </html>  );}
      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, intlayerProxy } from "vite-intlayer";export default defineConfig({  plugins: [    intlayerProxy(), // Прокси должен быть размещен перед сервером, если вы используете Nitro    nitro(),    intlayer(),    tanstackStart({      router: {        routeFileIgnorePattern:          ".content.(ts|tsx|js|mjs|cjs|jsx|json|jsonc|json5)$",      },    }),    viteReact(),  ],});
      import { createFileRoute } from "@tanstack/react-router";import { getIntlayer } from "intlayer";export const Route = createFileRoute("/{-$locale}/")({  component: RouteComponent,  head: ({ params }) => {    const { locale } = params;    const path = "/"; // The path for this route    const metaContent = getIntlayer("app", locale);    return {      links: [        // Canonical link: Points to the current localized page        { rel: "canonical", href: getLocalizedUrl(path, locale) },        // Hreflang: Tell Google about all localized versions        ...localeMap(({ locale: mapLocale }) => ({          rel: "alternate",          hrefLang: mapLocale,          href: getLocalizedUrl(path, mapLocale),        })),        // x-default: For users in unmatched languages        // Define the default fallback locale (usually your primary language)        {          rel: "alternate",          hrefLang: "x-default",          href: getLocalizedUrl(path, defaultLocale),        },      ],      meta: [        { title: metaContent.title },        { name: "description", content: metaContent.meta.description },      ],    };  },});
      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({    // Получить cookie из запроса (по умолчанию: 'INTLAYER_LOCALE')    getCookie: (name) => {      const cookieString = getRequestHeader("cookie");      return getCookie(name, cookieString);    },    // Получить заголовок из запроса (по умолчанию: 'x-intlayer-locale')    // Резервный вариант с использованием согласования Accept-Language    getHeader: (name) => getRequestHeader(name),  });  // Получить контент с помощью getIntlayer()  const content = getIntlayer("app", locale);  return { locale, content };});
      import { createFileRoute } from "@tanstack/react-router";// Это создает выделенный маршрут /[locale]/404// Он используется как прямой маршрут и импортируется как компонент в других файлахexport const Route = createFileRoute("/{-$locale}/404")({  component: NotFoundComponent,});// Экспортируется отдельно, чтобы можно было повторно использовать в notFoundComponent и catch-all маршрутахexport function NotFoundComponent() {  return (    <div>      <h1>404</h1>    </div>  );}
      import { createFileRoute, Outlet, redirect } from "@tanstack/react-router";import { validatePrefix } from "intlayer";import { NotFoundComponent } from "./404";export const Route = createFileRoute("/{-$locale}")({  // beforeLoad выполняется до рендеринга маршрута (как на сервере, так и на клиенте)  // Это идеальное место для проверки префикса локали  beforeLoad: ({ params }) => {    const localeParam = params.locale;    // validatePrefix проверяет, является ли локаль действительной согласно вашей конфигурации intlayer    const { isValid, localePrefix } = validatePrefix(localeParam);    if (!isValid) {      // Недействительный префикс локали - перенаправить на страницу 404 с действительным префиксом локали      throw redirect({        to: "/{-$locale}/404",        params: { locale: localePrefix },      });    }  },  component: Outlet,  // notFoundComponent вызывается, когда дочерний маршрут не существует  // например, /en/несуществующая-страница запускает это в макете /en  notFoundComponent: NotFoundComponent,});
      import { createFileRoute } from "@tanstack/react-router";import { NotFoundComponent } from "./404";// Маршрут $ (splat/catch-all) соответствует любому пути, который не соответствует другим маршрутам// например, /en/какой-то/глубоко/вложенный/недействительный/путь// Это гарантирует, что ВСЕ несоответствующие пути в локали показывают страницу 404// Без этого несоответствующие глубокие пути могут показать пустую страницу или ошибкуexport const Route = createFileRoute("/{-$locale}/$")({  component: NotFoundComponent,});
      npx intlayer extract
      import { defineConfig } from "vite";import { intlayer, intlayerCompiler } from "vite-intlayer";export default defineConfig({ plugins: [   intlayer(),   intlayerCompiler(), // Добавляет плагин компилятора ],});
      npm run build # Или npm run dev
      import { localeFlatMap } from "intlayer";// ... другие импортыexport const pathList = ["", "/about", "/404"];const localizedPages = localeFlatMap(({ urlPrefix }) =>  pathList.map((path) => ({    path: `${urlPrefix}${path}`,    prerender: {      enabled: true,    },  })));export default defineConfig({  plugins: [    // ... другие плагины    tanstackStart({      // ... другие настройки      sitemap: {        enabled: false,      },      prerender: {        enabled: true,        crawlLinks: false,        concurrency: 10,      },      pages: localizedPages,    }),  ],});
      import { createFileRoute } from "@tanstack/react-router";import { generateSitemap } from "intlayer";const SITE_URL = (  import.meta.env.VITE_SITE_URL ?? "http://localhost:3000").replace(/\/$/, "");export const Route = createFileRoute("/sitemap.xml")({  server: {    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" },        });      },    },  },});
      {  // ... ваши существующие настройки  include: [    // ... ваши существующие включения    ".intlayer/**/*.ts", // Включить автоматически сгенерированные типы  ],}
      # Игнорировать файлы, сгенерированные Intlayer.intlayer