How it Works
@lingo.dev/compiler transforms your React application into a multilingual app at build time through intelligent code analysis and AI-powered translation.
Build-Time Transformation
Traditional runtime i18n libraries (i18next, react-intl) work at runtime—they load translations, interpolate values, and format messages while your app runs. This adds bundle size, runtime overhead, and complexity.
@lingo.dev/compiler works differently: it transforms your code during the build process. Your React components stay clean, and translations are pre-compiled into optimized bundles.
Result: Zero runtime overhead, smaller bundles, and no manual translation key management.
The Process
1. AST Analysis
The compiler uses Babel to parse your React code into an Abstract Syntax Tree (AST). It walks through your JSX, identifying translatable content:
- Text nodes (
<p>Hello</p>) - String attributes (
<img alt="Logo" />) - Text in template expressions (
<p>Hello {name}</p>)
The compiler understands React component boundaries and maintains contextual information for accurate translations. Technical identifiers, code snippets, and non-translatable elements are automatically filtered out.
2. Content Extraction
For each translatable string, the compiler:
- Generates a stable hash-based identifier
- Preserves component context (file, location, surrounding elements)
- Extracts rich text structure (handles nested elements like
<strong>and<em>) - Maintains interpolation placeholders
This metadata is stored in .lingo/metadata.json—a versioned file that tracks all translatable content in your app.
3. Translation Generation
During development, the translation server handles on-demand translation:
- Pseudotranslator mode (default): Generates instant fake translations—useful for seeing what gets translated without API costs
- Real translation mode: Calls your configured LLM provider (Lingo.dev Engine or direct LLM)
The translation service is stateless and handles partial failures gracefully—if some translations fail, cached translations are still used.
4. Code Injection
The compiler injects translation lookups into your JSX:
// Your source code
<p>Hello {name}</p>
// Transformed code (simplified)
<p>{t('abc123', { name })}</p>
The t() function is optimized and injected automatically. It performs hash-based lookups in pre-loaded translation dictionaries.
5. Bundle Optimization
At build time:
- Per-locale bundles are created
- Only used translations are included
- Dead code elimination removes unused translations
- Dictionaries are tree-shaken per component
Development Workflow
Development Mode
{
dev: {
usePseudotranslator: true,
}
}
When you run npm run dev:
- The compiler starts a translation server (auto-finds ports 60000-60099)
- Your app makes requests to the server for translations
- Pseudotranslator generates instant fake translations
- Translations are cached in
.lingo/metadata.json - HMR works normally—state is preserved
The development widget (if enabled) lets you edit translations in-browser and see changes in real-time.
Production Mode
{
buildMode: "cache-only",
}
When you run npm run build:
- No translation server starts
- Only cached translations from
.lingo/metadata.jsonare used - If any translations are missing, build fails with clear error
- No API calls made—no API keys needed
Why cache-only? In production, you want deterministic builds. Translations should be generated in CI (where you have API keys), not during production builds.
Recommended Workflow
Local Development:
- Use pseudotranslator
- Fast feedback loop
- No API costs
CI/CD:
{
buildMode: "translate",
dev: {
usePseudotranslator: false,
}
}
- Generate real translations
- Run once per deployment
- Commit
.lingo/changes
Production Build:
{
buildMode: "cache-only",
}
- Use pre-generated translations
- No API keys required
- Fast, deterministic builds
Architecture
The compiler is organized around clear separation of concerns:
Metadata Manager
- CRUD operations for
.lingo/metadata.json - Thread-safe with file locking
- Hash-based identifiers for translations
Translation Service
- Orchestrates translation workflow
- Handles cache strategy
- Manages partial failures
- Returns both successful translations and errors
Translators (stateless)
- Pseudotranslator: Instant fake translations
- LCP Translator: Lingo.dev Engine integration
- LLM Translators: Direct provider integration
- No built-in caching—service layer handles that
Translation Server
- HTTP server for development
- WebSocket support for real-time widget updates
- Automatic port management
- Batch request handling
See the source code architecture doc for implementation details.
Framework Integration
Next.js
The compiler integrates via withLingo() wrapper:
- Supports both Webpack and Turbopack
- Works with React Server Components
- Async config for lazy plugin loading
- Automatic locale-based routing (if configured)
Vite
The compiler integrates via lingoCompilerPlugin:
- Unplugin-based (works with Vite, Webpack, Rollup)
- Full HMR support
- Efficient dev server integration
- Automatic virtual module generation
Common Questions
Does this work with Server Components? Yes. In Next.js, the compiler transforms both Server and Client Components. Translation lookups work isomorphically.
What about code splitting? Translations are split automatically alongside your components. Each chunk includes only the translations it needs.
How are translations cached?
All translations are stored in .lingo/metadata.json. This file is version-controlled and acts as your translation cache. The compiler uses content hashes—only new or changed text triggers re-translation.
What if a translation fails? The service returns partial results. Successful translations are cached and used, while errors are logged with context for debugging. Your app doesn't break—it falls back to cached translations or source text.
Can I see the transformed code?
Yes. In your build output, look for the transformed files. The transformations are minimal—just t() function calls with hash-based lookups.
Next Steps
- Configuration Reference — All configuration options
- Build Modes — Understand translate vs cache-only
- Development Tools — Pseudotranslator, dev widget, and translation server
- Project Structure — Understanding
.lingo/directory