Setting Up Internationalization (i18n) in Nuxt 3

Note: This guide is based on the current best practices and tools available for Nuxt 3 as of my last update. Always refer to the official Nuxt 3 and @nuxtjs/i18n documentation for the most up-to-date practices and features.

Internationalization (i18n) is a critical feature for modern web applications aiming to reach a global audience. It involves translating your app into multiple languages and adjusting it according to cultural nuances. Nuxt 3, a popular Vue.js framework, provides a straightforward approach to setting up i18n. Here's a comprehensive guide to implementing i18n in your Nuxt 3 project.

Quick Start

  1. Install @nuxtjs/i18n dependency to your project:
npm i -D @nuxtjs/i18n
  1. Add the following code to your nuxt.config.ts file:
nuxt.config.ts
export default defineNuxtConfig({
  modules: [
    '@nuxtjs/i18n',
  ],
  i18n: {
    /* module options */
  }
})

Configuring the Module (Options)

If you are a beginner You might need to get familiar with some definitions before we start:

  • by route we mean the URL of the page, for example, https://example.com/ https://example.com/about etc.
  • by locale we mean the language of the page, for example, en fr de etc. it can also be a combination of language and country, for example, en-US fr-CA de-DE etc.
  • by prefix we mean the locale prefix in the route https://example.com/<prefix>, for example, https://example.com/en https://example.com/fr https://example.com/de etc.

Strategies

Depending on your project's needs, you can choose from 4 different i18n strategies:

  1. No prefix - This strategy is best suited for SPA or client-side only applications that SEO is not a concern. It is the simplest strategy to implement, but it is not recommended for SEO purposes.
  2. Prefix_except_default - Using this strategy, all of your routes will have a locale prefix added except for the default language. Perfect choice for SEO and also you urls look good for your main users.
  3. Prefix - This strategy is similar to the previous one, but it adds a locale prefix to all routes, including the default language.
  4. prefix_and_default - This strategy combines both previous strategies behaviours, meaning that you will get URLs with prefixes for every language, but URLs for the default language will also have a non-prefixed version (though the prefixed version will be preferred when detectBrowserLanguage is enabled).

In our example we will use the prefix_except_default strategy and set defaultLocale to en.

nuxt.config.ts
export default defineNuxtConfig({
  modules: [
    '@nuxtjs/i18n',
  ],
  i18n: {
    strategy: 'prefix_except_default',
    defaultLocale: 'en'
  }
})

Locales, locales directory, lazy loading and auto detect browser language

The locales option is an array of objects that contains the locales you want to support in your application. Each locale object must have a code property that represents the locale code, file property that represents the locale file in your locale folder and a iso property that represents the locale ISO code. The iso property is optional, but it is recommended to use it as it is used by some features like detectBrowserLanguage.

nuxt.config.ts
export default defineNuxtConfig({
  modules: [
    '@nuxtjs/i18n',
  ],
  i18n: {
    strategy: 'prefix_except_default',
    defaultLocale: 'en',
    lazy: true,
    langDir: 'locales',
    baseUrl: 'https://my-awsome-website.com',
    locales: [
      { code: 'bg', name: 'Bulgarian', iso: 'bg-BG', file: 'bg.json' },
      { code: 'cs', name: 'Czech', iso: 'cs-CZ', file: 'cs.json' },
      { code: 'da', name: 'Danish', iso: 'da-DK', file: 'da.json' },
      { code: 'de', name: 'German', iso: 'de', file: 'de.json' },
      { code: 'el', name: 'Greek', iso: 'el-GR', file: 'el.json' },
      { code: 'en', name: 'English', iso: 'en', file: 'en.json' },
      { code: 'es', name: 'Spanish', iso: 'es-ES', file: 'es.json' },
      { code: 'et', name: 'Estonian', iso: 'et-EE', file: 'et.json' },
      { code: 'fi', name: 'Finnish', iso: 'fi-FI', file: 'fi.json' },
      { code: 'fr', name: 'French', iso: 'fr', file: 'fr.json' },
      { code: 'hu', name: 'Hungarian', iso: 'hu-HU', file: 'hu.json' },
      { code: 'id', name: 'Indonesian', iso: 'id-ID', file: 'id.json' },
      { code: 'it', name: 'Italian', iso: 'it-IT', file: 'it.json' },
      { code: 'ja', name: 'Japanese', iso: 'ja-JP', file: 'ja.json' },
      { code: 'ko', name: 'Korean', iso: 'ko-KR', file: 'ko.json' },
      { code: 'lt', name: 'Lithuanian', iso: 'lt-LT', file: 'lt.json' },
      { code: 'lv', name: 'Latvian', iso: 'lv-LV', file: 'lv.json' },
      { code: 'nb', name: 'Norwegian Bokmål', iso: 'nb-NO', file: 'nb.json' },
      { code: 'nl', name: 'Dutch', iso: 'nl-NL', file: 'nl.json' },
      { code: 'pl', name: 'Polish', iso: 'pl-PL', file: 'pl.json' },
      { code: 'pt-br', name: 'Portuguese (Brazil)', iso: 'pt-BR', file: 'pt-br.json' },
      { code: 'pt-pt', name: 'Portuguese (Portugal)', iso: 'pt-PT', file: 'pt-pt.json' },
      { code: 'ro', name: 'Romanian', iso: 'ro-RO', file: 'ro.json' },
      { code: 'ru', name: 'Russian', iso: 'ru-RU', file: 'ru.json' },
      { code: 'sk', name: 'Slovak', iso: 'sk-SK', file: 'sk.json' },
      { code: 'sl', name: 'Slovenian', iso: 'sl-SI', file: 'sl.json' },
      { code: 'sv', name: 'Swedish', iso: 'sv-SE', file: 'sv.json' },
      { code: 'tr', name: 'Turkish', iso: 'tr-TR', file: 'tr.json' },
      { code: 'uk', name: 'Ukrainian', iso: 'uk-UA', file: 'uk.json' },
      { code: 'zh', name: 'Chinese', iso: 'zh-CN', file: 'zh.json' },
    ],
    detectBrowserLanguage: {
      useCookie: true,
    },
    vueI18n: './i18n.config.ts',
  }
})

i18n config file

The vueI18n option is the path to the file that contains the configuration for the vue-i18n library. You can also pass the configuration directly to the vueI18n option, but it is recommended to use a separate file for the configuration. create a file named i18n.config.ts in the root of your project and add the following code to it:

i18n.config.ts
export default defineI18nConfig(() => ({
  legacy: false,
  lazy: true,
  strategy: 'prefix_except_default',
  langDir: 'locales',
  defaultLocale: 'en',
}))

Locale files

The langDir option is the path to the directory that contains the locale files. The locale files must be named with the locale code and the .json extension, for example, en.json, fr.json, de.json etc. The locale files must be placed in the langDir directory, for example, locales/en.json, locales/fr.json, locales/de.json etc. The locale files must be valid JSON files and must contain a JSON object with the translations for the locale. The keys of the JSON object must be the translation keys and the values must be the translations. The locale files must be valid JSON files and must contain a JSON object with the translations for the locale. The keys of the JSON object must be the translation keys and the values must be the translations.

Here is an example of a locale file for the en locale:

locales/en.json
{
  "hello": "Hello World!"
}

and here is an example of a locale file for the fr locale:

locales/fr.json
{
  "hello": "Bonjour le monde!"
}

Using Translations in Your App

Thanks to the @nuxtjs/i18n module, you can easily use translations in your app. The module injects the $t function into the context of your app, which you can use to translate your app. Here is an example of using the $t function in your app:

pages/index.vue

<template>
  <div>
    <h1>{{ $t('hello') }}</h1>
  </div>
</template>

You can use $t anywhere whithin your template tags, including in your components, pages, layouts, etc.

Happy coding!