Software localization
Deep Dive: Vue Translation with vue-i18next

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>
- We're using unpkg.com CDN to add Vue.js itself
- Then we load I18next
- Now, load vue-i18next (note that the order matters!)
- 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'] });
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 accordinglyfallbackLng
says which locale data to use if translations for the currently set language is unavailablewhitelist
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:
- 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 - Provide translations for the English locale
- 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:
- Use translation data inside a component to display a welcoming message
- Pass the I18next object to the Vue.js app
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 usetag
instructs to turn this component into alabel
for
is an attribute that will be added to thelabel
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.