Frameworks
Nuxt
Nuxt applications can use Messagevisor in server-rendered pages, Nitro API routes, and client-side plugins.
Install#
$ npm install @messagevisor/sdk @messagevisor/vue @messagevisor/module-icuServer utility#
Create a server-only loader that reads all supported locale datafiles into one SDK instance:
Copy the shared datafile loader beside this helper. It checks HTTP status, retries initial failures, and retains the last known good datafiles during refresh failures.
import { createMessagevisorLoader } from "./messagevisor-loader.js";const loader = createMessagevisorLoader();export const getMessagevisor = loader.get;export const refreshMessagevisor = loader.refresh;Nitro API routes pass locale per call. Do not call setLocale() or setContext() while handling requests.
Server-rendered page#
Nuxt pages can call a server API route during SSR and reuse the result on the client:
<script setup lang="ts">const route = useRoute();const locale = String(route.params.locale || "en-US");const { data } = await useAsyncData(`welcome-${locale}`, () => $fetch("/api/welcome", { query: { locale, plan: "pro", }, }));</script><template> <h1>{{ data?.message }}</h1></template>API routes#
Nitro server routes can return translated JSON:
export default defineEventHandler(async (event) => { const query = getQuery(event); const locale = String(query.locale || "en-US"); const plan = typeof query.plan === "string" ? query.plan : undefined; const m = await getMessagevisor(); return { message: m.translate( "dashboard.welcome", { name: "Ada" }, { locale, context: { platform: "web", plan } } ), };});Client plugin#
For browser-side evaluation, serve built datafiles from public/datafiles and create a Nuxt plugin. The browser has one active user, so keep locale at instance level:
import { createMessagevisor } from "@messagevisor/sdk";import { createICUModule } from "@messagevisor/module-icu";import { createMessagevisorProvider } from "@messagevisor/vue";export default defineNuxtPlugin(async (nuxtApp) => { const locale = "en-US"; const datafile = await $fetch(`/datafiles/messagevisor-web-${locale}.json`); const m = createMessagevisor({ datafile, locale, modules: [createICUModule({ ignoreTags: false })], }); nuxtApp.vueApp.use(createMessagevisorProvider({ instance: m })); return { provide: { messagevisor: m, }, };});This plugin runs only in the browser. Put components that consume it inside a ClientOnly boundary, with a fallback for server rendering and initial loading:
<template> <ClientOnly fallback-tag="span" fallback="Loading translations…"> <SignInButton /> </ClientOnly></template>Then use the injected instance inside that child component:
<script setup lang="ts">const { $messagevisor } = useNuxtApp();const label = $messagevisor.translate("auth.signin");</script><template> <button>{{ label }}</button></template>Alternatively, implement the same child component using the Vue composable:
<script setup lang="ts">import { useTranslation } from "@messagevisor/vue";const label = useTranslation("auth.signin");</script><template> <button>{{ label }}</button></template>If the user can switch locales in the browser, fetch the new datafile, call setDatafile(), then call setLocale().
Do not call these composables in the parent page's setup and then wrap only its template output in ClientOnly. The parent setup still runs on the server. For translated server HTML, use a provider created for each request instead of the client plugin above, and hydrate the browser with the same locale and datafile. The server rendering guidance explains the isolation requirements.
Browser-side components do not need to pass locale per call. The plugin instance represents the current user's active locale.
Loading from a CDN#
Set baseUrl when creating the shared loader:
const loader = createMessagevisorLoader({ baseUrl: "https://cdn.yoursite.com/datafiles",});The helper performs a new fetch when its cache expires or when you call its refresh() method. See explicit refresh for failure handling and deployment considerations.