feat: support Strapi Blocks (rich text) fields in content transfer (LOC-4580) - #129
Conversation
Blocks fields are now whitelisted for Content Transfer. Each blocks field travels as a single translatable key: on upload its AST is serialized to one JSON string (per David's "one story = one key"), and on download that string is parsed back and stored verbatim so what is stored is what renders — no source-AST overlay. Nested blocks in components and dynamic zones are covered by walking the schema. Malformed download payloads are skipped rather than written to avoid corrupting the field. Resolves LOC-4580 Co-Authored-By: Claude Opus 4.7 <[email protected]>
Reviewer — round 1 (head
|
Rework Strapi Blocks (rich text) content transfer to meet the original AC: upload now emits one translatable segment per text leaf (formatting carried as AST structure, not literal markup in a string) and download rebuilds the target document by overlaying the translated text onto the source-locale AST. - Replace whole-AST JSON serialization (blocks-field-serialization) with text-leaf projection + source-AST overlay (blocks-to-translatable). - Wire overlay into parsedLocalazyEntryToCreateEntry (covers create + update); no source AST to overlay onto -> skip the field, never write an invalid doc. - Identical translations round-trip byte-identically; marks, links, headings, nested lists, images, quotes and code blocks survive; untranslated leaves fall back to source text; nested-in-component/DZ covered by schema-walking. - Reuse BLOCKS_ATTRIBUTE_TYPE in isBlocks (review suggestion) and add tests for the download wiring (review should-fix). Resolves LOC-4580 Co-Authored-By: Claude Opus 4.7 <[email protected]>
Reviewer — round 2: PASS ✅Reviewed head What I verified (correctness, full round-trip)
Suggestions (non-blocking, optional)
Manual verification (needs a live Strapi/Localazy host — no host in sandbox)
DECISION: PASS |
Summary
Adds type-aware support for Strapi 5 Blocks (rich text) fields to the Content Transfer pipeline, per Fibery task LOC-4580. Blocks fields now appear in Content Transfer Setup and round-trip through Localazy as translatable text segments with formatting carried as structure — not as literal markup inside a string.
How it works
supported-content-type-fields.ts):blocksadded, so blocks fields surface as selectable leaves in Setup.blocks-to-translatable.ts→projectBlocksFields, wired intolocalazy-transfer-upload-service.ts): before flattening, each blocks AST is projected to a text-only skeleton — onlytextleaves are kept, in their original positions; block types, headings, lists, links, marks, images, quotes and code blocks are dropped from the payload.flattenObjectthen emits one translatable segment per text node. Blocks nested inside components / dynamic zones are reached by walking the schema.overlayBlocksTranslation, wired intoparsed-localazy-entry-to-create-entry.ts, which serves both create and update): the translated text skeleton is overlaid onto the source-locale AST — structure and formatting come from the source, only text is swapped. Consequences:model-utils.ts:isBlocksnow reuses the sharedBLOCKS_ATTRIBUTE_TYPEconstant (review suggestion).Test plan
npm run test:server— 234/234 pass (newblocks-to-translatable.test.tscovering projection + overlay incl. byte-identical round-trip, marks/links/lists/images/quotes/code survival, partial fallback, nesting; newparsed-localazy-entry-to-create-entry.blocks.test.tscovering the download wiring — the earlier review's should-fix)tscserver + admin — cleaneslint+prettier --check— cleannpm run build— succeedsblocksfield → appears in Setup; upload sends per-node text segments (no JSON/markup); download renders in the admin editor; round-trip preserves headings, lists (incl. nesting), marks, links, images, quotes, code; empty/partial locale handled.Notes
richtextfields are unchanged (already supported via the plain string path; a markdown string, not a Blocks AST).🤖 Generated with Claude Code