Project Structure
Understanding the .lingo/ directory and its contents.
Directory Overview
When you first run the compiler, it creates a .lingo/ directory in your project root:
your-project/
├── .lingo/
│ ├── metadata.json
│ ├── locale-resolver.server.ts (optional)
│ └── locale-resolver.client.ts (optional)
├── src/
├── package.json
└── ...
metadata.json
The core file containing all translation data.
Structure
{
"version": "1",
"sourceLocale": "en",
"targetLocales": ["es", "de", "fr"],
"translations": {
"abc123def456": {
"source": "Welcome to our app",
"context": {
"file": "app/page.tsx",
"line": 12,
"component": "HomePage"
},
"locales": {
"es": "Bienvenido a nuestra aplicación",
"de": "Willkommen in unserer App",
"fr": "Bienvenue dans notre application"
},
"metadata": {
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T10:30:00Z"
}
}
}
}
Key Fields
Version: Metadata format version. Current: "1"
Source/Target Locales: Your configured locales
Translations: Hash-based mapping of all translatable strings:
- Hash (
abc123def456): Stable identifier based on source text + context - source: Original English text
- context: Where this text appears (file, line, component)
- locales: Translations for each target locale
- metadata: When translation was created/updated
Hash Generation
Hashes are deterministic:
- Based on source text + component context
- Same text in different locations = different hashes
- Enables context-aware translations
Example:
// app/home/page.tsx
<button>Submit</button> // Hash: abc123
// app/checkout/page.tsx
<button>Submit</button> // Hash: def456 (different context)
Custom Locale Resolvers
Optional files for customizing locale detection and persistence.
locale-resolver.server.ts
Server-side locale detection (Next.js only):
// .lingo/locale-resolver.server.ts
export async function getServerLocale(): Promise<string> {
// Your custom logic
return "en";
}
locale-resolver.client.ts
Client-side locale detection and persistence:
// .lingo/locale-resolver.client.ts
export function getClientLocale(): string {
// Detect locale
return "en";
}
export function persistLocale(locale: string): void {
// Save locale preference
}
If these files don't exist, the compiler uses default cookie-based behavior.
See Custom Locale Resolvers for details.
Version Control
Should you commit .lingo/?
Yes. The .lingo/ directory should be version-controlled:
Commit:
metadata.json— Contains all translations- Custom locale resolvers (if created)
Don't commit:
- Nothing—all files in
.lingo/should be committed
Why Commit Translations?
- Version control — Track translation changes alongside code
- Team collaboration — Share translations across team
- CI/CD — Production builds use committed translations
- Audit trail — See when translations changed and why
Git Integration
Add .lingo/ to your repo:
git add .lingo/
git commit -m "chore: update translations"
git push
The compiler automatically updates .lingo/metadata.json when:
- New translatable text is added
- Existing text is modified
- Translations are generated
Commit these updates regularly.
File Size
metadata.json grows with your app:
- Small app (50 strings): ~10 KB
- Medium app (500 strings): ~100 KB
- Large app (5000 strings): ~1 MB
This is normal and acceptable for version control.
Cleaning Up
Remove Unused Translations
Over time, you may accumulate unused translations (from deleted components).
Manual cleanup:
- Search for hash in your codebase
- If not found, delete from
metadata.json
Automated cleanup (coming soon):
npx @lingo.dev/compiler clean
This will remove unused translations automatically.
Reset Translations
To regenerate all translations from scratch:
# Backup current translations
cp .lingo/metadata.json .lingo/metadata.backup.json
# Delete metadata
rm .lingo/metadata.json
# Regenerate
npm run dev # or npm run build
Migration
From Old Compiler
The old compiler used different file structure:
Old:
lingo/
├── dictionary.js
├── meta.json
└── [locale]/
└── *.json
New:
.lingo/
└── metadata.json
Migration is not automated. See Migration Guide for details.
Inspecting Translations
View in Editor
Open .lingo/metadata.json in your editor:
{
"translations": {
"abc123": {
"source": "Welcome",
"locales": {
"es": "Bienvenido"
}
}
}
}
Search for Translation
Find translation by source text:
grep -r "Welcome" .lingo/metadata.json
Find by Hash
grep -r "abc123" .lingo/metadata.json
Pretty Print
cat .lingo/metadata.json | jq '.'
Common Questions
Can I edit metadata.json manually?
Yes, but not recommended. Use data-lingo-override instead—it's safer and version-controlled in source code.
What if I delete metadata.json? The compiler regenerates it on next build. All translations will be generated fresh (costs API credits).
Can I move .lingo/ to a different directory?
Yes. Configure via lingoDir option:
{
lingoDir: "translations"
}
Does metadata.json contain sensitive data? No. It only contains source text and translations—no API keys or secrets.
Can I merge metadata.json from multiple branches? Yes. Git handles merges automatically. Conflicts are rare (hashes are unique).
What if two branches add the same translation? Git merges them automatically. If hashes differ (different context), both are kept.
Next Steps
- Custom Locale Resolvers — Customize persistence
- Development Tools — Work with pseudotranslator
- Build Modes — Understand caching behavior