diff --git a/core/lib/Drupal/Core/Cache/RuntimeCacheContextsDependencyInterface.php b/core/lib/Drupal/Core/Cache/RuntimeCacheContextsDependencyInterface.php
new file mode 100644
index 0000000..1971d59
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/RuntimeCacheContextsDependencyInterface.php
@@ -0,0 +1,27 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\Core\Cache\RuntimeCacheContextsDependencyInterface.
+ */
+
+namespace Drupal\Core\Cache;
+
+/**
+ * Allows to add cache contexts to an object for the current runtime.
+ *
+ * This must be used when changing an object in a way that affects its
+ * cacheability. For example, when changing the active translation of an entity
+ * based on the current content language then a cache context for that must be
+ * added.
+ */
+interface RuntimeCacheContextsDependencyInterface {
+
+  /**
+   * Adds runtime cache context tokens.
+   *
+   * @param array $cache_contexts
+   *   A list of runtime cache tokens.
+   */
+  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..d01c1ba 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;
   }
 
   /**
@@ -449,6 +456,9 @@ public function getCacheContexts() {
   public function getCacheTags() {
     // @todo Add bundle-specific listing cache tag?
     //   https://www.drupal.org/node/2145751
+    if ($this->isNew()) {
+      return [];
+    }
     return [$this->entityTypeId . ':' . $this->id()];
   }
 
@@ -608,4 +618,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..8fca582 100644
--- a/core/lib/Drupal/Core/Entity/EntityForm.php
+++ b/core/lib/Drupal/Core/Entity/EntityForm.php
@@ -101,6 +101,12 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       $this->init($form_state);
     }
 
+    // Ensure that edit forms have the correct cacheable metadata so they can be
+    // cached.
+    if (!$this->entity->isNew()) {
+      \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..495ffaa 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\RuntimeCacheContextsDependencyInterface;
 
 /**
  * Defines a common interface for all entity objects.
  *
  * @ingroup entity_api
  */
-interface EntityInterface extends AccessibleInterface, CacheableDependencyInterface {
+interface EntityInterface extends AccessibleInterface, CacheableDependencyInterface, RuntimeCacheContextsDependencyInterface {
 
   /**
    * 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..8281eb9 100644
--- a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
+++ b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
@@ -117,14 +117,10 @@ public function view(EntityInterface $entity, $view_mode = 'full', $langcode = N
    * {@inheritdoc}
    */
   public function viewMultiple(array $entities = array(), $view_mode = 'full', $langcode = NULL) {
-    if (!isset($langcode)) {
-      $langcode = $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId();
-    }
-
     $build_list = array(
       '#sorted' => TRUE,
       '#pre_render' => array(array($this, 'buildMultiple')),
-      '#langcode' => $langcode,
+      '#langcode' => $langcode ?: $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId(),
     );
     $weight = 0;
     foreach ($entities as $key => $entity) {
@@ -133,9 +129,10 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la
       $entity = $this->entityManager->getTranslationFromContext($entity, $langcode);
 
       // Set build defaults.
-      $build_list[$key] = $this->getBuildDefaults($entity, $view_mode, $langcode);
+      $entity_langcode = $entity->language()->getId();
+      $build_list[$key] = $this->getBuildDefaults($entity, $view_mode, $entity_langcode);
       $entityType = $this->entityTypeId;
-      $this->moduleHandler()->alter(array($entityType . '_build_defaults', 'entity_build_defaults'), $build_list[$key], $entity, $view_mode, $langcode);
+      $this->moduleHandler()->alter(array($entityType . '_build_defaults', 'entity_build_defaults'), $build_list[$key], $entity, $view_mode, $entity_langcode);
 
       $build_list[$key]['#weight'] = $weight++;
     }
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..1e6d716 100644
--- a/core/modules/comment/src/Tests/CommentTranslationUITest.php
+++ b/core/modules/comment/src/Tests/CommentTranslationUITest.php
@@ -33,6 +33,20 @@ 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);
+  }
+
 }
