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',},}); -
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) |
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) |
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 | Ref<boolean> | Whether translations are being loaded |
locales | readonly string[] | Configured locale code list |
defaultLocale | string | Configured default locale |
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>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/runtime/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.
HTML Language Attribute
Section titled “HTML Language Attribute”The module sets the <html lang> attribute automatically based on the active locale. For RTL locales, it also sets dir="rtl".
Hreflang Tags
Section titled “Hreflang Tags”Add hreflang meta tags so search engines discover all language versions of each page:
<script setup lang="ts">const { locale, locales } = useI18n();const switchLocalePath = useSwitchLocalePath();
useHead({ htmlAttrs: { lang: locale.value }, link: locales.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.