Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Lara Cli automates translation of your i18n files with a single command, preserv

Supports multiple file formats including JSON, PO (gettext), TypeScript, Vue I18n single-file components, Markdown and MDX files, Android XML string resource files, Xcode localization files (.strings, .stringsdict, .xcstrings), and plain text (.txt) files. See [Supported Formats](docs/config/formats.md) for details.

[![Version](https://img.shields.io/badge/version-1.5.0-blue.svg)](https://github.com/translated/lara-cli)
[![Version](https://img.shields.io/badge/version-1.6.0-blue.svg)](https://github.com/translated/lara-cli)

</div>

Expand Down
16 changes: 15 additions & 1 deletion docs/commands/translate.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ lara-cli translate [options]
| `-p, --paths <paths>` | Comma-separated list of specific file paths to translate (overrides config) |
| `-f, --force` | Force retranslation of all content, even if unchanged |
| `--no-trace` | Prevent server-side storage of translated content |
| `--orphan-keys <mode>` | `keep` or `delete`. What to do with keys that exist only in target files. Overrides `translation.orphanKeys` in `lara.yaml` for this run. Cannot be combined with `--file` or `--text` |
| `-h, --help` | Display help information |

## Examples
Expand Down Expand Up @@ -123,7 +124,20 @@ When you modify source locale files, the tool automatically detects changes and

### Keys Only Present in a Target File

Keys that exist in a target locale file but not in the source (for example, translations added manually or entries specific to one locale) are **preserved** and kept at their original position. They are never overwritten or removed by translation. A key is only removed from a target when it was previously translated from the source and is then deleted from the source file.
Keys that exist in a target locale file but not in the source (for example, translations added manually or entries specific to one locale) are called **orphan keys**. By default they are **preserved** and kept at their original position — never overwritten or removed by translation. A key is only removed from a target when it was previously translated from the source and is then deleted from the source file.

You can change this with `translation.orphanKeys` in `lara.yaml`, or per run with the `--orphan-keys` flag:

```yaml
translation:
orphanKeys: delete # keep (default) | delete
```

```bash
lara-cli translate --orphan-keys delete
```

With `delete`, orphan keys are removed so each target file mirrors the source exactly. Keys deleted from the source are still removed in both modes, and non-translatable entries (Android `translatable="false"`, `.xcstrings` `shouldTranslate: false`) are never deleted. See [Orphan Keys](../config/structure.md#orphan-keys) for the full behaviour.

> Notes: for Gettext PO, orphan messages are preserved but their exact interleaving with source messages is approximate. For Xcode `.stringsdict`, an orphan plural entry keeps its full original structure.

Expand Down
5 changes: 4 additions & 1 deletion docs/config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ memories:
glossaries:
- gls_xyz789
noTrace: false
translation:
batchSize: 50
orphanKeys: keep
files:
json:
include:
Expand All @@ -39,7 +42,7 @@ Lara CLI supports multiple file formats. See [Supported Formats](./formats.md) f

The configuration is divided into several sections:

- **[Configuration Schema](./structure.md)** - Schema structure and organization of the configuration file
- **[Configuration Schema](./structure.md)** - Schema structure and organization of the configuration file, including [translation batching](./structure.md#translation-batching) and [orphan key handling](./structure.md#orphan-keys)
- **[Supported Formats](./formats.md)** - List of supported file formats
- **[Locales](./locales.md)** - Source and target language configuration
- **[Files](./files.md)** - File paths and exclusion patterns
Expand Down
46 changes: 44 additions & 2 deletions docs/config/structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ lara.yaml
├── memories # Translation memory settings
├── glossaries # Terminology settings
├── noTrace # No-trace mode (prevents server-side storage)
├── translation # Translation tuning (batch size)
├── translation # Translation tuning (batch size, orphan key handling)
└── files # File path and processing rules
```

Expand Down Expand Up @@ -53,7 +53,8 @@ noTrace: false

# Translation tuning
translation:
batchSize: 50 # Max keys sent per translation request (default: 50)
batchSize: 50 # Max keys sent per translation request (default: 50)
orphanKeys: keep # What to do with target-only keys: keep | delete (default: keep)

# File path and processing rules
files:
Expand Down Expand Up @@ -130,6 +131,47 @@ translation:
- If a batch request fails after retries, the engine automatically falls back to translating each key in that batch one by one, so a single problematic string cannot block the rest of the file.
- `batchSize` defaults to `50` when the `translation` section is omitted. Existing `lara.yaml` files continue to work without changes.

## Orphan Keys

An **orphan key** is a key that exists in a target locale file but not in the source file. They usually appear when a translator adds a locale-specific entry by hand, or when a key is renamed in one place only.

```yaml
translation:
orphanKeys: keep # keep | delete
```

- **`keep`** (default) — orphan keys are preserved untouched, at their original position in the file. Nothing you add to a target file by hand is ever silently lost.
- **`delete`** — orphan keys are removed, so each target file mirrors the source exactly. Use this when the source is the single point of truth and target-only entries are considered leftovers.

Given this source and target:

```json
// en.json (source) // it.json (target)
{ {
"one": "One", "one": "Uno",
"two": "Two" "only_it": "Solo italiano",
} "two": "Due"
}
```

`only_it` is an orphan. With `keep` it stays between `one` and `two`; with `delete` it is removed and the file ends up with just `one` and `two`.

### Important distinctions

- **Keys deleted from the source are always removed**, in both modes. Lara CLI tracks the source with a checksum file, so a key that *used to* exist and was then removed is recognised as deleted rather than as an orphan.
- **Non-translatable entries are never deleted**, even with `orphanKeys: delete`. These are not orphans — they are entries the parser deliberately skips:
- Android XML resources marked `translatable="false"`
- Xcode `.xcstrings` entries marked `shouldTranslate: false`
- **Gettext PO files** cannot always restore an orphan's original position; preserved orphan messages are appended at the end of the file.

### Overriding per run

The [`translate`](../commands/translate.md) command accepts `--orphan-keys <keep|delete>`, which overrides the config file for a single run — handy for a one-off cleanup without editing `lara.yaml`:

```bash
lara-cli translate --orphan-keys delete
```

## Related Topics

Each section of the configuration has its own detailed documentation:
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@translated/lara-cli",
"type": "module",
"version": "1.5.0",
"version": "1.6.0",
"description": "CLI tool for automated i18n file translation using Lara Translate",
"repository": {
"type": "git",
Expand Down
17 changes: 17 additions & 0 deletions src/__tests__/integration/direct-translate.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,23 @@ describe('Direct Translation Integration Tests', () => {
])
).rejects.toThrow();
});

it('should error when --orphan-keys is used with --text', async () => {
// Orphan handling only applies to the config-driven flow, which builds a
// TranslationEngine; direct mode never touches existing target files.
await expect(
executeCommand(translateCommand, [
'--text',
'Hello',
'--source',
'en',
'--target',
'fr',
'--orphan-keys',
'delete',
])
).rejects.toThrow();
});
});

describe('file mode - txt files', () => {
Expand Down
Loading