diff --git a/core/modules/language/js/language.switcher.js b/core/modules/language/js/language.switcher.js
new file mode 100644
index 0000000..e03da3a
--- /dev/null
+++ b/core/modules/language/js/language.switcher.js
@@ -0,0 +1,25 @@
+/**
+ * @file
+ * Attaches behaviors for the Language switch block.
+ */
+(function ($, Drupal, drupalSettings) {
+
+  "use strict";
+
+  Drupal.behaviors.language_switcher = {
+    // @todo: We need to add the active class to the active language.
+    attach: function (context) {
+      if (!drupalSettings.path.isFront) {
+        var $context = $(context);
+        // Set the destination parameter on each of the contextual links.
+        var destination = 'destination=' + Drupal.encodePath(drupalSettings.path.currentPath);
+        $context.find('.language-switcher a').each(function () {
+          var url = this.getAttribute('href');
+          var glue = (url.indexOf('?') === -1) ? '?' : '&';
+          this.setAttribute('href', url + glue + destination);
+        });
+      }
+    }
+  }
+
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/modules/language/language.libraries.yml b/core/modules/language/language.libraries.yml
index 0c4cc8c..75c4c86 100644
--- a/core/modules/language/language.libraries.yml
+++ b/core/modules/language/language.libraries.yml
@@ -9,3 +9,12 @@ drupal.language.admin:
     - core/jquery
     - core/drupal
     - core/jquery.once
+
+drupal.language.switcher:
+  version: VERSION
+  js:
+    js/language.switcher.js: {}
+  dependencies:
+    - core/jquery
+    - core/drupal
+    - core/drupalSettings
diff --git a/core/modules/language/language.routing.yml b/core/modules/language/language.routing.yml
index 84740ee..63c199a 100644
--- a/core/modules/language/language.routing.yml
+++ b/core/modules/language/language.routing.yml
@@ -85,3 +85,10 @@ language.content_settings_page:
     _form: 'Drupal\language\Form\ContentLanguageSettingsForm'
   requirements:
     _permission: 'administer languages'
+
+language.redirect:
+  path: '/language-redirect'
+  defaults:
+    _controller: '\Drupal\language\Controller\LanguageController::languageSwitcherRedirect'
+  requirements:
+    _access: 'TRUE'
diff --git a/core/modules/language/src/Controller/LanguageController.php b/core/modules/language/src/Controller/LanguageController.php
new file mode 100644
index 0000000..a288908
--- /dev/null
+++ b/core/modules/language/src/Controller/LanguageController.php
@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\image\Controller\LanguageController.
+ */
+
+namespace Drupal\language\Controller;
+
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Url;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+use Symfony\Component\HttpFoundation\Request;
+
+class LanguageController extends ControllerBase {
+
+  public static function setLanguageSwitchDestination(array $element, array $context) {
+    $destination = \Drupal::routeMatch()->getRouteName() ? Url::fromRouteMatch(\Drupal::routeMatch())->getInternalPath() : '';
+
+    $callback =  '\Drupal\language\Controller\LanguageController::setLanguageSwitchDestination';
+    $placeholder = \Drupal::service('renderer')->generateCachePlaceholder($callback, $context);
+
+    // @todo: We need to add the active class to the active language.
+
+    // @todo This is working. But why?
+    $placeholder = str_replace('%2F', '/', urlencode($placeholder));
+
+    $element['#markup'] = str_replace($placeholder, $destination, $element['#markup']);
+    return $element;
+  }
+
+  public function languageSwitcherRedirect(Request $request) {
+    $destination = \Drupal::destination()->get();
+    if (!isset($destination)) {
+      $destination = $this->getUrlGenerator()->generateFromRoute('<front>');
+    }
+
+    return new RedirectResponse($destination, 301);
+  }
+
+}
diff --git a/core/modules/language/src/Plugin/Block/LanguageBlock.php b/core/modules/language/src/Plugin/Block/LanguageBlock.php
index 7aa9bb8..ec5ebbe 100644
--- a/core/modules/language/src/Plugin/Block/LanguageBlock.php
+++ b/core/modules/language/src/Plugin/Block/LanguageBlock.php
@@ -8,7 +8,10 @@
 namespace Drupal\language\Plugin\Block;
 
 use Drupal\Core\Block\BlockBase;
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Language\LanguageInterface;
 use Drupal\Core\Path\PathMatcherInterface;
+use Drupal\Core\Render\RendererInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
@@ -35,11 +38,18 @@ class LanguageBlock extends BlockBase implements ContainerFactoryPluginInterface
   protected $languageManager;
 
   /**
-   * The path matcher.
+   * The current user.
    *
-   * @var \Drupal\Core\Path\PathMatcherInterface
+   * @var \Drupal\Core\Session\AccountInterface
    */
-  protected $pathMatcher;
+  protected $currentUser;
+
+  /**
+   * The renderer service.
+   *
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
 
   /**
    * Constructs an LanguageBlock object.
@@ -52,16 +62,18 @@ class LanguageBlock extends BlockBase implements ContainerFactoryPluginInterface
    *   The plugin implementation definition.
    * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
    *   The language manager.
-   * @param \Drupal\Core\Path\PathMatcherInterface $path_matcher
-   *   The path matcher.
+   * @param \Drupal\Core\Session\AccountInterface $current_user
+   *   The current user.
+   * @param \Drupal\Core\Render\RendererInterface $renderer
+   *   The renderer service.
    */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, LanguageManagerInterface $language_manager, PathMatcherInterface $path_matcher) {
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, LanguageManagerInterface $language_manager, AccountInterface $current_user, RendererInterface $renderer) {
     parent::__construct($configuration, $plugin_id, $plugin_definition);
     $this->languageManager = $language_manager;
-    $this->pathMatcher = $path_matcher;
+    $this->currentUser = $current_user;
+    $this->renderer = $renderer;
   }
 
-
   /**
    * {@inheritdoc}
    */
@@ -71,7 +83,8 @@ public static function create(ContainerInterface $container, array $configuratio
       $plugin_id,
       $plugin_definition,
       $container->get('language_manager'),
-      $container->get('path.matcher')
+      $container->get('current_user'),
+      $container->get('renderer')
     );
   }
 
@@ -88,9 +101,23 @@ protected function blockAccess(AccountInterface $account) {
    */
   public function build() {
     $build = array();
-    $route_name = $this->pathMatcher->isFrontPage() ? '<front>' : '<current>';
     $type = $this->getDerivativeId();
-    $links = $this->languageManager->getLanguageSwitchLinks($type, Url::fromRoute($route_name));
+
+    $context = array(
+      'path' =>  \Drupal::routeMatch()->getRouteName() ? Url::fromRouteMatch(\Drupal::routeMatch())->getInternalPath() : '',
+      'type' => $type,
+    );
+
+    // For authenticated users, we just rely on Javascript for enhancing the
+    // block with the destination. In case of anonymous, we use
+    // post_render_cache for replacing a placeholder.
+    $switch_links_options = [];
+    if ($this->currentUser->isAnonymous()) {
+      $callback = '\Drupal\language\Controller\LanguageController::setLanguageSwitchDestination';
+      $switch_links_options['destination'] = $this->renderer->generateCachePlaceholder($callback, $context);
+    }
+
+    $links = $this->languageManager->getLanguageSwitchLinks($type, Url::fromRoute('language.redirect', $switch_links_options));
 
     if (isset($links->links)) {
       $build = array(
@@ -99,21 +126,44 @@ public function build() {
         '#attributes' => array(
           'class' => array(
             "language-switcher-{$links->method_id}",
+            "language-switcher"
           ),
         ),
         '#set_active_class' => TRUE,
       );
+
+      if ($this->currentUser->isAuthenticated()) {
+        $build['#attached']['library'][] = 'language/drupal.language.switcher';
+      }
+      else {
+        $build['#post_render_cache']['\Drupal\language\Controller\LanguageController::setLanguageSwitchDestination'] = array(
+          $context
+        );
+      }
     }
+
     return $build;
   }
 
   /**
    * {@inheritdoc}
-   *
-   * @todo Make cacheable in https://drupal.org/node/2232375.
+   */
+  public function getCacheContexts() {
+    // The "Language switcher" block must be cached per derivative, as we can
+    // have several blocks controlling different negotiation types.
+    return [
+      'languages:' . $this->getDerivativeId(),
+      'user.roles:anonymous'
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
    */
   public function getCacheMaxAge() {
-    return 0;
+    // @todo Ensure whenever language settings change, that appropriate cache
+    //   tags are invalidated, that allows us to cache this block forever.
+    return Cache::PERMANENT;
   }
 
 }
