diff --git a/core/includes/common.inc b/core/includes/common.inc
index 5e9ff1c..69363f3 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -1964,155 +1964,13 @@ function _format_date_callback(array $matches = NULL, $new_langcode = NULL) {
  * When creating links in modules, consider whether l() could be a better
  * alternative than url().
  *
- * @param $path
- *   (optional) The internal path or external URL being linked to, such as
- *   "node/34" or "http://example.com/foo". The default value is equivalent to
- *   passing in '<front>'. A few notes:
- *   - If you provide a full URL, it will be considered an external URL.
- *   - If you provide only the path (e.g. "node/34"), it will be
- *     considered an internal link. In this case, it should be a system URL,
- *     and it will be replaced with the alias, if one exists. Additional query
- *     arguments for internal paths must be supplied in $options['query'], not
- *     included in $path.
- *   - If you provide an internal path and $options['alias'] is set to TRUE, the
- *     path is assumed already to be the correct path alias, and the alias is
- *     not looked up.
- *   - The special string '<front>' generates a link to the site's base URL.
- *   - If your external URL contains a query (e.g. http://example.com/foo?a=b),
- *     then you can either URL encode the query keys and values yourself and
- *     include them in $path, or use $options['query'] to let this function
- *     URL encode them.
- * @param $options
- *   (optional) An associative array of additional options, with the following
- *   elements:
- *   - 'query': An array of query key/value-pairs (without any URL-encoding) to
- *     append to the URL.
- *   - 'fragment': A fragment identifier (named anchor) to append to the URL.
- *     Do not include the leading '#' character.
- *   - 'absolute': Defaults to FALSE. Whether to force the output to be an
- *     absolute link (beginning with http:). Useful for links that will be
- *     displayed outside the site, such as in an RSS feed.
- *   - 'alias': Defaults to FALSE. Whether the given path is a URL alias
- *     already.
- *   - 'external': Whether the given path is an external URL.
- *   - 'language': An optional language object. If the path being linked to is
- *     internal to the site, $options['language'] is used to look up the alias
- *     for the URL. If $options['language'] is omitted, the language will be
- *     obtained from language(LANGUAGE_TYPE_URL).
- *   - 'https': Whether this URL should point to a secure location. If not
- *     defined, the current scheme is used, so the user stays on HTTP or HTTPS
- *     respectively. TRUE enforces HTTPS and FALSE enforces HTTP, but HTTPS can
- *     only be enforced when the variable 'https' is set to TRUE.
- *   - 'base_url': Only used internally, to modify the base URL when a language
- *     dependent URL requires so.
- *   - 'prefix': Only used internally, to modify the path when a language
- *     dependent URL requires so.
- *   - 'script': Added to the URL between the base path and the path prefix.
- *     Defaults to empty string when clean URLs are in effect, and to
- *     'index.php/' when they are not.
- *   - 'entity_type': The entity type of the object that called url(). Only
- *     set if url() is invoked by Drupal\Core\Entity\Entity::uri().
- *   - 'entity': The entity object (such as a node) for which the URL is being
- *     generated. Only set if url() is invoked by Drupal\Core\Entity\Entity::uri().
- *
- * @return
- *   A string containing a URL to the given path.
+ * @see \Drupal\Core\Routing\PathBasedGeneratorInterface::generateFromPath().
  */
 function url($path = NULL, array $options = array()) {
-  // Merge in defaults.
-  $options += array(
-    'fragment' => '',
-    'query' => array(),
-    'absolute' => FALSE,
-    'alias' => FALSE,
-    'prefix' => '',
-    'script' => $GLOBALS['script_path'],
-  );
-
-  if (!isset($options['external'])) {
-    // Return an external link if $path contains an allowed absolute URL. Only
-    // call the slow drupal_strip_dangerous_protocols() if $path contains a ':'
-    // before any / ? or #. Note: we could use url_is_external($path) here, but
-    // that would require another function call, and performance inside url() is
-    // critical.
-    $colonpos = strpos($path, ':');
-    $options['external'] = ($colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && drupal_strip_dangerous_protocols($path) == $path);
-  }
-
-  // Preserve the original path before altering or aliasing.
-  $original_path = $path;
-
-  // Allow other modules to alter the outbound URL and options.
-  drupal_alter('url_outbound', $path, $options, $original_path);
-
-  if (isset($options['fragment']) && $options['fragment'] !== '') {
-    $options['fragment'] = '#' . $options['fragment'];
-  }
-
-  if ($options['external']) {
-    // Split off the fragment.
-    if (strpos($path, '#') !== FALSE) {
-      list($path, $old_fragment) = explode('#', $path, 2);
-      // If $options contains no fragment, take it over from the path.
-      if (isset($old_fragment) && !$options['fragment']) {
-        $options['fragment'] = '#' . $old_fragment;
-      }
-    }
-    // Append the query.
-    if ($options['query']) {
-      $path .= (strpos($path, '?') !== FALSE ? '&' : '?') . drupal_http_build_query($options['query']);
-    }
-    if (isset($options['https']) && variable_get('https', FALSE)) {
-      if ($options['https'] === TRUE) {
-        $path = str_replace('http://', 'https://', $path);
-      }
-      elseif ($options['https'] === FALSE) {
-        $path = str_replace('https://', 'http://', $path);
-      }
-    }
-    // Reassemble.
-    return $path . $options['fragment'];
-  }
-
-  global $base_url, $base_secure_url, $base_insecure_url;
-
-  // The base_url might be rewritten from the language rewrite in domain mode.
-  if (!isset($options['base_url'])) {
-    if (isset($options['https']) && variable_get('https', FALSE)) {
-      if ($options['https'] === TRUE) {
-        $options['base_url'] = $base_secure_url;
-        $options['absolute'] = TRUE;
-      }
-      elseif ($options['https'] === FALSE) {
-        $options['base_url'] = $base_insecure_url;
-        $options['absolute'] = TRUE;
-      }
-    }
-    else {
-      $options['base_url'] = $base_url;
-    }
-  }
-
-  // The special path '<front>' links to the default front page.
-  if ($path == '<front>') {
-    $path = '';
-  }
-  elseif (!empty($path) && !$options['alias']) {
-    $langcode = isset($options['language']) && isset($options['language']->langcode) ? $options['language']->langcode : '';
-    $alias = drupal_container()->get('path.alias_manager')->getPathAlias($original_path, $langcode);
-    if ($alias != $original_path) {
-      $path = $alias;
-    }
-  }
-
-  $base = $options['absolute'] ? $options['base_url'] . '/' : base_path();
-  $prefix = empty($path) ? rtrim($options['prefix'], '/') : $options['prefix'];
-
-  $path = drupal_encode_path($prefix . $path);
-  $query = $options['query'] ? ('?' . drupal_http_build_query($options['query'])) : '';
-  return $base . $options['script'] . $path . $query . $options['fragment'];
+  return drupal_container()->get('url_generator')->generateFromPath($path, $options);
 }
 
+
 /**
  * Returns TRUE if a path is external to Drupal (e.g. http://example.com).
  *
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 803947f..92e9ec6 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -367,6 +367,7 @@ function install_begin_request(&$install_state) {
       ))
       ->addMethodCall('setUserAgent', array('Drupal (+http://drupal.org/)'));
 
+    $container->register('url_generator', 'Drupal\Core\Routing\NullGenerator');
     drupal_container($container);
   }
 
diff --git a/core/lib/Drupal/Core/CoreBundle.php b/core/lib/Drupal/Core/CoreBundle.php
index 035f282..202c56c 100644
--- a/core/lib/Drupal/Core/CoreBundle.php
+++ b/core/lib/Drupal/Core/CoreBundle.php
@@ -10,6 +10,7 @@
 use Drupal\Core\DependencyInjection\Compiler\RegisterKernelListenersPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterAccessChecksPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterMatchersPass;
+use Drupal\Core\DependencyInjection\Compiler\RegisterPathProcessorsPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterRouteFiltersPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterRouteEnhancersPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterParamConvertersPass;
@@ -239,6 +240,7 @@ public function build(ContainerBuilder $container) {
       ->addTag('event_subscriber');
     $container->register('path_subscriber', 'Drupal\Core\EventSubscriber\PathSubscriber')
       ->addArgument(new Reference('path.alias_manager.cached'))
+      ->addArgument(new Reference('path_processor_manager'))
       ->addTag('event_subscriber');
     $container->register('legacy_request_subscriber', 'Drupal\Core\EventSubscriber\LegacyRequestSubscriber')
       ->addTag('event_subscriber');
@@ -264,6 +266,8 @@ public function build(ContainerBuilder $container) {
       ->addTag('event_subscriber')
       ->addArgument(array(new Reference('exception_controller'), 'execute'));
 
+    $this->registerPathProcessors($container);
+
     $container
       ->register('transliteration', 'Drupal\Core\Transliteration\PHPTransliteration');
 
@@ -316,13 +320,13 @@ protected function registerRouting(ContainerBuilder $container) {
     $container->register('router.matcher', 'Symfony\Cmf\Component\Routing\NestedMatcher\NestedMatcher')
       ->addArgument(new Reference('router.route_provider'))
       ->addMethodCall('setFinalMatcher', array(new Reference('router.matcher.final_matcher')));
-    $container->register('router.generator', 'Drupal\Core\Routing\UrlGenerator')
+    $container->register('url_generator', 'Drupal\Core\Routing\UrlGenerator')
       ->addArgument(new Reference('router.route_provider'))
-      ->addArgument(new Reference('path.alias_manager.cached'));
+      ->addArgument(new Reference('path_processor_manager'));
     $container->register('router.dynamic', 'Symfony\Cmf\Component\Routing\DynamicRouter')
       ->addArgument(new Reference('router.request_context'))
       ->addArgument(new Reference('router.matcher'))
-      ->addArgument(new Reference('router.generator'));
+      ->addArgument(new Reference('url_generator'));
 
     $container->register('legacy_generator', 'Drupal\Core\Routing\NullGenerator');
     $container->register('legacy_url_matcher', 'Drupal\Core\LegacyUrlMatcher');
@@ -367,4 +371,29 @@ protected function registerTwig(ContainerBuilder $container) {
       // @see http://drupal.org/node/1804998
       ->addMethodCall('addExtension', array(new Definition('Twig_Extension_Debug')));
   }
+
+  /**
+   * Register services related to path processing.
+   */
+  protected function registerPathProcessors(ContainerBuilder $container) {
+    // Register the path processor manager service.
+    $container->register('path_processor_manager', 'Drupal\Core\PathProcessor\PathProcessorManager');
+    // Register the processor that urldecodes the path.
+    $container->register('path_processor_decode', 'Drupal\Core\PathProcessor\PathProcessorDecode')
+      ->addTag('path_processor_inbound', array('priority' => 1000));
+    // Register the processor that resolves the front page.
+    $container->register('path_processor_front', 'Drupal\Core\PathProcessor\PathProcessorFront')
+      ->addArgument(new Reference('config.factory'))
+      ->addTag('path_processor_inbound', array('priority' => 200))
+      ->addTag('path_processor_outbound', array('priority' => 200));
+    // Register the alias path processor.
+    $container->register('path_processor_alias', 'Drupal\Core\PathProcessor\PathProcessorAlias')
+      ->addArgument(new Reference('path.alias_manager'))
+      ->addTag('path_processor_inbound', array('priority' => 100))
+      ->addTag('path_processor_outbound', array('priority' => 300));
+
+    // Add the compiler pass that will process the tagged services.
+    $container->addCompilerPass(new RegisterPathProcessorsPass());
+  }
+
 }
diff --git a/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterPathProcessorsPass.php b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterPathProcessorsPass.php
new file mode 100644
index 0000000..70de42d
--- /dev/null
+++ b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterPathProcessorsPass.php
@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\DependencyInjection\Compiler\RegisterPathProcessorsPass.
+ */
+
+namespace Drupal\Core\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+
+/**
+ * Adds services to the 'path_processor_manager service.
+ */
+class RegisterPathProcessorsPass implements CompilerPassInterface {
+
+  /**
+   * Adds services tagged 'path_processor_inbound' to the path processor manager.
+   *
+   * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
+   *  The container to process.
+   */
+  public function process(ContainerBuilder $container) {
+    if (!$container->hasDefinition('path_processor_manager')) {
+      return;
+    }
+    $manager = $container->getDefinition('path_processor_manager');
+    // Add inbound path processors.
+    foreach ($container->findTaggedServiceIds('path_processor_inbound') as $id => $attributes) {
+      $priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0;
+      $manager->addMethodCall('addInbound', array(new Reference($id), $priority));
+    }
+    // Add outbound path processors.
+    foreach ($container->findTaggedServiceIds('path_processor_outbound') as $id => $attributes) {
+      $priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0;
+      $manager->addMethodCall('addOutbound', array(new Reference($id), $priority));
+    }
+  }
+}
diff --git a/core/lib/Drupal/Core/EventSubscriber/PathSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/PathSubscriber.php
index 6e5b7a9..5915d4b 100644
--- a/core/lib/Drupal/Core/EventSubscriber/PathSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/PathSubscriber.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\EventSubscriber;
 
 use Drupal\Core\CacheDecorator\AliasManagerCacheDecorator;
+use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
 use Symfony\Component\HttpKernel\HttpKernelInterface;
 use Symfony\Component\HttpKernel\KernelEvents;
 use Symfony\Component\HttpKernel\Event\GetResponseEvent;
@@ -20,24 +21,24 @@
 class PathSubscriber extends PathListenerBase implements EventSubscriberInterface {
 
   protected $aliasManager;
+  protected $pathProcessor;
 
-  public function __construct(AliasManagerCacheDecorator $alias_manager) {
+  public function __construct(AliasManagerCacheDecorator $alias_manager, InboundPathProcessorInterface $path_processor) {
     $this->aliasManager = $alias_manager;
+    $this->pathProcessor = $path_processor;
   }
 
   /**
-   * Resolve the system path.
+   * Converts the request path to a system path.
    *
    * @param Symfony\Component\HttpKernel\Event\GetResponseEvent $event
    *   The Event to process.
    */
-  public function onKernelRequestPathResolve(GetResponseEvent $event) {
+  public function onKernelRequestConvertPath(GetResponseEvent $event) {
     $request = $event->getRequest();
-    $path = $this->extractPath($request);
-    $path = $this->aliasManager->getSystemPath($path);
-    $this->setPath($request, $path);
-    // If this is the master request, set the cache key for the caching of all
-    // system paths looked up during the request.
+    $path = trim($request->getPathInfo(), '/');
+    $path = $this->pathProcessor->processInbound($path, $request);
+    $request->attributes->set('system_path', $path);
     if ($event->getRequestType() == HttpKernelInterface::MASTER_REQUEST) {
       $this->aliasManager->setCacheKey($path);
     }
@@ -51,84 +52,14 @@ public function onKernelTerminate(PostResponseEvent $event) {
   }
 
   /**
-   * Resolve the front-page default path.
-   *
-   * @todo The path system should be objectified to remove the function calls in
-   *   this method.
-   *
-   * @param Symfony\Component\HttpKernel\Event\GetResponseEvent $event
-   *   The Event to process.
-   */
-  public function onKernelRequestFrontPageResolve(GetResponseEvent $event) {
-    $request = $event->getRequest();
-    $path = $this->extractPath($request);
-
-    if (empty($path)) {
-      // @todo Temporary hack. Fix when configuration is injectable.
-      $path = config('system.site')->get('page.front');
-      if (empty($path)) {
-        $path = 'user';
-      }
-    }
-
-    $this->setPath($request, $path);
-  }
-
-  /**
-   * Decode language information embedded in the request path.
-   *
-   * @param Symfony\Component\HttpKernel\Event\GetResponseEvent $event
-   *   The Event to process.
-   */
-  public function onKernelRequestLanguageResolve(GetResponseEvent $event) {
-    // We need to act only on the master request, otherwise subrequests will
-    // inherit the main request path and an infinite loop will be started.
-    if ($event->getRequestType() == HttpKernelInterface::MASTER_REQUEST) {
-      $path = _language_resolved_path();
-      if ($path !== NULL) {
-        $this->setPath($event->getRequest(), $path);
-      }
-    }
-  }
-
-  /**
-   * Decodes the path of the request.
-   *
-   * Parameters in the URL sometimes represent code-meaningful strings. It is
-   * therefore useful to always urldecode() those values so that individual
-   * controllers need not concern themselves with it. This is Drupal-specific
-   * logic and may not be familiar for developers used to other Symfony-family
-   * projects.
-   *
-   * @todo Revisit whether or not this logic is appropriate for here or if
-   *   controllers should be required to implement this logic themselves. If we
-   *   decide to keep this code, remove this TODO.
-   *
-   * @param Symfony\Component\HttpKernel\Event\GetResponseEvent $event
-   *   The Event to process.
-   */
-  public function onKernelRequestDecodePath(GetResponseEvent $event) {
-    $request = $event->getRequest();
-    $path = $this->extractPath($request);
-
-    $path = urldecode($path);
-
-    $this->setPath($request, $path);
-  }
-
-  /**
    * Registers the methods in this class that should be listeners.
    *
    * @return array
    *   An array of event listener definitions.
    */
   static function getSubscribedEvents() {
-    $events[KernelEvents::REQUEST][] = array('onKernelRequestDecodePath', 200);
-    $events[KernelEvents::REQUEST][] = array('onKernelRequestLanguageResolve', 150);
-    $events[KernelEvents::REQUEST][] = array('onKernelRequestFrontPageResolve', 101);
-    $events[KernelEvents::REQUEST][] = array('onKernelRequestPathResolve', 100);
+    $events[KernelEvents::REQUEST][] = array('onKernelRequestConvertPath', 200);
     $events[KernelEvents::TERMINATE][] = array('onKernelTerminate', 200);
-
     return $events;
   }
 }
diff --git a/core/lib/Drupal/Core/PathProcessor/InboundPathProcessorInterface.php b/core/lib/Drupal/Core/PathProcessor/InboundPathProcessorInterface.php
new file mode 100644
index 0000000..946b29c
--- /dev/null
+++ b/core/lib/Drupal/Core/PathProcessor/InboundPathProcessorInterface.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Core\PathProcessor\InboundPathProcessorInterface.
+ */
+
+namespace Drupal\Core\PathProcessor;
+
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Defines an interface for classes that process the inbound path.
+ */
+interface InboundPathProcessorInterface {
+
+  /**
+   * Processes the inbound path.
+   *
+   * @param string $path
+   *   The path to process.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The HttpRequest object representing the current request.
+   */
+  public function processInbound($path, Request $request);
+
+}
diff --git a/core/lib/Drupal/Core/PathProcessor/OutboundPathProcessorInterface.php b/core/lib/Drupal/Core/PathProcessor/OutboundPathProcessorInterface.php
new file mode 100644
index 0000000..7d94e1a
--- /dev/null
+++ b/core/lib/Drupal/Core/PathProcessor/OutboundPathProcessorInterface.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Core\PathProcessor\OutboundPathProcessorInterface.
+ */
+
+namespace Drupal\Core\PathProcessor;
+
+/**
+ * Defines an interface for classes that process the outbound path.
+ */
+interface OutboundPathProcessorInterface {
+
+  /**
+   * Processes the outbound path.
+   *
+   * @param string $path
+   *   The path to process.
+   *
+   * @param array $options
+   *   An array of options such as would be passed to url().
+   */
+  public function processOutbound($path, $options = array());
+
+}
diff --git a/core/lib/Drupal/Core/PathProcessor/PathProcessorAlias.php b/core/lib/Drupal/Core/PathProcessor/PathProcessorAlias.php
new file mode 100644
index 0000000..c672fad
--- /dev/null
+++ b/core/lib/Drupal/Core/PathProcessor/PathProcessorAlias.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\PathProcessor\PathProcessorAlias.
+ */
+
+namespace Drupal\Core\PathProcessor;
+
+use Drupal\Core\Path\AliasManagerInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Processes the inbound path using path alias lookups.
+ */
+class PathProcessorAlias implements InboundPathProcessorInterface, OutboundPathProcessorInterface {
+
+  /**
+   * An alias manager for looking up the system path.
+   *
+   * @var \Drupal\Core\Path\AliasManagerInterface
+   */
+  protected $aliasManager;
+
+  /**
+   * Constructs a PathProcessorAlias object.
+   *
+   * @param \Drupal\Core\Path\AliasManagerInterface $alias_manager
+   *   An alias manager for looking up the system path.
+   */
+  public function __construct(AliasManagerInterface $alias_manager) {
+    $this->aliasManager = $alias_manager;
+  }
+
+  /**
+   * Implements Drupal\Core\PathProcessor\InboundPathProcessorInterface::processInbound().
+   */
+  public function processInbound($path, Request $request) {
+    $path = $this->aliasManager->getSystemPath($path);
+    return $path;
+  }
+
+  /**
+   * Implements Drupal\Core\PathProcessor\OutboundPathProcessorInterface::processOutbound().
+   */
+  public function processOutbound($path, $options = array()) {
+    $path = $this->aliasManager->getPathAlias($path);
+    return $path;
+  }
+}
diff --git a/core/lib/Drupal/Core/PathProcessor/PathProcessorDecode.php b/core/lib/Drupal/Core/PathProcessor/PathProcessorDecode.php
new file mode 100644
index 0000000..0674cda
--- /dev/null
+++ b/core/lib/Drupal/Core/PathProcessor/PathProcessorDecode.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Core\PathProcessor\PathProcessorDecode.
+ */
+
+namespace Drupal\Core\PathProcessor;
+
+use Drupal\Core\Config\ConfigFactory;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Processes the inbound path by urldecoding it.
+ *
+ * Parameters in the URL sometimes represent code-meaningful strings. It is
+ * therefore useful to always urldecode() those values so that individual
+ * controllers need not concern themselves with it. This is Drupal-specific
+ * logic and may not be familiar for developers used to other Symfony-family
+ * projects.
+ *
+ * @todo Revisit whether or not this logic is appropriate for here or if
+ *   controllers should be required to implement this logic themselves. If we
+ *   decide to keep this code, remove this TODO.
+ */
+class PathProcessorDecode implements InboundPathProcessorInterface {
+
+  /**
+   * Implements Drupal\Core\PathProcessor\InboundPathProcessorInterface::processInbound().
+   */
+  public function processInbound($path, Request $request) {
+    return urldecode($path);
+  }
+
+}
diff --git a/core/lib/Drupal/Core/PathProcessor/PathProcessorFront.php b/core/lib/Drupal/Core/PathProcessor/PathProcessorFront.php
new file mode 100644
index 0000000..e2694b0
--- /dev/null
+++ b/core/lib/Drupal/Core/PathProcessor/PathProcessorFront.php
@@ -0,0 +1,59 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Core\PathProcessor\PathProcessorFront.
+ */
+
+namespace Drupal\Core\PathProcessor;
+
+use Drupal\Core\Config\ConfigFactory;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Processes the inbound path by resolving it to the front page if empty.
+ */
+class PathProcessorFront implements InboundPathProcessorInterface, OutboundPathProcessorInterface {
+
+  /**
+   * A config factory for retrieving required config settings.
+   *
+   * @var \Drupal\Core\Config\ConfigFactory
+   */
+  protected $config;
+
+  /**
+   * Constructs a PathProcessorFront object.
+   *
+   * @param Drupal\Core\Config\ConfigFactory $config
+   *   A config factory for retrieving the site front page configuration.
+   */
+  public function __construct(ConfigFactory $config) {
+    $this->config = $config;
+  }
+
+  /**
+   * Implements Drupal\Core\PathProcessor\InboundPathProcessorInterface::processInbound().
+   */
+  public function processInbound($path, Request $request) {
+    if (empty($path)) {
+      $path = $this->config->get('system.site')->get('page.front');
+      if (empty($path)) {
+        $path = 'user';
+      }
+    }
+    return $path;
+  }
+
+  /**
+   * Implements Drupal\Core\PathProcessor\OutboundPathProcessorInterface::processOutbound().
+   */
+  public function processOutbound($path, $options = array()) {
+    // The special path '<front>' links to the default front page.
+    if ($path == '<front>') {
+      $path = '';
+    }
+    return $path;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/PathProcessor/PathProcessorManager.php b/core/lib/Drupal/Core/PathProcessor/PathProcessorManager.php
new file mode 100644
index 0000000..142a3ba
--- /dev/null
+++ b/core/lib/Drupal/Core/PathProcessor/PathProcessorManager.php
@@ -0,0 +1,149 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Core\PathProcessor\PathProcessorManager.
+ */
+
+namespace Drupal\Core\PathProcessor;
+
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Path processor manager.
+ *
+ * Holds an array of path processor objects and uses them to sequentially process
+ * a path, in order of processor priority.
+ */
+class PathProcessorManager implements InboundPathProcessorInterface, OutboundPathProcessorInterface {
+
+  /**
+   * Holds the array of inbound processors to cycle through.
+   *
+   * @var array
+   *   An array whose keys are priorities and whose values are arrays of path
+   *   processor objects.
+   */
+  protected $inboundProcessors = array();
+
+  /**
+   * Holds the array of inbound processors, sorted by priority.
+   *
+   * @var array
+   *   An array of path processor objects.
+   */
+  protected $sortedInbound = array();
+
+
+  /**
+   * Holds the array of outbound processors to cycle through.
+   *
+   * @var array
+   *   An array whose keys are priorities and whose values are arrays of path
+   *   processor objects.
+   */
+  protected $outboundProcessors = array();
+
+  /**
+   * Holds the array of outbound processors, sorted by priority.
+   *
+   * @var array
+   *   An array of path processor objects.
+   */
+  protected $sortedOutbound = array();
+
+  /**
+   * Adds an inbound processor object to the $inboundProcessors property.
+   *
+   * @param \Drupal\Core\PathProcessor\InboundPathProcessorInterface $processor
+   *   The processor object to add.
+   *
+   * @param int $priority
+   *   The priority of the processor being added.
+   */
+  public function addInbound(InboundPathProcessorInterface $processor, $priority = 0) {
+    $this->inboundProcessors[$priority][] = $processor;
+    $this->sortedInbound = array();
+  }
+
+  /**
+   * Implements Drupal\Core\PathProcessor\InboundPathProcessorInterface::processInbound().
+   */
+  public function processInbound($path, Request $request) {
+    $processors = $this->getInbound();
+    foreach ($processors as $processor) {
+      $path = $processor->processInbound($path, $request);
+    }
+    return $path;
+  }
+
+  /**
+   * Returns the sorted array of inbound processors.
+   *
+   * @return array
+   *   An array of processor objects.
+   */
+  protected function getInbound() {
+    if (empty($this->sortedInbound)) {
+      $this->sortedInbound = $this->sortProcessors('inboundProcessors');
+    }
+
+    return $this->sortedInbound;
+  }
+
+
+  /**
+   * Adds an outbound processor object to the $outboundProcessors property.
+   *
+   * @param \Drupal\Core\PathProcessor\OutboundPathProcessorInterface $processor
+   *   The processor object to add.
+   *
+   * @param int $priority
+   *   The priority of the processor being added.
+   */
+  public function addOutbound(OutboundPathProcessorInterface $processor, $priority = 0) {
+    $this->outboundProcessors[$priority][] = $processor;
+    $this->sortedOutbound = array();
+  }
+
+  /**
+   * Implements Drupal\Core\PathProcessor\OutboundPathProcessorInterface::processOutbound().
+   */
+  public function processOutbound($path, $options = array()) {
+    $processors = $this->getOutbound();
+    foreach ($processors as $processor) {
+      $path = $processor->processOutbound($path, $options);
+    }
+    return $path;
+  }
+
+  /**
+   * Returns the sorted array of outbound processors.
+   *
+   * @return array
+   *   An array of processor objects.
+   */
+  protected function getOutbound() {
+    if (empty($this->sortedOutbound)) {
+      $this->sortedOutbound = $this->sortProcessors('outboundProcessors');
+    }
+
+    return $this->sortedOutbound;
+  }
+
+  /**
+   * Sorts the processors according to priority.
+   *
+   * @param string $type
+   *   The processor type to sort, e.g. 'inboundProcessors'.
+   */
+  protected function sortProcessors($type) {
+    $sorted = array();
+    krsort($this->{$type});
+
+    foreach ($this->{$type} as $processors) {
+      $sorted = array_merge($sorted, $processors);
+    }
+    return $sorted;
+  }
+}
diff --git a/core/lib/Drupal/Core/Routing/NullGenerator.php b/core/lib/Drupal/Core/Routing/NullGenerator.php
index 7228514..a3026d0 100644
--- a/core/lib/Drupal/Core/Routing/NullGenerator.php
+++ b/core/lib/Drupal/Core/Routing/NullGenerator.php
@@ -13,17 +13,22 @@
 /**
  * No-op implementation of a Url Generator, needed for backward compatibility.
  */
-class NullGenerator implements UrlGeneratorInterface {
+class NullGenerator extends UrlGenerator {
 
   /**
-   * Implements Symfony\Component\Routing\Generator\UrlGeneratorInterface::generate();
+   * Override the parent constructor.
+   */
+  public function __construct() {}
+
+  /**
+   * Overrides Drupal\Core\Routing\UrlGenerator::generate();
    */
   public function generate($name, $parameters = array(), $absolute = FALSE) {
     throw new RouteNotFoundException();
   }
 
   /**
-   * Implements Symfony\Component\Routing\RequestContextAwareInterface::setContext();
+   * Overrides Drupal\Core\Routing\UrlGenerator::setContext();
    */
   public function setContext(RequestContext $context) {
   }
@@ -33,4 +38,11 @@ public function setContext(RequestContext $context) {
    */
   public function getContext() {
   }
+
+  /**
+   * Overrides Drupal\Core\Routing\UrlGenerator::processPath().
+   */
+  protected function processPath($path, $options = array()) {
+    return $path;
+  }
 }
diff --git a/core/lib/Drupal/Core/Routing/PathBasedGeneratorInterface.php b/core/lib/Drupal/Core/Routing/PathBasedGeneratorInterface.php
new file mode 100644
index 0000000..16b258b
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/PathBasedGeneratorInterface.php
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Core\Routing\PathBasedGeneratorInterface.
+ */
+
+namespace Drupal\Core\Routing;
+
+
+/**
+ * Defines an interface for generating a url from a path as opposed to a route.
+ */
+interface PathBasedGeneratorInterface {
+
+  /**
+   * Generates an internal or external URL.
+   *
+   * @param $path
+   *   (optional) The internal path or external URL being linked to, such as
+   *   "node/34" or "http://example.com/foo". The default value is equivalent to
+   *   passing in '<front>'. A few notes:
+   *   - If you provide a full URL, it will be considered an external URL.
+   *   - If you provide only the path (e.g. "node/34"), it will be
+   *     considered an internal link. In this case, it should be a system URL,
+   *     and it will be replaced with the alias, if one exists. Additional query
+   *     arguments for internal paths must be supplied in $options['query'], not
+   *     included in $path.
+   *   - If you provide an internal path and $options['alias'] is set to TRUE, the
+   *     path is assumed already to be the correct path alias, and the alias is
+   *     not looked up.
+   *   - The special string '<front>' generates a link to the site's base URL.
+   *   - If your external URL contains a query (e.g. http://example.com/foo?a=b),
+   *     then you can either URL encode the query keys and values yourself and
+   *     include them in $path, or use $options['query'] to let this function
+   *     URL encode them.
+   * @param $options
+   *   (optional) An associative array of additional options, with the following
+   *   elements:
+   *   - 'query': An array of query key/value-pairs (without any URL-encoding) to
+   *     append to the URL.
+   *   - 'fragment': A fragment identifier (named anchor) to append to the URL.
+   *     Do not include the leading '#' character.
+   *   - 'absolute': Defaults to FALSE. Whether to force the output to be an
+   *     absolute link (beginning with http:). Useful for links that will be
+   *     displayed outside the site, such as in an RSS feed.
+   *   - 'alias': Defaults to FALSE. Whether the given path is a URL alias
+   *     already.
+   *   - 'external': Whether the given path is an external URL.
+   *   - 'language': An optional language object. If the path being linked to is
+   *     internal to the site, $options['language'] is used to look up the alias
+   *     for the URL. If $options['language'] is omitted, the language will be
+   *     obtained from language(LANGUAGE_TYPE_URL).
+   *   - 'https': Whether this URL should point to a secure location. If not
+   *     defined, the current scheme is used, so the user stays on HTTP or HTTPS
+   *     respectively. TRUE enforces HTTPS and FALSE enforces HTTP, but HTTPS can
+   *     only be enforced when the variable 'https' is set to TRUE.
+   *   - 'base_url': Only used internally, to modify the base URL when a language
+   *     dependent URL requires so.
+   *   - 'prefix': Only used internally, to modify the path when a language
+   *     dependent URL requires so.
+   *   - 'script': Added to the URL between the base path and the path prefix.
+   *     Defaults to empty string when clean URLs are in effect, and to
+   *     'index.php/' when they are not.
+   *   - 'entity_type': The entity type of the object that called url(). Only
+   *     set if url() is invoked by Drupal\Core\Entity\Entity::uri().
+   *   - 'entity': The entity object (such as a node) for which the URL is being
+   *     generated. Only set if url() is invoked by Drupal\Core\Entity\Entity::uri().
+   *
+   * @return
+   *   A string containing a URL to the given path.
+   */
+  public function generateFromPath($path = NULL, $options = array());
+
+}
diff --git a/core/lib/Drupal/Core/Routing/UrlGenerator.php b/core/lib/Drupal/Core/Routing/UrlGenerator.php
index 75779a2..0d32d79 100644
--- a/core/lib/Drupal/Core/Routing/UrlGenerator.php
+++ b/core/lib/Drupal/Core/Routing/UrlGenerator.php
@@ -12,19 +12,19 @@
 use Symfony\Cmf\Component\Routing\ProviderBasedGenerator;
 use Symfony\Cmf\Component\Routing\RouteProviderInterface;
 
-use Drupal\Core\Path\AliasManagerInterface;
+use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
 
 /**
  * A Generator creates URL strings based on a specified route.
  */
-class UrlGenerator extends ProviderBasedGenerator {
+class UrlGenerator extends ProviderBasedGenerator implements PathBasedGeneratorInterface {
 
   /**
-   * The alias manager that will be used to alias generated URLs.
+   * The path processor to convert the system path to one suitable for urls.
    *
-   * @var AliasManagerInterface
+   * @var \Drupal\Core\PathProcessor\OutboundPathProcessorInterface
    */
-  protected $aliasManager;
+  protected $pathProcessor;
 
   /**
    *  Constructs a new generator object.
@@ -36,23 +36,114 @@ class UrlGenerator extends ProviderBasedGenerator {
    * @param \Symfony\Component\HttpKernel\Log\LoggerInterface $logger
    *   An optional logger for recording errors.
    */
-  public function __construct(RouteProviderInterface $provider, AliasManagerInterface $alias_manager, LoggerInterface $logger = NULL) {
+  public function __construct(RouteProviderInterface $provider, OutboundPathProcessorInterface $path_processor, LoggerInterface $logger = NULL) {
     parent::__construct($provider, $logger);
 
-    $this->aliasManager = $alias_manager;
+    $this->pathProcessor = $path_processor;
   }
 
   /**
-   * Implements Symfony\Component\Routing\Generator\UrlGeneratorInterface::generate();
+   * Implements Symfony\Component\Routing\Generator\UrlGeneratorInterface::generate().
    */
   public function generate($name, $parameters = array(), $absolute = FALSE) {
     $path = parent::generate($name, $parameters, $absolute);
+    $path = $this->processPath($path);
 
-    // This method is expected to return a path with a leading /, whereas
-    // the alias manager has no leading /.
-    $path = '/' . $this->aliasManager->getPathAlias(trim($path, '/'));
+    return $path;
+  }
 
+  /**
+   * Passes the path to a processor manager to allow alterations.
+   */
+  protected function processPath($path, $options = array()) {
+    $path_parts = explode('?', $path);
+    $path = '/' . $this->pathProcessor->processOutbound(trim($path_parts[0], '/'), $options);
+    if (isset($path_parts[1])) {
+      $path .= '?' . $path_parts[1];
+    }
     return $path;
   }
 
+  /**
+   * Implements \Drupal\Core\Routing\PathBasedGeneratorInterface::generateFromPath().
+   */
+  public function generateFromPath($path = NULL, $options = array()) {
+    // Merge in defaults.
+    $options += array(
+      'fragment' => '',
+      'query' => array(),
+      'absolute' => FALSE,
+      'prefix' => '',
+      'script' => $GLOBALS['script_path'],
+    );
+
+    if (!isset($options['external'])) {
+      // Return an external link if $path contains an allowed absolute URL. Only
+      // call the slow drupal_strip_dangerous_protocols() if $path contains a ':'
+      // before any / ? or #. Note: we could use url_is_external($path) here, but
+      // that would require another function call, and performance inside url() is
+      // critical.
+      $colonpos = strpos($path, ':');
+      $options['external'] = ($colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && drupal_strip_dangerous_protocols($path) == $path);
+    }
+
+    if (isset($options['fragment']) && $options['fragment'] !== '') {
+      $options['fragment'] = '#' . $options['fragment'];
+    }
+
+    if ($options['external']) {
+      // Split off the fragment.
+      if (strpos($path, '#') !== FALSE) {
+        list($path, $old_fragment) = explode('#', $path, 2);
+        // If $options contains no fragment, take it over from the path.
+        if (isset($old_fragment) && !$options['fragment']) {
+          $options['fragment'] = '#' . $old_fragment;
+        }
+      }
+      // Append the query.
+      if ($options['query']) {
+        $path .= (strpos($path, '?') !== FALSE ? '&' : '?') . drupal_http_build_query($options['query']);
+      }
+      if (isset($options['https']) && variable_get('https', FALSE)) {
+        if ($options['https'] === TRUE) {
+          $path = str_replace('http://', 'https://', $path);
+        }
+        elseif ($options['https'] === FALSE) {
+          $path = str_replace('https://', 'http://', $path);
+        }
+      }
+      // Reassemble.
+      return $path . $options['fragment'];
+    }
+    else {
+      $path = ltrim($this->processPath($path, $options), '/');
+    }
+
+    global $base_url, $base_secure_url, $base_insecure_url;
+
+    // The base_url might be rewritten from the language rewrite in domain mode.
+    if (!isset($options['base_url'])) {
+      if (isset($options['https']) && variable_get('https', FALSE)) {
+        if ($options['https'] === TRUE) {
+          $options['base_url'] = $base_secure_url;
+          $options['absolute'] = TRUE;
+        }
+        elseif ($options['https'] === FALSE) {
+          $options['base_url'] = $base_insecure_url;
+          $options['absolute'] = TRUE;
+        }
+      }
+      else {
+        $options['base_url'] = $base_url;
+      }
+    }
+
+    $base = $options['absolute'] ? $options['base_url'] . '/' : base_path();
+    $prefix = empty($path) ? rtrim($options['prefix'], '/') : $options['prefix'];
+
+    $path = drupal_encode_path($prefix . $path);
+    $query = $options['query'] ? ('?' . drupal_http_build_query($options['query'])) : '';
+    return $base . $options['script'] . $path . $query . $options['fragment'];
+  }
+
 }
diff --git a/core/modules/language/language.negotiation.inc b/core/modules/language/language.negotiation.inc
index 9521247..d4cfdbb 100644
--- a/core/modules/language/language.negotiation.inc
+++ b/core/modules/language/language.negotiation.inc
@@ -282,11 +282,7 @@ function language_from_url($languages, Request $request = NULL) {
     case LANGUAGE_NEGOTIATION_URL_PREFIX:
 
       $request_path = urldecode(trim($request->getPathInfo(), '/'));
-
       list($language, $path) = language_url_split_prefix($request_path, $languages);
-      // Store the correct system path, i.e., the request path without the
-      // language prefix.
-      _language_resolved_path($path);
 
       if ($language !== FALSE) {
         $language_url = $language->langcode;
@@ -452,55 +448,45 @@ function language_url_rewrite_url(&$path, &$options) {
     return;
   }
 
-  if (isset($options['language'])) {
-    switch (config('language.negotiation')->get('url.source')) {
-      case LANGUAGE_NEGOTIATION_URL_DOMAIN:
-        $domains = language_negotiation_url_domains();
-        if (is_object($options['language']) && !empty($domains[$options['language']->langcode])) {
-          global $is_https;
+  if (isset($options['language']) && config('language.negotiation')->get('url.source') == LANGUAGE_NEGOTIATION_URL_DOMAIN) {
 
-          // Save the original base URL. If it contains a port, we need to
-          // retain it below.
-          if (!empty($options['base_url'])) {
-            // The colon in the URL scheme messes up the port checking below.
-            $normalized_base_url = str_replace(array('https://', 'http://'), '', $options['base_url']);
+    $domains = language_negotiation_url_domains();
+    if (is_object($options['language']) && !empty($domains[$options['language']->langcode])) {
+      global $is_https;
 
-          }
+      // Save the original base URL. If it contains a port, we need to
+      // retain it below.
+      if (!empty($options['base_url'])) {
+        // The colon in the URL scheme messes up the port checking below.
+        $normalized_base_url = str_replace(array('https://', 'http://'), '', $options['base_url']);
 
-          // Ask for an absolute URL with our modified base URL.
-          $url_scheme = ($is_https) ? 'https://' : 'http://';
-          $options['absolute'] = TRUE;
-          $options['base_url'] = $url_scheme . $domains[$options['language']->langcode];
-
-          // In case either the original base URL or the HTTP host contains a
-          // port, retain it.
-          $http_host = $_SERVER['HTTP_HOST'];
-          if (isset($normalized_base_url) && strpos($normalized_base_url, ':') !== FALSE) {
-            list($host, $port) = explode(':', $normalized_base_url);
-            $options['base_url'] .= ':' . $port;
-          }
-          elseif (strpos($http_host, ':') !== FALSE) {
-            list($host, $port) = explode(':', $http_host);
-            $options['base_url'] .= ':' . $port;
-          }
+      }
 
-          if (isset($options['https']) && variable_get('https', FALSE)) {
-            if ($options['https'] === TRUE) {
-              $options['base_url'] = str_replace('http://', 'https://', $options['base_url']);
-            }
-            elseif ($options['https'] === FALSE) {
-              $options['base_url'] = str_replace('https://', 'http://', $options['base_url']);
-            }
-          }
-        }
-        break;
+      // Ask for an absolute URL with our modified base URL.
+      $url_scheme = ($is_https) ? 'https://' : 'http://';
+      $options['absolute'] = TRUE;
+      $options['base_url'] = $url_scheme . $domains[$options['language']->langcode];
+
+      // In case either the original base URL or the HTTP host contains a
+      // port, retain it.
+      $http_host = $_SERVER['HTTP_HOST'];
+      if (isset($normalized_base_url) && strpos($normalized_base_url, ':') !== FALSE) {
+        list($host, $port) = explode(':', $normalized_base_url);
+        $options['base_url'] .= ':' . $port;
+      }
+      elseif (strpos($http_host, ':') !== FALSE) {
+        list($host, $port) = explode(':', $http_host);
+        $options['base_url'] .= ':' . $port;
+      }
 
-      case LANGUAGE_NEGOTIATION_URL_PREFIX:
-        $prefixes = language_negotiation_url_prefixes();
-        if (is_object($options['language']) &&!empty($prefixes[$options['language']->langcode])) {
-          $options['prefix'] = $prefixes[$options['language']->langcode] . '/';
+      if (isset($options['https']) && variable_get('https', FALSE)) {
+        if ($options['https'] === TRUE) {
+          $options['base_url'] = str_replace('http://', 'https://', $options['base_url']);
         }
-        break;
+        elseif ($options['https'] === FALSE) {
+          $options['base_url'] = str_replace('https://', 'http://', $options['base_url']);
+        }
+      }
     }
   }
 }
diff --git a/core/modules/language/lib/Drupal/language/HttpKernel/PathProcessorLanguage.php b/core/modules/language/lib/Drupal/language/HttpKernel/PathProcessorLanguage.php
new file mode 100644
index 0000000..8287571
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/HttpKernel/PathProcessorLanguage.php
@@ -0,0 +1,86 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\language\HttpKernel\PathProcessorLanguage.
+ */
+
+namespace Drupal\language\HttpKernel;
+
+use Drupal\Core\Config\ConfigFactory;
+use Drupal\Core\Language\LanguageManager;
+use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
+use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Processes the inbound path using path alias lookups.
+ */
+class PathProcessorLanguage implements InboundPathProcessorInterface, OutboundPathProcessorInterface {
+
+  /**
+   * A config factory for retrieving required config settings.
+   *
+   * @var \Drupal\Core\Config\ConfigFactory
+   */
+  protected $config;
+
+  /**
+   * Language manager for retrieving the url language type.
+   *
+   * @var \Drupal\Core\Language\LanguageManager
+   */
+  protected $languageManager;
+
+  public function __construct(ConfigFactory $config, LanguageManager $language_manager) {
+    $this->config = $config;
+    $this->languageManager = $language_manager;
+    $this->languages = language_list();
+  }
+
+  /**
+   * Implements Drupal\Core\PathProcessor\InboundPathProcessorInterface::processInbound().
+   */
+  public function processInbound($path, Request $request) {
+    if (!empty($path)) {
+      $args = explode('/', $path);
+      $prefix = array_shift($args);
+
+      // Search prefix within enabled languages.
+      $prefixes = $this->config->get('language.negotiation')->get('url.prefixes');
+      foreach ($this->languages as $language) {
+        if (isset($prefixes[$language->langcode]) && $prefixes[$language->langcode] == $prefix) {
+          // Rebuild $path with the language removed.
+          return implode('/', $args);
+        }
+      }
+    }
+    return $path;
+  }
+
+  /**
+   * Implements Drupal\Core\PathProcessor\InboundPathProcessorInterface::processOutbound().
+   */
+  public function processOutbound($path, $options = array()) {
+    $languages = array_flip(array_keys($this->languages));
+    // @todo Go back to using a constant instead of the string 'path_prefix' once we can use a class
+    //   constant.
+    if ($this->config->get('language.negotiation')->get('url.source') == 'path_prefix') {
+      // Language can be passed as an option, or we go for current URL language.
+      if (!isset($options['language'])) {
+        $language_url = $this->languageManager->getLanguage(LANGUAGE_TYPE_URL);
+        $options['language'] = $language_url;
+      }
+      // We allow only enabled languages here.
+      elseif (is_object($options['language']) && !isset($languages[$options['language']->langcode])) {
+        return $path;
+      }
+      $prefixes = $this->config->get('language.negotiation')->get('url.prefixes');
+      if (is_object($options['language']) && !empty($prefixes[$options['language']->langcode])) {
+        return $prefixes[$options['language']->langcode] . '/' . $path;
+      }
+    }
+    return $path;
+  }
+}
diff --git a/core/modules/language/lib/Drupal/language/LanguageBundle.php b/core/modules/language/lib/Drupal/language/LanguageBundle.php
new file mode 100644
index 0000000..a0a76f5
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/LanguageBundle.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\language\LanguageBundle.
+ */
+
+namespace Drupal\language;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\HttpKernel\Bundle\Bundle;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * language dependency injection container.
+ */
+class LanguageBundle extends Bundle {
+
+  /**
+   * Overrides Symfony\Component\HttpKernel\Bundle\Bundle::build().
+   */
+  public function build(ContainerBuilder $container) {
+    // Register the language-based path processor.
+    $container->register('path_processor_language', 'Drupal\language\HttpKernel\PathProcessorLanguage')
+      ->addArgument(new Reference('config.factory'))
+      ->addArgument(new Reference('language_manager'))
+      ->addTag('path_processor_inbound', array('priority' => 300))
+      ->addTag('path_processor_outbound', array('priority' => 100));
+  }
+
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Path/AliasTest.php b/core/modules/system/lib/Drupal/system/Tests/Path/AliasTest.php
index 275e1c4..5dc3158 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Path/AliasTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Path/AliasTest.php
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Definition of Drupal\system\Tests\Path\CrudTest.
+ * Contains Drupal\system\Tests\Path\AliasTest.
  */
 
 namespace Drupal\system\Tests\Path;
@@ -15,7 +15,7 @@
 /**
  * Tests path alias CRUD and lookup functionality.
  */
-class AliasTest extends DrupalUnitTestBase {
+class AliasTest extends PathUnitTestBase {
 
   public static function getInfo() {
     return array(
@@ -25,18 +25,6 @@ public static function getInfo() {
     );
   }
 
-  public function setUp() {
-    parent::setUp();
-    $this->fixtures = new UrlAliasFixtures();
-  }
-
-  public function tearDown() {
-    $this->fixtures->dropTables(Database::getConnection());
-
-    parent::tearDown();
-  }
-
-
   function testCRUD() {
     //Prepare database table.
     $connection = Database::getConnection();
diff --git a/core/modules/system/lib/Drupal/system/Tests/Path/PathUnitTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Path/PathUnitTestBase.php
new file mode 100644
index 0000000..c24506d
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Path/PathUnitTestBase.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\system\Tests\Path\PathUnitTestBase.
+ */
+
+namespace Drupal\system\Tests\Path;
+
+use Drupal\simpletest\DrupalUnitTestBase;
+use Drupal\Core\Database\Database;
+
+/**
+ * Defines a base class for path unit testing.
+ */
+class PathUnitTestBase extends DrupalUnitTestBase {
+
+  public function setUp() {
+    parent::setUp();
+    $this->fixtures = new UrlAliasFixtures();
+  }
+
+  public function tearDown() {
+    $this->fixtures->dropTables(Database::getConnection());
+
+    parent::tearDown();
+  }
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Path/UrlAliasFixtures.php b/core/modules/system/lib/Drupal/system/Tests/Path/UrlAliasFixtures.php
index 6fb02da..a51e3bf 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Path/UrlAliasFixtures.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Path/UrlAliasFixtures.php
@@ -16,7 +16,7 @@ class UrlAliasFixtures {
    *   The connection to use to create the tables.
    */
   public function createTables(Connection $connection) {
-    $tables = $this->urlAliasTableDefinition();
+    $tables = $this->tableDefinition();
     $schema = $connection->schema();
 
     foreach ($tables as $name => $table) {
@@ -32,7 +32,7 @@ public function createTables(Connection $connection) {
    *   The connection to use to drop the tables.
    */
   public function dropTables(Connection $connection) {
-    $tables = $this->urlAliasTableDefinition();
+    $tables = $this->tableDefinition();
     $schema = $connection->schema();
 
     foreach ($tables as $name => $table) {
@@ -77,7 +77,7 @@ public function sampleUrlAliases() {
    * @return array
    *   Table definitions.
    */
-  public function urlAliasTableDefinition() {
+  public function tableDefinition() {
     $tables = array();
 
     module_load_install('system');
diff --git a/core/modules/system/lib/Drupal/system/Tests/PathProcessor/PathProcessorFixtures.php b/core/modules/system/lib/Drupal/system/Tests/PathProcessor/PathProcessorFixtures.php
new file mode 100644
index 0000000..dbc987d
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/PathProcessor/PathProcessorFixtures.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace Drupal\system\Tests\PathProcessor;
+
+use Drupal\Core\Database\Connection;
+use Drupal\system\Tests\Path\UrlAliasFixtures;
+
+/**
+ * Utility methods to provide necessary database tables for tests.
+ */
+class PathProcessorFixtures extends UrlAliasFixtures {
+
+  /**
+   * Overrides Drupal\system\Tests\Path\UrlAliasFixtures::tableDefinition() .
+   */
+  public function tableDefinition() {
+    // In addition to the tables added by the parent method, we also need the
+    // language and variable tables for the path processor tests.
+    $tables = parent::tableDefinition();
+    $schema = system_schema();
+    $tables['variable'] = $schema['variable'];
+    module_load_install('language');
+    $schema = language_schema();
+    $tables['language'] = $schema['language'];
+    return $tables;
+  }
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/PathProcessor/PathProcessorTest.php b/core/modules/system/lib/Drupal/system/Tests/PathProcessor/PathProcessorTest.php
new file mode 100644
index 0000000..901ba81
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/PathProcessor/PathProcessorTest.php
@@ -0,0 +1,126 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\system\Tests\PathProcessor\PathProcessorTest.
+ */
+
+namespace Drupal\system\Tests\PathProcessor;
+
+use Drupal\system\Tests\Path\PathUnitTestBase;
+use Drupal\Core\Database\Database;
+use Drupal\Core\Path\Path;
+use Drupal\Core\Path\AliasManager;
+use Drupal\Core\PathProcessor\PathProcessorAlias;
+use Drupal\Core\PathProcessor\PathProcessorDecode;
+use Drupal\Core\PathProcessor\PathProcessorFront;
+use Drupal\Core\PathProcessor\PathProcessorManager;
+use Drupal\language\HttpKernel\PathProcessorLanguage;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Tests path processor functionality.
+ */
+class PathProcessorTest extends PathUnitTestBase {
+
+  public static function getInfo() {
+    return array(
+      'name' => t('Path Processor Unit Tests'),
+      'description' => t('Tests processing of the inbound path.'),
+      'group' => t('Path API'),
+    );
+  }
+
+  public function setUp() {
+    parent::setUp();
+    $this->fixtures = new PathProcessorFixtures();
+  }
+
+  /**
+   * Tests resolving the inbound path to the system path.
+   */
+  function testProcessInbound() {
+
+    // Ensure all tables needed for these tests are created.
+    $connection = Database::getConnection();
+    $this->fixtures->createTables($connection);
+
+    // Create dependecies needed by various path processors.
+    $language_manager = $this->container->get('language_manager');
+    $alias_manager = new AliasManager($connection, $this->container->get('state'), $language_manager);
+    $module_handler = $this->container->get('module_handler');
+    $config = $this->container->get('config.factory');
+
+    // Add a url alias for testing the alias-based processor.
+    $path_crud = new Path($connection, $alias_manager);
+    $path_crud->save('user/1', 'foo');
+
+    // Add a language for testing the language-based processor.
+    $module_handler->setModuleList(array('language' => 'core/modules/language/language.module'));
+    $module_handler->load('language');
+    $language = new \stdClass();
+    $language->langcode = 'fr';
+    $language->name = 'French';
+    language_save($language);
+
+    // Create the processors.
+    $alias_processor = new PathProcessorAlias($alias_manager);
+    $decode_processor = new PathProcessorDecode();
+    $front_processor = new PathProcessorFront($config);
+    $language_processor = new PathProcessorLanguage($config, $language_manager);
+
+    // First, test the processor manager with the processors in the incorrect
+    // order. The alias processor will run before the language processor, meaning
+    // aliases will not be found.
+    $priorities = array(
+      1000 => $alias_processor,
+      500 => $decode_processor,
+      300 => $front_processor,
+      200 => $language_processor,
+    );
+
+    // Create the processor manager and add the processors.
+    $processor_manager = new PathProcessorManager();
+    foreach ($priorities as $priority => $processor) {
+      $processor_manager->addInbound($processor, $priority);
+    }
+
+    // Test resolving the French homepage using the incorrect processor order.
+    $test_path = 'fr';
+    $request = Request::create($test_path);
+    $processed = $processor_manager->processInbound($test_path, $request);
+    $this->assertEqual($processed, '', 'Processing in the incorrect order fails to resolve the system path from the empty path');
+
+    // Test resolving an existing alias using the incorrect processor order.
+    $test_path = 'fr/foo';
+    $request = Request::create($test_path);
+    $processed = $processor_manager->processInbound($test_path, $request);
+    $this->assertEqual($processed, 'foo', 'Processing in the incorrect order fails to resolve the system path from an alias');
+
+    // Now create a new processor manager and add the processors, this time in
+    // the correct order.
+    $processor_manager = new PathProcessorManager();
+    $priorities = array(
+      1000 => $decode_processor,
+      500 => $language_processor,
+      300 => $front_processor,
+      200 => $alias_processor,
+    );
+    foreach ($priorities as $priority => $processor) {
+      $processor_manager->addInbound($processor, $priority);
+    }
+
+    // Test resolving the French homepage using the correct processor order.
+    $test_path = 'fr';
+    $request = Request::create($test_path);
+    $processed = $processor_manager->processInbound($test_path, $request);
+    $this->assertEqual($processed, 'user', 'Processing in the correct order resolves the system path from the empty path.');
+
+    // Test resolving an existing alias using the correct processor order.
+    $test_path = 'fr/foo';
+    $request = Request::create($test_path);
+    $processed = $processor_manager->processInbound($test_path, $request);
+    $this->assertEqual($processed, 'user/1', 'Processing in the correct order resolves the system path from an alias.');
+  }
+
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/UrlGeneratorTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/UrlGeneratorTest.php
index 0e9f13e..5ca0db6 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Routing/UrlGeneratorTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Routing/UrlGeneratorTest.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\system\Tests\Routing;
 
+use Drupal\Core\PathProcessor\PathProcessorAlias;
+use Drupal\Core\PathProcessor\PathProcessorManager;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\Routing\Route;
 use Symfony\Component\Routing\RouteCollection;
@@ -47,7 +49,10 @@ function setUp() {
     $context = new RequestContext();
     $context->fromRequest(Request::create('/some/path'));
 
-    $generator = new UrlGenerator($provider, $this->aliasManager);
+    $processor = new PathProcessorAlias($this->aliasManager);
+    $processor_manager = new PathProcessorManager();
+    $processor_manager->addOutbound($processor, 1000);
+    $generator = new UrlGenerator($provider, $processor_manager);
     $generator->setContext($context);
 
     $this->generator = $generator;
@@ -67,10 +72,8 @@ public function testAliasGeneration() {
    */
   public function testAliasGenerationWithParameters() {
     $this->aliasManager->addAlias('test/two/5', 'goodbye/cruel/world');
-
     $url = $this->generator->generate('test_2', array('narf' => '5'));
-
-    $this->assertEqual($url, '/goodbye/cruel/world', 'Correct URL generated including alias and parameters.');
+    $this->assertEqual($url, '/goodbye/cruel/world?other=stuff', 'Correct URL generated including alias and parameters.');
   }
 
 }
