Problem/Motivation

The draft-translation endpoints added in 2.16.0 (nodes) and 2.17.0 (paragraphs, node image alt with the same file) let a content-tier client draft an unpublished translation without touching the live default revision. Media entities are not covered.

On a site where every visitor-facing image is a media entity referenced from a node or a paragraph (an entity reference to media--image, not a node image field), the reader-facing alt text lives on the media entity's image field, next to its caption and name. None of that can be drafted in another language through the governed path. The only write available is the canonical media PATCH, which changes the live media entity and is exactly what a drafts-only translation policy forbids.

Observed while drafting a Spanish translation batch: 102 nodes and 141 paragraphs were drafted as unpublished translations, and the one image in scope (a media image referenced from a node figure field) had to be left to a human editor. The media read also redacts the image alt, so the current source value cannot be reviewed before translating.

Steps to reproduce

  1. Enable content translation on the image media bundle and on the node bundle that references it.
  2. As a content-tier principal with draft-write access, call the draft-translation endpoint for a media entity (POST /jsonapi/media/image/{uuid}/mcp-draft/translations with the language header).
  3. Result: no such route. The only way to set a translated alt or caption is a canonical PATCH on the live media entity.

Proposed resolution

  • Extend the draft-translation resource to media entities on translatable media bundles: create and continue an unpublished translation carrying name, caption-style text fields, and the image field's alt and title, keyed to the same file entity. Keep the same guarantees as node and paragraph drafts: live default revision, live translation set and file reference unchanged; stale state tokens refused; publication denied to content-tier callers.
  • Expose the image field's alt and title on the governed media read so a translator can see the source.
  • Fail closed when the media bundle or its image field is not translatable, with a clear conflict message, as the paragraph endpoint already does for shared-status bundles.

Remaining tasks

  • Decide whether media translations save on the media entity's current default revision (media is not moderated on most sites) or on a forward revision.
  • Functional tests against real storage: alt and caption drafted in the target language, English media and its file unchanged, anonymous rendering of the host node unchanged.
  • Matching client support in the connector.

User interface changes

None.

API changes

New governed routes for media draft translations; the governed media read gains image alt and title.

Data model changes

None.

Comments

jmcerda created an issue. See original summary.

  • jmcerda committed 7a7f5b42 on feature/3622412-media-draft-translation
    Issue #3622412: Skip the media id key on translation writes.
    

  • jmcerda committed 7a7f5b42 on 1.x
    Issue #3622412: Skip the media id key on translation writes.
    

  • jmcerda committed 52f7848f on 1.x
    Issue #3622412: Governed draft translation for media entities.
    
    Enrol...

  • jmcerda committed 7a7f5b42 on 1.x
    Issue #3622412: Skip the media id key on translation writes.
    

  • jmcerda committed 52f7848f on 1.x
    Issue #3622412: Governed draft translation for media entities.
    
    Enrol...
jmcerda’s picture

Status: Active » Fixed

Released in 2.20.0. The draft-translation surface now serves media bundles: POST /jsonapi/media/{bundle}/{uuid}/mcp-draft/translations, with PATCH/GET .../mcp-draft and GET .../mcp-translations. Translations live on an unpublished forward revision. Translatable name, caption, and image alt/title are copied onto it. The live default revision and its file reference stay unchanged; a different file target is refused; a bundle not enabled for content translation is refused with 400. Functional coverage: create, continue, inventory, draft read, anonymous isolation, file replacement refusal, and untranslatable-bundle refusal. The Drupal 10.6/11.3, PHP 8.3/8.4, functional, and PostgreSQL draft suites passed. The companion connector 2.17.0 accepts entityType: media on create/list translation tools.

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.