Best Practices

Recommended patterns and workflows for @lingo.dev/compiler.

Development Workflow

Use Pseudotranslator by Default

Do:

{
  dev: {
    usePseudotranslator: true, // Fast, free, instant feedback
  }
}

Why:

  • Instant feedback—no API latency
  • Zero cost—no API credits consumed
  • Visual markers show what gets translated
  • Tests UI with varying text lengths

Disable only when reviewing actual translation quality.

Separate Development, CI, and Production

Development:

{
  buildMode: "translate",
  dev: {
    usePseudotranslator: true,
  }
}

CI:

{
  buildMode: "translate",
  dev: {
    usePseudotranslator: false,
  }
}

Production:

{
  buildMode: "cache-only",
}

This workflow:

  • Keeps development fast and cheap
  • Generates real translations in CI once per deployment
  • Makes production builds deterministic and fast

Translation Strategy

Let AI Handle Most Translations

Do:

<p>Welcome to our application</p>

Don't:

<p data-lingo-override={{ es: "...", de: "...", fr: "..." }}>
  Welcome to our application
</p>

Use overrides sparingly—only for:

  • Brand names
  • Technical terms requiring specific translations
  • Legal text requiring certification
  • Marketing copy needing human review

Use Overrides Consistently

Do:

// Consistent brand name across app
<h1 data-lingo-override={{ es: "Lingo.dev", de: "Lingo.dev" }}>
  Lingo.dev
</h1>

<p>
  Welcome to <span data-lingo-override={{ es: "Lingo.dev", de: "Lingo.dev" }}>
    Lingo.dev
  </span>
</p>

Don't:

<h1 data-lingo-override={{ es: "Lingo.dev" }}>Lingo.dev</h1>
<p>Welcome to Lingo.dev</p> // Missing override—inconsistent

Configuration

Start Simple

Do:

{
  sourceLocale: "en",
  targetLocales: ["es", "de"],
  models: "lingo.dev",
}

Don't:

{
  sourceLocale: "en",
  targetLocales: ["es", "de", "fr", "pt", "it", "ja", "zh", "ar", "ru", "ko"],
  models: {
    "en:es": "groq:...",
    "en:de": "google:...",
    // Complex mappings for 10 locales
  },
  prompt: "Long custom prompt...",
  pluralization: { enabled: false },
}

Start with 2-3 target locales. Add more as needed. Avoid premature optimization.

Use Lingo.dev Engine

Do:

{
  models: "lingo.dev" // Simple, optimized, supports all features
}

Don't:

{
  models: {
    "*:*": "groq:...", // Requires manual model selection
  }
}

Lingo.dev Engine provides:

  • Automatic model selection
  • Fallback handling
  • Translation memory
  • Glossary support

Use direct LLM providers only if you need full control or cost optimization.

Locale Detection

Do:

{
  localePersistence: {
    type: "cookie",
    config: {
      name: "locale",
      maxAge: 31536000,
    },
  },
}

When to customize:

  • Need localStorage (SPA preference)
  • URL-based routing (/en/about)
  • Subdomain routing (es.example.com)
  • Database-backed user preferences

Only implement Custom Locale Resolvers when the default doesn't fit.

Version Control

Commit .lingo/ Directory

Do:

git add .lingo/
git commit -m "chore: update translations"
git push

Why:

  • Version control tracks translation changes
  • Team shares translations
  • CI/CD uses committed translations
  • Production builds don't need API keys

Commit After CI Runs

Do (in CI):

- name: Generate translations
  run: npm run build

- name: Commit translations
  run: |
    git add .lingo/
    git commit -m "chore: update translations" || exit 0
    git push

This ensures production builds always have up-to-date translations.

CI/CD

Generate Translations in CI

Do:

# GitHub Actions
- name: Generate translations
  env:
    LINGODOTDEV_API_KEY: ${{ secrets.LINGODOTDEV_API_KEY }}
  run: npm run build

Don't:

# Production build without API key
- name: Build
  run: npm run build # Fails if translations missing

Generate translations in CI where you have API keys. Production builds use cached translations.

Use cache-only in Production

Do:

# Production build
LINGO_BUILD_MODE=cache-only npm run build

Don't:

# Production build with translate mode
LINGO_BUILD_MODE=translate npm run build # Non-deterministic, requires API keys

Performance

Enable Pluralization Selectively

Do (if you use plural forms):

{
  pluralization: {
    enabled: true,
  }
}

Do (if you don't use plurals):

{
  pluralization: {
    enabled: false, // Skip plural detection—faster builds
  }
}

Pluralization adds small overhead (one LLM call per text with numbers). Disable if not needed.

Use Fast Models for Pluralization

Do:

{
  pluralization: {
    enabled: true,
    model: "groq:llama-3.1-8b-instant", // Fast, cheap
  }
}

Don't:

{
  pluralization: {
    model: "openai:gpt-4o", // Expensive overkill for plural detection
  }
}

Optimize Locale-Pair Mapping

Do (cost optimization):

{
  models: {
    "en:es": "groq:llama-3.3-70b-versatile", // Fast & cheap
    "en:ja": "openai:gpt-4o", // High quality for complex language
    "*:*": "lingo.dev", // Fallback
  }
}

Use fast/cheap models for similar languages (Romance, Germanic). Use high-quality models for complex languages (CJK, Arabic).

Testing

Test with Pseudotranslator First

Do:

  1. Enable pseudotranslator
  2. Test all UI components
  3. Fix layout issues (overflow, truncation)
  4. Then generate real translations

Why:

  • Pseudotranslations are instant
  • Reveals layout problems early
  • Saves API costs

Test All Target Locales

Do:

// Test with locale switcher
<LanguageSwitcher /> // Switch between all locales

// Or manually test
setLocale("es"); // Spanish
setLocale("de"); // German
setLocale("fr"); // French

Verify each locale:

  • Translations appear correctly
  • Layout accommodates text length
  • No untranslated text
  • RTL languages render correctly (if applicable)

Error Handling

Handle Missing Translations Gracefully

The compiler fails build if translations are missing. This is intentional—better to catch missing translations early than ship broken UI.

If build fails:

  1. Run with buildMode: "translate" to generate missing translations
  2. Commit .lingo/metadata.json
  3. Retry production build with buildMode: "cache-only"

Monitor Translation Failures

In CI, check for translation errors:

- name: Generate translations
  run: npm run build 2>&1 | tee build.log

- name: Check for translation errors
  run: |
    if grep -q "Failed to generate translation" build.log; then
      echo "Translation generation failed"
      exit 1
    fi

Maintenance

Regular Cleanup

Periodically remove unused translations:

# Backup first
cp .lingo/metadata.json .lingo/metadata.backup.json

# Manual: Search for each hash in codebase, remove if not found

# Automated (coming soon):
npx @lingo.dev/compiler clean

Monitor File Size

.lingo/metadata.json grows with your app. If it becomes large (>5 MB):

  • Consider splitting into multiple apps
  • Archive old translations
  • Use automated cleanup

Common Anti-Patterns

Don't Over-Use Overrides

Bad:

<p data-lingo-override={{ es: "...", de: "...", fr: "..." }}>
  Welcome to our app
</p>

Let AI handle regular text. Overrides are for exceptions.

Don't Commit API Keys

Bad:

// next.config.ts
{
  models: "lingo.dev",
  apiKey: "your-api-key-here", // NEVER commit API keys
}

Good:

# .env (not committed)
LINGODOTDEV_API_KEY=your_key_here

Don't Use translate Mode in Production

Bad:

// production config
{
  buildMode: "translate", // Non-deterministic, requires API keys
}

Good:

{
  buildMode: "cache-only", // Deterministic, no API keys
}

Don't Skip Version Control

Bad:

# .gitignore
.lingo/ # DON'T ignore translations

Good:

# .gitignore
.env # Ignore API keys only

Migration Strategy

Gradual Rollout

When adding compiler to existing app:

  1. Start with 1-2 locales
  2. Enable pseudotranslator
  3. Test all pages
  4. Fix layout issues
  5. Add more locales
  6. Generate real translations
  7. Deploy

Don't try to translate 20 locales on day one.

Incremental Adoption

You don't need to translate entire app at once:

{
  useDirective: true, // Opt-in per file
}

Add 'use i18n' directive to files you want to translate:

'use i18n'; // This file gets translated

export function HomePage() {
  return <h1>Welcome</h1>;
}

Other files remain untranslated until you opt them in.

Next Steps