VueJS - Internationalization
Pablo Veiga
Posted on June 25, 2021
Since the Internet's boom, more and more web applications are released each day (approximately 576k new websites per day*). Currently, there are over 1.7 billion websites* on the Internet and many of them are accessed by people from all around the world.
Probably, if you create a website, you will want it to be accessed by as many people as possible. To make that happen and also provide a nice experience for your users, you will have to think about how to deliver it in different languages.
* Source: Website Setup
TL;DR
In this article I'm going to show you how to:
- create a multi-language website using VueJS and Vue i18n;
- implement a language switcher;
- use the
vuex-persistedstate
package to avoid losing state when reloading the page;
Internationalization (i18n)
Let's start with some basic concepts.
If you're still not aware of Internationalization or what i18n really means, here is its official definition:
Internationalization is the design and development of a product, application or document content that enables easy localization for target audiences that vary in culture, region, or language.
(Source: W3.org)
Internationalization is often written i18n (English), where 18 is the number of letters between "i" and "n" in the English word (cool, right?!).
Vue i18n
If you perform a quick search on the internet you will find some solutions to implement i18n to your website or application built using VueJS.
Probably, the most famous (and easy-to-use) one is Vue i18n, an open source plugin for VueJS that provides a very friendly API to implement translation to different target languages in your website.
Installation
Assuming you've already created your VueJS project (take a look at this article if you don't now where to start from), the first step to begin using the plugin is installing it. In a terminal window, go to the root directory of your project and run the following command:
yarn add vue-i18n --save
You might use NPM as well, depending on your project configuration.
Configuration
The Vue i18n package works in a very simple way.
You can set several configurations, but these are the basic ones required for your project to work:
- the initial language: the language loaded by default;
- the messages: a simple JSON object that contains the messages (translation keys) used for each one of the languages;
First, create the folder structure that will hold everything together:
Create a folder called
i18n
in thesrc
directory of your project.Within the
i18n
folder, create anindex.js
file and a folder calledmessages
.
This is how this first index.js
file will look like:
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import messages from './messages'
Vue.use(VueI18n)
export default new VueI18n({
locale: 'en',
messages
})
- In this example, we're going to build a part of an application that needs to be delivered in three different languages: English (default), Spanish and Brazilian Portuguese.
Within the messages
folder, create three folders named en
, es
and pt-BR
and, inside each one of them (that represents a different language), create two files: one named menu.js
and another named index.js
.
The files will look like this:
English
// /src/i18n/messages/en/menu.js
export default {
home: 'Home',
about: 'About',
contact: 'Contact'
}
// /src/i18n/messages/en/index.js
import menu from './menu'
export default {
menu
}
Spanish
// /src/i18n/messages/es/menu.js
export default {
home: 'Pagina de Inicio',
about: 'Acerca de',
contact: 'Contacto'
}
// /src/i18n/messages/es/index.js
import menu from './menu'
export default {
menu
}
Portuguese (Brazil)
// /src/i18n/messages/pt-BR/menu.js
export default {
home: 'Início',
about: 'Sobre',
contact: 'Contato'
}
// /src/i18n/messages/pt-BR/index.js
import menu from './menu'
export default {
menu
}
If you need, you may create more levels within the messages
object to organise them better. Like this:
export default {
links: {
home: {
label: 'Home',
help: 'Click here to go to home page'
},
about: {
label: 'About',
help: 'Click here to know more about us'
},
contact: {
label: 'Contact',
help: 'Click here to go to reach out to us'
}
}
}
- Still inside the
messages
folder, create anindex.js
file like this:
import en from './en'
import es from './es'
import ptBR from './pt-BR'
export default {
en,
es,
'pt-BR': ptBR
}
- In the
main.js
file, import thei18n
package and set it to the Vue instance:
import App from './App.vue'
import i18n from './i18n'
new Vue({
i18n,
render: h => h(App)
}).$mount('#app')
Now your application is ready to take advantage of the vue-i18n plugin. Let's create a simple scenario to use it.
Implementation
We're going to implement a language switcher and put it into a navbar at the top of the page. This switcher will be responsible to set the current locale of the application using Vuex + Vuex Persisted State.
To make things easier, I chose to use Bootstrap Vue. If you don't know it yet, it's worth taking a look. It provides all of the Bootstrap components, wrapped into Vue components :)
Before creating the component itself, we're going to structure a basic Vuex module that will be responsible for managing the language state, we'll also make use of the Vuex Persisted State plugin, to easily store the state in the local storage so that, when refreshing the page, the user does not lose its selected language.
- To add
Vuex Persist
in your project, run the following command in the root directory of your project:
yarn add vuex-persistedstate --save
Create a file named
index.js
and a folder namedstore
inside thesrc
directory.Create folder named
modules
withinstore
.Create a file named
locale.js
inside themodules
folder and implement it like this:
// src/store/modules/locale.js
export default {
namespaced: true,
state: {
locale: 'en'
},
mutations: {
setLocale(state, locale) {
state.locale = locale
}
}
}
This is how the store/index.js
will look like:
// src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import createPersistedState from 'vuex-persistedstate'
import locale from './modules/locale'
const persistedState = createPersistedState({
key: 'vuejs-vue-i18n',
paths: ['locale']
})
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
locale
},
plugins: [persistedState]
})
- Now, let's implement the
LanguageSwitch.vue
component. It will hold all of the available languages and it will use Vuex helpers functions to update the current language:
<!-- src/components/LanguageSwitcher.vue (template) -->
<template>
<b-nav-item-dropdown :text="currentLocale" right>
<b-dropdown-item
:disabled="isCurrentLocale('en')"
@click="onSetLocale('en')"
>
EN
</b-dropdown-item>
<b-dropdown-item
:disabled="isCurrentLocale('es')"
@click="onSetLocale('es')"
>
ES
</b-dropdown-item>
<b-dropdown-item
:disabled="isCurrentLocale('pt-BR')"
@click="onSetLocale('pt-BR')"
>
PT-BR</b-dropdown-item
>
</b-nav-item-dropdown>
</template>
// src/components/LanguageSwitcher.vue (script)
<script>
import { mapState, mapMutations } from 'vuex'
export default {
name: 'LanguageSwitcher',
computed: {
...mapState('locale', ['locale']),
currentLocale() {
return this.locale.toUpperCase()
}
},
created() {
this.$i18n.locale = this.locale
},
methods: {
...mapMutations('locale', ['setLocale']),
onSetLocale(locale) {
this.$i18n.locale = locale
this.setLocale(locale)
},
isCurrentLocale(locale) {
return this.locale === locale
}
}
}
</script>
- Now, let's create a simple
Navbar.vue
component to put the recently createdLanguageSwitcher
one. Notice that, in this case, we use the global$t
helper provided by the Vue i18n plugin to get the proper translation we need to display according to the current locale. It's very simple to use, all you need to do, it call it passing a translation key as argument.
Example:
{{ $t('translation.key') }}
You may also use directly in the script
section of your components, if needed:
{
computed: {
label() {
// For this work, you have to create a file named `common.js` inside the folder of each language and export it in its respective `index.js` file.
return this.$t('common.label')
}
},
methods: {
getTitle() {
return this.$t('common.title')
}
}
}
This is how our Navbar.vue
component will look like:
<!-- src/components/Navbar.vue (template) -->
<template>
<b-navbar toggleable="lg" type="dark" variant="primary">
<b-navbar-brand>VueJS vue-i18n</b-navbar-brand>
<b-navbar-toggle target="nav-collapse" />
<b-collapse id="nav-collapse" is-nav>
<b-navbar-nav>
<b-nav-item :to="{ name: 'Home' }">
{{ $t('navbar.home') }}
</b-nav-item>
<b-nav-item :to="{ name: 'About' }">
{{ $t('navbar.about') }}
</b-nav-item>
<b-nav-item :to="{ name: 'Contact' }">
{{ $t('navbar.contact') }}
</b-nav-item>
</b-navbar-nav>
<b-navbar-nav class="ml-auto">
<LanguageSwitcher />
</b-navbar-nav>
</b-collapse>
</b-navbar>
</template>
<!-- src/components/Navbar.vue (script) -->
<script>
import LanguageSwitcher from '@/components/LanguageSwitcher/LanguageSwitcher'
export default {
name: 'Navbar',
components: {
LanguageSwitcher
}
}
</script>
- We're going to create a
Layout.vue
component that will hold theNavbar
and will be used within the Views we are going to create next:
<!-- src/views/Layout.vue (template) -->
<template>
<b-row>
<b-col>
<Navbar />
<b-container>
<slot />
</b-container>
</b-col>
</b-row>
</template>
// src/views/Layout.vue (script)
<script>
import Navbar from '@/components/Navbar'
export default {
name: 'Layout',
components: {
Navbar
}
}
</script>
- For everything to work properly, we need to create the views, implement the
Layout
component into them and add them to therouter/index.js
file. In this section, the most important thing is to use the global$t
helper, provided by the Vue i18n package
src/components/Home.vue
<template>
<Layout>
<h1>{{ $t('navbar.home') }}</h1>
</Layout>
</template>
<script>
import Layout from './Layout'
export default {
name: 'HomeView',
components: {
Layout
}
}
</script>
src/components/About.vue
<template>
<Layout>
<h1>{{ $t('navbar.about') }}</h1>
</Layout>
</template>
<script>
import Layout from './Layout'
export default {
name: 'AboutView',
components: {
Layout
}
}
</script>
src/components/Contact.vue
<template>
<Layout>
<h1>{{ $t('navbar.contact') }}</h1>
</Layout>
</template>
<script>
import Layout from './Layout'
export default {
name: 'ContactView',
components: {
Layout
}
}
</script>
In order to use nested translation keys, the process is simple, since Vue I18n works with the full translation key path, like this:
<template>
<Layout>
<h1>{{ $t('navbar.links.contact.label') }}</h1>
</Layout>
</template>
src/router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Home',
component: () => import('@/views/Home')
},
{
path: '/about',
name: 'About',
component: () => import('@/views/About')
},
{
path: '/contact',
name: 'Contact',
component: () => import('@/views/Contact')
}
]
const router = new VueRouter({
mode: 'history',
routes
})
export default router
This is how the application should work after the full implementation:
VueJS Internationalization Sample Video
You can find the fully-working source code in this link!
Other features
Besides translating simple pieces of text, Vue I18n also provides other useful features like:
You may explore the website to find out more about the tool.
I hope you liked it.
Please, comment and share!
Cover image by Ben White
Posted on June 25, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.