Skip to content

Patch wuchale so clean-translations keeps markup inside JSX expressions - #1897

Closed
Polleps wants to merge 1 commit into
developfrom
claude/nice-rubin-82ayvd-clean-translations
Closed

Polleps wants to merge 1 commit into
developfrom
claude/nice-rubin-82ayvd-clean-translations

Conversation

@Polleps

@Polleps Polleps commented Sep 29, 2026

Copy link
Copy Markdown
Collaborator

Related Issues

No issue. Several agents found that pnpm clean-translations looked unusable on a clean develop checkout. It logged "Cleaned 836 items" and showed about 10k removed .po lines, including strings that are still in use, such as "AI chat". PRs #1887, #1889, #1890, #1891, #1892 and #1895 added .po entries by hand to work around it.

What was actually going on

The CLI and the vite plugin were not out of sync. Both use the same config, the same file set (1126 files) and the same Hub/MixedVisitor code. On develop, --clean removed only the 836 obsolete (#~) entries. The dev server runs in dev: 'refs' mode, which marks unused entries obsolete but never deletes them, so they had piled up. No live entry was lost. "AI chat" is still there: with that many deletions, git diff shows it as removed in one place and added in another.

The real bug is in wuchale's extractor, and the CLI and vite share it. MixedVisitor visited JSX that sits inside an expression ({cond && <X/>}, a ternary, .map(...), title={<span>…</span>}) against the enclosing level's pending mod state:

  • A text-less element there, for example {a && <p>{x}</p>}, applied and reset that state early. The enclosing level kept a stale reference, so every later sibling's messages were silently dropped. They rendered untranslated and never reached the catalogs. This is why live strings such as "Fetch workspace", "Search again", "Restore version", "Publication history" and "Preview version" were sitting in the catalogs as #~ obsolete entries, and why hand-added entries are removed again by the next clean. Minimal repro: <Panel>{a && <p>{x}</p>}<p>Look here</p></Panel> extracts nothing.
  • Other trees had their text folded into the enclosing level and were extracted as sentence fragments ("Searching for" / "for", "resources have a" / "property").

The same code shape is still in wuchale 0.26.7, so bumping the version would not fix it.

Fix

  • browser/patches/wuchale@0.25.6.patch: a JSX tree visited as a direct element child keeps the child mod that its parent prepared. Any other tree gets its own mod state, which is applied when the tree ends and then restored. This covers both the CLI and the dev server.
  • Settled catalogs (committed here so that develop is a fixed point again):
    • 836 obsolete entries removed. I checked the ones whose text still appears in source: they are @wc-ignored, module-scope constants, comments, or model-facing strings.
    • 38 more live entries (for example "Fetch workspace" and "Search again").
    • 65 fragments re-keyed into whole sentences.
    • The 6 fr/es translations of re-keyed fragments carried over ("<0/> " + old).
    • Every other existing translation is unchanged.
  • AGENTS.md and browser/patches/README.md explain the patch and how to read a clean diff.

Verification

  • With the patch, pnpm clean-translations on a clean develop keeps all 2265 previously live msgids except the 65 re-keyed fragments. No msgstr changed.
  • Running it again gives no change: byte-identical in all 4 locales.
  • Dev server: after serving the affected modules with the patched plugin, the .po files were unchanged.
  • pnpm lint and pnpm typecheck in data-browser pass. pnpm install --frozen-lockfile accepts the lockfile.

Checklist

  • Add changelog entry linking to issue, describe API changes (not user-facing; none added)
  • Add or update tests if needed (patch to a third-party package; repro is described above and in patches/README.md)
  • Update docs if needed

Upstream: this still needs a wuchale issue or PR against adapter-utils/mixed-visitor.js (expression children should not share the parent's mod).

🤖 Generated with Claude Code

https://claude.ai/code/session_01P648McUYrscNsheXY5Ya81


Generated by Claude Code

@joepio

joepio commented Oct 4, 2026

Copy link
Copy Markdown
Member

@Polleps Trekmeester question, as part of the PR cleanup Joep agreed to: are your drafts #1887, #1892 and #1897 still needed? All three have conflicted with develop since 29 Sep. If yes, I'll update them with develop and batch them once you mark them ready. If no, I'll close them (the branches stay).

Wuchale 0.25 visited JSX that sits inside an expression ({cond && <X/>},
ternaries, .map, attributes) against the enclosing level's pending state.
A text-less element there, e.g. {a && <p>{x}</p>}, applied and reset that
state early, so every later sibling's messages were silently dropped: they
rendered untranslated and never reached the catalogs ("Fetch workspace",
"Restore version", ...). Other trees were folded into the enclosing level
and extracted as sentence fragments ("Searching for", "for").

The CLI and the vite plugin share this code, so they agreed with each
other; the large clean-translations diff was 836 obsolete (#~) entries the
dev server never removes, plus git diff showing kept entries such as
"AI chat" as moved. No live entry was lost by --clean itself.

- patches/wuchale@0.25.6.patch: give each non-child JSX tree its own mod
  state and apply it when the tree ends.
- Settle the catalogs: drop 836 obsolete entries, add 38 newly extracted
  live strings, re-key 65 fragments into whole sentences, and carry the 6
  fr/es translations of re-keyed fragments over ("<0/> " + old).
- Document the patch and how to read a clean-translations diff.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P648McUYrscNsheXY5Ya81
@joepio
joepio force-pushed the claude/nice-rubin-82ayvd-clean-translations branch from e167ef4 to bee1333 Compare October 5, 2026 09:33
joepio added a commit that referenced this pull request Oct 6, 2026
One tested commit for the open PRs of 5 October: link device to account (#1948), one profile form (#1953), faster branch runs (#1956), end-to-end encrypted conversations (#1971), passkey ceremony header (#1972), Flutter account package (#2035), cheap canvas undo (#2053), atomic.place tab title (#2056), table resource cell (#2057), small table fixes (#2058), storage map (#2061), Sync page cloud ladder (#2072), several served-domain suffixes (#2073), one sign-in (#2074), smaller chat messages (#2077), bring-along e2e fix (#2078), inline row data loss (#2079), Retry-After (#2080), #2070, #2071, #2066, #2067, #2068, #1969, #1887, #1897 and #1920.

Compressed stored values from #2077 stay readable, but a downgrade cannot read compressed rows.

Co-authored-by: Joep Meindertsma <joep@ontola.io>
@joepio joepio closed this Oct 6, 2026

joepio commented Oct 6, 2026

Copy link
Copy Markdown
Member

Merged into develop through #2085 (batch of 5 October), so this PR is closed. Your commits are in the squashed history.


Generated by Claude Code

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants