NPM библиотека для интернационализации и локализации i18n очень популярна, однако за последние годы она сильно разрослась. В ней много возможностей для локализации дат, чисел, плурализации, двунаправленных алфавитов, загрузки локалей с сервера и кучи еще чего. На сайте i18next она называется уже даже «интернационализационным фреймворком«.

в тему
Новый авиалайнер. Входит стюардесса в пассажирский салон:
— Вы находитесь на нашем новом авиалайнере. В носовой части самолета у нас находится кинозал, в хвостовой — зал игровых автоматов, на нижней палубе — бассейн, на верхней — сауна. А теперь, уважаемые господа, пристегните ремни, и мы со всей этой хренотенью попробуем взлететь.
В то же время часто для локализации сайта в большинстве случаев требуются очень простые вещи, занимающие всего пару процентов от всего функционала тяжеловеса i18n.
В частности лично мне обычно нужны:
-
Нахождение перевода по составному ключу —
t("finance.transactions.deposit") -
Перевод с параметром —
t("hello-message", "Вася") -
Массивы для списков или параграфов текста
На примере Vue 3 я покажу как можно избавиться от i18next без потери функционала в данном случае, не только облегчая js бандл, но и сокращая код, при этом с сохранением реактивности (смена языка сайта налету)
Простоту и элегантность нижеописанного рефакторинга обеспечит Vue 3 Composition API, но в целом данная методика должна подойти для любого реактивного фреймворка
Свой i18n
Вот чистая реализация вышеуказанного функционала в 30 строчек супротив полутора мегабайт без каких-либо зависимостей — https://stackblitz.com/edit/i18n-detox?file=src%2FApp.vue
Проект Vue 3 с i18next
Стандартно в проекте с Composition API подключение и использование i18next происходят примерно следующим образом
До
// main.js import { i18n, useI18n } from "@/app/composables/i18n"; const { initI18n } = useI18n(); initI18n(); app.use(i18n); // useI18n.js import { ref } from "vue"; import { api } from "@/services"; import { createI18n } from "vue-i18n"; const locales = [ { code: "en", name: "English", flag: "england", }, { code: "ru", name: "Pусский", flag: "russian", }, ]; const locale = ref(); export const i18n = createI18n({ I18nScope: "global", globalInjection: true, legacy: false, allowComposition: true, fallbackLocale: import.meta.env.VITE_I18N_FALLBACK_LOCALE || "en", formatFallbackMessages: true, }); export function useI18n() { function initI18n() { const lang = localStorage.getItem(import.meta.env.VITE_APP_NAME + "_lang") ?? (import.meta.env.VITE_DEFAULT_LOCALE || "en"); loadLanguage(lang); } async function loadLanguage(lang) { if (i18n.global.locale !== lang) { locale.value = locales.find((l) => l.code === lang); const data = await api.utils.downloadLanguage(lang); i18n.global.setLocaleMessage(lang, data[lang]); i18n.global.locale.value = lang; localStorage.setItem(import.meta.env.VITE_APP_NAME + "_lang", lang); } } return { i18n, locale, locales, initI18n, loadLanguage, }; }; // Использование в компонентах Composition API import { useI18n } from "vue-i18n"; const { t } = useI18n(); t("finance.transactions.deposit"), // Использование в js файлах import { i18n } from "@/app/composables/i18n"; i18n.global.t("finance.transactions.deposit")
Всё, что нужно для избавления от i18next, это задать явно объект messages для хранения всех локалей, и добавить в useI18n() реактивную функцию t(), которая как раз и будет обрабатывать составной ключ, параметр и массив.
После этого можно закомментировать всё использование библиотеки vue-i18n
После
// main.js import { useI18n } from "@/app/composables/i18n"; const { initI18n } = useI18n(); initI18n(); // app.use(i18n); // useI18n.js import { ref } from "vue"; import { api } from "@/services"; // import { createI18n } from "vue-i18n"; const locales = [ { code: "en", name: "English", flag: "england", }, { code: "ru", name: "Pусский", flag: "russian", }, ]; // export const i18n = createI18n({ // I18nScope: "global", // globalInjection: true, // legacy: false, // allowComposition: true, // fallbackLocale: import.meta.env.VITE_I18N_FALLBACK_LOCALE || "en", // formatFallbackMessages: true, // // messages: { en: messages } // }); const locale = ref(); let messages; // Делаем доступ для использования в js модулях export const t = useI18n().t; export function useI18n() { function initI18n() { messages = []; const lang = localStorage.getItem(import.meta.env.VITE_APP_NAME + "_lang") ?? (import.meta.env.VITE_APP_DEFAULT_LOCALE || "en"); loadLanguage(lang); } async function loadLanguage(lang) { if (locale.value !== lang) { const localeMessages = await api.utils.downloadLanguage(lang); messages[lang] = localeMessages[lang]; locale.value = locales.find((l) => l.code === lang); // i18n.global.setLocaleMessage(lang, localeMessages[lang]); // i18n.global.locale.value = lang; localStorage.setItem(import.meta.env.VITE_APP_NAME + "_lang", lang); } } function t(msg, param = null) { let val = msg.split(".").reduce((val, part) => val[part], messages[locale.value.code]); if (param) { val = val.replace("{0}", param); } return val; } return { t, // i18n, locale, locales, initI18n, loadLanguage, }; } // Использование в компонентах Composition API import { useI18n } from "@/app/composables/i18n"; const { t } = useI18n(); t("finance.transactions.deposit"), // ИЛИ import { t } from "@/app/composables/i18n"; t("finance.transactions.deposit") // Использование в js файлах import { t } from "@/app/composables/i18n"; t("finance.transactions.deposit")
В данном примере перевод для конкретной локали грузится с сервера по запросу, но объект messages можно иметь на клиенте сразу
Alias export const t = useI18n().t; позволяет использовать один синтаксис и в компонентах, и в js модулях.
I18next расширения
У I18next есть расширение для `Vue DevTools` (довольно бесполезное), и есть расширение I18next Ally для MS VS Code (весьма полезное). Так вот I18next Ally работает с новой реализацией если в package.json будет прописан пакет vue-i18n в dependencies (в коде подключать его не надо). Рекомендую. Оба расширения, впрочем, неплохо едят ресурсы, так что пользоваться ими лучше по надобности.
Итого
Мы закомментировали больше строк, чем добавили, и JavaScript бандл после билда уменьшился на 50 Кб. Функционал остался. Реактивный.
До (vue 3, vue-router, toaster, vue-i18n)

После (vue 3, vue-router, toaster)

Спасибо, I18next, и до свидания.
Другая моя статья по теме — «Работа с i18n — автоматизация Google Translate и другие полезные советы«
ссылка на оригинал статьи https://habr.com/ru/articles/736530/
Добавить комментарий