Nuxt I18n Micro
Nuxt I18n Micro

Всем привет!

Я занимаюсь разработкой на Nuxt с самого его появления, и у меня возникла очень большая проблема с реализацией i18n. Давайте по порядку.

Чем плох модуль nuxtjs/i18n:

  • значительно возрастает время сборки

  • большой вес бандла

  • огромный роутинг на крупных проектах

  • медленная работа

Пытаясь хоть как-то решить эти проблемы, я начал создавать пулреквесты в оригинальный модуль. К сожалению, приняли только один, остальные просто повисли. Автор пытается интегрировать какие-то странные решения, которые только замедляют работу фреймворка. Например, версия 9 немного увеличила скорость сборки, но при этом ещё больше замедлила rps.

Я долго думал и решил попробовать разработать какой-то аналог, учитывая, что я потратил большое количество времени на изучение логики работы i18n. Задача не казалась такой сложной, и первый прототип был готов всего за пару недель.

Модуль я назвал Nuxt I18n Micro. Идея была очень простой: по максимуму использовать стандартный функционал и отказаться от большинства сторонних зависимостей. Конечно, всё оказалось чуть сложнее, чем я планировал изначально, и уже сейчас модуль даже превосходит по функционалу оригинал. Также я вообще не использовал логику и код nuxtjs/i18n.

Теперь немного о логике получения и отдачи текста переводов. Модуль при первом заходе на страницу загружает глобальный и локальный (для текущей страницы) текст. Далее он его кеширует на стороне сервера и отдаёт клиенту. При повторном заходе на страницу перевод берётся из кеша. Это позволило немного ускорить работу модуля и интегрировать его, например, с Cloudflare Pages.

Как все это работает

Модуль состоит из нескольких частей: ядро, которое отвечает за управление роутингом, несколько плагинов, один из которых нужен для управления переводами и получения JSON, а также серверный middleware, который отвечает за формирование и отдачу JSON.

Модуль сильно отличается по логике работы и формированию текста переводов. Я полностью отказался от загрузки переводов, весь контент получаю при сборке и кеширую его. Это позволило немного увеличить производительность фреймворка.

Также я отказался от vue-i18n. К сожалению, он принёс мне только проблемы. Одна из них — новый JIT-компилятор, который превращает JSON в JS, чтобы ускорить поиск значений в больших файлах. Однако, так как я изначально заложил постраничную архитектуру переводов, это принесло только проблемы.

Folder Structure
Folder Structure

Тесты

Теперь я хочу показать результаты своей работы.

Build Time and Resource Consumption
Build Time and Resource Consumption
Server Performance Under Load​
Server Performance Under Load​

После создания прототипа я решил проверить, а какой вообще был смысл в моей работе, и написал скрипт для тестирования производительности и сборки. Я создал два идентичных проекта. Логика очень проста: тест во время работы собирает проект и отправляет запросы на разные страницы. В фоне собирается информация по потреблению ресурсов системы. Все тесты я проводил на своём рабочем устройстве, то есть условия были идентичны. Логику тестирования можно посмотреть здесь, а результаты и более подробное описание — здесь.

Также я написал большое количество тестов. Учитывая сложность задачи, я вообще не представляю разработку без тестирования, для этого я использовал Playwright.

Отличия от nuxtjs/i18n

Я полностью отказался от стратегии no_prefix. Вообще не вижу в ней перспектив на Nuxt. Несколько раз меня просили реализовать что-то похожее, и мне удалось через hash сделать упрощённую версию, но саму стратегию не планирую добавлять.

В моём модуле есть несколько Vue-компонентов, позволяющих протестировать или упростить разработку, таких как переключение локали, группы переводов, компонент для вывода ссылки и вывода текста перевода, который покрывает 99% задач.

Также я написал консольную утилиту, которая позволяет работать с переводами:

  • Искать пропущенные переводы

  • Импортировать/экспортировать переводы в разные форматы

  • Смотреть статистику по переводам

  • Переводить текст через разные сервисы (Google, Yandex, GPT и прочее)

  • Синхронизировать переводы

  • И многое другое

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

Также, из-за того что я отказался от vue-i18n, я смог вернуть функционал для получения объекта с переводами, от которого отказались при переходе на Nuxt 3, и мне его очень не хватало.

Я старался полностью сохранить структуру методов и событий для простого перехода на мой модуль.

Также все вопросы, которые мне присылают в issue, я собираю в разделе FAQ

Ссылки:

  1. Документация

  2. Github

  3. Лицензия

  4. changelog

Комментарии (2)


  1. danilovmy
    02.12.2024 13:21

    Спасибо, полезная штука. В тексте вероятно ошибка:

    Чем плох модуль nuxtjs/i18n:

    • значительно возрастает скорость сборки

    Вероятно, имеется в виду время сборки.

    Закачивать сразу все языки это беда. У нас 32 языка и около 16 тысяч статических переводов. Даже json одного языка со всеми переводами грузится долго. В итоге начал подгружать только нужные.

    Жесткий роутинг на языках создаёт большую таблицу - роутов. Потому бывает проще в больших приложениях префикс убрать. Потому все не однозначно.

    Не понял в статье - как собрать разметку всех статических переводов в проекте в json?


    1. s00d Автор
      02.12.2024 13:21

      Вероятно, имеется в виду время сборки.

      Спасибо, поправил, но тут не только время сборки, также время запуска в dev режиме тоже значительно лучше.

      Закачивать сразу все языки это беда. У нас 32 языка и около 16 тысяч статических переводов. Даже json одного языка со всеми переводами грузится долго. В итоге начал подгружать только нужные.

      Это была основная причина, по которой я решил заняться этим проектом. В тестах представлены примеры проектов с моим модулем и с nuxt-i18n, и разница действительно огромная. Вы можете сами убедиться в этом, проведя проверку. Я устал ждать и решил попробовать другой подход — и, похоже, что-то получилось.

      Жесткий роутинг на языках создаёт большую таблицу - роутов. Потому бывает проще в больших приложениях префикс убрать. Потому все не однозначно.

      Здесь начинается самое интересное. Я выбрал совершенно иной подход к формированию роутов — regexp-ротинг. Вместо создания отдельного роута для каждой локали, добавляется всего один дополнительный роут. Это означает, что если у вас есть 10 страниц и 30 локалей, итоговое количество роутов составит всего 20, а не 300, как это было бы в стандартной реализации.

      Но это еще не всё. К сожалению, сам Vue Router не отличается высокой скоростью. По умолчанию мой модуль добавляет каждую локаль в регулярное выражение, что снижает RPS (запросов в секунду). Чтобы решить эту проблему, я добавил настройку customRegexMatcher, которая позволяет полностью контролировать процесс обработки регулярных выражений и избавляться от лишних нагрузок. К сожалению с кастомными роутами проблема осталась, я пытался ее как-то решить, но пока не смог.

      Этот подход значительно оптимизирует работу приложения и особенно полезен для крупных проектов.

      Не понял в статье - как собрать разметку всех статических переводов в проекте в json?

      Не совсем понял, что вы имеете в виду под "разметкой статических переводов". Если речь идет просто о тексте, то такого нет. Мой CLI позволяет искать ссылки на переводы и формировать из них JSON. Также, например, он умеет синхронизировать несколько JSON-файлов на разных локалях между собой. Если я правильно понял, идея с поиском текста на странице действительно интересная, но её реализация довольно сложная.