Nuxt i18n Integration
Comvi’s Nuxt module gives you auto-imported composables, locale-aware routing, lazy-loaded translations, and full SSR support with zero client-side flash.
Installation
Section titled “Installation”pnpm add @comvi/nuxt @comvi/plugin-fetch-loader-
Register the module
Add
@comvi/nuxtto your Nuxt config and provide your project settings:nuxt.config.ts export default defineNuxtConfig({modules: ['@comvi/nuxt'],comvi: {defaultLocale: 'en',locales: [{ code: 'en', name: 'English' },{ code: 'de', name: 'Deutsch' },{ code: 'fr', name: 'Francais' },],cdnUrl: 'https://cdn.comvi.io/your-distribution-id',},}); -
Create a setup file
Create
comvi.setup.tsat the project root to register plugins. The module auto-discovers this file:comvi.setup.ts import { defineComviSetup } from '@comvi/nuxt/setup';import { FetchLoader } from '@comvi/plugin-fetch-loader';export default defineComviSetup(({ i18n, runtimeConfig }) => {i18n.use(FetchLoader({cdnUrl: runtimeConfig?.public?.comvi?.cdnUrl,}));}); -
Use in components
All composables are auto-imported. No
importstatements needed:pages/index.vue <script setup lang="ts">const { t, locale } = useI18n();</script><template><h1>{{ t('home.title') }}</h1><p>{{ t('home.description', { name: 'Comvi' }) }}</p><p>Current locale: {{ locale }}</p></template>
That’s it. The module handles plugin registration, translation loading, and SSR hydration automatically.
Configuration Options
Section titled “Configuration Options”Configure the comvi key in nuxt.config.ts:
| Option | Type | Default | Description |
|---|---|---|---|
defaultLocale | string | — | Locale used when no other locale is detected (required) |
locales | (string | LocaleObject)[] | — | Available locales (required). Strings or objects (see format below) |
localePrefix | 'always' | 'as-needed' | 'never' | 'as-needed' | Controls whether URLs include a locale prefix |
cdnUrl | string | — | CDN URL for loading translations in production |
apiKey | string | — | API key for authenticated requests (dev mode). See security note below. |
apiBaseUrl | string | — | API base URL for loading translations |
defaultNs | string | 'default' | Default namespace for translations |
fallbackLanguage | string | string[] | Same as defaultLocale | Language(s) to use when a translation key is missing |
detectBrowserLanguage | object | false | See below | Browser language detection settings |
Locale Object Format
Section titled “Locale Object Format”Each item in the locales array accepts these properties:
| Property | Type | Required | Description |
|---|---|---|---|
code | string | Yes | BCP 47 language code (e.g., en, de, ar) |
name | string | No | Human-readable name (e.g., English, Deutsch) |
dir | 'ltr' | 'rtl' | No | Text direction. Defaults to 'ltr' |
iso | string | No | ISO code for SEO (e.g., en-US). Used in hreflang tags |
comvi: { locales: [ { code: 'en', name: 'English' }, { code: 'ar', name: 'Arabic', dir: 'rtl' }, { code: 'ja', name: 'Japanese' }, ],}Browser Language Detection
Section titled “Browser Language Detection”Control how the module detects the user’s preferred language on first visit:
comvi: { detectBrowserLanguage: { useCookie: true, cookieName: 'i18n_locale', redirectOnFirstVisit: true, },}| Property | Type | Default | Description |
|---|---|---|---|
useCookie | boolean | true | Persist detected locale in a cookie |
cookieName | string | 'i18n_locale' | Cookie name for persisted locale |
cookieMaxAge | number | 31536000 | Cookie max age in seconds (default: 1 year) |
cookieSecure | boolean | true | Set the Secure flag outside dev mode |
redirectOnFirstVisit | boolean | true | Redirect to detected locale on first visit |
fallbackLocale | string | defaultLocale | Locale when detection fails or returns an unsupported locale |
Set detectBrowserLanguage: false to disable automatic detection entirely.
Composables
Section titled “Composables”All composables are auto-imported by the Nuxt module. You never need to write import statements for them.
useI18n()
Section titled “useI18n()”The primary composable for translating strings and reading locale state.
<script setup lang="ts">const { t, locale, isLoading } = useI18n();</script>
<template> <div v-if="isLoading">Loading translations...</div> <div v-else> <h1>{{ t('page.title') }}</h1> <p>Current locale: {{ locale }}</p> </div></template>Returned values:
| Property | Type | Description |
|---|---|---|
t | (key, params?) => string | Translate a key with optional parameters |
locale | Ref<string> | Current language (reactive, writable) |
setLocale | (lang: string) => Promise<void> | Switch language and wait for translations to load |
isLoading | Readonly<Ref<boolean>> | Whether translations are being loaded |
dir | ComputedRef<"ltr" | "rtl"> | Text direction for the current locale. Useful for RTL layout |
locales | readonly string[] | Configured locale code list |
defaultLocale | string | Configured default locale |
See the Nuxt API reference for the full return type including all methods.
useLocalePath()
Section titled “useLocalePath()”Returns a function that generates locale-prefixed paths:
<script setup lang="ts">const localePath = useLocalePath();</script>
<template> <NuxtLink :to="localePath('/about')">About</NuxtLink> <!-- Renders /de/about when locale is "de" --></template>useSwitchLocalePath()
Section titled “useSwitchLocalePath()”Returns a function that generates the current page’s URL in a different locale:
<script setup lang="ts">const switchLocalePath = useSwitchLocalePath();</script>
<template> <nav> <NuxtLink :to="switchLocalePath('en')">English</NuxtLink> <NuxtLink :to="switchLocalePath('de')">Deutsch</NuxtLink> <NuxtLink :to="switchLocalePath('fr')">Francais</NuxtLink> </nav></template>useLocaleRoute()
Section titled “useLocaleRoute()”Returns a function that resolves a full route object for a given path and locale. Use this when you need the complete RouteLocationResolved object rather than just a path string:
<script setup lang="ts">const localeRoute = useLocaleRoute();
function goToAbout() { const route = localeRoute('/about', 'de'); if (route) { navigateTo(route.fullPath); }}</script>useLocaleHead()
Section titled “useLocaleHead()”Automatically sets the <html lang> and dir attributes, adds a canonical URL, alternate hreflang links for every configured locale (using ISO codes when set), and Open Graph locale meta tags. The cleanest way to handle all i18n SEO in one call:
<script setup lang="ts">useLocaleHead({ baseUrl: 'https://yoursite.com',});</script>All outputs are enabled by default and can be selectively disabled:
<script setup lang="ts">useLocaleHead({ baseUrl: 'https://yoursite.com', addOgLocale: true, // og:locale + og:locale:alternate addAlternateLinks: true, // hreflang links addCanonical: true, // canonical <link> addDir: true, // html dir attribute addLang: true, // html lang attribute});</script>useRouteConfig()
Section titled “useRouteConfig()”Returns resolved routing configuration and helpers. Particularly useful for sitemap generation:
export default defineEventHandler(() => { const { locales, getPathname } = useRouteConfig();
const pages = ['/', '/about', '/contact']; const urls = pages.flatMap((page) => locales.map((locale) => ({ loc: `https://example.com${getPathname({ locale, href: page })}`, })) );});See the Nuxt API reference for the full return type.
The $t() Global Helper
Section titled “The $t() Global Helper”In templates, you can also use the globally available $t() function without calling useI18n():
<template> <footer> <p>{{ $t('footer.copyright', { year: 2025 }) }}</p> </footer></template>The <T> Component
Section titled “The <T> Component”For translations containing HTML or Vue components, use <T> instead of t(). It renders safely without v-html:
<template> <!-- Translation: "Read our <link>terms of service</link>" --> <T i18nKey="legal.tos"> <template #link="{ children }"> <NuxtLink to="/terms">{{ children }}</NuxtLink> </template> </T></template>Locale Routing
Section titled “Locale Routing”Locale Prefix Modes
Section titled “Locale Prefix Modes”The localePrefix option controls how locales appear in URLs:
| Mode | Default Locale URL | Other Locale URL | Description |
|---|---|---|---|
as-needed | /about | /de/about | Default locale has no prefix |
always | /en/about | /de/about | All locales get a prefix |
never | /about | /about | No URL prefixes (locale set via cookie/header) |
comvi: { localePrefix: 'as-needed',}<NuxtLinkLocale>
Section titled “<NuxtLinkLocale>”A locale-aware replacement for <NuxtLink> that automatically prefixes the href with the current locale:
<template> <NuxtLinkLocale to="/about">About Us</NuxtLinkLocale> <!-- Renders <a href="/de/about"> when locale is "de" -->
<NuxtLinkLocale to="/contact" locale="fr">Contact (FR)</NuxtLinkLocale> <!-- Always renders <a href="/fr/contact"> --></template>Language Switcher
Section titled “Language Switcher”Build a complete language switcher with useSwitchLocalePath and the locale list:
<script setup lang="ts">const { locale, locales } = useI18n();const switchLocalePath = useSwitchLocalePath();</script>
<template> <select :value="locale" @change="navigateTo(switchLocalePath(($event.target as HTMLSelectElement).value))" > <option v-for="loc in locales" :key="loc" :value="loc" > {{ loc }} </option> </select></template>Lazy Loading
Section titled “Lazy Loading”Translations are loaded through the loaders you register on the i18n instance. Install @comvi/plugin-fetch-loader and register it in comvi.setup.ts to fetch from the Comvi CDN/API:
import { defineComviSetup } from '@comvi/nuxt/setup';import { FetchLoader } from '@comvi/plugin-fetch-loader';
export default defineComviSetup(({ i18n, runtimeConfig }) => { i18n.use(FetchLoader({ cdnUrl: runtimeConfig?.public?.comvi?.cdnUrl, }));});The module creates the Nuxt/Vue i18n instance and runs your setup hook before initialization. Server utilities use the same setup hook for request-scoped i18n instances.
Server-Side Rendering
Section titled “Server-Side Rendering”Translations can be fully rendered on the server when a loader is registered. The module handles the following automatically:
- Detects the locale from the URL, cookie, or
Accept-Languageheader - Runs your
comvi.setuphook for server-side i18n instances - Hydrates the client with the loaded translations (no duplicate fetch)
- Sets the
langanddirattributes on<html>
Register a loader in comvi.setup.ts so SSR helpers can load missing translations.
Automatic SEO Tags with useLocaleHead
Section titled “Automatic SEO Tags with useLocaleHead”The easiest way to handle all i18n SEO is useLocaleHead(). It automatically manages:
<html lang>anddirattributes- Canonical URL
hreflangalternate links for every locale (usingisocodes when configured on locale objects)og:localeandog:locale:alternateOpen Graph tags
<script setup lang="ts">useLocaleHead({ baseUrl: 'https://yoursite.com' });</script>Manual SEO Tags
Section titled “Manual SEO Tags”If you need to control individual tags, build them manually using useSwitchLocalePath and useHead:
<script setup lang="ts">const { locale, locales } = useI18n();const switchLocalePath = useSwitchLocalePath();
useHead({ htmlAttrs: { lang: locale.value }, link: locales.value.map((loc) => ({ rel: 'alternate', hreflang: loc, href: `https://yoursite.com${switchLocalePath(loc)}`, })),});</script>TypeScript
Section titled “TypeScript”Enable type-safe translations with auto-generated types:
comvi: { // Type generation picks up your default locale's keys defaultLocale: 'en',}<script setup lang="ts">const { t } = useI18n();
t('home.title'); // Autocompletest('nonexistent.key'); // Type error</script>See Type-Safe Translations for the full setup, including the CLI command to generate types.