Uh oh!
There was an error while loading. Please reload this page.
docs(cli): document the i18n merge consequence for a corrected source string - #9775
Conversation
… string `os i18n extract` merges against committed bundles by default so a re-run never wipes a hand translation, and `--fill=default` only fills gaps. That means a source label/description that is later corrected does not propagate into a locale that already holds a translation of the old text -- a present-but-stale string is not a gap, so it is left as-is. This is intended behaviour and is unchanged; it was simply undocumented where the next reader looks. Two places now say so: - the merge-options comment in `os i18n extract`'s command implementation (packages/cli/src/commands/i18n/extract.ts) - the header comment written into every generated bundle (packages/cli/src/utils/i18n-extract.ts's renderTranslationModule) Committed bundles across the 9 packages that own i18n-extract.config.ts are regenerated (`node scripts/check-i18n-bundles.mjs --write`) so their header matches the new template -- translated content is byte-identical apart from the added header lines. Fixes#9672 Co-Authored-By: Claude <noreply@anthropic.com>
…n-merge-consequence-documented
📓 Docs Drift CheckThis PR changes 10 package(s): 2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 3 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 42 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 6d397439f7e6cf1a868d1282e65c7aac0b1d0e40 && git checkout 6d397439f7e6cf1a868d1282e65c7aac0b1d0e40
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin e9534a4acab63aa91a67dfb76110b6695be68df0 cff1dd33cefd873247deb26e8693463ad87d06ed && git checkout -B drift-repro e9534a4acab63aa91a67dfb76110b6695be68df0 && git merge --no-ff cff1dd33cefd873247deb26e8693463ad87d06ed
node scripts/docs-audit/affected-docs.mjs --json e9534a4acab63aa91a67dfb76110b6695be68df0
|
os-zhuang
commented
Aug 18, 2026
PM review — The one thing worth verifying independentlyA PR that regenerates 42 committed i18n bundles to change a header comment has exactly one way to The rest
Generated by Claude Code |
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 32196793380 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 失败的 job(日志抽取,best effort):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
Uh oh!
There was an error while loading. Please reload this page.
os-zhuang
commented
Aug 18, 2026
Dequeued by a known flake — diagnosed, and deliberately not re-queuedQueue build 32196793380 It is not a regression in this PR — measured, not assertedThe failing turbo task is Zero tests failed. The non-zero exit comes from three unhandled rejections, all identical: That is a vitest worker-teardown race — the RPC channel closes while a console-log delivery is still The control, on the identical diff
Identical verdict; the queue runner was ~3× slower on transform and import. The race tracks load, not Known signature — second sighting#9371 ( Why I am not re-queuingThe triage bot's own guidance: 「修好前重排只会再烧一轮全队列」. With the flake confirmed rather than State: open, non-draft, Generated by Claude Code |
os-zhuang
commented
Aug 18, 2026
Disposition reversed — re-queued at 23:42Z. Correcting my previous comment, which said this seat The maintainer's instruction (verbatim): 「你收工了也要跟踪合并」 — going off shift does not release The reason I gave for holding no longer applies either. I wrote that a re-queue would "spend a ⛔ Unchanged: nothing weakened, skipped, quarantined or retry-wrapped; all six required checks still Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#9672
What this documents
os i18n extract's merge behaviour was already correct and already intentional: itnever overwrites an existing non-default-locale translation, so
--fill=defaultonlyfills gaps. The consequence — that a source label/description correction does not
propagate into a locale that already holds a translation of the old text — was true and
unchanged, but stated nowhere the next reader (a translator, or the next author touching
this tool) would look. This PR closes that gap with two paragraphs, per the triage
ruling on #9672 — no behaviour change.
1.
packages/cli/src/commands/i18n/extract.ts— a comment above themergeExistingoption passed toextractTranslations, explaining why merge is thedefault and what it means for a corrected source string.
2.
packages/cli/src/utils/i18n-extract.ts— six new lines inrenderTranslationModule's defaultheader, the single template every generated<locale>.objects.generated.ts/<locale>.metadata-forms.generated.tsbundle isbuilt from (confirmed: not duplicated anywhere else in the repo — 41 generated files,
one call site, no override).
Because the header is baked into every generated bundle, all 9 packages that own an
i18n-extract.config.tswere regenerated (node scripts/check-i18n-bundles.mjs --write) so the committed output matches the new template. Translated content isbyte-identical; only the header comment gained 6 lines per file (42 files touched, 256
insertions, 0 deletions — confirmed via diff).
What this deliberately does NOT do
Per the triage ruling, the source-hash staleness detector sketched in #9672's body is
explicitly out of scope — it's a real design, but new gate surface behind one measured
incident (#9046), an appetite question rather than a queue item. No staleness marker,
no
check:i18nvalue comparison, no threshold change.Mechanism re-verified on current
mainThe issue measured
origin/mainatd693ba158. Re-checked before writing:packages/cli/src/commands/i18n/extract.ts,packages/cli/src/utils/i18n-extract.ts,packages/cli/src/commands/i18n/check.ts,packages/cli/src/utils/i18n-coverage.ts,and
scripts/check-i18n-bundles.mjsall show zero commits betweend693ba158andthis branch's merge base — the mechanism the card describes is unchanged. Both halves
hold:
mergeExisting(merge, the default) only skips a gap; a present translated valuefor a non-default locale is preserved verbatim, corrected source or not.
check:i18n's bundle-drift verdict compares a fresh extract's content against thecommitted file — and a fresh extract for a non-default locale reuses the same stale
merged value, so drift never fires on staleness.
computeI18nCoverage(which backsos i18n check) only asks whether a key's value isundefined, never whether itmatches its source's meaning.
Verification (commit
cff1dd33c, after mergingmain)pnpm check:i18n— 9/9 packages in sync, no undeclared authoring keys, self-testgreen.
pnpm --filter @objectstack/cli typecheck— clean.pnpm --filter @objectstack/cli test— 134 test files / 1458 tests passed (ran widerthan intended — the
--file-path filters were absorbed as vitest positional argsthat matched everything rather than narrowing the run; all green regardless).
eslint --no-inline-configon both edited source files — 0 findings.check:cross-package-test-inputs,check:test-source-alias,check:type-source-resolution,check:changeset-gate-self-tests(empty-changeset / adr-0087 / no-major),
check:objectui-changeset,check-affected-docs.mjsself-test — all green.check:nul-bytes— clean (self-test + real scan).check:i18n-coverage— not measured: this fresh worktree has no build output for@objectstack/connector-mcp, whichexamples/app-showcase's config needs to lint,and that gate is not implicated by this diff's own path derivation
(
node scripts/pm/dispatch-gates.mjs). Declaring not-measured rather than guessing.Changeset
@objectstack/cliis publishable and this diff touches itssrc/**with a real,if purely textual, change to what the CLI writes to disk — not a docs/tests-only PR, so
skip-changesetdoes not apply. Added.changeset/i18n-merge-consequence-documented.md(patch).
Generated by Claude Code