# Однофайловые компоненты

# Базовое использование

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

Код компонента из примера использования с однофайловыми компонентами (opens new window):

<i18n>
{
  "en": {
    "hello": "hello world!"
  },
  "ru": {
    "hello": "Привет мир!"
  }
}
</i18n>

<template>
  <div id="app">
    <label for="locale">Язык</label>
    <select v-model="locale">
      <option>en</option>
      <option>ru</option>
    </select>
    <p>Сообщение: {{ $t('hello') }}</p>
  </div>
</template>

<script>
export default {
  name: 'app',
  data () {
    this.$i18n.locale = 'ru';
    return { locale: 'ru' }
  },
  watch: {
    locale (val) {
      this.$i18n.locale = val
    }
  }
}
</script>

# Установка vue-i18n-loader

Требуется установить vue-loader и vue-i18n-loader чтобы использовать пользовательские блоки <i18n>. Скорее всего vue-loader (opens new window) уже используется в проекте, если уже работаете с однофайловыми компонентами, но необходимо дополнительно установить vue-i18n-loader (opens new window):

npm i --save-dev @kazupon/vue-i18n-loader

# Webpack

Для Webpack требуется следующая конфигурация:

Для vue-loader v15 или более поздних версий:

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      {
        resourceQuery: /blockType=i18n/,
        type: 'javascript/auto',
        loader: '@kazupon/vue-i18n-loader'
      }
      // ...
    ]
  }
  // ...
}

Для vue-loader v14:

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          loaders: {
            // необходимо указать ключ `i18n` для загрузчика `vue-i18n-loader`
            // (https://github.com/kazupon/vue-i18n-loader)
            i18n: '@kazupon/vue-i18n-loader'
          }
        }
      }
      // ...
    ]
  }
  // ...
}

# Vue CLI 3.0

Vue CLI 3.0 (opens new window) скрывает конфигурацию Webpack, поэтому для добавления поддержки тегов <i18n> в однофайловых компонентах необходимо изменить существующую конфигурацию.

Для этого нужно создать файл vue.config.js в корне проекта и добавить в него следующее:

Для vue-loader v15 или более поздних версий:

module.exports = {
  chainWebpack: config => {
    config.module
      .rule('i18n')
      .resourceQuery(/blockType=i18n/)
      .type('javascript/auto')
      .use('i18n')
        .loader('@kazupon/vue-i18n-loader')
        .end()
  }
}

Для vue-loader v14:

const merge = require('deepmerge')

module.exports = {
  chainWebpack: config => {
    config.module
      .rule('vue')
      .use('vue-loader')
      .tap(options =>
        merge(options, {
          loaders: {
            i18n: '@kazupon/vue-i18n-loader'
          }
        })
      )
  }
}

Не забудьте установить deepmerge (opens new window)! (npm i deepmerge -D или yarn add deepmerge -D)

Подробнее о возможностях изменения существующей конфигурации Webpack можно изучить здесь (opens new window).

# Laravel-Mix

Для Laravel-mix 4 с vue-loader v15 или более поздней версии:

// Расширяем Mix с помощью метода "i18n", который загрузит vue-i18n-loader
mix.extend( 'i18n', new class {
        webpackRules() {
            return [
                {
                    resourceQuery: /blockType=i18n/,
                    type:          'javascript/auto',
                    loader:        '@kazupon/vue-i18n-loader',
                },
            ];
        }
    }(),
);

// Убедитесь что вызвали .i18n() (для загрузки загрузчика) перед .js(..., ...)
mix.i18n()
   .js( 'resources/js/App.js', 'public/js/app.js' )
   ...

Для Laravel-mix 2 с vue-loader v14:

В Laravel-mix, начиная с версии V2.1 (opens new window), можно добавлять пользовательские правила с помощью mix.extend(). Laravel-mix уже имеет собственные правила для обработки .vue файлов. Чтобы добавить vue-i18n-loader, нужно добавить в webpack.mix.js следующее:

// Код ниже внедрит загрузчик i18n (@kazupon/vue-i18n-loader) в качестве загрузчика .vue файлов.
mix.extend( 'i18n', function( webpackConfig, ...args ) {
    webpackConfig.module.rules.forEach( ( module ) => {
        // Поиск компонента "vue-loader", который обрабатывает .vue файлы.
        if( module.loader !== 'vue-loader' ) {
            return;
        }

        // В этом модуле добавляем vue-i18n-loader для тега i18n.
        module.options.loaders.i18n = '@kazupon/vue-i18n-loader';
    } );
} );

// Убедитесь что вызвали .i18n() (для загрузки загрузчика) перед .js(..., ...)
mix.i18n()
   .js( 'resources/assets/js/App.js', 'public/js/app.js' )
   ...

# Загрузка YAML

Пользовательские блоки i18n можно указывать в формате JSON или YAML (используя функцию предварительного загрузчика vue-loader).

Пользовательский блок i18n в формате YAML:

<i18n>
  en:
    hello: "hello world!"
  ru:
    hello: "привет мир!"
</i18n>

Конфигурация Webpack:

Для vue-loader v15 или более поздних версий:

// Vue CLI 3.0
module.exports = {
  chainWebpack: config => {
    config.module
      .rule('i18n')
      .resourceQuery(/blockType=i18n/)
      .type('javascript/auto')
      .use('i18n')
        .loader('@kazupon/vue-i18n-loader')
        .end()
      .use('yaml')
        .loader('yaml-loader')
        .end()
  }
}

Для vue-loader v14:

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          preLoaders: {
            i18n: 'yaml-loader'
          },
          loaders: {
            i18n: '@kazupon/vue-i18n-loader'
          }
        }
      }
      // ...
    ]
  }
  // ...
}

# Несколько пользовательских блоков

Можно использовать сообщения локализации из нескольких пользовательских блоков i18n.

<i18n src="./common/locales.json"></i18n>
<i18n>
  {
    "en": {
      "hello": "hello world!"
    },
    "ru": {
      "hello": "Привет мир!"
    }
  }
</i18n>

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

Несколько пользовательских блоков полезны, когда использовать их в качестве модулей.

# Локальные стили

При использовании vue-i18n с локальными стилями (style scoped) необходимо помнить и использовать глубокий селектор (opens new window) для стилизации элемента внутри строки перевода. Например:

Когда перевод содержит только текст (работает без глубокого селектора)

<i18n>
  {
    "en": {
      "hello": "hello world!"
    },
    "ru": {
      "hello": "Привет мир!"
    }
  }
</i18n>

<template>
  <div class="parent">
    <p>Сообщение: {{ $t('hello') }}</p>
  </div>
</template>

<!-- Будет работать -->
<style scoped>
  .parent p {
    color: #42b883;
  }
</style>

Когда перевод содержит HTML-элемент (необходимо использовать глубокий селектор)

<i18n>
  {
    "en": {
      "hello": "hello<span>world!</span>"
    },
    "ru": {
      "hello": "привет <span>мир!</span>"
    }
  }
</i18n>

<template>
  <div class="parent">
    <p v-html="$t('hello')"></p>
  </div>
</template>

<!-- НЕ БУДЕТ РАБОТАТЬ! -->
<style scoped>
  .parent p {
    color: #42b883;
  }

  .parent p span {
    color: red;
  }
</style>

<!-- Будет работать >>> -->
<style scoped>
  .parent p {
    color: #42b883;
  }

  .parent p >>> span {
    color: red;
  }
</style>

<!-- Будет работать /deep/ -->
<style scoped>
  .parent p {
    color: #42b883;
  }

  .parent p /deep/ span {
    color: red;
  }
</style>

<!-- Будет работать ::v-deep -->
<style scoped>
  .parent p {
    color: #42b883;
  }

  ::v-deep .parent p span {
    color: red;
  }
</style>

# Пользовательские блоки в функциональном компоненте

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

Например, следующий код не может использовать сообщения локализации из блока i18n.

<i18n>
  {
    "en": {
      "hello": "hello world"
    },
    "ru": {
      "hello": "привет мир"
    }
  }
</i18n>

<template functional>
  <!-- Сообщение локализации 'hello' из родительского экземпляра -->
  <p>{{ parent.$t('hello') }}</p>
</template>