Getting started
This page takes a Nuxt app from no i18n to a translated page with a language switcher. You need it once, at the start of a project. You end up with localized URLs (/about, /de/about), one JSON file per locale and page, and $t in your templates.
Install
The module needs Nuxt 3.18 or newer (Nuxt 4 works too) and Node 20 or newer.
npm install nuxt-i18n-micro
pnpm add nuxt-i18n-micro
yarn add nuxt-i18n-micro
bun add nuxt-i18n-micro
Configure the locales
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['nuxt-i18n-micro'],
i18n: {
locales: [
{ code: 'en', iso: 'en-US', displayName: 'English' },
{ code: 'de', iso: 'de-DE', displayName: 'Deutsch' },
{ code: 'fr', iso: 'fr-FR', displayName: 'Français' },
],
defaultLocale: 'en',
},
})
That is all the configuration a first app needs. The defaults give you:
strategy: 'prefix_except_default': the default locale has no prefix (/about), the others do (/de/about). See Routing strategies.translationDir: 'locales': translation files live inlocales/at the project root, also with the Nuxt 4app/layout.meta: true:hreflang, canonical andog:localetags, pluslanganddiron<html>. See SEO.- A visitor who opens
/is sent to the language their browser asks for (Accept-Language). See Locale detection.
iso is the language tag used in SEO tags; code is what appears in the URL. Every option is listed in Module options.
Add translation files
Start the dev server with nuxt dev. When Nuxt resolves the pages, the module creates an empty {} file for every locale and page that has none (turn this off with disableWatcher):
locales/
├── en.json # root: keys every page can use
├── de.json
├── fr.json
└── pages/
├── index/ # pages/index.vue
│ ├── en.json
│ ├── de.json
│ └── fr.json
└── about/ # pages/about.vue
├── en.json
├── de.json
└── fr.json
Fill in the root file and a page file:
// locales/en.json
{ "nav": { "home": "Home", "about": "About" } }
// locales/pages/index/en.json
{ "welcome": "Welcome, {name}!", "items": "no items | one item | {count} items" }
A page sees the root keys and its own keys; its own file wins on a clash. Folder names follow the route name (pages/articles/[id].vue is locales/pages/articles-id/). Translation files covers the layout, fallbacks and loading.
Use $t on a page
<!-- pages/index.vue -->
<script setup lang="ts">
const { t, tc, getLocale } = useI18n()
const title = t('nav.home') // 'Home'
</script>
<template>
<h1>{{ $t('welcome', { name: 'Ada' }) }}</h1>
<!-- Welcome, Ada! -->
<p>{{ $tc('items', 3) }}</p>
<!-- 3 items -->
<p>{{ getLocale() }}</p>
<!-- en -->
</template>
- In templates every helper is available with a
$($t,$tc,$localePath, …). - In
<script setup>,useI18n()returns the same helpers, with and without the$(tand$tare one function). It is auto-imported. {name}placeholders are filled from the params object;{0},{1}from an array ($t('pair', ['Ann', 'Bob'])with"pair": "{0} and {1}"). Plural forms are separated by|; the count picks the form (0, 1, then the last one for everything above).
All helpers are listed in Methods.
Link between pages and switch locale
<!-- components/AppHeader.vue -->
<template>
<nav>
<i18n-link :to="{ name: 'index' }">{{ $t('nav.home') }}</i18n-link>
<i18n-link :to="{ name: 'about' }">{{ $t('nav.about') }}</i18n-link>
<i18n-switcher />
</nav>
</template>
<i18n-link>is aNuxtLinkthat points at the page in the current locale: on/dethe about link goes to/de/about.<i18n-switcher>is a dropdown of the enabled locales; it keeps the visitor on the same page.- For your own buttons call
$switchLocale('de'), which navigates to the current page in German and remembers the choice. For a plainNuxtLink, use$localePath({ name: 'about' })or$switchLocalePath('de').
Check it
Run nuxt dev and open:
| URL | Shows |
|---|---|
/ | English home (or a redirect to /de when your browser prefers German) |
/de | German home |
/de/about | German about page |
/en/about | 404: the default locale has no prefix under prefix_except_default |
Edits to the JSON files apply in the open page without a reload.