diff --git a/core/lib/Drupal/Core/Entity/EntityAccessController.php b/core/lib/Drupal/Core/Entity/EntityAccessController.php
index 2c9d6fd..15e7ca2 100644
--- a/core/lib/Drupal/Core/Entity/EntityAccessController.php
+++ b/core/lib/Drupal/Core/Entity/EntityAccessController.php
@@ -23,12 +23,25 @@ class EntityAccessController implements EntityAccessControllerInterface {
   protected $accessCache = array();
 
   /**
+   * The entity type of the access controller instance.
+   *
+   * @var string
+   */
+  protected $entity_type;
+
+  /**
+   * @param string $entity_type
+   *   The entity type of the access controller instance.
+   */
+  public function __construct($entity_type) {
+    $this->entity_type = $entity_type;
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function access(EntityInterface $entity, $operation, $langcode = Language::LANGUAGE_DEFAULT, AccountInterface $account = NULL) {
-    if (!$account) {
-      $account = $GLOBALS['user'];
-    }
+    $account = $this->prepareUser($account);
 
     if (($access = $this->getCache($entity, $operation, $langcode, $account)) !== NULL) {
       // Cache hit, no work necessary.
@@ -45,17 +58,35 @@ public function access(EntityInterface $entity, $operation, $langcode = Language
     // - At least one module says to grant access.
     $access = module_invoke_all($entity->entityType() . '_access', $entity->getBCEntity(), $operation, $account, $langcode);
 
+    if ($return = $this->processAccessHookResults($access) === NULL) {
+      // No result from hook, so entity checks are done.
+      $return = (bool) $this->checkAccess($entity, $operation, $langcode, $account);
+    }
+    return $this->setCache($return, $entity, $operation, $langcode, $account);
+  }
+
+  /**
+   * We grant access to the entity if both of these conditions are met:
+   * - No modules say to deny access.
+   * - At least one module says to grant access.
+   */
+  protected function processAccessHookResults($access) {
     if (in_array(FALSE, $access, TRUE)) {
-      $return = FALSE;
+      return FALSE;
     }
     elseif (in_array(TRUE, $access, TRUE)) {
-      $return = TRUE;
+      return TRUE;
     }
     else {
-      // No result from hook, so entity checks are done.
-      $return = (bool) $this->checkAccess($entity, $operation, $langcode, $account);
+      return;
     }
-    return $this->setCache($return, $entity, $operation, $langcode, $account);
+  }
+
+  protected function prepareUser($account) {
+    if (!$account) {
+      $account = $GLOBALS['user'];
+    }
+    return $account;
   }
 
   /**
@@ -85,8 +116,8 @@ protected function checkAccess(EntityInterface $entity, $operation, $langcode, A
   /**
    * Tries to retrieve a previously cached access value from the static cache.
    *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity for which to check 'create' access.
+   * @param \Drupal\Core\Entity\EntityInterface|string $entity
+   *   The entity for which to check access or a custom string.
    * @param string $operation
    *   The entity operation. Usually one of 'view', 'update', 'create' or
    *   'delete'.
@@ -100,13 +131,21 @@ protected function checkAccess(EntityInterface $entity, $operation, $langcode, A
    *   is no record for the given user, operation, langcode and entity in the
    *   cache.
    */
-  protected function getCache(EntityInterface $entity, $operation, $langcode, AccountInterface $account) {
+  protected function getCache($entity, $operation, $langcode, AccountInterface $account) {
     $uid = $account ? $account->id() : 0;
-    $uuid = $entity->uuid();
+
+    // The cache key might be either the UUID of an existing entity or a custom
+    // string, for example used by the createAccess method.
+    if ($entity instanceof EntityInterface) {
+      $cid = $entity->uuid();
+    }
+    else {
+      $cid = $entity;
+    }
 
     // Return from cache if a value has been set for it previously.
-    if (isset($this->accessCache[$uid][$uuid][$langcode][$operation])) {
-      return $this->accessCache[$uid][$uuid][$langcode][$operation];
+    if (isset($this->accessCache[$uid][$cid][$langcode][$operation])) {
+      return $this->accessCache[$uid][$cid][$langcode][$operation];
     }
   }
 
@@ -115,8 +154,8 @@ protected function getCache(EntityInterface $entity, $operation, $langcode, Acco
    *
    * @param bool $access
    *   TRUE if the user has access, FALSE otherwise.
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity for which to check 'create' access.
+   * @param \Drupal\Core\Entity\EntityInterface|string $entity
+   *   The entity for which to check access or a custom string.
    * @param string $operation
    *   The entity operation. Usually one of 'view', 'update', 'create' or
    *   'delete'.
@@ -128,12 +167,21 @@ protected function getCache(EntityInterface $entity, $operation, $langcode, Acco
    * @return bool
    *   TRUE if access was granted, FALSE otherwise.
    */
-  protected function setCache($access, EntityInterface $entity, $operation, $langcode, AccountInterface $account) {
+  protected function setCache($access, $entity, $operation, $langcode, AccountInterface $account) {
     $uid = $account ? $account->id() : 0;
-    $uuid = $entity->uuid();
+
+
+    // The cache key might be either the UUID of an existing entity or a custom
+    // string, for example used by the createAccess method.
+    if ($entity instanceof EntityInterface) {
+      $cid = $entity->uuid();
+    }
+    else {
+      $cid = $entity;
+    }
 
     // Save the given value in the static cache and directly return it.
-    return $this->accessCache[$uid][$uuid][$langcode][$operation] = (bool) $access;
+    return $this->accessCache[$uid][$cid][$langcode][$operation] = (bool) $access;
   }
 
   /**
@@ -143,4 +191,55 @@ public function resetCache() {
     $this->accessCache = array();
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function createAccess($type, AccountInterface $account = NULL, $context = array()) {
+    $account = $this->prepareUser($account);
+    $context += array(
+      'langcode' => Language::LANGCODE_DEFAULT,
+    );
+
+    if (($access = $this->getCache('create', 'create', $context['langcode'], $account)) !== NULL) {
+      // Cache hit, no work necessary.
+      return $access;
+    }
+
+    // Invoke hook_entity_access(), hook results take precedence over overridden
+    // implementations of EntityAccessController::checkAccess(). Entities
+    // that have checks that need to be done before the hook is invoked should
+    // do so by overridding this method.
+
+    // We grant access to the entity if both of these conditions are met:
+    // - No modules say to deny access.
+    // - At least one module says to grant access.
+    $access = module_invoke_all($this->entity_type . '_create_access', $account, $context['langcode']);
+
+    if ($return = $this->processAccessHookResults($access) === NULL) {
+      // No result from hook, so entity checks are done.
+      $return = (bool) $this->checkCreateAccess($access, $context);
+    }
+    return $this->setCache($return, 'create', 'create', $context['langcode'], $account);
+  }
+
+  /**
+   * Performs create access checks.
+   *
+   * This method is supposed to be overwritten by extending classes that
+   * do their own custom access checking.
+   *
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   The user for which to check access.
+   *
+   * @param array $context
+   *   An array of key-value pairs to pass additional context when needed.
+   *
+   * @return bool|null
+   *   TRUE if access was granted, FALSE if access was denied and NULL if access
+   *   could not be determined.
+   */
+  protected function checkCreateAccess(AccountInterface $account, array $context) {
+    return NULL;
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityAccessControllerInterface.php b/core/lib/Drupal/Core/Entity/EntityAccessControllerInterface.php
index f7b4348..9054f98 100644
--- a/core/lib/Drupal/Core/Entity/EntityAccessControllerInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityAccessControllerInterface.php
@@ -36,6 +36,19 @@
   public function access(EntityInterface $entity, $operation, $langcode = Language::LANGCODE_DEFAULT, AccountInterface $account = NULL);
 
   /**
+   *  Checks access to create an entity.
+   *
+   * @param string $type
+   *   The node type to check access for.
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   (optional) The user session for which to check access, or NULL to check
+   *   access for the current user. Defaults to NULL.
+   * @param array $context
+   *   An array of key-value pairs to pass additional context when needed.
+   */
+  public function createAccess($type, AccountInterface $account = NULL, $context = array());
+
+    /**
    * Clears all cached access checks.
    */
   public function resetCache();
diff --git a/core/modules/node/lib/Drupal/node/NodeAccessController.php b/core/modules/node/lib/Drupal/node/NodeAccessController.php
index d7cfd2c..deae4b9 100644
--- a/core/modules/node/lib/Drupal/node/NodeAccessController.php
+++ b/core/modules/node/lib/Drupal/node/NodeAccessController.php
@@ -32,6 +32,41 @@ public function access(EntityInterface $entity, $operation, $langcode = Language
   }
 
   /**
+   * @param string $type
+   *   The node type to check access for.
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   (optional) The user session for which to check access, or NULL to check
+   *   access for the current user. Defaults to NULL.
+   * @param array $context
+   *   An array of key-value pairs to pass additional context when needed.
+   */
+  public function createAccess($type, AccountInterface $account = NULL, $context = array()) {
+    $account = $this->prepareUser($account);
+
+    if (user_access('bypass node access', $account)) {
+      return TRUE;
+    }
+    if (!user_access('access content', $account)) {
+      return FALSE;
+    }
+
+    $access = module_invoke_all('node_create_access', $account, $context);
+
+    $configured_types = node_permissions_get_configured_types();
+    if (isset($configured_types[$type])) {
+      if (user_access('create ' . $type . ' content', $account)) {
+        $access[] = NODE_ACCESS_ALLOW;
+      }
+    }
+
+    if ($return = $this->processAccessHookResults($access) !== NULL) {
+      return $return;
+    }
+
+    return NODE_ACCESS_DENY;
+  }
+
+  /**
    * {@inheritdoc}
    */
   protected function checkAccess(EntityInterface $node, $operation, $langcode, AccountInterface $account) {
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 7057db2..7cb5d6b 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -2155,8 +2155,12 @@ function node_form_system_themes_admin_form_submit($form, &$form_state) {
  * @see node_menu()
  */
 function node_access($op, $node, $account = NULL, $langcode = NULL) {
+  $access_controller = Drupal::entityManager()->getAccessController('node');
+
   if (!$node instanceof EntityInterface) {
     $node = entity_create('node', array('type' => $node));
+    $access_controller->createAccess($node, )
+    $access_controller = entity_access_controller('node');
   }
   elseif ($node instanceof NodeTypeInterface) {
     $node = entity_create('node', array('type' => $node->id()));
@@ -2185,7 +2189,7 @@ function node_access($op, $node, $account = NULL, $langcode = NULL) {
     $account = user_load($account->uid);
   }
 
-  return entity_access_controller('node')->access($node, $op, $langcode, $account);
+  return $access_controller->access($node, $op, $langcode, $account);
 }
 
 /**
diff --git a/core/modules/translation/translation.module b/core/modules/translation/translation.module
index 9c88847..95dbd9f 100644
--- a/core/modules/translation/translation.module
+++ b/core/modules/translation/translation.module
@@ -121,14 +121,14 @@ function translation_permission() {
 }
 
 /**
- * Implements hook_node_access().
+ * Implements hook_node_create_access().
  */
-function translation_node_access($node, $op, $account, $langcode) {
+function translation_node_create_access($account) {
   $query = Drupal::request()->query;
   $translation = $query->get('translation');
   $target = $query->get('target');
   $request_has_translation_arg = !empty($translation) && !empty($target) && is_numeric($translation);
-  if ($op == 'create' && $request_has_translation_arg) {
+  if ($request_has_translation_arg) {
     $source_node = node_load($translation);
     if (empty($source_node) || !translation_user_can_translate_node($source_node, $account)){
       return NODE_ACCESS_DENY;
