Translate the remaining German UI text into English - #22
Merged
Conversation
Switching the app to English left large parts of the interface German:
settings and section headings, action titles and descriptions, status and
error text. Two mechanisms coexist — `AppLanguage.text(de, en)` for plain
`String`s and `Localizable.strings` keyed on the German source for
`LocalizedStringKey` positions — and both had gaps.
Verified first that `.environment(\.locale, …)` really does drive the
strings table (it does, and a missing `de.lproj` correctly falls back to
the German keys rather than to English), so the mechanism was sound and
only its coverage was missing.
- Add 230 entries to `en.lproj/Localizable.strings`, including every
German `title`, `description`, `category`, `availabilityNote` and
gesture `shortcut` carried in the two action catalogue JSON files.
- Translate the catalogue on read in `CodexActionCatalog` rather than at
decode time, so a language switch takes effect without a restart. The
JSON stays the German source of truth.
- Wrap the String-typed text that never passes through
`LocalizedStringKey`: view models, diagnostics entries, device and
bridge status, permission notifications, and the errors surfaced via
`lastError`.
- Rewrite interpolated `Text("… \(x) …")` to `AppLanguage.text`, which
`LocalizedStringKey` interpolation does not resolve reliably.
Persisted labels stay German on disk and are looked up at render time
via `KeyboardAction.displayLabel`, so saved profiles and the legacy
migration keep working. Accessibility strings matched against Claude's
own German UI are deliberately untouched — translating those would break
the automation.
Two latent bugs surfaced along the way: `sampleThreads` and the LED
colour presets were stored statics, so their text froze at whatever
language was active on first access; and `control.title` prefix strips
looked for "Taste " even in English.
Also fail the build loudly when `en.lproj` is missing instead of
silently shipping a German-only app.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Switching the app to English left large parts of the interface German — settings and section headings, action titles and descriptions, status and error text. Five parallel audits swept every view, model, store, service and both action-catalogue JSON files.
Root cause
Two localization mechanisms coexist and both had gaps:
AppLanguage.text(de, en)for plainStrings.Localizable.strings, keyed on the German source, forLocalizedStringKeypositions.I verified empirically (rendering
Textunder each locale) that.environment(\.locale, …)really does drive the strings table, and that a missingde.lprojcorrectly falls back to the German keys rather than to English. The mechanism was sound; only its coverage was missing.The biggest single gap was the action catalogues:
CodexActions.jsonandClaudeActions.jsoncarry Germandescription,categoryandavailabilityNotevalues that were rendered raw.Changes
en.lproj/Localizable.strings, covering every German value in both catalogue JSON files, including gestureshortcuts like⌘F17 halten.CodexActionCatalogtranslates on read, not at decode time — the catalogue is built once inAppState.init, so decode-time translation would have needed an app restart to follow a language switch. The JSON stays the German source of truth.AppLanguage.text: view models, diagnostics entries, device and bridge status, permission notifications, and the errors surfaced throughlastError.Textrewritten toAppLanguage.text—LocalizedStringKeyinterpolation does not resolve reliably.Two latent bugs found along the way
OnboardingAssignmentDemo.sampleThreadsand the LED colour presets were storedstatic lets. A stored static initialises lazily exactly once, so their text froze at whatever language was active on first access and never followed a later switch. Now computed.control.titleprefix strips searched for"Taste ", which silently stopped matching once the title was English.Deliberately not translated
"menü","aufwand", effort/model keywords). Translating these would break the automation.KeyboardAction.displayLabel, so saved profiles and the legacyPet anzeigenmigration keep working.PendingApproval.summary,lastResolvedAction).Each of these was traced before being left alone; a verification script re-checks that the remaining German literals are exactly this set.
Verification
swift buildclean; 117 tests pass.testEnglishLocalizationResolvesForTableAndActionCatalogasserts the strings table is reachable, that unknown keys fall back to German rather than an empty string, and that no catalogue field still contains German characters under English. This is what would have caught the original bug.build_and_run.shnow fails loudly whenen.lprojis missing instead of silently shipping a German-only app.Not verified: I did not launch the built
.appfor a visual pass, since rebuilding can void the Accessibility grant.🤖 Generated with Claude Code