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 overridesdata-lingo-override attribute
  • 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 sourceRoot option
  • withLingo wrapper instead of lingoCompiler.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
  • sourceLocale is 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 loadDictionary prop—handled internally
  • Simpler API

5. File Structure

Old:

lingo/
├── dictionary.js
├── meta.json
└── [locale]/
    └── *.json

New:

.lingo/
└── metadata.json

Changes:

  • Directory renamed (.lingo vs lingo)
  • 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 FeatureNew EquivalentNotes
dictionary.js.lingo/metadata.jsonDifferent format
meta.json.lingo/metadata.jsonMerged into single file
"use i18n" (required)"use i18n" (optional)Now opt-in, not required
Custom promptsprompt config optionSame functionality
Editing translationsdata-lingo-overrideAttribute-based overrides
Skipping translationsdata-lingo-override + emptyOr use useDirective
Overriding translationsdata-lingo-overrideAttribute-based
Switching localesuseLocale/setLocaleSame API
LLM providersmodels configSame providers supported

New Features (Not in Old Compiler)

  • Build modestranslate vs cache-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:

  1. Delete old lingo/ directory
  2. Run new compiler
  3. 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

  1. Generate new translations for most text
  2. Use data-lingo-override for 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:

  1. LingoProvider is in root layout
  2. .lingo/metadata.json exists
  3. 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