Key Preserving
Lingo.dev CLI allows you to preserve specific translation keys so they are initialized once and never overwritten by subsequent CLI operations. Preserved keys are copied from source as placeholders, then protected from automatic translation updates.
When you preserve keys, the CLI initializes them with source values and then skips them during future translation runs.
Setting Up Key Preserving
Add preservedKeys to your bucket configuration in i18n.json:
{
"locale": {
"source": "en",
"targets": ["es", "fr", "de"]
},
"buckets": {
"json": {
"include": ["locales/[locale].json"],
"preservedKeys": ["legal/privacy", "legal/terms", "marketing/tagline"]
}
}
}
The preservedKeys array uses forward slash (/) notation to specify nested keys and asterisk (*) to match multiple keys.
How Key Preserving Works
During translation processing, Lingo.dev CLI:
- Identifies preserved keys from your configuration
- Excludes them from translation — preserved keys are never sent to the translation engine
- Initializes missing keys — copies source values as placeholders for new target files
- Protects existing values — keeps manually translated content unchanged
Example workflow:
// locales/en.json (source)
{
"welcome": "Welcome to our platform",
"legal": {
"privacy": "We respect your privacy and protect your data.",
"terms": "By using this service, you agree to our terms."
}
}
With preserved keys configuration:
{
"preservedKeys": ["legal/privacy", "legal/terms"]
}
Generated Spanish translation (first run):
// locales/es.json (generated)
{
"welcome": "Bienvenido a nuestra plataforma",
"legal": {
"privacy": "We respect your privacy and protect your data.",
"terms": "By using this service, you agree to our terms."
}
}
The welcome key gets translated. The legal section is copied as-is for manual translation later.
Nested Key Paths
Use forward slash (/) notation to preserve keys at any depth:
{
"preservedKeys": [
"legal/privacy/full",
"legal/terms/conditions",
"marketing/campaigns/holiday"
]
}
This notation works with complex nested structures:
// Source structure
{
"legal": {
"privacy": {
"full": "Full privacy policy text..."
}
}
}
The path legal/privacy/full preserves this specific nested key.
Keys with Dots
Forward slash notation handles keys that contain dots in their names:
// Source with dotted key names
{
"legal": {
"privacy.policy": "Privacy policy content",
"terms.of.service": "Terms of service content"
}
}
Preserve these keys with:
{
"preservedKeys": ["legal/privacy.policy", "legal/terms.of.service"]
}
Multiple Bucket Types
Different file formats can have different preserved keys:
{
"buckets": {
"json": {
"include": ["locales/[locale].json"],
"preservedKeys": ["legal/privacy", "legal/terms"]
},
"yaml": {
"include": ["translations/[locale].yml"],
"preservedKeys": ["compliance/gdpr", "compliance/ccpa"]
}
}
}
Each bucket type maintains its own preserved keys list based on the content structure.
Key Preserving vs Key Locking
Key preserving and key locking serve different purposes:
Key Preserving (preservedKeys):
- Keys are initialized once with source values as placeholders
- Existing target values are never overwritten by CLI
- Used for content requiring manual translation or legal review
Key Locking (lockedKeys):
- Keys are included in translation processing but values remain unchanged
- Locked keys always match source values exactly
- Used for technical identifiers, component names, or values that must stay consistent
Example comparison:
// Source file
{
"welcome": "Welcome",
"system": {
"component": "Lingo.dev Engine"
},
"legal": {
"privacy": "Privacy policy text"
}
}
// Configuration
{
"lockedKeys": ["system/component"],
"preservedKeys": ["legal/privacy"]
}
// Generated target file
{
"welcome": "Bienvenido",
"system": {
"component": "Lingo.dev Engine"
},
"legal": {
"privacy": "Privacy policy text"
}
}
After manually translating legal/privacy to Spanish, subsequent runs keep the manual translation. The system/component key always stays as English regardless of target file content.