Как правильно настроить и реализовать поддержку нескольких языков в проекте на Next.js 14?

Привет. Нужно сделать мультиязычный сайт по образцу <a href="https://prismatest.com/ru" rel="nofollow">https://prismatest.com/ru</a>. Тематика у нас схожая, поэтому заказчик привёл именно этот сайт как пример и хочет такой же — мне интересно, как у него реализована мультиязычность.
  • 5 апреля 2026 г. 23:00
Ответы на вопрос 1
Ниже — практическое руководство, как грамотно реализовать мультиязычность в проекте на Next.js (App Router, Next.js 13/14-подобная структура). Я опишу варианты, структуру, код-образцы и рекомендации (SEO, переключатель языков, детекции локали и загрузки словарей). Оно даёт то, что нужно, чтобы сделать сайт вида https://prismatest.com/ru (подпапка для каждой локали).

Ключевые решения, которые нужно принять заранее
- Структура URL: subpath (example.com/ru/...), subdomain (ru.example.com) или отдельные домены. Для примера используем subpath (/ru), как на prismatest.
- Статическая отдача (SSG) или динамика (SSR/SSG смешанная). Для маркетингового сайта чаще подходит SSG + ISR.
- Где хранятся переводы: JSON-файлы, CMS/translation service (Lokalise, Phrase), или headless CMS — влияет на загрузку словарей.
- Будете ли локализовать пути (slug'и) и мета-теги — это влияет на реализацию роутинга.

1) Базовая конфигурация (next.config.js)
Добавим поддержку локалей в next.config.js — благодаря этому Next автоматически поддерживает маршруты вида /ru, а компоненты Link умеют переключать локаль.

Пример:
module.exports = {
  i18n: {
    locales: ['en', 'ru'],
    defaultLocale: 'en',
    localeDetection: false // или true, если хотите автоматическую детекцию
  }
};

2) Структура приложения (App Router)
Рекомендуемый подход — вложенный сегмент маршрута для локали: app/[locale]/...

Пример структуры:
app/
  [locale]/
    layout.tsx
    page.tsx
    about/
      page.tsx
    products/
      [slug]/page.tsx
  layout.tsx        // корневой layout (часто переносим туда head общие вещи)

Используем params.locale внутри компонентов (server components) чтобы подгрузить словари для данной локали.

3) Загрузка словарей и провайдер
Пример server layout (app/[locale]/layout.tsx) — подгружаем JSON словарь асинхронно на сервере и передаём в провайдер (пример с собственной простейшей i18n-обёрткой; можно использовать библиотеки, см. ниже):

// app/[locale]/layout.tsx (TypeScript-образец)
import React from 'react';
import { notFound } from 'next/navigation';
import I18nProvider from '../../lib/I18nProvider'; // ваша реализация или библиотечный провайдер

export default async function LocaleLayout({ children, params }: { children: React.ReactNode; params: { locale: string } }) {
  const locale = params.locale;
  const supported = ['en', 'ru'];
  if (!supported.includes(locale)) notFound();

  // загружаем словарь SSG-friendly: import JSON на сервере
  const messages = await import(`../../../locales/${locale}.json`).then(mod => mod.default);

  return (
    <html lang={locale}>
      <body>
        <I18nProvider locale={locale} messages={messages}>
          {children}
        </I18nProvider>
      </body>
    </html>
  );
}

Файлы локалей:
locales/en.json
locales/ru.json

4) generateStaticParams для SSG
Чтобы Next заранее построил страницы для всех локалей, экспортируйте generateStaticParams в соответствующих route-ах:

// app/[locale]/page.tsx
export async function generateStaticParams() {
  return [{ locale: 'en' }, { locale: 'ru' }];
}

5) Детекция локали и редиректы (Middleware)
Если хотите, чтобы example.com -> автоматически редиректило на /ru или /en в зависимости от Accept-Language или cookie, используйте middleware:

// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

const PUBLIC_FILE = /\.(.*)$/;
const SUPPORTED = ['en', 'ru'];
const DEFAULT = 'en';

export function middleware(req: NextRequest) {
  const { pathname } = req.nextUrl;

  // пропускаем статику, API и уже локализованные пути
  if (PUBLIC_FILE.test(pathname) || pathname.startsWith('/api') || pathname.startsWith('/_next')) {
    return;
  }
  const segments = pathname.split('/');
  if (segments[1] && SUPPORTED.includes(segments[1])) {
    return; // уже с локалью
  }

  // можно взять куку, или Accept-Language
  const cookieLocale = req.cookies.get('NEXT_LOCALE')?.value;
  const acceptLang = req.headers.get('accept-language');
  const detected = cookieLocale || (acceptLang?.startsWith('ru') ? 'ru' : null) || DEFAULT;

  const url = req.nextUrl.clone();
  url.pathname = `/${detected}${pathname}`;
  return NextResponse.redirect(url);
}

6) Переключатель языков и ссылки
Если используете встроенную i18n (next.config.js), компонент Link умеет переключать локаль. В App Router можно вручную строить ссылку с нужной локалью:

- Серверный способ (простое решение): генерируйте <a href={`/${locale}${currentPath}`}>.
- С next/link: <Link href={href} locale={locale}>... </Link> (если Link поддерживает locale в вашей версии).

Важно: при переключении сохранить тот же внутренний маршрут (например /ru/products/slug), т.е. переводить slug если у вас локализованные slug'и.

7) Локализация роутов (translated slugs)
Есть 2 подхода:
- Не переводить slug: одинаковые слуги везде; проще.
- Переводить slug: нужно хранить mapping slug'ов на каждой локали и генерировать статические страницы для каждой комбинации. Например:
  app/[locale]/products/[slug]/page.tsx
  generateStaticParams возвращает все пары { locale, slug } (переводы slug-ов в источнике данных).

8) SEO: hreflang, canonical, sitemaps
- Генерируйте <link rel="alternate" hreflang="ru" href="https://example.com/ru/..." /> и для каждой локали, а также x-default.
- Указывайте html lang в документе.
- Локализованные sitemaps (или единый sitemap с перечислением локализованных URL) — важно.
- Canonical корректно: canonical должен указывать на страницу в текущей локали (или на основной локализованный URL), не на /.

Пример генерации hreflang (серверный компонент в head области): перебрать locales и current path и вывести ссылки для каждой локали.

9) Библиотеки и рекомендации
- next-intl — хорош для App Router, поддерживает server components, загрузку namespaces, перевод форматирования дат/чисел.
- i18next (react-i18next) — мощный, но требует настройки + CSR часть.
- next-i18next — популярно но преимущественно для Pages Router (есть хаки для App Router).
- next-translate — упрощает file-based словари, но нужно проверять совместимость с App Router.

Для нового App Router я рекомендую next-intl или собственную лёгкую обёртку для простых сайтов (JSON-словари), потому что loading на сервере прост и эффективен (импорт JSON в серверных компонентах).

10) Работа с динамическими данными (CMS/API)
Если текст контента приходит из CMS (например, статьи), храните переводы в CMS (переводные поля) и запрашивайте данные с фильтром по locale. При SSG генерите страницы для каждой локали.

11) Кеширование и производительность
- Загрузку словаря желательно делать на сервере и кешировать (import JSON + не делать лишних fetch'ей).
- CDN-кеширование: помните, что страницы для разных локалей — разные URL (хорошо для CDN).
- Если используете Edge Middleware и Accept-Language — не ломайте CDN кеш (редиректы могут быть кешируемыми).

12) Контентная стратегия и перевод
- Разделяйте UI-переводы (кнопки, лейблы) и контентные переводы (статьи).
- Подключите TMS (Lokalise, Phrase) для удобной работы переводчиков.
- Поддерживайте fallback: если строка отсутствует в locale, используйте defaultLocale строку или логирование.

13) Пример мини-архитектуры (шаги реализации)
1. Добавить i18n в next.config.js.
2. Создать структуру app/[locale]/... и layout который подгружает messages.
3. Разместить JSON-файлы перевода в /locales.
4. Сделать generateStaticParams для SSG всех локалей.
5. Реализовать middleware для редиректа с корня и/или детекции локали.
6. Добавить переключатель языка (с установкой cookie NEXT_LOCALE при смене).
7. Сгенерировать hreflang и sitemap.
8. Тестировать: SEO-браузеры, редиректы, fallback-и.

14) Пример простого переключателя (серверный + клиентский cookie)
Кнопка переключения вызывает API (или просто редирект на /ru/...), и ставит cookie 'NEXT_LOCALE' — чтобы middleware мог запомнить выбор.

15) Особенности как у prismatest.com
- Присмотритесь к тому, что они используют: /ru/ — subpath. Значит используется i18n subpath routing.
- Возможно, они подгружают контент из CMS с локализацией и отдельными URL для каждой локали.
- Часто такие сайты используют статическую генерацию (SSG) для публичных страниц + переводы в JSON / CMS.

Короткая сводка рекомендаций
- Use next.config.js i18n + app/[locale] structure for clarity.
- Load translations on the server (import JSON), use provider to expose t() to components.
- Use middleware for root redirect / locale detection if needed.
- Implement generateStaticParams to pre-render locales.
- Add hreflang and localized sitemap for SEO.
- Choose a library: next-intl for App Router if you want ready-made features.

Если нужно — могу:
- Прислать готовый репозиторий-скелет (app + locales + middleware + переключатель).
- Показать пример с next-intl (настройка, usage в server/client components).
- Подсказать, как локализовать динамические slug'и и генерировать страницы для них.

Какая часть нужна в первую очередь — скаффолд (репо-скелет) или пример с next-intl?
Похожие вопросы