diff --git a/core/modules/translation_entity/lib/Drupal/translation_entity/Access/EntityTranslationManageAccessCheck.php b/core/modules/translation_entity/lib/Drupal/translation_entity/Access/EntityTranslationManageAccessCheck.php
new file mode 100644
index 0000000..c2b45b8
--- /dev/null
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Access/EntityTranslationManageAccessCheck.php
@@ -0,0 +1,87 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\translation_entity\Access\EntityTranslationManageAccessCheck.
+ */
+
+namespace Drupal\translation_entity\Access;
+
+use Symfony\Component\Routing\Route;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Entity\EntityManager;
+use Drupal\Core\Access\AccessCheckInterface;
+use Drupal\Core\ControllerInterface;
+
+/**
+ * Access check for entity translation CURD operation.
+ */
+class EntityTranslationManageAccessCheck implements AccessCheckInterface, ControllerInterface {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManager
+   */
+  protected $entityManager;
+
+  /**
+   * Constructs a EntityTranslationAccessAdd object.
+   *
+   * @param \Drupal\Core\Entity\EntityManager $manager
+   *   The entity type manager.
+   */
+  public function __construct(EntityManager $manager) {
+    $this->entityManager = $manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('plugin.manager.entity')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function applies(Route $route) {
+    return array_key_exists('_translation_entity_manage_access', $route->getRequirements());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function access(Route $route, Request $request) {
+    if ($entity = $request->attributes->get('entity')) {
+      $route_requirements = $route->getRequirements();
+      $operation = isset($route_requirements['_translation_entity_manage_access']) ?: NULL;
+
+      // Load translation.
+      $translations = $entity->getTranslationLanguages();
+      $languages = language_list();
+
+      switch ($operation) {
+        case 'create':
+          $source = language_load($request->attributes->get('source'));
+          $target = language_load($request->attributes->get('target'));
+          return $source->langcode != $target->langcode && isset($languages[$source->langcode])
+          && isset($languages[$target->langcode])
+          && !isset($translations[$target->langcode])
+          && translation_entity_access($entity, $operation);
+          break;
+        case 'update':
+          $language = language_load($request->attributes->get('language'));
+          return isset($languages[$language->langcode])
+          && $language->langcode != $entity->language()->langcode
+          && isset($translations[$language->langcode])
+          && translation_entity_access($entity, $operation);
+          break;
+      }
+    }
+    return static::DENY;
+  }
+}
diff --git a/core/modules/translation_entity/lib/Drupal/translation_entity/Access/EntityTranslationOverviewAccess.php b/core/modules/translation_entity/lib/Drupal/translation_entity/Access/EntityTranslationOverviewAccess.php
new file mode 100644
index 0000000..eb2eee7
--- /dev/null
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Access/EntityTranslationOverviewAccess.php
@@ -0,0 +1,83 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\translation_entity\Access\EntityTranslationOverviewAccess.
+ */
+
+namespace Drupal\translation_entity\Access;
+
+use Symfony\Component\Routing\Route;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Entity\EntityManager;
+use Drupal\Core\Access\AccessCheckInterface;
+use Drupal\Core\ControllerInterface;
+
+/**
+ * Access check for entity translation overview.
+ */
+class EntityTranslationOverviewAccess implements AccessCheckInterface, ControllerInterface {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManager
+   */
+  protected $entityManager;
+
+  /**
+   * Constructs a EntityTranslationOverviewAccess object.
+   *
+   * @param \Drupal\Core\Entity\EntityManager $manager
+   *   The entity type manager.
+   */
+  public function __construct(EntityManager $manager) {
+    $this->entityManager = $manager;
+  }
+
+  /**
+   * {inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('plugin.manager.entity')
+    );
+  }
+
+  /**
+   * Implements AccessCheckInterface::applies().
+   */
+  public function applies(Route $route) {
+    return array_key_exists('_translation_entity_access', $route->getRequirements());
+  }
+
+  /**
+   * Implements AccessCheckInterface::access().
+   */
+  public function access(Route $route, Request $request) {
+    if ($entity = $request->attributes->get('entity')) {
+      // Get entity base info.
+      $entity_type = $entity->entityType();
+      $bundle = $entity->bundle();
+
+      // Get entity access callback.
+      $definitions = $this->entityManager->getDefinitions();
+      $access_callback = $definitions[$entity_type]['translation']['translation_entity']['access_callback'];
+      if (call_user_func($access_callback, $entity)) {
+        return TRUE;
+      }
+
+      // Check per entity permission.
+      $permission = "translate {$entity_type}";
+      if ($definitions[$entity_type]['permission_granularity'] == 'bundle') {
+        $permission = "translate {$bundle} {$entity_type}";
+      }
+      if (user_access($permission)) {
+        return TRUE;
+      }
+    }
+
+    return NULL;
+  }
+}
diff --git a/core/modules/translation_entity/lib/Drupal/translation_entity/Controller/EntityTranslationController.php b/core/modules/translation_entity/lib/Drupal/translation_entity/Controller/EntityTranslationController.php
new file mode 100644
index 0000000..9dcbcd3
--- /dev/null
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Controller/EntityTranslationController.php
@@ -0,0 +1,291 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\translation_entity\Controller\EntityTranslationController.
+ */
+
+namespace Drupal\translation_entity\Controller;
+
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\ControllerInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Entity\EntityManager;
+
+/**
+ * Base class for entity translation controllers.
+ */
+class EntityTranslationController implements ControllerInterface {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManager
+   */
+  protected $entityManager;
+
+  /**
+   * The module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
+   * Constructs a EntityTranslationController object.
+   *
+   * @param \Drupal\Core\Entity\EntityManager $entity_manager
+   *   The entity manager.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler.
+   */
+  public function __construct(EntityManager $entity_manager, ModuleHandlerInterface $module_handler) {
+    $this->entityManager = $entity_manager;
+    $this->moduleHandler = $module_handler;
+  }
+
+  /**
+   * {inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('plugin.manager.entity'),
+      $container->get('module_handler')
+    );
+  }
+
+  /**
+   * Translations overview page builder.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity to be translated.
+   */
+  public function overview(EntityInterface $entity) {
+    $this->moduleHandler->loadInclude('translation_entity', 'inc', 'translation_entity.pages');
+    $entity_type = $entity->entityType();
+
+    $controller_class = $this->entityManager->getControllerClass($entity_type, 'translation');
+    $controller = new $controller_class($entity_type, $this->entityManager->getDefinition($entity_type));
+
+    $languages = language_list();
+    $original = $entity->language()->langcode;
+    $translations = $entity->getTranslationLanguages();
+    $field_ui = $this->moduleHandler->moduleExists('field_ui') && user_access('administer ' . $entity_type . ' fields');
+
+    $path = $controller->getViewPath($entity);
+    $base_path = $controller->getBasePath($entity);
+    $edit_path = $controller->getEditPath($entity);
+
+    $header = array(
+      t('Language'),
+      t('Translation'),
+      t('Source language'),
+      t('Status'),
+      t('Operations'),
+    );
+    $rows = array();
+
+    if (language_multilingual()) {
+      // If we have a view path defined for the current entity get the switch
+      // links based on it.
+      if ($path) {
+        $links = _translation_entity_get_switch_links($path);
+      }
+
+      // Determine whether the current entity is translatable.
+      $translatable = FALSE;
+      foreach (field_info_instances($entity_type, $entity->bundle()) as $instance) {
+        $field_name = $instance['field_name'];
+        $field = field_info_field($field_name);
+        if ($field['translatable']) {
+          $translatable = TRUE;
+          break;
+        }
+      }
+
+      foreach ($languages as $language) {
+        $language_name = $language->name;
+        $langcode = $language->langcode;
+        $add_path = $base_path . '/translations/add/' . $original . '/' . $langcode;
+        $translate_path = $base_path . '/translations/edit/' . $langcode;
+        $delete_path = $base_path . '/translations/delete/' . $langcode;
+
+        if ($base_path) {
+          $add_links = _translation_entity_get_switch_links($add_path);
+          $edit_links = _translation_entity_get_switch_links($edit_path);
+          $translate_links = _translation_entity_get_switch_links($translate_path);
+          $delete_links = _translation_entity_get_switch_links($delete_path);
+        }
+
+        $operations = array(
+          'data' => array(
+            '#type' => 'operations',
+            '#links' => array(),
+          ),
+        );
+        $links = &$operations['data']['#links'];
+
+        if (isset($translations[$langcode])) {
+          // Existing translation in the translation set: display status.
+          $source = isset($entity->translation[$langcode]['source']) ? $entity->translation[$langcode]['source'] : '';
+          $is_original = $langcode == $original;
+          $translation = $translations[$langcode];
+          $label = $entity->label($langcode);
+          $link = isset($links->links[$langcode]['href']) ? $links->links[$langcode] : array(
+            'href' => $path,
+            'language' => $language,
+          );
+          $row_title = l($label, $link['href'], $link);
+
+          if (empty($link['href'])) {
+            $row_title = $is_original ? $label : t('n/a');
+          }
+
+          // If the user is allowed to edit the entity we point the edit link to
+          // the entity form, otherwise if we are not dealing with the original
+          // language we point the link to the translation form.
+          if ($edit_path && $controller->getAccess($entity, 'update')) {
+            $links['edit'] = isset($edit_links->links[$langcode]['href']) ? $edit_links->links[$langcode] : array(
+              'href' => $edit_path,
+              'language' => $language,
+            );
+          }
+          elseif (!$is_original && $controller->getTranslationAccess($entity, 'update')) {
+            $links['edit'] = isset($translate_links->links[$langcode]['href']) ? $translate_links->links[$langcode] : array(
+              'href' => $translate_path,
+              'language' => $language,
+            );
+          }
+
+          if (isset($links['edit'])) {
+            $links['edit']['title'] = t('Edit');
+          }
+          $translation = $entity->translation[$langcode];
+          $status = !empty($translation['status']) ? t('Published') : t('Not published');
+          // @todo Add a theming function here.
+          $status = '<span class="status">' . $status . '</span>' . (!empty($translation['outdated']) ? ' <span class="marker">' . t('outdated') . '</span>' : '');
+
+          if ($is_original) {
+            $language_name = t('<strong>@language_name (Original language)</strong>', array('@language_name' => $language_name));
+            $source_name = t('n/a');
+          }
+          else {
+            $source_name = isset($languages[$source]) ? $languages[$source]->name : t('n/a');
+            if ($controller->getTranslationAccess($entity, 'delete')) {
+              $links['delete'] = isset($delete_links->links[$langcode]['href']) ? $delete_links->links[$langcode] : array(
+                'href' => $delete_links,
+                'language' => $language,
+              );
+              $links['delete']['title'] = t('Delete');
+            }
+          }
+        }
+        else {
+          // No such translation in the set yet: help user to create it.
+          $row_title = $source_name = t('n/a');
+          $source = $entity->language()->langcode;
+
+          if ($source != $langcode && $controller->getTranslationAccess($entity, 'create')) {
+            if ($translatable) {
+              $links['add'] = isset($add_links->links[$langcode]['href']) ? $add_links->links[$langcode] : array(
+                'href' => $add_path,
+                'language' => $language,
+              );
+              $links['add']['title'] = t('Add');
+            }
+            elseif ($field_ui) {
+              $entity_path = $this->entityManager->getAdminPath($entity_type, $entity->bundle());
+              // Link directly to the fields tab to make it easier to find the
+              // setting to enable translation on fields.
+              $path = $entity_path . '/fields';
+              $links['nofields'] = array(
+                'title' => t('No translatable fields'),
+                'href' => $path,
+                'language' => $language,
+              );
+            }
+          }
+
+          $status = t('Not translated');
+        }
+
+        $rows[] = array(
+          $language_name,
+          $row_title,
+          $source_name,
+          $status,
+          $operations,
+        );
+      }
+    }
+
+    drupal_set_title(t('Translations of %label', array('%label' => $entity->label())), PASS_THROUGH);
+
+    // Add metadata to the build render array to let other modules know about
+    // which entity this is.
+    $build['#entity'] = $entity;
+
+    $build['translation_entity_overview'] = array(
+      '#theme' => 'table',
+      '#header' => $header,
+      '#rows' => $rows,
+    );
+
+    return $build;
+  }
+
+  /**
+   * Page callback for the translation addition page.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity being translated.
+   * @param \Drupal\language\Plugin\Condition\Language $source
+   *   (optional) The language of the values being translated. Defaults to the
+   *   entity language.
+   * @param \Drupal\language\Plugin\Condition\Language $target
+   *   (optional) The language of the translated values. Defaults to the current
+   *   content language.
+   *
+   * @return array
+   *   A processed form array ready to be rendered.
+   */
+  public function add(EntityInterface $entity, $source = NULL, $target = NULL) {
+    $this->moduleHandler->loadInclude('translation_entity', 'inc', 'translation_entity.pages');
+
+    $source = language_load($source);
+    $target = language_load($target);
+    // @todo Exploit the upcoming hook_entity_prepare() when available.
+    translation_entity_prepare_translation($entity, $source, $target);
+    $info = $entity->entityInfo();
+    $operation = isset($info['default_operation']) ? $info['default_operation'] : 'default';
+    $form_state['langcode'] = $target->langcode;
+    $form_state['translation_entity']['source'] = $source;
+    $form_state['translation_entity']['target'] = $target;
+    $controller = translation_entity_controller($entity->entityType());
+    $form_state['translation_entity']['translation_form'] = !$controller->getAccess($entity, 'update');
+    $this->entityManager->getForm($entity, $operation, $form_state);
+  }
+
+  /**
+   * Page callback for the edit translation page.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity being translated.
+   * @param \Drupal\language\Plugin\Condition\Language $language
+   *   (optional) The language of the translated values. Defaults to the current
+   *   content language.
+   *
+   * @return array
+   *   A processed form array ready to be rendered.
+   */
+  public function edit(EntityInterface $entity, $language = NULL) {
+    $this->moduleHandler->loadInclude('translation_entity', 'inc', 'translation_entity.pages');
+    $language = language_load($language);
+    $info = $entity->entityInfo();
+    $operation = isset($info['default_operation']) ? $info['default_operation'] : 'default';
+    $form_state['langcode'] = $language->langcode;
+    $form_state['translation_entity']['translation_form'] = TRUE;
+    $this->entityManager->getForm($entity, $operation, $form_state);
+  }
+}
diff --git a/core/modules/translation_entity/lib/Drupal/translation_entity/Routing/EntityTranslationRouteSubscriber.php b/core/modules/translation_entity/lib/Drupal/translation_entity/Routing/EntityTranslationRouteSubscriber.php
new file mode 100644
index 0000000..ec7a77e
--- /dev/null
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Routing/EntityTranslationRouteSubscriber.php
@@ -0,0 +1,108 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\translation_entity\Routing\EntityTranslationRouteSubscriber.
+ */
+
+namespace Drupal\translation_entity\Routing;
+
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\Routing\Route;
+use Drupal\Core\Routing\RouteBuildEvent;
+use Drupal\Core\Routing\RoutingEvents;
+use Drupal\Core\Entity\EntityManager;
+
+/**
+ * Subscriber for entity translation routes.
+ */
+class EntityTranslationRouteSubscriber implements EventSubscriberInterface {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManager
+   */
+  protected $manager;
+
+  /**
+   * Constructs a EntityTranslationRouteSubscriber object.
+   *
+   * @param \Drupal\Core\Entity\EntityManager $manager
+   *   The entity type manager.
+   */
+  public function __construct(EntityManager $manager) {
+    $this->manager = $manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    $events[RoutingEvents::DYNAMIC] = 'routes';
+    return $events;
+  }
+
+  /**
+   * Adds routes for entity translations.
+   */
+  public function routes(RouteBuildEvent $event) {
+    $collection = $event->getRouteCollection();
+    foreach ($this->manager->getDefinitions() as $entity_type => $entity_info) {
+      if ($entity_info['translatable'] && isset($entity_info['translation'])) {
+        $route = new Route(
+         '/' . str_replace($entity_info['menu_path_wildcard'], '{entity}', $entity_info['menu_base_path']) . "/translations",
+          array(
+            '_content' => '\Drupal\translation_entity\Controller\EntityTranslationController::overview',
+          ),
+          array(
+            '_translation_entity_access' => $entity_type,
+            '_permission' => 'translate any entity',
+          ),
+          array(
+            'converters' => array(
+              'entity' => $entity_type,
+            ),
+          )
+        );
+        $collection->add("translation_entity.translation_overview.$entity_type", $route);
+
+        $route = new Route(
+          '/' . str_replace($entity_info['menu_path_wildcard'], '{entity}', $entity_info['menu_base_path']) . "/translations/add/{source}/{target}",
+          array(
+            '_content' => '\Drupal\translation_entity\Controller\EntityTranslationController::add',
+
+          ),
+          array(
+            '_permission' => 'translate any entity',
+            '_translation_entity_manage_access' => 'create',
+          ),
+          array(
+            'converters' => array(
+              'entity' => $entity_type,
+            ),
+          )
+        );
+        $collection->add("translation_entity.translation_add.$entity_type", $route);
+
+        $route = new Route(
+          '/' . str_replace($entity_info['menu_path_wildcard'], '{entity}', $entity_info['menu_base_path']) . "/translations/edit/{language}",
+          array(
+            '_content' => '\Drupal\translation_entity\Controller\EntityTranslationController::edit',
+
+          ),
+          array(
+            '_permission' => 'translate any entity',
+            '_translation_entity_manage_access' => 'update',
+          ),
+          array(
+            'converters' => array(
+              'entity' => $entity_type,
+            ),
+          )
+        );
+        $collection->add("translation_entity.translation_edit.$entity_type", $route);
+      }
+    }
+  }
+}
diff --git a/core/modules/translation_entity/translation_entity.module b/core/modules/translation_entity/translation_entity.module
index dec0460..e92e91c 100644
--- a/core/modules/translation_entity/translation_entity.module
+++ b/core/modules/translation_entity/translation_entity.module
@@ -148,9 +148,8 @@ function translation_entity_menu() {
 
       $items["$path/translations"] = array(
         'title' => 'Translate',
-        'page callback' => 'translation_entity_overview',
-        'page arguments' => array($entity_position),
         'type' => MENU_LOCAL_TASK,
+        'route_name' => "translation_entity.translation_overview.$entity_type",
         'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
         'weight' => 2,
       ) + $item;
@@ -164,31 +163,22 @@ function translation_entity_menu() {
       // Add translation callback.
       // @todo Add the access callback instead of replacing it as soon as the
       // routing system supports multiple callbacks.
-      $language_position = $entity_position + 3;
-      $args = array($entity_position, $language_position, $language_position + 1);
       $items["$path/translations/add/%language/%language"] = array(
         'title' => 'Add',
-        'page callback' => 'translation_entity_add_page',
-        'page arguments' => $args,
-        'access callback' => 'translation_entity_add_access',
-        'access arguments' => $args,
-        'type' => MENU_LOCAL_TASK,
-        'weight' => 1,
-      ) + $item;
+        'route_name' => "translation_entity.translation_add.$entity_type",
+        'weight'     => 1,
+      );
 
       // Edit translation callback.
-      $args = array($entity_position, $language_position);
       $items["$path/translations/edit/%language"] = array(
         'title' => 'Edit',
-        'page callback' => 'translation_entity_edit_page',
-        'page arguments' => $args,
-        'access callback' => 'translation_entity_edit_access',
-        'access arguments' => $args,
-        'type' => MENU_LOCAL_TASK,
-        'weight' => 1,
-      ) + $item;
+        'route_name' => "translation_entity.translation_edit.$entity_type",
+        'weight'     => 1,
+      );
 
       // Delete translation callback.
+      $language_position = $entity_position + 3;
+      $args = array($entity_position, $language_position, $language_position + 1);
       $items["$path/translations/delete/%language"] = array(
         'title' => 'Delete',
         'page callback' => 'drupal_get_form',
@@ -207,7 +197,6 @@ function translation_entity_menu() {
     'access arguments' => array('administer entity translation'),
     'file' => 'translation_entity.admin.inc',
   );
-
   return $items;
 }
 
@@ -300,42 +289,6 @@ function translation_entity_view_access(EntityInterface $entity, $langcode, Acco
 }
 
 /**
- * Access callback for the translation addition page.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The entity being translated.
- * @param \Drupal\Core\Language\Language $source
- *   (optional) The language of the values being translated. Defaults to the
- *   entity language.
- * @param \Drupal\Core\Language\Language $target
- *   (optional) The language of the translated values. Defaults to the current
- *   content language.
- */
-function translation_entity_add_access(EntityInterface $entity, Language $source = NULL, Language $target = NULL) {
-  $source = !empty($source) ? $source : $entity->language();
-  $target = !empty($target) ? $target : language(Language::TYPE_CONTENT);
-  $translations = $entity->getTranslationLanguages();
-  $languages = language_list();
-  return $source->langcode != $target->langcode && isset($languages[$source->langcode]) && isset($languages[$target->langcode]) && !isset($translations[$target->langcode]) && translation_entity_access($entity, 'create');
-}
-
-/**
- * Access callback for the translation edit page.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The entity being translated.
- * @param \Drupal\Core\Language\Language $language
- *   (optional) The language of the translated values. Defaults to the current
- *   content language.
- */
-function translation_entity_edit_access(EntityInterface $entity, Language $language = NULL) {
-  $language = !empty($language) ? $language : language(Language::TYPE_CONTENT);
-  $translations = $entity->getTranslationLanguages();
-  $languages = language_list();
-  return isset($languages[$language->langcode]) && $language->langcode != $entity->language()->langcode && isset($translations[$language->langcode]) && translation_entity_access($entity, 'update');
-}
-
-/**
  * Access callback for the translation delete page.
  *
  * @param \Drupal\Core\Entity\EntityInterface $entity
diff --git a/core/modules/translation_entity/translation_entity.pages.inc b/core/modules/translation_entity/translation_entity.pages.inc
index cd212eb..1b780fb 100644
--- a/core/modules/translation_entity/translation_entity.pages.inc
+++ b/core/modules/translation_entity/translation_entity.pages.inc
@@ -10,152 +10,6 @@
 use Drupal\Core\Entity\EntityNG;
 
 /**
- * Translations overview page callback.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The entity whose translation overview should be displayed.
- */
-function translation_entity_overview(EntityInterface $entity) {
-  $controller = translation_entity_controller($entity->entityType());
-  $entity_manager = Drupal::entityManager();
-  $languages = language_list();
-  $original = $entity->language()->langcode;
-  $translations = $entity->getTranslationLanguages();
-  $field_ui = module_exists('field_ui') && user_access('administer ' . $entity->entityType() . ' fields');
-
-  $path = $controller->getViewPath($entity);
-  $base_path = $controller->getBasePath($entity);
-  $edit_path = $controller->getEditPath($entity);
-
-  $header = array(t('Language'), t('Translation'), t('Source language'), t('Status'), t('Operations'));
-  $rows = array();
-
-  if (language_multilingual()) {
-    // If we have a view path defined for the current entity get the switch
-    // links based on it.
-    if ($path) {
-      $links = _translation_entity_get_switch_links($path);
-    }
-
-    // Determine whether the current entity is translatable.
-    $translatable = FALSE;
-    foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
-      $field_name = $instance['field_name'];
-      $field = field_info_field($field_name);
-      if ($field['translatable']) {
-        $translatable = TRUE;
-        break;
-      }
-    }
-
-    foreach ($languages as $language) {
-      $language_name = $language->name;
-      $langcode = $language->langcode;
-      $add_path = $base_path . '/translations/add/' . $original . '/' . $langcode;
-      $translate_path = $base_path . '/translations/edit/' . $langcode;
-      $delete_path = $base_path . '/translations/delete/' . $langcode;
-
-      if ($base_path) {
-        $add_links = _translation_entity_get_switch_links($add_path);
-        $edit_links = _translation_entity_get_switch_links($edit_path);
-        $translate_links = _translation_entity_get_switch_links($translate_path);
-        $delete_links = _translation_entity_get_switch_links($delete_path);
-      }
-
-      $operations = array(
-        'data' => array(
-          '#type' => 'operations',
-          '#links' => array(),
-        ),
-      );
-      $links = &$operations['data']['#links'];
-
-      if (isset($translations[$langcode])) {
-        // Existing translation in the translation set: display status.
-        $source = isset($entity->translation[$langcode]['source']) ? $entity->translation[$langcode]['source'] : '';
-        $is_original = $langcode == $original;
-        $translation = $translations[$langcode];
-        $label = $entity->label($langcode);
-        $link = isset($links->links[$langcode]['href']) ? $links->links[$langcode] : array('href' => $path, 'language' => $language);
-        $row_title = l($label, $link['href'], $link);
-
-        if (empty($link['href'])) {
-          $row_title = $is_original ? $label : t('n/a');
-        }
-
-        // If the user is allowed to edit the entity we point the edit link to
-        // the entity form, otherwise if we are not dealing with the original
-        // language we point the link to the translation form.
-        if ($edit_path && $controller->getAccess($entity, 'update')) {
-          $links['edit'] = isset($edit_links->links[$langcode]['href']) ? $edit_links->links[$langcode] : array('href' => $edit_path, 'language' => $language);
-        }
-        elseif (!$is_original && $controller->getTranslationAccess($entity, 'update')) {
-          $links['edit'] = isset($translate_links->links[$langcode]['href']) ? $translate_links->links[$langcode] : array('href' => $translate_path, 'language' => $language);
-        }
-
-        if (isset($links['edit'])) {
-          $links['edit']['title'] = t('Edit');
-        }
-
-        $translation = $entity->translation[$langcode];
-        $status = !empty($translation['status']) ? t('Published') : t('Not published');
-        // @todo Add a theming function here.
-        $status = '<span class="status">' . $status . '</span>' . (!empty($translation['outdated']) ? ' <span class="marker">' . t('outdated') . '</span>' : '');
-
-        if ($is_original) {
-          $language_name = t('<strong>@language_name (Original language)</strong>', array('@language_name' => $language_name));
-          $source_name = t('n/a');
-        }
-        else {
-          $source_name = isset($languages[$source]) ? $languages[$source]->name : t('n/a');
-          if ($controller->getTranslationAccess($entity, 'delete')) {
-            $links['delete'] = isset($delete_links->links[$langcode]['href']) ? $delete_links->links[$langcode] : array('href' => $delete_links, 'language' => $language);
-            $links['delete']['title'] = t('Delete');
-          }
-        }
-      }
-      else {
-        // No such translation in the set yet: help user to create it.
-        $row_title = $source_name = t('n/a');
-        $source = $entity->language()->langcode;
-
-        if ($source != $langcode && $controller->getTranslationAccess($entity, 'create')) {
-          if ($translatable) {
-            $links['add'] = isset($add_links->links[$langcode]['href']) ? $add_links->links[$langcode] : array('href' => $add_path, 'language' => $language);
-            $links['add']['title'] = t('Add');
-          }
-          elseif ($field_ui) {
-            $entity_path = $entity_manager->getAdminPath($entity->entityType(), $entity->bundle());
-            // Link directly to the fields tab to make it easier to find the
-            // setting to enable translation on fields.
-            $path = $entity_path . '/fields';
-            $links['nofields'] = array('title' => t('No translatable fields'), 'href' => $path, 'language' => $language);
-          }
-        }
-
-        $status = t('Not translated');
-      }
-
-      $rows[] = array($language_name, $row_title, $source_name, $status, $operations);
-    }
-  }
-
-  drupal_set_title(t('Translations of %label', array('%label' => $entity->label())), PASS_THROUGH);
-
-  // Add metadata to the build render array to let other modules know about
-  // which entity this is.
-  $build['#entity'] = $entity;
-
-  $build['translation_entity_overview'] = array(
-    '#theme' => 'table',
-    '#header' => $header,
-    '#rows' => $rows,
-  );
-
-  return $build;
-}
-
-/**
  * Returns the localized links for the given path.
  *
  * @param string $path
@@ -176,57 +30,6 @@ function _translation_entity_get_switch_links($path) {
 }
 
 /**
- * Page callback for the translation addition page.
- *
- * @param EntityInterface $entity
- *   The entity being translated.
- * @param Language $source
- *   (optional) The language of the values being translated. Defaults to the
- *   entity language.
- * @param Language $target
- *   (optional) The language of the translated values. Defaults to the current
- *   content language.
- *
- * @return array
- *   A processed form array ready to be rendered.
- */
-function translation_entity_add_page(EntityInterface $entity, Language $source = NULL, Language $target = NULL) {
-  $source = !empty($source) ? $source : $entity->language();
-  $target = !empty($target) ? $target : language(Language::TYPE_CONTENT);
-  // @todo Exploit the upcoming hook_entity_prepare() when available.
-  translation_entity_prepare_translation($entity, $source, $target);
-  $info = $entity->entityInfo();
-  $operation = isset($info['default_operation']) ? $info['default_operation'] : 'default';
-  $form_state['langcode'] = $target->langcode;
-  $form_state['translation_entity']['source'] = $source;
-  $form_state['translation_entity']['target'] = $target;
-  $controller = translation_entity_controller($entity->entityType());
-  $form_state['translation_entity']['translation_form'] = !$controller->getAccess($entity, 'update');
-  return Drupal::entityManager()->getForm($entity, $operation, $form_state);
-}
-
-/**
- * Page callback for the translation edit page.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The entity being translated.
- * @param \Drupal\Core\Language\Language $language
- *   (optional) The language of the translated values. Defaults to the current
- *   content language.
- *
- * @return array
- *   A processed form array ready to be rendered.
- */
-function translation_entity_edit_page(EntityInterface $entity, Language $language = NULL) {
-  $language = !empty($language) ? $language : language(Language::TYPE_CONTENT);
-  $info = $entity->entityInfo();
-  $operation = isset($info['default_operation']) ? $info['default_operation'] : 'default';
-  $form_state['langcode'] = $language->langcode;
-  $form_state['translation_entity']['translation_form'] = TRUE;
-  return Drupal::entityManager()->getForm($entity, $operation, $form_state);
-}
-
-/**
  * Populates target values with the source values.
  *
  * @param \Drupal\Core\Entity\EntityInterface $entity
diff --git a/core/modules/translation_entity/translation_entity.services.yml b/core/modules/translation_entity/translation_entity.services.yml
index 541bd3d..6116916 100644
--- a/core/modules/translation_entity/translation_entity.services.yml
+++ b/core/modules/translation_entity/translation_entity.services.yml
@@ -2,3 +2,18 @@ services:
   translation_entity.synchronizer:
     class: Drupal\translation_entity\FieldTranslationSynchronizer
     arguments: ['@plugin.manager.entity']
+  translation_entity.subscriber:
+    class: Drupal\translation_entity\Routing\EntityTranslationRouteSubscriber
+    arguments: ['@plugin.manager.entity']
+    tags:
+      - { name: event_subscriber }
+  translation_entity.overview_access:
+    class: Drupal\translation_entity\Access\EntityTranslationOverviewAccess
+    arguments: ['@plugin.manager.entity']
+    tags:
+      - { name: access_check }
+  translation_entity.manage_access:
+    class: Drupal\translation_entity\Access\EntityTranslationManageAccess
+    arguments: ['@plugin.manager.entity']
+    tags:
+      - { name: access_check }
