feat: make orphan key handling configurable#75
Merged
Conversation
## Changed - Orphan keys (present in a target file, absent from the source) are still preserved by default, so existing projects are unaffected - `lara init` now writes `translation.orphanKeys` explicitly in the generated config ## New - `translation.orphanKeys: keep | delete` in lara.yaml, controlling whether target-only keys survive a translation run - `lara-cli translate --orphan-keys <keep|delete>` overrides the config for a single run; rejected with `--file`/`--text`, which bypass the engine - Source-deleted keys are still removed in both modes, and non-translatable entries (Android `translatable="false"`, `.xcstrings` `shouldTranslate: false`) are never deleted - Docs for both the setting and the flag, plus integration coverage for every keyed format, the Android carve-out, and CLI/config precedence in both directions
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.
Tip: disable this comment in your organization's Code Review settings.
There was a problem hiding this comment.
Pull request overview
This PR makes orphan key handling configurable in the translation engine and CLI, allowing projects to either preserve target-only keys (default) or delete them so targets mirror the source exactly.
Changes:
- Add
translation.orphanKeysto the config schema with modeskeep(default) anddelete, plus a--orphan-keysCLI override with correct precedence. - Update the translation engine’s orphan weaving to short-circuit when
orphanKeys: delete, letting existing merge-based parsers naturally prune target-only entries. - Add/expand integration and schema tests and update documentation/versioning to reflect the new feature.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/modules/translation/translation.engine.ts | Adds orphanKeys option and skips orphan weaving when mode is delete. |
| src/modules/config/config.types.ts | Introduces orphan key modes/defaults and extends Zod schema with translation.orphanKeys. |
| src/messages/messages.ts | Adds error message for invalid flag usage with direct translation modes. |
| src/cli/cmd/translate/translate.ts | Adds --orphan-keys option, enforces incompatibility with --file/--text, and passes resolved mode into the engine. |
| src/cli/cmd/init/init.ts | Ensures generated config explicitly includes the new translation.orphanKeys field. |
| src/tests/utils/config-orphan-keys.test.ts | Adds schema-level tests for parsing/defaults/invalid values of translation.orphanKeys. |
| src/tests/integration/orphan-keys.integration.test.ts | Adds end-to-end coverage for keep vs delete behavior across keyed formats and CLI/config precedence. |
| src/tests/integration/direct-translate.integration.test.ts | Verifies --orphan-keys is rejected with direct translation (--text). |
| README.md | Updates version badge to 1.6.0. |
| package.json | Bumps package version to 1.6.0. |
| docs/config/structure.md | Documents the new orphan key configuration and behavior nuances. |
| docs/config/README.md | Updates config example and links to orphan key docs. |
| docs/commands/translate.md | Documents --orphan-keys and clarifies orphan key behavior/options. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Orphan keys — keys that exist in a target locale file but not in the source — have been preserved unconditionally since 1.5.0. Some projects want the target to mirror the source exactly and treat leftovers as noise, so this makes the behaviour configurable without changing the default.
Configuration
Plus a per-run override:
The flag takes precedence over the config file in both directions, so an explicit
--orphan-keys keepwins over a config set todelete. It is rejected with--file/--text, which bypass the translation engine entirely, matching how--forceand--pathsare already handled.Implementation
The engine turned out to be the single lever — no parser needed modification.
weaveOrphansinclassifyEntriesis the only place orphan preservation is introduced, and every merge-based parser already derives its own graft filter from the engine's emitteddata:xcode-stringsandxcode-stringsdictfilter onkept = dataKeys/dataEntryKeysxcode-xcstringsprunes target localizations absent fromdatajson,ts,vue,poare data-driven throughoutSo shrinking
datashrinks the graft automatically. The diff touches zero parser files.Behaviour that does not change
deleted, so they are not orphans.delete. Androidtranslatable="false"resources and.xcstringsshouldTranslate: falseentries are skipped byparse(), so they never reach the engine — they are invisible-to-the-engine entries, not orphans. Deleting them would be data loss.Testing
Integration coverage for all 8 keyed formats in both modes, sharing one fixture seed per format so the two halves cannot drift. Plus the Android carve-out, the schema default when the
translationsection is absent, CLI/config precedence in both directions, and that source-deleted keys disappear in both modes. Schema-level tests cover parsing, the default, and rejection of invalid values.Verified the tests actually bite: disabling the engine guard fails 9 of them.
849 tests pass; build, lint and format checks are clean.
Version
Minor bump to 1.6.0 — backward-compatible addition, default behaviour unchanged.