Skip to content

Content Translation: support list item, verse, preformatted, and pullquote blocks - #1009

Open
dugyen wants to merge 8 commits into
WordPress:developfrom
dugyen:content-translation-more-block-types
Open

dugyen wants to merge 8 commits into
WordPress:developfrom
dugyen:content-translation-more-block-types

Conversation

@dugyen

@dugyen dugyen commented Sep 7, 2026 •

Copy link
Copy Markdown

What?

Closes #979

Expands the Content Translation experiment's editor UI to support translating core/list-item, core/verse, core/preformatted, and core/pullquote blocks, in addition to the existing core/paragraph and core/heading.

Why?

The Content Translation experiment (#747) only translates paragraph and heading blocks, even though the plugin already ships shared infrastructure for reading and writing a block's primary text attribute regardless of block type — the Editorial Updates experiment already uses it for a wider set of blocks (getEditableTextAttribute() in src/utils/blocks.ts). This PR takes advantage of that existing infrastructure for Content Translation, as suggested in #979.

How?

  • src/experiments/content-translation/constants.ts: added core/list-item, core/verse, core/preformatted, and core/pullquote to TRANSLATION_SUPPORTED_BLOCK_TYPES.
  • src/experiments/content-translation/utils/index.ts: getTranslatableBlock() now also resolves and returns the block's editable attribute via the shared getEditableTextAttribute() helper.
  • src/experiments/content-translation/hooks/useContentTranslation.ts: the write-back path previously hard-coded the content attribute when applying a translation. That's a latent bug for any block whose text lives elsewhere — core/pullquote uses value — so adding it to the supported list without this fix would have translated the block but silently failed to apply the result. The write-back now uses the attribute resolved above.
  • Updated docs/experiments/content-translation.md to reflect the new supported blocks and explain the RichText-attribute constraint.
  • Added an e2e test (tests/e2e/specs/experiments/content-translation.spec.js) covering all four new block types, including that the pullquote's value attribute (not content) gets updated.

Deliberately out of scope, with reasoning left as a code comment in constants.ts:

  • core/table and core/image: their text isn't a single RichText attribute, so they need dedicated read/write handling this experiment doesn't have.
  • core/quote's and core/pullquote's citation field, and core/details's summary: secondary text the current one-attribute-per-block write-back path doesn't target.
  • core/code: source code, not prose to translate.
  • Container blocks (core/quote, core/list) need no direct entry: their own text lives in child blocks (core/paragraph, core/list-item), which are already reached because the block tree is flattened before filtering.

Use of AI Tools

AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Sonnet 5
Used for: Surveying open issues to find an unclaimed, well-scoped one; tracing the existing block-support and write-back code across the Content Translation and Editorial Updates experiments to find the shared getEditableTextAttribute() infrastructure; identifying the value-attribute write-back bug; implementing the fix, docs, and e2e test. I reviewed the diff, ran npm run typecheck and wp-scripts lint-js locally (both clean), and take responsibility for the change.

Testing Instructions

  1. Enable Experiments globally, then enable the Content Translation experiment.
  2. Create a post with a core/list-item (inside a core/list), a core/verse, a core/preformatted, and a core/pullquote block, each with enough content to pass the minimum length.
  3. Open the post sidebar, click Generate Translation, pick a target language, and translate.
  4. Verify all four blocks are translated in place — in particular, verify the pullquote's quote text updates (not just paragraph/heading).
  5. npm run typecheck and npx wp-scripts lint-js on the changed files.

Changelog Entry

Changed - Content Translation now also translates list item, verse, preformatted, and pullquote blocks, not just paragraphs and headings.

Open WordPress Playground Preview

…quote blocks

The editor UI only translated core/paragraph and core/heading, even
though the plugin already has shared infrastructure (used by the
Editorial Updates experiment) for reading and writing a block's
primary RichText attribute regardless of block type.

- Add core/list-item, core/verse, core/preformatted, and
  core/pullquote to TRANSLATION_SUPPORTED_BLOCK_TYPES.
- Fix the write-back path, which hard-coded the `content` attribute
  and would have silently failed to apply translations to
  core/pullquote (whose text lives in `value`). It now resolves the
  correct attribute per block via the shared getEditableTextAttribute()
  helper, the same one Editorial Updates already uses.
- Update the experiment docs and add an e2e test covering the new
  block types and the value-attribute write-back.

core/table, core/image, and secondary fields like a pullquote's or
quote's citation are left out: their text isn't a single RichText
attribute this experiment's write-back path can target.

Closes WordPress#979

AI assistance: Yes. Tool(s): Claude Code (Sonnet 5). Used for: tracing
the existing block-support and write-back code, identifying the
value-attribute bug, implementing the fix, and updating docs/tests.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@dugyen
dugyen requested a review from a team September 7, 2026 06:44
@dugyen
dugyen requested a review from jeffpaul as a code owner September 7, 2026 06:44
@github-actions

github-actions Bot commented Sep 7, 2026 •

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Unlinked Accounts

The following contributors have not linked their GitHub and WordPress.org accounts: @ugyendorji@Ugyens-MacBook-Air.local.

Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Unlinked contributors: ugyendorji@Ugyens-MacBook-Air.local.

Co-authored-by: dugyen <ugyensupport@git.wordpress.org>
Co-authored-by: dkotter <dkotter@git.wordpress.org>
Co-authored-by: yogeshbhutkar <yogeshbhutkar@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@codecov

codecov Bot commented Sep 7, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 81.50%. Comparing base (13b91a9) to head (a1293b0).

Additional details and impacted files
@@            Coverage Diff             @@
##             develop    #1009   +/-   ##
==========================================
  Coverage      81.50%   81.50%           
  Complexity      2923     2923           
==========================================
  Files            122      122           
  Lines          11653    11653           
==========================================
  Hits            9498     9498           
  Misses          2155     2155           
Flag Coverage Δ
unit 81.50% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@dugyen

dugyen commented Sep 7, 2026

Copy link
Copy Markdown
Author

Test report

Tested commit 47979acc0882738d3cf238dd56c3c8a5edc4154b against develop.

Verdict: looks correct, recommend approve. Static checks are clean and the code change matches the stated fix. Live, end-to-end translation in a running editor could not be exercised in this environment (details below).

Verified

  • ✅ Plugin builds and installs cleanly — installed the PR's CI-artifact zip into a fresh WordPress Playground instance; Plugins page shows AI 1.3.0 active, no activation errors.
  • ✅ Content Translation experiment surfaces and toggles on under Settings → AI → Editor Experiments.
  • ✅ npm run typecheck — 0 errors.
  • ✅ npx wp-scripts lint-js on the four changed files — 0 errors, 0 warnings.
  • ✅ Write-back logic reviewed against the claimed fix: getEditableTextAttribute() resolves content for list item/verse/preformatted and value for pullquote; useContentTranslation.ts now writes to that resolved key instead of a hard-coded content. Matches the PR description.
  • ✅ New e2e spec (tests/e2e/specs/experiments/content-translation.spec.js) reviewed — inserts all four new block types, translates via a mocked response, and asserts each block's own attribute updates, including that pullquote checks value not content. Well-targeted.

Not run

  • The included Playwright e2e suite (needs wp-env/Docker — unavailable in this environment).
  • Live "Generate Translation" click-through against a real AI connector — no API key was entered (out of scope for this session), and separately, the Playground editor's iframe stopped accepting synthetic input partway through testing (reproduced across two fresh Playground sessions; unrelated to this PR's code — ordinary wp-admin settings pages responded fine).

Minor finding (non-blocking)

includes/Experiments/Content_Translation/Content_Translation.php:47 — the experiment's admin-facing description (Settings → AI → Editor Experiments card) still reads "Translate paragraph and heading blocks into a different language." docs/experiments/content-translation.md was updated to list all six supported blocks in this PR, but this PHP string wasn't. Cosmetic, but now inconsistent with the docs this same PR updated.


🤖 Tested and reported by Claude Code

The Settings > AI > Editor Experiments card description still said
"paragraph and heading blocks" after this PR added list item, verse,
preformatted, and pullquote support. docs/experiments/content-translation.md
was updated to reflect the new block types; this admin-facing string
was missed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@dugyen

dugyen commented Sep 7, 2026

Copy link
Copy Markdown
Author

Fixed the stale description string flagged above in eac36cc — the Settings → AI card now lists all six supported block types.

Gutenberg renamed the core/verse block's display title to "Poetry"
while keeping the block name core/verse for backward compatibility,
so the block wrapper's accessible name is "Block: Poetry", not
"Block: Verse". Update the e2e assertion to match, fixing a false
failure in the new list item/verse/preformatted/pullquote coverage.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@dugyen

dugyen commented Sep 7, 2026

Copy link
Copy Markdown
Author

Ran the new e2e coverage (tests/e2e/specs/experiments/content-translation.spec.js) for real against a live WordPress site (bypassing the Docker/wp-env requirement noted in my earlier comment, by pointing Playwright at an already-running instance with the ai-provider-for-openai connector + the tests/e2e-testing mock plugin active, matching the project's own .wp-env.json test setup).

Found one real bug, isolated to the test itself (not the PR's translation code): the "Translates list item, verse, preformatted, and pullquote blocks" test asserted on getByRole('document', { name: 'Block: Verse' }), but current Gutenberg has renamed core/verse's display title to "Poetry" (wp.blocks.getBlockType('core/verse').title === 'Poetry') while keeping the block name core/verse for backward compatibility. So the block's accessible name is actually "Block: Poetry", and the locator never matched — even though the translation itself was applied correctly (verified manually via the block editor's data store). Since .wp-env.json tracks WP "latest stable," this would very likely have failed the same way in CI.

Fixed in eac36cc..8b9d22a by updating the locator to Block: Poetry.

Result: 10/10 e2e tests passing on the current branch tip (8b9d22a8), including full coverage of all four new block types (list item, verse, preformatted, pullquote) and the retry-on-failure flow.

Verdict: looks correct, recommend approve.

dugyen and others added 4 commits September 7, 2026 20:21
The PHP 7.4/8.0 test jobs failed on a transient Debian security
mirror 404 during Docker image build (apt-get install sudo /
dpkg-dev), unrelated to this PR's diff -- the same jobs passed
cleanly on develop 3 days ago. Empty commit to retrigger CI.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
PHP 8.0 (WP 7.0/latest/trunk) still 404ing on deb.debian.org/debian-security
fetching sudo_1.9.5p2-3+deb11u4_amd64.deb during Docker image build --
a live Debian mirror/CDN inconsistency outside this repo (the Dockerfile
is generated by @wordpress/env, not checked in here; confirmed the
latest published @wordpress/env 11.14.0 doesn't yet rewrite
bullseye-security to archive.debian.org the way it already does for
buster/stretch). Retrying once more since it's a live network condition
that can clear between runs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Test PHP 8.0 (and intermittently 7.4) jobs have been failing across
every open PR, not just this one: the wp-env Docker build 404s
fetching packages (sudo, libc-dev-bin, ...) from
deb.debian.org/debian-security's bullseye-security suite, which is
serving a stale/inconsistent index on some mirror nodes even though
bullseye isn't officially archived yet.

@wordpress/env's generated Dockerfile already routes stretch and
buster through archive.debian.org for exactly this kind of EOL/mirror
inconsistency, but doesn't yet do the same for bullseye (confirmed
against the latest published 11.14.0, which also lacks it). Patch it
in via patch-package -- applied automatically through postinstall --
until upstream ships a real fix.

Verified: reinstalled a pristine @wordpress/env and confirmed `npm
run postinstall` (what `npm ci` runs) reapplies the patch cleanly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@dkotter

dkotter commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Thanks for the PR @dugyen. Increasing the blocks we support is great but wondering if there's a better way to ensure we support all Core blocks, rather than only supporting a subset? I understand not all blocks have text to translate so we may need to maintain some sort of list but it would be ideal if any text within any Core block can be translated.

I just fear we'll have to continue to open new PRs to increase our block support here and would be ideal if we can just support everything (more or less). I know our Editorial Notes feature does already support most blocks (including tables) so that's an area I'd suggest looking at for ideas.

@dkotter dkotter added this to the Future Release milestone Sep 11, 2026
@jeffpaul jeffpaul modified the milestones: Future Release, 1.5.0 Sep 21, 2026
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.

Increase the types of blocks Content Translation supports

3 participants