diff --git a/core/lib/Drupal/Core/Entity/EntityChangedInterface.php b/core/lib/Drupal/Core/Entity/EntityChangedInterface.php
index 747571799c..8f57863d94 100644
--- a/core/lib/Drupal/Core/Entity/EntityChangedInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityChangedInterface.php
@@ -37,10 +37,31 @@ public function setChangedTime($timestamp);
   /**
    * Gets the timestamp of the last entity change across all translations.
    *
+   * This method will return the highest timestamp across all translations. If
+   * you need to check that no translation was changed after a certain time, you
+   * have to check each translation's timestamp individually.
+   *
    * @return int
    *   The timestamp of the last entity save operation across all
    *   translations.
    */
+  public function getLatestChangedTimeAcrossTranslations();
+
+  /**
+   * Gets the timestamp of the last entity change across all translations.
+   *
+   * This method will return the highest timestamp across all translations. If
+   * you need to check that no translation was changed after a certain time, you
+   * have to check each translation's timestamp individually.
+   *
+   * @return int
+   *   The timestamp of the last entity save operation across all
+   *   translations.
+   *
+   * @deprecated in Drupal 8.5.x and will be removed before Drupal 9.0.0. Use
+   *   \Drupal\Core\Entity\EntityChangedInterface::getLatestChangedTimeAcrossTranslations()
+   *   instead.
+   */
   public function getChangedTimeAcrossTranslations();
 
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityChangedTrait.php b/core/lib/Drupal/Core/Entity/EntityChangedTrait.php
index f94441bab6..654d4b17a4 100644
--- a/core/lib/Drupal/Core/Entity/EntityChangedTrait.php
+++ b/core/lib/Drupal/Core/Entity/EntityChangedTrait.php
@@ -8,13 +8,9 @@
 trait EntityChangedTrait {
 
   /**
-   * Returns the timestamp of the last entity change across all translations.
-   *
-   * @return int
-   *   The timestamp of the last entity save operation across all
-   *   translations.
+   * {@inheritdoc}
    */
-  public function getChangedTimeAcrossTranslations() {
+  public function getLatestChangedTimeAcrossTranslations() {
     $changed = $this->getUntranslated()->getChangedTime();
     foreach ($this->getTranslationLanguages(FALSE) as $language) {
       $translation_changed = $this->getTranslation($language->getId())->getChangedTime();
@@ -24,6 +20,13 @@ public function getChangedTimeAcrossTranslations() {
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function getChangedTimeAcrossTranslations() {
+    return $this->getLatestChangedTimeAcrossTranslations();
+  }
+
+  /**
    * Gets the timestamp of the last entity change for the current translation.
    *
    * @return int
diff --git a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityChangedConstraintValidator.php b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityChangedConstraintValidator.php
index b1fee2a294..271e24def2 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityChangedConstraintValidator.php
+++ b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityChangedConstraintValidator.php
@@ -18,10 +18,31 @@ public function validate($entity, Constraint $constraint) {
       /** @var \Drupal\Core\Entity\EntityInterface $entity */
       if (!$entity->isNew()) {
         $saved_entity = \Drupal::entityManager()->getStorage($entity->getEntityTypeId())->loadUnchanged($entity->id());
-        // A change to any other translation must add a violation to the current
-        // translation because there might be untranslatable shared fields.
-        if ($saved_entity && $saved_entity->getChangedTimeAcrossTranslations() > $entity->getChangedTimeAcrossTranslations()) {
-          $this->context->addViolation($constraint->message);
+        // Ensure that all the entity translations match their current version
+        // in the storage in order to avoid reverting other changes. The entity
+        // object might contain an older entity translation when a translation
+        // is being concurrently edited. If the form that is submitted last
+        // is not yet cached, the entity will be retrieved from the entity
+        // storage on submit, and that entity object will be the updated one
+        // from the previous form submission. To prevent the entity from the
+        // second submission from being saved and overwriting changes from the
+        // first, the changed timestamp can be added to the form so that it
+        // updates the entity object on submit. We use the timestamp to
+        // determine when a previous entity translation version has been edited
+        // and is currently being saved.
+        // @see \Drupal\node\NodeForm::form().
+        if ($saved_entity) {
+          $common_translation_languages = array_intersect_key($entity->getTranslationLanguages(), $saved_entity->getTranslationLanguages());
+          foreach (array_keys($common_translation_languages) as $langcode) {
+            // Merely comparing the latest changed timestamps across all
+            // translations is not sufficient since other translations may have
+            // been edited and saved in the meanwhile. Therefore, compare the
+            // changed timestamps of each entity translation individually.
+            if ($saved_entity->getTranslation($langcode)->getChangedTime() > $entity->getTranslation($langcode)->getChangedTime()) {
+              $this->context->addViolation($constraint->message);
+              break;
+            }
+          }
         }
       }
     }
diff --git a/core/modules/comment/src/CommentStatistics.php b/core/modules/comment/src/CommentStatistics.php
index b58452c594..869106adb1 100644
--- a/core/modules/comment/src/CommentStatistics.php
+++ b/core/modules/comment/src/CommentStatistics.php
@@ -124,7 +124,7 @@ public function create(FieldableEntityInterface $entity, $fields) {
       // @todo Make comment statistics language aware and add some tests. See
       //   https://www.drupal.org/node/2318875
       if ($entity instanceof EntityChangedInterface) {
-        $last_comment_timestamp = $entity->getChangedTimeAcrossTranslations();
+        $last_comment_timestamp = $entity->getLatestChangedTimeAcrossTranslations();
       }
       $query->values([
         'entity_id' => $entity->id(),
@@ -242,7 +242,7 @@ public function update(CommentInterface $comment) {
           'comment_count' => 0,
           // Use the changed date of the entity if it's set, or default to
           // REQUEST_TIME.
-          'last_comment_timestamp' => ($entity instanceof EntityChangedInterface) ? $entity->getChangedTimeAcrossTranslations() : REQUEST_TIME,
+          'last_comment_timestamp' => ($entity instanceof EntityChangedInterface) ? $entity->getLatestChangedTimeAcrossTranslations() : REQUEST_TIME,
           'last_comment_name' => '',
           'last_comment_uid' => $last_comment_uid,
         ])
diff --git a/core/modules/comment/src/Form/CommentAdminOverview.php b/core/modules/comment/src/Form/CommentAdminOverview.php
index 1ffa2c618a..9adbae88ca 100644
--- a/core/modules/comment/src/Form/CommentAdminOverview.php
+++ b/core/modules/comment/src/Form/CommentAdminOverview.php
@@ -218,7 +218,7 @@ public function buildForm(array $form, FormStateInterface $form_state, $type = '
             '#url' => $commented_entity->urlInfo(),
           ],
         ],
-        'changed' => $this->dateFormatter->format($comment->getChangedTimeAcrossTranslations(), 'short'),
+        'changed' => $this->dateFormatter->format($comment->getLatestChangedTimeAcrossTranslations(), 'short'),
       ];
       $comment_uri_options = $comment->urlInfo()->getOptions() + ['query' => $destination];
       $links = [];
diff --git a/core/modules/comment/tests/src/Functional/CommentTokenReplaceTest.php b/core/modules/comment/tests/src/Functional/CommentTokenReplaceTest.php
index a7fe6b5e07..1f1ef3cd7d 100644
--- a/core/modules/comment/tests/src/Functional/CommentTokenReplaceTest.php
+++ b/core/modules/comment/tests/src/Functional/CommentTokenReplaceTest.php
@@ -78,7 +78,7 @@ public function testCommentTokenReplacement() {
     $tests['[comment:edit-url]'] = $comment->url('edit-form', $url_options);
     $tests['[comment:created]'] = \Drupal::service('date.formatter')->format($comment->getCreatedTime(), 'medium', ['langcode' => $language_interface->getId()]);
     $tests['[comment:created:since]'] = \Drupal::service('date.formatter')->formatTimeDiffSince($comment->getCreatedTime(), ['langcode' => $language_interface->getId()]);
-    $tests['[comment:changed:since]'] = \Drupal::service('date.formatter')->formatTimeDiffSince($comment->getChangedTimeAcrossTranslations(), ['langcode' => $language_interface->getId()]);
+    $tests['[comment:changed:since]'] = \Drupal::service('date.formatter')->formatTimeDiffSince($comment->getLatestChangedTimeAcrossTranslations(), ['langcode' => $language_interface->getId()]);
     $tests['[comment:parent:cid]'] = $comment->hasParentComment() ? $comment->getParentComment()->id() : NULL;
     $tests['[comment:parent:title]'] = $parent_comment->getSubject();
     $tests['[comment:entity]'] = Html::escape($node->getTitle());
diff --git a/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php b/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php
index e7a7dfc9dd..3de2197b56 100644
--- a/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php
+++ b/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php
@@ -566,7 +566,7 @@ protected function doTestTranslationChanged() {
         $storage->resetCache([$this->entityId]);
         $entity = $storage->load($this->entityId);
         $this->assertEqual(
-          $entity->getChangedTimeAcrossTranslations(), $entity->getTranslation($langcode)->getChangedTime(),
+          $entity->getLatestChangedTimeAcrossTranslations(), $entity->getTranslation($langcode)->getChangedTime(),
           format_string('Changed time for language %language is the latest change over all languages.', ['%language' => $language->getName()])
         );
       }
diff --git a/core/modules/content_translation/tests/src/Functional/ContentTranslationUITestBase.php b/core/modules/content_translation/tests/src/Functional/ContentTranslationUITestBase.php
index c755ac4906..6cd574dad7 100644
--- a/core/modules/content_translation/tests/src/Functional/ContentTranslationUITestBase.php
+++ b/core/modules/content_translation/tests/src/Functional/ContentTranslationUITestBase.php
@@ -564,7 +564,7 @@ protected function doTestTranslationChanged() {
         $storage->resetCache([$this->entityId]);
         $entity = $storage->load($this->entityId);
         $this->assertEqual(
-          $entity->getChangedTimeAcrossTranslations(), $entity->getTranslation($langcode)->getChangedTime(),
+          $entity->getLatestChangedTimeAcrossTranslations(), $entity->getTranslation($langcode)->getChangedTime(),
           format_string('Changed time for language %language is the latest change over all languages.', ['%language' => $language->getName()])
         );
       }
diff --git a/core/tests/Drupal/KernelTests/Core/Entity/ContentEntityChangedTest.php b/core/tests/Drupal/KernelTests/Core/Entity/ContentEntityChangedTest.php
index aa0044bd8e..888bbb3da0 100644
--- a/core/tests/Drupal/KernelTests/Core/Entity/ContentEntityChangedTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Entity/ContentEntityChangedTest.php
@@ -83,7 +83,7 @@ public function testChanged() {
     );
 
     $this->assertEqual(
-      $entity->getChangedTime(), $entity->getChangedTimeAcrossTranslations(),
+      $entity->getChangedTime(), $entity->getLatestChangedTimeAcrossTranslations(),
       'Changed time of original language is the same as changed time across all translations.'
     );
 
@@ -105,7 +105,7 @@ public function testChanged() {
     );
 
     $this->assertEqual(
-      $german->getChangedTime(), $entity->getChangedTimeAcrossTranslations(),
+      $german->getChangedTime(), $entity->getLatestChangedTimeAcrossTranslations(),
       'Changed time of the German translation is the newest time across all translations.'
     );
 
@@ -162,7 +162,7 @@ public function testChanged() {
     );
 
     $this->assertEqual(
-      $entity->getChangedTime(), $entity->getChangedTimeAcrossTranslations(),
+      $entity->getChangedTime(), $entity->getLatestChangedTimeAcrossTranslations(),
       'Changed time of the original language is the newest time across all translations.'
     );
 
@@ -311,7 +311,7 @@ public function testRevisionChanged() {
     );
 
     $this->assertEqual(
-      $entity->getChangedTime(), $entity->getChangedTimeAcrossTranslations(),
+      $entity->getChangedTime(), $entity->getLatestChangedTimeAcrossTranslations(),
       'Changed time of original language is the same as changed time across all translations.'
     );
 
@@ -368,7 +368,7 @@ public function testRevisionChanged() {
     );
 
     $this->assertEqual(
-      $german->getChangedTime(), $entity->getChangedTimeAcrossTranslations(),
+      $german->getChangedTime(), $entity->getLatestChangedTimeAcrossTranslations(),
       'Changed time of the German translation is the newest time across all translations.'
     );
 
@@ -423,7 +423,7 @@ public function testRevisionChanged() {
     );
 
     $this->assertEqual(
-      $german->getChangedTime(), $entity->getChangedTimeAcrossTranslations(),
+      $german->getChangedTime(), $entity->getLatestChangedTimeAcrossTranslations(),
       'Changed time of the German translation is the newest time across all translations.'
     );
 
@@ -458,7 +458,7 @@ public function testRevisionChanged() {
     );
 
     $this->assertEqual(
-      $french->getChangedTime(), $entity->getChangedTimeAcrossTranslations(),
+      $french->getChangedTime(), $entity->getLatestChangedTimeAcrossTranslations(),
       'Changed time of the French translation is the newest time across all translations.'
     );
 
diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityValidationTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityValidationTest.php
index bf2a147e0a..3b1e63c5f5 100644
--- a/core/tests/Drupal/KernelTests/Core/Entity/EntityValidationTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityValidationTest.php
@@ -3,6 +3,7 @@
 namespace Drupal\KernelTests\Core\Entity;
 
 use Drupal\Core\Entity\Plugin\Validation\Constraint\CompositeConstraintBase;
+use Drupal\language\Entity\ConfigurableLanguage;
 
 /**
  * Tests the Entity Validation API.
@@ -16,7 +17,7 @@ class EntityValidationTest extends EntityKernelTestBase {
    *
    * @var array
    */
-  public static $modules = ['filter', 'text'];
+  public static $modules = ['filter', 'text', 'language'];
 
   /**
    * @var string
@@ -39,6 +40,10 @@ class EntityValidationTest extends EntityKernelTestBase {
   protected function setUp() {
     parent::setUp();
 
+    // Enable an additional language.
+    ConfigurableLanguage::createFromLangcode('de')
+      ->save();
+
     // Create the test field.
     module_load_install('entity_test');
     entity_test_install();
@@ -200,4 +205,49 @@ public function testCompositeConstraintValidation() {
     $this->assertEqual($constraint->coversFields(), ['name', 'type'], 'Information about covered fields can be retrieved.');
   }
 
+  /**
+   * Tests the EntityChangedConstraintValidator.
+   */
+  public function testEntityChangedConstraintOnFormConcurrentEditing() {
+    $this->installEntitySchema('entity_test_mulrev_changed');
+    $storage = \Drupal::entityTypeManager()
+      ->getStorage('entity_test_mulrev_changed');
+
+    // Create a test entity.
+    $entity = $this->createTestEntity('entity_test_mulrev_changed');
+    $entity->save();
+
+    $entity->setChangedTime($entity->getChangedTime() - 1);
+    $violations = $entity->validate();
+    $this->assertEquals(1, $violations->count());
+    $this->assertEqual($violations[0]->getMessage(), 'The content has either been modified by another user, or you have already submitted modifications. As a result, your changes cannot be saved.');
+
+    $entity = $storage->loadUnchanged($entity->id());
+    $translation = $entity->addTranslation('de');
+    $entity->save();
+
+    // Ensure that the new translation has a newer changed timestamp than the
+    // default translation.
+    $this->assertGreaterThan($entity->getChangedTime(), $translation->getChangedTime());
+
+    // Simulate concurrent form editing by saving the entity with an altered
+    // non-translatable field in order for the changed timestamp to be updated
+    // across all entity translations.
+    $original_entity_time = $entity->getChangedTime();
+    $entity->set('not_translatable', $this->randomString());
+    $entity->save();
+    // Simulate form submission of an uncached form by setting the previous
+    // timestamp of an entity translation on the saved entity object. This
+    // happens in the entity form API where we put the changed timestamp of
+    // the entity in a form hidden value and then set it on the entity which on
+    // form submit is loaded from the storage if the form is not yet cached.
+    $entity->setChangedTime($original_entity_time);
+    // Setting the changed timestamp from the user input on the entity loaded
+    // from the storage is used as a prevention from saving a form built with a
+    // previous version of the entity and thus reverting changes by other users.
+    $violations = $entity->validate();
+    $this->assertEquals(1, $violations->count());
+    $this->assertEqual($violations[0]->getMessage(), 'The content has either been modified by another user, or you have already submitted modifications. As a result, your changes cannot be saved.');
+  }
+
 }
