Skip to content

feat(desktop): materialize German (de) locale from #51762 - #92909

Draft
andrexibiza wants to merge 5 commits into
NousResearch:mainfrom
andrexibiza:materialize/pr-51762-base-891ec3fb
Draft

andrexibiza wants to merge 5 commits into
NousResearch:mainfrom
andrexibiza:materialize/pr-51762-base-891ec3fb

Conversation

@andrexibiza

@andrexibiza andrexibiza commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Trusted materialization of #51762, preserving Oliver Hees' complete German (de) desktop locale while removing the external-fork workflow-approval boundary that still prevents the source PR from producing executable CI evidence.

Closes #51217

Provenance and attribution

The original four source commits retain Oliver Hees <oliverhees@gmail.com> as author. After current-main renamed the zones locale contract from showHeader / hideHeader to showTabStrip / hideTabStrip, Oliver added the exact two-line repair at the source head. The materialized branch now carries that repair byte-for-byte with explicit co-authorship: its pre-fix de.ts blob matched the source commit's parent exactly, and its repaired blob matches the source head exactly. The materializer does not claim translation authorship.

Why this PR exists

At source head fcc36318…, all three pull-request workflows remain blocked at action_required before jobs execute:

This branch removes that transport boundary without rewriting the translation or widening the locale contract.

Exact scope

The materialized head has the same five-commit result, eight-file diff, and +3759/-10 footprint as the current source head:

  • apps/desktop/DESIGN.md
  • apps/desktop/src/i18n/catalog.ts
  • apps/desktop/src/i18n/context.test.tsx
  • apps/desktop/src/i18n/de.ts
  • apps/desktop/src/i18n/languages.test.ts
  • apps/desktop/src/i18n/languages.ts
  • apps/desktop/src/i18n/types.ts
  • contributors/emails/oliverhees@gmail.com

No renderer components, build tooling, workflow files, or unrelated locale implementation are included.

Collision disposition

The open German-locale PRs collide in the locale registration/catalog surface. This is the narrow seed and canonical first landing:

Verification

Exact materialized head 31015d09983f2f1f94b6f3a4eb9e656071eb9aec is mergeable and all current pull-request workflows completed successfully:

The prior red JS & TS aggregate was not a JavaScript test failure. All JS/Vitest surfaces passed; the sole blocker was desktop TypeScript error TS2353 in de.ts because the German locale still used the retired showHeader key. The exact source-author repair is now applied, desktop typecheck passes, and the required-check aggregator is green.

oliverhees and others added 4 commits August 23, 2026 06:40
Adds German as a desktop UI locale using the existing per-file
defineLocale() pattern already shipped for ja/zh/zh-hant/ar. No new
build tooling, no sync step.

- New apps/desktop/src/i18n/de.ts covering every Translations section,
  including the surfaces that landed since June (starmap, webhooks,
  zones) and the settings field labels.
- Register `de` in the Locale union (types.ts), the picker and alias
  table (languages.ts: de, de-DE, de-AT, de-CH, de-LI, de-LU, german,
  deutsch), and the catalog (catalog.ts).
- languages.test.ts extends the existing `ar` assertions rather than
  replacing them; both locales are covered side by side.
- context.test.tsx used 'de' as its example of an *unsupported*
  language, which this change would silently invert. Switched to 'xx'
  so the assertion stays meaningful as more locales land.
- DESIGN.md's i18n contract now lists all six locales (it still said
  four after `ar` shipped).

UI chrome is translated; established product/tech terms stay English
(Gateway, Skill, Toolset, Agent, Cron, Token, MCP, Provider, Worktree).
Informal "du" throughout. Callback-valued strings keep their signatures
so remote-reauth, webhook toggles and the sidebar toggle format
correctly.

Verified against current main: tsc -p . --noEmit clean for this locale,
eslint src/i18n reports no errors (one fewer warning than baseline),
vitest run src/i18n 28/28 pass.

Co-authored-by: Luna <luna161@users.noreply.github.com>
The Contributor Attribution Check fails on any commit whose author email
is neither a GitHub id+login noreply address nor mapped under
contributors/emails/. This branch's commit is authored from
oliverhees@gmail.com, which is neither, so the job would exit 1.

Adds the mapping file per contributors/README.md: one file per email,
no AUTHOR_MAP edit.
Rebased onto current main and filled in every string that landed in
`en.ts` since the branch was last refreshed on 2026-08-10.

- 1080 missing entries translated (902 plain strings, 178 callback-valued),
  covering the surfaces that shipped since: Hermes Cloud sign-in and agent
  discovery (`settings.gateway.cloud*`), registered gateways
  (`settings.connections.*`), desktop and agent plugins
  (`settings.plugins.*`), the SSH connection mode, cron blueprints, the
  skill hub, projects and worktrees in the sidebar, the working-tree
  status stack, quick entry, and the per-tool assistant labels.
- 6 stale keys dropped. `settings.gateway.{appliesTo,allProfiles,
  defaultConnection,profileConnection}` moved to `settings.profileScope`
  upstream, `onboarding.flowSubtitles.loopback` and
  `keybinds.actions.'view.closePreviewTab'` were removed.
- `titlebar.layoutEditorTitle` became `(modifier: string) => string`
  upstream; the German entry is now a callback and interpolates the
  modifier instead of hardcoding ⌘.
- Fixed two pre-existing defects this pass surfaced: `skills.toggleToolset`
  dropped its `enabled` argument, so the accessibility label said
  "umschalten" without naming the target state (every other locale
  interpolates it); `rightSidebar.folderTip` appended an invented
  "click to change folder" hint, while `en`, `ja`, `zh`, `zh-hant`, and
  `ar` all return the bare path.

`de.ts` now covers 2920 of 2920 leaf keys: no missing entries, and no keys
that no longer exist in `en.ts`.

Verified against current main: prettier clean, `tsc -p . --noEmit` reports
0 errors, `eslint src/i18n` reports 0 errors (4 pre-existing warnings in
context.test.tsx, unchanged from baseline), `vitest run src/i18n` 28/28
pass. Every callback entry was checked against its English counterpart for
matching arity and interpolation variables.

Co-authored-by: Luna <luna161@users.noreply.github.com>
…int/fade, intro splash, voice controls, update dialog)
@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/desktop Electron desktop app (apps/desktop/*) area/i18n Localization, locales, translations sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Aug 23, 2026

andrexibiza commented Aug 23, 2026

Copy link
Copy Markdown
Contributor Author

Superseded exact-head settlement — 2026-08-23

The earlier version of this comment described head 158277f1 and a manual review-label gate. That state is no longer current.

Oliver pushed the source repair in #51762 at fcc36318c6bfeb7ee929e1ed0cb4050a4feb7113. I applied that exact two-line de.ts contract repair byte-for-byte to the trusted successor, removed the abandoned one-shot workflow commits from branch history, and force-set the clean materialized head to 31015d09983f2f1f94b6f3a4eb9e656071eb9aec.

Current exact-head receipts:

The prior red JS & TS aggregate was solely desktop TypeScript TS2353 from the retired showHeader locale key; the JS/Vitest surfaces were already passing. The source-author repair changed it to showTabStrip, and the complete required-check graph is now green. No ci-reviewed label rerun or special maintainer recovery remains; #92909 is open, non-draft, mergeable, and ready for normal review/merge.

Source/provenance remains #51762. Broader renderer polish remains #85236 and should rebase after this seed lands.

@oliverhees

Copy link
Copy Markdown

Heads up: the one-shot repair workflow hasn't run. There are no workflow runs on materialize/pr-51762-base-891ec3fb, and the head at cea6cf5 still has showHeader/hideHeader in de.ts, which is why JS & TS is red on the same TS2353.

You don't need to script it. I already pushed the same rename to #51762. Head is fcc36318, rebased onto bdf1047. Verified locally before pushing:

  • tsc -p ., tsc -p tsconfig.electron.json, tsc -p tsconfig.e2e.json all clean
  • npm run lint 0 errors
  • vitest src/i18n/ 4 files, 28 tests pass

Re-materializing from fcc36318 should give you a green JS & TS.

On the Python e2e failure: that's test_plaintext_restart_gateway_in_group_stays_plain_text[telegram] asserting a mock was called. This branch only touches apps/desktop/src/i18n/de.ts, so I don't see a path from a desktop locale string to a Telegram slash-command test. I couldn't compare against another run because no other open PR currently has Python e2e running.

Upstream renamed zones.showHeader/hideHeader to
showTabStrip/hideTabStrip when tab-strip hiding got its own command.
de.ts still carried the old names, which fails the desktop typecheck
(TS2353) on current main.

Materialized from Oliver Hees' source commit
fcc3631. The pre-fix de.ts blob
matched that source commit's parent byte-for-byte.

Co-authored-by: Oliver Hees <oliverhees@gmail.com>
@andrexibiza
andrexibiza force-pushed the materialize/pr-51762-base-891ec3fb branch from 8290fb0 to 31015d0 Compare August 23, 2026 12:48

Copy link
Copy Markdown
Contributor Author

@oliverhees your diagnosis and source repair are now fully materialized.

I replaced the stale German locale keys with the exact de.ts blob from source head fcc36318c6bfeb7ee929e1ed0cb4050a4feb7113, rather than independently rewriting the translation. The target pre-fix blob matched your repair commit's parent byte-for-byte; the repaired blob now matches your source head byte-for-byte. I also removed the abandoned one-shot workflow commits from the branch history.

Clean successor head: 31015d09983f2f1f94b6f3a4eb9e656071eb9aec

Verification is complete:

The earlier Python e2e failure also cleared on the clean head, confirming it was not caused by the locale change. #92909 is open, non-draft, mergeable, and ready for normal review/merge.

@oliverhees

Copy link
Copy Markdown

Confirmed on my side too: the de.ts blob at 31015d09 is b37bc712, same hash as at fcc36318. Byte for byte identical, so nothing got lost in the transfer.

Thanks for taking the byte-exact route instead of rewriting the strings. That's the part I was quietly worried about.

I'll close #51762 as soon as this merges. If review turns up anything else, ping me and I'll fix it the same day.

@oliverhees

Copy link
Copy Markdown

One more push on my side, head is now 39b26831.

I ran a value comparison of de.ts against en.ts and found 230 German leaves still holding the English string, including the whole sendDiagnostics block and the cloud-down boot copy. defineLocale() merges onto the English base, so those render in English and nothing reports it. That commit translates every one that has a German form. Additions only: 102 insertions, 0 deletions, no existing translation touched.

Same checks as before, all clean on bdf1047: three tsc configs, lint with 0 errors, vitest src/i18n/ 28 tests. Value-comparison coverage went from 92.7% to 95.3%, and what is left identical to English is only words that are identical in German plus proper nouns.

Whenever you re-materialize, 39b26831 is the head to take.

@oliverhees

Copy link
Copy Markdown

@andrexibiza the conflict is smaller than it looks, so here's the exact resolution to save you the digging.

One file, apps/desktop/src/i18n/types.ts, one hunk. Main added an import directly above the line this PR touches:

import type { TipId } from '@/lib/tips/catalog'

export type Locale = 'en' | 'zh' | 'zh-hant' | 'ja' | 'ar'

Keep main's import, append | 'de' to the union:

import type { TipId } from '@/lib/tips/catalog'

export type Locale = 'en' | 'zh' | 'zh-hant' | 'ja' | 'ar' | 'de'

That's all of it. I ran git merge-tree against current main (31e41ee) and types.ts is the only file that comes back conflicted.

One thing to know while you're in there: main has added keys since this PR's base, among them gatewayConnectionLostDetail, resetHudLayout and the tips/tours settings block. de.ts goes through defineLocale(), so those fall back to English and still type-check. Nothing breaks, there are just a few more untranslated strings, and I'll pick them up in the follow-up.

If you're short on time, I'll take it off your hands. Add me as a collaborator on your fork and I'll push the resolution myself, or say the word and I'll hand you a patch instead. Whichever is less work for you.


@OutThisLife once this is unblocked, would you mind taking a look? You own this area, so let me keep the ask short.

CI is already green here: 33 check runs, all required checks passing. The diff is mechanical, one locale file plus the small registration changes in catalog.ts, languages.ts and types.ts with the matching test updates, the same shape as the ar locale you shipped in #70870. I'm not asking anyone to read 3,715 lines of German. It can be measured instead, and #93715 adds the script that does it: measured at this PR's head, de sits at 92.7% translated by value, and the follow-up branch I have ready takes it to 95.3%.

@andrexibiza
andrexibiza marked this pull request as draft August 28, 2026 11:02

Copy link
Copy Markdown
Contributor Author

Current-main landing state correction (2026-08-28 06:02 CDT): this PR is now draft because the live object is conflicted, not because the German locale contract is invalid.

Exact delivery head remains 31015d09983f2f1f94b6f3a4eb9e656071eb9aec (5 commits / 8 files), and its registered CI/Docker/Nix runs are green. GitHub currently reports the PR dirty / non-rebaseable against main. Oliver's fresh merge-tree note correctly isolated the conflict to the locale type contract at the main object he tested, but that proof does not transfer automatically across later main heads.

I re-read current main at c30ac90a92097058ddd6f9db3fa2e3182a7bfdcc: apps/desktop/src/i18n/types.ts still carries the new TipId import and still defines Locale = 'en' | 'zh' | 'zh-hant' | 'ja' | 'ar'. The semantic resolution remains: preserve current-main imports/contracts and add de; do not overwrite newer i18n surface.

Landing gate: rematerialize/rebase the exact 8-file locale object onto current main, prove the resulting conflict set rather than inheriting the earlier merge-tree result, preserve Oliver Hees' source authorship/co-authorship, and obtain executable CI/Docker/Nix for every commit in the final submitted object. Once that exact object is clean, this can return to review. No parallel German-locale carrier is needed.

@oliverhees

Copy link
Copy Markdown

Fresh catch-up, and then one question I would rather ask plainly than keep guessing at.

I brought the German locale current against today's main (18a76be) and pushed it as oliverhees:de-catchup-0901. That branch merges into main with no conflicts right now. This PR still collides in types.ts, because main added an import directly above the Locale union.

The numbers, measured by comparing values against en.ts rather than key presence. defineLocale() merges every locale onto English, so key coverage always reads 100% and tells you nothing:

  • de sits at 95.6% of 3,242 leaves
  • the 143 that remain are words spelled the same in both languages, Hermes, Slack, MCP, Terminal, Port
  • tsc --noEmit clean, vitest src/i18n 28 of 28

Now the question. CONTRIBUTING.md says nothing about locales, and looking at what actually merges here, I think I understand why the normal path does not fit. Of the last 100 merged PRs, two came from outside the team. Both landed today, both were fix(...), four files, a couple of hundred lines, merged within hours of opening. A locale is one 3,947 line file. It cannot be small, and it cannot be split into something small.

@teknium1 is there a route for that class of contribution, or is a community locale simply not something you want in the tree? Either answer is fine and I would genuinely rather know than keep rebasing.

If you do want it, I will own de.ts. I run Hermes every day with a German-speaking community, so the drift lands on my screen before it lands anywhere else, and I have been catching it up since June. Tell me the shape you want it in and I will ship exactly that.

@oliverhees

oliverhees commented Sep 3, 2026

Copy link
Copy Markdown

@teknium1 @andrexibiza Russian landed yesterday via #100429, and since it touched the same four wiring files this locale uses, the German branch picked up conflicts. Rebuilt it today.

Branch: oliverhees:de-catchup-0903 (cd14db0). Merges into current main (561b053) with no conflicts. One commit, six files, +4158/-6. Same seam as the Russian salvage: types.ts, catalog.ts, languages.ts, languages.test.ts, plus de.ts and the context test. defineLocale() with defineFieldCopy(), so anything added to en.ts later falls back to English.

Also translated everything that landed in en.ts since Sep 1: the Local Models settings page, the status bar resource meters, the restart-to-finish-update banner, and the new local-setup tip.

Checks:

The German cluster is the same shape as the Russian one, just bigger: #51762 (Jun 24), #62592, #67077, #71161, #81775, #82411, #85236, #95862, this PR, and issue #51217. Yesterday @Euterer posted an independent verification on #51762 instead of opening another one. If you want to salvage German the way you did Russian, this branch is meant to be the base. The Kanban plugin bundle in #81775 is separate work and should land under @goetterbote2342's name.

I will keep the branch current against main. If a specific SHA helps, say which.

@Euterer

Euterer commented Sep 3, 2026

Copy link
Copy Markdown

Verified oliverhees:de-catchup-0903 @ cd14db0ca1 locally against main @ 561b053: git merge-tree --write-tree returns clean, no conflicts. 6 files, +4158/-6, same seam as the Russian salvage. de.ts uses defineLocale() with defineFieldCopy(), so later en.ts additions fall back to English rather than rendering empty, and context.test.tsx moves its unsupported-language fixture from de to xx so the assertion cannot silently invert when the next locale lands.

The blocker here is procedural, not technical: this is the only conflict-free German tree in the cluster and it has no PR. @oliverhees — please open it as one against main. Maintainers cannot merge a branch name mentioned in a comment thread.

One data point that it is worth building on: I have a German bundle for the Bot Mode plugin catalog that cannot compile without it. BOTS_LOCALES cannot name de until Locale in src/i18n/types.ts includes it, so on bare main it fails with TS2353. Stacked on cd14db0ca1 it is clean — npm run typecheck passes across renderer, electron and e2e, and vitest run src/plugins/hermes-bots/i18n.test.ts src/i18n/ is 42/42. I will open that as a follow-up once this base has a PR to target.

(Minor: the coverage figure differs between the commit message — 95.6% of 3,242 leaves — and the comment above — 95.7% of 3,368. Both fine, worth stating which en.ts SHA each was measured against.)

@Kynto-intel

Copy link
Copy Markdown

The German locale work now has a clean PR: #108324 — feat(desktop): add German (de) desktop locale.

Unlike #51762 and this draft, that branch is conflict-free against current main (git merge-tree = 0 conflicts), tsc = 0 errors in i18n/, and 31/31 i18n tests pass (verified locally).

Requesting CI approval: since the PR comes from an external fork, its checks are waiting on the first-time workflow approval. A single Approve would let everything run green — the content is done, only the workflow gate is missing.

This would finally land the German locale on main and clear the blocker that kept #92909 in draft with merge conflicts.

@alt-glitch alt-glitch removed the sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades label Sep 11, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/i18n Localization, locales, translations comp/desktop Electron desktop app (apps/desktop/*) P3 Low — cosmetic, nice to have type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Add German (de) locale to Hermes Desktop i18n

5 participants