i18n-keyless vs Lingui: which to pick in 2026
Lingui is the "modern i18next" — a lightweight, TypeScript-first i18n library with compile-time message extraction and full ICU MessageFormat support. Where i18next grew organically over a decade of plugins, Lingui was designed from scratch with a cleaner API: macros that look like regular function calls, a compiler that extracts messages at build time, and catalogs that stay in sync automatically.
i18n-keyless skips the catalog entirely. There are no message IDs, no extraction step, no .po or .json files to maintain. The source string is the key, translations are AI-generated, and the whole pipeline disappears.
This is an honest comparison. Lingui is a genuinely good library and we'll tell you when it's the better choice.
TL;DR
| i18n-keyless | Lingui | |
|---|---|---|
| Setup time | ~5 minutes | ~30 minutes (plus extraction pipeline) |
| Translation keys | None — source string is the key | Auto-extracted message IDs |
| Locale files | None | .po or .json catalogs, one per locale |
| AI translation | Built in | None — integrate a TMS or MT service |
| ICU MessageFormat | Limited | Full support (plurals, select, nested) |
| Extraction step | None | Required — lingui extract in CI |
| Bundle size | Small runtime | Very small (messages compile away) |
| Best for | MVPs, multi-framework, engineer-driven | Apps needing ICU, compile-time checks, small bundles |
Pick i18n-keyless if you want multilingual in an afternoon and don't need ICU plural rules. Pick Lingui if you need full ICU MessageFormat, compile-time guarantees, and the smallest possible runtime bundle.
The code difference
Lingui setup
npm install @lingui/core @lingui/react @lingui/cli @lingui/macro @lingui/vite-plugin
// lingui.config.ts
export default {
locales: ["en", "fr", "de", "es"],
catalogs: [{ path: "src/locales/{locale}", include: ["src/"] }],
sourceLocale: "en",
};
// In a component
import { Trans, t } from "@lingui/macro";
function Checkout() {
return (
<div>
<h1><Trans>Your cart</Trans></h1>
<button><Trans>Confirm payment</Trans></button>
<input placeholder={t`Search products`} />
</div>
);
}
Then run lingui extract to scan the code and generate catalog files:
npx lingui extract
# Creates src/locales/en.po, src/locales/fr.po, etc.
Each .po file needs translations filled in — by a translator, a TMS, or a machine translation pass. Then compile:
npx lingui compile
# Generates optimized runtime catalogs
i18n-keyless setup
npm install i18n-keyless-react
import { I18nKeylessText, useTranslation } from "i18n-keyless-react";
function Checkout() {
const placeholder = useTranslation("Search products");
return (
<div>
<h1><I18nKeylessText>Your cart</I18nKeylessText></h1>
<button><I18nKeylessText>Confirm payment</I18nKeylessText></button>
<input placeholder={placeholder} />
</div>
);
}
No extraction. No catalogs. No compile step. Translations are AI-generated the first time a string is encountered, cached, and overridable from the dashboard.
Where Lingui genuinely wins
Full ICU MessageFormat
Lingui handles every ICU construct — plurals with CLDR rules, select, selectordinal, nested arguments:
<Plural value={count} zero="No items" one="1 item" other="# items" />
<Trans>
{gender, select, male {He} female {She} other {They}} liked your post
</Trans>
Polish (4 plural forms), Arabic (6 plural forms), Russian — Lingui gets them right because it follows the CLDR specification. i18n-keyless uses string interpolation and relies on the AI to produce the correct plural form per language. For pluralization-heavy apps, Lingui is more precise.
Compile-time extraction
lingui extract scans your codebase and finds every translatable string automatically. You know at build time:
- Which strings exist.
- Which locales are missing translations.
- Which strings were added or removed since the last extraction.
This is a real safety net. With i18n-keyless, a string exists when it's first rendered — there's no upfront inventory.
Tiny runtime bundle
Lingui compiles message catalogs into optimized JavaScript. The runtime is minimal — messages are resolved from a precompiled lookup, not fetched from a server. For apps where every kilobyte matters (mobile web, poor connections), Lingui's approach produces smaller bundles.
i18n-keyless includes a small runtime SDK and makes an API call to fetch translations (cached locally after the first load). It's not heavy, but it's not zero.
Macro-based DX
Lingui's macros (t, <Trans>, <Plural>) look like regular code. The compiler transforms them at build time, so there's no runtime overhead for the extraction. It's elegant — you write natural-looking JSX and the tooling handles the rest.
Active, growing community
Lingui has a dedicated community, good documentation, and active maintenance. It's increasingly recommended as the "modern alternative to react-intl" — and rightfully so.
Where i18n-keyless wins
No extraction pipeline
Lingui requires an extract → translate → compile pipeline. That's three CLI commands, a CI step, and a process for getting translations into the catalog files. Skip a step and you ship untranslated strings.
i18n-keyless has no pipeline. Write a string, it translates itself.
No catalog files to manage
Lingui keeps .po or .json catalogs in your repo — one per locale. These need to be committed, reviewed, merged, and kept in sync. A new locale means a new file to maintain.
i18n-keyless stores translations in its backend. No files in your repo, no merge conflicts, no "which branch has the latest French translations?" questions.
AI translation is built in
Adding a new language to a Lingui project means: run lingui extract, get empty catalog entries for the new locale, fill them with translations (human or machine), run lingui compile, deploy. Adding a new language to i18n-keyless means: add the language code to the supported array.
Works beyond React
Lingui is primarily a React library (with growing Svelte and Vue support). i18n-keyless has SDKs for React, React Native, Vue, Angular, Svelte, Node.js, Laravel, Rails, Flutter, Go, Python, Swift, and Kotlin — same dashboard, same API key, same translations across your entire stack.
No bundler plugin required
Lingui needs a Vite/webpack/Babel plugin for its macros to work. i18n-keyless is a regular npm package — install it, import it, call init(). No build config to touch.
When to choose Lingui
✅ You need full ICU MessageFormat with CLDR plural rules. ✅ Compile-time extraction and type safety are important to your team. ✅ Bundle size is a critical constraint (mobile web, offline-first). ✅ You want an open-source, self-contained solution with no external API dependency. ✅ You have translators filling catalog files (or a TMS connected to Lingui).
When to choose i18n-keyless
✅ You want multilingual in a day, not after setting up an extraction pipeline. ✅ You don't have (or don't want) translators in the loop. ✅ Your product spans multiple frameworks and languages. ✅ You want to add locales frequently without touching catalogs each time. ✅ Simplicity matters more than compile-time guarantees on message IDs.
FAQ
Does Lingui support AI translation?
Not built in. You can pipe extracted catalogs through a machine translation API (Google Translate, DeepL, or an LLM), but you'd build and maintain that pipeline yourself. Some TMS platforms (Crowdin, Phrase) have Lingui integrations that include MT.
Is i18n-keyless as type-safe as Lingui?
No. Lingui's macros produce typed message descriptors — a missing or mistyped key is a compile error. i18n-keyless uses source strings as keys, so a typo creates a new translation rather than a build failure. The trade-off: less compile-time safety, more code readability.
What about ICU MessageFormat?
i18n-keyless supports simple value interpolation (replace) but doesn't model CLDR plural rules natively. For most SaaS UI strings, the AI produces correct plurals per language. For apps where locale-specific plural forms (Arabic, Polish, Russian) are critical and must be deterministic, Lingui's ICU support is more reliable.
Can I use both together?
Yes — keep Lingui for existing screens, use i18n-keyless for new features. The libraries don't conflict. Over time, you can replace <Trans> wrappers with <I18nKeylessText> and drop the extraction pipeline.
Next steps
- Compare i18next too: i18n-keyless vs i18next.
- See vs react-intl: i18n-keyless vs react-intl.
- Try keyless: Quick setup guide — 5 minutes.
- Read the philosophy: The complete guide to keyless i18n.