Skip to main content

Typed Message IDs

Lingui supports opt-in typed message IDs via TypeScript module augmentation. When enabled, all functions and components that accept a message id (i.e. i18n._(), i18n.t(), and <Trans>) will be narrowed to your specific set of known IDs, giving you autocomplete and compile-time errors for invalid IDs.

Without augmentation, id remains string and existing behavior is unchanged.

Setup

Create a declaration file that augments the Register interface from @lingui/core:

src/lingui.d.ts
import type enMessages from "./locales/en/messages.json";

declare module "@lingui/core" {
interface Register {
messageIds: keyof typeof enMessages;
}
}

The messageIds key must resolve to a union of string literal types. When Register is augmented with messageIds, the MessageId type resolves to that union. When Register is empty (the default), MessageId falls back to string.

Usage

Once set up, TypeScript will flag invalid IDs:

import { t } from "@lingui/core/macro";
import { Trans } from "@lingui/react";

// ✅ Valid — "welcome.title" exists in your catalog
const msg = t({ id: "welcome.title", message: "Welcome!" });

// ❌ Type error — "welcme.title" is not a known message ID
const bad = t({ id: "welcme.title", message: "Welcome!" });

// ✅ Autocomplete works in JSX too
<Trans id="welcome.title">Welcome!</Trans>;

Notes

  • This feature is fully opt-in. Without the .d.ts augmentation, all existing code continues to work with string IDs.
  • There is no runtime impact. The Register interface and MessageId type are erased at compile time.
  • You can point messageIds at any source that produces a string union, such as a compiled catalog JSON import, a hand-maintained union type, or a generated .d.ts file.