diff --git a/core/lib/Drupal/Core/Entity/ContentEntityInterface.php b/core/lib/Drupal/Core/Entity/ContentEntityInterface.php
index 3053d23694..f43bc3b453 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityInterface.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityInterface.php
@@ -2,8 +2,6 @@
 
 namespace Drupal\Core\Entity;
 
-use Drupal\Core\TypedData\TranslatableInterface;
-
 /**
  * Defines a common interface for all content entity objects.
  *
@@ -20,70 +18,7 @@
  *
  * @ingroup entity_api
  */
-interface ContentEntityInterface extends \Traversable, FieldableEntityInterface, RevisionableInterface, TranslatableInterface {
-
-  /**
-   * Determines if the current translation of the entity has unsaved changes.
-   *
-   * @return bool
-   *   TRUE if the current translation of the entity has changes.
-   */
-  public function hasTranslationChanges();
-
-  /**
-   * Marks the current revision translation as affected.
-   *
-   * Setting the revision translation affected flag through the setter or
-   * through the field directly will always enforce it, which will be used by
-   * the entity storage to determine if the flag should be recomputed or the set
-   * value should be used instead.
-   * @see \Drupal\Core\Entity\ContentEntityStorageBase::populateAffectedRevisionTranslations()
-   *
-   * @param bool|null $affected
-   *   The flag value. A NULL value can be specified to reset the current value
-   *   and make sure a new value will be computed by the system.
-   *
-   * @return $this
-   */
-  public function setRevisionTranslationAffected($affected);
-
-  /**
-   * Checks whether the current translation is affected by the current revision.
-   *
-   * @return bool
-   *   TRUE if the entity object is affected by the current revision, FALSE
-   *   otherwise.
-   */
-  public function isRevisionTranslationAffected();
-
-  /**
-   * Checks if the revision translation affected flag value has been enforced.
-   *
-   * @return bool
-   *   TRUE if revision translation affected flag is enforced, FALSE otherwise.
-   *
-   * @internal
-   */
-  public function isRevisionTranslationAffectedEnforced();
-
-  /**
-   * Enforces the revision translation affected flag value.
-   *
-   * Note that this method call will not have any influence on the storage if
-   * the value of the revision translation affected flag is NULL which is used
-   * as an indication for the storage to recompute the flag.
-   * @see \Drupal\Core\Entity\ContentEntityInterface::setRevisionTranslationAffected()
-   *
-   * @param bool $enforced
-   *   If TRUE, the value of the revision translation affected flag will be
-   *   enforced so that on entity save the entity storage will not recompute it.
-   *   Otherwise the storage will recompute it.
-   *
-   * @return $this
-   *
-   * @internal
-   */
-  public function setRevisionTranslationAffectedEnforced($enforced);
+interface ContentEntityInterface extends \Traversable, FieldableEntityInterface, TranslatableRevisionableInterface {
 
   /**
    * Gets the loaded Revision ID of the entity.
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
index a3bb20508f..285bae2d21 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
@@ -151,7 +151,8 @@ protected function initFieldValues(ContentEntityInterface $entity, array $values
   /**
    * {@inheritdoc}
    */
-  public function createTranslation(ContentEntityInterface $entity, $langcode, array $values = []) {
+  public function createTranslation(TranslatableInterface $entity, $langcode, array $values = []) {
+    /** @var \Drupal\Core\Entity\ContentEntityInterface $translation */
     $translation = $entity->getTranslation($langcode);
     $definitions = array_filter($translation->getFieldDefinitions(), function (FieldDefinitionInterface $definition) {
       return $definition->isTranslatable();
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityStorageInterface.php b/core/lib/Drupal/Core/Entity/ContentEntityStorageInterface.php
index 678937bdc0..9b35a84bef 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityStorageInterface.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageInterface.php
@@ -5,24 +5,7 @@
 /**
  * A storage that supports content entity types.
  */
-interface ContentEntityStorageInterface extends EntityStorageInterface, RevisionableStorageInterface {
-
-  /**
-   * Constructs a new entity translation object, without permanently saving it.
-   *
-   * @param \Drupal\Core\Entity\ContentEntityInterface $entity
-   *   The entity object being translated.
-   * @param string $langcode
-   *   The translation language code.
-   * @param array $values
-   *   (optional) An associative array of initial field values keyed by field
-   *   name. If none is provided default values will be applied.
-   *
-   * @return \Drupal\Core\Entity\ContentEntityInterface
-   *   A new entity translation object.
-   */
-  public function createTranslation(ContentEntityInterface $entity, $langcode, array $values = []);
-
+interface ContentEntityStorageInterface extends EntityStorageInterface, TranslatableRevisionableStorageInterface {
 
   /**
    * Creates an entity with sample field values.
diff --git a/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php b/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php
index 1604e31113..7c7d62e04d 100644
--- a/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php
+++ b/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php
@@ -8,7 +8,7 @@
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\FieldableEntityInterface;
 use Drupal\Core\Entity\EntityDisplayBase;
-use Drupal\Core\TypedData\TranslatableInterface;
+use Drupal\Core\TypedData\TranslatableInterface as TranslatableDataInterface;
 
 /**
  * Configuration entity that contains display options for all components of a
@@ -253,7 +253,7 @@ public function buildMultiple(array $entities) {
           // those values using:
           // - the entity language if the entity is translatable,
           // - the current "content language" otherwise.
-          if ($entity instanceof TranslatableInterface && $entity->isTranslatable()) {
+          if ($entity instanceof TranslatableDataInterface && $entity->isTranslatable()) {
             $view_langcode = $entity->language()->getId();
           }
           else {
diff --git a/core/lib/Drupal/Core/Entity/EntityRepository.php b/core/lib/Drupal/Core/Entity/EntityRepository.php
index 986cc50b54..37a89d793e 100644
--- a/core/lib/Drupal/Core/Entity/EntityRepository.php
+++ b/core/lib/Drupal/Core/Entity/EntityRepository.php
@@ -5,7 +5,7 @@
 use Drupal\Core\Config\Entity\ConfigEntityTypeInterface;
 use Drupal\Core\Language\LanguageInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
-use Drupal\Core\TypedData\TranslatableInterface;
+use Drupal\Core\TypedData\TranslatableInterface as TranslatableDataInterface;
 
 /**
  * Provides several mechanisms for retrieving entities.
@@ -82,7 +82,7 @@ public function loadEntityByConfigTarget($entity_type_id, $target) {
   public function getTranslationFromContext(EntityInterface $entity, $langcode = NULL, $context = []) {
     $translation = $entity;
 
-    if ($entity instanceof TranslatableInterface && count($entity->getTranslationLanguages()) > 1) {
+    if ($entity instanceof TranslatableDataInterface && count($entity->getTranslationLanguages()) > 1) {
       if (empty($langcode)) {
         $langcode = $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId();
         $entity->addCacheContexts(['languages:' . LanguageInterface::TYPE_CONTENT]);
diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
index e95c01b2c8..faeb1473df 100644
--- a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
+++ b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
@@ -11,7 +11,7 @@
 use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Theme\Registry;
-use Drupal\Core\TypedData\TranslatableInterface;
+use Drupal\Core\TypedData\TranslatableInterface as TranslatableDataInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -189,7 +189,7 @@ protected function getBuildDefaults(EntityInterface $entity, $view_mode) {
         'bin' => $this->cacheBin,
       ];
 
-      if ($entity instanceof TranslatableInterface && count($entity->getTranslationLanguages()) > 1) {
+      if ($entity instanceof TranslatableDataInterface && count($entity->getTranslationLanguages()) > 1) {
         $build['#cache']['keys'][] = $entity->language()->getId();
       }
     }
diff --git a/core/lib/Drupal/Core/Entity/KeyValueStore/KeyValueContentEntityStorage.php b/core/lib/Drupal/Core/Entity/KeyValueStore/KeyValueContentEntityStorage.php
index 2c57405c19..3724d4329a 100644
--- a/core/lib/Drupal/Core/Entity/KeyValueStore/KeyValueContentEntityStorage.php
+++ b/core/lib/Drupal/Core/Entity/KeyValueStore/KeyValueContentEntityStorage.php
@@ -2,8 +2,8 @@
 
 namespace Drupal\Core\Entity\KeyValueStore;
 
-use Drupal\Core\Entity\ContentEntityInterface;
 use Drupal\Core\Entity\ContentEntityStorageInterface;
+use Drupal\Core\Entity\TranslatableInterface;
 
 /**
  * Provides a key value backend for content entities.
@@ -13,7 +13,7 @@ class KeyValueContentEntityStorage extends KeyValueEntityStorage implements Cont
   /**
    * {@inheritdoc}
    */
-  public function createTranslation(ContentEntityInterface $entity, $langcode, array $values = []) {
+  public function createTranslation(TranslatableInterface $entity, $langcode, array $values = []) {
     // @todo Complete the content entity storage implementation in
     //   https://www.drupal.org/node/2618436.
   }
diff --git a/core/lib/Drupal/Core/Entity/TranslatableInterface.php b/core/lib/Drupal/Core/Entity/TranslatableInterface.php
new file mode 100644
index 0000000000..573310b6c2
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/TranslatableInterface.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace Drupal\Core\Entity;
+
+use Drupal\Core\TypedData\TranslatableInterface as TranslatableDataInterface;
+
+/**
+ * Provides methods for an entity to support translation.
+ */
+interface TranslatableInterface extends TranslatableDataInterface {
+
+  /**
+   * Determines if the current translation of the entity has unsaved changes.
+   *
+   * @return bool
+   *   TRUE if the current translation of the entity has changes.
+   */
+  public function hasTranslationChanges();
+
+}
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityInterface.php b/core/lib/Drupal/Core/Entity/TranslatableRevisionableInterface.php
similarity index 55%
copy from core/lib/Drupal/Core/Entity/ContentEntityInterface.php
copy to core/lib/Drupal/Core/Entity/TranslatableRevisionableInterface.php
index 3053d23694..ac1f2e9001 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityInterface.php
+++ b/core/lib/Drupal/Core/Entity/TranslatableRevisionableInterface.php
@@ -2,33 +2,10 @@
 
 namespace Drupal\Core\Entity;
 
-use Drupal\Core\TypedData\TranslatableInterface;
-
 /**
- * Defines a common interface for all content entity objects.
- *
- * Content entities use fields for all their entity properties and are
- * translatable and revisionable, while translations and revisions can be
- * enabled per entity type. It's best practice to always implement
- * ContentEntityInterface for content-like entities that should be stored in
- * some database, and enable/disable revisions and translations as desired.
- *
- * When implementing this interface which extends Traversable, make sure to list
- * IteratorAggregate or Iterator before this interface in the implements clause.
- *
- * @see \Drupal\Core\Entity\ContentEntityBase
- *
- * @ingroup entity_api
+ * Provides methods for an entity to support revision translation.
  */
-interface ContentEntityInterface extends \Traversable, FieldableEntityInterface, RevisionableInterface, TranslatableInterface {
-
-  /**
-   * Determines if the current translation of the entity has unsaved changes.
-   *
-   * @return bool
-   *   TRUE if the current translation of the entity has changes.
-   */
-  public function hasTranslationChanges();
+interface TranslatableRevisionableInterface extends TranslatableInterface, RevisionableInterface {
 
   /**
    * Marks the current revision translation as affected.
@@ -85,25 +62,4 @@ public function isRevisionTranslationAffectedEnforced();
    */
   public function setRevisionTranslationAffectedEnforced($enforced);
 
-  /**
-   * Gets the loaded Revision ID of the entity.
-   *
-   * @return int
-   *   The loaded Revision identifier of the entity, or NULL if the entity
-   *   does not have a revision identifier.
-   */
-  public function getLoadedRevisionId();
-
-  /**
-   * Updates the loaded Revision ID with the revision ID.
-   *
-   * This method should not be used, it could unintentionally cause the original
-   * revision ID property value to be lost.
-   *
-   * @internal
-   *
-   * @return $this
-   */
-  public function updateLoadedRevisionId();
-
 }
diff --git a/core/lib/Drupal/Core/Entity/TranslatableRevisionableStorageInterface.php b/core/lib/Drupal/Core/Entity/TranslatableRevisionableStorageInterface.php
new file mode 100644
index 0000000000..eded8ed37c
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/TranslatableRevisionableStorageInterface.php
@@ -0,0 +1,9 @@
+<?php
+
+namespace Drupal\Core\Entity;
+
+/**
+ * A storage that supports translatable and revisionable entity types.
+ */
+interface TranslatableRevisionableStorageInterface extends TranslatableStorageInterface, RevisionableStorageInterface {
+}
diff --git a/core/lib/Drupal/Core/Entity/TranslatableStorageInterface.php b/core/lib/Drupal/Core/Entity/TranslatableStorageInterface.php
new file mode 100644
index 0000000000..56c25f6408
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/TranslatableStorageInterface.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace Drupal\Core\Entity;
+
+/**
+ * A storage that supports translatable entity types.
+ */
+interface TranslatableStorageInterface {
+
+  /**
+   * Constructs a new entity translation object, without permanently saving it.
+   *
+   * @param \Drupal\Core\Entity\TranslatableInterface $entity
+   *   The entity object being translated.
+   * @param string $langcode
+   *   The translation language code.
+   * @param array $values
+   *   (optional) An associative array of initial field values keyed by field
+   *   name. If none is provided default values will be applied.
+   *
+   * @return \Drupal\Core\Entity\TranslatableInterface
+   *   Another instance of the specified entity object class with the specified
+   *   active language and initial values.
+   */
+  public function createTranslation(TranslatableInterface $entity, $langcode, array $values = []);
+
+}
diff --git a/core/lib/Drupal/Core/Entity/entity.api.php b/core/lib/Drupal/Core/Entity/entity.api.php
index bf4abec5c8..0af0b53232 100644
--- a/core/lib/Drupal/Core/Entity/entity.api.php
+++ b/core/lib/Drupal/Core/Entity/entity.api.php
@@ -68,6 +68,57 @@
  * - During many operations, static methods are called on the entity class,
  *   which implements \Drupal\Entity\EntityInterface.
  *
+ * @section entities_revisions_translations Entities, revisions and translations
+ * A content entity can have multiple stored variants: based on its definition,
+ * it can be revisionable, translatable, or both.
+ *
+ * A revisionable entity can keep track of the changes that affect its data. In
+ * fact all previous revisions of the entity can be stored and made available as
+ * "historical" information. The "default" revision is the canonical variant of
+ * the entity, the one that is loaded when no specific revision is requested.
+ * Only changes to the default revision may be performed without triggering the
+ * creation of a new revision, in any other case revision data is not supposed
+ * to change. Aside from historical revisions, there can be "pending" revisions,
+ * that contain changes that did not make their way into the default revision.
+ * Typically these revisions contain data that is waiting for some form of
+ * approval, before being accepted as canonical.
+ * @see \Drupal\Core\Entity\RevisionableInterface
+ * @see \Drupal\Core\Entity\RevisionableStorageInterface
+ *
+ * A translatable entity can contain multiple translations of the same content.
+ * Content entity data is stored via fields, and each field can have one version
+ * for each enabled language. Some fields may be defined as untranslatable,
+ * which means that their values are shared among all translations. The
+ * "default" translation is the canonical variant of the entity, the one whose
+ * content will be accessible in the entity field data. Other translations
+ * can be instantiated from the default one. Every translation has an "active
+ * language" that is used to determine which field translation values should be
+ * handled. Typically the default translation's active language is the language
+ * of the content that was originally entered and served as source for the other
+ * translations.
+ * @see \Drupal\Core\Entity\TranslatableInterface
+ * @see \Drupal\Core\Entity\TranslatableStorageInterface
+ *
+ * An entity that is both revisionable and translatable has all the features
+ * described above: every revision can contain one or more translations. The
+ * canonical variant of the entity is the default translation of the default
+ * revision. Any revision will be initially loaded as the default translation,
+ * the other revision translations can be instantiated from this one. If a
+ * translation has changes in a certain revision, the translation is considered
+ * "affected" by that revision, and will be flagged as such via the
+ * "revision_translation_affected" field. With the built-in UI, every time a new
+ * revision is saved, the changes for the edited translations will be stored,
+ * while all field values for the other translations will be copied as-is.
+ * However, if multiple translations of the default revision are being
+ * subsequently modified without creating a new revision when saving, they will
+ * all be affected by the default revision. Additionally, all revision
+ * translations will be affected when saving a revision containing changes for
+ * untranslatable fields. On the other hand, pending revisions are not supposed
+ * to contain multiple affected translations, even when they are being
+ * manipulated via the API.
+ * @see \Drupal\Core\Entity\TranslatableRevisionableInterface
+ * @see \Drupal\Core\Entity\TranslatableRevisionableStorageInterface
+ *
  * @section create Create operations
  * To create an entity:
  * @code
@@ -84,6 +135,10 @@
  * Hooks invoked during the create operation:
  * - hook_ENTITY_TYPE_create()
  * - hook_entity_create()
+ * - When handling content entities, if a new translation is added to the entity
+ *   object:
+ *   - hook_ENTITY_TYPE_translation_create()
+ *   - hook_entity_translation_create()
  *
  * See @ref save below for the save portion of the operation.
  *
@@ -113,17 +168,6 @@
  * @endcode
  * This involves the same hooks and operations as regular entity loading.
  *
- * @section entities_revisions_translations Entities, revisions and translations
- *
- * A translation is not a revision and a revision is not necessarily a
- * translation. Revisions and translations are the two axes on the "spreadsheet"
- * of an entity. If you use the built-in UI and have revisions enabled, then a
- * new translation change would create a new revision (with a copy of all data
- * for other languages in that revision). If an entity does not use revisions or
- * the entity is being modified via the API, then multiple translations can be
- * modified in a single revision. Conceptually, the revisions are columns on the
- * spreadsheet and translations are rows.
- *
  * @section save Save operations
  * To update an existing entity, you will need to load it, change properties,
  * and then save; as described above, when creating a new entity, you will also
