Начало работы с интернационализацией (i18n) с Intlayer и Next.js 15 App Router
Что такое 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:
npm install intlayer next-intlayer
intlayer
Основной пакет, предоставляющий инструменты интернационализации для управления конфигурацией, переводами, декларацией контента, транспиляцией и CLI-командами.
next-intlayer
Пакет, интегрирующий Intlayer с Next.js. Он предоставляет провайдеры контекста и хуки для интернационализации в Next.js. Кроме того, он включает плагин Next.js для интеграции Intlayer с Webpack или Turbopack, а также middleware для определения предпочтительной локали пользователя, управления cookies и обработки перенаправлений URL.
Шаг 2: Настройте ваш проект
Создайте файл конфигурации для настройки языков вашего приложения:
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:
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 и замените следующим кодом:
import type { PropsWithChildren, FC } from "react";import "./globals.css";const RootLayout: FC<PropsWithChildren> = ({ children }) => children;export default RootLayout;
Оставляя компонент RootLayout пустым, вы можете установить атрибуты lang и dir для тега <html>.
Для реализации динамической маршрутизации предоставьте путь для локали, добавив новый layout в ваш каталог [locale]:
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: Объявите ваш контент
Создайте и управляйте объявлениями контента для хранения переводов:
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.{ts,tsx,js,jsx,mjs,cjs}). Для получения дополнительной информации обратитесь к документации по объявлениям контента.
Шаг 6: Используйте контент в вашем коде
Получите доступ к словарям контента в вашем приложении:
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 { title, 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), что приводит к созданию нового “контекста” для разных сегментов приложения. Размещение провайдера в общем макете нарушит эту изоляцию, предотвращая корректное распространение значений серверного контекста на ваши серверные компоненты.
"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> );};
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 для определения предпочтительной локали пользователя:
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 для перевода ваших метаданных.
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.
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;
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. Эта функция позволяет установить локаль приложения и обновить контент соответствующим образом.
"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:
"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 и усиления вашего кода.
Убедитесь, что ваша конфигурация TypeScript включает автоматически сгенерированные типы.
{ // ... Ваши существующие конфигурации TypeScript "include": [ // ... Ваши существующие конфигурации TypeScript ".intlayer/**/*.ts", // Включить автоматически сгенерированные типы ],}
Конфигурация Git
Рекомендуется игнорировать файлы, сгенерированные Intlayer. Это позволит избежать их добавления в ваш репозиторий Git.
Для этого вы можете добавить следующие инструкции в ваш файл .gitignore:
# Игнорировать файлы, сгенерированные Intlayer.intlayer
Дальнейшие шаги
Чтобы пойти дальше, вы можете внедрить визуальный редактор или вынести ваш контент с использованием CMS.
Если у вас есть идея по улучшению этой документации, не стесняйтесь внести свой вклад, подав запрос на вытягивание на GitHub.
Ссылка на документацию GitHub