
Полное руководство по интернационализации Next.js
Настройте next-intl с App Router, сконфигурируйте маршрутизацию локалей и автоматизируйте перевод с помощью ИИ.
Установить next-intl
next-intl — единый пакет, который обрабатывает маршрутизацию локалей, загрузку сообщений и хуки перевода для App Router в Next.js.
npm install next-intlСоздать конфигурацию запросов i18n
Создайте два файла: src/i18n/request.ts для загрузки сообщений и src/i18n/routing.ts для определения локалей. Они задают, как next-intl разрешает сообщения и маршруты.
// src/i18n/routing.ts
import { defineRouting } from 'next-intl/routing';
import { createNavigation } from 'next-intl/navigation';
export const routing = defineRouting({
locales: ['en', 'de', 'ja', 'es'],
defaultLocale: 'en',
localePrefix: 'as-needed', // /about for en, /de/about for de
});
export const { Link, redirect, usePathname, useRouter } =
createNavigation(routing);Настроить промежуточное ПО
Добавьте middleware.ts для определения локали, перезаписи URL и перенаправлений. Промежуточное ПО перехватывает каждый запрос и обеспечивает применение правильной локали.
// middleware.ts <- Must be in project ROOT, not src/
import createMiddleware from 'next-intl/middleware';
import { routing } from './src/i18n/routing';
export default createMiddleware(routing);
export const config = {
matcher: ['/((?!api|_next|.*\\..*).*)'],
};Настроить структуру папки [locale]
Переместите маршруты приложения внутрь app/[locale]/. Добавьте generateStaticParams, чтобы во время сборки создавать страницы для каждой локали. В результате получится структура URL /en/about, /de/about и так далее.
// app/[locale]/layout.tsx
import { routing } from '@/i18n/routing';
export function generateStaticParams() {
return routing.locales.map((locale) => ({ locale }));
}Обновить корневой макет
Загрузите сообщения с помощью getMessages() и передайте их в NextIntlClientProvider в корневом макете локали. Задайте атрибут lang элемента html из параметра locale.
// app/[locale]/layout.tsx
import { NextIntlClientProvider } from 'next-intl';
import { getMessages, setRequestLocale } from 'next-intl/server';
import { routing } from '@/i18n/routing';
import { notFound } from 'next/navigation';
export function generateStaticParams() {
return routing.locales.map((locale) => ({ locale }));
}
export default async function LocaleLayout({
children,
params,
}: {
children: React.ReactNode;
params: { locale: string };
}) {
const { locale } = await params;
if (!routing.locales.includes(locale as any)) notFound();
setRequestLocale(locale);
const messages = await getMessages();
return (
<html lang={locale}>
<body>
<NextIntlClientProvider locale={locale} messages={messages}>
{children}
</NextIntlClientProvider>
</body>
</html>
);
}Использовать переводы в компонентах
Серверные компоненты используют асинхронную функцию getTranslations с await. Клиентские компоненты используют хук useTranslations. Выбирайте в зависимости от места отрисовки компонента: серверные компоненты полностью исключают переводы из пакета JavaScript.
// Server Component (default)
import { getTranslations, setRequestLocale } from 'next-intl/server';
export default async function AboutPage({
params,
}: { params: { locale: string } }) {
const { locale } = await params;
setRequestLocale(locale);
const t = await getTranslations('AboutPage');
return <h1>{t('title')}</h1>;
}
// Client Component ('use client')
'use client';
import { useTranslations } from 'next-intl';
export default function SearchBar() {
const t = useTranslations('SearchBar');
return <input placeholder={t('placeholder')} />;
}Добавить SEO: метаданные и hreflang
Используйте generateMetadata для создания заголовков и описаний страниц для каждой локали. Добавьте alternates.languages для тегов hreflang, чтобы поисковые системы находили все языковые версии каждой страницы.
// app/[locale]/layout.tsx or any page.tsx
import { getTranslations } from 'next-intl/server';
import { routing } from '@/i18n/routing';
export async function generateMetadata({
params,
}: { params: { locale: string } }) {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: 'Metadata' });
return {
title: t('title'),
description: t('description'),
alternates: {
languages: Object.fromEntries(
routing.locales.map((l) => [l, `/${l}`])
),
},
};
}Обработать страницы ошибок и отсутствующих ресурсов
error.tsx и not-found.tsx требуют специальной обработки, поскольку могут отрисовываться вне обычного макета локали. Корневому not-found.tsx нужна собственная настройка поставщика i18n для отображения локализованных сообщений об ошибках.
// app/[locale]/error.tsx
'use client';
import { useTranslations } from 'next-intl';
export default function Error() {
const t = useTranslations('Error');
return (
<div>
<h1>{t('title')}</h1>
<p>{t('description')}</p>
</div>
);
}
// app/not-found.tsx (root level -- needs own provider)
import { routing } from '@/i18n/routing';
export default async function GlobalNotFound() {
return (
<html lang={routing.defaultLocale}>
<body>
<h1>404 - Page Not Found</h1>
</body>
</html>
);
}Автоматизировать перевод
После завершения настройки i18n переводите файлы сообщений с помощью ИИ прямо из IDE или используйте CLI i18n Agent в конвейере CI/CD для автоматического перевода при каждом развёртывании.
# In your IDE, ask your AI assistant:
> Translate messages/en.json to German, Japanese, and Spanish
✓ messages/de.json created (1.1s)
✓ messages/ja.json created (1.4s)
✓ messages/es.json created (1.0s)Автоматизировать контроль качества перевода
Инструменты с открытым исходным кодом для i18n Next.js
Эти пакеты с открытым исходным кодом решают распространённые проблемы в процессах интернационализации Next.js.
next-intl-localechain
Стандартный next-intl при отсутствии перевода сразу переходит на локаль по умолчанию. Пользователь бразильского португальского видит английский вместо подходящего перевода pt-PT. next-intl-localechain добавляет умные цепочки резервных локалей: глубоко объединяет переводы из родственных локалей, чтобы региональные пользователи всегда видели ближайший доступный перевод.
import { getRequestConfig } from 'next-intl/server';
import { withLocaleChain } from 'next-intl-localechain';
export default getRequestConfig(withLocaleChain({
loadMessages: (locale) =>
import(`../../messages/${locale}.json`).then(m => m.default),
defaultLocale: 'en'
}));@i18n-agent/cli
Инструмент командной строки для перевода файлов сообщений Next.js без выхода из терминала. Переводите файлы напрямую, проверяйте состояние заданий и скачивайте результаты. Работает в конвейерах CI/CD с аутентификацией по ключу API для полностью автоматизированной локализации.
# Install the CLI
npm install -g @i18n-agent/cli
# Authenticate
i18nagent login
# Translate your message files
i18nagent translate ./messages/en.json --lang de,ja,es
# Or use in CI/CD with an API key
export I18N_AGENT_API_KEY=your-key-here
i18nagent translate ./messages/en.json --lang de,ja,esРаспространённые ошибки
"Unable to find next-intl locale"
Промежуточное ПО не сопоставило запрос. Проверьте: находится ли middleware.ts в корне проекта? Правильно ли шаблон matcher исключает статические файлы? Включена ли локаль в конфигурацию маршрутизации?
Неожиданная динамическая отрисовка
В странице или макете отсутствует setRequestLocale(locale). Без него next-intl определяет локаль по заголовкам или cookie, что принудительно включает динамическую отрисовку и препятствует статическому созданию.
Параллельные маршруты нарушаются при использовании i18n
У параллельных маршрутов (@modal) и перехватывающих маршрутов ((.)photo) есть известные проблемы совместимости с динамическим сегментом [locale]. Для таких сложных шаблонов используйте маршрутизацию на основе промежуточного ПО в качестве обходного решения.
При переключении языка теряется текущий маршрут
При переключении локалей сохраните текущий pathname с помощью usePathname() и замените только сегмент локали. Будьте внимательны с параметрами динамического маршрута: их необходимо повторно разрешить для новой локали.
Рекомендуемая структура файлов
my-nextjs-app/
├── middleware.ts # Locale routing (project root!)
├── next.config.mjs
├── messages/
│ ├── en.json # Source messages
│ ├── de.json
│ └── ja.json
├── src/
│ ├── i18n/
│ │ ├── request.ts # Message loading config
│ │ └── routing.ts # Locale definitions
│ └── app/
│ └── [locale]/
│ ├── layout.tsx # Root locale layout
│ ├── page.tsx # Home page
│ ├── error.tsx # Localized error page
│ ├── not-found.tsx # Localized 404
│ └── about/
│ └── page.tsx
└── package.jsonМожно также перевести:
Попробовать i18n Agent
Перетащите сюда файл перевода
JSON, YAML, PO, XML, CSV, Markdown, Properties
или нажмите, чтобы выбрать
Целевые языки
Резервные локали с next-intl-localechain
Когда в региональной локали, например pt-BR, отсутствует ключ перевода, next-intl сразу переходит на локаль по умолчанию, не проверяя сначала родительскую локаль pt.
npm install next-intl-localechainimport { withLocaleChain } from 'next-intl-localechain';
export default withLocaleChain({
fallbacks: {
'pt-BR': ['pt', 'en'],
'zh-Hant-HK': ['zh-Hant', 'zh', 'en'],
},
defaultLocale: 'en',
loadMessages: (locale) => import(`./messages/${locale}.json`),
});Полный список поддерживаемых фреймворков и 75 встроенных цепочек приведён в нашем руководстве по резервным локалям. Learn more →