comvi
@comvi/nuxt

i18n with type-checked keys, ICU plurals and a <T> component for markup inside translations.

Comvi

@comvi/nuxt

Nuxt module for Comvi i18n: auto-imports, SSR, and locale-prefixed routes.

npmBundle sizeLicense: MIT


@comvi/nuxt is a Nuxt module: it auto-imports the composables and <T>, clones your pages into locale-prefixed routes, builds one i18n instance per request on the server and hydrates the client from what the server loaded. The full guide is Nuxt on the docs site.

Install

npm install @comvi/nuxt
# Peer: nuxt ^3.0.0 || ^4.0.0

Setup

Add the module and declare the locales, in nuxt.config.ts:

import { defineNuxtConfig } from "nuxt/config";

export default defineNuxtConfig({
  modules: ["@comvi/nuxt"],
  comvi: {
    defaultLocale: "en",
    locales: ["en", "uk"],
    messages: {
      en: { greeting: "Hello, {name}!" },
      uk: { greeting: "Привіт, {name}!" },
    },
  },
});

Nuxt renders the pages through an outlet, in app/app.vue:

<template>
  <NuxtPage />
</template>

comvi.messages is the inline catalog, nested or flat, and must stay JSON-serializable. The module writes <html lang dir> from the active locale on the server and on every switch.

Use in components

In a page, app/pages/index.vue:

<script setup lang="ts">
const { t, locale } = useI18n();
</script>

<template>
  <h1>{{ t("greeting", { name: "Alice" }) }}</h1>
  <NuxtLinkLocale to="/" :locale="locale === 'en' ? 'uk' : 'en'">Switch</NuxtLinkLocale>
</template>

useI18n() is auto-imported. It returns the @comvi/vue composable plus locales and defaultLocale from runtime config. setLocale() loads the new catalog and advances the locale state the route middleware watches. $t() works in any template without calling useI18n().

Load from Comvi

npm install @comvi/plugin-fetch-loader

Compose the loader in comvi.i18n.ts:

import { createI18n, defineComviI18n } from "@comvi/nuxt/i18n";
import { cdnLoader } from "@comvi/plugin-fetch-loader";

export default defineComviI18n((options) =>
  createI18n(options).use(cdnLoader({ cdnUrl: "https://cdn.comvi.io/<project-token>" })),
);

Add this file next to nuxt.config.ts. The module picks up ./comvi.i18n.* by convention and logs the path; comvi.i18nModule points at a file elsewhere. The default export receives the resolved comvi options and must return a new instance from createI18n on every call; in development the module throws when it gets the same instance twice. The CDN URL is on Connect to Comvi. If your keys use ICU ({count, plural, …}), set comvi.icu: true or pass compiler: icuCompiler in the factory, as on Plurals and ICU; the platform and the compiler are independent.

Specific to Nuxt

  • The module builds one i18n instance per request, detects the locale from the URL path, the query parameter, the cookie or the Accept-Language header, runs comvi.setup.ts for that instance, and serializes the loaded catalog into the payload, so the client hydrates with no second fetch and no flash.
  • Without a comvi.i18n.ts file the module builds the base instance, with no loader and no ICU compiler. Every installer comvi.i18n.ts needs (createI18n, loader, icuCompiler, plugins, devtools) comes from @comvi/nuxt/i18n, so the file resolves from npm install @comvi/nuxt alone.
  • In a Nitro route the server auto-imports are useTranslation(event), loadMessages(event, locale), getRequestI18n(event, locale) and getServerRuntimeConfig(event); the app-side composables do not exist there. useTranslation(event) resolves the locale in the route middleware's order: the path prefix, then localeDetection.order.
  • <T> is auto-imported. Each tag a translator wrote becomes a named slot, or an entry in the components prop. Never render a translation with v-html.
localePrefixDefault localeOther locale
as-needed/about/de/about
always/en/about/de/about
never/about/about, locale from query, cookie or header

The route middleware detects the locale, persists it in the i18n_locale cookie and pre-loads that locale's catalog before the page renders. Pair a switcher with useSwitchLocalePath() when the URL should change too.

Two options control it, with the same names, values and defaults as @comvi/next's middleware except the cookie name. localeCookie takes a name, { name, maxAge, secure, sameSite, domain }, or false to persist nothing. localeDetection takes { order, queryParam, headerName, resolveAcceptLanguage }, or false to leave the URL path alone in charge. comvi.ns lists the namespaces to load besides defaultNs, as core's ns does.

Advanced

Set comvi.head: false to write the <html lang dir> attributes yourself, and call useLocaleHead() for canonical, hreflang and og:locale tags.

Next steps

License

MIT © Comvi