Начало работы с интернационализацией (i18n) с Intlayer и Next.js, используя Page Router

    Что такое Intlayer?

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

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

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

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


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

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

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

    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:

    next.config.mjs
    import { withIntlayer } from "next-intlayer/server";/** @type {import('next').NextConfig} */const nextConfig = {  // Ваша существующая конфигурация Next.js};export default withIntlayer(nextConfig);

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

    Шаг 4: Настройка 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).*)",};

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

    Шаг 5: Определение динамических маршрутов локали

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

    1. Создайте страницы для конкретных локалей:

      Переименуйте основной файл страницы, чтобы включить динамический сегмент [locale].

      bash
      mv src/pages/index.tsx src/pages/[locale]/index.tsx
    2. Обновите _app.tsx для обработки локализации:

      Измените ваш _app.tsx, чтобы включить провайдеры Intlayer.

      src/pages/_app.tsx
      import type { FC } from "react";import type { AppProps } from "next/app";import { IntlayerClientProvider } from "next-intlayer";const App = FC<AppProps>({ Component, pageProps }) => {  const { locale } = pageProps;  return (    <IntlayerClientProvider locale={locale}>      <Component {...pageProps} />    </IntlayerClientProvider>  );}export default MyApp;
    3. Настройте getStaticPaths и getStaticProps:

      В вашем [locale]/index.tsx определите пути и свойства для обработки различных локалей.

      src/pages/[locale]/index.tsx
      import type { FC } from "react";import type { GetStaticPaths, GetStaticProps } from "next";import { type Locales, getConfiguration } from "intlayer";

    const HomePage: FC = () =>

    {/ Ваш контент здесь /}
    ;

    export const getStaticPaths: GetStaticPaths = () => { const { internationalization } = getConfiguration(); const { locales } = internationalization; const paths = locales.map((locale) => ({ params: { locale }, })); return { paths, fallback: false }; }; export const getStaticProps: GetStaticProps = ({ params }) => { const locale = params?.locale as string; return { props: { locale, }, }; }; export default HomePage; ``` ```jsx fileName="src/pages/[locale]/index.mjx" codeFormat="esm" import { getConfiguration } from "intlayer"; import { ComponentExample } from "@components/ComponentExample"; const HomePage = () => <div>{/* Ваш контент здесь */}</div>; export const getStaticPaths = () => { const { internationalization } = getConfiguration(); const { locales } = internationalization; const paths = locales.map((locale) => ({ params: { locale }, })); return { paths, fallback: false }; }; export const getStaticProps = ({ params }) => { const locale = params?.locale; return { props: { locale, }, }; }; ``` ```jsx fileName="src/pages/[locale]/index.csx" codeFormat="commonjs" const { getConfiguration } = require("intlayer"); const { ComponentExample } = require("@components/ComponentExample"); const HomePage = () => <div>{/* Ваш контент здесь */}</div>; const getStaticPaths = async () => { const { internationalization } = getConfiguration(); const { locales } = internationalization; const paths = locales.map((locale) => ({ params: { locale }, })); return { paths, fallback: false }; }; const getStaticProps = async ({ params }) => { const locale = params?.locale; return { props: { locale, }, }; }; module.exports = { getStaticProps, getStaticPaths, default: HomePage, }; ```

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

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

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

    src/pages/[locale]/home.content.ts
    import { t, type Dictionary } from "intlayer";const homeContent = {  key: "home",  content: {    title: t({      ru: "Добро пожаловать на мой сайт",      en: "Welcome to My Website",      fr: "Bienvenue sur mon site Web",      es: "Bienvenido a mi sitio web",    }),    description: t({      ru: "Начните с редактирования этой страницы.",      en: "Get started by editing this page.",      fr: "Commencez par éditer cette page.",      es: "Comience por editar esta página.",    }),  },} satisfies Dictionary;export default homeContent;

    Для получения дополнительной информации об объявлении контента обратитесь к руководству по объявлению контента.

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

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

    src/pages/[locale]/index.tsx
    import type { FC } from "react";import { useIntlayer } from "next-intlayer";import { ComponentExample } from "@components/ComponentExample";const HomePage: FC = () => {  const content = useIntlayer("home");  return (    <div>      <h1>{content.title}</h1>      <p>{content.description}</p>      <ComponentExample />      {/* Дополнительные компоненты */}    </div>  );};// ... Остальная часть кода, включая getStaticPaths и getStaticPropsexport default HomePage;
    src/components/ComponentExample.tsx
    import type { FC } from "react";import { useIntlayer } from "next-intlayer";export const ComponentExample: FC = () => {  const content = useIntlayer("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, обратитесь к документации.

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

    Для интернационализации метаданных, таких как заголовки страниц и описания, используйте функцию getStaticProps в сочетании с функцией getTranslation из Intlayer.

    src/pages/[locale]/index.tsx
    import { GetStaticPaths, GetStaticProps } from "next";import { type IConfigLocales, getTranslation, Locales } from "intlayer";import { useIntlayer } from "next-intlayer";interface HomePageProps {  locale: string;  metadata: Metadata;}const HomePage = ({ metadata }: HomePageProps) => {  // Метаданные могут использоваться в head или других компонентах по мере необходимости  return (    <div>      <Head>        <title>{metadata.title}</title>        <meta name="description" content={metadata.description} />      </Head>      {/* Дополнительный контент */}    </div>  );};export const getStaticProps: GetStaticProps = async ({ params }) => {  const locale = params?.locale as string;  const t = <T,>(content: IConfigLocales<T>) => getTranslation(content, locale);  const metadata = {    title: t({      ru: "Мой Вебсайт",      en: "My Website",      fr: "Mon Site Web",      es: "Mi Sitio Web",    }),    description: t({      ru: "Добро пожаловать на мой вебсайт.",      en: "Welcome to my website.",      fr: "Bienvenue sur mon site Web.",      es: "Bienvenido a mi sitio web.",    }),  };  return {    props: {      locale,      metadata,    },  };};export default HomePage;// ... Остальная часть кода, включая getStaticPaths

    (Необязательно) Шаг 9: Изменение языка вашего контента

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

    src/components/LanguageSwitcher.tsx
    import {  Locales,  getHTMLTextDir,  getLocaleName,  getLocalizedUrl,} from "intlayer";import { useLocalePageRouter } from "next-intlayer";import { type FC } from "react";import Link from "next/link";const LocaleSwitcher: FC = () => {  const { locale, pathWithoutLocale, availableLocales, setLocale } =    useLocalePageRouter();  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>  );};

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

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

    (Опционально) Шаг 10: Создание локализованного компонента ссылки

    Чтобы обеспечить соблюдение текущей локали при навигации в вашем приложении, вы можете создать пользовательский компонент 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 { forwardRef, PropsWithChildren, type ForwardedRef } from "react";/** * Утилита для проверки, является ли данный URL внешним. * Если URL начинается с http:// или https://, он считается внешним. */export const checkIsExternalLink = (href?: string): boolean =>  /^https?:///.test(href ?? "");/** * Пользовательский компонент Link, который адаптирует атрибут href в зависимости от текущей локали. * Для внутренних ссылок используется `getLocalizedUrl` для добавления префикса URL с локалью (например, /fr/about). * Это гарантирует, что навигация остается в рамках текущей локали. */export const Link = forwardRef<  HTMLAnchorElement,  PropsWithChildren<NextLinkProps>>(({ href, children, ...props }, ref: ForwardedRef<HTMLAnchorElement>) => {  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} ref={ref} {...props}>      {children}    </NextLink>  );});Link.displayName = "Link";

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

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

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

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

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

    Настройка TypeScript

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

    alt text

    alt text

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

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

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

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

    Добавьте следующие строки в ваш файл .gitignore:

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

    Дополнительные ресурсы

    Следуя этому руководству, вы сможете эффективно интегрировать Intlayer в ваше приложение Next.js, используя Page Router, что обеспечит надежную и масштабируемую поддержку интернационализации для ваших веб-проектов.

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

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

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

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