Skip to main content

i18n-keyless vs Lingui: which to pick in 2026

· 9 min read
Founder of i18n-keyless

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-keylessLingui
Setup time~5 minutes~30 minutes (plus extraction pipeline)
Translation keysNone — source string is the keyAuto-extracted message IDs
Locale filesNone.po or .json catalogs, one per locale
AI translationBuilt inNone — integrate a TMS or MT service
Plurals, ordinals, gendercount / ordinal / select, forms written by the modelHand-authored ICU (plurals, select, nested)
Extraction stepNoneRequired — lingui extract in CI
Bundle sizeSmall runtimeVery small (messages compile away)
Best forMVPs, multi-framework, engineer-drivenApps needing ICU, compile-time checks, small bundles

Pick i18n-keyless if you want multilingual in an afternoon and don't need to hand-author the ICU branches yourself. Pick Lingui if you want to hand-author ICU MessageFormat, need 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​

Hand-authored ICU MessageFormat​

Lingui handles every ICU construct — plurals with CLDR rules, select, selectordinal, nested arguments — and you write every branch:

<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 you follow the CLDR specification. i18n-keyless covers the same CLDR rules from the model's side: pass count or select and one source form, and the model writes every branch each language needs, validated against Intl.PluralRules before they're saved (see the plurals and genders guide). The difference is authorship: with Lingui you write the branches in catalog files; with i18n-keyless the model writes them and you edit in the dashboard when you disagree.

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 want every plural branch in a catalog file you control. ✅ 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 covers all CLDR plural forms: pass count (or select for gender) with one source form, and the model produces every branch each language needs, validated against Intl.PluralRules before they're saved — see the plurals and genders guide. The difference is authorship: with Lingui you write the branches yourself; with i18n-keyless the model writes them and you edit in the dashboard when you disagree.

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​