Localize a Nuxt 3 app: complete guide with Localingos
Nuxt 3 with @nuxtjs/i18n is the cleanest server-rendered Vue i18n stack available — locale routing, SEO meta generation, and hreflang annotations all out of the box. This guide combines it with Localingos for the translation pipeline. About 15 minutes start to finish.
Step 1 — Install
npm install @nuxtjs/i18n
npm install -g @localingos/cli
localingos login
Step 2 — Configure @nuxtjs/i18n
nuxt.config.ts:
export default defineNuxtConfig({
modules: ['@nuxtjs/i18n'],
i18n: {
defaultLocale: 'en',
strategy: 'prefix_except_default', // English at /, others at /es/, /de/...
locales: [
{ code: 'en', iso: 'en-US', file: 'en.json' },
{ code: 'es', iso: 'es-ES', file: 'es.json' },
{ code: 'de', iso: 'de-DE', file: 'de.json' },
{ code: 'fr', iso: 'fr-FR', file: 'fr.json' },
{ code: 'ja', iso: 'ja-JP', file: 'ja.json' },
],
langDir: 'i18n/locales/',
lazy: true,
detectBrowserLanguage: {
useCookie: true,
cookieKey: 'i18n_locale',
fallbackLocale: 'en',
redirectOn: 'root', // only auto-redirect on /, not deep links (SEO-safe)
},
},
});
Three things to understand here:
strategy: 'prefix_except_default'— English at/pricing, Spanish at/es/pricing. The recommended setup for SEO.lazy: true— each locale's JSON is fetched on demand. Only the active locale ships in the initial bundle.redirectOn: 'root'— auto-redirects first-time visitors only on the homepage, not on deep links. Critical: deep-link redirects break crawlers and SEO.
Step 3 — Source of truth
i18n/locales/en.json:
{
"welcome": "Welcome, {name}",
"cart": "no items in cart | one item in cart | {count} items in cart"
}
Vue's | pluralization syntax.
Step 4 — Configure Localingos
localingos.json:
{
"projectId": "your-project-id",
"source": { "path": "i18n/locales/en.json", "locale": "en" },
"targets": {
"path": "i18n/locales/{locale}.json",
"locales": ["es", "de", "fr", "ja", "pt-BR", "zh-CN", "ko"]
},
"placeholders": ["{name}", "{count}"]
}
localingos sync
Step 5 — Use in components
<script setup lang="ts">
const { t } = useI18n();
defineProps<{ userName: string; itemCount: number }>();
</script>
<template>
<header>
<h1>{{ t('welcome', { name: userName }) }}</h1>
<p>{{ t('cart', itemCount) }}</p>
</header>
</template>
Step 6 — Locale-aware links
Use Nuxt's built-in helper to generate URLs in the active locale:
<template>
<NuxtLinkLocale to="/pricing">{{ t('nav.pricing') }}</NuxtLinkLocale>
</template>
When the active locale is es, this generates /es/pricing. When it's en, /pricing. Saves manual URL construction.
Step 7 — SEO meta and hreflang
Nuxt i18n generates hreflang automatically when you use useLocaleHead:
<script setup lang="ts">
const head = useLocaleHead({ addSeoAttributes: true });
useHead(head);
</script>
This injects per-page <link rel="alternate" hreflang="..."> for every configured locale and a <link rel="canonical">. Google sees the right localized variant and treats /pricing as canonical with /es/pricing as an alternate.
Step 8 — Language switcher
<script setup lang="ts">
const { locale, locales, setLocale } = useI18n();
const switchLocaleTo = (newLocale: string) => setLocale(newLocale);
</script>
<template>
<select :value="locale" @change="e => switchLocaleTo((e.target as HTMLSelectElement).value)">
<option v-for="l in locales" :key="l.code" :value="l.code">{{ l.code.toUpperCase() }}</option>
</select>
</template>
setLocale() updates the URL to the locale-prefixed path automatically — /pricing → /es/pricing. Plays nicely with browser back/forward.
Step 9 — Automate sync in CI
# .github/workflows/i18n.yml
name: i18n-sync
on:
push: { branches: [main], paths: ['i18n/locales/en.json'] }
jobs:
sync:
runs-on: ubuntu-latest
permissions: { contents: write, pull-requests: write }
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: npm install -g @localingos/cli
- run: localingos sync
env: { LOCALINGOS_API_KEY: '${{ secrets.LOCALINGOS_API_KEY }}' }
- uses: peter-evans/create-pull-request@v6
with:
branch: i18n/auto-sync
title: 'chore(i18n): sync translations'
commit-message: 'chore(i18n): sync translations'
Production checklist
- Static generation (
nuxt generate). Each locale gets its own pre-rendered HTML — no extra work needed. - Server-side rendering. Locale detected from cookie/header before render; no hydration mismatch.
- Sitemap. Use
@nuxtjs/sitemapalongside i18n — it auto-generates per-locale entries with hreflang. - RTL. Set
dirinuseLocaleHead({ addDirAttribute: true }).
Wrap up
A Nuxt 3 app with locale routing, SEO meta, hreflang, and a CI translation pipeline — all in about 15 minutes. The prefix_except_default strategy plus auto-generated alternates is the most SEO-friendly setup available for a Vue stack.
Free tier covers typical Nuxt site string corpora end to end.