Инструменты

  1. Vue.js вместе с Vue CLI.

  2. vue-i18n

  3. VSCode

  4. 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)


  1. borovinskiy
    20.08.2021 17:05

    Когда в реакте встретился с вопросом локализации, просто наколхозил mixin с функцией t(key) и объект для перевода прямо в компоненте (без выноса в отдельный json).

    Кода там два десятка строк и fallback на английский, если перевода нет на текущем языке.

    Понятно, внешние json легче отдавать переводчику. Иметь перевод в компоненте - значит хранить всё в одном месте, что вполне себе vue-way.


  1. 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 )
            }
          
        }
    }


    1. dopusteam
      21.08.2021 19:54

      А как параметры передать? А если параметр перед передачей нужно отдельно локализовать?


      1. nin-jin
        21.08.2021 23:00
        -1

        Что за параметры?


        1. dopusteam
          22.08.2021 03:32

          Ну локализовать не просто 'Привет', а 'Привет #имя пользователя#'


          1. nin-jin
            22.08.2021 09:49

            А, так просто берёте скриптом и заменяете:

            title() {
              	return super.title().replace( '#username#', this.user().name().slice( 0, 100 ) )
            }


            1. dopusteam
              22.08.2021 13:21

              А это где пишется и как будет выглядеть 'разметка'? И куда делась локализация?

              И ещё, как дёрнуть локализацию из кода, а не из 'шаблона'?


              1. 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"
                }


        1. vanxant
          23.08.2021 21:51

          В корзине %d товар(ов)
          Вот надо просклонять для d из 0..99


          1. nin-jin
            24.08.2021 12:58

            Для этой задачи красивого решения пока нет. Она не так проста, как кажется на первый взгляд. Например:

            Алиса открыла Бобу доступ ко 2 задачам, 5 папкам и одному проекту
            Боб открыл Алекс доступ к одной папке
            Алекс открыла Алисе доступ к 21 задаче

            Тут одно слово надо просклонять по роду (минимум 3 словоформы), который зависит от гендера, ещё три слова по разным по числам (до 6 словоформ каждого), плюс в зависимости от языка правильно сформировать списки. Имена надо вставлять в разных падежах (порядка 6 словоформ). А что с предлогом делать вообще не понятно. И это только приколы одного языка, а в каждом языке приколы свои.

            Отчасти поэтому я стараюсь не использовать излишнее очеловечивание текста. Другая причина - очеловеченный текст долго читать. Когда постоянно работаешь с интерфейсом, хочется мгновенно считывать важную информацию, а не вчитываться каждый раз в текст. Поэтому предпочтительнее более короткий формальный вывод:

            Открыт доступ
            	Кто: Алекс
            	Кому: Алиса
            	Задач: 2
            	Папок: 5
            	Проект: 1


  1. parlament91
    25.08.2021 14:27

    Можете объяснить для чего используете require.context ?


    1. serhsavchuk Автор
      25.08.2021 14:32

      Здесь мы перебираем .json файлы по имени и подгружаем в JavaScript. Подробнее о require.context.


  1. gmtd
    12.09.2021 09:28

    Когда текста для перевода много, удобно делать локали не файлами json, а подпапками с файлами json

    Подпапка 'ru'
    В ней файлы account.json, footer.json и так далее
    В каждом соответствующие переводы, иерархично
    Обращение: $t("account.profile.usernameTitle")

    можно в этих файлах задействовать массивы и выводить их циклом в тэгах li или p по надобности


    1. serhsavchuk Автор
      24.09.2021 22:12

      Если использовать i18n Ally, то достаточно одного файла для одного языка. Плагин автоматически генерирует ключи, там можно подправить в настройках как их генерировать если нужно. Меня вполне устраивает стандартный транслит, если основной язык не английский. Так же плагин позволяет на месте посмотреть что в ключе и даёт возможность подправить перевод на всех языках. Посмотрите как нибудь, быть может отпадет надобность разбивать по папкам и вручную прописывать ключи.