Mastering Internationalization: A Guide to Localization in Vue Apps with Vue I18n
Introduction
Localization is the process of adapting your Application to the culture and language of users in a target market. Localization plays a huge role in making your website or service more appealing to the end users.
For any end user, nothing's more pleasing than experiencing the services in the local language hence Localisation is proven to add value to the application by boosting the user experience and engagement and plays a key role in gaining reach and credibility in the global market.
This article guides you through concepts of localisation in the Vue.js application using Vue I18n
one of the popular localisation libraries and how to implement useful features like lazy loading locales and getting the browser language preference of the user and persisting the user locale preference through building a demo Vue app.
This article deals with
Scaffolding a Vue project using Vue CLI.
Adding Vue I18n as a dependency in the project.
Creating constants for localisation setup.
Adding locales for supported languages.
Creating the i18n plugin and installing it globally in the Vue app.
Creating utils to handle all localisation-related operations.
Adding basic UI components for the demo app and adding localisation to the content.
Let's code!!!
Scaffolding a Vue project using Vue CLI
One of the effortless and efficient ways to scaffold a Vue application template is to use Vue CLI.
A basic Vue 3 object API template was used in this project
vue create vue-localization-demo
install Vue I18n
as a dependency
Vue I18n is one of the most popular and easy-to-integrate internationalization plugins available for Vue applications.
npm install vue-i18n
Creating constants for localisation setup.
Create constants
folder in src
and add localisationConstants.js
file with default, fallback and supported locales.
// src/constants/localisationConstants.js
export const DEFAULT_LANGUAGE = "en";
export const FALLBACK_LANGUAGE = "en";
export const SUPPORTED_LANGUAGES = ["en", "es"];
Adding locales for supported languages.
Create locales
folder in src
and add locales for all the supported languages.
Add file names as
<locale-code>.js
. example: en.json, es.json, etc.
// en.json
{
"heading": "Vue Localization Demo",
"subHeading": "What is localization?",
"demoText": "Localization is the adaptation of a product or service to meet the needs of a particular language, culture or desired population's 'look-and-feel.' A successfully localized service or product is one that appears to have been developed within the local culture."
}
// es.json
{
"heading": "Demostración de localización de Vue",
"subHeading": "¿Qué es la localización?",
"demoText": "La localización es la adaptación de un producto o servicio para satisfacer las necesidades de un idioma en particular, una cultura o la 'apariencia' deseada de la población. Un servicio o producto localizado con éxito es aquel que parece haber sido desarrollado dentro de la cultura local."
}
Creating the i18n plugin and installing it globally in the Vue app.
Create plugins
folder in src
and add i18n.js
file with the below content in it.
Static loading of all locales at the initial render of the app increases load time and is not optimal. So the best practice is to lazy load the locale messages i.e loading them as the user changes the locale.
// src/plugins/i18n.js
import { createI18n } from "vue-i18n/index";
// importing locale messages for en locale
import enMessages from "../locales/en.json";
import {
DEFAULT_LANGUAGE,
FALLBACK_LANGUAGE,
} from "../constants/localisationConstants.js";
// createI18n creates i18n plugin
const i18n = createI18n({
locale: DEFAULT_LANGUAGE, // set i18n locale
fallbackLocale: FALLBACK_LANGUAGE, // fallback locale
messages: { en: enMessages }, // set locale messages
});
export { i18n };
Visit docs to explore in detail all available config options for createI18n.
Now as our i18n
plugin is created, by using app.use()
function we can install it globally in the created Vue app and then it can be accessed as this.$i18n inside the components.
// main.js
import { createApp } from "vue";
import App from "./App.vue";
import { i18n } from "@/plugins/i18n";
// use function of createApp to register it globally.
createApp(App).use(i18n).mount("#app");
Creating utils to handle all localisation-related operations.
Create a utils
folder in src
and add translation.js
and paste the following code into it.
Here are a few useful functions to automate localisation operations. please go through the attached comments to understand what each function does.
// translation.js
import {
DEFAULT_LANGUAGE,
SUPPORTED_LANGUAGES,
} from "../constants/localisationConstants";
import { i18n } from "../plugins/i18n";
// By default english locale messages are loaded.
const loadedLocales = ["en"];
const i18nGlobal = i18n.global;
//* set current locale in i18n
function setCurrentLocale(locale) {
i18nGlobal.locale = locale;
}
//* Set i18n locale to default locale and loads its locale messages
export async function getDefaultLocaleAndLoadMessages() {
await loadLocaleMessages(DEFAULT_LANGUAGE);
setCurrentLocale(DEFAULT_LANGUAGE);
}
}
//* updates locale in i18n
function setI18nLocaleInServices(locale) {
setCurrentLocale(locale);
return locale;
}
//* loads locale messages based on the locale
async function loadLocaleMessages(locale) {
if (!isLocaleSupported(locale))
return Promise.reject(new Error("Locale not supported"));
if (!loadedLocales.includes(locale)) {
const msgs = await loadLocaleFile(locale);
i18nGlobal.setLocaleMessage(locale, msgs.default || msgs);
loadedLocales.push(locale);
}
}
//* handles the change in locale
export async function changeLocale(locale) {
if (!isLocaleSupported(locale))
return Promise.reject(new Error("Locale not supported"));
if (i18nGlobal.locale === locale) return Promise.resolve(locale);
if (!loadedLocales.includes(locale)) {
//* lazy loading locale messages
const msgs = await loadLocaleFile(locale);
i18nGlobal.setLocaleMessage(locale, msgs.default || msgs);
//* saving locale in loadedLocales
loadedLocales.push(locale);
}
//* setting current locale on i18n
return setI18nLocaleInServices(locale);
}
//* load the messages file based on locale
function loadLocaleFile(locale) {
return import(`../locales/${locale}.json`);
}
//* check if locale is supported
function isLocaleSupported(locale) {
return SUPPORTED_LANGUAGES.includes(locale);
}
To load the browser-preferred locale as the app reloads and to persist the user-preferred locale in local storage modify the translation.js
as shown below
// add getBrowserPrefLocale functions to the above code
// get user locale from browser language preference
function getBrowserPrefLocale() {
let locale =
window.navigator.language ||
window.navigator.userLanguage ||
DEFAULT_LANGUAGE;
return {
locale: locale,
localeNoISO: locale.split("-")[0],
};
}
// Modify the getDefaultLocaleAndLoadMessages function
// get browser preferred locale and load locale messages
export async function getDefaultLocaleAndLoadMessages() {
const userPreferredBrowserLocale = getBrowserPrefLocale();
// get the locale saved in the local storage
const localSavedLocale = localStorage.getItem("locale");
if (userPreferredBrowserLocale === DEFAULT_LANGUAGE) {
if (localSavedLocale === DEFAULT_LANGUAGE) return;
localStorage.setItem("locale", DEFAULT_LANGUAGE);
return;
}
let browserPrefLocale = "";
if (isLocaleSupported(userPreferredBrowserLocale.locale)) {
browserPrefLocale = userPreferredBrowserLocale.locale;
} else if (isLocaleSupported(userPreferredBrowserLocale.localeNoISO)) {
browserPrefLocale = userPreferredBrowserLocale.localeNoISO;
} else {
browserPrefLocale = DEFAULT_LANGUAGE;
}
//* retrieving any user saved local i.e local storage
//* if yes lazy load local saved locale messages
//* if no lazy load browser preferred language locale
if (!localSavedLocale) {
localStorage.setItem("locale", browserPrefLocale);
await loadLocaleMessages(browserPrefLocale);
setCurrentLocale(browserPrefLocale);
} else {
await loadLocaleMessages(localSavedLocale);
setCurrentLocale(localSavedLocale);
}
}
//* function to handle change in locale
export async function changeLocale(locale) {
...
...
if (i18nGlobal.locale === locale) return Promise.resolve(locale);
// persists the user preferred locale in local storage
localStorage.setItem("locale", locale);
...
...
}
Adding basic UI components for the demo app and adding localisation to the content
Now as our localisation plugins, utils and locales are ready it's time to create some UI components to implement localisation and I have added very minimally styled UI to demonstrate localisation.
Creating a language switcher component
Key concepts
$i18n.locale is used to get the current locale.
changeLocale is the util function to change the current locale.
Create components
folder in src
and add the LanguageSwitcher component to toggle between the supported locales.
<template>
<div>
<button
:style="
$i18n.locale === 'en'
? 'background:green; border-right:none; padding:2px; font-size:18px; color:white; width:100px; height:45px;'
: 'background:light-gray; border-right:none; padding:2px; font-size:18px; width:100px; height:45px;'
"
@click="handleChangeLocale('en')"
>
English
</button>
<button
:style="
$i18n.locale === 'es'
? 'background:green; border-left:none; padding:2px; font-size:18px; color:white; width:100px; height:45px;'
: 'background:light-gray; border-left:none; padding:2px; font-size:18px; width:100px; height:45px;'
"
@click="handleChangeLocale('es')"
>
Espanol
</button>
</div>
</template>
<script>
import { changeLocale } from "../utils/Translation";
export default {
name: "LocalisationDemo",
methods: {
// changing locale when language switcher is toggled
handleChangeLocale(newLocale) {
if (this.$i18n.locale !== newLocale) {
changeLocale(newLocale);
}
return;
},
},
};
</script>
Modify the app.js file to implement text localisation
Key concepts
Replace your static text with $t("locale-key-for-static-text") to implement text localisation and to learn more about different concepts of localisation like formatting, DateTime localisation, number localisation etc refer to docs
Add getDefaultLocaleAndLoadMessages() inside the created hook to set the default or user-preferred locale as the current locale and load the messages accordingly on initial render and reload.
Modify the App.js content to the below code.
// App.js
<template>
<div class="main_wrapper">
<div class="heading_wrapper">
<h1 style="color: green">{{ $t("heading") }}</h1>
<LanguageSwitcher />
</div>
<div>
<h2>{{ $t("subHeading") }}</h2>
<h3 class="localised_text">{{ $t("demoText") }}</h3>
</div>
</div>
</template>
<script>
import LanguageSwitcher from "./components/LanguageSwitcher.vue";
import { getDefaultLocaleAndLoadMessages } from "./utils/Translation";
export default {
name: "App",
components: {
LanguageSwitcher,
},
created() {
getDefaultLocaleAndLoadMessages();
},
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
display: flex;
justify-content: center;
align-items: center;
margin-top: 60px;
}
.main_wrapper {
width: 800px;
height: 600px;
padding: 20px;
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
box-shadow: 0.3rem 0.3rem 0.6rem #a1a1a1, -0.2rem -0.2rem 0.5rem #e8e8e8;
}
.heading_wrapper {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.localised_text {
line-height: 40px;
max-width: 700px;
color: #2c3e50;
}
</style>
Conclusion
Localization is In this article I tried to cover some basic concepts and best practices of localisation from scaffolding a Vue app to localising it and these util functions and folder structure provided are generic and can be used to localise your existing Vue projects.
You can access the complete code for the above demo from github. Feel free to contact me and drop your views and queries in the comments section below.
Hope you find this article useful. Thanks and happy learning!
Subscribe to my newsletter
Read articles from Takasi Venkata Sandeep directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Takasi Venkata Sandeep
Takasi Venkata Sandeep
A passionate developer who loves problem-solving and building interactive web and mobile applications. Always Open to try and explore new technologies.