Migration Guide
Migrate from the old compiler (lingo.dev/compiler) to the new @lingo.dev/compiler.
Why Migrate?
The new compiler offers:
- Better DX — Automatic by default (no
'use i18n'required) - Better performance — Faster builds, better HMR
- Build modes — Separate dev/CI/prod concerns
- Manual overrides —
data-lingo-overrideattribute - Custom locale resolvers — Flexible locale detection
- Development tools — Pseudotranslator, dev widget (coming soon)
- Cleaner architecture — Better separation of concerns
Breaking Changes
1. Package Name
Old:
npm install lingo.dev
New:
npm install @lingo.dev/compiler
2. Import Paths
Old:
import lingoCompiler from "lingo.dev/compiler";
import { LingoProvider } from "lingo.dev/react/rsc";
New:
import { withLingo } from "@lingo.dev/compiler/next";
import { LingoProvider } from "@lingo.dev/compiler/react";
3. Configuration API
Next.js
Old:
// next.config.js
import lingoCompiler from "lingo.dev/compiler";
export default lingoCompiler.next({
sourceLocale: "en",
targetLocales: ["es", "de"],
models: "lingo.dev",
})(nextConfig);
New:
// next.config.ts
import { withLingo } from "@lingo.dev/compiler/next";
export default async function (): Promise<NextConfig> {
return await withLingo(nextConfig, {
sourceRoot: "./app", // New: specify source directory
sourceLocale: "en",
targetLocales: ["es", "de"],
models: "lingo.dev",
});
}
Changes:
- Config must be async function
- New
sourceRootoption withLingowrapper instead oflingoCompiler.next
Vite
Old:
import lingoCompiler from "lingo.dev/compiler";
export default defineConfig(() =>
lingoCompiler.vite({
sourceRoot: "src",
targetLocales: ["es", "de"],
models: "lingo.dev",
})(viteConfig)
);
New:
import { lingoCompilerPlugin } from "@lingo.dev/compiler/vite";
export default defineConfig({
plugins: [
lingoCompilerPlugin({
sourceRoot: "src",
sourceLocale: "en", // New: required
targetLocales: ["es", "de"],
models: "lingo.dev",
}),
react(),
],
});
Changes:
- Plugin-based instead of config wrapper
sourceLocaleis now required- Place before
react()plugin
4. Provider Setup
Old:
import { LingoProvider, loadDictionary } from "lingo.dev/react/rsc";
export default function Layout({ children }) {
return (
<LingoProvider loadDictionary={(locale) => loadDictionary(locale)}>
{children}
</LingoProvider>
);
}
New:
import { LingoProvider } from "@lingo.dev/compiler/react";
export default function Layout({ children }) {
return (
<LingoProvider>
{children}
</LingoProvider>
);
}
Changes:
- No
loadDictionaryprop—handled internally - Simpler API
5. File Structure
Old:
lingo/
├── dictionary.js
├── meta.json
└── [locale]/
└── *.json
New:
.lingo/
└── metadata.json
Changes:
- Directory renamed (
.lingovslingo) - Single metadata file instead of multiple files
- Different JSON structure
6. "use i18n" Directive
Old: Required by default. Add to every file you want to translate:
'use i18n';
export function Component() { ... }
New: Optional. By default, all files are translated automatically. To opt-in:
{
useDirective: true, // Enable opt-in behavior
}
Then add directive:
'use i18n';
export function Component() { ... }
Migration Steps
Step 1: Update Package
# Uninstall old package
npm uninstall lingo.dev
# Install new package
npm install @lingo.dev/compiler
Step 2: Update Configuration
Next.js
Before:
// next.config.js
import lingoCompiler from "lingo.dev/compiler";
export default lingoCompiler.next({
sourceLocale: "en",
targetLocales: ["es", "de"],
models: "lingo.dev",
})(nextConfig);
After:
// next.config.ts
import type { NextConfig } from "next";
import { withLingo } from "@lingo.dev/compiler/next";
const nextConfig: NextConfig = {};
export default async function (): Promise<NextConfig> {
return await withLingo(nextConfig, {
sourceRoot: "./app",
sourceLocale: "en",
targetLocales: ["es", "de"],
models: "lingo.dev",
dev: {
usePseudotranslator: true, // Recommended for development
},
});
}
Vite
Before:
import lingoCompiler from "lingo.dev/compiler";
export default defineConfig(() =>
lingoCompiler.vite({
sourceRoot: "src",
targetLocales: ["es", "de"],
models: "lingo.dev",
})(viteConfig)
);
After:
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { lingoCompilerPlugin } from "@lingo.dev/compiler/vite";
export default defineConfig({
plugins: [
lingoCompilerPlugin({
sourceRoot: "src",
sourceLocale: "en",
targetLocales: ["es", "de"],
models: "lingo.dev",
dev: {
usePseudotranslator: true,
},
}),
react(),
],
});
Step 3: Update Provider
Before:
import { LingoProvider, loadDictionary } from "lingo.dev/react/rsc";
export default function Layout({ children }) {
return (
<LingoProvider loadDictionary={(locale) => loadDictionary(locale)}>
{children}
</LingoProvider>
);
}
After:
import { LingoProvider } from "@lingo.dev/compiler/react";
export default function Layout({ children }) {
return (
<LingoProvider>
{children}
</LingoProvider>
);
}
Step 4: Clean Up Old Files
# Backup old translations (optional)
mv lingo lingo.backup
# Remove old directory
rm -rf lingo
# New directory will be created automatically
# on first build
Step 5: Test with Pseudotranslator
npm run dev
With usePseudotranslator: true, you'll see instant fake translations. Verify:
- All expected text is translated
- No compilation errors
- Layout handles varying text lengths
Step 6: Generate Real Translations
Update config to disable pseudotranslator:
{
dev: {
usePseudotranslator: false,
}
}
Restart dev server. The compiler will generate real translations for any new or changed text.
Step 7: Commit New Translations
git add .lingo/
git commit -m "chore: migrate to @lingo.dev/compiler"
git push
Feature Mapping
Old Features → New Equivalents
| Old Feature | New Equivalent | Notes |
|---|---|---|
dictionary.js | .lingo/metadata.json | Different format |
meta.json | .lingo/metadata.json | Merged into single file |
| "use i18n" (required) | "use i18n" (optional) | Now opt-in, not required |
| Custom prompts | prompt config option | Same functionality |
| Editing translations | data-lingo-override | Attribute-based overrides |
| Skipping translations | data-lingo-override + empty | Or use useDirective |
| Overriding translations | data-lingo-override | Attribute-based |
| Switching locales | useLocale/setLocale | Same API |
| LLM providers | models config | Same providers supported |
New Features (Not in Old Compiler)
- Build modes —
translatevscache-only - Pseudotranslator — Instant fake translations
- Development widget — In-browser editing (coming soon)
- Custom locale resolvers — Flexible locale detection
- Automatic pluralization — ICU MessageFormat support
- Translation server — On-demand translations in dev
Translating Existing Translations
The new compiler uses a different file format. Existing translations are not automatically migrated.
Options:
Option 1: Regenerate All Translations
Let the compiler generate fresh translations:
- Delete old
lingo/directory - Run new compiler
- Generate translations using AI
Pros: Clean start, latest AI models Cons: API costs, may lose nuances
Option 2: Manual Migration Script
Create a script to convert old format to new:
// migrate-translations.ts
import * as fs from "fs";
const oldDir = "./lingo";
const newFile = "./.lingo/metadata.json";
// Read old translations
const oldTranslations = {}; // Parse old files
// Convert to new format
const newMetadata = {
version: "1",
sourceLocale: "en",
targetLocales: ["es", "de"],
translations: {}, // Convert old translations
};
// Write new metadata
fs.writeFileSync(newFile, JSON.stringify(newMetadata, null, 2));
This is manual work and format-specific.
Option 3: Hybrid Approach
- Generate new translations for most text
- Use
data-lingo-overridefor critical translations that need exact wording
Common Issues
"Cannot find module '@lingo.dev/compiler'"
Run npm install @lingo.dev/compiler
"Config must be async function" (Next.js)
Wrap your config in async function:
export default async function () {
return await withLingo(...);
}
"sourceLocale is required"
Add sourceLocale: "en" to your config.
Translations not showing Check:
LingoProvideris in root layout.lingo/metadata.jsonexists- No console errors
FAQ
Can I run both compilers simultaneously? No. Uninstall the old compiler before installing the new one.
Do I lose my translations? Not if you migrate them manually. Otherwise, regenerate using AI (costs API credits).
What if my framework isn't supported yet? The new compiler currently supports Next.js and Vite. Other frameworks coming soon. Continue using the old compiler or contribute support for your framework.
How long does migration take?
- Simple project: 15-30 minutes
- Complex project: 1-2 hours
- Most time is testing and verifying translations
Should I migrate now or wait? Migrate when:
- You need new features (build modes, overrides, custom resolvers)
- You're starting a new project
- You want better DX
Wait if:
- Your project is working fine with old compiler
- You need frameworks not yet supported by new compiler
Next Steps
- Quick Start — Set up the new compiler
- Configuration Reference — Explore all options
- Best Practices — Recommended workflows
- Troubleshooting — Common issues