Skip to content

Astro: i18n Context

Middleware is the recommended initialization point regardless of output mode. Astro runs middleware for every request in server and hybrid output, and for every page during the static build in static output. Either way, setLinguiContext runs before the page renders.

lingui-for-astro uses Astro.locals as the context carrier in all cases.

Call setLinguiContext in middleware so it runs once per page, before any translated content renders.

src/middleware.ts
import { defineMiddleware } from "astro:middleware";
import { setupI18n } from "@lingui/core";
import { setLinguiContext } from "lingui-for-astro";
import { catalog } from "./lib/i18n/catalog";
export const onRequest = defineMiddleware((context, next) => {
const locale = resolveLocale(context); // your locale resolution logic
const i18n = setupI18n({ locale, messages: catalog });
setLinguiContext(context.locals, i18n);
return next();
});

Everything rendered in that request (or build) can then read the same i18n instance. No extra props or manual context plumbing are needed.

For a static site with a fixed locale and no per-page locale logic, initializing Lingui in page frontmatter is also valid. Skip the middleware entirely.

src/pages/index.astro
---
import { setupI18n } from "@lingui/core";
import { setLinguiContext } from "lingui-for-astro";
import { t } from "lingui-for-astro/macro";
import { catalog } from "../lib/i18n/catalog";
const locale = "en"; // or derive from Astro.params, static paths config, etc.
const i18n = setupI18n({ locale, messages: catalog });
setLinguiContext(Astro.locals, i18n);
---
<h1>{t`Hello`}</h1>

In .astro files, the macro transform reads the Lingui instance from Astro.locals automatically. You write macros normally.

---
import { t, Trans } from "lingui-for-astro/macro";
---
<h1>{t`Hello`}</h1>
<p><Trans>Welcome to the site.</Trans></p>

For normal .astro templates, this is all you need.

Accessing the instance in frontmatter helpers

Section titled “Accessing the instance in frontmatter helpers”

If you need the I18n instance in a plain TypeScript helper called from frontmatter, pass Astro.locals explicitly.

src/lib/buildPageMeta.ts
import { msg } from "@lingui/core/macro";
import { getLinguiContext } from "lingui-for-astro";
const pageTitle = msg({ id: "page.title", message: "My Site" });
export function buildPageMeta(locals: App.Locals) {
const { i18n } = getLinguiContext(locals);
return {
title: i18n._(pageTitle),
};
}
---
import { buildPageMeta } from "../lib/buildPageMeta";
const meta = buildPageMeta(Astro.locals);
---
<title>{meta.title}</title>
import { setLinguiContext } from "lingui-for-astro";
function setLinguiContext(locals: object, instance: I18n): LinguiContext;

Attaches the Lingui instance to the request locals bag. Pass Astro.locals (in page frontmatter) or context.locals (in middleware) as the first argument. Call this before any translated Astro content renders.

import { getLinguiContext } from "lingui-for-astro";
function getLinguiContext(locals: object): LinguiContext;

Reads the Lingui context from Astro.locals (or context.locals in middleware). Symmetric with setLinguiContext.

Compiled macro output in .astro files calls this automatically. You only need it when accessing i18n from a plain TypeScript helper that receives Astro.locals.

import type { LinguiContext } from "lingui-for-astro";
type LinguiContext = {
i18n: I18n;
};

The value returned by both functions. Access .i18n to use the Lingui instance for imperative calls. i18n._() is the low-level translation function that takes a message descriptor directly; prefer msg from @lingui/core/macro to define descriptors so they are extracted correctly. See the example above and Plain JS/TS Setup for the recommended pattern.