# General Translation Knowledge Base: Как добавить i18n в приложение Next.js на Pages Router?
URL: https://generaltranslation.com/ru/kb/add-i18n-to-nextjs-pages-router.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: Добавьте в next.config.js блок i18n с locales и defaultLocale — и Next.js будет отдавать страницы по адресам /es и /fr, перенаправлять посетителей в зависимости от языка браузера и передавать локаль в getStaticProps и getServerSideProps. Рендеринг сообщений берёт на себя библиотека; gt-next от General Translation, кроме того, генерирует переводы из текста, написанного прямо в JSX.
Category: Интернационализация
Last updated: 2026-10-28
Authors: General Translation Team
Published: 2026-10-28

Начиная с Next.js 10, в Pages Router есть встроенная интернационализированная маршрутизация: перечислите локали и локаль по умолчанию в блоке `i18n` файла `next.config.js`, и Next.js будет отдавать каждый язык по собственному URL, перенаправлять новых посетителей в зависимости от языка браузера и передавать активную локаль в ваши функции получения данных. При этом Next.js не загружает, не рендерит и не переводит текст: маршрутизация изначально задумана для работы в связке с библиотекой интернационализации, поэтому вам всё равно нужно выбрать библиотеку для сообщений и способ получения переводов ([интернационализация в Next.js Pages Router](https://nextjs.org/docs/pages/guides/internationalization)).

## Что делает встроенная маршрутизация i18n

Вся эта функциональность настраивается одним блоком конфигурации:

```js title="next.config.js"
/** @type {import('next').NextConfig} */
module.exports = {
  i18n: {
    // Все локали, доступные на сайте, включая локаль по умолчанию
    locales: ['en', 'es', 'fr'],
    // Открывается без префикса: /about, /es/about, /fr/about
    defaultLocale: 'en',
  },
};
```

После такой настройки Next.js берёт на себя четыре задачи:

* **URL-адреса.** При маршрутизации по подпутям локаль по умолчанию остаётся на `/about`, а остальные локали получают префикс (`/fr/about`). Если вместо этого нужно обслуживать локали с отдельных доменов, например `example.fr`, добавьте список `domains`, указав в каждой записи `domain` и `defaultLocale`.
* **Определение локали.** При переходе на `/` Next.js считывает заголовок `Accept-Language` и выполняет перенаправление на подходящий префикс или домен. Cookie-файл `NEXT_LOCALE`, который устанавливает ваш переключатель языка, имеет приоритет над заголовком. Чтобы отключить перенаправление, задайте `localeDetection: false`.
* **Локаль в коде.** `useRouter()` возвращает `locale`, `locales` и `defaultLocale`. `getStaticProps` и `getServerSideProps` получают `locale` в своём контексте, а `getStaticPaths` — `locales`.
* **`<html lang>`.** Next.js подставляет в этот атрибут активную локаль, если только ваш `_document` не задаёт собственное значение.

Этого достаточно, чтобы реализовать i18n вручную. Эта страница загружает по одному JSON-файлу на каждую локаль во время сборки:

```tsx title="pages/index.tsx"
import type { GetStaticProps, InferGetStaticPropsType } from 'next';
import Link from 'next/link';
import { useRouter } from 'next/router';

export const getStaticProps = (async ({ locale }) => {
  // Next.js вызывает эту функцию один раз для каждой настроенной локали во время сборки.
  const messages = (await import(`../messages/${locale}.json`)).default;
  return { props: { messages } };
}) satisfies GetStaticProps;

export default function Home({ messages }: InferGetStaticPropsType<typeof getStaticProps>) {
  const { locales } = useRouter(); // ['en', 'es', 'fr']

  return (
    <main>
      <h1>{messages.title}</h1>
      <nav>
        {locales?.map((code) => (
          // Та же страница на каждом языке: /, /es, /fr
          <Link key={code} href="/" locale={code} lang={code}>
            {new Intl.DisplayNames([code], { type: 'language' }).of(code)}
          </Link>
        ))}
      </nav>
    </main>
  );
}
```

Сборка для рабочей среды отдаёт `/es` как `<html lang="es">` с заголовком на испанском, а на запрос к `/` с `Accept-Language: fr-CA` возвращается перенаправление 307 на `/fr`. В ссылках каждый язык назван на нём самом (English, español, français), как и положено [переключателю языка](/kb/language-switcher).

### Что остаётся на вас

Маршрутизация ограничивается URL. Формы множественного числа, форматирование чисел и дат, интерполяция строк — всё это задача библиотеки, как и синхронизация файлов сообщений при изменении компонентов. Кроме того, Next.js не добавляет ссылки `hreflang` между языковыми версиями — их нужно добавить самостоятельно с помощью `next/head`. (См. [Что такое hreflang?](/kb/what-is-hreflang).) И, наконец, ничто из этого не создаёт сам перевод.

При планировании стоит учитывать три ограничения:

* **Нет статического экспорта.** Интернационализированная маршрутизация не работает с `output: 'export'`, поскольку опирается на слой маршрутизации Next.js.
* **Время сборки растёт с каждой локалью.** Каждая нединамическая страница с `getStaticProps` собирается отдельно для каждой локали, поэтому 50 локалей и 10 страниц дают 500 вызовов. Динамические маршруты должны возвращать каждую локаль из `getStaticPaths`, иначе Next.js соберёт только локаль по умолчанию.
* **Лимиты.** `locales` и `domains` могут содержать не более 100 записей каждый.

### В App Router она не работает

Конфигурация `i18n` — это возможность Pages Router. В App Router `useRouter` из `next/navigation` уже не возвращает `locale`, `locales` и `defaultLocale`, а в документации Next.js сказано, что встроенные функции i18n в директории `app` не нужны ([руководство по миграции](https://nextjs.org/docs/app/guides/migrating/app-router-migration#step-5-migrating-routing-hooks)). Вместо этого в приложениях на App Router локаль задаётся в сегменте `[locale]` или разрешается в промежуточном ПО. (См. [Как добавить i18n в приложение Next.js на App Router?](/kb/add-i18n-to-nextjs))

## Библиотеки для рендеринга сообщений

| Библиотека                                                                 | Настройка в Pages Router                                                                                                                                             | Как пишется текст                                                                                                                 | Откуда берутся переводы                                                                                             |
| -------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| [next-i18next](https://github.com/i18next/next-i18next#pages-router-setup) | `appWithTranslation` оборачивает `_app`; `serverSideTranslations(locale, ['common'])` из `next-i18next/pages` вызывается в `getStaticProps` или `getServerSideProps` | Ключи в пространствах имён JSON, рендеринг через `t('title')`                                                                     | Вы, переводчики или система управления переводами (TMS), например Locize                                            |
| [next-intl](https://next-intl.dev/docs/getting-started/pages-router)       | `NextIntlClientProvider` в `_app`, `messages` возвращаются из `getStaticProps`                                                                                       | Ключи в отдельном JSON-файле для каждой локали, рендеринг через [`useTranslations`](/docs/react/reference/hooks/use-translations) | Вы или ваши переводчики                                                                                             |
| [react-intl](https://formatjs.github.io/docs/react-intl)                   | `IntlProvider` в `_app`, которому передаются активная локаль и её сообщения                                                                                          | Встроенные ICU-сообщения в `defaultMessage`, извлекаются с помощью `formatjs extract`                                             | Вы, переводчики или TMS                                                                                             |
| General Translation (`gt-next`)                                            | `withGTServerSideProps` или `withGTStaticProps` на каждой странице; [`GTProvider`](/docs/react/reference/components/gt-provider) в `_app`                            | JSX, обёрнутый в [`<T>`](/docs/react/reference/components/t), строки через [`useGT`](/docs/react/reference/hooks/use-gt)          | Генерируются командой [`npx gt translate`](/docs/cli/reference/commands/translate), проверяются в панели управления |

next-i18next 16 поддерживает оба роутера и сохраняет привычный API для Pages Router в `next-i18next/pages`. Документация next-intl рекомендует App Router, но Pages Router по-прежнему поддерживается полностью. В Pages Router обе библиотеки работают в связке с описанным выше блоком `i18n`, который отвечает за URL и определение локали.

Выбирайте next-i18next, если у вас уже есть пространства имён i18next или вам нужна его экосистема плагинов. Выбирайте next-intl, если планируете перейти на App Router и хотите использовать один и тот же API до и после миграции. Выбирайте react-intl, если ваши сообщения — это строки ICU, с которыми уже работают переводчики. Выбирайте General Translation, если предпочитаете держать текст прямо в компонентах, а переводы — генерировать, проверять и обновлять автоматически по мере изменения кода. (Подробное сравнение см. в статье [next-intl vs. gt-next](/kb/next-intl-vs-gt-next).)

Если вы остаётесь на next-i18next или next-intl, CLI General Translation всё равно может переводить их JSON-файлы, включая строки ICU, без всякой миграции ([перевод JSON-файлов](/docs/cli/reference/formats/json-files)).

## Добавление General Translation в приложение на Pages Router

Эта настройка добавляет в приложение на Pages Router испанский и французский языки, причём каждый язык доступен по собственному URL. Она протестирована на приложении, созданном с помощью `create-next-app` 16.4.3 (`--no-app`), с Next.js 16.4.3, `gt-next@11.4.16` и `gt@2.26.11`. `gt-next` поддерживает Next.js 13.0.0 и новее, за исключением версий 15.2.1 и 15.2.2. Точка входа `gt-next/server` работает только с App Router.

### 1. Установите библиотеку и CLI

```bash
npm install gt-next@11.4.16
npm install --save-dev gt@2.26.11
```

### 2. Укажите языки

```json title="gt.config.json"
{
  "defaultLocale": "en",
  "locales": ["en", "es", "fr"],
  "files": {
    "gt": {
      "output": "public/_gt/[locale].json"
    }
  }
}
```

Не удаляйте `en` из `locales`: этот же список передаётся в блок `i18n` Next.js, где обязательно должна быть указана локаль по умолчанию.

### 3. Включите маршрутизацию i18n в Next.js

```ts title="next.config.ts"
import type { NextConfig } from 'next';
import { withGTConfig } from 'gt-next/config';
import gtConfig from './gt.config.json';

const nextConfig: NextConfig = {
  i18n: {
    locales: gtConfig.locales,
    defaultLocale: gtConfig.defaultLocale,
  },
};

export default withGTConfig(nextConfig);
```

Next.js сам выполняет описанные выше маршрутизацию и определение локали. Промежуточное ПО `gt-next` и папка `pages/[locale]` не нужны.

`create-next-app` прописывает `<Html lang="en">` в `pages/_document.tsx`, и это переопределяет локаль: в тестовом приложении страница `/es` возвращалась с `lang="en"`, хотя текст был на испанском. Удалите этот проп, чтобы Next.js устанавливал значение сам:

```tsx title="pages/_document.tsx"
import { Html, Head, Main, NextScript } from 'next/document';

export default function Document() {
  return (
    <Html>
      <Head />
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}
```

### 4. Загрузите переводы из локальных файлов

```ts title="loadTranslations.ts"
export default async function loadTranslations(locale: string) {
  try {
    const translations = await import(`./public/_gt/${locale}.json`);
    return translations.default;
  } catch {
    // Пока файлов нет, рендерится исходный текст
    return {};
  }
}
```

`withGTConfig` находит `loadTranslations.ts` в корневом каталоге проекта или в `src/`. Переводы входят в сборку, поэтому во время выполнения приложение не обращается к General Translation. (См. [хранение переводов](/docs/react/guides/storing-translations).)

### 5. Передайте локаль и переводы на каждую страницу

Оберните функцию получения данных на каждой странице. Обёртка считывает `context.locale` из Next.js и добавляет `locale` и `translations` в пропсы страницы:

```tsx title="pages/about.tsx"
import { T, withGTStaticProps } from 'gt-next';

// Next.js собирает эту страницу отдельно для каждой локали.
export const getStaticProps = withGTStaticProps();

export default function About() {
  return (
    <T>
      <h1>About us</h1>
    </T>
  );
}
```

Точно так же используйте `withGTServerSideProps()` на страницах, отрисованных на сервере. Обе функции принимают в качестве аргумента вашу собственную функцию получения данных, например `withGTStaticProps(async (context) => ({ props: { posts } }))`, и передают результат `redirect` или `notFound` без изменений. Оберните каждую страницу, которая рендерит переведённый контент.

Затем передайте эти пропсы в [`GTProvider`](/docs/react/reference/components/gt-provider) в `_app`:

```tsx title="pages/_app.tsx"
import type { AppProps } from 'next/app';
import Router from 'next/router';
import { GTProvider, type WithGTServerSideProps } from 'gt-next';

export default function App({ Component, pageProps }: AppProps<WithGTServerSideProps>) {
  const { locale, translations } = pageProps;

  return (
    <GTProvider
      locale={locale}
      translations={translations}
      // Переключение языков через навигацию Next.js: /es, /fr и обратно на /
      _reload={({ locale: nextLocale }) => {
        void Router.push(Router.pathname, Router.asPath, { locale: nextLocale });
      }}
    >
      <Component {...pageProps} />
    </GTProvider>
  );
}
```

### 6. Отметьте контент для перевода

```tsx title="pages/index.tsx"
import { LocaleSelector, T, useGT, withGTServerSideProps } from 'gt-next';

export const getServerSideProps = withGTServerSideProps();

export default function Home() {
  const gt = useGT();

  return (
    <main>
      <LocaleSelector />
      <T>
        <h1>Find your next project</h1>
        <p>Explore projects from teams around the world.</p>
      </T>
      <input type="search" placeholder={gt('Search projects')} aria-label={gt('Search projects')} />
    </main>
  );
}
```

[`<T>`](/docs/react/reference/components/t) переводит заголовок и абзац целиком, используя их разметку как контекст. [`useGT`](/docs/react/reference/hooks/use-gt) обрабатывает строки в атрибутах. [`<LocaleSelector>`](/docs/react/reference/components/locale-selector) выводит список ваших локалей, называя каждую на её собственном языке. При выборе Español сохраняется `NEXT_LOCALE=es`: Next.js считывает это значение при следующем посещении `/`, а функция обратного вызова `_reload` перенаправляет страницу на `/es`.

### 7. Создайте переводы и проверьте сборку

Войдите в систему с помощью [`npx gt login`](/docs/cli/reference/commands/login), задайте `GT_PROJECT_ID` и запустите [`npx gt translate`](/docs/cli/reference/commands/translate) — команда создаст файлы `public/_gt/es.json` и `fr.json`. Чтобы опробовать настройку без учётной записи, используйте [`npx gt generate`](/docs/cli/reference/commands/generate): эта команда создаёт те же файлы с исходным текстом на английском, который можно перевести вручную.

В тестовом приложении после заполнения испанского файла `next build` завершился успешно, а `next start` отдавал:

* `/` — как `<html lang="en">` с английским заголовком
* `/es` — как `<html lang="es">` с испанским заголовком и плейсхолдером
* `/es/about`, собранную с помощью `withGTStaticProps`, — на испанском
* `/` с `Accept-Language: es` — как перенаправление 307 на `/es`

## Выпуск и актуализация переводов

Запускайте перевод перед каждой сборкой, чтобы новые тексты сразу выходили на всех языках:

```json title="package.json"
{
  "scripts": {
    "build": "gt validate && gt translate && next build"
  }
}
```

В настройках хостинг-провайдера задайте `GT_PROJECT_ID` и ключ API проекта в переменной `GT_API_KEY` с правами **Files &gt; Write** и **Translation queue &gt; Enabled**. Используйте ключ только на серверной стороне и никогда не добавляйте к нему префикс `NEXT_PUBLIC_`. Если сборка каждый раз заново генерирует `public/_gt/`, добавьте этот каталог в `.gitignore`. [`gt validate`](/docs/cli/reference/commands/validate) прерывает сборку ещё до обращений к API, если контент может сломать перевод, а при последующих запусках переводится только новый или изменённый исходный текст.

Рецензенты могут исправить любой перевод в [Редакторе переводов](/docs/platform/dashboard/guides/reviewing-translations) General Translation, а [контекстная группа](/docs/platform/dashboard/guides/defining-context-for-translations) обеспечивает единообразие терминов Glossary и тона во всех ваших проектах. Чтобы языковые версии индексировались, добавьте `hreflang`, канонические URL и переведённые заголовки — подробнее о каждом из них см. в статье [многоязычное SEO для Next.js](/kb/multilingual-nextjs-seo).

## Частые вопросы

### Работает ли опция i18n из next.config.js в App Router?

Нет. Это возможность Pages Router: приложения на App Router используют сегмент маршрута `[locale]` или промежуточное ПО, а для `gt-next` в документации описана отдельная настройка под App Router.

### Можно ли использовать getStaticProps с gt-next?

Да. Оберните её в `withGTStaticProps`, и Next.js сгенерирует отдельную версию страницы для каждой локали. Для динамических маршрутов `getStaticPaths` по-прежнему должна возвращать путь для каждой локали.

### Нужно ли промежуточное ПО для i18n в Pages Router?

Нет. Next.js разрешает локаль по URL, cookie-файлу `NEXT_LOCALE` и заголовку `Accept-Language` ещё до выполнения ваших функций получения данных.

## Основные выводы

* Блок `i18n` в `next.config.js` добавляет в Pages Router URL-адреса с локалью, перенаправления по `Accept-Language` с `/`, cookie-файл `NEXT_LOCALE`, а также `locale` в `useRouter()` и функциях получения данных.
* Он не выполняет рендеринг и перевод сообщений, не добавляет `hreflang`, не работает с `output: 'export'` и не распространяется на App Router.
* Удалите жёстко заданный `lang="en"` из `pages/_document.tsx`, иначе HTML для каждой локали будет объявлять английский язык.
* next-i18next, next-intl и react-intl выполняют рендеринг переводов, которые предоставляете вы; `gt-next` от General Translation ещё и генерирует их — с помощью `withGTStaticProps` или `withGTServerSideProps` на каждой странице и [`GTProvider`](/docs/react/reference/components/gt-provider) в `_app`.
* Добавьте [`gt validate && gt translate`](/docs/cli/reference/commands/validate) перед `next build`, чтобы каждый релиз выходил сразу на всех языках.

## Related questions

- [Как добавить i18n в приложение Next.js на App Router?](https://generaltranslation.com/ru/kb/add-i18n-to-nextjs.mdx): Начните с gt init, чтобы настроить General Translation в поддерживаемом проекте на App Router, затем разметьте исходное содержимое, создайте переводы и проверьте переключение языка. gt-next хранит тексты прямо в JSX вместе с контекстом перевода, а CLI, редактор в панели управления и Locadex берут на себя генерацию, проверку и последующие обновления. В этом руководстве также показана явная настройка с локальными файлами.
- [Альтернативы next-intl: gt-next или next-intl](https://generaltranslation.com/ru/kb/next-intl-vs-gt-next.mdx): next-intl рендерит переводы, которые вы предоставляете в каталоге сообщений для каждого языка. gt-next тоже рендерит переводы, а General Translation, кроме того, генерирует их из текста, написанного прямо в вашем JSX. Обе библиотеки распространяются по лицензии MIT и поддерживают App Router и серверные компоненты. Если ваши каталоги next-intl уже работают, их можно оставить и переводить с помощью General Translation без миграции.
- [Как настроить SEO для многоязычного приложения Next.js?](https://generaltranslation.com/ru/kb/multilingual-nextjs-seo.mdx): Выделите каждому языку собственный URL с префиксом, например /en/about и /es/about, а затем укажите для каждого опубликованного языка html lang, канонический URL, ссылающийся на саму страницу, альтернативные версии hreflang с x-default, переведённые заголовок и описание, а также запись в карте сайта. Неопубликованные версии пометьте noindex. gt-next от General Translation направляет каждый язык на собственный URL и переводит заголовки с помощью getGT.
- [Переключатель языка: лучшие практики UX, SEO и доступности](https://generaltranslation.com/ru/kb/language-switcher.mdx): Называйте каждый язык на нём самом, например Deutsch или 日本語, и не используйте флаги — они обозначают страны. Каждый вариант должен вести на ту же страницу на выбранном языке через индексируемую ссылку. Запоминайте выбор в cookie-файле и никогда не перенаправляйте автоматически с локализованного URL.
- [Альтернативы i18next: General Translation или i18next](https://generaltranslation.com/ru/kb/general-translation-vs-i18next.mdx): Выбирайте General Translation, если хотите хранить тексты прямо в React-компонентах и создавать переводы с учётом их JSX-структуры и контекста продукта. Можно также оставить i18next и переводить существующие каталоги с помощью General Translation. i18next отвечает за рендеринг сообщений и выбор форм множественного числа, а сами переводы предоставляет отдельный сервис.

## Previous and next questions

- Previous question: [Как добавить i18n в TanStack Start?](https://generaltranslation.com/ru/kb/add-i18n-to-tanstack-start.mdx)
- Next question: [Как локализовать Android-приложение с помощью strings.xml?](https://generaltranslation.com/ru/kb/localize-android-app.mdx)

## Sitemap

See the [Knowledge Base sitemap](https://generaltranslation.com/ru/kb/sitemap.md) for every article.
