Skip to content

Load Compiled Catalogs

After running lingui compile, each locale produces a compiled catalog file (.ts or .js). This guide covers how to import those files and wire them into the Lingui runtime.

Keep all locale imports and the SupportedLocale type in one place:

src/lib/i18n/catalog.ts
import { messages as en } from "./locales/en";
import { messages as ja } from "./locales/ja";
export const catalog = {
en,
ja,
} as const;
export type SupportedLocale = keyof typeof catalog;

The SupportedLocale type is derived from the object keys, so adding or removing a locale automatically updates the type. No need to keep a separate locale list in sync with lingui.config.

Static imports are the simplest approach. All catalog files are included in the initial bundle and switching is synchronous.

Pass the full catalog map to setupI18n. All messages are available immediately. Use activate() to switch. No load() call is needed.

src/lib/i18n/instance.ts
import { setupI18n } from "@lingui/core";
import { catalog, type SupportedLocale } from "./catalog";
export const i18n = setupI18n({
locale: "en",
messages: catalog,
});
export function switchLocale(next: SupportedLocale): void {
i18n.activate(next);
}

Import this shared instance in your root layout and pass it to setLinguiContext:

src/routes/+layout.svelte
<script lang="ts">
import { setLinguiContext } from "lingui-for-svelte";
import { i18n } from "$lib/i18n/instance";
const { children } = $props();
setLinguiContext(i18n);
</script>
{@render children()}

Statically import all catalogs but call loadAndActivate with one locale at a time. Bundle size is the same as the bundle-all approach, but the API matches the lazy-load pattern if you plan to migrate later.

const i18n = setupI18n();
i18n.loadAndActivate({ locale: "en", messages: catalog.en });
function switchLocale(next: SupportedLocale): void {
i18n.loadAndActivate({ locale: next, messages: catalog[next] });
}

Dynamic imports let your bundler split each catalog into a separate chunk. Only the requested locale is fetched, which reduces the initial bundle size.

const i18n = setupI18n();
async function loadLocale(locale: SupportedLocale): Promise<void> {
const { messages } = await import(`./locales/${locale}.ts`);
i18n.loadAndActivate({ locale, messages });
}

Svelte <script> blocks do not support top-level await. Load the initial locale with a static import to keep setup synchronous, then use dynamic imports only for subsequent locale switches.

src/routes/+layout.svelte
<script lang="ts">
import { setupI18n } from "@lingui/core";
import { setLinguiContext } from "lingui-for-svelte";
// Initial locale: static import so setup stays synchronous.
import { messages as enMessages } from "$lib/i18n/locales/en";
import type { SupportedLocale } from "$lib/i18n/catalog";
const i18n = setupI18n();
i18n.loadAndActivate({ locale: "en", messages: enMessages });
setLinguiContext(i18n);
async function switchLocale(next: SupportedLocale): Promise<void> {
const { messages } = await import(`$lib/i18n/locales/${next}.ts`);
i18n.loadAndActivate({ locale: next, messages });
}
</script>

Astro middleware runs in an async function, so dynamic imports work without any extra wiring.

src/middleware.ts
import { defineMiddleware } from "astro:middleware";
import { setupI18n } from "@lingui/core";
import { setLinguiContext } from "lingui-for-astro";
import type { SupportedLocale } from "./lib/i18n/catalog";
export const onRequest = defineMiddleware(async (context, next) => {
const locale = resolveLocale(context) as SupportedLocale;
const { messages } = await import(`./lib/i18n/locales/${locale}.ts`);
const i18n = setupI18n();
i18n.loadAndActivate({ locale, messages });
setLinguiContext(context.locals, i18n);
return next();
});
Static importsDynamic imports
Initial bundle sizeAll locales includedOnly the first locale
SwitchingSynchronousAsynchronous (await)
Svelte top-level setupStraightforwardInitial locale must be statically imported
Astro middlewareStraightforwardStraightforward (async already)
When to useDefault for most appsWhen catalog size is a concern