Skip to content

Plain JS/TS Setup

Plain .js, .ts, .jsx, and .tsx files use @lingui/core/macro with unplugin-lingui-macro for the build transform and @lingui/cli/api/extractors/babel for extraction.

This also applies to .svelte.js and .svelte.ts files. Those go through the plain JS/TS pipeline, not the Svelte syntax transform.

Terminal window
vp add -D unplugin-lingui-macro

@lingui/cli and @lingui/conf are required for extraction and catalog compilation. If you followed a framework getting-started guide they are already installed.

Add linguiMacro() to Vite plugins. In a framework project, place it before the framework plugin.

vite.config.ts
import linguiMacro from "unplugin-lingui-macro/vite";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [
linguiMacro(),
// sveltekit(), svelte(), etc.
],
});

For Astro, add it under vite.plugins in astro.config.ts.

Add the Babel extractor to lingui.config.ts. In a project that already has a framework extractor, list both.

lingui.config.ts
import { defineConfig } from "@lingui/conf";
import babelExtractor from "@lingui/cli/api/extractors/babel";
// import { svelteExtractor } from "lingui-for-svelte/extractor";
// import { astroExtractor } from "lingui-for-astro/extractor";
export default defineConfig({
locales: ["en", "ja"],
sourceLocale: "en",
catalogs: [{ path: "src/lib/i18n/locales/{locale}" }],
extractors: [
babelExtractor,
// svelteExtractor,
// astroExtractor,
],
});

Define message descriptors, not translated strings

Section titled “Define message descriptors, not translated strings”

In plain JS/TS, t and plural resolve through @lingui/core’s global i18n instance, not the instance registered via setLinguiContext in your Svelte or Astro code. In a framework app that does not configure the global instance, calling t directly in a .ts file will either fail or translate against the wrong locale.

The recommended approach is to define message descriptors with msg or defineMessage, and translate them in framework code where the i18n context is available. This matches Lingui’s Lazy Translations pattern.

src/lib/labels.ts
import { defineMessage, msg } from "@lingui/core/macro";
// Descriptors only. No translation happens here.
export const labels = {
greeting: msg`Hello`,
submit: msg`Submit`,
} as const;
export const welcomeMessage = defineMessage({
id: "welcome",
message: "Welcome back",
});

Then translate the descriptors in framework code where the context is set:

src/lib/MyComponent.svelte
<script lang="ts">
import { t } from "lingui-for-svelte/macro";
import { labels } from "./labels";
</script>
<p>{$t(labels.greeting)}</p>
<button>{$t(labels.submit)}</button>
src/pages/index.astro
---
import { t } from "lingui-for-astro/macro";
import { labels } from "../lib/labels";
---
<p>{t(labels.greeting)}</p>

See Share Messages Across Files for more patterns, and msg and defineMessage for the full descriptor API.