diff --git a/core/core.libraries.yml b/core/core.libraries.yml
index ce12db5..c4ca44c 100644
--- a/core/core.libraries.yml
+++ b/core/core.libraries.yml
@@ -203,6 +203,15 @@ drupal.dropbutton:
     - core/drupalSettings
     - core/jquery.once
 
+drupal.entity-form:
+  version: VERSION
+  js:
+    misc/entity-form.js: {}
+  dependencies:
+    - core/jquery
+    - core/drupal
+    - core/drupal.form
+
 drupal.form:
   version: VERSION
   js:
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityForm.php b/core/lib/Drupal/Core/Entity/ContentEntityForm.php
index 29b0a9b..565da93 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityForm.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityForm.php
@@ -22,6 +22,13 @@ class ContentEntityForm extends EntityForm implements ContentEntityFormInterface
   protected $entityManager;
 
   /**
+   * The entity being used by this form.
+   *
+   * @var \Drupal\Core\Entity\ContentEntityInterface|\Drupal\Core\Entity\RevisionLogInterface
+   */
+  protected $entity;
+
+  /**
    * Constructs a ContentEntityForm object.
    *
    * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
@@ -43,8 +50,45 @@ public static function create(ContainerInterface $container) {
   /**
    * {@inheritdoc}
    */
+  protected function prepareEntity() {
+    parent::prepareEntity();
+
+    // Hide the current revision log message in UI.
+    if ($this->showRevisionUi() && !$this->entity->isNew()) {
+      $this->entity->setRevisionLogMessage(NULL);
+    }
+  }
+
+  /**
+   * Returns the bundle entity of the entity, or NULL if there is none.
+   *
+   * @return \Drupal\Core\Entity\EntityInterface|null
+   *   The bundle entity.
+   */
+  protected function getBundleEntity() {
+    if ($bundle_entity_type = $this->entity->getEntityType()->getBundleEntityType()) {
+      return $this->entityTypeManager->getStorage($bundle_entity_type)->load($this->entity->bundle());
+    }
+    return NULL;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function form(array $form, FormStateInterface $form_state) {
+
+    if ($this->showRevisionUi()) {
+      // Advanced tab must be the first, because other fields rely on that.
+      if (!isset($form['advanced'])) {
+        $form['advanced'] = [
+          '#type' => 'vertical_tabs',
+          '#weight' => 99,
+        ];
+      }
+    }
+
     $form = parent::form($form, $form_state);
+
     // Content entity forms do not use the parent's #after_build callback
     // because they only need to rebuild the entity in the validation and the
     // submit handler because Field API uses its own #after_build callback for
@@ -54,6 +98,11 @@ public function form(array $form, FormStateInterface $form_state) {
     $this->getFormDisplay($form_state)->buildForm($this->entity, $form, $form_state);
     // Allow modules to act before and after form language is updated.
     $form['#entity_builders']['update_form_langcode'] = '::updateFormLangcode';
+
+    if ($this->showRevisionUi()) {
+      $this->addRevisionableFormFields($form);
+    }
+
     return $form;
   }
 
@@ -76,6 +125,22 @@ public function buildEntity(array $form, FormStateInterface $form_state) {
     // Mark the entity as requiring validation.
     $entity->setValidationRequired(!$form_state->getTemporaryValue('entity_validated'));
 
+    if ($this->showRevisionUi()) {
+      // Save as a new revision if requested to do so.
+      if (!$form_state->isValueEmpty('revision')) {
+        $entity->setNewRevision();
+        if ($entity instanceof RevisionLogInterface) {
+          // If a new revision is created, save the current user as
+          // revision author.
+          $entity->setRevisionUserId(\Drupal::currentUser()->id());
+          $entity->setRevisionCreationTime(\Drupal::time()->getRequestTime());
+        }
+      }
+      else {
+        $entity->setNewRevision(FALSE);
+      }
+    }
+
     return $entity;
   }
 
@@ -283,8 +348,89 @@ public function updateFormLangcode($entity_type_id, EntityInterface $entity, arr
    */
   public function updateChangedTime(EntityInterface $entity) {
     if ($entity->getEntityType()->isSubclassOf(EntityChangedInterface::class)) {
-      $entity->setChangedTime(REQUEST_TIME);
+      $entity->setChangedTime(\Drupal::time()->getRequestTime());
+    }
+  }
+
+  /**
+   * Add revision form fields if the entity enabled the UI.
+   *
+   * @param array $form
+   *   An associative array containing the structure of the form.
+   */
+  public function addRevisionableFormFields(&$form) {
+    $entity_type = $this->entity->getEntityType();
+
+    if ($this->operation == 'edit') {
+      $bundle_info = \Drupal::service('entity_type.bundle.info')->getBundleInfo($entity_type->id());
+      if ($bundle_info[$this->entity->bundle()]) {
+        $form['#title'] = $this->t('<em>Edit @bundle_label</em> @label', [
+          '@bundle_label' => $bundle_info[$this->entity->bundle()]['label'],
+          '@label' => $this->entity->label(),
+        ]);
+      }
+      else {
+        $form['#title'] = $this->t('<em>Edit</em> @label', [
+          '@label' => $this->entity->label(),
+        ]);
+      }
+    }
+
+    $new_revision_default = FALSE;
+    $bundle_entity = $this->getBundleEntity();
+    if ($bundle_entity instanceof RevisionableEntityBundleInterface) {
+      // Always use the default revision setting.
+      $new_revision_default = $bundle_entity->shouldCreateNewRevision();
+    }
+
+    // Add a log field if the "Create new revision" option is checked, or if the
+    // current user has the ability to check that option.
+    $form['revision_information'] = [
+      '#type' => 'details',
+      '#title' => $this->t('Revision information'),
+      // Open by default when "Create new revision" is checked.
+      '#open' => $new_revision_default,
+      '#group' => 'advanced',
+      '#weight' => 20,
+      '#access' => $new_revision_default || $this->entity->get($entity_type->getKey('revision'))->access('update'),
+      '#optional' => TRUE,
+      '#attributes' => [
+        'class' => ['entity-content-form-revision-information'],
+      ],
+      '#attached' => [
+        'library' => ['core/drupal.entity-form'],
+      ],
+    ];
+
+    $form['revision'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Create new revision'),
+      '#default_value' => $new_revision_default,
+      '#access' => !$this->entity->isNew() && $this->entity->get($entity_type->getKey('revision'))->access('update'),
+      '#group' => 'revision_information',
+    ];
+
+    if (isset($form['revision_log'])) {
+      $form['revision_log'] += [
+        '#group' => 'revision_information',
+        '#states' => [
+          'visible' => [
+            ':input[name="revision"]' => ['checked' => TRUE],
+          ],
+        ],
+      ];
     }
   }
 
+
+  /**
+   * Checks whether the revision form fields should be added to the form.
+   *
+   * @return bool
+   *   TRUE if the form field should be added, FALSE otherwise.
+   */
+  protected function showRevisionUi() {
+    return $this->entity->getEntityType()->showRevisionUi();
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityType.php b/core/lib/Drupal/Core/Entity/EntityType.php
index 89b83cf..0461dd0 100644
--- a/core/lib/Drupal/Core/Entity/EntityType.php
+++ b/core/lib/Drupal/Core/Entity/EntityType.php
@@ -175,6 +175,13 @@ class EntityType implements EntityTypeInterface {
   protected $translatable = FALSE;
 
   /**
+   * Indicates whether the revision form fields should be added to the form.
+   *
+   * @var bool
+   */
+  protected $show_revision_ui = FALSE;
+
+  /**
    * The human-readable name of the type.
    *
    * @var string
@@ -692,6 +699,13 @@ public function getBaseTable() {
   /**
    * {@inheritdoc}
    */
+  public function showRevisionUi() {
+    return $this->show_revision_ui;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function isTranslatable() {
     return !empty($this->translatable);
   }
diff --git a/core/lib/Drupal/Core/Entity/EntityTypeInterface.php b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
index c6336c9..60d60b5 100644
--- a/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
@@ -574,6 +574,14 @@ public function getBaseTable();
   public function isTranslatable();
 
   /**
+   * Indicates whether the revision form fields should be added to the form.
+   *
+   * @return bool
+   *   TRUE if the form field should be added, FALSE otherwise.
+   */
+  public function showRevisionUi();
+
+  /**
    * Indicates whether entities of this type have revision support.
    *
    * @return bool
diff --git a/core/lib/Drupal/Core/Entity/RevisionableEntityBundleInterface.php b/core/lib/Drupal/Core/Entity/RevisionableEntityBundleInterface.php
new file mode 100644
index 0000000..caf44f2
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/RevisionableEntityBundleInterface.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace Drupal\Core\Entity;
+
+use Drupal\Core\Config\Entity\ConfigEntityInterface;
+
+/**
+ * Provides an interface defining a revisionable entity bundle.
+ */
+interface RevisionableEntityBundleInterface extends ConfigEntityInterface {
+
+  /**
+   * Gets whether a new revision should be created by default.
+   *
+   * @return bool
+   *   TRUE if a new revision should be created by default.
+   */
+  public function shouldCreateNewRevision();
+
+}
diff --git a/core/modules/block_content/js/block_content.js b/core/misc/entity-form.js
similarity index 82%
rename from core/modules/block_content/js/block_content.js
rename to core/misc/entity-form.js
index bbc0533..f86c416 100644
--- a/core/modules/block_content/js/block_content.js
+++ b/core/misc/entity-form.js
@@ -8,20 +8,20 @@
   'use strict';
 
   /**
-   * Sets summaries about revision and translation of block content.
+   * Sets summaries about revision and translation of entities.
    *
    * @type {Drupal~behavior}
    *
    * @prop {Drupal~behaviorAttach} attach
-   *   Attaches summary behaviour block content form tabs.
+   *   Attaches summary behaviour entity form tabs.
    *
    *   Specifically, it updates summaries to the revision information and the
    *   translation options.
    */
-  Drupal.behaviors.blockContentDetailsSummaries = {
+  Drupal.behaviors.entityContentDetailsSummaries = {
     attach: function (context) {
       var $context = $(context);
-      $context.find('.block-content-form-revision-information').drupalSetSummary(function (context) {
+      $context.find('.entity-content-form-revision-information').drupalSetSummary(function (context) {
         var $revisionContext = $(context);
         var revisionCheckbox = $revisionContext.find('.js-form-item-revision input');
 
@@ -36,7 +36,7 @@
         return Drupal.t('No revision');
       });
 
-      $context.find('fieldset.block-content-translation-options').drupalSetSummary(function (context) {
+      $context.find('details.entity-translation-options').drupalSetSummary(function (context) {
         var $translationContext = $(context);
         var translate;
         var $checkbox = $translationContext.find('.js-form-item-translation-translate input');
diff --git a/core/modules/block_content/block_content.libraries.yml b/core/modules/block_content/block_content.libraries.yml
deleted file mode 100644
index 5a1b5b6..0000000
--- a/core/modules/block_content/block_content.libraries.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-drupal.block_content:
-  version: VERSION
-  js:
-    js/block_content.js: {}
-  dependencies:
-    - core/jquery
-    - core/drupal
-    - core/drupal.form
diff --git a/core/modules/block_content/src/BlockContentForm.php b/core/modules/block_content/src/BlockContentForm.php
index 8815f56..a63a370 100644
--- a/core/modules/block_content/src/BlockContentForm.php
+++ b/core/modules/block_content/src/BlockContentForm.php
@@ -4,11 +4,7 @@
 
 use Drupal\Component\Utility\Html;
 use Drupal\Core\Entity\ContentEntityForm;
-use Drupal\Core\Entity\EntityManagerInterface;
-use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Language\LanguageManagerInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Form handler for the custom block edit forms.
@@ -16,27 +12,6 @@
 class BlockContentForm extends ContentEntityForm {
 
   /**
-   * The custom block storage.
-   *
-   * @var \Drupal\Core\Entity\EntityStorageInterface
-   */
-  protected $blockContentStorage;
-
-  /**
-   * The custom block type storage.
-   *
-   * @var \Drupal\Core\Entity\EntityStorageInterface
-   */
-  protected $blockContentTypeStorage;
-
-  /**
-   * The language manager.
-   *
-   * @var \Drupal\Core\Language\LanguageManagerInterface
-   */
-  protected $languageManager;
-
-  /**
    * The block content entity.
    *
    * @var \Drupal\block_content\BlockContentInterface
@@ -44,177 +19,62 @@ class BlockContentForm extends ContentEntityForm {
   protected $entity;
 
   /**
-   * Constructs a BlockContentForm object.
-   *
-   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
-   *   The entity manager.
-   * @param \Drupal\Core\Entity\EntityStorageInterface $block_content_storage
-   *   The custom block storage.
-   * @param \Drupal\Core\Entity\EntityStorageInterface $block_content_type_storage
-   *   The custom block type storage.
-   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
-   *   The language manager.
-   */
-  public function __construct(EntityManagerInterface $entity_manager, EntityStorageInterface $block_content_storage, EntityStorageInterface $block_content_type_storage, LanguageManagerInterface $language_manager) {
-    parent::__construct($entity_manager);
-    $this->blockContentStorage = $block_content_storage;
-    $this->blockContentTypeStorage = $block_content_type_storage;
-    $this->languageManager = $language_manager;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container) {
-    $entity_manager = $container->get('entity.manager');
-    return new static(
-      $entity_manager,
-      $entity_manager->getStorage('block_content'),
-      $entity_manager->getStorage('block_content_type'),
-      $container->get('language_manager')
-    );
-  }
-
-  /**
-   * Overrides \Drupal\Core\Entity\EntityForm::prepareEntity().
-   *
-   * Prepares the custom block object.
-   *
-   * Fills in a few default values, and then invokes
-   * hook_block_content_prepare() on all modules.
-   */
-  protected function prepareEntity() {
-    $block = $this->entity;
-    // Set up default values, if required.
-    $block_type = $this->blockContentTypeStorage->load($block->bundle());
-    if (!$block->isNew()) {
-      $block->setRevisionLogMessage(NULL);
-    }
-    // Always use the default revision setting.
-    $block->setNewRevision($block_type->shouldCreateNewRevision());
-  }
-
-  /**
    * {@inheritdoc}
    */
   public function form(array $form, FormStateInterface $form_state) {
     $block = $this->entity;
-    $account = $this->currentUser();
+
+    $form = parent::form($form, $form_state);
 
     if ($this->operation == 'edit') {
-      $form['#title'] = $this->t('Edit custom block %label', array('%label' => $block->label()));
+      $form['#title'] = $this->t('Edit custom block %label', ['%label' => $block->label()]);
     }
     // Override the default CSS class name, since the user-defined custom block
     // type name in 'TYPE-block-form' potentially clashes with third-party class
     // names.
     $form['#attributes']['class'][0] = 'block-' . Html::getClass($block->bundle()) . '-form';
 
-    $form['advanced'] = array(
-      '#type' => 'vertical_tabs',
-      '#weight' => 99,
-    );
-
-    // Add a log field if the "Create new revision" option is checked, or if the
-    // current user has the ability to check that option.
-    $form['revision_information'] = array(
-      '#type' => 'details',
-      '#title' => $this->t('Revision information'),
-      // Open by default when "Create new revision" is checked.
-      '#open' => $block->isNewRevision(),
-      '#group' => 'advanced',
-      '#attributes' => array(
-        'class' => array('block-content-form-revision-information'),
-      ),
-      '#attached' => array(
-        'library' => array('block_content/drupal.block_content'),
-      ),
-      '#weight' => 20,
-      '#access' => $block->isNewRevision() || $account->hasPermission('administer blocks'),
-    );
-
-    $form['revision_information']['revision'] = array(
-      '#type' => 'checkbox',
-      '#title' => $this->t('Create new revision'),
-      '#default_value' => $block->isNewRevision(),
-      '#access' => $account->hasPermission('administer blocks'),
-    );
-
-    // Check the revision log checkbox when the log textarea is filled in.
-    // This must not happen if "Create new revision" is enabled by default,
-    // since the state would auto-disable the checkbox otherwise.
-    if (!$block->isNewRevision()) {
-      $form['revision_information']['revision']['#states'] = array(
-        'checked' => array(
-          'textarea[name="revision_log"]' => array('empty' => FALSE),
-        ),
-      );
-    }
-
-    $form['revision_information']['revision_log'] = array(
-      '#type' => 'textarea',
-      '#title' => $this->t('Revision log message'),
-      '#rows' => 4,
-      '#default_value' => $block->getRevisionLog(),
-      '#description' => $this->t('Briefly describe the changes you have made.'),
-    );
-
-    return parent::form($form, $form_state, $block);
+    return $form;
   }
 
   /**
    * {@inheritdoc}
    */
   public function save(array $form, FormStateInterface $form_state) {
-    $block = $this->entity;
-
-    // Save as a new revision if requested to do so.
-    if (!$form_state->isValueEmpty('revision')) {
-      $block->setNewRevision();
-      // If a new revision is created, save the current user as revision author.
-      $block->setRevisionCreationTime(REQUEST_TIME);
-      $block->setRevisionUserId(\Drupal::currentUser()->id());
-    }
-
-    $insert = $block->isNew();
-    $block->save();
-    $context = array('@type' => $block->bundle(), '%info' => $block->label());
-    $logger = $this->logger('block_content');
-    $block_type = $this->blockContentTypeStorage->load($block->bundle());
-    $t_args = array('@type' => $block_type->label(), '%info' => $block->label());
+    $insert = $this->entity->isNew();
+    $this->entity->save();
+    $context = ['@type' => $this->entity->bundle(), '%info' => $this->entity->label()];
+    $logger = $this->logger($this->entity->getEntityTypeId());
+    $bundle_entity = $this->getBundleEntity();
+    $t_args = ['@type' => $bundle_entity ? $bundle_entity->label() : $this->entity->getEntityType()->getLabel(), '%label' => $this->entity->label()];
 
     if ($insert) {
       $logger->notice('@type: added %info.', $context);
-      drupal_set_message($this->t('@type %info has been created.', $t_args));
+      drupal_set_message($this->t('@type %label has been created.', $t_args));
     }
     else {
       $logger->notice('@type: updated %info.', $context);
-      drupal_set_message($this->t('@type %info has been updated.', $t_args));
+      drupal_set_message($this->t('@type %label has been updated.', $t_args));
     }
 
-    if ($block->id()) {
-      $form_state->setValue('id', $block->id());
-      $form_state->set('id', $block->id());
-      if ($insert) {
-        if (!$theme = $block->getTheme()) {
-          $theme = $this->config('system.theme')->get('default');
-        }
-        $form_state->setRedirect(
-          'block.admin_add',
-          array(
-            'plugin_id' => 'block_content:' . $block->uuid(),
-            'theme' => $theme,
-          )
-        );
-      }
-      else {
-        $form_state->setRedirectUrl($block->urlInfo('collection'));
-      }
+    if ($this->entity->getEntityType()->hasLinkTemplate('collection')) {
+      $form_state->setRedirectUrl($this->entity->toUrl('collection'));
     }
     else {
-      // In the unlikely case something went wrong on save, the block will be
-      // rebuilt and block form redisplayed.
-      drupal_set_message($this->t('The block could not be saved.'), 'error');
-      $form_state->setRebuild();
+      $form_state->setRedirectUrl($this->entity->toUrl('canonical'));
+    }
+
+    if ($insert) {
+      if (!$theme = $this->entity->getTheme()) {
+        $theme = $this->config('system.theme')->get('default');
+      }
+      $form_state->setRedirect(
+        'block.admin_add',
+        [
+          'plugin_id' => 'block_content:' . $this->entity->uuid(),
+          'theme' => $theme,
+        ]
+      );
     }
   }
 
diff --git a/core/modules/block_content/src/BlockContentTranslationHandler.php b/core/modules/block_content/src/BlockContentTranslationHandler.php
index 781ba1b..b446cde 100644
--- a/core/modules/block_content/src/BlockContentTranslationHandler.php
+++ b/core/modules/block_content/src/BlockContentTranslationHandler.php
@@ -5,7 +5,6 @@
 use Drupal\block_content\Entity\BlockContentType;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\content_translation\ContentTranslationHandler;
-use Drupal\Core\Form\FormStateInterface;
 
 /**
  * Defines the translation handler for custom blocks.
@@ -15,23 +14,6 @@ class BlockContentTranslationHandler extends ContentTranslationHandler {
   /**
    * {@inheritdoc}
    */
-  public function entityFormAlter(array &$form, FormStateInterface $form_state, EntityInterface $entity) {
-    parent::entityFormAlter($form, $form_state, $entity);
-    // Move the translation fieldset to a vertical tab.
-    if (isset($form['translation'])) {
-      $form['translation'] += array(
-        '#group' => 'additional_settings',
-        '#weight' => 100,
-        '#attributes' => array(
-          'class' => array('block-content-translation-options'),
-        ),
-      );
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   protected function entityFormTitle(EntityInterface $entity) {
     $block_type = BlockContentType::load($entity->bundle());
     return t('<em>Edit @type</em> @title', array('@type' => $block_type->label(), '@title' => $entity->label()));
diff --git a/core/modules/block_content/src/BlockContentTypeInterface.php b/core/modules/block_content/src/BlockContentTypeInterface.php
index 9229dab..da3864e 100644
--- a/core/modules/block_content/src/BlockContentTypeInterface.php
+++ b/core/modules/block_content/src/BlockContentTypeInterface.php
@@ -3,11 +3,12 @@
 namespace Drupal\block_content;
 
 use Drupal\Core\Config\Entity\ConfigEntityInterface;
+use Drupal\Core\Entity\RevisionableEntityBundleInterface;
 
 /**
  * Provides an interface defining a custom block type entity.
  */
-interface BlockContentTypeInterface extends ConfigEntityInterface {
+interface BlockContentTypeInterface extends ConfigEntityInterface, RevisionableEntityBundleInterface {
 
   /**
    * Returns the description of the block type.
@@ -17,12 +18,4 @@
    */
   public function getDescription();
 
-  /**
-   * Returns whether a new revision should be created by default.
-   *
-   * @return bool
-   *   TRUE if a new revision should be created by default.
-   */
-  public function shouldCreateNewRevision();
-
 }
diff --git a/core/modules/block_content/src/Entity/BlockContent.php b/core/modules/block_content/src/Entity/BlockContent.php
index 13af342..51ae6f6 100644
--- a/core/modules/block_content/src/Entity/BlockContent.php
+++ b/core/modules/block_content/src/Entity/BlockContent.php
@@ -35,6 +35,7 @@
  *   base_table = "block_content",
  *   revision_table = "block_content_revision",
  *   data_table = "block_content_field_data",
+ *   show_revision_ui = TRUE,
  *   links = {
  *     "canonical" = "/block/{block_content}",
  *     "delete-form" = "/block/{block_content}/delete",
@@ -182,7 +183,14 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     $fields['revision_log'] = BaseFieldDefinition::create('string_long')
       ->setLabel(t('Revision log message'))
       ->setDescription(t('The log entry explaining the changes in this revision.'))
-      ->setRevisionable(TRUE);
+      ->setRevisionable(TRUE)
+      ->setDisplayOptions('form', array(
+        'type' => 'string_textarea',
+        'weight' => 25,
+        'settings' => array(
+          'rows' => 4,
+        ),
+      ));
 
     $fields['changed'] = BaseFieldDefinition::create('changed')
       ->setLabel(t('Changed'))
diff --git a/core/modules/content_translation/src/ContentTranslationHandler.php b/core/modules/content_translation/src/ContentTranslationHandler.php
index 6fd2254..7995125 100644
--- a/core/modules/content_translation/src/ContentTranslationHandler.php
+++ b/core/modules/content_translation/src/ContentTranslationHandler.php
@@ -384,6 +384,16 @@ public function entityFormAlter(array &$form, FormStateInterface $form_state, En
         '#multilingual' => TRUE,
       );
 
+      if (isset($form['advanced'])) {
+        $form['content_translation'] += array(
+          '#group' => 'advanced',
+          '#weight' => 100,
+          '#attributes' => array(
+            'class' => array('entity-translation-options'),
+          ),
+        );
+      }
+
       // A new translation is enabled by default.
       $metadata = $this->manager->getTranslationMetadata($entity);
       $status = $new_translation || $metadata->isPublished();
diff --git a/core/modules/node/node.js b/core/modules/node/node.js
index 98af6fd..086263d 100644
--- a/core/modules/node/node.js
+++ b/core/modules/node/node.js
@@ -18,21 +18,6 @@
   Drupal.behaviors.nodeDetailsSummaries = {
     attach: function (context) {
       var $context = $(context);
-      $context.find('.node-form-revision-information').drupalSetSummary(function (context) {
-        var $revisionContext = $(context);
-        var revisionCheckbox = $revisionContext.find('.js-form-item-revision input');
-
-          // Return 'New revision' if the 'Create new revision' checkbox is
-          // checked, or if the checkbox doesn't exist, but the revision log does.
-          // For users without the "Administer content" permission the checkbox
-          // won't appear, but the revision log will if the content type is set to
-          // auto-revision.
-        if (revisionCheckbox.is(':checked') || (!revisionCheckbox.length && $revisionContext.find('.js-form-item-revision-log textarea').length)) {
-          return Drupal.t('New revision');
-        }
-
-        return Drupal.t('No revision');
-      });
 
       $context.find('.node-form-author').drupalSetSummary(function (context) {
         var $authorContext = $(context);
@@ -64,22 +49,6 @@
           return Drupal.t('Not promoted');
         }
       });
-
-      $context.find('fieldset.node-translation-options').drupalSetSummary(function (context) {
-        var $translationContext = $(context);
-        var translate;
-        var $checkbox = $translationContext.find('.js-form-item-translation-translate input');
-
-        if ($checkbox.size()) {
-          translate = $checkbox.is(':checked') ? Drupal.t('Needs to be updated') : Drupal.t('Does not need to be updated');
-        }
-        else {
-          $checkbox = $translationContext.find('.js-form-item-translation-retranslate input');
-          translate = $checkbox.is(':checked') ? Drupal.t('Flag other translations as outdated') : Drupal.t('Do not flag other translations as outdated');
-        }
-
-        return translate;
-      });
     }
   };
 
diff --git a/core/modules/node/node.libraries.yml b/core/modules/node/node.libraries.yml
index 22c93ac..59947a2 100644
--- a/core/modules/node/node.libraries.yml
+++ b/core/modules/node/node.libraries.yml
@@ -6,10 +6,8 @@ drupal.node:
   js:
     node.js: {}
   dependencies:
-    - core/jquery
-    - core/drupal
+    - core/drupal.entity-form
     - core/drupalSettings
-    - core/drupal.form
 
 drupal.node.preview:
   version: VERSION
diff --git a/core/modules/node/src/Entity/Node.php b/core/modules/node/src/Entity/Node.php
index f034957..68afc83 100644
--- a/core/modules/node/src/Entity/Node.php
+++ b/core/modules/node/src/Entity/Node.php
@@ -46,6 +46,7 @@
  *   data_table = "node_field_data",
  *   revision_table = "node_revision",
  *   revision_data_table = "node_field_revision",
+ *   show_revision_ui = TRUE,
  *   translatable = TRUE,
  *   list_cache_contexts = { "user.node_grants:view" },
  *   entity_keys = {
diff --git a/core/modules/node/src/Entity/NodeType.php b/core/modules/node/src/Entity/NodeType.php
index 91d8a90..23f24a4 100644
--- a/core/modules/node/src/Entity/NodeType.php
+++ b/core/modules/node/src/Entity/NodeType.php
@@ -205,4 +205,11 @@ public static function postDelete(EntityStorageInterface $storage, array $entiti
     $storage->resetCache(array_keys($entities));
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function shouldCreateNewRevision() {
+    return $this->isNewRevision();
+  }
+
 }
diff --git a/core/modules/node/src/NodeForm.php b/core/modules/node/src/NodeForm.php
index 2828030..9235146 100644
--- a/core/modules/node/src/NodeForm.php
+++ b/core/modules/node/src/NodeForm.php
@@ -4,6 +4,7 @@
 
 use Drupal\Core\Entity\ContentEntityForm;
 use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\user\PrivateTempStoreFactory;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -11,7 +12,7 @@
 /**
  * Form handler for the node edit forms.
  */
-class NodeForm extends ContentEntityForm {
+class NodeForm extends ContentEntityForm  {
 
   /**
    * The tempstore factory.
@@ -26,7 +27,7 @@ class NodeForm extends ContentEntityForm {
   protected $hasBeenPreviewed = FALSE;
 
   /**
-   * Constructs a ContentEntityForm object.
+   * Constructs a NodeForm object.
    *
    * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager.
@@ -51,19 +52,6 @@ public static function create(ContainerInterface $container) {
   /**
    * {@inheritdoc}
    */
-  protected function prepareEntity() {
-    /** @var \Drupal\node\NodeInterface $node */
-    $node = $this->entity;
-
-    if (!$node->isNew()) {
-      // Remove the revision log message from the original node entity.
-      $node->revision_log = NULL;
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function form(array $form, FormStateInterface $form_state) {
     // Try to restore from temp store, this must be done before calling
     // parent::form().
@@ -98,10 +86,6 @@ public function form(array $form, FormStateInterface $form_state) {
     /** @var \Drupal\node\NodeInterface $node */
     $node = $this->entity;
 
-    if ($this->operation == 'edit') {
-      $form['#title'] = $this->t('<em>Edit @type</em> @title', array('@type' => node_get_type_label($node), '@title' => $node->label()));
-    }
-
     $current_user = $this->currentUser();
 
     // Changed must be sent to the client, for later overwrite error checking.
@@ -110,47 +94,11 @@ public function form(array $form, FormStateInterface $form_state) {
       '#default_value' => $node->getChangedTime(),
     );
 
-    $form['advanced'] = array(
-      '#type' => 'vertical_tabs',
-      '#attributes' => array('class' => array('entity-meta')),
-      '#weight' => 99,
-    );
     $form = parent::form($form, $form_state);
 
-    // Add a revision_log field if the "Create new revision" option is checked,
-    // or if the current user has the ability to check that option.
-    $form['revision_information'] = array(
-      '#type' => 'details',
-      '#group' => 'advanced',
-      '#title' => t('Revision information'),
-      // Open by default when "Create new revision" is checked.
-      '#open' => $node->isNewRevision(),
-      '#attributes' => array(
-        'class' => array('node-form-revision-information'),
-      ),
-      '#attached' => array(
-        'library' => array('node/drupal.node'),
-      ),
-      '#weight' => 20,
-      '#optional' => TRUE,
-    );
-
-    $form['revision'] = array(
-      '#type' => 'checkbox',
-      '#title' => t('Create new revision'),
-      '#default_value' => $node->type->entity->isNewRevision(),
-      '#access' => $current_user->hasPermission('administer nodes') && !$node->isNew(),
-      '#group' => 'revision_information',
-    );
+    $form['advanced']['#attributes']['class'][] = 'entity-meta';
 
-    $form['revision_log'] += array(
-      '#states' => array(
-        'visible' => array(
-          ':input[name="revision"]' => array('checked' => TRUE),
-        ),
-      ),
-      '#group' => 'revision_information',
-    );
+    $form['revision']['#access'] = $current_user->hasPermission('administer nodes') && !$node->isNew();
 
     // Node author information for administrators.
     $form['author'] = array(
@@ -304,32 +252,6 @@ protected function actions(array $form, FormStateInterface $form_state) {
   }
 
   /**
-   * {@inheritdoc}
-   *
-   * Updates the node object by processing the submitted values.
-   *
-   * This function can be called by a "Next" button of a wizard to update the
-   * form state's entity with the current step's values before proceeding to the
-   * next step.
-   */
-  public function submitForm(array &$form, FormStateInterface $form_state) {
-    // Build the node object from the submitted values.
-    parent::submitForm($form, $form_state);
-    $node = $this->entity;
-
-    // Save as a new revision if requested to do so.
-    if (!$form_state->isValueEmpty('revision') && $form_state->getValue('revision') != FALSE) {
-      $node->setNewRevision();
-      // If a new revision is created, save the current user as revision author.
-      $node->setRevisionCreationTime(REQUEST_TIME);
-      $node->setRevisionUserId(\Drupal::currentUser()->id());
-    }
-    else {
-      $node->setNewRevision(FALSE);
-    }
-  }
-
-  /**
    * Form submission handler for the 'preview' action.
    *
    * @param $form
diff --git a/core/modules/node/src/NodeTranslationHandler.php b/core/modules/node/src/NodeTranslationHandler.php
index 9c9741e..2f6d2ab 100644
--- a/core/modules/node/src/NodeTranslationHandler.php
+++ b/core/modules/node/src/NodeTranslationHandler.php
@@ -17,17 +17,7 @@ class NodeTranslationHandler extends ContentTranslationHandler {
   public function entityFormAlter(array &$form, FormStateInterface $form_state, EntityInterface $entity) {
     parent::entityFormAlter($form, $form_state, $entity);
 
-    // Move the translation fieldset to a vertical tab.
     if (isset($form['content_translation'])) {
-      $form['content_translation'] += array(
-        '#group' => 'advanced',
-        '#attributes' => array(
-          'class' => array('node-translation-options'),
-        ),
-      );
-
-      $form['content_translation']['#weight'] = 100;
-
       // We do not need to show these values on node forms: they inherit the
       // basic node property values.
       $form['content_translation']['status']['#access'] = FALSE;
diff --git a/core/modules/node/src/NodeTypeInterface.php b/core/modules/node/src/NodeTypeInterface.php
index c034ffb..1aab64d 100644
--- a/core/modules/node/src/NodeTypeInterface.php
+++ b/core/modules/node/src/NodeTypeInterface.php
@@ -3,11 +3,12 @@
 namespace Drupal\node;
 
 use Drupal\Core\Config\Entity\ConfigEntityInterface;
+use Drupal\Core\Entity\RevisionableEntityBundleInterface;
 
 /**
  * Provides an interface defining a node type entity.
  */
-interface NodeTypeInterface extends ConfigEntityInterface {
+interface NodeTypeInterface extends ConfigEntityInterface, RevisionableEntityBundleInterface {
 
   /**
    * Determines whether the node type is locked.
@@ -28,7 +29,7 @@ public function isNewRevision();
   /**
    * Sets whether a new revision should be created by default.
    *
-   * @param bool $new_revision_
+   * @param bool $new_revision
    *   TRUE if a new revision should be created by default.
    */
   public function setNewRevision($new_revision);
diff --git a/core/modules/serialization/tests/src/Kernel/EntitySerializationTest.php b/core/modules/serialization/tests/src/Kernel/EntitySerializationTest.php
index 0f84d3d..47be4de 100644
--- a/core/modules/serialization/tests/src/Kernel/EntitySerializationTest.php
+++ b/core/modules/serialization/tests/src/Kernel/EntitySerializationTest.php
@@ -29,7 +29,7 @@ class EntitySerializationTest extends NormalizerTestBase {
   /**
    * The test entity.
    *
-   * @var \Drupal\Core\Entity\ContentEntityBase
+   * @var \Drupal\Core\Entity\ContentEntityInterface|\Drupal\Core\Entity\RevisionLogInterface
    */
   protected $entity;
 
@@ -123,6 +123,11 @@ public function testNormalize() {
         array('value' => TRUE),
       ),
       'non_rev_field' => array(),
+      'revision_created' => array(
+        array('value' => $this->entity->getRevisionCreationTime()),
+      ),
+      'revision_user' => array(),
+      'revision_log_message' => array(),
       'field_test_text' => array(
         array(
           'value' => $this->values['field_test_text']['value'],
@@ -192,6 +197,9 @@ public function testSerialize() {
       'revision_id' => '<revision_id><value>' . $this->entity->getRevisionId() . '</value></revision_id>',
       'default_langcode' => '<default_langcode><value>1</value></default_langcode>',
       'non_rev_field' => '<non_rev_field/>',
+      'revision_created' => '<revision_created><value>' . $this->entity->getRevisionCreationTime() . '</value></revision_created>',
+      'revision_user' => '<revision_user/>',
+      'revision_log_message' => '<revision_log_message/>',
       'field_test_text' => '<field_test_text><value>' . $this->values['field_test_text']['value'] . '</value><format>' . $this->values['field_test_text']['format'] . '</format></field_test_text>',
     );
     // Sort it in the same order as normalised.
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php
index 0ee28bf..f01675f 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php
@@ -27,6 +27,7 @@
  *   revision_data_table = "entity_test_mulrev_property_revision",
  *   admin_permission = "administer entity_test content",
  *   translatable = TRUE,
+ *   show_revision_ui = TRUE,
  *   entity_keys = {
  *     "id" = "id",
  *     "uuid" = "uuid",
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php
index 47035ea..49c5aca 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php
@@ -3,6 +3,8 @@
 namespace Drupal\entity_test\Entity;
 
 use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\RevisionLogEntityTrait;
+use Drupal\Core\Entity\RevisionLogInterface;
 use Drupal\Core\Field\BaseFieldDefinition;
 
 /**
@@ -28,6 +30,7 @@
  *   base_table = "entity_test_rev",
  *   revision_table = "entity_test_rev_revision",
  *   admin_permission = "administer entity_test content",
+ *   show_revision_ui = TRUE,
  *   entity_keys = {
  *     "id" = "id",
  *     "uuid" = "uuid",
@@ -45,7 +48,9 @@
  *   }
  * )
  */
-class EntityTestRev extends EntityTest {
+class EntityTestRev extends EntityTest implements RevisionLogInterface {
+
+  use RevisionLogEntityTrait;
 
   /**
    * {@inheritdoc}
@@ -53,13 +58,8 @@ class EntityTestRev extends EntityTest {
   public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     $fields = parent::baseFieldDefinitions($entity_type);
 
-    $fields['revision_id'] = BaseFieldDefinition::create('integer')
-      ->setLabel(t('Revision ID'))
-      ->setDescription(t('The version id of the test entity.'))
-      ->setReadOnly(TRUE)
-      ->setSetting('unsigned', TRUE);
+    $fields += static::revisionLogBaseFieldDefinitions($entity_type);
 
-    $fields['langcode']->setRevisionable(TRUE);
     $fields['name']->setRevisionable(TRUE);
     $fields['user_id']->setRevisionable(TRUE);
 
diff --git a/core/modules/views/tests/src/Unit/EntityViewsDataTest.php b/core/modules/views/tests/src/Unit/EntityViewsDataTest.php
index 60ba8e5..acb84e5 100644
--- a/core/modules/views/tests/src/Unit/EntityViewsDataTest.php
+++ b/core/modules/views/tests/src/Unit/EntityViewsDataTest.php
@@ -89,6 +89,12 @@ protected function setUp() {
     $typed_data_manager->expects($this->any())
         ->method('createDataDefinition')
         ->willReturn($this->getMock('Drupal\Core\TypedData\DataDefinitionInterface'));
+
+    $typed_data_manager->expects($this->any())
+      ->method('getDefinition')
+      ->with($this->equalTo('field_item:string_long'))
+      ->willReturn(array('class' => '\Drupal\Core\Field\Plugin\Field\FieldType\StringLongItem'));
+
     $this->baseEntityType = new TestEntityType([
       'base_table' => 'entity_test',
       'id' => 'entity_test',
@@ -1107,3 +1113,12 @@ function t($string, array $args = []) {
     return strtr($string, $args);
   }
 }
+
+
+namespace Drupal\Core\Entity;
+
+if (!function_exists('t')) {
+  function t($string, array $args = []) {
+    return strtr($string, $args);
+  }
+}
diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php
index 23cf034..e49ee7e 100644
--- a/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php
@@ -102,6 +102,9 @@ public function testEntityTypeUpdateWithoutData() {
     $expected = array(
       'entity_test_update' => array(
         t('The %entity_type entity type needs to be updated.', ['%entity_type' => $this->entityManager->getDefinition('entity_test_update')->getLabel()]),
+        // The revision key is now defined, so the revision field needs to be
+        // created.
+        t('The %field_name field needs to be installed.', ['%field_name' => 'Revision ID']),
       ),
     );
     $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected); //, 'EntityDefinitionUpdateManager reports the expected change summary.');
@@ -797,7 +800,7 @@ public function testBaseFieldEntityKeyUpdateWithExistingData() {
   /**
    * Check that field schema is correctly handled with long-named fields.
    */
-  function testLongNameFieldIndexes() {
+  public function testLongNameFieldIndexes() {
     $this->addLongNameBaseField();
     $entity_type_id = 'entity_test_update';
     $entity_type = $this->entityManager->getDefinition($entity_type_id);
diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityFieldTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityFieldTest.php
index 56af888..5dc1a94 100644
--- a/core/tests/Drupal/KernelTests/Core/Entity/EntityFieldTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityFieldTest.php
@@ -3,6 +3,7 @@
 namespace Drupal\KernelTests\Core\Entity;
 
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\RevisionLogInterface;
 use Drupal\Core\Entity\TypedData\EntityDataDefinition;
 use Drupal\Core\Entity\TypedData\EntityDataDefinitionInterface;
 use Drupal\Core\Field\BaseFieldDefinition;
@@ -572,6 +573,12 @@ protected function doTestDataStructureInterfaces($entity_type) {
       // Field format.
       NULL,
     );
+
+    if ($entity instanceof RevisionLogInterface) {
+      // Adding empty string for revision message.
+      $target_strings[] = '';
+    }
+
     asort($strings);
     asort($target_strings);
     $this->assertEqual(array_values($strings), array_values($target_strings), format_string('%entity_type: All contained strings found.', array('%entity_type' => $entity_type)));
