Frameworks
Angular
Angular applications can load Messagevisor datafiles through an injectable service and render translations from components.
Install#
$ npm install @messagevisor/sdk @messagevisor/module-icuProvide HttpClient#
Configure Angular's HttpClient at the application root:
import { ApplicationConfig } from "@angular/core";import { provideHttpClient, withFetch } from "@angular/common/http";export const appConfig: ApplicationConfig = { providers: [provideHttpClient(withFetch())],};withFetch() is useful when the same application also uses Angular SSR.
Messagevisor service#
Create a client-side service that fetches built datafiles and keeps the active locale in Angular state:
import { Injectable, signal } from "@angular/core";import { HttpClient } from "@angular/common/http";import { firstValueFrom, timeout } from "rxjs";import { createMessagevisor, type DatafileContent, type Messagevisor } from "@messagevisor/sdk";import { createICUModule } from "@messagevisor/module-icu";@Injectable({ providedIn: "root" })export class MessagevisorService { private readonly cache = new Map<string, Messagevisor>(); private readonly pending = new Map<string, Promise<Messagevisor>>(); readonly locale = signal("en-US"); constructor(private readonly http: HttpClient) {} async getInstance(locale = this.locale()) { const cached = this.cache.get(locale); if (cached) return cached; return this.refresh(locale); } refresh(locale = this.locale()) { const pending = this.pending.get(locale); if (pending) return pending; const load = (async () => { const datafile = await firstValueFrom( this.http.get<DatafileContent>(`/datafiles/messagevisor-web-${locale}.json`).pipe( timeout(10_000) ) ); const m = createMessagevisor({ datafile, modules: [createICUModule({ ignoreTags: false })], }); try { const loaded = m.getDatafile(locale); if (loaded.target !== "web") throw new Error("Unexpected target"); } catch (error) { await m.close(); throw error; } this.cache.set(locale, m); return m; })().finally(() => this.pending.delete(locale)); this.pending.set(locale, load); return load; } async setLocale(locale: string) { await this.getInstance(locale); this.locale.set(locale); }}Place generated datafiles under public/datafiles or whatever static asset path your Angular build serves.
HttpClient rejects unsuccessful HTTP responses. Concurrent loads for one locale share a pending promise, and failures clear it so another call can retry. refresh(locale) performs a new request even when a cached instance exists. It publishes only a valid replacement; failed refreshes leave the last known good instance in the cache. Configure HTTP caching so that request can see updated data.
Catch an initial getInstance() failure in the component and offer a retry or unavailable state. After an awaited refresh() succeeds, read the new instance and update displayed signals. Existing references keep their previous snapshot. For SSR, use the shared server loader and pass locale and context per request rather than sharing this browser locale signal between users.
Component usage#
Components can await the service and pass user context per evaluation. In the browser, the service tracks one active locale for one user, so translation calls do not need per-call locale:
import { Component, OnInit, signal } from "@angular/core";import { MessagevisorService } from "./messagevisor.service";@Component({ selector: "app-dashboard", standalone: true, template: `<h1>{{ title() }}</h1>`,})export class DashboardComponent implements OnInit { readonly title = signal(""); constructor(private readonly messagevisor: MessagevisorService) {} async ngOnInit() { const m = await this.messagevisor.getInstance(); this.title.set( m.translate( "dashboard.welcome", { name: "Ada" }, { context: { platform: "web", plan: "pro" } } ) ); }}Do not call setContext() on a shared instance for user-specific values. Pass context in evaluation options. For Angular SSR server code that reuses one Messagevisor instance across requests, load all needed locale datafiles into that server instance and pass locale per call. For browser-only components, setLocale() remains the right model for user-driven language switches.
Locale switching#
When the user switches locale, load the new datafile before updating UI state:
await this.messagevisor.setLocale("nl-NL");const m = await this.messagevisor.getInstance("nl-NL");For highly reactive UIs, wrap common translation calls in your own Angular service methods and update signals after setLocale().
Loading from a CDN#
If datafiles are published separately, point the service at your CDN:
this.http.get( `https://cdn.yoursite.com/datafiles/messagevisor-web-${locale}.json`);