Cloudflare Workers with Hono
Cloudflare Workers with Hono lets you run translated API or HTML responses at the edge while loading Messagevisor datafiles from Workers Static Assets.
This guide is for applications where the Worker itself evaluates translations. If you only need to serve JSON datafiles for another app to fetch, use Cloudflare Workers Static Assets or Cloudflare Workers KV.
How it works#
┌─────────────────────────────────┐│ Messagevisor repository ││ YAML definitions + tests │└───────────────┬─────────────────┘ │ lint → test → build ▼┌─────────────────────────────────┐│ datafiles/*.json ││ deployed as Workers Assets │└───────────────┬─────────────────┘ │ env.ASSETS.fetch() ▼┌─────────────────────────────────┐│ Hono Worker ││ createMessagevisor + evaluate │└───────────────┬─────────────────┘ │ translated HTML / JSON ▼┌─────────────────────────────────┐│ Browser or API consumer │└─────────────────────────────────┘Prerequisites#
- A Cloudflare account
wranglerinstalled in your project (npm install --save-dev wrangler)- Workers types installed for TypeScript (
npm install --save-dev @cloudflare/workers-types) - Hono installed in the Worker package (
npm install hono) - Messagevisor runtime packages installed (
npm install @messagevisor/sdk @messagevisor/module-icu)
Wrangler configuration#
Bind the built datafiles/ directory as Workers Static Assets:
name = "messagevisor-hono-worker"compatibility_date = "2024-01-01"main = "src/index.ts"[assets]directory = "./datafiles"binding = "ASSETS"With this configuration, a file such as datafiles/messagevisor-web-en-US.json is available to the Worker through the ASSETS binding.
Worker entry point#
The Worker loads a locale datafile, creates an SDK instance, and returns translated output:
import { Hono } from "hono";import { HTTPException } from "hono/http-exception";import { createMessagevisor, type Messagevisor } from "@messagevisor/sdk";import { createICUModule } from "@messagevisor/module-icu";type Bindings = { ASSETS: Fetcher;};const app = new Hono<{ Bindings: Bindings }>();const cache = new Map<string, Messagevisor>();async function getMessagevisor(env: Bindings, requestUrl: string, locale: string) { const cached = cache.get(locale); if (cached) return cached; const assetUrl = new URL(`/messagevisor-web-${locale}.json`, requestUrl); const response = await env.ASSETS.fetch(assetUrl.toString()); if (!response.ok) { throw new HTTPException(404, { message: "Datafile not found" }); } const datafile = await response.json(); const m = createMessagevisor({ datafile, locale, modules: [createICUModule({ ignoreTags: false })], }); cache.set(locale, m); return m;}app.get("/", async (c) => { const locale = c.req.query("locale") || "en-US"; const plan = c.req.query("plan"); const m = await getMessagevisor(c.env, c.req.url, locale); const title = m.translate( "dashboard.welcome", { name: "Ada" }, { context: { platform: "web", plan } } ); return c.html(`<h1>${title}</h1>`);});app.get("/api/welcome", async (c) => { const locale = c.req.query("locale") || "en-US"; const m = await getMessagevisor(c.env, c.req.url, locale); return c.json({ message: m.translate("dashboard.welcome", { name: "Ada" }), });});export default app;The Map cache is per warm Worker isolate. It reduces repeated parsing and formatter setup, but new isolates can still load the same datafile from assets on demand.
Build and deploy#
Build datafiles before deploying the Worker:
$ npx messagevisor lint$ npx messagevisor test$ npx messagevisor build$ npx wrangler deployFor sets-based projects, build the production set before deploy:
$ npx messagevisor build --set=production$ npx wrangler deployGitHub Actions workflow#
Deploy on merges to main after validating the Messagevisor project:
name: Messagevisor Hono Workeron: pull_request: push: branches: [main]jobs: validate: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 - run: npm ci - run: npx messagevisor lint - run: npx messagevisor test - run: npx messagevisor build --showSize deploy: runs-on: ubuntu-latest needs: validate if: github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 - run: npm ci - run: npx messagevisor build - run: npx wrangler deploy env: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}CLOUDFLARE_API_TOKEN is a repository secret set in GitHub → Settings → Secrets and variables → Actions.

