Поскольку Vue.js — это доступная, производительная и универсальная платформа для создания пользовательских веб-интерфейсов, для нее также требуется лучшее в своем классе решение для интернационализации.
Возможно, вы знаете vue-i18n, но для тех, кто уже знает i18next адаптированная для Vue.js версия i18next была бы более подходящей.
В этом уроке мы будем использовать модуль i18next-vue.
Итак, прежде всего: «Почему i18next?»
Что касается локализации React. Одним из самых популярных является i18next с его расширением Vue i18next-vue, и на то есть веские причины:
i18next был создан в конце 2011 года. Он старше, чем большинство библиотек, которые вы будете использовать в настоящее время, включая вашу основную технологию интерфейса (React, Angular, Vue,…).
➡️ экологичный
Исходя из того, как долго i18next уже доступен с открытым исходным кодом, не существует реального дела i18n, которое нельзя было бы решить с помощью i18next.
➡️ зрелый
i18next можно использовать в любой среде javascript (и некоторых не-javascript — .net, elm, iOS, android, ruby, …), с любой структурой пользовательского интерфейса, с любым форматом i18n, … возможности бесконечный.
➡️ расширяемый
С i18next вы получите множество функций и возможностей по сравнению с другими обычными платформами 18n.
➡️ богатый
Здесь вы можете найти больше информации о том, почему i18next особенный и как он работает.
Давайте углубимся в это…
Предпосылки
Убедитесь, что у вас установлены Node.js и npm. Лучше всего, если у вас есть некоторый опыт работы с простым HTML, JavaScript и базовым Vue.js, прежде чем переходить к i18next-vue.
Начиная
Возьмите свой собственный проект Vue или создайте новый, новый, т.е. с помощью команды vue create cli.
npx @vue/cli create vue-starter-project
Мы собираемся адаптировать приложение для определения языка в соответствии с предпочтениями пользователя.
И мы создадим переключатель языков, чтобы контент переключался между разными языками.
Давайте установим некоторые зависимости i18next:
npm install i18next i18next-vue i18next-browser-languagedetector
Подготовим файл i18n.js
:
import i18next from 'i18next'
import I18NextVue from 'i18next-vue'
import LanguageDetector from 'i18next-browser-languagedetector'
i18next
// detect user language
// learn more: https://github.com/i18next/i18next-browser-languageDetector
.use(LanguageDetector)
// init i18next
// for all options read: https://www.i18next.com/overview/configuration-options
.init({
debug: true,
fallbackLng: 'en',
resources: {
en: {
translation: {
// here we will place our translations...
}
}
}
});
export default function (app) {
app.use(I18NextVue, { i18next })
return app
}
Давайте импортируем этот файл в наш файл main.js
:
import { createApp } from 'vue'
import i18n from './i18n'
import App from './App.vue'
i18n(createApp(App)).mount('#app')
Теперь давайте попробуем перенести какой-нибудь жестко закодированный текст в переводы.
Для первого текста мы просто используем простую клавишу welcome
для прямого вызова функции $t
. $t
более или менее совпадает с i18next.t
.
Для второго текста мы будем использовать директиву v-html
для прямого вывода реального HTML.
Предупреждение системы безопасности
Динамическое отображение произвольного HTML-кода на вашем веб-сайте может быть очень опасным, поскольку может легко привести к XSS-уязвимостям. Используйте v-html только для надежного контента и никогда для контента, предоставленного пользователями.
<template>
<div class="hello">
<h1>{{ $t('welcome') }}</h1>
<p v-html="$t('descr')"></p>
</div>
</template>
<script>
export default {
name: 'TranslationShowCase'
}
</script>
Тексты теперь являются частью ресурсов перевода:
import i18next from 'i18next'
import I18NextVue from 'i18next-vue'
import LanguageDetector from 'i18next-browser-languagedetector'
i18next
// detect user language
// learn more: https://github.com/i18next/i18next-browser-languageDetector
.use(LanguageDetector)
// init i18next
// for all options read: https://www.i18next.com/overview/configuration-options
.init({
debug: true,
fallbackLng: 'en',
resources: {
en: {
translation: {
welcome: 'Welcome to Your Vue.js App',
descr: 'For a guide and recipes on how to configure / customize '
+ 'this project,<br>check out the '
+ '<a href="https://cli.vuejs.org" target="_blank" '
+ 'rel="noopener">vue-cli documentation</a>.'
}
}
}
});
export default function (app) {
app.use(I18NextVue, { i18next })
return app
}
Переключатель языков
Теперь давайте определим переключатель языка:
<template>
<div class="hello">
<h1>{{ $t('welcome') }}</h1>
<p v-html="$t('descr')"></p>
<hr />
<div>
<div v-if="languages">
<span v-for="(lng, index) in Object.keys(languages)" :key="lng">
<a v-if="$i18next.resolvedLanguage !== lng" v-on:click="$i18next.changeLanguage(lng)">
{{ languages[lng].nativeName }}
</a>
<strong v-if="$i18next.resolvedLanguage === lng">
{{ languages[lng].nativeName }}
</strong>
<span v-if="index < (Object.keys(languages).length - 1)"> | </span>
</span>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'TranslationShowCase',
data () {
return {
languages: {
en: { nativeName: 'English' },
de: { nativeName: 'Deutsch' }
}
}
}
}
</script>
А также добавить несколько переводов для нового языка:
import i18next from 'i18next'
import I18NextVue from 'i18next-vue'
import LanguageDetector from 'i18next-browser-languagedetector'
i18next
// detect user language
// learn more: https://github.com/i18next/i18next-browser-languageDetector
.use(LanguageDetector)
// init i18next
// for all options read: https://www.i18next.com/overview/configuration-options
.init({
debug: true,
fallbackLng: 'en',
resources: {
en: {
translation: {
welcome: 'Welcome to Your Vue.js App',
descr: 'For a guide and recipes on how to configure / customize '
+ 'this project,<br>check out the '
+ '<a href="https://cli.vuejs.org" target="_blank" '
+ 'rel="noopener">vue-cli documentation</a>.'
}
},
de: {
translation: {
welcome: 'Willkommen zu Deiner Vue.js App',
descr: 'Eine Anleitung und Rezepte zum Konfigurieren/Anpassen '
+ 'dieses Projekts findest du<br>in der '
+ '<a href="https://cli.vuejs.org" target="_blank" '
+ 'rel="noopener">vue-cli-Dokumentation</a>.'
}
}
}
});
export default function (app) {
app.use(I18NextVue, { i18next })
return app
}
🥳 Отлично, вы только что создали свой первый переключатель языков!
Благодаря i18next-browser-languagedetector теперь он пытается определить язык браузера и автоматически использовать этот язык, если вы предоставили для него переводы. Язык, выбранный вручную в переключателе языков, сохраняется в локальном хранилище, и при следующем посещении страницы этот язык будет использоваться в качестве предпочтительного.
Как получить текущий язык?
Начиная с i18next v21 существует i18next.resolvedLanguage
.
Он установлен на текущий разрешенный язык и может использоваться в качестве основного используемого языка, например, в переключателе языков.
Если ваш обнаруженный язык, например, en-US
, и вы предоставили переводы только для en
(fallbackLng) вместо i18next.resolvedLanguage
будет возвращено en
.
i18next.language, i18next.languages и i18next.resolvedLanguage
/* language */ i18next.language; // Is set to the current detected or set language.
/* language */ i18next.languages; // Is set to an array of language codes that will be used to look up the translation value. // When the language is set, this array is populated with the new language codes. // Unless overridden, this array is populated with less-specific versions of that code for fallback purposes, followed by the list of fallback languages
// initialize with fallback languages i18next.init({ fallbackLng: ["es", "fr", "en-US", "dev"] }); // change the language i18next.changeLanguage("en-US-xx"); // new language and its more generic forms, followed by fallbacks i18next.languages; // ["en-US-xx", "en-US", "en", "es", "fr", "dev"] // change the language again i18next.changeLanguage("de-DE"); // previous language is not retained i18next.languages; // ["de-DE", "de", "es", "fr", "en-US", "dev"]
/* resolvedLanguage */ i18next.resolvedLanguage; // Is set to the current resolved language. // It can be used as primary used language, // for example in a language switcher.
Интерполяция и плюрализация
i18next не ограничивается только стандартными функциями i18n.
Но наверняка он способен обрабатывать множественное число и интерполяцию.
Если вам интересно посмотреть, как это работает, взгляните на этот раздел в той другой записи в блоге.
Форматирование
Также возможно форматирование.
Если вам интересно посмотреть, как это работает, взгляните на этот раздел в той другой записи в блоге.
Контекст
Как насчет конкретного приветственного сообщения, основанного на текущем дневном времени? то есть утро, вечер и т. д.
Это возможно благодаря контекстной функции i18next.
Если вам интересно посмотреть, как это работает, взгляните на этот раздел в той другой записи в блоге.
Отделить переводы от кода
Наличие переводов в нашем файле i18n.js
работает, но не совсем подходит для работы с переводчиками.
Давайте отделим переводы от кода и поместим их в специальные файлы json.
Поскольку это веб-приложение, i18next-http-backend поможет нам в этом.
npm install i18next-http-backend
Переместите переводы в общую папку:
Адаптируйте файл i18n.js
для использования i18next-http-backend
:
import i18next from 'i18next' import I18NextVue from 'i18next-vue' import LanguageDetector from 'i18next-browser-languagedetector' import Backend from 'i18next-http-backend'
i18next // i18next-http-backend // loads translations from your server // https://github.com/i18next/i18next-http-backend .use(Backend) // detect user language // learn more: https://github.com/i18next/i18next-browser-languageDetector .use(LanguageDetector) // init i18next // for all options read: https://www.i18next.com/overview/configuration-options .init({ debug: true, fallbackLng: 'en' });
export default function (app) { app.use(I18NextVue, { i18next }) return app }
Теперь переводы загружаются асинхронно.
Если у вас медленное подключение к сети, вы можете заметить, что до загрузки переводов отображаются только ключи i18n.
Чтобы предотвратить это, мы используем новую функциональность Приостановка Vue.js.
Сначала давайте адаптируем файл i18n.js
, экспортировав обещание инициализации i18next:
import i18next from 'i18next' import I18NextVue from 'i18next-vue' import LanguageDetector from 'i18next-browser-languagedetector' import Backend from 'i18next-http-backend'
export const i18nextPromise = i18next // i18next-http-backend // loads translations from your server // https://github.com/i18next/i18next-http-backend .use(Backend) // detect user language // learn more: https://github.com/i18next/i18next-browser-languageDetector .use(LanguageDetector) // init i18next // for all options read: https://www.i18next.com/overview/configuration-options .init({ debug: true, fallbackLng: 'en' });
export default function (app) { app.use(I18NextVue, { i18next }) return app }
…и используйте это обещание в App.vue
:
<template> <img alt="Vue logo" src="./assets/logo.png"> <TranslationShowCase /> </template>
<script> import TranslationShowCase from './components/TranslationShowCase.vue' import { i18nextPromise } from './i18n.js'
export default { name: 'App', components: { TranslationShowCase }, // used in combination with Suspense. // useful when translations are not in-memory... async setup() { await i18nextPromise return {} } } </script>
Давайте создадим новый файл: например, Suspenser.vue
:
<template> <Suspense> <template #default> <App /> </template> <template #fallback> <div> <img alt="Vue logo" src="./assets/logo.png"> <h1>Loading...</h1> </div> </template> </Suspense> </template>
<script> import App from './App.vue'
export default { name: 'Suspenser', components: { App } } </script>
И используйте это в своем файле main.js
:
import { createApp } from 'vue' import i18n from './i18n' import App from './Suspenser.vue'
i18n(createApp(App)).mount('#app')
Теперь, пока ваши переводы загружены, вы увидите резервный шаблон:
Теперь ваше приложение выглядит так же, но ваши переводы разделены.
Если вы хотите поддерживать новый язык, вы просто создаете новую папку и новый JSON-файл перевода.
Это дает вам возможность отправить переводы некоторым переводчикам.
Или, если вы работаете с системой управления переводами, вы можете просто синхронизировать файлы с cli.
Лучшее управление переводами
Отправляя переводы некоторым переводчикам или бюро переводов, вы получаете больше контроля и прямой контакт с ними. Но это также означает, что у вас будет больше работы.
Это традиционный способ. Но имейте в виду, что отправка файлов всегда создает накладные расходы.
Есть ли вариант получше?
Конечно!
i18next помогает перевести приложение, и это здорово, но это еще не все.
- Как вы интегрируете какие-либо переводческие услуги/агентства?
- Как вы отслеживаете новый или удаленный контент?
- Как вы справляетесь с правильным управлением версиями?
- Как вы развертываете изменения перевода без развертывания всего приложения?
- и многое другое…
Ищу что-то подобное❓
- Легко интегрируется
- Непрерывное развертывание? Постоянная локализация!
- Управляйте файлами перевода с легкостью
- Заказать профессиональные переводы
- Аналитика и статистика
- Прибыль от нашей сети доставки контента (CDN)
- Версионирование ваших переводов
- Автоматический и машинный перевод по требованию
- Без риска: возьмите свои данные с собой
- Прозрачное и честное ценообразование
- и многое другое…
Как это выглядит?
Сначала нужно зарегистрироваться в locize и войти.
Затем создать новый проект в locize и добавить свои переводы. Вы можете добавить свои переводы либо с помощью cli, либо импортируя отдельные файлы json, либо через API.
Сделав это, мы заменим i18next-http-backend на i18next-locize-backend.
npm install i18next-locize-backend
После импорта переводов для локализации удалите папку locales.
Адаптируйте файл i18n.js
для использования i18next-locize-backend
и убедитесь, что вы скопировали идентификатор проекта и ключ API из вашего проекта locize:
import I18NextVue from 'i18next-vue' import i18next from 'i18next' import Backend from 'i18next-locize-backend' import LanguageDetector from 'i18next-browser-languagedetector'
const locizeOptions = { projectId: '94c21299-0cf5-4ad3-92eb-91f36fc3f20f', apiKey: 'bc8586d9-fceb-489c-86ac-2985393ed955', // YOU should not expose your apps API key to production!!! version: 'latest' }
export const i18nextPromise = i18next // i18next-locize-backend // loads translations from your project, saves new keys to it (saveMissing: true) // https://github.com/locize/i18next-locize-backend .use(Backend) // detect user language // learn more: https://github.com/i18next/i18next-browser-languageDetector .use(LanguageDetector) // init i18next // for all options read: https://www.i18next.com/overview/configuration-options .init({ debug: true, fallbackLng: 'en', backend: locizeOptions })
export default function (app) { app.use(I18NextVue, { i18next }) return app }
i18next-locize-backend предлагает функциональность для получения доступных языков непосредственно из locize, давайте воспользуемся ею:
<template> <div class="hello"> <h1>{{ $t('welcome') }}</h1> <p v-html="$t('descr')"></p> <i>{{ $t('new.key', 'this will be added automatically') }}</i> <hr /> <div> <div v-if="languages"> <span v-for="(lng, index) in Object.keys(languages)" :key="lng"> <a v-if="$i18next.resolvedLanguage !== lng" v-on:click="$i18next.changeLanguage(lng)"> {{ languages[lng].nativeName }} </a> <strong v-if="$i18next.resolvedLanguage === lng"> {{ languages[lng].nativeName }} </strong> <span v-if="index < (Object.keys(languages).length - 1)"> | </span> </span> </div> </div> </div> </template>
<script> import i18next from 'i18next'
export default { name: 'TranslationShowCase', data () { return { languages: [] } }, async mounted () { this.languages = await i18next.services.backendConnector.backend.getLanguages() } } </script>
сохранить недостающие переводы
Благодаря использованию функции saveMissing новые ключи добавляются для автоматического определения местоположения при разработке приложения.
Просто передайте saveMissing: true
в параметрах i18next:
import I18NextVue from 'i18next-vue' import i18next from 'i18next' import Backend from 'i18next-locize-backend' import LanguageDetector from 'i18next-browser-languagedetector'
const locizeOptions = { projectId: '94c21299-0cf5-4ad3-92eb-91f36fc3f20f', apiKey: 'bc8586d9-fceb-489c-86ac-2985393ed955', // YOU should not expose your apps API key to production!!! version: 'latest' }
export const i18nextPromise = i18next // i18next-locize-backend // loads translations from your project, saves new keys to it (saveMissing: true) // https://github.com/locize/i18next-locize-backend .use(Backend) // detect user language // learn more: https://github.com/i18next/i18next-browser-languageDetector .use(LanguageDetector) // init i18next // for all options read: https://www.i18next.com/overview/configuration-options .init({ debug: true, fallbackLng: 'en', backend: locizeOptions, saveMissing: true })
export default function (app) { app.use(I18NextVue, { i18next }) return app }
Каждый раз, когда вы будете использовать новый ключ, он будет отправляться на локализацию, т.е.:
<i>{{ $t('new.key', 'this will be added automatically') }}</i>
приведет к локализации следующим образом:
👀 но есть еще…
Благодаря плагину locize-lastused вы сможете находить и фильтровать в locize, какие ключи используются или уже не используются.
С помощью плагина locize вы сможете использовать свое приложение в locize InContext Editor.
Наконец, с помощью рабочего процесса автоматического машинного перевода и использования функции saveMissing новые ключи не только добавляются для автоматической локализации при разработке приложения, но также автоматически переводятся на целевые языки с помощью машинного перевода. .
Посмотрите это видео, чтобы увидеть, как выглядит рабочий процесс автоматического машинного перевода!
npm install locize-lastused locize
используйте их в i18n.js
:
import I18NextVue from 'i18next-vue' import i18next from 'i18next' import Backend from 'i18next-locize-backend' import LanguageDetector from 'i18next-browser-languagedetector' import LastUsed from 'locize-lastused' import { locizePlugin } from 'locize'
const locizeOptions = { projectId: '94c21299-0cf5-4ad3-92eb-91f36fc3f20f', apiKey: 'bc8586d9-fceb-489c-86ac-2985393ed955', // YOU should not expose your apps API key to production!!! version: 'latest' }
export const i18nextPromise = i18next // locize-lastused // sets a timestamp of last access on every translation segment on locize // -> safely remove the ones not being touched for weeks/months // https://github.com/locize/locize-lastused .use(LastUsed) // locize-editor // InContext Editor of locize .use(locizePlugin) // i18next-locize-backend // loads translations from your project, saves new keys to it (saveMissing: true) // https://github.com/locize/i18next-locize-backend .use(Backend) // detect user language // learn more: https://github.com/i18next/i18next-browser-languageDetector .use(LanguageDetector) // init i18next // for all options read: https://www.i18next.com/overview/configuration-options .init({ debug: true, fallbackLng: 'en', saveMissing: true, backend: locizeOptions, locizeLastUsed: locizeOptions })
export default function (app) { app.use(I18NextVue, { i18next }) return app }
Автоматический машинный перевод:
Фильтр последних использованных переводов:
📦 Подготовимся к производству 🚀
Теперь мы готовим приложение к выходу в производство.
Сначала в locize создайте специальную версию для производства. Не включайте автоматическую публикацию для этой версии, а публикуйте вручную или через API или через CLI.
Наконец, включите максимальный возраст Cache-Control для этой рабочей версии.
Давайте воспользуемся особенностью окружения реактивных скриптов.
Давайте создадим файл среды по умолчанию, один для разработки и один для производства:
.env
:
VUE_APP_LOCIZE_PROJECTID=94c21299-0cf5-4ad3-92eb-91f36fc3f20f
.env.development
:
VUE_APP_LOCIZE_VERSION=latest
VUE_APP_LOCIZE_APIKEY=bc8586d9-fceb-489c-86ac-2985393ed955
.env.production
:
VUE_APP_LOCIZE_VERSION=production
Теперь адаптируем файл i18n.js:
import I18NextVue from 'i18next-vue' import i18next from 'i18next' import Backend from 'i18next-locize-backend' import LanguageDetector from 'i18next-browser-languagedetector' import LastUsed from 'locize-lastused' import { locizePlugin } from 'locize'
const isProduction = process.env.NODE_ENV === 'production'
const locizeOptions = { projectId: process.env.VUE_APP_LOCIZE_PROJECTID, apiKey: process.env.VUE_APP_LOCIZE_APIKEY, // YOU should not expose your apps API key to production!!! version: process.env.VUE_APP_LOCIZE_VERSION }
if (!isProduction) { // locize-lastused // sets a timestamp of last access on every translation segment on locize // -> safely remove the ones not being touched for weeks/months // https://github.com/locize/locize-lastused i18next.use(LastUsed); }
export const i18nextPromise = i18next // locize-editor // InContext Editor of locize .use(locizePlugin) // i18next-locize-backend // loads translations from your project, saves new keys to it (saveMissing: true) // https://github.com/locize/i18next-locize-backend .use(Backend) // detect user language // learn more: https://github.com/i18next/i18next-browser-languageDetector .use(LanguageDetector) // init i18next // for all options read: https://www.i18next.com/overview/configuration-options .init({ debug: !isProduction, fallbackLng: 'en', saveMissing: !isProduction, backend: locizeOptions, locizeLastUsed: locizeOptions })
export default function (app) { app.use(I18NextVue, { i18next }) return app }
Теперь, во время разработки, вы продолжите сохранять отсутствующие ключи и использовать функцию lastused. =› запуск npm подача
А в продакшене saveMissing и lastused отключены, а API-ключ не выставлен. => npm запустить сборку
🧑💻 Полный код можно найти здесь.
Посмотрите также часть интеграции кода в этом видео YouTube.
🎉🥳 Поздравляем 🎊🎁
Надеюсь, вы узнали что-то новое о i18next, локализации Vue.js и современных рабочих процессах локализации.
Так что если вы хотите вывести свою тему i18n на новый уровень, стоит попробовать платформу управления локализацией — locize.
Основатели locize также являются создателями i18next. Таким образом, используя locize, вы напрямую поддерживаете будущее i18next.