Вы неправильно используете i18n в Next.js
Интернационализация в JavaScript устоялась вокруг несовершенной практики: вынести каждую пользовательскую строку интерфейса в JSON‑файл, назначить ей ключ и ссылаться на этот ключ из компонентов. t('home.hero.title') вместо самого текста. Ваш UI живёт в одном месте, ваш контент — в другом.
Это работает. Тысячи приложений выпускаются именно так. Но для разработчиков это не лучший опыт.
Чтение t('checkout.summary.total') на код‑ревью ничего не говорит — нужно открывать JSON‑файл, чтобы увидеть, что изменилось. Ключи нужно придумывать, пространственно организовывать и постоянно держать в синхронизации. Накапливаются устаревшие переводы, потому что никто не знает, какие ключи всё ещё используются. Проблема настолько распространена, что вокруг неё возник целый класс инструментов: расширения IDE, которые автодополняют ключи, генераторы типов, которые их валидируют, линтеры, отмечающие неиспользуемые. Эти инструменты решают проблему, которую создала неудачная парадигма.
Компонент <T>
Содержимое не должно отделяться от места, где оно используется. Компонент, который рендерит заголовок, абзац и кнопку, должен быть единственным источником истины для текста этих элементов — а не прокси, указывающим на строки, где‑то отдельно хранящиеся. Когда ваш код и ваши переводы — это две параллельные системы, они со временем расходятся. Неизбежно.
А что, если бы библиотека работала наоборот — подстраивалась под ваш код, а не заставляла вас его переделывать? Вот так и должен выглядеть i18n.
import { T } from 'gt-next';
function Hero() {
return (
<T>
<h1>Ship your product worldwide</h1>
<p>Reach every market without rewriting your app.</p>
</T>
);
}Оборачивайте JSX в <T>. Английский текст остаётся ровно там, где вы его написали. Когда пользователь заходит на сайт с испанской или японской локалью, содержимое внутри <T> переводится — вместе со структурой, форматированием и всем остальным.
Никаких ключей. Никаких JSON-файлов. Никаких кросс-ссылок. Единственный источник истины — ваш код.
Настройка
Приведённый выше синтаксис взят из gt-next, библиотеки i18n с открытым исходным кодом для Next.js App Router. Начать работу можно с одной команды:
npx gtx-cli@latest initМастер настройки устанавливает зависимости, оборачивает вашу конфигурацию Next.js с помощью withGTConfig, добавляет GTProvider в корневой layout, создаёт файл gt.config.json с вашими локалями, настраивает dev API‑ключи для горячего обновления переводов и конфигурирует хранилище переводов в CDN (сети доставки контента) — всё в интерактивном режиме.
После этого оберните контент в <T>, запустите dev‑сервер и используйте компонент <LocaleSelector>, чтобы переключаться между языками:
import { LocaleSelector } from 'gt-next';
function Header() {
return (
<header>
<nav>{/* ... */}</nav>
<LocaleSelector />
</header>
);
}В режиме разработки переводы выполняются по требованию, так что вы можете сразу увидеть своё приложение на любом языке.
Развертывание
В продакшене переводы генерируются заранее.
-
Получите production API key на dash.generaltranslation.com. Production‑ключи начинаются с
gtx-api-(в отличие от локальных ключейgtx-dev-). -
Добавьте шаг translate в сборку:
{
"scripts": {
"build": "npx gtx-cli translate --publish && next build"
}
}Команда translate сканирует вашу кодовую базу, чтобы найти все вхождения <T>, генерирует переводы и публикует их в CDN (сеть доставки контента). Когда ваше приложение собирается, каждая локаль уже готова.
Дальнейшие шаги
- Компоненты переменных — обрабатывайте динамический контент внутри
<T>с помощью<Var>,<Num>и<Currency> - Компоненты ветвления — условно рендерьте контент в зависимости от локали с помощью
<Plural>и<Branch> useGTиgetGT— переводите простые строки для атрибутов, плейсхолдеров и метаданных- Автономный режим — используйте gt-next без платформы General Translation