Инструменты
Vue.js вместе с Vue CLI.
vue-i18n
VSCode
BabelEdit
Vue.js вместе с Vue CLI
Для тех кто не знает как работать с Vue CLI вот документация. Если кратко, то для установки нам нужно ввести две команды npm:
npm install -g @vue/cli //Установка
vue create test-project //Создание проекта
vue-i18n
Пакет для локализации. Для установки нужно ввести команду:
npm install vue-i18n
Следующим шагом в корне проекта создаем папку locales с файлами .json (например: ru.json, en.json...). Здесь мы будем сохранять ключи и значения к отдельному языку. Ключи во всех файлах одинаковы, а значения переведены под конкретный язык.
Далее создаем папку helpers так же в корне проекта, в ней создаем файл i18n.js. Он нужен нам для определения языка на момент загрузки приложения, и для подгрузки наших json ключей в JavaScript. В созданный файл копируем следующий код:
import Vue from "vue";
import VueI18n from "vue-i18n"; //Импорт установленного пакета
Vue.use(VueI18n);
function loadLocaleMessages() {
const locales = require.context(
"@/locales", // Путь к папке с нашими json файлами локализации
true,
/[A-Za-z0-9-_,\s]+\.json$/i
);
const messages = {};
locales.keys().forEach(key => {
const matched = key.match(/([A-Za-z0-9-_]+)\./i);
if (matched && matched.length > 1) {
const locale = matched[1];
messages[locale] = locales(key);
}
});
return messages;
}
function checkDefaultLanguage() { //Определяем язык браузера
let matched = null;
let languages = Object.getOwnPropertyNames(loadLocaleMessages());
languages.forEach(lang => {
if (lang === navigator.language) {
matched = lang;
}
});
if (!matched) {
languages.forEach(lang => {
let languagePartials = navigator.language.split("-")[0];
if (lang === languagePartials) {
matched = lang;
}
});
}
return matched;
}
export const selectedLocale = localStorage.getItem('locale')
|| checkDefaultLanguage()
|| "ru";
export const languages = Object.getOwnPropertyNames(loadLocaleMessages());
export default new VueI18n({
locale: selectedLocale || "ru",
globalInjection: true,
fallbackLocale: "ru",
messages: loadLocaleMessages()
});
Следующим шагом нужно импортировать созданный файл в main.js:
import Vue from 'vue'
import router from './router/router.js'
import App from './App.vue'
import store from './store/store.js'
import i18n from "./helpers/i18n.js"
new Vue({
store,
router,
i18n,
render: h => h(App),
}).$mount('#app')
Наш i18n теперь подключен, остается сделать переключатель языка. Тут уже кто на что горазд, я сделал обычным селектом и добавил в vuex стор файл locale.js в котором импортировал файл i18n.js из нашей папки helpers. Сам код из стора:
import Vue from 'vue';
import Vuex from 'vuex';
import i18n, { selectedLocale } from 'src/helpers/i18n';
Vue.use(Vuex);
export default{
state: {
locale: selectedLocale
},
getters: {
getLocale: state => state.locale
},
mutations: {
updateLocale(state, newLocale) {
i18n.locale = newLocale // важно оставить, что б менялся язык
state.locale = newLocale // а здесь меняем значение для переключателя
localStorage.setItem('locale', newLocale); // запоминаем текущий язык, что б после перезагрузки страницы не сбросило на стандартный
}
},
}
Ну и сам селект:
<div>
<select v-model="lang">
<option value="en">{{ $t('english') }}</option>
<option value="ru">{{ $t('russkii') }}</option>
</select>
</div>
export default {
computed: {
lang: {
get() {
return this.$store.getters.getLocale;
},
set(newValue) {
this.$store.commit("updateLocale", newValue);
}
}
}
}
Как вы могли заметить $t обращается по названию ключа к i18n и показывает значение, так и происходит локализация. Главное в каждом json файле заполнить все ключи. А для того что бы самому не вводить каждый раз ключ вручную есть у VSCode отличный плагин i18n-Ally.
VSCode
Плагин i18n-Ally позволяет автоматически создавать ключи из hard coded строк, переводить значения с помощью переводчика, и автозаполнять строку нужным вызовом функции ( $t(...), this.$t(...) и т.д.) Загрузить его можно здесь. Документация тут. Примеры можно посмотреть на странице документации.
У меня возникла проблема когда проект сделан и его нужно перевести. Перелопатить каждый компонент задача не из интересных да и времени не так что б много. Хорошо что контент заполнялся на русском языке и его можно отличить при глобальном поиске (Ctrl + Shift + F) от кода и разметки. Так я и зделал, прописал регулярное выражение:
[а-яА-Я]+
Немного трудозатратно конечно, но для меня это единственный нормальный рабочий способ. В интернете есть способы том как автоматически собрать hard coded строки, но мне они не подошли. То вылезал null то ключ не так задавал, то разрывал строку. Вобщем пришел к такому решению.
BabelEdit
Для повседневной работы i18n-Ally должно хватить с головой. Но когда нужно сразу перевести 300-500 ключей то у меня вылезла ошибка 429 с ограничением запросов на гугл переводчик. Здесь то нам и пригодится BabelEdit, у него есть пробная лицензия которую путем нехитрых гуглений можно продлить легально. Про него достаточно понятно написано в документации, не буду тут повторять, единственное я просто сохранил .babel проект в корень приложения. Документация и загрузка доступна по ссылке здесь.
Комментарии (14)
nin-jin
20.08.2021 19:47-1Для сравнения, как прикрутить локализацию к любому приложению на $mol..
Допустим, к у нас такое простое приложение:
$my_app $mol_page title \Hello World
Чтобы подключить библиотеку локализации, определить дефолтный язык, загрузить соответствующий json с текстами, которые предварительно вытащены из кода, нужно добавить всего один символ перед переводимой строкой:
$my_app $mol_page title @ \Hello Woorld
И даже без расширения для VSCode обошлись. Почему в Vue до сих пор не так?
Если очень хочется, можем добавить и переключатель языка, сохраняющийся в локальное хранилище:
$my_app $mol_page title @ \Hello Woorld body / <= Lang $mol_switch value?next <=> lang?next \en options * en \English ru \Русский
namespace $.$$ { export class $my_app extends $.$my_app { lang( next?: string ) { return this.$.$mol_locale.lang( next ) } } }
dopusteam
21.08.2021 19:54А как параметры передать? А если параметр перед передачей нужно отдельно локализовать?
nin-jin
21.08.2021 23:00-1Что за параметры?
dopusteam
22.08.2021 03:32Ну локализовать не просто 'Привет', а 'Привет #имя пользователя#'
nin-jin
22.08.2021 09:49А, так просто берёте скриптом и заменяете:
title() { return super.title().replace( '#username#', this.user().name().slice( 0, 100 ) ) }
dopusteam
22.08.2021 13:21А это где пишется и как будет выглядеть 'разметка'? И куда делась локализация?
И ещё, как дёрнуть локализацию из кода, а не из 'шаблона'?
nin-jin
22.08.2021 16:25В классе с логикой. Полный код как-то так выглядит:
$my_app $mol_page title @ \Hello #username# username?mut @ \Anonymous
namespace $.$$ { export class $my_app extends $.$my_app { title() { return super.title().replace( '#username#', this.username() ) } } }
Тут мы, собственно, дёргаем оба локализованных свойства из кода. Второе при этом можно динамически менять.
Переводчику же уйдёт JSON такого рода:
{ "$my_app_title": "Hello, #username#", "$my_app_username": "Anonymous" }
vanxant
23.08.2021 21:51В корзине %d товар(ов)
Вот надо просклонять для d из 0..99nin-jin
24.08.2021 12:58Для этой задачи красивого решения пока нет. Она не так проста, как кажется на первый взгляд. Например:
Алиса открыла Бобу доступ ко 2 задачам, 5 папкам и одному проекту Боб открыл Алекс доступ к одной папке Алекс открыла Алисе доступ к 21 задаче
Тут одно слово надо просклонять по роду (минимум 3 словоформы), который зависит от гендера, ещё три слова по разным по числам (до 6 словоформ каждого), плюс в зависимости от языка правильно сформировать списки. Имена надо вставлять в разных падежах (порядка 6 словоформ). А что с предлогом делать вообще не понятно. И это только приколы одного языка, а в каждом языке приколы свои.
Отчасти поэтому я стараюсь не использовать излишнее очеловечивание текста. Другая причина - очеловеченный текст долго читать. Когда постоянно работаешь с интерфейсом, хочется мгновенно считывать важную информацию, а не вчитываться каждый раз в текст. Поэтому предпочтительнее более короткий формальный вывод:
Открыт доступ Кто: Алекс Кому: Алиса Задач: 2 Папок: 5 Проект: 1
parlament91
25.08.2021 14:27Можете объяснить для чего используете require.context ?
serhsavchuk Автор
25.08.2021 14:32Здесь мы перебираем .json файлы по имени и подгружаем в JavaScript. Подробнее о require.context.
gmtd
12.09.2021 09:28Когда текста для перевода много, удобно делать локали не файлами json, а подпапками с файлами json
Подпапка 'ru'
В ней файлы account.json, footer.json и так далее
В каждом соответствующие переводы, иерархично
Обращение:$t("account.profile.usernameTitle")
можно в этих файлах задействовать массивы и выводить их циклом в тэгах li или p по надобностиserhsavchuk Автор
24.09.2021 22:12Если использовать i18n Ally, то достаточно одного файла для одного языка. Плагин автоматически генерирует ключи, там можно подправить в настройках как их генерировать если нужно. Меня вполне устраивает стандартный транслит, если основной язык не английский. Так же плагин позволяет на месте посмотреть что в ключе и даёт возможность подправить перевод на всех языках. Посмотрите как нибудь, быть может отпадет надобность разбивать по папкам и вручную прописывать ключи.
borovinskiy
Когда в реакте встретился с вопросом локализации, просто наколхозил mixin с функцией t(key) и объект для перевода прямо в компоненте (без выноса в отдельный json).
Кода там два десятка строк и fallback на английский, если перевода нет на текущем языке.
Понятно, внешние json легче отдавать переводчику. Иметь перевод в компоненте - значит хранить всё в одном месте, что вполне себе vue-way.