Skip to content
Hono

Hono i18n

One middleware detects the request locale and hands every route handler a translator. Messages come from the CDN, so new copy ships without a redeploy.

Setup

Set up Hono with Better I18N

Install the packages

Add Hono and the server adapter. Nothing else is required.

terminal
npm install hono @better-i18n/server

Create the i18n singleton

Build it once at module scope — the message cache is shared across every request.

src/i18n.ts
import { createServerI18n } from '@better-i18n/server';

// Module scope, not per request: the TtlCache is shared across requests.
export const i18n = createServerI18n({
  project: 'your-org/your-project',
  defaultLocale: 'en',
});

Mount the middleware

After app.use, every handler can read locale and t from the context.

src/index.ts
import { Hono } from 'hono';
import { betterI18n } from '@better-i18n/server/hono';
import type { Translator } from '@better-i18n/server';
import { i18n } from './i18n';

const app = new Hono<{
  Variables: { locale: string; t: Translator };
}>();

app.use('*', betterI18n(i18n));

app.get('/users/:id', (c) => {
  const t = c.get('t');
  return c.json({ error: t('errors.notFound') }, 404);
});

export default app;
Capabilities

Why Hono teams choose Better I18N

One line of setup: app.use('*', betterI18n(i18n))
Built on Web Standards — request headers are read directly, with no adapter layer
Runs anywhere Hono runs: Cloudflare Workers, Bun, Deno and Node
A module-scope instance shares its message cache across requests
New messages arrive from the CDN in about 60 seconds, with no redeploy
Typed context variables for locale and t
Example

A localized Hono route

The middleware runs first, so the handler only reads what it needs.

import { OpenAPIHono } from '@hono/zod-openapi';
import { betterI18n } from '@better-i18n/server/hono';
import type { Translator } from '@better-i18n/server';
import { i18n } from './i18n';

const app = new OpenAPIHono<{
  Variables: { locale: string; t: Translator };
}>();

// Detect locale + inject `locale` and `t` into every request context
app.use('*', betterI18n(i18n));

app.get('/greeting', (c) => {
  const t = c.get('t');
  const locale = c.get('locale');

  return c.json({
    locale,
    message: t('greeting.hello'),
  });
});

export default app;

Other frameworks

Get started

Ship your Hono API in every language

Connect a project, add the middleware, and translate without touching the deploy pipeline.