Software localization

Deep Dive: Vue Translation with vue-i18next

Your advanced guide to Vue translation bringing all the tips, tricks, and how-tos around vue-i18next in one place.
Software localization blog category featured image | Phrase

Vue.js is one of the most popular front-end frameworks out there. It was introduced in 2014 by Evan You who worked for Google and utilized AngularJS. He decided to reconcile Angular's concepts and create a framework that would be less opinionated, easier to use, and wouldn't have that steep learning curve (take a look at this comparison to see all the differences).

Basically, the framework is so straightforward and lightweight that you may already start crafting real projects only after a couple of days of reading the official guide. In this guide, assuming you are familiar with the framework's basics, we'll go through the process of Vue translation to see how your Vue.JS application can go multilingual.

🔗 Resource » Check out our ultimate guide to JavaScript localization for a deep dive into more topics surrounding Vue and other frameworks.

🔗 Resource » If you're interested in Vue localization with the Vue I18n library, check out this comprehensive guide to Vue localization.

To tackle this, we'll use a tool called vue-i18next which, as the name implies, is a plugin for the i18next framework, a complex and feature-rich internationalization solution built for web applications. In this article, we'll create a sample Vue.js app and cover the following topics:

  • Installing vue-i18next
  • Setting it up
  • Providing locale data
  • Performing translations
  • Switching locales
  • Using directive-based translations
  • Performing pluralization
  • Loading translation bundles

Installation

So, the first step is, of course, installing vue-18next. We have a handful of available options:

  • Hook it up straight from CDN (suitable for cases when you do not use any package manager)
  • Install with NPM
  • Install with Yarn
  • Clone the source code from GitHub and build the library manually

For example, to install it with NPM, you'll have to run the following commands:

npm install i18next --save

npm install @panter/vue-i18next --save

Don't forget to install I18next itself; otherwise, you'll get an error saying this module cannot be found.

However, in this tutorial, I won't be creating an NPM project for simplicity. Instead, let's just create a plain HTML page called index.html with the following markup:

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="utf-8">

  <title>Vue-i18next PhraseApp Demo</title>

  <script src="https://unpkg.com/vue@2.2.2/dist/vue.js"></script> <!-- 1 -->

  <script src="https://unpkg.com/i18next@8.0.0/i18next.js"></script> <!-- 2 -->

  <script src="https://unpkg.com/@panter/vue-i18next/dist/vue-i18next.js"></script> <!-- 3 -->

</head>

<body>

  <div id="app">

    <app />

  </div>

  <script src="app.js"></script> <!-- 4 -->

</body>

</html>

  1. We're using unpkg.com CDN to add Vue.js itself
  2. Then we load I18next
  3. Now, load vue-i18next (note that the order matters!)
  4. Lastly, load app.js with the project's source code

Finally, create an app.js file in the same directory and you are good to go!

Configuration

Vue-i18next relies on I18next to set everything up, therefore you might want to open the Configuration Options page. Add the following code to the app.js file:

i18next.init({

  lng: 'en',

  fallbackLng: 'ru',

  whitelist: ['en', 'ru']

});
  1. lng sets a default language. Note that if this option is not set, I18next will try to perform language detection automatically and set the locale accordingly
  2. fallbackLng says which locale data to use if translations for the currently set language is unavailable
  3. whitelist says which locales are permitted in this app. We'll stick to English and Russian

Translation Data

The next step is to provide translation data for the available locales. The easiest way is to store them in the same app.js file:

const locales = { // 1

  en: {

    welcome: 'Welcome to our demo!'

  },

  ru: {

    welcome: 'Добро пожаловать в наше демо!'

  },

};

i18next.init({

  lng: 'en',

  fallbackLng: 'ru',

  whitelist: ['en', 'ru'],

  resources: {

    en: { translation: locales.en }, // 2

    ru: { translation: locales.ru }, // 3

  },

});

Here are the main points:

  1. Provide translations for each locale. For convenience, I'm storing it in a separate constant. welcome serves as a key, whereas the corresponding string as a value. Note that the keys can be nested as well
  2. Provide translations for the English locale
  3. Provide Russian translation

That's it! Later, we'll see other ways of providing translation data.

When Vue Meets I18next

So, the initial configuration is ready, and we may proceed to Vue-specific tasks. Actually, there are two of them:

To accomplish the first task, use the following code:

// ...

Vue.component('app', {

  template: `

    <p>{{ $t("welcome") }}</p>

  `,

});

$t is a special function that accepts a translation key and returns translation data based on the currently set locale. In our case, it will return "Welcome to our demo!".

Now we need to equip Vue with I18next features:

// init function...

const i18n = new VueI18next(i18next);

// your component

new Vue({

  i18n,

}).$mount('#app');

Having this code in place, you are creating a Vue application powered by I18next. Now you may open the index.html page in the browser and check that the welcoming message shows up properly!

Switching the Language

So, our application seems to be working fine, but we can't switch the currently set locale. Let's fix that now by creating a new language-changer Vue component:

Vue.component('language-changer', {

  template: `

    <ul>

      <li><a href="#" @click.prevent="changeLanguage('en')">EN</a></li>

      <li><a href="#" @click.prevent="changeLanguage('ru')">RU</a></li>

    </ul>`,

  methods: {

    changeLanguage(lang) {

      this.$i18n.i18next.changeLanguage(lang);

    },

  },

});

The template contains an unordered list with two links that have an attached click event (with a preventDefault event modifier). When one of the links is clicked, we call a changeLanguage method. Inside this method, we can access the$i18n object and invoke I18next-specific methods like changeLanguage. Note that this method returns a promise so you may further tweak the code as necessary.

Next utilize the newly created component:

Vue.component('app', {

  template: `

    <main>

      <language-changer></language-changer>

      <p>{{ $t("welcome") }}</p>

    </main>

  `,

});

I've just added the language-changer component into the app. Remember that the template may have only one root element, therefore we have to wrap the component and the paragraph with the main tag.

Lastly, reload your HTML page and try switching the language: the welcoming message should update almost instantly!

Other Ways of Working With Translations

Using a Directive

We've already seen how to implement translation using the $t function but that's not the only way. It's also possible to use a directive:

Vue.component('app', {

  template: `

    <main>

      <language-changer></language-changer>

      <p>{{ $t("welcome") }}</p>

      <p v-t="{path:'welcome'}"></p> // <--- add this

    </main>

  `,

});

All you need to do is provide a v-t directive with the proper arguments. path accepts the name of the translation key. It is also possible to explicitly set a language and provide other arguments, for example: v-t="{ path: 'welcomeWithName', language: 'en', args: { name: 'Hans' } }.

Using a I18next Component

Next, it seems that you don't have to create a custom component every time. There's an i18next component available that supports various directives:

Vue.component('app', {

  template: `

    <main>

      <language-changer></language-changer>

      <p>{{ $t("welcome") }}</p>

      <p v-t="{path:'welcome'}"></p>

      <i18next path="term" tag="label" for="tos"> // <---

        <a href="#" target="_blank">{{ $t("tos") }}</a> // <---

      </i18next> // <---

    </main>

  `,

});

What's going on in this example?

  • path points out which translation key to use
  • tag instructs to turn this component into a label
  • for is an attribute that will be added to the label so you will see <label for="tos"></label> as a result
  • Inside this label, we'll have a link with its own translated content

Here is the translation data for this example:

const locales = {

  en: {

    welcome: 'Welcome to our demo!',

    tos: 'Terms of service',

    term: 'I accept {{0}}.'

  },

  ru: {

    welcome: 'Добро пожаловать в наше демо!',

    tos: 'Условия использования',

    term: 'Я принимаю {{0}}.'

  },

};

term is the key used in the path directive. Note that its value contains a {{0}} part which is called interpolation. It simply means that the {{0}} will be replaced with some content. In our case, the content is the a link:

<a href="#" target="_blank">{{ $t("tos") }}</a>

It means that we'll see the following markup as the final result:

<label for="tos">I accept <a href="#" target="_blank">Terms of service</a>.</label>

Neat, isn't it?

Prividing Key Prefix

Next, suppose you have a bunch of nested translations. For example:

const locales = {

  en: {

    welcome: 'Welcome to our demo!',

    info: {

      warning: 'Warning message'

    }

  },

  ru: {

    welcome: 'Добро пожаловать в наше демо!',

    info: {

      warning: 'Предупреждение'

    }

  },

};

The warning key is nested under info. Suppose now you would like to create a new component that uses this key. You may access it inside the $t function using the info.warning notation, but there is another approach available:

Vue.component('key-prefix', {

  i18nOptions: {

    keyPrefix: 'info',

  },

  template: `

    <div>

      <p>{{ $t('warning') }}</p>

    </div>`,

});

The keyPrefix option will automatically append the info prefix to all translation keys inside the current component. Quite convenient!

Pluralization

Pluralization is a very common task when translating your app. Various languages have different pluralization rules but, luckily, I18next does all the heavy lifting for us. Let's take a look at the following example:

const locales = {

  en: {

    welcome: 'Welcome to our demo!',

    tos: 'Terms of service',

    term: 'I accept {{0}}.',

    info: {

      warning: 'Warning message'

    },

    apple: 'One apple', // <---

    apple_plural: '{{count}} apples' // <---

  },

  ru: {

    welcome: 'Добро пожаловать в наше демо!',

    tos: 'Условия использования',

    term: 'Я принимаю {{0}}.',

    info: {

      warning: 'Предупреждение'

    },

    apple_0: "{{count}} яблоко", // <---

    apple_1: "{{count}} яблока", // <---

    apple_2: "{{count}} яблок" // <---

  },

};

We're providing plural and singular forms for the word "apple". In Russian, things are a bit more complex so I have to specify three possible cases (therefore I am saying apple_0, apple_1, apple_2 – not sure about apple_plural). If you are not sure how many cases should be provided, just use this handy tool.

After listing the translations, modify the base template again:

Vue.component('app', {

  template: `

    <main>

      <language-changer></language-changer>

      <p>{{ $t("welcome") }}</p>

      <p v-t="{path:'welcome'}"></p>

      <inline-translations></inline-translations>

      <key-prefix></key-prefix>

      <i18next path="term" tag="label" for="tos">

        <a href="#" target="_blank">{{ $t("tos") }}</a>

      </i18next>

      <p>{{ $t("apple", {count: 100}) }}</p> // <---

    </main>

  `,

});

Note that we are using an apple key without any postfixes, and then pass a count option. I18next will read this option and pick the appropriate translation. The number itself will also be interpolated at the {{count}} placeholder.

Using the same approach, you may provide context for your translations.

Loading Translation Data

Translation Bundles

Sometimes you may require to load additional translation data on the fly. To do that, use an addResourceBundle function:

Vue.component('load-bundle', {

  template: `

    <div>

      <a @click="loadBundle">Load french</a>

    </div>`,

  methods: {

    loadBundle() {

      this.$i18n.i18next.addResourceBundle('fr', 'translation', {key: 'value'});

    },

  },

});

Here, we're creating a component with a single link. As soon as it's clicked, call the addResourceBundle function and load translations for the French language.

Inline Translations

Let's take a look at another example. At the beginning of the article, we provided translation data in a separate constant. However, it is possible to store translations inline, right inside the component:

Vue.component('inline-translations', {

  i18nOptions: {

    messages: {

      en: {

        demo: 'Demo'

      },

      ru: {

        demo: 'Демо'

      },

    },

  },

  template: `

    <div>

      {{$t('demo')}}

    </div>`,

});

We're creating a new component with i18nOptions that stores inline translations. These translations are available inside the template, and so we employ the $t function again to display the demo message.

Don't forget to include this component to see it in action:

Vue.component('app', {

  template: `

    <main>

      <language-changer></language-changer>

      <p>{{ $t("welcome") }}</p>

      <p v-t="{path:'welcome'}"></p>

      <inline-translations></inline-translations> // <--- add this

    </main>

  `,

});

Note that there are other ways of loading translations from various backends. I18next has lots of plugins supporting asynchronous loading, Gettext, MongoDB, and others...

Phrase and Translation Files

Working with translation files can be quite complicated, especially when the app is of bigger scope and is supposed to support many languages. You might easily miss some translations for a specific language, and that will confuse the end-user…

Take Phrase for a spin: Grab your 14-day trial. Phrase supports many different languages and frameworks, including JavaScript. It allows you to easily import and export translation data. What is more important: You can quickly check which translation keys are missing. On top of that, you can collaborate with translators as it’s much better to have professionally done localization for your website.

Conclusion

In this article, we discussed the process of translating Vue.js applications with the help of the vue-i18next library. As you see, this library is easy to get started with and provides lots of features thanks to the underlying I18next framework! This framework has lots of other features and plugins, so be sure to browse its official documentation.

What tools are you using to localize your Vue.js application? Share your experience with us.