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

    Смотреть последнюю версию оригинального контента на английском

    Начало работы с интернационализацией (i18n) с Intlayer и Next.js 15 App Router

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

    Что такое Intlayer?

    Intlayer , это инновационная, открытая библиотека интернационализации (i18n), разработанная для упрощения поддержки многоязычности в современных веб-приложениях. Intlayer бесшовно интегрируется с последней версией фреймворка Next.js 15, включая его мощный App Router. Она оптимизирована для работы с Server Components для эффективного рендеринга и полностью совместима с Turbopack.

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

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

    Intlayer совместим с Next.js 12, 13, 14 и 15. Если вы используете Next.js Page Router, вы можете обратиться к этому руководству. Для Next.js 12, 13, 14 с App Router, обратитесь к этому руководству.


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

    Шаг 1: Установите зависимости

    Установите необходимые пакеты с помощью npm:

    bash
    npm install intlayer next-intlayer
    • intlayer

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

    • next-intlayer

      Пакет, интегрирующий Intlayer с Next.js. Он предоставляет провайдеры контекста и хуки для интернационализации в Next.js. Кроме того, он включает плагин Next.js для интеграции Intlayer с Webpack или Turbopack, а также middleware для определения предпочтительной локали пользователя, управления cookies и обработки перенаправлений URL.

    Шаг 2: Настройте ваш проект

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

    intlayer.config.ts
    import { Locales, type IntlayerConfig } from "intlayer";
    
    const config: IntlayerConfig = {
      internationalization: {
        locales: [
          Locales.ENGLISH,
          Locales.FRENCH,
          Locales.SPANISH,
          // Ваши другие локали
        ],
        defaultLocale: Locales.ENGLISH,
      },
    };
    
    export default config;

    Через этот файл конфигурации вы можете настроить локализованные URL, перенаправления middleware, имена cookies, расположение и расширение ваших деклараций контента, отключить логи Intlayer в консоли и многое другое. Для полного списка доступных параметров обратитесь к документации по конфигурации.

    Шаг 3: Интеграция Intlayer в конфигурацию Next.js

    Настройте ваш проект Next.js для использования Intlayer:

    typescript
    import type { NextConfig } from "next";
    import { withIntlayer } from "next-intlayer/server";
    
    const nextConfig: NextConfig = {
      /* параметры конфигурации */
    };
    
    export default withIntlayer(nextConfig);

    Плагин withIntlayer() для Next.js используется для интеграции Intlayer с Next.js. Он обеспечивает построение файлов декларации контента и их мониторинг в режиме разработки. Он определяет переменные окружения Intlayer в средах Webpack или Turbopack. Кроме того, он предоставляет алиасы для оптимизации производительности и обеспечивает совместимость с серверными компонентами.

    Шаг 4: Определите динамические маршруты локалей

    Удалите все из RootLayout и замените следующим кодом:

    src/app/layout.tsx
    import type { PropsWithChildren, FC } from "react";
    import "./globals.css";
    
    const RootLayout: FC<PropsWithChildren> = ({ children }) => children;
    
    export default RootLayout;

    Оставляя компонент RootLayout пустым, вы можете установить атрибуты lang и dir для тега <html>.

    Для реализации динамической маршрутизации предоставьте путь для локали, добавив новый layout в ваш каталог [locale]:

    src/app/[locale]/layout.tsx
    import type { NextLayoutIntlayer } from "next-intlayer";
    import { Inter } from "next/font/google";
    import { getHTMLTextDir } from "intlayer";
    
    const inter = Inter({ subsets: ["latin"] });
    
    const LocaleLayout: NextLayoutIntlayer = async ({ children, params }) => {
      const { locale } = await params;
      return (
        <html lang={locale} dir={getHTMLTextDir(locale)}>
          <body className={inter.className}>{children}</body>
        </html>
      );
    };
    
    export default LocaleLayout;

    module.exports = LocaleLayout;

    > Сегмент пути `[locale]` используется для определения локали. Пример: `/en-US/about` будет относиться к `en-US`, а `/fr/about` к `fr`. Затем реализуйте функцию `generateStaticParams` в макете вашего приложения. ```tsx {1} fileName="src/app/[locale]/layout.tsx" codeFormat="typescript" export { generateStaticParams } from "next-intlayer"; // Строка для вставки const LocaleLayout: NextLayoutIntlayer = async ({ children, params }) => { /*... Остальной код*/ }; export default LocaleLayout;

    generateStaticParams гарантирует, что ваше приложение предварительно создает необходимые страницы для всех локалей, уменьшая вычисления во время выполнения и улучшая пользовательский опыт. Для получения дополнительной информации обратитесь к документации Next.js о generateStaticParams.

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

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

    src/app/[locale]/page.content.ts
    import { t, type Dictionary } from "intlayer";
    
    const pageContent = {
      key: "page",
      content: {
        getStarted: {
          main: t({
            ru: "Начните с редактирования",
            en: "Get started by editing",
            fr: "Commencez par éditer",
            es: "Comience por editar",
          }),
          pageLink: "src/app/page.tsx",
        },
      },
    } satisfies Dictionary;
    
    export default pageContent;

    Ваши объявления контента могут быть определены в любом месте вашего приложения, если они включены в каталог contentDir (по умолчанию, ./src) и соответствуют расширению файла объявления контента (по умолчанию, .content.{json,ts,tsx,js,jsx,mjs,mjx,cjs,cjx}). Для получения дополнительной информации обратитесь к документации по объявлениям контента.

    Шаг 6: Используйте контент в вашем коде

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

    src/app/[locale]/page.tsx
    import type { FC } from "react";
    import { ClientComponentExample } from "@components/ClientComponentExample";
    import { ServerComponentExample } from "@components/ServerComponentExample";
    import { type NextPageIntlayer, IntlayerClientProvider } from "next-intlayer";
    import { IntlayerServerProvider, useIntlayer } from "next-intlayer/server";
    
    const PageContent: FC = () => {
      const content = useIntlayer("page");
    
      return (
        <>
          <p>{content.getStarted.main}</p>
          <code>{content.getStarted.pageLink}</code>
        </>
      );
    };
    
    const Page: NextPageIntlayer = async ({ params }) => {
      const { locale } = await params;
    
      return (
        <IntlayerServerProvider locale={locale}>
          <PageContent />
          <ServerComponentExample />
    
          <IntlayerClientProvider locale={locale}>
            <ClientComponentExample />
          </IntlayerClientProvider>
        </IntlayerServerProvider>
      );
    };
    
    export default Page;
    • IntlayerClientProvider используется для предоставления локали клиентским компонентам. Он может быть размещен в любом родительском компоненте, включая макет. Однако рекомендуется размещать его в макете, так как Next.js использует общий код макета для страниц, что делает его более эффективным. Используя IntlayerClientProvider в макете, вы избегаете его повторной инициализации для каждой страницы, улучшая производительность и поддерживая единый контекст локализации во всем приложении.
    • IntlayerServerProvider используется для предоставления локали серверным дочерним компонентам. Он не может быть установлен в макете.

      Макет и страница не могут делить общий серверный контекст, так как система серверного контекста основана на хранилище данных для каждого запроса (через механизм React’s cache), что приводит к созданию нового “контекста” для разных сегментов приложения. Размещение провайдера в общем макете нарушит эту изоляцию, предотвращая корректное распространение значений серверного контекста на ваши серверные компоненты.

    src/components/ClientComponentExample.tsx
    "use client";
    
    import type { FC } from "react";
    import { useIntlayer } from "next-intlayer";
    
    export const ClientComponentExample: FC = () => {
      const content = useIntlayer("client-component-example"); // Создать декларацию связанного контента
    
      return (
        <div>
          <h2>{content.title} </h2>
          <p>{content.content}</p>
        </div>
      );
    };
    src/components/ServerComponentExample.tsx
    import type { FC } from "react";
    import { useIntlayer } from "next-intlayer/server";
    
    export const ServerComponentExample: FC = () => {
      const content = useIntlayer("server-component-example"); // Создать декларацию связанного контента
    
      return (
        <div>
          <h2>{content.title} </h2>
          <p>{content.content}</p>
        </div>
      );
    };

    Если вы хотите использовать ваш контент в атрибуте string, таком как alt, title, href, aria-label и т.д., вы должны вызвать значение функции, например:

    jsx
    <img src={content.image.src.value} alt={content.image.value} />

    Чтобы узнать больше о хуке useIntlayer, обратитесь к документации.

    (Опционально) Шаг 7: Настройка Middleware для определения локали

    Настройте middleware для определения предпочтительной локали пользователя:

    src/middleware.ts
    export { intlayerMiddleware as middleware } from "next-intlayer/middleware";
    
    export const config = {
      matcher:
        "/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\..*|_next).*)",
    };

    intlayerMiddleware используется для определения предпочтительной локали пользователя и перенаправления его на соответствующий URL, указанный в конфигурации. Кроме того, он позволяет сохранять предпочтительную локаль пользователя в cookie.

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

    Если вы хотите интернационализировать ваши метаданные, такие как заголовок страницы, вы можете использовать функцию generateMetadata, предоставляемую Next.js. Внутри функции используйте функцию getTranslation для перевода ваших метаданных.

    src/app/[locale]/layout.tsx or src/app/[locale]/page.tsx
    import {
      type IConfigLocales,
      getTranslation,
      getMultilingualUrls,
    } from "intlayer";
    import type { Metadata } from "next";
    import type { LocalPromiseParams } from "next-intlayer";
    
    export const generateMetadata = async ({
      params,
    }: LocalPromiseParams): Promise<Metadata> => {
      const { locale } = await params;
      const t = <T>(content: IConfigLocales<T>) => getTranslation(content, locale);
    
      /**
       * Генерирует объект, содержащий все URL для каждой локали.
       *
       * Пример:
       * ```ts
       *  getMultilingualUrls('/about');
       *
       *  // Возвращает
       *  // {
       *  //   en: '/about',
       *  //   fr: '/fr/about',
       *  //   es: '/es/about',
       *  // }
       * ```
       */
      const multilingualUrls = getMultilingualUrls("/");
    
      return {
        title: t<string>({
          ru: "Мой заголовок",
          en: "My title",
          fr: "Mon titre",
          es: "Mi título",
        }),
        description: t({
          ru: "Мое описание",
          en: "My description",
          fr: "Ma description",
          es: "Mi descripción",
        }),
        alternates: {
          canonical: "/ru/",
          languages: { ...multilingualUrls, "x-default": "/" },
        },
        openGraph: {
          url: multilingualUrls[locale],
        },
      };
    };
    
    // ... Остальная часть кода

    Узнайте больше об оптимизации метаданных в официальной документации Next.js.

    (Опционально) Шаг 9: Интернационализация вашего sitemap.xml и robots.txt

    Для интернационализации вашего sitemap.xml и robots.txt вы можете использовать функцию getMultilingualUrls, предоставляемую Intlayer. Эта функция позволяет генерировать многоязычные URL для вашего sitemap.

    src/app/sitemap.ts
    import { getMultilingualUrls } from "intlayer";
    import type { MetadataRoute } from "next";
    
    const sitemap = (): MetadataRoute.Sitemap => [
      {
        url: "https://example.com",
        alternates: {
          languages: { ...getMultilingualUrls("https://example.com") },
        },
      },
      {
        url: "https://example.com/login",
        alternates: {
          languages: { ...getMultilingualUrls("https://example.com/login") },
        },
      },
      {
        url: "https://example.com/register",
        alternates: {
          languages: { ...getMultilingualUrls("https://example.com/register") },
        },
      },
    ];
    
    export default sitemap;
    src/app/robots.ts
    import type { MetadataRoute } from "next";
    import { getMultilingualUrls } from "intlayer";
    
    const getAllMultilingualUrls = (urls: string[]) =>
      urls.flatMap((url) => Object.values(getMultilingualUrls(url)) as string[]);
    
    const robots = (): MetadataRoute.Robots => ({
      rules: {
        userAgent: "*",
        allow: ["/"],
        disallow: getAllMultilingualUrls(["/login", "/register"]),
      },
      host: "https://example.com",
      sitemap: `https://example.com/sitemap.xml`,
    });
    
    export default robots;

    Узнайте больше об оптимизации sitemap в официальной документации Next.js. Узнайте больше об оптимизации robots.txt в официальной документации Next.js.

    (Опционально) Шаг 10: Изменение языка вашего контента

    Чтобы изменить язык вашего контента, вы можете использовать функцию setLocale, предоставляемую хуком useLocale. Эта функция позволяет установить локаль приложения и обновить контент соответствующим образом.

    src/components/LocaleSwitcher.tsx
    "use client";
    
    import type { FC } from "react";
    import {
      Locales,
      getHTMLTextDir,
      getLocaleName,
      getLocalizedUrl,
    } from "intlayer";
    import { useLocale } from "next-intlayer";
    import Link from "next/link";
    
    export const LocaleSwitcher: FC = () => {
      const { locale, pathWithoutLocale, availableLocales, setLocale } =
        useLocale();
    
      return (
        <div>
          <button popoverTarget="localePopover">{getLocaleName(locale)}</button>
          <div id="localePopover" popover="auto">
            {availableLocales.map((localeItem) => (
              <Link
                href={getLocalizedUrl(pathWithoutLocale, localeItem)}
                hrefLang={localeItem}
                key={localeItem}
                aria-current={locale === localeItem ? "page" : undefined}
                onClick={(e) => {
                  e.preventDefault();
                  setLocale(localeItem);
                }}
              >
                <span>
                  {/* Локаль - например, FR */}
                  {localeItem}
                </span>
                <span>
                  {/* Язык на своей локали - например, Français */}
                  {getLocaleName(localeItem, locale)}
                </span>
                <span dir={getHTMLTextDir(localeItem)} lang={localeItem}>
                  {/* Язык на текущей локали - например, Francés, если текущая локаль установлена на Locales.SPANISH */}
                  {getLocaleName(localeItem)}
                </span>
                <span dir="ltr" lang={Locales.ENGLISH}>
                  {/* Язык на английском - например, French */}
                  {getLocaleName(localeItem, Locales.ENGLISH)}
                </span>
              </Link>
            ))}
          </div>
        </div>
      );
    };

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

    (Необязательно) Шаг 11: Создание компонента локализованной ссылки

    Чтобы обеспечить навигацию в приложении в соответствии с текущей локалью, вы можете создать пользовательский компонент Link. Этот компонент автоматически добавляет префикс к внутренним URL-адресам с текущим языком. Например, когда пользователь, говорящий на французском, нажимает на ссылку на страницу "О нас", он перенаправляется на /ru/about вместо /about.

    Это поведение полезно по нескольким причинам:

    • SEO и пользовательский опыт: Локализованные URL-адреса помогают поисковым системам правильно индексировать страницы на определенных языках и предоставляют пользователям контент на их предпочтительном языке.
    • Последовательность: Используя локализованную ссылку по всему приложению, вы гарантируете, что навигация остается в пределах текущей локали, предотвращая неожиданные переключения языка.
    • Поддерживаемость: Централизуя логику локализации в одном компоненте, вы упрощаете управление URL-адресами, делая ваш код более удобным для поддержки и расширения по мере роста приложения.

    Ниже приведена реализация локализованного компонента Link на TypeScript:

    src/components/Link.tsx
    "use client";
    
    import { getLocalizedUrl } from "intlayer";
    import NextLink, { type LinkProps as NextLinkProps } from "next/link";
    import { useLocale } from "next-intlayer";
    import type { PropsWithChildren, FC } from "react";
    
    /**
     * Утилита для проверки, является ли данный URL внешним.
     * Если URL начинается с http:// или https://, он считается внешним.
     */
    export const checkIsExternalLink = (href?: string): boolean =>
      /^https?:///.test(href ?? "");
    
    /**
     * Пользовательский компонент Link, который адаптирует атрибут href на основе текущей локали.
     * Для внутренних ссылок используется `getLocalizedUrl` для добавления префикса URL с локалью (например, /fr/about).
     * Это гарантирует, что навигация остается в контексте той же локали.
     */
    export const Link: FC<PropsWithChildren<NextLinkProps>> = ({
      href,
      children,
      ...props
    }) => {
      const { locale } = useLocale();
      const isExternalLink = checkIsExternalLink(href.toString());
    
      // Если ссылка внутренняя и предоставлен допустимый href, получить локализованный URL.
      const hrefI18n: NextLinkProps["href"] =
        href && !isExternalLink ? getLocalizedUrl(href.toString(), locale) : href;
    
      return (
        <NextLink href={hrefI18n} {...props}>
          {children}
        </NextLink>
      );
    };

    Как это работает

    • Определение внешних ссылок:
      Вспомогательная функция checkIsExternalLink определяет, является ли URL внешним. Внешние ссылки остаются неизменными, так как они не нуждаются в локализации.

    • Получение текущей локали:
      Хук useLocale предоставляет текущую локаль (например, fr для французского).

    • Локализация URL:
      Для внутренних ссылок (т.е. не внешних) используется getLocalizedUrl, чтобы автоматически добавлять префикс к URL с текущей локалью. Это означает, что если ваш пользователь находится на французском языке, передача /about в качестве href преобразует его в /fr/about.

    • Возврат ссылки:
      Компонент возвращает элемент <a> с локализованным URL, обеспечивая согласованную навигацию в соответствии с локалью.

    Интегрируя этот компонент Link в вашем приложении, вы поддерживаете согласованный и языково-осведомленный пользовательский опыт, а также улучшаете SEO и удобство использования.

    Настройка TypeScript

    Intlayer использует расширение модулей для получения преимуществ TypeScript и усиления вашего кода.

    alt text

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

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

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

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

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

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

    Дальнейшие шаги

    Чтобы пойти дальше, вы можете внедрить визуальный редактор или вынести ваш контент с использованием CMS.

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

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