diff --git a/core/core.services.yml b/core/core.services.yml
index 9afeed2..b52199b 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -388,7 +388,7 @@ services:
     class: Drupal\Core\EventSubscriber\ViewSubscriber
     tags:
       - { name: event_subscriber }
-    arguments: ['@content_negotiation']
+    arguments: ['@content_negotiation', '@title_resolver']
   private_key:
     class: Drupal\Core\PrivateKey
     arguments: ['@state']
diff --git a/core/lib/Drupal/Core/Controller/TitleResolver.php b/core/lib/Drupal/Core/Controller/TitleResolver.php
index 17b7a2e..359af1d 100644
--- a/core/lib/Drupal/Core/Controller/TitleResolver.php
+++ b/core/lib/Drupal/Core/Controller/TitleResolver.php
@@ -57,8 +57,12 @@ public function getTitle(Request $request, Route $route) {
       $route_title = call_user_func_array($callable, $arguments);
     }
     elseif ($title = $route->getDefault('_title')) {
+      $options = array();
+      if ($context = $route->getDefault('_title_context')) {
+        $options['context'] = $context;
+      }
       // Fall back to a static string from the route.
-      $route_title = $this->translationManager->translate($title);
+      $route_title = $this->translationManager->translate($title, array(), $options);
     }
     return $route_title;
   }
diff --git a/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php
index ae1d083..d85dded 100644
--- a/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\Core\EventSubscriber;
 
+use Drupal\Core\Controller\TitleResolverInterface;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\HttpFoundation\JsonResponse;
 use Symfony\Component\HttpKernel\KernelEvents;
@@ -25,10 +27,31 @@
  */
 class ViewSubscriber implements EventSubscriberInterface {
 
+  /**
+   * The content negotiation.
+   *
+   * @var \Drupal\Core\ContentNegotiation
+   */
   protected $negotiation;
 
-  public function __construct(ContentNegotiation $negotiation) {
+  /**
+   * The title resolver.
+   *
+   * @var \Drupal\Core\Controller\TitleResolverInterface
+   */
+  protected $titleResolver;
+
+  /**
+   * Constructs a new ViewSubscriber.
+   *
+   * @param \Drupal\Core\ContentNegotiation $negotiation
+   *   The content negotiation.
+   * @param \Drupal\Core\Controller\TitleResolverInterface $title_resolver
+   *   The title resolver.
+   */
+  public function __construct(ContentNegotiation $negotiation, TitleResolverInterface $title_resolver) {
     $this->negotiation = $negotiation;
+    $this->titleResolver = $title_resolver;
   }
 
   /**
@@ -73,8 +96,8 @@ public function onView(GetResponseForControllerResultEvent $event) {
       }
 
       // If no title was returned fall back to one defined in the route.
-      if (!isset($page_result['#title']) && $request->attributes->has('_title')) {
-        $page_result['#title'] = $request->attributes->get('_title');
+      if (!isset($page_result['#title'])) {
+        $page_result['#title'] = $this->titleResolver->getTitle($request, $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT));
       }
 
       $event->setResponse(new Response(drupal_render_page($page_result)));
@@ -91,8 +114,8 @@ public function onView(GetResponseForControllerResultEvent $event) {
       }
 
       // If no title was returned fall back to one defined in the route.
-      if (!isset($page_result['#title']) && $request->attributes->has('_title')) {
-        $page_result['#title'] = $request->attributes->get('_title');
+      if (!isset($page_result['#title'])) {
+        $page_result['#title'] = $this->titleResolver->getTitle($request, $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT));
       }
 
       $event->setResponse(new Response(drupal_render($page_result)));
@@ -154,6 +177,13 @@ public function onIframeUpload(GetResponseForControllerResultEvent $event) {
    */
   public function onHtml(GetResponseForControllerResultEvent $event) {
     $page_callback_result = $event->getControllerResult();
+    $request = $event->getRequest();
+
+    // If no title was returned fall back to one defined in the route.
+    if (!isset($page_callback_result['#title'])) {
+      $page_callback_result['#title'] = $this->titleResolver->getTitle($request, $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT));
+    }
+
     return new Response(drupal_render_page($page_callback_result));
   }
 
diff --git a/core/lib/Drupal/Core/Menu/LocalActionDefault.php b/core/lib/Drupal/Core/Menu/LocalActionDefault.php
index be6e6ab..1661ac9 100644
--- a/core/lib/Drupal/Core/Menu/LocalActionDefault.php
+++ b/core/lib/Drupal/Core/Menu/LocalActionDefault.php
@@ -67,7 +67,11 @@ public function getRouteName() {
    */
   public function getTitle() {
     // Subclasses may pull in the request or specific attributes as parameters.
-    return $this->t($this->pluginDefinition['title']);
+    $options = array();
+    if (!empty($this->pluginDefinition['title_context'])) {
+      $options['context'] = $this->pluginDefinition['title_context'];
+    }
+    return $this->t($this->pluginDefinition['title'], array(), $options);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Menu/LocalTaskDefault.php b/core/lib/Drupal/Core/Menu/LocalTaskDefault.php
index 058de09..ef8d023 100644
--- a/core/lib/Drupal/Core/Menu/LocalTaskDefault.php
+++ b/core/lib/Drupal/Core/Menu/LocalTaskDefault.php
@@ -75,7 +75,11 @@ public function getRouteParameters(Request $request) {
    */
   public function getTitle() {
     // Subclasses may pull in the request or specific attributes as parameters.
-    return $this->t($this->pluginDefinition['title']);
+    $options = array();
+    if (!empty($this->pluginDefinition['title_context'])) {
+      $options['context'] = $this->pluginDefinition['title_context'];
+    }
+    return $this->t($this->pluginDefinition['title'], array(), $options);
   }
 
   /**
diff --git a/core/modules/comment/lib/Drupal/comment/CommentFormController.php b/core/modules/comment/lib/Drupal/comment/CommentFormController.php
index 50e7caf..0d01506 100644
--- a/core/modules/comment/lib/Drupal/comment/CommentFormController.php
+++ b/core/modules/comment/lib/Drupal/comment/CommentFormController.php
@@ -115,6 +115,9 @@ public function form(array $form, array &$form_state) {
           '%title' => $comment->subject->value,
         ));
       }
+      else {
+        $form['#title'] = $this->t('Preview comment');
+      }
     }
     else {
       if ($this->currentUser->isAuthenticated()) {
@@ -358,7 +361,6 @@ public function submit(array $form, array &$form_state) {
    */
   public function preview(array $form, array &$form_state) {
     $comment = $this->entity;
-    drupal_set_title(t('Preview comment'), PASS_THROUGH);
     $form_state['comment_preview'] = comment_preview($comment);
     $form_state['rebuild'] = TRUE;
   }
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Tests/FieldUIRouteTest.php b/core/modules/field_ui/lib/Drupal/field_ui/Tests/FieldUIRouteTest.php
index 669b58e..d3764a4 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/Tests/FieldUIRouteTest.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/Tests/FieldUIRouteTest.php
@@ -51,7 +51,7 @@ public function testFieldUIRoutes() {
     //$this->assertText('No fields are present yet.');
 
     $this->drupalGet('admin/structure/types/manage/article/fields');
-    $this->assertTitle('Article | Drupal');
+    $this->assertTitle('Manage fields | Drupal');
   }
 
 }
diff --git a/core/modules/system/system.routing.yml b/core/modules/system/system.routing.yml
index 4d1c90ac..7874406 100644
--- a/core/modules/system/system.routing.yml
+++ b/core/modules/system/system.routing.yml
@@ -239,6 +239,8 @@ system.date_format_localize_reset:
 system.modules_list:
   path: '/admin/modules'
   defaults:
+    _title: 'Extend'
+    _title_context: 'With components'
     _form: 'Drupal\system\Form\ModulesListForm'
   requirements:
     _permission: 'administer modules'
diff --git a/core/tests/Drupal/Tests/Core/Controller/TitleResolverTest.php b/core/tests/Drupal/Tests/Core/Controller/TitleResolverTest.php
new file mode 100644
index 0000000..085ef60
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Controller/TitleResolverTest.php
@@ -0,0 +1,135 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Controller\TitleResolverTest.
+ */
+
+namespace Drupal\Tests\Core\Controller;
+
+use Drupal\Component\Utility\String;
+use Drupal\Core\Controller\TitleResolver;
+use Drupal\Tests\UnitTestCase;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Route;
+
+/**
+ * Tests the title resolver.
+ *
+ * @see \Drupal\Core\Controller\TitleResolver
+ */
+class TitleResolverTest extends UnitTestCase {
+
+  /**
+   * The mocked controller resolver.
+   *
+   * @var \Drupal\Core\Controller\ControllerResolverInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $controllerResolver;
+
+  /**
+   * The mocked translation manager.
+   *
+   * @var \Drupal\Core\StringTranslation\TranslationInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $translationManager;
+
+  /**
+   * The actual tested title resolver.
+   *
+   * @var \Drupal\Core\Controller\TitleResolver
+   */
+  protected $titleResolver;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Title resolver',
+      'description' => 'Tests the title resolver.',
+      'group' => 'Routing',
+    );
+  }
+
+  protected function setUp() {
+    $this->controllerResolver = $this->getMock('\Drupal\Core\Controller\ControllerResolverInterface');
+    $this->translationManager = $this->getMock('\Drupal\Core\StringTranslation\TranslationInterface');
+
+    $this->titleResolver = new TitleResolver($this->controllerResolver, $this->translationManager);
+  }
+
+  /**
+   * Tests a static title without a context.
+   *
+   * @see \Drupal\Core\Controller\TitleResolver::getTitle()
+   */
+  public function testStaticTitle() {
+    $request = new Request();
+    $route = new Route('/test-route', array('_title' => 'static title'));
+
+    $this->translationManager->expects($this->once())
+      ->method('translate')
+      ->with('static title', array(), array())
+      ->will($this->returnValue('translated title'));
+
+    $this->assertEquals('translated title', $this->titleResolver->getTitle($request, $route));
+  }
+
+  /**
+   * Tests a static title with a context.
+   *
+   * @see \Drupal\Core\Controller\TitleResolver::getTitle()
+   */
+  public function testStaticTitleWithContext() {
+    $request = new Request();
+    $route = new Route('/test-route', array('_title' => 'static title', '_title_context' => 'context'));
+
+    $this->translationManager->expects($this->once())
+      ->method('translate')
+      ->with('static title', array(), array('context' => 'context'))
+      ->will($this->returnValue('translated title with context'));
+
+    $this->assertEquals('translated title with context', $this->titleResolver->getTitle($request, $route));
+  }
+
+  /**
+   * Tests a dynamic title.
+   *
+   * @see \Drupal\Core\Controller\TitleResolver::getTitle()
+   */
+  public function testDynamicTitle() {
+    $request = new Request();
+    $route = new Route('/test-route', array('_title' => 'static title', '_title_callback' => 'Drupal\Tests\Core\Controller\TitleCallback::example'));
+
+    $callable = array(new TitleCallback(), 'example');
+    $this->controllerResolver->expects($this->once())
+      ->method('getControllerFromDefinition')
+      ->with('Drupal\Tests\Core\Controller\TitleCallback::example')
+      ->will($this->returnValue($callable));
+    $this->controllerResolver->expects($this->once())
+      ->method('getArguments')
+      ->with($request, $callable)
+      ->will($this->returnValue(array('example')));
+
+    $this->assertEquals('test example', $this->titleResolver->getTitle($request, $route));
+  }
+
+}
+
+/**
+ * Provides an example title callback for the testDynamicTitle method above.
+ */
+class TitleCallback {
+
+  /**
+   * Gets the example string.
+   *
+   * @param string $value
+   *   The dynamic value.
+   *
+   * @return string
+   *   Returns the example string.
+   */
+  public function example($value) {
+    return String::format('test @value', array('@value' => $value));
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Menu/LocalActionDefaultTest.php b/core/tests/Drupal/Tests/Core/Menu/LocalActionDefaultTest.php
new file mode 100644
index 0000000..6039e83
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Menu/LocalActionDefaultTest.php
@@ -0,0 +1,124 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Menu\LocalActionDefaultTest.
+ */
+
+namespace Drupal\Tests\Core\Menu;
+
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\Menu\LocalActionDefault;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * Tests the local action default class.
+ *
+ * @see \Drupal\Core\Menu\LocalActionDefault
+ */
+class LocalActionDefaultTest extends UnitTestCase {
+
+  /**
+   * The tested local action default plugin.
+   *
+   * @var \Drupal\Core\Menu\LocalActionDefault
+   */
+  protected $localActionDefault;
+
+  /**
+   * The used plugin configuration.
+   *
+   * @var array
+   */
+  protected $config = array();
+
+  /**
+   * The used plugin ID.
+   *
+   * @var string
+   */
+  protected $pluginId = 'local_action_default';
+
+  /**
+   * The used plugin definition.
+   *
+   * @var array
+   */
+  protected $pluginDefinition = array(
+    'id' => 'local_action_default',
+  );
+
+  /**
+   * The mocked translator.
+   *
+   * @var \Drupal\Core\StringTranslation\TranslationInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $stringTranslation;
+
+  /**
+   * The mocked route provider.
+   *
+   * @var \Drupal\Core\Routing\RouteProviderInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $routeProvider;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Local actions default plugin.',
+      'description' => 'Tests the local action default class.',
+      'group' => 'Menu',
+    );
+  }
+
+  protected function setUp() {
+    parent::setUp();
+
+    $this->stringTranslation = $this->getMock('Drupal\Core\StringTranslation\TranslationInterface');
+    $this->routeProvider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface');
+  }
+
+  /**
+   * Setups the local action default.
+   */
+  protected function setupLocalActionDefault() {
+    $container = new ContainerBuilder();
+    $container->set('string_translation', $this->stringTranslation);
+    \Drupal::setContainer($container);
+
+    $this->localActionDefault = new LocalActionDefault($this->config, $this->pluginId, $this->pluginDefinition, $this->routeProvider);
+  }
+
+  /**
+   * Tests the getTitle method without a translation context.
+   *
+   * @see \Drupal\Core\Menu\LocalTaskDefault::getTitle()
+   */
+  public function testGetTitle() {
+    $this->pluginDefinition['title'] = 'Example';
+    $this->stringTranslation->expects($this->once())
+      ->method('translate')
+      ->with($this->pluginDefinition['title'], array(), array())
+      ->will($this->returnValue('Example translated'));
+
+    $this->setupLocalActionDefault();
+    $this->assertEquals('Example translated', $this->localActionDefault->getTitle());
+  }
+
+  /**
+   * Tests the getTitle method with a translation context.
+   *
+   * @see \Drupal\Core\Menu\LocalTaskDefault::getTitle()
+   */
+  public function testGetTitleWithContext() {
+    $this->pluginDefinition['title'] = 'Example';
+    $this->pluginDefinition['title_context'] = 'context';
+    $this->stringTranslation->expects($this->once())
+      ->method('translate')
+      ->with($this->pluginDefinition['title'], array(), array('context' => 'context'))
+      ->will($this->returnValue('Example translated with context'));
+
+    $this->setupLocalActionDefault();
+    $this->assertEquals('Example translated with context', $this->localActionDefault->getTitle());
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Menu/LocalTaskDefaultTest.php b/core/tests/Drupal/Tests/Core/Menu/LocalTaskDefaultTest.php
index 8cf9e96..e708c6a 100644
--- a/core/tests/Drupal/Tests/Core/Menu/LocalTaskDefaultTest.php
+++ b/core/tests/Drupal/Tests/Core/Menu/LocalTaskDefaultTest.php
@@ -253,14 +253,15 @@ public function testActive() {
   }
 
   /**
-   * Tests the getTitle method.
+   * Tests the getTitle method without a translation context.
    *
    * @see \Drupal\Core\Menu\LocalTaskDefault::getTitle()
    */
   public function testGetTitle() {
     $this->pluginDefinition['title'] = 'Example';
     $this->stringTranslation->expects($this->once())
-      ->method('translate', $this->pluginDefinition['title'])
+      ->method('translate')
+      ->with($this->pluginDefinition['title'], array(), array())
       ->will($this->returnValue('Example translated'));
 
     $this->setupLocalTaskDefault();
@@ -268,6 +269,23 @@ public function testGetTitle() {
   }
 
   /**
+   * Tests the getTitle method with a translation context.
+   *
+   * @see \Drupal\Core\Menu\LocalTaskDefault::getTitle()
+   */
+  public function testGetTitleWithContext() {
+    $this->pluginDefinition['title'] = 'Example';
+    $this->pluginDefinition['title_context'] = 'context';
+    $this->stringTranslation->expects($this->once())
+      ->method('translate')
+      ->with($this->pluginDefinition['title'], array(), array('context' => 'context'))
+      ->will($this->returnValue('Example translated with context'));
+
+    $this->setupLocalTaskDefault();
+    $this->assertEquals('Example translated with context', $this->localTaskBase->getTitle());
+  }
+
+  /**
    * Tests the getOption method.
    *
    * @see \Drupal\Core\Menu\LocalTaskDefault::getOption()
