diff --git a/core/core.services.yml b/core/core.services.yml
index c749b8e5639d099cda5f0cfaa76259a25500913a..a0fcf9450e6a9d0ec5c2a530b03242dccff7da42 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -1,6 +1,8 @@
 parameters:
   session.storage.options: {}
   twig.config: {}
+  renderer.config:
+    required_cache_contexts: ['languages:language_interface', 'theme']
   factory.keyvalue:
     default: keyvalue.database
   factory.keyvalue.expirable:
@@ -1333,6 +1335,6 @@ services:
     lazy: true
   renderer:
     class: Drupal\Core\Render\Renderer
-    arguments: ['@controller_resolver', '@theme.manager', '@plugin.manager.element_info', '@request_stack', '@cache_factory', '@cache_contexts']
+    arguments: ['@controller_resolver', '@theme.manager', '@plugin.manager.element_info', '@request_stack', '@cache_factory', '@cache_contexts', '%renderer.config%']
   email.validator:
     class: Egulias\EmailValidator\EmailValidator
diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
index 071de3ec5c187e0ccd65b79344f77eab6740a75c..d12d028d6de60d813041eb169f259a7976100e3d 100644
--- a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
+++ b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
@@ -170,7 +170,6 @@ protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langco
       '#cache' => array(
         'tags' => Cache::mergeTags($this->getCacheTags(), $entity->getCacheTags()),
         'contexts' => [
-          'theme',
           'user.roles',
         ],
       ),
diff --git a/core/lib/Drupal/Core/Render/Renderer.php b/core/lib/Drupal/Core/Render/Renderer.php
index b1d300e2e6c0d24d67a1bb52cca70a5b9b6de541..9205f6018a63c1012a1b3c23a1e0d7f30c73331b 100644
--- a/core/lib/Drupal/Core/Render/Renderer.php
+++ b/core/lib/Drupal/Core/Render/Renderer.php
@@ -64,6 +64,13 @@ class Renderer implements RendererInterface {
    */
   protected $cacheContexts;
 
+  /**
+   * The renderer configuration array.
+   *
+   * @var array
+   */
+  protected $rendererConfig;
+
   /**
    * The stack containing bubbleable rendering metadata.
    *
@@ -86,14 +93,17 @@ class Renderer implements RendererInterface {
    *   The cache factory.
    * @param \Drupal\Core\Cache\CacheContexts $cache_contexts
    *   The cache contexts service.
+   * @param array $renderer_config
+   *   The renderer configuration array.
    */
-  public function __construct(ControllerResolverInterface $controller_resolver, ThemeManagerInterface $theme, ElementInfoManagerInterface $element_info, RequestStack $request_stack, CacheFactoryInterface $cache_factory, CacheContexts $cache_contexts) {
+  public function __construct(ControllerResolverInterface $controller_resolver, ThemeManagerInterface $theme, ElementInfoManagerInterface $element_info, RequestStack $request_stack, CacheFactoryInterface $cache_factory, CacheContexts $cache_contexts, array $renderer_config) {
     $this->controllerResolver = $controller_resolver;
     $this->theme = $theme;
     $this->elementInfo = $element_info;
     $this->requestStack = $request_stack;
     $this->cacheFactory = $cache_factory;
     $this->cacheContexts = $cache_contexts;
+    $this->rendererConfig = $renderer_config;
   }
 
   /**
@@ -164,10 +174,25 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
     }
     static::$stack->push(new BubbleableMetadata());
 
+    // Set the bubbleable rendering metadata that has configurable defaults, if:
+    // - this is the root call, to ensure that the final render array definitely
+    //   has these configurable defaults, even when no subtree is render cached.
+    // - this is a render cacheable subtree, to ensure that the cached data has
+    //   the configurable defaults (which may affect the ID and invalidation).
+    if ($is_root_call || isset($elements['#cache']['keys'])) {
+      $required_cache_contexts = $this->rendererConfig['required_cache_contexts'];
+      if (isset($elements['#cache']['contexts'])) {
+        $elements['#cache']['contexts'] = Cache::mergeContexts($elements['#cache']['contexts'], $required_cache_contexts);
+      }
+      else {
+        $elements['#cache']['contexts'] = $required_cache_contexts;
+      }
+    }
+
     // Try to fetch the prerendered element from cache, run any
     // #post_render_cache callbacks and return the final markup.
     $pre_bubbling_cid = NULL;
-    if (isset($elements['#cache'])) {
+    if (isset($elements['#cache']['keys'])) {
       $cached_element = $this->cacheGet($elements);
       if ($cached_element !== FALSE) {
         $elements = $cached_element;
@@ -216,7 +241,6 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
     }
 
     // Defaults for bubbleable rendering metadata.
-    $elements['#cache']['contexts'] = isset($elements['#cache']['contexts']) ? $elements['#cache']['contexts'] : array();
     $elements['#cache']['tags'] = isset($elements['#cache']['tags']) ? $elements['#cache']['tags'] : array();
     $elements['#cache']['max-age'] = isset($elements['#cache']['max-age']) ? $elements['#cache']['max-age'] : Cache::PERMANENT;
     $elements['#attached'] = isset($elements['#attached']) ? $elements['#attached'] : array();
diff --git a/core/modules/block/src/BlockViewBuilder.php b/core/modules/block/src/BlockViewBuilder.php
index c2a0ba8960e6f5aa23f099e8f4f6e8dfc1208ac7..9d5c7a88e1d2794a487a71800dc745139e46e54b 100644
--- a/core/modules/block/src/BlockViewBuilder.php
+++ b/core/modules/block/src/BlockViewBuilder.php
@@ -36,12 +36,6 @@ public function view(EntityInterface $entity, $view_mode = 'full', $langcode = N
    * {@inheritdoc}
    */
   public function viewMultiple(array $entities = array(), $view_mode = 'full', $langcode = NULL) {
-    // @todo Remove when https://www.drupal.org/node/2453059 lands.
-    $default_cache_contexts = [
-      'languages',
-      'theme',
-    ];
-
     /** @var \Drupal\block\BlockInterface[] $entities */
     $build = array();
     foreach ($entities as  $entity) {
@@ -70,7 +64,7 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la
         '#derivative_plugin_id' => $derivative_id,
         '#id' => $entity->id(),
         '#cache' => [
-          'contexts' => Cache::mergeContexts($default_cache_contexts, $plugin->getCacheContexts()),
+          'contexts' => $plugin->getCacheContexts(),
           'tags' => Cache::mergeTags(
             $this->getCacheTags(), // Block view builder cache tag.
             $entity->getCacheTags(), // Block entity cache tag.
diff --git a/core/modules/block_content/src/Tests/BlockContentCacheTagsTest.php b/core/modules/block_content/src/Tests/BlockContentCacheTagsTest.php
index 5629039bafb827ed55e338d78770e023f0d2c689..8fb80d5da017219e5bb83e93b4b2a8b620bd7644 100644
--- a/core/modules/block_content/src/Tests/BlockContentCacheTagsTest.php
+++ b/core/modules/block_content/src/Tests/BlockContentCacheTagsTest.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Language\LanguageInterface;
 use Drupal\field\Entity\FieldStorageConfig;
 use Drupal\system\Tests\Entity\EntityCacheTagsTestBase;
 use Symfony\Component\HttpFoundation\Request;
@@ -80,7 +81,7 @@ public function testBlock() {
     // Expected keys, contexts, and tags for the block.
     // @see \Drupal\block\BlockViewBuilder::viewMultiple()
     $expected_block_cache_keys = ['entity_view', 'block', $block->id()];
-    $expected_block_cache_contexts = ['languages', 'theme'];
+    $expected_block_cache_contexts = ['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme'];
     $expected_block_cache_tags = Cache::mergeTags(['block_view', 'rendered'], $block->getCacheTags(), $block->getPlugin()->getCacheTags());
 
     // Expected contexts and tags for the BlockContent entity.
diff --git a/core/modules/filter/src/Tests/FilterAPITest.php b/core/modules/filter/src/Tests/FilterAPITest.php
index a1444c9e5c54c54845e8c307f094e73beac85696..49b99d2d20fee81ce75f8cfac7d3b8172c4a2d2b 100644
--- a/core/modules/filter/src/Tests/FilterAPITest.php
+++ b/core/modules/filter/src/Tests/FilterAPITest.php
@@ -261,6 +261,9 @@ function testProcessedTextElement() {
     $expected_cache_contexts = [
       // The cache context set by the filter_test_cache_contexts filter.
       'languages:' . LanguageInterface::TYPE_CONTENT,
+      // The default cache contexts for Renderer.
+      'languages:' . LanguageInterface::TYPE_INTERFACE,
+      'theme',
     ];
     $this->assertEqual($expected_cache_contexts, $build['#cache']['contexts'], 'Expected cache contexts present.');
     $expected_markup = '<p>Hello, world!</p><p>This is a dynamic llama.</p>';
diff --git a/core/modules/node/src/Tests/NodeListBuilderTest.php b/core/modules/node/src/Tests/NodeListBuilderTest.php
index ec4b1ea89f8de9da77f4b290b6dc4f7534a4829d..798a0566dcf1a764695175cceaf463c3a50e8eb5 100644
--- a/core/modules/node/src/Tests/NodeListBuilderTest.php
+++ b/core/modules/node/src/Tests/NodeListBuilderTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\node\Tests;
 
+use Drupal\Core\Language\LanguageInterface;
 use Drupal\simpletest\KernelTestBase;
 
 /**
@@ -36,9 +37,9 @@ public function testCacheContexts() {
     $list_builder = $this->container->get('entity.manager')->getListBuilder('node');
 
     $build = $list_builder->render();
-    $this->container->get('renderer')->render($build);
+    $this->container->get('renderer')->renderRoot($build);
 
-    $this->assertEqual(['url.query_args.pagers:0', 'user.node_grants:view'], $build['#cache']['contexts']);
+    $this->assertEqual(['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'url.query_args.pagers:0', 'user.node_grants:view'], $build['#cache']['contexts']);
   }
 
 }
diff --git a/core/modules/node/src/Tests/Views/FrontPageTest.php b/core/modules/node/src/Tests/Views/FrontPageTest.php
index ed6fd3ddcde9cb41409e765b722d6f9a1cc9b895..b8a6910c4c2dbb72a946870bf31c3132bed0e417 100644
--- a/core/modules/node/src/Tests/Views/FrontPageTest.php
+++ b/core/modules/node/src/Tests/Views/FrontPageTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\node\Tests\Views;
 
 use Drupal\Core\Cache\Cache;
+use Drupal\Core\Language\LanguageInterface;
 use Drupal\Core\Url;
 use Drupal\node\Entity\Node;
 use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
@@ -241,7 +242,13 @@ protected function assertFrontPageViewCacheTags($do_assert_views_caches) {
     $view = Views::getView('frontpage');
     $view->setDisplay('page_1');
 
-    $cache_contexts = ['user.node_grants:view', 'languages'];
+    $cache_contexts = [
+      // Cache contexts associated with the view.
+      'user.node_grants:view',
+      'languages:' . LanguageInterface::TYPE_INTERFACE,
+      // Default cache contexts of the renderer.
+      'theme',
+    ];
 
     // Test before there are any nodes.
     $empty_node_listing_cache_tags = [
@@ -280,7 +287,6 @@ protected function assertFrontPageViewCacheTags($do_assert_views_caches) {
       $node->save();
     }
     $cache_contexts = Cache::mergeContexts($cache_contexts, [
-      'theme',
       'timezone',
       'user.roles'
     ]);
diff --git a/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php b/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php
index 8078913da528afb6d2c052513882bf96c861efc5..1fbfcfd61b67e36537ab9215e92ecb0ba343f6c2 100644
--- a/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php
+++ b/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\system\Tests\Cache;
 
+use Drupal\Core\Language\LanguageInterface;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -68,7 +69,7 @@ function testPageCacheTags() {
     ));
 
     $cache_contexts = [
-      'languages',
+      'languages:' . LanguageInterface::TYPE_INTERFACE,
       'route.menu_active_trails:account',
       'route.menu_active_trails:footer',
       'route.menu_active_trails:main',
diff --git a/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php b/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php
index 3b382e134d392be6aabe066f50eb4fb5ffe599d2..df0b6d76a3db1ad1fc3c2d023fb462df2bef1de5 100644
--- a/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php
+++ b/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php
@@ -10,6 +10,7 @@
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\Language\LanguageInterface;
 use Drupal\Core\Url;
 use Drupal\field\Entity\FieldStorageConfig;
 use Drupal\field\Entity\FieldConfig;
@@ -315,7 +316,8 @@ public function testReferencedEntity() {
     $nonempty_entity_listing_url = Url::fromRoute('entity.entity_test.collection_labels_alphabetically', ['entity_type_id' => $entity_type]);
 
     // The default cache contexts for rendered entities.
-    $entity_cache_contexts = ['theme', 'user.roles'];
+    $default_cache_contexts = ['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme'];
+    $entity_cache_contexts = Cache::mergeContexts($default_cache_contexts, ['user.roles']);
 
     // Cache tags present on every rendered page.
     $page_cache_tags = Cache::mergeTags(
@@ -395,7 +397,7 @@ public function testReferencedEntity() {
     $this->verifyPageCache($empty_entity_listing_url, 'HIT', $empty_entity_listing_cache_tags);
     // Verify the entity type's list cache contexts are present.
     $contexts_in_header = $this->drupalGetHeader('X-Drupal-Cache-Contexts');
-    $this->assertEqual($this->getAdditionalCacheContextsForEntityListing(), empty($contexts_in_header) ? [] : explode(' ', $contexts_in_header));
+    $this->assertEqual(Cache::mergeContexts($default_cache_contexts, $this->getAdditionalCacheContextsForEntityListing()), empty($contexts_in_header) ? [] : explode(' ', $contexts_in_header));
 
 
     $this->pass("Test listing containing referenced entity.", 'Debug');
@@ -405,7 +407,7 @@ public function testReferencedEntity() {
     $this->verifyPageCache($nonempty_entity_listing_url, 'HIT', $nonempty_entity_listing_cache_tags);
     // Verify the entity type's list cache contexts are present.
     $contexts_in_header = $this->drupalGetHeader('X-Drupal-Cache-Contexts');
-    $this->assertEqual($this->getAdditionalCacheContextsForEntityListing(), empty($contexts_in_header) ? [] : explode(' ', $contexts_in_header));
+    $this->assertEqual(Cache::mergeContexts($default_cache_contexts, $this->getAdditionalCacheContextsForEntityListing()), empty($contexts_in_header) ? [] : explode(' ', $contexts_in_header));
 
 
     // Verify that after modifying the referenced entity, there is a cache miss
diff --git a/core/modules/system/src/Tests/Entity/EntityListBuilderTest.php b/core/modules/system/src/Tests/Entity/EntityListBuilderTest.php
index fa5097a8bac4aa32fed142e75f4163680cd667e0..10becbf5c85524bc7e3d27b6527a0eaea3a7ee6d 100644
--- a/core/modules/system/src/Tests/Entity/EntityListBuilderTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityListBuilderTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\system\Tests\Entity;
 
+use Drupal\Core\Language\LanguageInterface;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -64,9 +65,9 @@ public function testCacheContexts() {
     $list_builder = $this->container->get('entity.manager')->getListBuilder('entity_test');
 
     $build = $list_builder->render();
-    $this->container->get('renderer')->render($build);
+    $this->container->get('renderer')->renderRoot($build);
 
-    $this->assertEqual(['entity_test_view_grants', 'url.query_args.pagers:0'], $build['#cache']['contexts']);
+    $this->assertEqual(['entity_test_view_grants', 'languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'url.query_args.pagers:0'], $build['#cache']['contexts']);
   }
 
 }
diff --git a/core/modules/system/src/Tests/Entity/EntityViewBuilderTest.php b/core/modules/system/src/Tests/Entity/EntityViewBuilderTest.php
index 15881508d5ea12a5f315bf1526c15833c018d858..6e87d2dff28804d71f2aa0edc5b16a0b9c0f0bd7 100644
--- a/core/modules/system/src/Tests/Entity/EntityViewBuilderTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityViewBuilderTest.php
@@ -7,7 +7,9 @@
 
 namespace Drupal\system\Tests\Entity;
 
+use Drupal\Core\Language\LanguageInterface;
 use Drupal\entity_reference\Tests\EntityReferenceTestTrait;
+use Drupal\Core\Cache\Cache;
 use Drupal\user\Entity\Role;
 use Drupal\user\RoleInterface;
 
@@ -61,7 +63,7 @@ public function testEntityViewBuilderCache() {
     // Get a fully built entity view render array.
     $entity_test->save();
     $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full');
-    $cid_parts = array_merge($build['#cache']['keys'], $cache_contexts->convertTokensToKeys($build['#cache']['contexts']));
+    $cid_parts = array_merge($build['#cache']['keys'], $cache_contexts->convertTokensToKeys(Cache::mergeContexts($build['#cache']['contexts'], ['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme'])));
     $cid = implode(':', $cid_parts);
     $bin = $build['#cache']['bin'];
 
@@ -111,7 +113,7 @@ public function testEntityViewBuilderCacheWithReferences() {
 
     // Get a fully built entity view render array for the referenced entity.
     $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test_reference, 'full');
-    $cid_parts = array_merge($build['#cache']['keys'], $cache_contexts->convertTokensToKeys($build['#cache']['contexts']));
+    $cid_parts = array_merge($build['#cache']['keys'], $cache_contexts->convertTokensToKeys(Cache::mergeContexts($build['#cache']['contexts'], ['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme'])));
     $cid_reference = implode(':', $cid_parts);
     $bin_reference = $build['#cache']['bin'];
 
@@ -130,7 +132,7 @@ public function testEntityViewBuilderCacheWithReferences() {
 
     // Get a fully built entity view render array.
     $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full');
-    $cid_parts = array_merge($build['#cache']['keys'], $cache_contexts->convertTokensToKeys($build['#cache']['contexts']));
+    $cid_parts = array_merge($build['#cache']['keys'], $cache_contexts->convertTokensToKeys(Cache::mergeContexts($build['#cache']['contexts'], ['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme'])));
     $cid = implode(':', $cid_parts);
     $bin = $build['#cache']['bin'];
 
diff --git a/core/modules/system/src/Tests/Entity/EntityWithUriCacheTagsTestBase.php b/core/modules/system/src/Tests/Entity/EntityWithUriCacheTagsTestBase.php
index c72d7d0bfc82a064c1483553a2555186cff592eb..299f811127756588c3feb55aed277970e7a316eb 100644
--- a/core/modules/system/src/Tests/Entity/EntityWithUriCacheTagsTestBase.php
+++ b/core/modules/system/src/Tests/Entity/EntityWithUriCacheTagsTestBase.php
@@ -8,6 +8,7 @@
 namespace Drupal\system\Tests\Entity;
 
 use Drupal\Core\Cache\Cache;
+use Drupal\Core\Language\LanguageInterface;
 use Drupal\field\Entity\FieldStorageConfig;
 use Drupal\field\Entity\FieldConfig;
 
@@ -31,7 +32,7 @@ public function testEntityUri() {
     $view_mode = $this->selectViewMode($entity_type);
 
     // The default cache contexts for rendered entities.
-    $entity_cache_contexts = ['theme', 'user.roles'];
+    $entity_cache_contexts = ['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.roles'];
 
     // Generate the standardized entity cache tags.
     $cache_tag = $this->entity->getCacheTags();
diff --git a/core/modules/views/src/Entity/View.php b/core/modules/views/src/Entity/View.php
index 24d1ec387100075da1539f5cea6bc87e1fd23e13..3a21a1379b4d5cdae2207f8af54134661fb31e65 100644
--- a/core/modules/views/src/Entity/View.php
+++ b/core/modules/views/src/Entity/View.php
@@ -11,6 +11,7 @@
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Language\LanguageInterface;
 use Drupal\views\Views;
 use Drupal\views\ViewEntityInterface;
 
@@ -316,9 +317,9 @@ protected function addCacheMetadata() {
       $executable->setDisplay($display_id);
 
       list($display['cache_metadata']['cacheable'], $display['cache_metadata']['contexts']) = $executable->getDisplay()->calculateCacheMetadata();
-      // Always include at least the 'languages' context as there will most
+      // Always include at least the 'languages:' context as there will most
       // probably be translatable strings in the view output.
-      $display['cache_metadata']['contexts'] = Cache::mergeContexts($display['cache_metadata']['contexts'], ['languages']);
+      $display['cache_metadata']['contexts'] = Cache::mergeContexts($display['cache_metadata']['contexts'], ['languages:' . LanguageInterface::TYPE_INTERFACE]);
     }
     // Restore the previous active display.
     $executable->setDisplay($current_display);
diff --git a/core/modules/views/src/Tests/GlossaryTest.php b/core/modules/views/src/Tests/GlossaryTest.php
index 954f7edfc3462a20209f45c5ba9387195943fd29..91ccead78e9c9adcc4bbfdbfe470b254a56c9727 100644
--- a/core/modules/views/src/Tests/GlossaryTest.php
+++ b/core/modules/views/src/Tests/GlossaryTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\views\Tests;
 
 use Drupal\Component\Utility\Unicode;
+use Drupal\Core\Language\LanguageInterface;
 use Drupal\Core\Url;
 use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
 use Drupal\views\Views;
@@ -87,7 +88,7 @@ public function testGlossaryView() {
 
     // Verify cache tags.
     $this->enablePageCaching();
-    $this->assertPageCacheContextsAndTags(Url::fromRoute('view.glossary.page_1'), ['languages', 'url', 'user.node_grants:view'], [
+    $this->assertPageCacheContextsAndTags(Url::fromRoute('view.glossary.page_1'), ['languages', 'theme', 'url', 'user.node_grants:view'], [
       'config:views.view.glossary',
       'node:' . $nodes_by_char['a'][0]->id(), 'node:' . $nodes_by_char['a'][1]->id(), 'node:' . $nodes_by_char['a'][2]->id(),
       'node_list',
diff --git a/core/modules/views/src/Tests/RenderCacheIntegrationTest.php b/core/modules/views/src/Tests/RenderCacheIntegrationTest.php
index ad2621a78d5469c79d3eb2ef1906fe93e3a1209d..4eda894d05c683c0dc798c734c7c412f7f40e6ff 100644
--- a/core/modules/views/src/Tests/RenderCacheIntegrationTest.php
+++ b/core/modules/views/src/Tests/RenderCacheIntegrationTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\views\Tests;
 
 use Drupal\Core\Cache\Cache;
+use Drupal\Core\Language\LanguageInterface;
 use Drupal\entity_test\Entity\EntityTest;
 use Drupal\views\Views;
 use Drupal\views\Entity\View;
@@ -221,7 +222,7 @@ public function testViewAddCacheMetadata() {
     $view = View::load('test_display');
     $view->save();
 
-    $this->assertEqual(['languages', 'user.node_grants:view'], $view->getDisplay('default')['cache_metadata']['contexts']);
+    $this->assertEqual(['languages', 'languages:' . LanguageInterface::TYPE_INTERFACE, 'user.node_grants:view'], $view->getDisplay('default')['cache_metadata']['contexts']);
   }
 
 }
diff --git a/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php b/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php
index efe5ef585c85aa9ec461965086fcdce09450db53..7e5abf420c2b20271ac4d01471651a4e4a1d0641 100644
--- a/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php
+++ b/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\KeyValueStore\KeyValueMemoryFactory;
 use Drupal\Core\Render\Element;
+use Drupal\Core\Render\Renderer;
 use Drupal\Core\State\State;
 use Drupal\Core\Cache\Cache;
 
@@ -22,6 +23,10 @@ class RendererBubblingTest extends RendererTestBase {
    * {@inheritdoc}
    */
   protected function setUp() {
+    // Disable the required cache contexts, so that this test can test just the
+    // bubbling behavior.
+    $this->rendererConfig['required_cache_contexts'] = [];
+
     parent::setUp();
 
     $this->setUpRequest();
@@ -249,21 +254,6 @@ public function testConditionalCacheContextBubblingSelfHealing() {
 
     $this->setUpRequest();
     $this->setupMemoryCache();
-    $this->cacheContexts->expects($this->any())
-      ->method('convertTokensToKeys')
-      ->willReturnCallback(function($context_tokens) {
-        global $current_user_role;
-        $keys = [];
-        foreach ($context_tokens as $context_id) {
-          if ($context_id === 'user.roles') {
-            $keys[] = 'r.' . $current_user_role;
-          }
-          else {
-            $keys[] = $context_id;
-          }
-        }
-        return $keys;
-      });
 
     $test_element = [
       '#cache' => [
diff --git a/core/tests/Drupal/Tests/Core/Render/RendererPostRenderCacheTest.php b/core/tests/Drupal/Tests/Core/Render/RendererPostRenderCacheTest.php
index c76554da8bcb68036a47962f2f6c3ec82358c609..9cddf0740754a1923afab81958f7a6e8a13ab601 100644
--- a/core/tests/Drupal/Tests/Core/Render/RendererPostRenderCacheTest.php
+++ b/core/tests/Drupal/Tests/Core/Render/RendererPostRenderCacheTest.php
@@ -17,6 +17,17 @@
  */
 class RendererPostRenderCacheTest extends RendererTestBase {
 
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    // Disable the required cache contexts, so that this test can test just the
+    // #post_render_cache behavior.
+    $this->rendererConfig['required_cache_contexts'] = [];
+
+    parent::setUp();
+  }
+
   /**
    * Generates an element with a #post_render_cache callback.
    *
diff --git a/core/tests/Drupal/Tests/Core/Render/RendererTest.php b/core/tests/Drupal/Tests/Core/Render/RendererTest.php
index 36cc3bc9a0b3e582270797dadf2677ab2d840e54..7ac67b1b4b90d5a36f3750dde8f74a161b85edbb 100644
--- a/core/tests/Drupal/Tests/Core/Render/RendererTest.php
+++ b/core/tests/Drupal/Tests/Core/Render/RendererTest.php
@@ -19,7 +19,10 @@ class RendererTest extends RendererTestBase {
 
   protected $defaultThemeVars = [
     '#cache' => [
-      'contexts' => [],
+      'contexts' => [
+        'languages:language_interface',
+        'theme',
+      ],
       'tags' => [],
       'max-age' => Cache::PERMANENT,
     ],
@@ -499,7 +502,7 @@ public function testRenderWithoutThemeArguments() {
       ->willReturn('foobar');
 
     // Test that defaults work.
-    $this->assertEquals($this->renderer->render($element), 'foobar', 'Defaults work');
+    $this->assertEquals($this->renderer->renderRoot($element), 'foobar', 'Defaults work');
   }
 
   /**
@@ -521,7 +524,7 @@ public function testRenderWithThemeArguments() {
       });
 
     // Tests that passing arguments to the theme function works.
-    $this->assertEquals($this->renderer->render($element), $element['#foo'] . $element['#bar'], 'Passing arguments to theme functions works');
+    $this->assertEquals($this->renderer->renderRoot($element), $element['#foo'] . $element['#bar'], 'Passing arguments to theme functions works');
   }
 
   /**
@@ -573,7 +576,7 @@ public function testRenderCache() {
     $this->assertEquals($expected_tags, $element['#cache']['tags'], 'Cache tags were collected from the element and its subchild.');
 
     // The cache item also has a 'rendered' cache tag.
-    $cache_item = $this->cacheFactory->get('render')->get('render_cache_test');
+    $cache_item = $this->cacheFactory->get('render')->get('render_cache_test:en:stark');
     $this->assertSame(Cache::mergeTags($expected_tags, ['rendered']), $cache_item->tags);
   }
 
@@ -599,7 +602,7 @@ public function testRenderCacheMaxAge($max_age, $is_render_cached, $render_cache
     ];
     $this->renderer->render($element);
 
-    $cache_item = $this->cacheFactory->get('render')->get('render_cache_test');
+    $cache_item = $this->cacheFactory->get('render')->get('render_cache_test:en:stark');
     if (!$is_render_cached) {
       $this->assertFalse($cache_item);
     }
diff --git a/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php b/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php
index 15a3b052f85a1bec8e4ebdd9997fa33f7415debc..28aec89c7d5b5a942a165d53b4057ac965ff6ff6 100644
--- a/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php
+++ b/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Cache\MemoryBackend;
+use Drupal\Core\Language\LanguageInterface;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Render\Renderer;
 use Drupal\Tests\UnitTestCase;
@@ -69,6 +70,18 @@ class RendererTestBase extends UnitTestCase {
    */
   protected $memoryCache;
 
+  /**
+   * The mocked renderer configuration.
+   *
+   * @var array
+   */
+  protected $rendererConfig = [
+    'required_cache_contexts' => [
+      'languages:language_interface',
+      'theme',
+    ],
+  ];
+
   /**
    * {@inheritdoc}
    */
@@ -83,7 +96,29 @@ protected function setUp() {
     $this->cacheContexts = $this->getMockBuilder('Drupal\Core\Cache\CacheContexts')
       ->disableOriginalConstructor()
       ->getMock();
-    $this->renderer = new Renderer($this->controllerResolver, $this->themeManager, $this->elementInfo, $this->requestStack, $this->cacheFactory, $this->cacheContexts);
+    $this->cacheContexts->expects($this->any())
+      ->method('convertTokensToKeys')
+      ->willReturnCallback(function($context_tokens) {
+        global $current_user_role;
+        $keys = [];
+        foreach ($context_tokens as $context_id) {
+          switch ($context_id) {
+            case 'user.roles':
+              $keys[] = 'r.' . $current_user_role;
+              break;
+            case 'languages:language_interface':
+              $keys[] = 'en';
+              break;
+            case 'theme':
+              $keys[] = 'stark';
+              break;
+            default:
+              $keys[] = $context_id;
+          }
+        }
+        return $keys;
+      });
+    $this->renderer = new Renderer($this->controllerResolver, $this->themeManager, $this->elementInfo, $this->requestStack, $this->cacheFactory, $this->cacheContexts, $this->rendererConfig);
 
     $container = new ContainerBuilder();
     $container->set('cache_contexts', $this->cacheContexts);
diff --git a/sites/default/default.services.yml b/sites/default/default.services.yml
index 77e99ccf6a90a2781f52361283506cf7ee6cf94e..c44c73b9c9fa50d9f355ef98ce877b7b3503e51c 100644
--- a/sites/default/default.services.yml
+++ b/sites/default/default.services.yml
@@ -76,6 +76,14 @@ parameters:
     # Not recommended in production environments
     # @default true
     cache: true
+  renderer.config:
+    # Renderer required cache contexts:
+    #
+    # The Renderer will automatically associate these cache contexts with every
+    # render array, hence varying every render array by these cache contexts.
+    #
+    # @default ['languages:language_interface', 'theme']
+    required_cache_contexts: ['languages:language_interface', 'theme']
   factory.keyvalue:
     {}
     # Default key/value storage service to use.