Skip to content

fix(translator): pair localized blocks/arrays by id across locales#31

Open
SearheiParkhamchuk wants to merge 1 commit into
mainfrom
fix/translator-reconciler-block-id
Open

fix(translator): pair localized blocks/arrays by id across locales#31
SearheiParkhamchuk wants to merge 1 commit into
mainfrom
fix/translator-reconciler-block-id

Conversation

@SearheiParkhamchuk

Copy link
Copy Markdown
Contributor

Problem

The translation pipeline paired source/target array & block elements by position in two places. When a localized blocks/array field is reordered or independently authored per locale, position no longer identifies the same element:

  • DataReconciler merged each source block with whatever target block sat at the same index → values leaked between unrelated blocks (blockType from one, values from another).
  • FieldChunkCollector fed the wrong element's value into strategy.shouldTranslate → under skip_existing, a block needing translation was silently skipped (a populated, unrelated block sat at its index) and an already-translated one re-sent.

Same root cause as the per-field translate control will hit — there is no cross-locale block identity for localized blocks (independent per-locale partitions).

Fix

Extract one shared helper matchElementById into shared/field-traversal (beside resolveBlockFields), used by both pipeline sites: pair the target element by id (and, for blocks, blockType); no match → fall back to source. The walkFields engine stays data-agnostic — pairing is caller-side.

Field shape Source/target ids Behaviour
Non-localized blocks/array (leaves localized) shared id-match == old positional → unchanged, now order-robust
Localized, reordered, ids preserved correspond correct element paired regardless of order (was the bug)
Localized, independently authored diverge no match → mirror source, never graft another block's values (was silent corruption / wrong skip)
Same id, different blockType not the same block → source

Honest limit: removes the corruption / wrong-skip; it does not make skip_existing preserve per-block target edits for genuinely-independent localized blocks — no identity to preserve them by. See the design doc.

Scope

Document-level pipeline only. The field-level per-field control (separate feature) gets its own guard (refuse with a notice under a localized blocks/array ancestor) — tracked in the design doc, ships with that feature.

Tests / verification

  • matchElementById unit tests (kernel.test.ts)
  • reconciler: reorder / independent-ids / different-type / count-mismatch / array reorder / null-id
  • collector: reorder-under-skip_existing regression (collects the right block, skips the right one)
  • check-types ✓ · ultracite ✓ · vitest 688 passed

Design: packages/payload-plugin-translator/docs/plans/2026-06-12-cross-locale-block-identity.md

🤖 Generated with Claude Code

The translation pipeline paired source and target array/block elements by
position in two places. When a localized blocks/array field is reordered or
independently authored per locale, position no longer identifies the same
element:

- DataReconciler merged each source block with whatever target block sat at
  the same index — values leaked between unrelated blocks.
- FieldChunkCollector fed the wrong element's value into
  strategy.shouldTranslate, so under skip_existing a block needing translation
  was silently skipped (and an already-translated one re-sent).

Extract a shared `matchElementById` helper (shared/field-traversal, beside
resolveBlockFields) used by both sites: pair the target by id (and, for
blocks, blockType); no match falls back to source. The walkFields engine stays
data-agnostic — pairing is caller-side. Non-localized fields share the id
across locales (behaviour unchanged, now order-robust); independent localized
content has diverging ids, so we mirror source instead of grafting mismatched
values.

Adds the "Cross-locale block identity" design doc and tests: matchElementById
unit tests, reconciler reorder/independent/different-type cases, and a
collector reorder-under-skip_existing regression.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
turbo-cms-kit-payload Ready Ready Preview, Comment Jun 12, 2026 8:57pm

Request Review

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.

1 participant