Коротко — для SEO лучше всего использовать префиксы в путях (/ru/, /en/) + корректные rel="alternate" (hreflang) и канонические URL. Ниже — почему и как это правильно организовать в Next.js (App Router) с конкретными рекомендациями и примерами кода.
Почему префиксы в путях (recommended)
- Google рекомендует отдельные URL для каждой языковой версии (поддомены, субдиректории или ccTLD). Субдиректории (/en/, /ru/) проще в настройке и управлении.
- Явный URL позволяет ссылкам, индексации и ссылочному весу быть привязанными к конкретной локали.
- Итого: используйте /en/... и /ru/... (а не ?lang= или cookies-only).
Структура папок (App Router)
Простой и гибкий вариант — динамический сегмент [lang], чтобы переиспользовать лэйауты и логику:
app/
[lang]/
layout.tsx
page.tsx // /ru и /en
about/
page.tsx // /ru/about и /en/about
blog/
[slug]/
page.tsx
layout.tsx // глобальный root, можно здесь делать редирект/детект
Пример layout для локали (app/[lang]/layout.tsx):
- ставим атрибут lang на тег html (важно для доступа поисковиков и доступности).
- используем params.lang для выбора текста/локали.
Пример:
export default function LocaleLayout({ children, params }) {
return (
<html lang={params.lang}>
<body>{children}</body>
</html>
);
}
Генерация метаданных (hreflang + canonical)
В App Router можно использовать metadata API (generateMetadata) либо вручную вставлять теги <link rel="alternate"> и <link rel="canonical">. Лучше: в каждой странице/лейауте сгенерировать alternates для всех локалей.
Пример generateMetadata (в app/[lang]/page.tsx или layout):
export async function generateMetadata({ params }) {
const base = 'https://example.com';
const lang = params.lang; // 'ru' или 'en'
return {
alternates: {
canonical: `${base}/${lang}`,
languages: {
ru: `${base}/ru`,
en: `${base}/en`
}
}
};
}
Если вы не используете metadata API, добавьте вручную:
<link rel="alternate" href="https://example.com/ru/page" hreflang="ru" />
<link rel="alternate" href="https://example.com/en/page" hreflang="en" />
<link rel="alternate" href="https://example.com/" hreflang="x-default" /> // по желанию
Sitemap
Генерируйте sitemap.xml (или sitemap index) с URL-ами для всех локалей. Можно также включать hreflang в sitemap. Это улучшает обнаружение версий страницы.
Canonical и duplicate content
- Каждая локализованная страница должна иметь ссылку canonical на свой собственный URL.
- Не дублируйте контент без hreflang/canonical — это может привести к падению SEO.
Redirects / Detect language
- Авто-редиректы с корня (/) на /ru или /en можно сделать, но будьте осторожны: для SEO лучше давать уникальные URL и не полагаться только на Accept-Language.
- Если делаете редирект по заголовку Accept-Language, используйте временный редирект (302/307), чтобы не «закладывать» его в индекс навсегда. Также не редиректите поисковых роботов — лучше показать выбор языка или ссылку на другие версии.
Пример middleware для редиректа корня:
import { NextResponse } from 'next/server';
export function middleware(req) {
const url = req.nextUrl.clone();
if (url.pathname === '/') {
// простая логика — детект по headers или fallback
const accept = req.headers.get('accept-language') || '';
const lang = accept.startsWith('en') ? 'en' : 'ru';
url.pathname = `/${lang}`;
return NextResponse.redirect(url);
}
return NextResponse.next();
}
i18n в next.config.js
Next.js поддерживает встроенную i18n-конфигурацию:
module.exports = {
i18n: {
locales: ['ru', 'en'],
defaultLocale: 'ru',
localeDetection: true
}
}
Учтите: встроенная i18n влияет на поведение маршрутизации и автоматическое добавление префикса для defaultLocale (по умолчанию Next может не добавлять префикс для defaultLocale). Если хотите, чтобы для defaultLocale тоже использовался префикс (/ru), проще сделать структуру вручную через app/[lang] (или дополнительно настроить маршрутизацию).
SEO-практики и чеклист
- Используйте отдельные URL (/ru/, /en/).
- Для каждой страницы добавьте rel="alternate" hreflang для всех локалей + x-default при необходимости.
- Установите canonical на соответствующую локаль.
- Установите атрибут lang в теге <html>.
- Генерируйте sitemap с URL для всех локалей.
- Рендерьте контент на стороне сервера (SSR или SSG) для лучшей индексации.
- Не используйте только cookie/JS-переключение языка без изменения URL.
- По возможности переводите не только текст, но и метаданные (title, description) для каждой локали.
Итого: оптимальная схема — префиксы /ru/ и /en/, корректные hreflang и canonical, lang на html, sitemap со всеми локалями, SSG/SSR и аккуратные редиректы. Если нужно — могу дать готовый минимальный репозиторий/пример проекта с реализацией (app/[lang] структура, middleware, generateMetadata и скрипт генерации sitemap).