Skip to content

Translation & Revisions

When to Use

When working with revisionable entities (nodes, media, custom entities) that support both translation and revision tracking.

Decision: Revision Handling Approaches

If you need... Use... Why
Independent revisions per translation Default behavior Each translation has own revision history
Draft translations with published source Moderation states per translation Spanish draft while English is published
Synchronized revision creation Custom entity hooks All translations get new revision simultaneously
Latest revision per language latestRevision() entity query Query most recent revision for specific language

Pattern: Translation Revision Behavior

Default behavior: - Each translation has independent revision history - Editing Spanish translation creates new revision for Spanish only - English translation revision unchanged

Example: 1. Create English node, revision 1 2. Add Spanish translation, revision 2 (Spanish added, English unchanged) 3. Edit Spanish, revision 3 (Spanish updated, English unchanged) 4. Edit English, revision 4 (English updated, Spanish unchanged)

Entity revision loading:

// Load default (published) revision
$node = \Drupal\node\Entity\Node::load(1);

// Load specific revision
$revision = \Drupal::entityTypeManager()
  ->getStorage('node')
  ->loadRevision(4);

// Load latest revision
$latest = \Drupal::entityTypeManager()
  ->getStorage('node')
  ->loadRevision($node->getLatestRevisionId());

Pattern: Translation with Content Moderation

When using Content Moderation module, each translation can have independent moderation state.

Example states: - English: Published - Spanish: Draft - French: Needs Review

Workflow: 1. Create English content, publish 2. Add Spanish translation, save as Draft 3. Spanish stays Draft, English stays Published 4. Publish Spanish independently

Common issue: Default revision vs latest revision - Default revision — the published revision loaded by Entity::load() - Latest revision — most recent revision regardless of published status

Query for latest translation revision:

$query = \Drupal::entityQuery('node')
  ->condition('nid', 1)
  ->latestRevision()
  ->accessCheck(FALSE);
$result = $query->execute();

Note: latestRevision() is NOT language-aware. Returns latest revision ID across all languages.

Common Mistakes

  • Assuming latestRevision() is language-awarelatestRevision() returns latest revision ID regardless of language. Spanish edit creates revision 5, querying for latest English revision still returns 5
  • Confusing default revision with latest revision → Default revision is published version. Latest revision may be unpublished draft. Loading entity by ID returns default, not latest
  • Not understanding translation independence → Editing one translation doesn't create revision for other translations. Each translation has own revision timeline
  • Moderation state confusion → With Content Moderation, "Published" state for Spanish is independent of English. One can be Draft while other is Published
  • Forgetting to check access on revisions → Viewing draft revisions requires "view any unpublished content" permission. Use accessCheck() in queries

See Also

  • Translating Content Entities — creating translations
  • Language-Aware Queries — querying by language and revision
  • Reference: core/lib/Drupal/Core/Entity/ContentEntityBase.php
  • Reference: https://www.drupal.org/project/drupal/issues/3088341 (latestRevision() language awareness issue)