Локализуем React (NextJS, TypeScript) сайт на несколько языков с помощью i18next
У меня появилась задача в проекте:
- Перевести личный кабинет пользователя на русский и английский (в перспективе и на другие языки).
- При этом, определять язык пользователя при первом заходе в ЛК и давать его изменить.
- Запоминать выбранный язык при перезагрузке страницы.
- Сделать так, чтобы в проектах была типизация файлов с переводами (чтобы нельзя было забыть добавить один из языков).
Как я это делал — расскажу в статье.
Первоначальное решение
Сначала я решил задачу, просто закинув все переводы в несколько .ts-файлов с общим интерфейсом и выбирая язык через Redux. Всё работало, но было ощущение, что это я переизобрел велосипед.
Хотелось чего-то более стандартного и популярного на рынке: по-любому эту задачу кто-то уже решил более качественно. Да и всё-таки онбординг новых разработчиков никто не отменял. Поэтому было принято решение: выбрать популярную библиотеку и перенести переводы на неё.
Выбор библиотеки переводов
Для решения задачи я выбрал i18next.
Почему именно i18next?
- Имеет поддержку типизации "из коробки" (дружит TS типы и даже кое-как с автокомплишном).
- "Дружит" с React Server Component в Next.js (для Next.js 13+).
- Поддерживает lazy loading (разделение переводов по чанкам/файлам) для ускорения страниц.
- Всё выше делается просто относительной других популярных библиотек.
Глобально, сейчас из этого всего в проекте нужна только типизация. Но закладываю серверные компоненты и разделение кода на будущее. Проект планирует расширять, и эти возможности пригодятся для SEO.
Для наглядности сделал таблицу, где сравнил три самые популярные библиотеки:
*оценка может быть субъективной из расчёта на конкретный проект, на объективность не претендую.
*i18n - расшифровывается как "internationalization".
Минутка самопиара
У меня есть Telegram-канал, где я собираю ссылки на свои статьи про Full-Stack разработку, развитие SaaS-продуктов и управление IT-проектами.
Шаблон проекта
Для примера будем делать одну страницу с переключателем языка и несколькими текстовыми полями:
Для начала создадим новый проект на Next.js с TypeScript шаблоном.
Выполняем команды:
Я сразу добавил ESLint, TailwindCSS и Turbopack:
Появляется структура:
И сразу добавляем библиотеки i18next:
react-i18next — адаптер для React.
i18next-browser-languagedetector — плагин для определения языка в браузере.
Добавляем локализацию
Создаём переводы
Создаём тип переводов и сами переводы в папке i18n:
Разумеется, можно назвать файлы и папки по-другому, главное, чтобы была понятная структура. Я выбрал нейминг, стандартный для Feature Sliced Design (но FSD мы здесь не используем).
Далее сами файлы:
Добавляем типы переводов в проект
Чтобы включить типизацию, нужно воспользоваться встроенным механизмом декларации типов i18next. Создадим файл resources.d.ts (или i18n.d.ts) в корне проекта или в папке types, где пропишем:
Теперь при использовании useTranslation и t в нашем коде TypeScript будет подсказывать, какие ключи перевода у нас существуют.
Инициализируем i18next
Добавим файл i18n.ts в папку /i18n:
Обратите внимание, что i18next-browser-languagedetector смотрит, какой язык установлен в браузере, а также может работать с cookie/localStorage. Это решает задачу "запоминать язык при перезагрузке страницы".
Здесь мы указываем логику, в которой сначала пытаемся брать язык из localStorage, а затем из браузера:
i18next умеет сам выбирать нужный язык в зависимости от настройки браузера (ru, en, sp и другие). Нам нужно только указать нужный файл для языка:
Добавляем выбор языка
Чтобы пользователь мог переключать язык, создадим компонент выбора языка:
Теперь мы можем переиспользовать этот компонент на любой странице или в header'e.
Проброс языка в API
Если у вас локализация распространяется и на API, вам нужно прокидывать язык в запросы. Его можно брать из нашего файла i18n и добавлять в заголовок. Например, в fetch-запросе:
Итоговая страница
Полный пример страницы с переводами выглядит вот так:
Как результат смены языка в LanguageSwitcher будут меняться все надписи, а при перезагрузке страницы сохранится последний выбранный язык:
Конкретно в этом примере мы используем "use client" для упрощения. В следующей статье я покажу, как использовать i18next с SSR'ом.
Заключение
Итого, при смене языка у нас меняются все тексты на странице:
- Заголовок «Личный кабинет» <> «User Dashboard»
- Приветствие «Привет, John!» <> «Hello, John!»
- Кнопка для профиля «Мой профиль» <> «My profile»
А при перезагрузке приложения язык остаётся выбранным, так как i18next-browser-languagedetector сохраняет язык в localStorage'e.
Чтобы добавить новые языки (испанский, китайский и т.д.) нужно расширить ресурс в i18n.ts и добавить новые файлы с переводами (например, es_translation.json, zh_translation.json). Типизация подскажет, не забыли ли мы какие-то поля.
P.S. Напомню, что у меня есть Telegram-канал, где я собираю ссылки на свои статьи про Full-Stack разработку, развитие SaaS-продуктов и управление IT-проектами.
Если остались вопросы, пишите в комментариях!