DEV Community

Cover image for Introducing Lingo.dev Compiler: Localize a React app without rewriting its code
Max Prilutskiy
Max Prilutskiy

Posted on

Introducing Lingo.dev Compiler: Localize a React app without rewriting its code

Today, we're introducing Lingo.dev Compiler: An npm library, that lets you build multilingual React apps without having to rewrite the existing React code.

It doesn't require extracting i18n keys, or wrapping text in tags.

100% free and open-source.

Here's how it works:

The core challenge: How do you translate React components without touching the source code?

Traditional i18n requires rewriting your entire codebase - extracting strings to JSON files, wrapping components in translation tags, maintaining separate dictionaries.

We asked ourselves: "What if that actually wasn't necessary?"

AST

So, we came up with this idea to processes React app's Abstract Syntax Tree and perform localization at build time.

But here's the tricky part: we had to solve how to deterministically group elements that should be translated together.

For example, this entire block gets processed as one translation unit:

Image description

Not three separate fragments that lose meaning when isolated. This prevents mistranslations and maintains semantic relationships that LLMs need for context.

HMR

The biggest technical uncertainty was Hot Module Replacement compatibility.

We were lucky to solve this one pretty quickly, but picture this: you're coding in English but need to instantly see how your UI looks in Spanish or Japanese. Text expansion/contraction can break layouts in ways you'd never catch otherwise.

Image description

We rebuilt our processor to work seamlessly with HMR, so there’s no need in building twice to see translations.

Performance

Large codebases, including ours, can't afford slow incremental builds, so performance was another beast entirely. We're talking about processing thousands of components, each potentially containing dozens of translatable strings.

Image description

Our solution: aggressive caching that stores AST analysis results between runs + heavily parallelized LLM calls. Also Groq. So, only changed components get reprocessed. Everything else hits the cache.

By the way

What's interesting, is that this approach was technically possible before LLMs, but practically useless. You'd still need human translators familiar with your product domain for precise translations. The AST processing was just expensive preprocessing for manual work.

However now, with context-aware models, we can generate quality translations automatically. That's why compile-time, AI-powered localization isn't just possible – it's becoming the most practical approach for modern UIs.

We predict this compile-time approach will become standard. Think about it: why should developers maintain separate translation files when the compiler can generate them automatically?

Major i18n libraries will adopt similar AST-based models. Frameworks like Next.js and Remix will eventually bake this directly into their core. The traditional extract-wrap-maintain cycle is becoming obsolete.

Frameworks

Here's how we handle framework integration:

// Next.js
export default lingoCompiler.next({
  sourceLocale: "en",
  targetLocales: ["es", "fr", "de"],
  models: { "*:*": "groq:mistral-saba-24b" }

})(nextConfig);
Enter fullscreen mode Exit fullscreen mode

Works with Next.js App Router, React Router, Remix, and Vite. Zero runtime cost - translations are pre-compiled into optimized bundles.

The compiler creates versioned dictionaries using MD5 fingerprinting.

// Generated in lingo/ directory
{
  "content": "Welcome to our platform!",
  "fingerprint": "a1b2c3d4e5f6",
  "translations": {
    "es": "¡Bienvenido a nuestra plataforma!",
    "fr": "Bienvenue sur notre plateforme!"
  }
}
Enter fullscreen mode Exit fullscreen mode

Only retranslates when content actually changes. Git integration tracks translation history. Delta processing minimizes costs.

Bonus

Bonus feature I didn't mention: the compiler gracefully handles dynamic React patterns too:

// Compiler handles dynamic content gracefully

function UserGreeting({ name, count }) {
  return (
    div
      Welcome back, {name}! You have {count} messages.
    /div
  );
}

// Preserves React behavior while translating static text
// "Welcome back" + "You have" + "messages" get translated
// {name} and {count} remain dynamic
Enter fullscreen mode Exit fullscreen mode

We were able to get the best of both worlds - no code changes + full React compatibility.

Workflow

The development workflow is beautifully simple:

  1. Write React components using natural language text

  2. Run development server - compiler extracts and translates content, using your API key

  3. Commit translation files to version control

  4. Deploy with built-in multilingual support

No configuration files to maintain. No manual string extraction. Just build and ship.

Next steps

Break it, test it, let us know what you think!

Here’s how to get started:

npm i lingo.dev

Docs: https://lingo.dev/compiler

GitHub: https://github.com/lingodotdev/lingo.dev

Demo: https://www.youtube.com/watch?v=sSo2ERxAvB4

Discord: https://lingo.dev/go/discord

We're scratching our own itch here, and we're excited to share it with the dev community!

(We’ve also just launch on HackerNews, please look us up, upvote + reply 😅)

Image description

https://news.ycombinator.com

Top comments (10)

Collapse
 
ryanguitar profile image
Ryan Els

The Compiler is truly amazing 🤩 👌

Collapse
 
nevodavid profile image
Nevo David

insane, feels like this saves so much brainpower tbh - you think stuff like this will end up just being the default for all web dev, or will there always be people sticking to the old ways?

Collapse
 
chiragagg5k profile image
Chirag Aggarwal

Thats such a huge problem Lingo just solved, cudos to the team 👏

Collapse
 
mathio profile image
Matej Lednický

Really excited we got this one out to the public! Go team 👏

Collapse
 
javadd profile image
zayn

just do it you are not on your own buddy

Collapse
 
dotallio profile image
Dotallio

This is so impressive, I love how you sidestep the extract-and-wrap grind completely. Have you run into any tough edge cases with dynamic content in big apps?

Collapse
 
vrcprl profile image
Veronica Prilutskaya 🌱

This looks cool! Excited it is finally launched!

Collapse
 
urlrw profile image
Url.rw

Highly recommended

Collapse
 
omriluz1 profile image
Omri Luz

Hey Max, Looks great! 2 Critics I have about it though, Maybe I could open an issue soon

  1. There is no way to match the locale to a url specific languge (/fr /es etc) when switching language (for SEO purposes its important)
  2. is there a way to customize the localeswitcher component? with maybe flags or anything i want and pass a component and a localeId or something?
Collapse
 
nathan_tarbert profile image
Nathan Tarbert

Pretty cool honestly, nothing kills momentum faster than dealing with manual translations so making this hands-off is big for me

Some comments may only be visible to logged-in visitors. Sign in to view all comments.