Существует два способа перевода сайта Next.js. The кодовый маршрут: поместите каждый маршрут под app/[lang] сегмент, определить язык посетителя в proxy.js, загрузите словарь перевода для каждой локали и выведите теги hreflang с помощью alternates поле метаданных. The маршрут без кода: добавьте скрипт службы перевода в корневой макет и позвольте ему переводить отрендеренные страницы, при этом переведенные URL-адреса будут обслуживаться из подкаталога или поддомена для SEO.
Маршрут кода дает вам полный контроль и работает лучше всего, когда текст находится в вашей кодовой базе и разработчик поддерживает его. Маршрут без кода быстрее, когда текст поступает из CMS, когда лицам, не являющимся разработчиками, необходимо редактировать переводы или когда вам нужно одновременно использовать несколько языков. В этом руководстве показаны оба варианта на основе документации Next.js 16, а в конце приводится контрольный список для многоязычного SEO в любом случае.
Ключевые выводы
- Маршрутизатор приложений: вложенные страницы под
app/[lang], перенаправить вproxy.js(называетсяmiddleware.jsперед Next.js 16) загрузите словари JSON в компоненты сервера и выполните предварительную визуализацию локалей с помощьюgenerateStaticParams. - Маршрутизатор страниц: используйте встроенный
i18nконфигурация вnext.config.js. Это не работает сoutput: 'export'. - Next.js не добавляет для вас hreflang. В маршрутизаторе приложений используйте
alternates.languagesвgenerateMetadata. - Вариант без кода — один из них
<Script>тег вapp/layout.tsx. Для индексируемых переведенных страниц используйте настройку подкаталога или поддомена, а не перевод только в браузере. - Какой бы маршрут вы ни выбрали, установите
<html lang>, переводить заголовки и описания и связывать каждую языковую версию с hreflang.
Вариант 1: Интернационализация Next.js с помощью App Router
Это следует из официальное руководство по интернационализации Next.js (версия 16.3 на момент написания статьи).
Шаг 1: Поместите свои маршруты в языковой сегмент
Переместите свои страницы и макеты в app/[lang]/. Затем каждая страница получает локаль в качестве параметра маршрута:
// app/[lang]/page.tsx
export default async function Page({ params }: PageProps<'/[lang]'>) {
const { lang } = await params;
return <h1>{lang}</h1>;
}Маршрутизация может использовать подпуть (/fr/products) или домен (my-site.fr/products). Подпуть проще размещать, и все языки хранятся в одном домене.
Шаг 2: Определите язык и перенаправьте в proxy.js
В Next.js 16 middleware соглашение о файлах было переименовано в proxy (есть кодовый мод: npx @next/codemod@canary middleware-to-proxy .). Прокси-сервер проверяет, есть ли у URL-адреса локаль, и если нет, перенаправляет:
// proxy.js
import { NextResponse } from 'next/server';
const locales = ['en', 'fr', 'de'];
function getLocale(request) {
// Read Accept-Language, e.g. with @formatjs/intl-localematcher and negotiator
return 'en';
}
export function proxy(request) {
const { pathname } = request.nextUrl;
const hasLocale = locales.some((l) => pathname.startsWith(`/${l}/`) || pathname === `/${l}`);
if (hasLocale) return;
request.nextUrl.pathname = `/${getLocale(request)}${pathname}`;
return NextResponse.redirect(request.nextUrl);
}
export const config = {
matcher: ['/((?!_next).*)'],
};Будьте осторожны с автоматическими перенаправлениями. Руководство Google для многоязычные сайты рекомендует предоставить пользователям возможность выбора: “Рассмотреть возможность добавления гиперссылок на другие языковые версии страницы.” Перенаправляйте только в том случае, если URL-адрес не имеет локали, никогда не отходите от локали, запрошенной посетителем (или Googlebot), и всегда показывайте переключатель языка.
Шаг 3: Загрузите словарь для каждого языка
Сохраните один JSON-файл на каждый язык и загрузите его на сервер:
// app/[lang]/dictionaries.ts
import 'server-only';
const dictionaries = {
en: () => import('./dictionaries/en.json').then((m) => m.default),
fr: () => import('./dictionaries/fr.json').then((m) => m.default),
};
export type Locale = keyof typeof dictionaries;
export const hasLocale = (l: string): l is Locale => l in dictionaries;
export const getDictionary = async (l: Locale) => dictionaries[l]();// app/[lang]/page.tsx
import { notFound } from 'next/navigation';
import { getDictionary, hasLocale } from './dictionaries';
export default async function Page({ params }: PageProps<'/[lang]'>) {
const { lang } = await params;
if (!hasLocale(lang)) notFound();
const dict = await getDictionary(lang);
return <button>{dict.products.cart}</button>;
}Поскольку страницы App Router по умолчанию являются компонентами сервера, словари не отправляются в браузер. Документы также описывают next/root-params, что позволяет любому компоненту сервера читать lang не передавая его через реквизит.
Шаг 4: Предварительная визуализация каждого языка и установка атрибута html lang
// app/[lang]/layout.tsx
export async function generateStaticParams() {
return [{ lang: 'en' }, { lang: 'fr' }, { lang: 'de' }];
}
export default async function RootLayout({ children, params }: LayoutProps<'/[lang]'>) {
return (
<html lang={(await params).lang}>
<body>{children}</body>
</html>
);
}Шаг 5: Добавьте теги hreflang и canonical
Next.js не знает, какие страницы являются переводами друг друга, поэтому вы объявляете об этом. С API метаданных, alternates выводит как каноническую, так и hreflang ссылки:
// app/[lang]/pricing/page.tsx
export async function generateMetadata({ params }) {
const { lang } = await params;
return {
alternates: {
canonical: `https://example.com/${lang}/pricing`,
languages: {
en: 'https://example.com/en/pricing',
fr: 'https://example.com/fr/pricing',
de: 'https://example.com/de/pricing',
'x-default': 'https://example.com/en/pricing',
},
},
};
}Каждая языковая версия должна содержать список всех версий, включая ее саму. Наша статья о самоссылающиеся теги hreflang объясняет, почему это важно.
Библиотеки, которые выполняют тяжелую работу
В документации Next.js перечислены несколько библиотек для маршрутизации и трансляции, в том числе next-intl, next-international, next-i18n-router, paraglide-next, lingui и tolgee. Они добавляют множественное число, форматирование чисел и дат, а также типобезопасные ключи сообщений. Если вы хотите создать что-то большее, чем просто небольшой сайт, выбирайте его, а не пишите свой собственный.
Вариант 1б: Маршрутизатор страниц
Если ваш проект все еще использует pages/ каталог, Next.js имеет встроенную маршрутизацию i18n начиная с версии 10. Добавьте локали в next.config.js:
module.exports = {
i18n: {
locales: ['en-US', 'fr', 'de'],
defaultLocale: 'en-US',
},
};Это дает вам /fr/blog и /de/blog автоматически и устанавливает <html lang>. По данным Страницы Руководство по маршрутизатору, два ограничения имеют значение: вы все равно добавляете hreflang самостоятельно (с next/head) и “Интернационализированная маршрутизация не интегрируется с output: 'export'”. Для статического экспорта требуется подход App Router или другая настройка.
Сколько стоит вам кодовый маршрут
Маршрут кода — правильный выбор для пользовательского интерфейса продукта, строки которого находятся в компонентах. Все остальное обходится дорого:
- Контент вне вашего кода (безголовая CMS, ленты продуктов, пользовательский контент) нуждается в собственном конвейере перевода.
- Каждая новая строка перед выпуском необходим ключ и перевод на все языки.
- Неразработчики не могут исправить перевод без запроса на извлечение, если только вы не добавите инструмент управления переводами.
- Больше языков — больше файлов для синхронизации.
Вариант 2: Переведите сайт Next.js, не меняя код
Служба перевода веб-сайтов, такая как ConveyThis, переводит HTML-код, отображаемый вашим приложением Next.js, поэтому вам не придется создавать словари или менять маршруты.
Шаг 1: Создайте учетную запись и добавьте свой домен
Создать учетную запись ConveyThis, добавьте свой домен и выберите исходный и целевой языки.
Шаг 2: Добавьте скрипт в корневой макет
ConveyThis предоставляет вам URL-адрес скрипта с вашим ключом API. В маршрутизаторе приложений добавьте его один раз app/layout.tsx с next/script:
// app/layout.tsx
import Script from 'next/script';
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
{children}
<Script
src="https://cdn.conveythis.com/javascript/conveythis.js?api_key=YOUR_API_KEY"
strategy="afterInteractive"
/>
</body>
</html>
);
}Виджет отслеживает изменения страниц и навигацию на стороне клиента, поэтому контент, который React отображает после первой загрузки, и маршруты, которые изменяются без полной перезагрузки, также транслируются. Он также устанавливает страницу lang атрибут отображаемого языка. Общие шаги такие же, как и в нашем Статья помощи по переводу React и JavaScript руководство по интеграции.
Шаг 3: Выберите SEO-дружественную структуру URL-адресов
Посетителям достаточно скрипта, который меняет текст в браузере, но поисковым системам нужен отдельный URL-адрес для каждого языка, чтобы индексировать его. На панели управления ConveyThis выберите Поддомен (fr.example.com) или Подкаталог (example.com/fr/) в структуре URL. Переведенные страницы затем обслуживаются по собственным URL-адресам с добавлением тегов hreflang. Для специально созданного сайта поддомен обычно является более простым выбором: ваш хост Next.js продолжает обслуживать основной домен, и вы добавляете одну запись CNAME на каждый язык, в то время как подкаталог означает маршрутизацию всего домена через ConveyThis. Оба варианта указаны в бизнес-плане и выше; Справочная статья по поддоменам и подкаталогам объясняет, какие записи DNS нужны каждому. Если ваш домен уже находится на Cloudflare, Настройка Cloudflare Workers (O2O) сохраняет вашу зону на месте.
Шаг 4: Просмотрите и уточните переводы
Машинный перевод позволяет получить полную первую версию за считанные минуты. Затем используйте визуальный редактор, чтобы скорректировать формулировку в контексте, добавить термины бренда в глоссарий и пригласить переводчика или коллегу с командными ролями. Полный список смотрите на страница функций.
Кодовый маршрут или маршрут без кода?
| Маршрутизатор приложений i18n (код) | ConveyThis (без кода) | |
|---|---|---|
| Настраивать | Реструктуризация маршрутов, добавление прокси, словарей, метаданных | Один тег скрипта плюс DNS для URL-адресов SEO |
| Откуда берется текст | Ваши словари | Что бы ни отображала страница, включая контент CMS |
| Кто редактирует переводы | Разработчики (или TMS) | Любой, у кого есть доступ к панели управления |
| хрефланг | Вы добавляете его с помощью alternates | Добавлено с URL-адресами подкаталогов или поддоменов |
| Лучше всего подходит для | Строки пользовательского интерфейса приложения, полный контроль | Маркетинговые сайты, контент CMS, много языков |
| Стоимость | Время разработчика | План, основанный на словах и языках (ценообразование) |
Многие команды объединяют их: кодовый i18n для входа в приложение, сервис перевода для маркетингового сайта и справочный центр.
Многоязычный контрольный список SEO для Next.js
- Один URL-адрес на язык (
/fr/…илиfr.example.com), никогда не используйте один и тот же URL-адрес для каждого языка. <html lang>соответствует языку страницы.- Название и метаописание переведены на каждый язык.
- Канонический текст на каждой переведенной странице указывает на себя, а не на английскую страницу.
- Hreflang перечисляет все языковые версии, включая саму страницу, а также
x-default. - Карта сайта включает URL-адреса всех языков.
- Видимый переключатель языка с реальными ссылками, а не только раскрывающийся список, изменяющий состояние.
- Никаких принудительных перенаправлений с языка, запрошенного посетителем.
Для более широкой картины прочтите наш многоязычный обзор SEO.
ЧАСТО ЗАДАВАЕМЫЕ ВОПРОСЫ
Есть ли в Next.js встроенный перевод? Он имеет встроенную интернационализацию маршрутизация в маршрутизаторе страниц и задокументированных шаблонах i18n для маршрутизатора приложений. Сам переведенный текст взят из ваших словарей, библиотеки или службы перевода.
Что случилось с middleware.js? В Next.js 16 middleware Соглашение о файлах устарело и переименовано в proxy. Логика определения местоположения та же.
Добавляет ли Next.js теги hreflang автоматически? Нет. Использовать alternates.languages в метаданных App Router, или next/head в маршрутизаторе страниц.
Могу ли я перевести статически экспортированный сайт Next.js? Маршрутизация Pages Router i18n не работает с output: 'export', а прокси не поддерживается для статического экспорта. Используйте предварительно сгенерированные [lang] маршруты или служба перевода, которая обслуживает переведенные страницы из своего собственного подкаталога или поддомена.
Хотите, чтобы ваш сайт Next.js был на нескольких языках на этой неделе, а не в следующем квартале? Создать учетную запись ConveyThis, добавьте скрипт в корневой макет и выберите структуру URL, которая соответствует вашему плану SEO.