Больше никаких common.json: Как я написал свой CLI для автоматизации i18next в Next.js (и перешел с Gulp на AST)

Введение
Каждый, кто делал мультиязычные приложения на React/Next.js, знает эту боль. Ты создаешь новый компонент, пишешь t("Submit Button"), а потом...
Идешь в
locales/en/common.json.Добавляешь ключ вручную.
Идешь в
locales/ru/common.json.Добавляешь перевод.
Повторяешь для казахского языка.
А если ты хочешь разбить переводы по файлам (Namespaces)? Тогда начинается ад с импортами и путаницей в путях.
Я устал от этого. Два года я решал эту проблему с помощью самописных Gulp-скриптов. Но в 2025 году я решил: хватит. Пора делать по-взрослому.
Я переписал всё на TypeScript, использовал AST-парсинг и создал smart-i18next-cli — инструмент, который делает всю грязную работу за меня.
В этой статье расскажу, как я хакнул парсер i18next, подружил его с TFunction и заставил DeepL переводить мой проект одной командой.
Проблема 1: Ад с Неймспейсами
В больших проектах файл common.json разрастается до тысяч строк. Работать с ним невозможно.
Я хотел, чтобы структура JSON-файлов один-в-один повторяла структуру моих компонентов.
Файл:
src/features/auth/Login.tsxНужен JSON:
locales/en/features.auth.Login.json
Решение:
Стандартный i18next-cli (и старый scanner) требует явно писать useTranslation('ns'). Я же хотел, чтобы CLI сам понимал, в каком файле мы находимся.
Я написал плагин, который во время парсинга смотрит на путь файла и автоматически "привязывает" найденные ключи к нужному неймспейсу.
Проблема 2: Типизация и "Слепые" Парсеры
В TypeScript мы часто передаем функцию перевода как проп:
TypeScript
export const schema = (t: TFunction<"features.auth">) => { ... }
Проблема в том, что ни один парсер (ни i18next-scanner, ни новый i18next-cli) не видит ключи внутри таких конструкций, потому что там нет явного вызова useTranslation.
Моё решение (The "Smart" Hack):
Я не стал писать свой сложный AST-анализатор с нуля. Вместо этого я сделал "Inject" на лету.
Мой CLI читает файл, и если находит TFunction<"my.ns">, он виртуально (в памяти, не меняя файл на диске) добавляет туда код:
TypeScript
// In-memory injection
const { t } = useTranslation("my.ns");
Парсер i18next-cli "проглатывает" этот код и — вуаля! — ключи и неймспейсы определяются корректно. Это позволило добиться 100% точности экстракции без магии в рантайме.
Проблема 3: Лень переводить (Killer Feature)
Когда я добавляю новый ключ, я хочу видеть его в интерфейсе сразу, хотя бы на английском. А еще лучше — сразу на всех языках.
Я написал команду translate. Она работает так:
Сканирует все JSON-файлы.
Ищет ключи, где
key === value(мой признак "непереведенного" ключа).Отправляет их в RapidAPI (DeepL).
Сохраняет результат.
Теперь я пишу код, запускаю pnpm i18n:translate, и через секунду у меня готова казахская и русская локализация.
Итог: Архитектура v1.0.0
В итоге родился @sayyyat/smart-i18next-cli.
Это Open Source инструмент, который работает как обертка (wrapper) над официальным i18next-cli.
Ядро: Официальный
i18next-cli(для надежного парсинга).Плагин: Моя логика для 1-to-1 неймспейсинга и инъекций кода.
CLI: Commander.js для удобных команд
init,sync,translate.
Теперь мой workflow выглядит так:
Пишу код:
t("Hello World").Сохраняю.
CLI сам создает файл
app.page.json.CLI сам генерирует строгие типы TypeScript.
Если вам тоже надоело ручное управление JSON-ами в Next.js — попробуйте.
🔗 GitHub
📦 NPM