Remix
Remix applications can use Messagevisor in loaders, resource routes, and client components through the React provider.
Install#
$ npm install @messagevisor/sdk @messagevisor/react @messagevisor/module-icuServer helper#
Create a server-only helper that loads all supported locale datafiles into one SDK instance:
import { createMessagevisor, type Messagevisor } from "@messagevisor/sdk";import { createICUModule } from "@messagevisor/module-icu";const supportedLocales = ["en-US", "nl-NL"];let instancePromise: Promise<Messagevisor> | undefined;export async function getMessagevisor() { if (instancePromise) return instancePromise; instancePromise = Promise.all( supportedLocales.map((locale) => fetch(`https://cdn.yoursite.com/datafiles/messagevisor-web-${locale}.json`).then( (response) => response.json() ) ) ).then((datafiles) => { const m = createMessagevisor({ datafile: datafiles[0], modules: [createICUModule({ ignoreTags: false })], }); datafiles.slice(1).forEach((datafile) => m.setDatafile(datafile)); return m; }); return instancePromise;}Loaders and resource routes pass locale per call. Do not call setLocale() or setContext() while handling requests.
Loader data#
Load translated values in Remix loaders and read them with useLoaderData():
import { json, type LoaderFunctionArgs } from "@remix-run/node";import { useLoaderData } from "@remix-run/react";import { getMessagevisor } from "../messagevisor.server";export async function loader({ params, request }: LoaderFunctionArgs) { const locale = params.locale || "en-US"; const url = new URL(request.url); const plan = url.searchParams.get("plan") || undefined; const m = await getMessagevisor(); return json({ title: m.translate( "dashboard.welcome", { name: "Ada" }, { locale, context: { platform: "web", plan } } ), });}export default function DashboardRoute() { const { title } = useLoaderData<typeof loader>(); return <h1>{title}</h1>;}Resource routes#
Resource routes can return translated JSON for API-style consumers:
import { json, type LoaderFunctionArgs } from "@remix-run/node";import { getMessagevisor } from "../messagevisor.server";export async function loader({ request }: LoaderFunctionArgs) { const locale = new URL(request.url).searchParams.get("locale") || "en-US"; const m = await getMessagevisor(); return json({ message: m.translate("dashboard.welcome", { name: "Ada" }, { locale }), });}React provider hydration#
For client-side hooks, pass a loaded datafile into MessagevisorProvider:
import { useMemo } from "react";import { createMessagevisor } from "@messagevisor/sdk";import { createICUModule } from "@messagevisor/module-icu";import { MessagevisorProvider } from "@messagevisor/react";export function MessagevisorClientProvider(props: { datafile: any; children: React.ReactNode;}) { const m = useMemo( () => createMessagevisor({ datafile: props.datafile, modules: [createICUModule({ ignoreTags: false })], }), [props.datafile] ); return <MessagevisorProvider instance={m}>{props.children}</MessagevisorProvider>;}Then components below the provider can use React SDK hooks.
Client components do not need to pass locale per call. They receive one datafile for the current user-facing locale, and the provider instance keeps that locale as its active locale. If the user switches language in the browser, load the new datafile, call setDatafile(), then call setLocale().
Loading from a CDN#
If datafiles are published under a different path, update the CDN URL in your server helper:
const datafile = await fetch( `https://cdn.yoursite.com/datafiles/messagevisor-web-${locale}.json`).then((response) => response.json());
