diff --git a/core/lib/Drupal/Core/Routing/UrlGenerator.php b/core/lib/Drupal/Core/Routing/UrlGenerator.php
index d990e40..5b59755 100644
--- a/core/lib/Drupal/Core/Routing/UrlGenerator.php
+++ b/core/lib/Drupal/Core/Routing/UrlGenerator.php
@@ -310,6 +310,7 @@ public function generateFromRoute($name, $parameters = array(), $options = array
     $name = $this->getRouteDebugMessage($name);
     $this->processRoute($name, $route, $parameters, $generated_url);
     $path = $this->getInternalPathFromRoute($name, $route, $parameters, $query_params);
+    $options['route_name'] = $name;
     $path = $this->processPath($path, $options, $generated_url);
 
     if (!empty($options['prefix'])) {
diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module
index 0370367..9dae374 100644
--- a/core/modules/content_translation/content_translation.module
+++ b/core/modules/content_translation/content_translation.module
@@ -53,7 +53,7 @@ function content_translation_help($route_name, RouteMatchInterface $route_match)
  */
 function content_translation_module_implements_alter(&$implementations, $hook) {
   switch ($hook) {
-    // Move some of our hook implementations to the end of the list.
+    // Move our hook_entity_type_alter() implementation to the end of the list.
     case 'entity_type_alter':
       $group = $implementations['content_translation'];
       unset($implementations['content_translation']);
@@ -140,7 +140,11 @@ function content_translation_entity_type_alter(array &$entity_types) {
       if ($entity_type->hasLinkTemplate('canonical')) {
         // Provide default route names for the translation paths.
         if (!$entity_type->hasLinkTemplate('drupal:content-translation-overview')) {
-          $entity_type->setLinkTemplate('drupal:content-translation-overview', $entity_type->getLinkTemplate('canonical') . '/translations');
+          $translations_path = $entity_type->getLinkTemplate('canonical') . '/translations';
+          $entity_type->setLinkTemplate('drupal:content-translation-overview', $translations_path);
+          $entity_type->setLinkTemplate('drupal:content-translation-add', $translations_path . '/add/{source}/{target}');
+          $entity_type->setLinkTemplate('drupal:content-translation-edit', $translations_path . '/edit/{language}');
+          $entity_type->setLinkTemplate('drupal:content-translation-delete', $translations_path . '/delete/{language}');
         }
         // @todo Remove this as soon as menu access checks rely on the
         //   controller. See https://www.drupal.org/node/2155787.
diff --git a/core/modules/content_translation/src/ContentTranslationHandler.php b/core/modules/content_translation/src/ContentTranslationHandler.php
index d7eb42a..1b8d77d 100644
--- a/core/modules/content_translation/src/ContentTranslationHandler.php
+++ b/core/modules/content_translation/src/ContentTranslationHandler.php
@@ -652,7 +652,7 @@ public function entityFormSourceChange($form, FormStateInterface $form_state) {
     $source = $form_state->getValue(array('source_langcode', 'source'));
 
     $entity_type_id = $entity->getEntityTypeId();
-    $form_state->setRedirect('content_translation.translation_add_' . $entity_type_id, array(
+    $form_state->setRedirect("entity.$entity_type_id.content_translation_add", array(
       $entity_type_id => $entity->id(),
       'source' => $source,
       'target' => $form_object->getFormLangcode($form_state),
@@ -689,7 +689,7 @@ function entityFormDeleteTranslation($form, FormStateInterface $form_state) {
       $form_state->setRedirectUrl($entity->urlInfo('delete-form'));
     }
     else {
-      $form_state->setRedirect('content_translation.translation_delete_' . $entity_type_id, [
+      $form_state->setRedirect("entity.$entity_type_id.content_translation_delete", [
         $entity_type_id => $entity->id(),
         'language' => $form_object->getFormLangcode($form_state),
       ]);
diff --git a/core/modules/content_translation/src/Controller/ContentTranslationController.php b/core/modules/content_translation/src/Controller/ContentTranslationController.php
index 9c6879a..8c19de1 100644
--- a/core/modules/content_translation/src/Controller/ContentTranslationController.php
+++ b/core/modules/content_translation/src/Controller/ContentTranslationController.php
@@ -127,7 +127,7 @@ public function overview(RouteMatchInterface $route_match, $entity_type_id = NUL
         $langcode = $language->getId();
 
         $add_url = new Url(
-          'content_translation.translation_add_' . $entity_type_id,
+          "entity.$entity_type_id.content_translation_add",
           array(
             'source' => $original,
             'target' => $language->getId(),
@@ -138,7 +138,7 @@ public function overview(RouteMatchInterface $route_match, $entity_type_id = NUL
           )
         );
         $edit_url = new Url(
-          'content_translation.translation_edit_' . $entity_type_id,
+          "entity.$entity_type_id.content_translation_edit",
           array(
             'language' => $language->getId(),
             $entity_type_id => $entity->id(),
@@ -148,7 +148,7 @@ public function overview(RouteMatchInterface $route_match, $entity_type_id = NUL
           )
         );
         $delete_url = new Url(
-          'content_translation.translation_delete_' . $entity_type_id,
+          "entity.$entity_type_id.content_translation_delete",
           array(
             'language' => $language->getId(),
             $entity_type_id => $entity->id(),
diff --git a/core/modules/content_translation/src/Routing/ContentTranslationRouteSubscriber.php b/core/modules/content_translation/src/Routing/ContentTranslationRouteSubscriber.php
index 91d4314..92eea52 100644
--- a/core/modules/content_translation/src/Routing/ContentTranslationRouteSubscriber.php
+++ b/core/modules/content_translation/src/Routing/ContentTranslationRouteSubscriber.php
@@ -113,7 +113,7 @@ protected function alterRoutes(RouteCollection $collection) {
           '_admin_route' => $is_admin,
         )
       );
-      $collection->add("content_translation.translation_add_$entity_type_id", $route);
+      $collection->add("entity.$entity_type_id.content_translation_add", $route);
 
       $route = new Route(
         $path . '/edit/{language}',
@@ -138,7 +138,7 @@ protected function alterRoutes(RouteCollection $collection) {
           '_admin_route' => $is_admin,
         )
       );
-      $collection->add("content_translation.translation_edit_$entity_type_id", $route);
+      $collection->add("entity.$entity_type_id.content_translation_edit", $route);
 
       $route = new Route(
         $path . '/delete/{language}',
@@ -163,7 +163,7 @@ protected function alterRoutes(RouteCollection $collection) {
           '_admin_route' => $is_admin,
         )
       );
-      $collection->add("content_translation.translation_delete_$entity_type_id", $route);
+      $collection->add("entity.$entity_type_id.content_translation_delete", $route);
     }
   }
 
diff --git a/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php b/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php
index de8c349..5b734ad 100644
--- a/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php
+++ b/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php
@@ -107,7 +107,8 @@ protected function doTestBasicTranslation() {
     $language = ConfigurableLanguage::load($langcode);
     $values[$langcode] = $this->getNewEntityValues($langcode);
 
-    $add_url = Url::fromRoute('content_translation.translation_add_' . $entity->getEntityTypeId(), [
+    $entity_type_id = $entity->getEntityTypeId();
+    $add_url = Url::fromRoute("entity.$entity_type_id.content_translation_add", [
       $entity->getEntityTypeId() => $entity->id(),
       'source' => $default_langcode,
       'target' => $langcode
@@ -167,7 +168,8 @@ protected function doTestBasicTranslation() {
     $language = ConfigurableLanguage::load($langcode);
     $source_langcode = 'it';
     $edit = array('source_langcode[source]' => $source_langcode);
-    $add_url = Url::fromRoute('content_translation.translation_add_' . $entity->getEntityTypeId(), [
+    $entity_type_id =  $entity->getEntityTypeId();
+    $add_url = Url::fromRoute("entity.$entity_type_id.content_translation_add", [
       $entity->getEntityTypeId() => $entity->id(),
       'source' => $default_langcode,
       'target' => $langcode
@@ -180,7 +182,8 @@ protected function doTestBasicTranslation() {
     // Add another translation and mark the other ones as outdated.
     $values[$langcode] = $this->getNewEntityValues($langcode);
     $edit = $this->getEditValues($values, $langcode) + array('content_translation[retranslate]' => TRUE);
-    $add_url = Url::fromRoute('content_translation.translation_add_' . $entity->getEntityTypeId(), [
+    $entity_type_id = $entity->getEntityTypeId();
+    $add_url = Url::fromRoute("entity.$entity_type_id.content_translation_add", [
       $entity->getEntityTypeId() => $entity->id(),
       'source' => $source_langcode,
       'target' => $langcode
@@ -207,13 +210,15 @@ protected function doTestBasicTranslation() {
    */
   protected function doTestTranslationOverview() {
     $entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
-    $this->drupalGet($entity->urlInfo('drupal:content-translation-overview'));
+    $translate_url = $entity->urlInfo('drupal:content-translation-overview');
+    $this->drupalGet($translate_url);
+    $translate_url->setAbsolute(FALSE);
 
     foreach ($this->langcodes as $langcode) {
       if ($entity->hasTranslation($langcode)) {
         $language = new Language(array('id' => $langcode));
-        $view_path = $entity->url('canonical', array('language' => $language));
-        $elements = $this->xpath('//table//a[@href=:href]', array(':href' => $view_path));
+        $view_url = $entity->url('canonical', ['language' => $language]);
+        $elements = $this->xpath('//table//a[@href=:href]', [':href' => $view_url]);
         $this->assertEqual((string) $elements[0], $entity->getTranslation($langcode)->label(), format_string('Label correctly shown for %language translation.', array('%language' => $langcode)));
         $edit_path = $entity->url('edit-form', array('language' => $language));
         $elements = $this->xpath('//table//ul[@class="dropbutton"]/li/a[@href=:href]', array(':href' => $edit_path));
@@ -343,7 +348,7 @@ protected function doTestTranslationDeletion() {
 
     // Check that the translator cannot delete the original translation.
     $args = [$this->entityTypeId => $entity->id(), 'language' => 'en'];
-    $this->drupalGet(Url::fromRoute('content_translation.translation_delete_' . $this->entityTypeId, $args));
+    $this->drupalGet(Url::fromRoute("entity.$this->entityTypeId.content_translation_delete", $args));
     $this->assertResponse(403);
   }
 
diff --git a/core/modules/content_translation/src/Tests/ContentTranslationWorkflowsTest.php b/core/modules/content_translation/src/Tests/ContentTranslationWorkflowsTest.php
index b602be0..1216c0c 100644
--- a/core/modules/content_translation/src/Tests/ContentTranslationWorkflowsTest.php
+++ b/core/modules/content_translation/src/Tests/ContentTranslationWorkflowsTest.php
@@ -73,7 +73,7 @@ protected function setupEntity() {
 
     // Create a translation.
     $this->drupalLogin($this->translator);
-    $add_translation_url = Url::fromRoute('content_translation.translation_add_' . $this->entityTypeId, [$this->entityTypeId => $this->entity->id(), 'source' => $default_langcode, 'target' => $this->langcodes[2]]);
+    $add_translation_url = Url::fromRoute("entity.$this->entityTypeId.content_translation_add", [$this->entityTypeId => $this->entity->id(), 'source' => $default_langcode, 'target' => $this->langcodes[2]]);
     $this->drupalPostForm($add_translation_url, array(), t('Save'));
     $this->rebuildContainer();
   }
@@ -175,7 +175,7 @@ protected function doTestWorkflows(UserInterface $user, $expected_status) {
     $this->assertResponse($expected_status['overview'], SafeMarkup::format('The @user_label has the expected translation overview access.', $args));
 
     // Check whether the user is allowed to create a translation.
-    $add_translation_url = Url::fromRoute('content_translation.translation_add_' . $this->entityTypeId, [$this->entityTypeId => $this->entity->id(), 'source' => $default_langcode, 'target' => $langcode], $options);
+    $add_translation_url = Url::fromRoute("entity.$this->entityTypeId.content_translation_add", [$this->entityTypeId => $this->entity->id(), 'source' => $default_langcode, 'target' => $langcode], $options);
     if ($expected_status['add_translation'] == 200) {
       $this->clickLink('Add');
       $this->assertUrl($add_translation_url->toString(), [], 'The translation overview points to the translation form when creating translations.');
@@ -193,7 +193,7 @@ protected function doTestWorkflows(UserInterface $user, $expected_status) {
     // Check whether the user is allowed to edit a translation.
     $langcode = $this->langcodes[2];
     $options['language'] = $languages[$langcode];
-    $edit_translation_url = Url::fromRoute('content_translation.translation_edit_' . $this->entityTypeId, [$this->entityTypeId => $this->entity->id(), 'language' => $langcode], $options);
+    $edit_translation_url = Url::fromRoute("entity.$this->entityTypeId.content_translation_edit", [$this->entityTypeId => $this->entity->id(), 'language' => $langcode], $options);
     if ($expected_status['edit_translation'] == 200) {
       $this->drupalGet($translations_url);
       $editor = $expected_status['edit'] == 200;
@@ -221,7 +221,7 @@ protected function doTestWorkflows(UserInterface $user, $expected_status) {
     // Check whether the user is allowed to delete a translation.
     $langcode = $this->langcodes[2];
     $options['language'] = $languages[$langcode];
-    $delete_translation_url = Url::fromRoute('content_translation.translation_delete_' . $this->entityTypeId, [$this->entityTypeId => $this->entity->id(), 'language' => $langcode], $options);
+    $delete_translation_url = Url::fromRoute("entity.$this->entityTypeId.content_translation_delete", [$this->entityTypeId => $this->entity->id(), 'language' => $langcode], $options);
     if ($expected_status['delete_translation'] == 200) {
       $this->drupalGet($translations_url);
       $editor = $expected_status['delete'] == 200;
diff --git a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationContentEntity.php b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationContentEntity.php
new file mode 100644
index 0000000..4e2f458
--- /dev/null
+++ b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationContentEntity.php
@@ -0,0 +1,308 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationContentEntity.
+ */
+
+namespace Drupal\language\Plugin\LanguageNegotiation;
+
+use Drupal\Component\Utility\UrlHelper;
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Render\BubbleableMetadata;
+use Drupal\Core\Url;
+use Drupal\language\LanguageNegotiationMethodBase;
+use Drupal\language\LanguageSwitcherInterface;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Drupal\Core\Routing\RouteProviderInterface;
+use Drupal\Core\Entity\ContentEntityInterface;
+
+/**
+ * Class for identifying the content translation language.
+ *
+ * @LanguageNegotiation(
+ *   id = Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationContentEntity::METHOD_ID,
+ *   types = {Drupal\Core\Language\LanguageInterface::TYPE_CONTENT},
+ *   weight = -9,
+ *   name = @Translation("Content language"),
+ *   description = @Translation("Determines the translation language from a request parameter."),
+ * )
+ */
+class LanguageNegotiationContentEntity extends LanguageNegotiationMethodBase implements OutboundPathProcessorInterface, LanguageSwitcherInterface, ContainerFactoryPluginInterface {
+
+  /**
+   * The language negotiation method id.
+   */
+  const METHOD_ID = 'language-content-entity';
+
+  /**
+   * The query string parameter.
+   */
+  const QUERY_PARAMETER = 'language_content_entity';
+
+  /**
+   * A list of all the link paths of enabled content entities.
+   *
+   * @var array
+   */
+  protected $contentEntityPaths;
+
+  /**
+   * Checks criteria for outbound processing.
+   *
+   * @var bool
+   */
+  protected $meetsConfigurationCondition;
+
+  /**
+   * The route provider.
+   *
+   * @var \Symfony\Cmf\Component\Routing\RouteProviderInterface;
+   */
+  protected $routeProvider;
+
+  /**
+   * The entity manager
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface
+   */
+  protected $entityManager;
+
+  /**
+   * Static cache of outbound routes per request.
+   *
+   * @var \SplObjectStorage
+   */
+  protected $paths;
+
+  /**
+   * Constructs a new LanguageNegotiationContentEntity instance.
+   *
+   * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
+   *   The route provider.
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager.
+   */
+  public function __construct(RouteProviderInterface $route_provider, EntityManagerInterface $entity_manager) {
+    $this->routeProvider = $route_provider;
+    $this->entityManager = $entity_manager;
+    $this->paths = new \SplObjectStorage();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static($container->get('router.route_provider'), $container->get('entity.manager'));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLangcode(Request $request = NULL) {
+    $langcode = $request->get(static::QUERY_PARAMETER);
+
+    $language_enabled = array_key_exists($langcode, $this->languageManager->getLanguages());
+    return $language_enabled ? $langcode : NULL;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function processOutbound($path, &$options = [], Request $request = NULL, BubbleableMetadata $bubbleable_metadata = NULL) {
+    // Check if processing conditions are met.
+    if (!($request && isset($options['route_name']) && ($outbound_route_name = $options['route_name']) && $this->meetsConfigurationCondition() && $this->meetsContentEntityRoutesCondition($outbound_route_name, $request))) {
+      return $path;
+    }
+
+    if (isset($options['language']) || $langcode = $this->getLangcode($request)) {
+      // If the language option is set, unset it, so that the url language
+      // negotiator does not rewrite the url.
+      if (isset($options['language'])) {
+        $langcode = $options['language']->getId();
+        unset($options['language']);
+      }
+
+      if (isset($options['query']) && is_string($options['query'])) {
+        $query = [];
+        parse_str($options['query'], $query);
+        $options['query'] = $query;
+      }
+      else {
+        $options['query'] = [];
+      }
+
+      if (!isset($options['query'][static::QUERY_PARAMETER])) {
+        $query_addon = [static::QUERY_PARAMETER => $langcode];
+        $options['query'] += $query_addon;
+        // @todo Remove this once https://www.drupal.org/node/2507005 lands.
+        $path .= (strpos($path, '?') !== FALSE ? '&' : '?') . UrlHelper::buildQuery($query_addon);
+      }
+
+      if ($bubbleable_metadata) {
+        // Cached URLs that have been processed by this outbound path
+        // processor must be:
+        $bubbleable_metadata
+          // - varied by the content language query parameter.
+          ->addCacheContexts(['url.query_args:' . static::QUERY_PARAMETER]);
+      }
+    }
+
+    return $path;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLanguageSwitchLinks(Request $request, $type, Url $url) {
+    $links = [];
+    $query = [];
+    parse_str($request->getQueryString(), $query);
+
+    foreach ($this->languageManager->getNativeLanguages() as $language) {
+      $langcode = $language->getId();
+      $query[static::QUERY_PARAMETER] = $langcode;
+      $links[$langcode] = [
+        'url' => $url,
+        'title' => $language->getName(),
+        'attributes' => ['class' => ['language-link']],
+        'query' => $query,
+      ];
+    }
+
+    return $links;
+  }
+
+  /**
+   * Determines the execution permission based on the current configuration.
+   *
+   * Requirement: the content entity language negotiator has higher priority
+   * than the url language negotiator.
+   *
+   * @return bool
+   *   TRUE if the configuration condition is met, FALSE otherwise.
+   */
+  protected function meetsConfigurationCondition() {
+    if (!isset($this->meetsConfigurationCondition)) {
+      // Only run if the LanguageNegotiationContentEntity outbound function is
+      // being executed before the outbound function of LanguageNegotiationUrl.
+      $enabled_methods_content = $this->config->get('language.types')->get('negotiation.language_content.enabled') ? : [];
+
+      // Check if the content language is configured to be dependent on the
+      // url negotiator directly or indirectly over the interface negotiator.
+      $check_interface_method = FALSE;
+      if (isset($enabled_methods_content[LanguageNegotiationUI::METHOD_ID])) {
+        $enabled_methods_interface = $this->config->get('language.types')->get('negotiation.language_interface.enabled') ? : [];
+        $check_interface_method = isset($enabled_methods_interface[LanguageNegotiationUrl::METHOD_ID]);
+      }
+      if ($check_interface_method) {
+        $check_against_weight = $enabled_methods_content[LanguageNegotiationUI::METHOD_ID];
+        $check_against_weight = isset($enabled_methods_content[LanguageNegotiationUrl::METHOD_ID]) ? max($check_against_weight, $enabled_methods_content[LanguageNegotiationUrl::METHOD_ID]) : $check_against_weight;
+      }
+      else {
+        $check_against_weight = isset($enabled_methods_content[LanguageNegotiationUrl::METHOD_ID]) ? $enabled_methods_content[LanguageNegotiationUrl::METHOD_ID] : PHP_INT_MAX;
+      }
+
+      $this->meetsConfigurationCondition = $enabled_methods_content[static::METHOD_ID] < $check_against_weight;
+    }
+
+    return $this->meetsConfigurationCondition;
+  }
+
+  /**
+   * Determines if content entity route condition is met.
+   *
+   * Requirements: currently being on an content entity route and processing
+   * outbound url pointing to the same content entity.
+   *
+   * @param string $outbound_route_name
+   *   The route name for the current outbound url being processed.
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The HttpRequest object representing the current request.
+   *
+   * @return bool
+   *   TRUE if the content entity route condition is met, FALSE otherwise.
+   */
+  protected function meetsContentEntityRoutesCondition($outbound_route_name, Request $request) {
+    $storage = isset($this->paths[$request]) ? $this->paths[$request] : [];
+    if (!isset($storage[$outbound_route_name])) {
+      $content_entity_type_id_for_current_route = $this->getContentEntityTypeIdForCurrentRequest($request);
+      $meets_condition = $content_entity_type_id_for_current_route ? $this->meetsOutboundRouteToCurrentContentEntityCondition($outbound_route_name, $request) : FALSE;
+      $storage[$outbound_route_name] = $meets_condition;
+      $this->paths[$request] = $storage;
+    }
+
+    return $storage[$outbound_route_name];
+  }
+
+  /**
+   * Returns the content entity type id from the current request for the route.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The HttpRequest object representing the current request.
+   *
+   * @return string
+   *   The entity type id for the route from the request.
+   */
+  protected function getContentEntityTypeIdForCurrentRequest(Request $request) {
+    $content_entity_type_id_for_current_route = '';
+
+    if ($current_route_name = $request->attributes->get(RouteObjectInterface::ROUTE_NAME)) {
+      $current_route_path = $this->routeProvider->getRouteByName($current_route_name)->getPath();
+      $content_entity_type_id_for_current_route = isset($this->getContentEntityPaths()[$current_route_path]) ? $this->getContentEntityPaths()[$current_route_path] : '';
+    }
+
+    return $content_entity_type_id_for_current_route;
+  }
+
+  /**
+   * Determines if outbound route points to the current entity.
+   *
+   * @param $outbound_route_name
+   *   The route name for the current outbound url being processed.
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The HttpRequest object representing the current request.
+   *
+   * @return bool
+   *   TRUE if the outbound route points to the entity on the current route,
+   *   FALSE otherwise.
+   */
+  protected function meetsOutboundRouteToCurrentContentEntityCondition($outbound_route_name, Request $request) {
+    if ($content_entity_type_id_for_current_route = $this->getContentEntityTypeIdForCurrentRequest($request)) {
+      $outbound_route_path = $this->routeProvider->getRouteByName($outbound_route_name)->getPath();
+      if (!empty($this->getContentEntityPaths()[$outbound_route_path]) && $content_entity_type_id_for_current_route == $this->getContentEntityPaths()[$outbound_route_path]) {
+        return TRUE;
+      }
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Returns the paths for the link templates of all content entities.
+   *
+   * The array will be keyed by the link template paths with the corresponding
+   * entity type ids as values.
+   *
+   * @return array
+   *   The array of the link templates paths of all content entities, as keys.
+   */
+  protected function getContentEntityPaths() {
+    if (!isset($this->contentEntityPaths)) {
+      $this->contentEntityPaths = [];
+      $entity_types = $this->entityManager->getDefinitions();
+      foreach ($entity_types as $entity_type_id => $entity_type) {
+        if ($entity_type->isSubclassOf(ContentEntityInterface::class)) {
+          $entity_paths = array_fill_keys($entity_type->getLinkTemplates(), $entity_type_id);
+          $this->contentEntityPaths = array_merge($this->contentEntityPaths, $entity_paths);
+        }
+      }
+    }
+
+    return $this->contentEntityPaths;
+  }
+}
diff --git a/core/modules/language/src/Tests/EntityUrlLanguageTest.php b/core/modules/language/src/Tests/EntityUrlLanguageTest.php
index 915f7f6..3bb72f9 100644
--- a/core/modules/language/src/Tests/EntityUrlLanguageTest.php
+++ b/core/modules/language/src/Tests/EntityUrlLanguageTest.php
@@ -7,22 +7,31 @@
 
 namespace Drupal\language\Tests;
 
+use Drupal\Core\Language\LanguageInterface;
 use Drupal\entity_test\Entity\EntityTest;
 use Drupal\language\Entity\ConfigurableLanguage;
-use Drupal\simpletest\KernelTestBase;
+use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationContentEntity;
+use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
 
 /**
  * Tests the language of entity URLs.
  * @group language
  */
-class EntityUrlLanguageTest extends KernelTestBase {
+class EntityUrlLanguageTest extends LanguageTestBase {
 
   /**
    * Modules to enable.
    *
    * @var array
    */
-  public static $modules = ['language', 'entity_test', 'user', 'system'];
+  public static $modules = ['entity_test', 'user'];
+
+  /**
+   * The entity being used for testing.
+   *
+   * @var \Drupal\Core\Entity\ContentEntityInterface
+   */
+  protected $entity;
 
   protected function setUp() {
     parent::setUp();
@@ -37,33 +46,86 @@ protected function setUp() {
     ConfigurableLanguage::create(['id' => 'es'])->save();
     ConfigurableLanguage::create(['id' => 'fr'])->save();
 
-    $this->config('language.types')->setData([
-      'configurable' => ['language_interface'],
-      'negotiation' => ['language_interface' => ['enabled' => ['language-url' => 0]]],
-    ])->save();
-    $this->config('language.negotiation')->setData([
-      'url' => [
-        'source' => 'path_prefix',
-        'prefixes' => ['en' => 'en', 'es' => 'es', 'fr' => 'fr']
-      ],
-    ])->save();
-    $this->kernel->rebuildContainer();
-    $this->container = $this->kernel->getContainer();
-    \Drupal::setContainer($this->container);
+    $config = $this->config('language.negotiation');
+    $config->set('url.prefixes', ['en' => 'en', 'es' => 'es', 'fr' => 'fr'])
+      ->save();
+
+    $this->rebuildContainer();
+
+    $this->createTranslatableEntity();
   }
 
   /**
    * Ensures that entity URLs in a language have the right language prefix.
    */
   public function testEntityUrlLanguage() {
-    $entity = EntityTest::create();
-    $entity->addTranslation('es', ['name' => 'name spanish']);
-    $entity->addTranslation('fr', ['name' => 'name french']);
-    $entity->save();
-
-    $this->assertTrue(strpos($entity->urlInfo()->toString(), '/en/entity_test/' . $entity->id()) !== FALSE);
-    $this->assertTrue(strpos($entity->getTranslation('es')->urlInfo()->toString(), '/es/entity_test/' . $entity->id()) !== FALSE);
-    $this->assertTrue(strpos($entity->getTranslation('fr')->urlInfo()->toString(), '/fr/entity_test/' . $entity->id()) !== FALSE);
+    $this->assertTrue(strpos($this->entity->urlInfo()->toString(), '/en/entity_test/' . $this->entity->id()) !== FALSE);
+    $this->assertTrue(strpos($this->entity->getTranslation('es')->urlInfo()->toString(), '/es/entity_test/' . $this->entity->id()) !== FALSE);
+    $this->assertTrue(strpos($this->entity->getTranslation('fr')->urlInfo()->toString(), '/fr/entity_test/' . $this->entity->id()) !== FALSE);
+  }
+
+  /**
+   * Ensures correct entity URLs with the method language-content-entity enabled.
+   *
+   * Test case with the method language-content-entity enabled and configured
+   * with higher and also with lower priority than the method language-url.
+   */
+  public function testEntityUrlLanguageWithLanguageContentEnabled() {
+    // Define the method language-content-entity with a higher priority than
+    // language-url.
+    $config = $this->config('language.types');
+    $config->set('configurable', [LanguageInterface::TYPE_INTERFACE, LanguageInterface::TYPE_CONTENT]);
+    $config->set('negotiation.language_content.enabled', [
+      LanguageNegotiationContentEntity::METHOD_ID => 0,
+      LanguageNegotiationUrl::METHOD_ID => 1
+    ]);
+    $config->save();
+
+    // In order to reflect the changes for a multilingual site in the container
+    // we have to rebuild it.
+    $this->rebuildContainer();
+
+    // Without being on an content entity route the default entity url tests
+    // should still pass.
+    $this->testEntityUrlLanguage();
+
+    // Now switching to an entity route, so that the url links are generated
+    // while being on an entity route.
+    $this->setCurrentRequestForRoute('/entity_test/{entity_test}', 'entity.entity_test.canonical');
+
+    // The method language-content-entity should run before language-url and
+    // append query parameter for the content language and prevent language-url
+    // from overwriting the url.
+    $this->assertTrue(strpos($this->entity->urlInfo('canonical')->toString(), '/en/entity_test/' . $this->entity->id() . '?' . LanguageNegotiationContentEntity::QUERY_PARAMETER . '=en') !== FALSE);
+    $this->assertTrue(strpos($this->entity->getTranslation('es')->urlInfo('canonical')->toString(), '/en/entity_test/' . $this->entity->id() . '?' . LanguageNegotiationContentEntity::QUERY_PARAMETER . '=es') !== FALSE);
+    $this->assertTrue(strpos($this->entity->getTranslation('fr')->urlInfo('canonical')->toString(), '/en/entity_test/' . $this->entity->id() . '?' . LanguageNegotiationContentEntity::QUERY_PARAMETER . '=fr') !== FALSE);
+
+    // Define the method language-url with a higher priority than
+    // language-content-entity. This configuration should match the default one,
+    // where the language-content-entity is turned off.
+    $config->set('negotiation.language_content.enabled', [
+      LanguageNegotiationUrl::METHOD_ID => 0,
+      LanguageNegotiationContentEntity::METHOD_ID => 1
+    ]);
+    $config->save();
+
+    // In order to reflect the changes for a multilingual site in the container
+    // we have to rebuild it.
+    $this->rebuildContainer();
+
+    // The default entity url tests should pass again with the current
+    // configuration.
+    $this->testEntityUrlLanguage();
+  }
+
+  /**
+   * Creates a translated entity.
+   */
+  protected function createTranslatableEntity() {
+    $this->entity = EntityTest::create();
+    $this->entity->addTranslation('es', ['name' => 'name spanish']);
+    $this->entity->addTranslation('fr', ['name' => 'name french']);
+    $this->entity->save();
   }
 
 }
diff --git a/core/modules/language/src/Tests/LanguageNegotiationContentEntityTest.php b/core/modules/language/src/Tests/LanguageNegotiationContentEntityTest.php
new file mode 100644
index 0000000..8b3daf2
--- /dev/null
+++ b/core/modules/language/src/Tests/LanguageNegotiationContentEntityTest.php
@@ -0,0 +1,162 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\Tests\LanguageNegotiationLanguageContentEntityTest.
+ */
+
+namespace Drupal\language\Tests;
+
+use Drupal\Component\Render\FormattableMarkup;
+use Drupal\Core\Language\LanguageInterface;
+use Drupal\entity_test\Entity\EntityTest;
+use Drupal\language\Entity\ConfigurableLanguage;
+use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationContentEntity;
+use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Tests language negotiation with the language negotiator content entity.
+ *
+ * @group language
+ */
+class LanguageNegotiationContentEntityTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('language', 'language_test', 'entity_test', 'system');
+
+  /**
+   * The entity being used for testing.
+   *
+   * @var \Drupal\Core\Entity\ContentEntityInterface
+   */
+  protected $entity;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    ConfigurableLanguage::create(['id' => 'es'])->save();
+    ConfigurableLanguage::create(['id' => 'fr'])->save();
+
+    // In order to reflect the changes for a multilingual site in the container
+    // we have to rebuild it.
+    $this->rebuildContainer();
+
+    $this->createTranslatableEntity();
+
+    $user = $this->drupalCreateUser(array('view test entity'));
+    $this->drupalLogin($user);
+  }
+
+  /**
+   * Tests default with content language remaining same as interface language.
+   */
+  public function testDefaultConfiguration() {
+    $translation = $this->entity;
+    $this->drupalGet($translation->urlInfo());
+    $last = $this->container->get('state')->get('language_test.language_negotiation_last');
+    $last_content_language = $last[LanguageInterface::TYPE_CONTENT];
+    $last_interface_language = $last[LanguageInterface::TYPE_INTERFACE];
+    $this->assertTrue(($last_interface_language == $last_content_language) && ($last_content_language == $translation->language()->getId()), new FormattableMarkup('Interface language %interface_language and Content language %content_language are the same as the translation language %translation_language of the entity.', ['%interface_language' => $last_interface_language, '%content_language' => $last_content_language, '%translation_language' => $translation->language()->getId()]));
+
+    $translation = $this->entity->getTranslation('es');
+    $this->drupalGet($translation->urlInfo());
+    $last = $this->container->get('state')->get('language_test.language_negotiation_last');
+    $last_content_language = $last[LanguageInterface::TYPE_CONTENT];
+    $last_interface_language = $last[LanguageInterface::TYPE_INTERFACE];
+    $this->assertTrue(($last_interface_language == $last_content_language) && ($last_content_language == $translation->language()->getId()), new FormattableMarkup('Interface language %interface_language and Content language %content_language are the same as the translation language %translation_language of the entity.', ['%interface_language' => $last_interface_language, '%content_language' => $last_content_language, '%translation_language' => $translation->language()->getId()]));
+
+    $translation = $this->entity->getTranslation('fr');
+    $this->drupalGet($translation->urlInfo());
+    $last = $this->container->get('state')->get('language_test.language_negotiation_last');
+    $last_content_language = $last[LanguageInterface::TYPE_CONTENT];
+    $last_interface_language = $last[LanguageInterface::TYPE_INTERFACE];
+    $this->assertTrue(($last_interface_language == $last_content_language) && ($last_content_language == $translation->language()->getId()), new FormattableMarkup('Interface language %interface_language and Content language %content_language are the same as the translation language %translation_language of the entity.', ['%interface_language' => $last_interface_language, '%content_language' => $last_content_language, '%translation_language' => $translation->language()->getId()]));
+  }
+
+  /**
+   * Test case for enabling the language negotiator language_content_entity.
+   */
+  public function testEnabledLanguageContentNegotiator() {
+    // Define the method language-url with a higher priority than
+    // language-content-entity. This configuration should match the default one,
+    // where the language-content-entity is turned off.
+    $config = $this->config('language.types');
+    $config->set('configurable', [LanguageInterface::TYPE_INTERFACE, LanguageInterface::TYPE_CONTENT]);
+    $config->set('negotiation.language_content.enabled', [
+      LanguageNegotiationUrl::METHOD_ID => 0,
+      LanguageNegotiationContentEntity::METHOD_ID => 1
+    ]);
+    $config->save();
+
+    // In order to reflect the changes for a multilingual site in the container
+    // we have to rebuild it.
+    $this->rebuildContainer();
+
+    // The tests for the default configuration should still pass.
+    $this->testDefaultConfiguration();
+
+    // Define the method language-content-entity with a higher priority than
+    // language-url.
+    $config->set('negotiation.language_content.enabled', [
+      LanguageNegotiationContentEntity::METHOD_ID => 0,
+      LanguageNegotiationUrl::METHOD_ID => 1
+    ]);
+    $config->save();
+
+    // In order to reflect the changes for a multilingual site in the container
+    // we have to rebuild it.
+    $this->rebuildContainer();
+
+    // The method language-content-entity should run before language-url and
+    // append query parameter for the content language and prevent language-url
+    // from overwriting the url.
+    $default_site_langcode = $this->config('system.site')->get('default_langcode');
+
+    // Now switching to an entity route, so that the url links are generated
+    // while being on an entity route.
+    $this->setCurrentRequestForRoute('/entity_test/{entity_test}', 'entity.entity_test.canonical');
+
+    $translation = $this->entity;
+    $this->drupalGet($translation->urlInfo());
+    $last = $this->container->get('state')->get('language_test.language_negotiation_last');
+    $last_content_language = $last[LanguageInterface::TYPE_CONTENT];
+    $last_interface_language = $last[LanguageInterface::TYPE_INTERFACE];
+    $this->assertTrue(($last_interface_language == $default_site_langcode) && ($last_interface_language == $last_content_language) && ($last_content_language == $translation->language()->getId()), 'Interface language and Content language are the same as the default translation language of the entity.');
+    $this->assertTrue($last_interface_language == $default_site_langcode, 'Interface language did not change from the default site language.');
+    $this->assertTrue($last_content_language == $translation->language()->getId(), 'Content language matches the current entity translation language.');
+
+    $translation = $this->entity->getTranslation('es');
+    $this->drupalGet($translation->urlInfo());
+    $last = $this->container->get('state')->get('language_test.language_negotiation_last');
+    $last_content_language = $last[LanguageInterface::TYPE_CONTENT];
+    $last_interface_language = $last[LanguageInterface::TYPE_INTERFACE];
+    $this->assertTrue($last_interface_language == $default_site_langcode, 'Interface language did not change from the default site language.');
+    $this->assertTrue($last_content_language == $translation->language()->getId(), 'Content language matches the current entity translation language.');
+
+    $translation = $this->entity->getTranslation('fr');
+    $this->drupalGet($translation->urlInfo());
+    $last = $this->container->get('state')->get('language_test.language_negotiation_last');
+    $last_content_language = $last[LanguageInterface::TYPE_CONTENT];
+    $last_interface_language = $last[LanguageInterface::TYPE_INTERFACE];
+    $this->assertTrue($last_interface_language == $default_site_langcode, 'Interface language did not change from the default site language.');
+    $this->assertTrue($last_content_language == $translation->language()->getId(), 'Content language matches the current entity translation language.');
+  }
+
+  /**
+   * Creates a translated entity.
+   */
+  protected function createTranslatableEntity() {
+    $this->entity = EntityTest::create();
+    $this->entity->addTranslation('es', ['name' => 'name spanish']);
+    $this->entity->addTranslation('fr', ['name' => 'name french']);
+    $this->entity->save();
+  }
+}
diff --git a/core/modules/language/src/Tests/LanguageUILanguageNegotiationTest.php b/core/modules/language/src/Tests/LanguageUILanguageNegotiationTest.php
index 4645d68..16ba13e 100644
--- a/core/modules/language/src/Tests/LanguageUILanguageNegotiationTest.php
+++ b/core/modules/language/src/Tests/LanguageUILanguageNegotiationTest.php
@@ -221,8 +221,13 @@ function testUILanguageNegotiation() {
 
     // Unknown language prefix should return 404.
     $definitions = \Drupal::languageManager()->getNegotiator()->getNegotiationMethods();
+    // Enable only methods, which are either not limited to a specific language
+    // type or are supporting the interface language type.
+    $language_interface_method_definitions = array_filter($definitions, function ($method_definition) {
+      return !isset($method_definition['types']) || (isset($method_definition['types']) && in_array(LanguageInterface::TYPE_INTERFACE, $method_definition['types']));
+    });
     $this->config('language.types')
-      ->set('negotiation.' . LanguageInterface::TYPE_INTERFACE . '.enabled', array_flip(array_keys($definitions)))
+      ->set('negotiation.' . LanguageInterface::TYPE_INTERFACE . '.enabled', array_flip(array_keys($language_interface_method_definitions)))
       ->save();
     $this->drupalGet("$langcode_unknown/admin/config", array(), $http_header_browser_fallback);
     $this->assertResponse(404, "Unknown language path prefix should return 404");
diff --git a/core/modules/node/src/Tests/NodeTranslationUITest.php b/core/modules/node/src/Tests/NodeTranslationUITest.php
index 490f5b1..45bb340 100644
--- a/core/modules/node/src/Tests/NodeTranslationUITest.php
+++ b/core/modules/node/src/Tests/NodeTranslationUITest.php
@@ -94,7 +94,8 @@ function testPublishedStatusNoFields() {
     $language = ConfigurableLanguage::load($langcode);
     $values[$langcode] = array('title' => array(array('value' => $this->randomMachineName())));
 
-    $add_url = Url::fromRoute('content_translation.translation_add_' . $entity->getEntityTypeId(), [
+    $entity_type_id = $entity->getEntityTypeId();
+    $add_url = Url::fromRoute("entity.$entity_type_id.content_translation_add", [
       $entity->getEntityTypeId() => $entity->id(),
       'source' => $default_langcode,
       'target' => $langcode
diff --git a/core/modules/simpletest/src/TestBase.php b/core/modules/simpletest/src/TestBase.php
index 7abfa2f..8826d24 100644
--- a/core/modules/simpletest/src/TestBase.php
+++ b/core/modules/simpletest/src/TestBase.php
@@ -19,6 +19,9 @@
 use Drupal\Core\Site\Settings;
 use Drupal\Core\StreamWrapper\PublicStream;
 use Drupal\Core\Utility\Error;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Route;
 
 /**
  * Base class for Drupal tests.
@@ -1586,4 +1589,28 @@ public function getTempFilesDirectory() {
     return $this->tempFilesDirectory;
   }
 
+  /**
+   * Force a container rebuild.
+   */
+  protected function rebuildContainer() {
+    // Rebuild the kernel and bring it back to a fully bootstrapped state.
+    $this->container = $this->kernel->rebuildContainer();
+  }
+
+  /**
+   * Sets the current request to a specific path with the corresponding route.
+   *
+   * @param string $path
+   *   The path for which the current request should be created.
+   * @param string $route_name
+   *   The route name for which the route object for the request should be
+   *   created.
+   */
+  protected function setCurrentRequestForRoute($path, $route_name) {
+    $request = Request::create($path);
+    $request->attributes->set(RouteObjectInterface::ROUTE_NAME, $route_name);
+    $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route($path));
+    \Drupal::requestStack()->push($request);
+  }
+
 }
diff --git a/core/modules/simpletest/src/WebTestBase.php b/core/modules/simpletest/src/WebTestBase.php
index f5c90cf..a392d3e 100644
--- a/core/modules/simpletest/src/WebTestBase.php
+++ b/core/modules/simpletest/src/WebTestBase.php
@@ -1173,8 +1173,7 @@ protected function writeCustomTranslations() {
    *   enabled modules to be immediately available in the same request.
    */
   protected function rebuildContainer() {
-    // Rebuild the kernel and bring it back to a fully bootstrapped state.
-    $this->container = $this->kernel->rebuildContainer();
+    parent::rebuildContainer();
 
     // Make sure the url generator has a request object, otherwise calls to
     // $this->drupalGet() will fail.
