How to Globalize Your Vue.js App with i18n: A Step-by-Step Guide

Sahil AhmedSahil Ahmed
7 min read

Introduction: The Power of i18n Across Frameworks

Internationalization (i18n) is a crucial aspect of modern web development, allowing applications to adapt to various languages and regions seamlessly. The key aspects of i18n include :
Locale detection: Identifying the user's locale (language and region) to tailor the application's content accordingly.

Text translation: Managing translations for all text elements in the application, including user interface elements, messages, and labels.

Formatting: Displaying numbers, dates, times, and currencies in a format appropriate for the user's locale.

The importance of i18n with local languages cannot be overstated. It allows your application to:

  1. Reach a broader audience

  2. Improve user experience for non-native speakers

  3. Comply with local regulations and cultural norms

  4. Increase user engagement and retention

While i18n can be implemented in various frameworks like React, Angular, or vanilla JavaScript, today we'll dive deep into vue-i18n, a plugin specifically focusing on Vue 3 with the Composition API and <script setup> method, an area where comprehensive tutorials are surprisingly scarce online.

The Core of i18n: Key-Value Pairs and Language Switching

At its heart, i18n in Vue.js revolves around two main concepts:

  1. Key-Value Pairs: We define translation strings as key-value pairs, where the key remains constant across languages, and the value changes based on the selected locale.

  2. Language Switching: We implement a mechanism to change the active locale, causing our application to re-render with the new language.

Let's see how this looks in practice:

// en.json
{
  "greeting": "Hello, world!"
}

// hi.json
{
  "greeting": "नमस्ते, दुनिया!"
}

In this example, we have translations for English and Hindi. The key greeting remains the same, but the value changes for each language.

Setting Up Vue i18n in Your Project

Step 1: Installing Dependencies

First, we need to install the necessary packages:

npm install vue-i18n@9 @intlify/unplugin-vue-i18n

Vue I18n is the core library for internationalization in Vue.js applications. The @intlify/unplugin-vue-i18n is a Vite plugin that helps with the automatic importing of locale messages.

Step 2 : Plugin Registration

Now, let's configure Vue i18n in our project:

// main.ts
import { createApp } from "vue";
import { createPinia } from "pinia";
import App from "./App.vue";
import i18n from "./i18n";

const app = createApp(App);
app.use(createPinia()).use(i18n).mount("#app");

Step 3: Configuring Vite

Next, we configure Vite to work with our i18n setup:

import VueI18nPlugin from "@intlify/unplugin-vue-i18n/vite";
import path from "path";

export default defineConfig({
  // ... other configurations
  plugins: [
    // ... other plugins
    VueI18nPlugin({
      runtimeOnly: false,
      include: path.resolve(__dirname, "src/i18n/locales/**"),
    }),
  ],
  // ... other configurations
});

This configuration tells Vite to use the Vue I18n plugin and where to find our locale files. The runtimeOnly: false option allows us to use the full version of Vue I18n, which includes the compiler. This is necessary if we want to use custom formatting or pluralization rules.

Step 4: Setting Up Environment Variables

Create a .env file in your project root:

VITE_DEFAULT_LOCALE=en
VITE_FALLBACK_LOCALE=en
VITE_SUPPORTED_LOCALES=en,hindi,kannada,telugu,marathi,punjabi,tamil,malyalam,gujrati,bengali,odia,urdu

These variables define our default locale, fallback locale, and all supported locales. Using environment variables allows us to easily change these settings without modifying our code.

Step 5: Creating Locale JSON Files

For each supported language, create a JSON file in the src/i18n/locales/ directory. For example, en.json for English:

{
  "my_profile": {
    "first_name": "First Name",
    "last_name": "Last Name",
    "mobile_number": "Mobile Number",
    "email": "Email",
    "change_language": "Change Language",
    "alert": "Language changed successfully!"
  },
  "locale": {
    "en": "English",
    "hindi": "Hindi",
    "kannada": "Kannada"
    // ... other languages
  }
}

To effectively manage translations in your Vue.js application, create separate .json files for each supported language or locale. Each file should be structured as an object, where nested objects may represent each page within your app. Inside these nested objects, define key-value pairs for each translatable text. The nested structure allows for better organization and maintainability of translations. For higher accuracy, consider using professional third-party translators instead of relying on automated tools like Google Translate or AI.

Step 6: Initializing i18n

Create an index.ts file in your src/i18n/ directory:

import { createI18n } from "vue-i18n";
import en from "./locales/en.json";

export default createI18n({
  locale: import.meta.env.VITE_DEFAULT_LOCALE,
  fallbackLocale: import.meta.env.VITE_FALLBACK_LOCALE,
  legacy: false,
  globalInjection: true,
  messages: { en },
});

This sets up our i18n instance with the default locale and fallback locale from our environment variables. The legacy: false the option enables the Composition API style, and globalInjection: true allows us to use the $t function in our templates.

Step 7: Creating a Translation Utility

Create a translate.ts file in your src/i18n/ directory:

import i18n from "@/i18n";
import { nextTick } from "vue";

const Trans = {
  get defaultLocale() {
    return import.meta.env.VITE_DEFAULT_LOCALE;
  },

  get supportedLocales() {
    return import.meta.env.VITE_SUPPORTED_LOCALES.split(",");
  },

  set currentLocale(newLocale: string) {
    i18n.global.locale.value = newLocale;
  },

  getUserLocale() {
    const locale = window.navigator.language || Trans.defaultLocale;
    return {
      locale: locale,
      localeNoRegion: locale.split("-")[0],
    };
  },

  getPersistedLocale() {
    const persistedLocale = localStorage.getItem("user-locale");
    if (Trans.isLocaleSupported(persistedLocale)) {
      return persistedLocale;
    } else {
      return null;
    }
  },

  guessDefaultLocale() {
    const userPersistedLocale = Trans.getPersistedLocale();
    if (userPersistedLocale) {
      return userPersistedLocale;
    }
    const userPreferredLocale = Trans.getUserLocale();
    if (Trans.isLocaleSupported(userPreferredLocale.locale)) {
      return userPreferredLocale.locale;
    }
    if (Trans.isLocaleSupported(userPreferredLocale.localeNoRegion)) {
      return userPreferredLocale.localeNoRegion;
    }

    return Trans.defaultLocale;
  },

  isLocaleSupported(locale: string | null) {
    return Trans.supportedLocales.includes(locale);
  },

  async switchLanguage(newLocale: string) {
    await Trans.loadLocaleMessages(newLocale);
    Trans.currentLocale = newLocale;
    document.querySelector("html")!.setAttribute("lang", newLocale);
    localStorage.setItem("user-locale", newLocale);
  },

  async loadLocaleMessages(locale: string) {
    if (!i18n.global.availableLocales.includes(locale)) {
      const messages = await import(`@/i18n/locales/${locale}.json`);
      i18n.global.setLocaleMessage(locale, messages.default);
    }
    return nextTick();
  },
};

export default Trans;

This utility provides methods for switching languages, loading locale messages, and other helpful functions. It includes logic for guessing the user's preferred locale based on their browser settings and previously selected language.

Step 8: Using i18n in Your Components

Now, let's see how to use i18n in a Vue component. Here's an example from the Profile.vue component:

<script setup lang="ts">
import { useI18n } from "vue-i18n";
import Tr from "@/i18n/translation";

const { t, locale } = useI18n({ useScope: "global" });
const supportedLocales = Tr.supportedLocales;

const switchLanguage = async (event: Event) => {
  alert(t("my_profile.alert"));
  const newLocale = (event.target as HTMLOptionElement).value;
  await Tr.switchLanguage(newLocale);
};
</script>

<template>
  <div class="form-control w-full">
    <label class="text-xs font-semibold text-gray-500">
      {{ $t("my_profile.first_name") }}
      <input
        v-model="userStore.userProfile.first_name"
        type="text"
        :placeholder="userStore.userProfile.first_name"
        class="input text-sm font-normal"
        disabled
      />
    </label>
  </div>

  <div class="flex flex-col w-full">
    <span class="text-xs font-semibold text-gray-500">{{
      $t("my_profile.change_language")
    }}</span>
    <select
      @change="switchLanguage"
      class="input rounded-md border focus:ring focus:ring-opacity-50 text-sm"
    >
      <option
        v-for="sLocale in supportedLocales"
        :key="`locale-${sLocale}`"
        :value="sLocale"
        :selected="locale === sLocale"
      >
        {{ t(`locale.${sLocale}`) }}
      </option>
    </select>
  </div>
</template>

In this component, we're using the $t function in the template to translate our keys, and the t function in the script for the same purpose. We've also created a custom dropdown to switch between languages.

This setup provides a flexible and maintainable internationalization system that can grow with your application. It allows for easy addition of new languages and provides a smooth user experience for switching between languages.

In this example, we're using the useI18n composable to access translation functions and the current locale. We also implement a language switcher that allows users to change the application's language dynamically.

Managing Translations with BabelEdit

While we've covered the basics of implementing i18n in your Vue 3 app, managing translations across multiple languages can become challenging as your app grows. This is where tools like BabelEdit can be incredibly helpful.

BabelEdit is a powerful translation editor for web apps that supports Vue I18n out of the box. It provides a user-friendly interface for managing your translation files, making it easier to keep your translations organized and up-to-date.

BabelEdit

Some key features of BabelEdit include:

  1. Visual editing of JSON and YAML translation files

  2. Side-by-side comparison of translations

  3. Auto-translation suggestions

  4. Search and filter functionality

  5. Export to various formats

Refer to the BabelEdit documentation to learn how to use it.

Alternatives

While Vue I18n is a popular choice for internationalization in Vue applications, there are other alternatives you might consider for other frameworks:

  1. react-i18next: If you're working with React

  2. Globalize: A JavaScript library for internationalization and localization

  3. FormatJS: A modular collection of JavaScript libraries for internationalization

  4. Google Translate: While not a full i18n solution, it can be used for quick, automated translations

Each of these libraries has its own strengths and might be more suitable depending on your specific project requirements.

Remember, the key to successful internationalization is planning for it from the start of your project. It's much easier to build with i18n in mind than to retrofit it into an existing application.

TLDR:

In this article, we've explored how to implement i18n in a Vue.js application using Vue 3's Composition API with script setup. We've covered:

  1. The basic setup of vue-i18n

  2. Using translations in both templates and the Composition API's script setup

  3. Managing translation files with BabelEdit

  4. Suggesting alternatives to i18n

Resources and further reading

For more in-depth information on Vue i18n and advanced use cases like Datetime, currencies, pluralisation, number localization, interpolation and its integration with Vue Router for multiple locales, check out the official Vue i18n documentation or this Lokalise blog post.

0
Subscribe to my newsletter

Read articles from Sahil Ahmed directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Sahil Ahmed
Sahil Ahmed

I'm Sahil Ahmed, an experienced software engineer who wants to be a digital polymath. My journey in software development is driven by a curiosity to create efficient, user-centric solutions for real-world problems across web and mobile platforms while writing clean code. I also love to read classic literature and play football.