diff --git a/core/lib/Drupal/Core/Cache/RuntimeCacheableDependencyInterface.php b/core/lib/Drupal/Core/Cache/RuntimeCacheableDependencyInterface.php
new file mode 100644
index 0000000..6be18d6
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/RuntimeCacheableDependencyInterface.php
@@ -0,0 +1,13 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\Core\Cache\RuntimeCacheableDependencyInterface.
+ */
+
+namespace Drupal\Core\Cache;
+
+interface RuntimeCacheableDependencyInterface {
+
+  public function addRuntimeCacheContexts(array $cache_contexts);
+
+}
diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php
index 9ffa497..777d15c 100644
--- a/core/lib/Drupal/Core/Entity/Entity.php
+++ b/core/lib/Drupal/Core/Entity/Entity.php
@@ -50,6 +50,13 @@
   protected $typedData;
 
   /**
+   * Runtime cache contexts.
+   *
+   * @var array
+   */
+  protected $runtimeCacheContexts = [];
+
+  /**
    * Constructs an Entity object.
    *
    * @param array $values
@@ -440,7 +447,7 @@ public function referencedEntities() {
    * {@inheritdoc}
    */
   public function getCacheContexts() {
-    return [];
+    return $this->runtimeCacheContexts;
   }
 
   /**
@@ -608,4 +615,10 @@ public function getConfigTarget() {
     return $this->uuid();
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function addRuntimeCacheContexts(array $cache_contexts) {
+    $this->runtimeCacheContexts = Cache::mergeContexts($this->runtimeCacheContexts, $cache_contexts);
+  }
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityForm.php b/core/lib/Drupal/Core/Entity/EntityForm.php
index 6d61d0c..e216099 100644
--- a/core/lib/Drupal/Core/Entity/EntityForm.php
+++ b/core/lib/Drupal/Core/Entity/EntityForm.php
@@ -101,6 +101,10 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       $this->init($form_state);
     }
 
+    // Ensure that forms have the correct cacheable metadata so they can be
+    // cached.
+    \Drupal::service('renderer')->addCacheableDependency($form, $this->entity);
+
     // Retrieve the form array using the possibly updated entity in form state.
     $form = $this->form($form, $form_state);
 
diff --git a/core/lib/Drupal/Core/Entity/EntityInterface.php b/core/lib/Drupal/Core/Entity/EntityInterface.php
index 68dafd0..49c25a2 100644
--- a/core/lib/Drupal/Core/Entity/EntityInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityInterface.php
@@ -9,13 +9,14 @@
 
 use Drupal\Core\Access\AccessibleInterface;
 use Drupal\Core\Cache\CacheableDependencyInterface;
+use Drupal\Core\Cache\RuntimeCacheableDependencyInterface;
 
 /**
  * Defines a common interface for all entity objects.
  *
  * @ingroup entity_api
  */
-interface EntityInterface extends AccessibleInterface, CacheableDependencyInterface {
+interface EntityInterface extends AccessibleInterface, CacheableDependencyInterface, RuntimeCacheableDependencyInterface {
 
   /**
    * Gets the entity UUID (Universally Unique Identifier).
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index 1e1323e..dd76354 100644
--- a/core/lib/Drupal/Core/Entity/EntityManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -969,6 +969,7 @@ public function getTranslationFromContext(EntityInterface $entity, $langcode = N
     if ($entity instanceof TranslatableInterface && count($entity->getTranslationLanguages()) > 1) {
       if (empty($langcode)) {
         $langcode = $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId();
+        $entity->addRuntimeCacheContexts(['languages:' . LanguageInterface::TYPE_CONTENT]);
       }
 
       // Retrieve language fallback candidates to perform the entity language
diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
index 09736d2..c0a0443 100644
--- a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
+++ b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
@@ -119,6 +119,11 @@ public function view(EntityInterface $entity, $view_mode = 'full', $langcode = N
   public function viewMultiple(array $entities = array(), $view_mode = 'full', $langcode = NULL) {
     if (!isset($langcode)) {
       $langcode = $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId();
+      foreach ($entities as $entity) {
+        if ($entity instanceof TranslatableInterface && count($entity->getTranslationLanguages()) > 1) {
+          $entity->addRuntimeCacheContexts(['languages:' . LanguageInterface::TYPE_CONTENT]);
+        }
+      }
     }
 
     $build_list = array(
diff --git a/core/modules/comment/src/CommentViewBuilder.php b/core/modules/comment/src/CommentViewBuilder.php
index 6923220..d9ae407 100644
--- a/core/modules/comment/src/CommentViewBuilder.php
+++ b/core/modules/comment/src/CommentViewBuilder.php
@@ -71,11 +71,9 @@ protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langco
       ->getFieldDefinition($entity->getFieldName())
       ->getSetting('default_mode') === CommentManagerInterface::COMMENT_MODE_THREADED;
     // If threading is enabled, don't render cache individual comments, but do
-    // keep the cache tags, so they can bubble up.
+    // keep the cacheable metadata, so it can bubble up.
     if ($build['#comment_threaded']) {
-      $cache_tags = $build['#cache']['tags'];
-      $build['#cache'] = [];
-      $build['#cache']['tags'] = $cache_tags;
+      unset($build['#cache']['keys']);
     }
 
     return $build;
diff --git a/core/modules/comment/src/Tests/CommentTranslationUITest.php b/core/modules/comment/src/Tests/CommentTranslationUITest.php
index cfb63c8..7edf1af 100644
--- a/core/modules/comment/src/Tests/CommentTranslationUITest.php
+++ b/core/modules/comment/src/Tests/CommentTranslationUITest.php
@@ -33,6 +33,13 @@ class CommentTranslationUITest extends ContentTranslationUITestBase {
   protected $adminUser;
 
   /**
+   * Default cache contexts expected on a non-translated entity.
+   *
+   * @var array
+   */
+  protected $defaultCacheContexts = ['languages:language_interface', 'theme' , 'user.permissions', 'timezone', 'url.query_args.pagers:0', 'user.roles'];
+
+  /**
    * Modules to install.
    *
    * @var array
diff --git a/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php b/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php
index e524359..a9c87ec 100644
--- a/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php
+++ b/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php
@@ -7,18 +7,22 @@
 
 namespace Drupal\content_translation\Tests;
 
+use Drupal\Core\Cache\Cache;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Language\Language;
 use Drupal\Core\Language\LanguageInterface;
 use Drupal\Core\Url;
 use Drupal\language\Entity\ConfigurableLanguage;
 use Drupal\Component\Utility\SafeMarkup;
+use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
 
 /**
  * Tests the Content Translation UI.
  */
 abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
 
+  use AssertPageCacheContextsAndTagsTrait;
+
   /**
    * The id of the entity being translated.
    *
@@ -34,6 +38,15 @@
   protected $testLanguageSelector = TRUE;
 
   /**
+   * Default cache contexts expected on a non-translated entity.
+   *
+   * Cache contexts will not be checked if this list is empty.
+   *
+   * @var array
+   */
+  protected $defaultCacheContexts = ['languages:language_interface', 'theme' , 'user.permissions'];
+
+  /**
    * Tests the basic translation UI.
    */
   function testTranslationUI() {
@@ -64,6 +77,11 @@ protected function doTestBasicTranslation() {
     $this->assertTrue($entity, 'Entity found in the database.');
     $this->drupalGet($entity->urlInfo());
     $this->assertResponse(200, 'Entity URL is valid.');
+
+    // Ensure that the content language cache context is not yet added to the
+    // page.
+    $this->assertCacheContexts($this->defaultCacheContexts);
+
     $this->drupalGet($entity->urlInfo('drupal:content-translation-overview'));
     $this->assertNoText('Source language', 'Source language column correctly hidden.');
 
@@ -87,9 +105,14 @@ protected function doTestBasicTranslation() {
     ], array('language' => $language));
     $this->drupalPostForm($add_url, $this->getEditValues($values, $langcode), $this->getFormSubmitActionForNewTranslation($entity, $langcode));
 
-    // Get the entity and reset its cache, so that the new translation gets the
+    // Ensure that the content language cache context is not yet added to the
+    // page.
+    $entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
+    $this->drupalGet($entity->urlInfo());
+    $this->assertCacheContexts(Cache::mergeContexts(['languages:language_content'], $this->defaultCacheContexts));
+
+    // Reset the cache of the entity, so that the new translation gets the
     // updated values.
-    $entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
     $metadata_source_translation = $this->manager->getTranslationMetadata($entity->getTranslation($default_langcode));
     $metadata_target_translation = $this->manager->getTranslationMetadata($entity->getTranslation($langcode));
 
diff --git a/core/modules/menu_link_content/src/Tests/MenuLinkContentTranslationUITest.php b/core/modules/menu_link_content/src/Tests/MenuLinkContentTranslationUITest.php
index b068808..faab9e6 100644
--- a/core/modules/menu_link_content/src/Tests/MenuLinkContentTranslationUITest.php
+++ b/core/modules/menu_link_content/src/Tests/MenuLinkContentTranslationUITest.php
@@ -18,6 +18,13 @@
 class MenuLinkContentTranslationUITest extends ContentTranslationUITestBase {
 
   /**
+   * Default cache contexts expected on a non-translated entity.
+   *
+   * @var array
+   */
+  protected $defaultCacheContexts = ['languages:language_interface', 'theme' , 'user.permissions', 'user.roles:authenticated'];
+
+  /**
    * Modules to enable.
    *
    * @var array
diff --git a/core/modules/node/src/Tests/NodeTranslationUITest.php b/core/modules/node/src/Tests/NodeTranslationUITest.php
index 287920b..5476ed9 100644
--- a/core/modules/node/src/Tests/NodeTranslationUITest.php
+++ b/core/modules/node/src/Tests/NodeTranslationUITest.php
@@ -22,6 +22,23 @@
 class NodeTranslationUITest extends ContentTranslationUITestBase {
 
   /**
+   * Default cache contexts expected on a non-translated entity.
+   *
+   * @var array
+   */
+  protected $defaultCacheContexts = [
+    'languages:language_interface',
+    'theme',
+    'user.permissions',
+    'route.menu_active_trails:account',
+    'route.menu_active_trails:footer',
+    'route.menu_active_trails:main',
+    'route.menu_active_trails:tools',
+    'timezone',
+    'user.roles'
+  ];
+
+  /**
    * Modules to enable.
    *
    * @var array
diff --git a/core/modules/shortcut/src/Tests/ShortcutTranslationUITest.php b/core/modules/shortcut/src/Tests/ShortcutTranslationUITest.php
index 3fc8232..007baa4 100644
--- a/core/modules/shortcut/src/Tests/ShortcutTranslationUITest.php
+++ b/core/modules/shortcut/src/Tests/ShortcutTranslationUITest.php
@@ -19,6 +19,13 @@
 class ShortcutTranslationUITest extends ContentTranslationUITestBase {
 
   /**
+   * Default cache contexts expected on a non-translated entity.
+   *
+   * @var array
+   */
+  protected $defaultCacheContexts = ['languages:language_interface', 'theme' , 'user'];
+
+  /**
    * Modules to enable.
    *
    * @var array
diff --git a/core/modules/views_ui/src/ViewUI.php b/core/modules/views_ui/src/ViewUI.php
index 05451be..b738d94 100644
--- a/core/modules/views_ui/src/ViewUI.php
+++ b/core/modules/views_ui/src/ViewUI.php
@@ -1336,4 +1336,11 @@ public function hasTrustedData() {
     return $this->storage->hasTrustedData();
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function addRuntimeCacheContexts(array $cache_contexts) {
+    $this->storage->addRuntimeCacheContexts($cache_contexts);
+  }
+
 }
