From 94cd6717a8bd07eb1333d79fb9ab8aa61d845129 Mon Sep 17 00:00:00 2001
From: Nathaniel Catchpole <catch@35733.no-reply.drupal.org>
Date: Tue, 27 Aug 2013 11:36:16 +0100
Subject: [PATCH] Issue #1939994 by Berdir, rlmumford: Complete conversion of
 nodes to the new Entity Field API.

---
 .../Core/Entity/EntityAccessController.php    |   2 +-
 .../Core/Entity/EntityFormController.php      |   4 +-
 core/modules/book/book.module                 |  10 ++
 .../book/lib/Drupal/book/Tests/BookTest.php   |   4 +-
 .../templates/book-node-export-html.html.twig |   2 +-
 core/modules/comment/comment.module           |  24 +--
 core/modules/comment/comment.pages.inc        |   2 +-
 .../Drupal/comment/CommentFormController.php  |   2 +-
 .../Drupal/comment/Tests/CommentLinksTest.php |   2 +-
 .../Tests/EntityReferenceAutoCreateTest.php   |   2 +-
 .../Drupal/field/Plugin/views/field/Field.php |   8 +-
 .../Tests/Views/HandlerFieldFieldTest.php     |  12 +-
 .../file/Tests/FileFieldDisplayTest.php       |   2 +-
 .../Drupal/file/Tests/FileFieldPathTest.php   |   6 +-
 .../file/Tests/FileFieldRSSContentTest.php    |   2 +-
 .../file/Tests/FileFieldRevisionTest.php      |  10 +-
 .../file/Tests/FileFieldValidateTest.php      |  12 +-
 .../Drupal/file/Tests/FileFieldWidgetTest.php |   8 +-
 .../lib/Drupal/file/Tests/FileListingTest.php |   2 +-
 .../lib/Drupal/file/Tests/FilePrivateTest.php |   4 +-
 .../file/Tests/FileTokenReplaceTest.php       |   2 +-
 .../filter/Tests/FilterSecurityTest.php       |   4 +-
 core/modules/forum/forum.module               |  33 ++--
 .../lib/Drupal/forum/Tests/ForumTest.php      |   2 +-
 .../templates/forum-topic-list.html.twig      |   8 +-
 .../image/Tests/ImageFieldDisplayTest.php     |   8 +-
 .../node/lib/Drupal/node/Entity/Node.php      |  16 +-
 .../node/lib/Drupal/node/NodeBCDecorator.php  | 148 ------------------
 .../lib/Drupal/node/NodeFormController.php    |  46 ++++--
 .../Drupal/node/NodeGrantDatabaseStorage.php  |   2 +-
 .../lib/Drupal/node/NodeStorageController.php |  32 +---
 .../lib/Drupal/node/Plugin/views/row/Rss.php  |   2 +-
 .../Tests/NodeFieldMultilingualTestCase.php   |   6 +-
 .../node/Tests/NodeRevisionsAllTestCase.php   |   6 +-
 .../Drupal/node/Tests/NodeRevisionsTest.php   |  12 +-
 .../lib/Drupal/node/Tests/NodeSaveTest.php    |   2 +-
 .../node/Tests/NodeTokenReplaceTest.php       |  15 +-
 .../node/Tests/NodeTranslationUITest.php      |   2 +-
 .../Drupal/node/Tests/Views/RowPluginTest.php |  16 +-
 core/modules/node/node.api.php                |   4 +-
 core/modules/node/node.module                 |  45 +-----
 core/modules/node/node.pages.inc              |  17 +-
 core/modules/node/node.tokens.inc             |   2 +-
 core/modules/node/node.views.inc              |   2 +-
 .../node_access_test/node_access_test.module  |  29 +++-
 .../node_access_test_language.module          |   3 +-
 .../tests/modules/node_test/node_test.module  |   6 +-
 core/modules/path/path.module                 | 101 ++++--------
 .../picture/Tests/PictureFieldDisplayTest.php |   2 +-
 .../rdf/Tests/FileFieldAttributesTest.php     |   2 +-
 .../rdf/Tests/ImageFieldAttributesTest.php    |   2 +-
 .../Tests/SearchMultilingualEntityTest.php    |   6 +-
 .../system/Tests/Plugin/PluginTestBase.php    |   2 +-
 .../Tests/Upgrade/FieldUpgradePathTest.php    |  17 +-
 .../plugin_test/Plugin/MockBlockManager.php   |   2 +-
 .../Drupal/taxonomy/Tests/TermIndexTest.php   |   4 +-
 core/modules/taxonomy/taxonomy.module         |  19 +--
 .../translation/Tests/TranslationTest.php     |  23 ++-
 core/modules/translation/translation.module   |  74 +++++----
 .../modules/translation/translation.pages.inc |  13 +-
 .../Drupal/views/Tests/DefaultViewsTest.php   |   2 +-
 61 files changed, 309 insertions(+), 550 deletions(-)
 delete mode 100644 core/modules/node/lib/Drupal/node/NodeBCDecorator.php

diff --git a/core/lib/Drupal/Core/Entity/EntityAccessController.php b/core/lib/Drupal/Core/Entity/EntityAccessController.php
index 164b83e4dfa7..4658d50f1974 100644
--- a/core/lib/Drupal/Core/Entity/EntityAccessController.php
+++ b/core/lib/Drupal/Core/Entity/EntityAccessController.php
@@ -58,7 +58,7 @@ public function access(EntityInterface $entity, $operation, $langcode = Language
     // 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($entity->entityType() . '_access', $entity->getBCEntity(), $operation, $account, $langcode);
+    $access = module_invoke_all($entity->entityType() . '_access', $entity, $operation, $account, $langcode);
 
     if (($return = $this->processAccessHookResults($access)) === NULL) {
       // No module had an opinion about the access, so let's the access
diff --git a/core/lib/Drupal/Core/Entity/EntityFormController.php b/core/lib/Drupal/Core/Entity/EntityFormController.php
index 372bf359a77e..bc84d1677064 100644
--- a/core/lib/Drupal/Core/Entity/EntityFormController.php
+++ b/core/lib/Drupal/Core/Entity/EntityFormController.php
@@ -480,9 +480,7 @@ public function getEntity() {
    */
   protected function getTranslatedEntity(array $form_state) {
     $langcode = $this->getFormLangcode($form_state);
-    $translation = $this->entity->getTranslation($langcode);
-    // Ensure that the entity object is a BC entity if the original one is.
-    return $this->entity instanceof EntityBCDecorator ? $translation->getBCEntity() : $translation;
+    return $this->entity->getTranslation($langcode);
   }
 
   /**
diff --git a/core/modules/book/book.module b/core/modules/book/book.module
index e9e863e785cd..525056e15558 100644
--- a/core/modules/book/book.module
+++ b/core/modules/book/book.module
@@ -327,9 +327,19 @@ function book_form_node_form_alter(&$form, &$form_state, $form_id) {
         'css' => array(drupal_get_path('module', 'book') . '/css/book.admin.css'),
       ),
     );
+    $form['#entity_builders'][] = 'book_node_builder';
   }
 }
 
+/**
+ * Entity form builder to add the book information to the node.
+ *
+ * @todo: Remove this in favor of an entity field.
+ */
+function book_node_builder($entity_type, $entity, &$form, &$form_state) {
+  $entity->book = $form_state['values']['book'];
+}
+
 /**
  * Form submission handler for node_form().
  *
diff --git a/core/modules/book/lib/Drupal/book/Tests/BookTest.php b/core/modules/book/lib/Drupal/book/Tests/BookTest.php
index 85c8c85e357c..f4ad8754bd80 100644
--- a/core/modules/book/lib/Drupal/book/Tests/BookTest.php
+++ b/core/modules/book/lib/Drupal/book/Tests/BookTest.php
@@ -206,7 +206,7 @@ function checkBookNode(EntityInterface $node, $nodes, $previous = FALSE, $up = F
     // Check printer friendly version.
     $this->drupalGet('book/export/html/' . $node->id());
     $this->assertText($node->label(), 'Printer friendly title found.');
-    $this->assertRaw(check_markup($node->body[Language::LANGCODE_NOT_SPECIFIED][0]['value'], $node->body[Language::LANGCODE_NOT_SPECIFIED][0]['format']), 'Printer friendly body found.');
+    $this->assertRaw($node->body->processed, 'Printer friendly body found.');
 
     $number++;
   }
@@ -281,7 +281,7 @@ function testBookExport() {
     // Make sure each part of the book is there.
     foreach ($nodes as $node) {
       $this->assertText($node->label(), 'Node title found in printer friendly version.');
-      $this->assertRaw(check_markup($node->body[Language::LANGCODE_NOT_SPECIFIED][0]['value'], $node->body[Language::LANGCODE_NOT_SPECIFIED][0]['format']), 'Node body found in printer friendly version.');
+      $this->assertRaw($node->body->processed, 'Node body found in printer friendly version.');
     }
 
     // Make sure we can't export an unsupported format.
diff --git a/core/modules/book/templates/book-node-export-html.html.twig b/core/modules/book/templates/book-node-export-html.html.twig
index 7719bc7339a0..0efa9a7adcf4 100644
--- a/core/modules/book/templates/book-node-export-html.html.twig
+++ b/core/modules/book/templates/book-node-export-html.html.twig
@@ -15,7 +15,7 @@
  * @ingroup themeable
  */
 #}
-<article id="node-{{ node.nid }}" class="section-{{ depth }}">
+<article id="node-{{ node.id }}" class="section-{{ depth }}">
   <h1 class="book-heading">{{ title }}</h1>
   {{ content }}
   {{ children }}
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index b7869fbe8acb..ef12cc11bee3 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -516,7 +516,7 @@ function theme_comment_block($variables) {
 function comment_node_view(EntityInterface $node, EntityDisplay $display, $view_mode) {
   $links = array();
 
-  if ($node->comment != COMMENT_NODE_HIDDEN) {
+  if ($node->comment->value != COMMENT_NODE_HIDDEN) {
     if ($view_mode == 'rss') {
       // Add a comments RSS element which is a URL to the comments of this node.
       $node->rss_elements[] = array(
@@ -550,7 +550,7 @@ function comment_node_view(EntityInterface $node, EntityDisplay $display, $view_
           }
         }
       }
-      if ($node->comment == COMMENT_NODE_OPEN) {
+      if ($node->comment->value == COMMENT_NODE_OPEN) {
         $comment_form_location = variable_get('comment_form_location_' . $node->getType(), COMMENT_FORM_BELOW);
         if (user_access('post comments')) {
           $links['comment-add'] = array(
@@ -580,7 +580,7 @@ function comment_node_view(EntityInterface $node, EntityDisplay $display, $view_
       // allowed to post comments and if this node is allowing new comments.
       // But we don't want this link if we're building the node for search
       // indexing or constructing a search result excerpt.
-      if ($node->comment == COMMENT_NODE_OPEN) {
+      if ($node->comment->value == COMMENT_NODE_OPEN) {
         $comment_form_location = variable_get('comment_form_location_' . $node->getType(), COMMENT_FORM_BELOW);
         if (user_access('post comments')) {
           // Show the "post comment" link if the form is on another page, or
@@ -655,7 +655,7 @@ function comment_node_page_additions(EntityInterface $node) {
   }
 
   // Append comment form if needed.
-  if (user_access('post comments') && $node->comment == COMMENT_NODE_OPEN && (variable_get('comment_form_location_' . $node->getType(), COMMENT_FORM_BELOW) == COMMENT_FORM_BELOW)) {
+  if (user_access('post comments') && $node->comment->value == COMMENT_NODE_OPEN && (variable_get('comment_form_location_' . $node->getType(), COMMENT_FORM_BELOW) == COMMENT_FORM_BELOW)) {
     $additions['comment_form'] = comment_add($node);
   }
 
@@ -874,7 +874,7 @@ function comment_view(Comment $comment, $view_mode = 'full', $langcode = NULL) {
  */
 function comment_links(Comment $comment, EntityInterface $node) {
   $links = array();
-  if ($node->comment == COMMENT_NODE_OPEN) {
+  if ($node->comment->value == COMMENT_NODE_OPEN) {
     if ($comment->access('delete')) {
       $links['comment-delete'] = array(
         'title' => t('delete'),
@@ -1072,7 +1072,7 @@ function comment_form_node_form_alter(&$form, $form_state) {
     '#weight' => 30,
   );
   $comment_count = $node->id() ? db_query('SELECT comment_count FROM {node_comment_statistics} WHERE nid = :nid', array(':nid' => $node->id()))->fetchField() : 0;
-  $comment_settings = ($node->comment == COMMENT_NODE_HIDDEN && empty($comment_count)) ? COMMENT_NODE_CLOSED : $node->comment;
+  $comment_settings = ($node->comment->value == COMMENT_NODE_HIDDEN && empty($comment_count)) ? COMMENT_NODE_CLOSED : $node->comment->value;
   $form['comment_settings']['comment'] = array(
     '#type' => 'radios',
     '#title' => t('Comments'),
@@ -1113,7 +1113,7 @@ function comment_node_load($nodes, $types) {
   // assign values without hitting the database.
   foreach ($nodes as $node) {
     // Store whether comments are enabled for this node.
-    if ($node->comment != COMMENT_NODE_HIDDEN) {
+    if ($node->comment->value != COMMENT_NODE_HIDDEN) {
       $comments_enabled[] = $node->id();
     }
     else {
@@ -1142,7 +1142,7 @@ function comment_node_load($nodes, $types) {
  * Implements hook_node_prepare_form().
  */
 function comment_node_prepare_form(NodeInterface $node, $form_display, $operation, array &$form_state) {
-  if (!isset($node->comment)) {
+  if (!isset($node->comment->value)) {
     $node->comment = variable_get('comment_' . $node->getType(), COMMENT_NODE_OPEN);
   }
 }
@@ -1209,8 +1209,8 @@ function comment_node_update_index(EntityInterface $node, $langcode) {
 
   if ($index_comments) {
     $mode = variable_get('comment_default_mode_' . $node->getType(), COMMENT_MODE_THREADED);
-    $comments_per_page = variable_get('comment_default_per_page_' . $node->getType(), 50);
-    if ($node->comment && $cids = comment_get_thread($node, $mode, $comments_per_page)) {
+    $comments_per_page = variable_get('comment_default_per_page_' . $node->bundle(), 50);
+    if ($node->comment->value && $cids = comment_get_thread($node, $mode, $comments_per_page)) {
       $comments = comment_load_multiple($cids);
       comment_prepare_thread($comments);
       $build = comment_view_multiple($comments, $langcode);
@@ -1236,11 +1236,11 @@ function comment_update_index() {
  */
 function comment_node_search_result(EntityInterface $node) {
   // Do not make a string if comments are hidden.
-  if (user_access('access comments') && $node->comment != COMMENT_NODE_HIDDEN) {
+  if (user_access('access comments') && $node->comment->value != COMMENT_NODE_HIDDEN) {
     $comments = db_query('SELECT comment_count FROM {node_comment_statistics} WHERE nid = :nid', array('nid' => $node->id()))->fetchField();
     // Do not make a string if comments are closed and there are currently
     // zero comments.
-    if ($node->comment != COMMENT_NODE_CLOSED || $comments > 0) {
+    if ($node->comment->value != COMMENT_NODE_CLOSED || $comments > 0) {
       return array('comment' => format_plural($comments, '1 comment', '@count comments'));
     }
   }
diff --git a/core/modules/comment/comment.pages.inc b/core/modules/comment/comment.pages.inc
index b11b4938be51..d193132b84ff 100644
--- a/core/modules/comment/comment.pages.inc
+++ b/core/modules/comment/comment.pages.inc
@@ -84,7 +84,7 @@ function comment_reply(EntityInterface $node, $pid = NULL) {
     }
 
     // Should we show the reply box?
-    if ($node->comment != COMMENT_NODE_OPEN) {
+    if ($node->comment->value != COMMENT_NODE_OPEN) {
       drupal_set_message(t("This discussion is closed: you can't post new comments."), 'error');
       return new RedirectResponse(url('node/' . $node->id(), array('absolute' => TRUE)));
     }
diff --git a/core/modules/comment/lib/Drupal/comment/CommentFormController.php b/core/modules/comment/lib/Drupal/comment/CommentFormController.php
index ad4b9a007f79..750f7481db14 100644
--- a/core/modules/comment/lib/Drupal/comment/CommentFormController.php
+++ b/core/modules/comment/lib/Drupal/comment/CommentFormController.php
@@ -318,7 +318,7 @@ public function save(array $form, array &$form_state) {
     $node = node_load($form_state['values']['nid']);
     $comment = $this->entity;
 
-    if (user_access('post comments') && (user_access('administer comments') || $node->comment == COMMENT_NODE_OPEN)) {
+    if (user_access('post comments') && (user_access('administer comments') || $node->comment->value == COMMENT_NODE_OPEN)) {
       // Save the anonymous user information to a cookie for reuse.
       if (user_is_anonymous()) {
         user_cookie_save(array_intersect_key($form_state['values'], array_flip(array('name', 'mail', 'homepage'))));
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentLinksTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentLinksTest.php
index fd86097fba36..bf7cdb5b255c 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentLinksTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentLinksTest.php
@@ -169,7 +169,7 @@ function setEnvironment(array $info) {
     // Change comment settings.
     variable_set('comment_form_location_' . $this->node->getType(), $info['form']);
     variable_set('comment_anonymous_' . $this->node->getType(), $info['contact']);
-    if ($this->node->comment != $info['comments']) {
+    if ($this->node->comment->value != $info['comments']) {
       $this->node->comment = $info['comments'];
       $this->node->save();
     }
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceAutoCreateTest.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceAutoCreateTest.php
index f5f74d0a95d0..a19e1a97ce49 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceAutoCreateTest.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceAutoCreateTest.php
@@ -113,7 +113,7 @@ public function testAutoCreate() {
 
     $referencing_nid = key($result);
     $referencing_node = node_load($referencing_nid);
-    $this->assertEqual($referenced_nid, $referencing_node->test_field[Language::LANGCODE_NOT_SPECIFIED][0]['target_id'], 'Newly created node is referenced from the referencing node.');
+    $this->assertEqual($referenced_nid, $referencing_node->test_field->target_id, 'Newly created node is referenced from the referencing node.');
 
     // Now try to view the node and check that the referenced node is shown.
     $this->drupalGet('node/' . $referencing_node->id());
diff --git a/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php b/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php
index fdfea1062825..a487a7afc1c5 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php
@@ -726,10 +726,10 @@ function process_entity(EntityInterface $entity) {
       if ($data) {
         // Now, overwrite the original value with our aggregated value.
         // This overwrites it so there is always just one entry.
-        $processed_entity->{$this->definition['field_name']}[$langcode] = array($base_value);
+        $processed_entity->getTranslation($langcode)->{$this->definition['field_name']} = array($base_value);
       }
       else {
-        $processed_entity->{$this->definition['field_name']}[$langcode] = array();
+        $processed_entity->getTranslation($langcode)->{$this->definition['field_name']} = array();
       }
     }
 
@@ -740,7 +740,7 @@ function process_entity(EntityInterface $entity) {
 
     // We are supposed to show only certain deltas.
     if ($this->limit_values && !empty($processed_entity->{$this->definition['field_name']})) {
-      $all_values = !empty($processed_entity->{$this->definition['field_name']}[$langcode]) ? $processed_entity->{$this->definition['field_name']}[$langcode] : array();
+      $all_values = !empty($processed_entity->getTranslation($langcode)->{$this->definition['field_name']}) ? $processed_entity->getTranslation($langcode)->{$this->definition['field_name']}->getValue() : array();
       if ($this->options['delta_reversed']) {
         $all_values = array_reverse($all_values);
       }
@@ -786,7 +786,7 @@ function process_entity(EntityInterface $entity) {
           }
         }
       }
-      $processed_entity->{$this->definition['field_name']}[$langcode] = $new_values;
+      $processed_entity->getTranslation($langcode)->{$this->definition['field_name']} = $new_values;
     }
 
     return $processed_entity;
diff --git a/core/modules/field/lib/Drupal/field/Tests/Views/HandlerFieldFieldTest.php b/core/modules/field/lib/Drupal/field/Tests/Views/HandlerFieldFieldTest.php
index 8658c523e659..c42950d55743 100644
--- a/core/modules/field/lib/Drupal/field/Tests/Views/HandlerFieldFieldTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/Views/HandlerFieldFieldTest.php
@@ -107,7 +107,7 @@ public function _testSimpleFieldRender() {
       for ($key = 0; $key < 2; $key++) {
         $field = $this->fields[$key];
         $rendered_field = $view->style_plugin->getField($i, $field['field_name']);
-        $expected_field = $this->nodes[$i]->{$field['field_name']}[Language::LANGCODE_NOT_SPECIFIED][0]['value'];
+        $expected_field = $this->nodes[$i]->{$field['field_name']}->value;
         $this->assertEqual($rendered_field, $expected_field);
       }
     }
@@ -146,7 +146,7 @@ public function _testMultipleFieldRender() {
     for ($i = 0; $i < 3; $i++) {
       $rendered_field = $view->style_plugin->getField($i, $field_name);
       $items = array();
-      $pure_items = $this->nodes[$i]->{$field_name}[Language::LANGCODE_NOT_SPECIFIED];
+      $pure_items = $this->nodes[$i]->{$field_name}->getValue();
       $pure_items = array_splice($pure_items, 0, 3);
       foreach ($pure_items as $j => $item) {
         $items[] = $pure_items[$j]['value'];
@@ -169,7 +169,7 @@ public function _testMultipleFieldRender() {
     for ($i = 0; $i < 3; $i++) {
       $rendered_field = $view->style_plugin->getField($i, $field_name);
       $items = array();
-      $pure_items = $this->nodes[$i]->{$field_name}[Language::LANGCODE_NOT_SPECIFIED];
+      $pure_items = $this->nodes[$i]->{$field_name}->getValue();
       $pure_items = array_splice($pure_items, 1, 3);
       foreach ($pure_items as $j => $item) {
         $items[] = $pure_items[$j]['value'];
@@ -189,7 +189,7 @@ public function _testMultipleFieldRender() {
     for ($i = 0; $i < 3; $i++) {
       $rendered_field = $view->style_plugin->getField($i, $field_name);
       $items = array();
-      $pure_items = $this->nodes[$i]->{$field_name}[Language::LANGCODE_NOT_SPECIFIED];
+      $pure_items = $this->nodes[$i]->{$field_name}->getValue();
       array_splice($pure_items, 0, -3);
       $pure_items = array_reverse($pure_items);
       foreach ($pure_items as $j => $item) {
@@ -210,7 +210,7 @@ public function _testMultipleFieldRender() {
     for ($i = 0; $i < 3; $i++) {
       $rendered_field = $view->style_plugin->getField($i, $field_name);
       $items = array();
-      $pure_items = $this->nodes[$i]->{$field_name}[Language::LANGCODE_NOT_SPECIFIED];
+      $pure_items = $this->nodes[$i]->{$field_name}->getValue();
       $items[] = $pure_items[0]['value'];
       $items[] = $pure_items[4]['value'];
       $this->assertEqual($rendered_field, implode(', ', $items), 'Take sure that the amount of items are limited.');
@@ -228,7 +228,7 @@ public function _testMultipleFieldRender() {
     for ($i = 0; $i < 3; $i++) {
       $rendered_field = $view->style_plugin->getField($i, $field_name);
       $items = array();
-      $pure_items = $this->nodes[$i]->{$field_name}[Language::LANGCODE_NOT_SPECIFIED];
+      $pure_items = $this->nodes[$i]->{$field_name}->getValue();
       $pure_items = array_splice($pure_items, 0, 3);
       foreach ($pure_items as $j => $item) {
         $items[] = $pure_items[$j]['value'];
diff --git a/core/modules/file/lib/Drupal/file/Tests/FileFieldDisplayTest.php b/core/modules/file/lib/Drupal/file/Tests/FileFieldDisplayTest.php
index 4ef654dc981f..7f3f5c8bba94 100644
--- a/core/modules/file/lib/Drupal/file/Tests/FileFieldDisplayTest.php
+++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldDisplayTest.php
@@ -60,7 +60,7 @@ function testNodeDisplay() {
 
     // Check that the default formatter is displaying with the file name.
     $node = node_load($nid, TRUE);
-    $node_file = file_load($node->{$field_name}[Language::LANGCODE_NOT_SPECIFIED][0]['target_id']);
+    $node_file = file_load($node->{$field_name}->target_id);
     $file_link = array(
       '#theme' => 'file_link',
       '#file' => $node_file,
diff --git a/core/modules/file/lib/Drupal/file/Tests/FileFieldPathTest.php b/core/modules/file/lib/Drupal/file/Tests/FileFieldPathTest.php
index f2c45b670bfd..374b5fd06916 100644
--- a/core/modules/file/lib/Drupal/file/Tests/FileFieldPathTest.php
+++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldPathTest.php
@@ -35,7 +35,7 @@ function testUploadPath() {
 
     // Check that the file was uploaded to the file root.
     $node = node_load($nid, TRUE);
-    $node_file = file_load($node->{$field_name}[Language::LANGCODE_NOT_SPECIFIED][0]['target_id']);
+    $node_file = file_load($node->{$field_name}->target_id);
     $this->assertPathMatch('public://' . $test_file->getFilename(), $node_file->getFileUri(), format_string('The file %file was uploaded to the correct path.', array('%file' => $node_file->getFileUri())));
 
     // Change the path to contain multiple subdirectories.
@@ -46,7 +46,7 @@ function testUploadPath() {
 
     // Check that the file was uploaded into the subdirectory.
     $node = node_load($nid, TRUE);
-    $node_file = file_load($node->{$field_name}[Language::LANGCODE_NOT_SPECIFIED][0]['target_id'], TRUE);
+    $node_file = file_load($node->{$field_name}->target_id, TRUE);
     $this->assertPathMatch('public://foo/bar/baz/' . $test_file->getFilename(), $node_file->getFileUri(), format_string('The file %file was uploaded to the correct path.', array('%file' => $node_file->getFileUri())));
 
     // Check the path when used with tokens.
@@ -58,7 +58,7 @@ function testUploadPath() {
 
     // Check that the file was uploaded into the subdirectory.
     $node = node_load($nid, TRUE);
-    $node_file = file_load($node->{$field_name}[Language::LANGCODE_NOT_SPECIFIED][0]['target_id']);
+    $node_file = file_load($node->{$field_name}->target_id);
     // Do token replacement using the same user which uploaded the file, not
     // the user running the test case.
     $data = array('user' => $this->admin_user);
diff --git a/core/modules/file/lib/Drupal/file/Tests/FileFieldRSSContentTest.php b/core/modules/file/lib/Drupal/file/Tests/FileFieldRSSContentTest.php
index d031ffe492bb..7f051f5f201c 100644
--- a/core/modules/file/lib/Drupal/file/Tests/FileFieldRSSContentTest.php
+++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldRSSContentTest.php
@@ -67,7 +67,7 @@ function testFileFieldRSSContent() {
 
     // Get the uploaded file from the node.
     $node = node_load($nid, TRUE);
-    $node_file = file_load($node->{$field_name}[Language::LANGCODE_NOT_SPECIFIED][0]['target_id']);
+    $node_file = file_load($node->{$field_name}->target_id);
 
     // Check that the RSS enclosure appears in the RSS feed.
     $this->drupalGet('rss.xml');
diff --git a/core/modules/file/lib/Drupal/file/Tests/FileFieldRevisionTest.php b/core/modules/file/lib/Drupal/file/Tests/FileFieldRevisionTest.php
index a6225b501062..c7c2c72164d9 100644
--- a/core/modules/file/lib/Drupal/file/Tests/FileFieldRevisionTest.php
+++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldRevisionTest.php
@@ -47,7 +47,7 @@ function testRevisions() {
 
     // Check that the file exists on disk and in the database.
     $node = node_load($nid, TRUE);
-    $node_file_r1 = file_load($node->{$field_name}[Language::LANGCODE_NOT_SPECIFIED][0]['target_id']);
+    $node_file_r1 = file_load($node->{$field_name}->target_id);
     $node_vid_r1 = $node->getRevisionId();
     $this->assertFileExists($node_file_r1, 'New file saved to disk on node creation.');
     $this->assertFileEntryExists($node_file_r1, 'File entry exists in database on node creation.');
@@ -56,7 +56,7 @@ function testRevisions() {
     // Upload another file to the same node in a new revision.
     $this->replaceNodeFile($test_file, $field_name, $nid);
     $node = node_load($nid, TRUE);
-    $node_file_r2 = file_load($node->{$field_name}[Language::LANGCODE_NOT_SPECIFIED][0]['target_id']);
+    $node_file_r2 = file_load($node->{$field_name}->target_id);
     $node_vid_r2 = $node->getRevisionId();
     $this->assertFileExists($node_file_r2, 'Replacement file exists on disk after creating new revision.');
     $this->assertFileEntryExists($node_file_r2, 'Replacement file entry exists in database after creating new revision.');
@@ -64,7 +64,7 @@ function testRevisions() {
 
     // Check that the original file is still in place on the first revision.
     $node = node_revision_load($node_vid_r1);
-    $current_file = file_load($node->{$field_name}[Language::LANGCODE_NOT_SPECIFIED][0]['target_id']);
+    $current_file = file_load($node->{$field_name}->target_id);
     $this->assertEqual($node_file_r1->id(), $current_file->id(), 'Original file still in place after replacing file in new revision.');
     $this->assertFileExists($node_file_r1, 'Original file still in place after replacing file in new revision.');
     $this->assertFileEntryExists($node_file_r1, 'Original file entry still in place after replacing file in new revision');
@@ -74,7 +74,7 @@ function testRevisions() {
     // Check that the file is still the same as the previous revision.
     $this->drupalPost('node/' . $nid . '/edit', array('revision' => '1'), t('Save and keep published'));
     $node = node_load($nid, TRUE);
-    $node_file_r3 = file_load($node->{$field_name}[Language::LANGCODE_NOT_SPECIFIED][0]['target_id']);
+    $node_file_r3 = file_load($node->{$field_name}->target_id);
     $node_vid_r3 = $node->getRevisionId();
     $this->assertEqual($node_file_r2->id(), $node_file_r3->id(), 'Previous revision file still in place after creating a new revision without a new file.');
     $this->assertFileIsPermanent($node_file_r3, 'New revision file is permanent.');
@@ -82,7 +82,7 @@ function testRevisions() {
     // Revert to the first revision and check that the original file is active.
     $this->drupalPost('node/' . $nid . '/revisions/' . $node_vid_r1 . '/revert', array(), t('Revert'));
     $node = node_load($nid, TRUE);
-    $node_file_r4 = file_load($node->{$field_name}[Language::LANGCODE_NOT_SPECIFIED][0]['target_id']);
+    $node_file_r4 = file_load($node->{$field_name}->target_id);
     $node_vid_r4 = $node->getRevisionId();
     $this->assertEqual($node_file_r1->id(), $node_file_r4->id(), 'Original revision file still in place after reverting to the original revision.');
     $this->assertFileIsPermanent($node_file_r4, 'Original revision file still permanent after reverting to the original revision.');
diff --git a/core/modules/file/lib/Drupal/file/Tests/FileFieldValidateTest.php b/core/modules/file/lib/Drupal/file/Tests/FileFieldValidateTest.php
index a7cf1e57bc03..2b3019cdf32d 100644
--- a/core/modules/file/lib/Drupal/file/Tests/FileFieldValidateTest.php
+++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldValidateTest.php
@@ -47,7 +47,7 @@ function testRequired() {
 
     $node = node_load($nid, TRUE);
 
-    $node_file = file_load($node->{$field_name}[Language::LANGCODE_NOT_SPECIFIED][0]['target_id']);
+    $node_file = file_load($node->{$field_name}->target_id);
     $this->assertFileExists($node_file, 'File exists after uploading to the required field.');
     $this->assertFileEntryExists($node_file, 'File entry exists after uploading to the required field.');
 
@@ -63,7 +63,7 @@ function testRequired() {
     // Create a new node with the uploaded file into the multivalue field.
     $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
     $node = node_load($nid, TRUE);
-    $node_file = file_load($node->{$field_name}[Language::LANGCODE_NOT_SPECIFIED][0]['target_id']);
+    $node_file = file_load($node->{$field_name}->target_id);
     $this->assertFileExists($node_file, 'File exists after uploading to the required multiple value field.');
     $this->assertFileEntryExists($node_file, 'File entry exists after uploading to the required multiple value field.');
   }
@@ -93,7 +93,7 @@ function testFileMaxSize() {
       // Create a new node with the small file, which should pass.
       $nid = $this->uploadNodeFile($small_file, $field_name, $type_name);
       $node = node_load($nid, TRUE);
-      $node_file = file_load($node->{$field_name}[Language::LANGCODE_NOT_SPECIFIED][0]['target_id']);
+      $node_file = file_load($node->{$field_name}->target_id);
       $this->assertFileExists($node_file, format_string('File exists after uploading a file (%filesize) under the max limit (%maxsize).', array('%filesize' => format_size($small_file->getSize()), '%maxsize' => $max_filesize)));
       $this->assertFileEntryExists($node_file, format_string('File entry exists after uploading a file (%filesize) under the max limit (%maxsize).', array('%filesize' => format_size($small_file->getSize()), '%maxsize' => $max_filesize)));
 
@@ -109,7 +109,7 @@ function testFileMaxSize() {
     // Upload the big file successfully.
     $nid = $this->uploadNodeFile($large_file, $field_name, $type_name);
     $node = node_load($nid, TRUE);
-    $node_file = file_load($node->{$field_name}[Language::LANGCODE_NOT_SPECIFIED][0]['target_id']);
+    $node_file = file_load($node->{$field_name}->target_id);
     $this->assertFileExists($node_file, format_string('File exists after uploading a file (%filesize) with no max limit.', array('%filesize' => format_size($large_file->getSize()))));
     $this->assertFileEntryExists($node_file, format_string('File entry exists after uploading a file (%filesize) with no max limit.', array('%filesize' => format_size($large_file->getSize()))));
   }
@@ -131,7 +131,7 @@ function testFileExtension() {
     // Check that the file can be uploaded with no extension checking.
     $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
     $node = node_load($nid, TRUE);
-    $node_file = file_load($node->{$field_name}[Language::LANGCODE_NOT_SPECIFIED][0]['target_id']);
+    $node_file = file_load($node->{$field_name}->target_id);
     $this->assertFileExists($node_file, 'File exists after uploading a file with no extension checking.');
     $this->assertFileEntryExists($node_file, 'File entry exists after uploading a file with no extension checking.');
 
@@ -149,7 +149,7 @@ function testFileExtension() {
     // Check that the file can be uploaded with extension checking.
     $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
     $node = node_load($nid, TRUE);
-    $node_file = file_load($node->{$field_name}[Language::LANGCODE_NOT_SPECIFIED][0]['target_id']);
+    $node_file = file_load($node->{$field_name}->target_id);
     $this->assertFileExists($node_file, 'File exists after uploading a file with extension checking.');
     $this->assertFileEntryExists($node_file, 'File entry exists after uploading a file with extension checking.');
   }
diff --git a/core/modules/file/lib/Drupal/file/Tests/FileFieldWidgetTest.php b/core/modules/file/lib/Drupal/file/Tests/FileFieldWidgetTest.php
index e48138470e04..24a54752f482 100644
--- a/core/modules/file/lib/Drupal/file/Tests/FileFieldWidgetTest.php
+++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldWidgetTest.php
@@ -46,7 +46,7 @@ function testSingleValuedWidget() {
       //   does not yet support file uploads.
       $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
       $node = node_load($nid, TRUE);
-      $node_file = file_load($node->{$field_name}[Language::LANGCODE_NOT_SPECIFIED][0]['target_id']);
+      $node_file = file_load($node->{$field_name}->target_id);
       $this->assertFileExists($node_file, 'New file saved to disk on node creation.');
 
       // Ensure the file can be downloaded.
@@ -79,7 +79,7 @@ function testSingleValuedWidget() {
       // Save the node and ensure it does not have the file.
       $this->drupalPost(NULL, array(), t('Save and keep published'));
       $node = node_load($nid, TRUE);
-      $this->assertTrue(empty($node->{$field_name}[Language::LANGCODE_NOT_SPECIFIED][0]['target_id']), 'File was successfully removed from the node.');
+      $this->assertTrue(empty($node->{$field_name}->target_id), 'File was successfully removed from the node.');
     }
   }
 
@@ -196,7 +196,7 @@ function testMultiValuedWidget() {
       preg_match('/node\/([0-9]+)/', $this->getUrl(), $matches);
       $nid = $matches[1];
       $node = node_load($nid, TRUE);
-      $this->assertTrue(empty($node->{$field_name}[Language::LANGCODE_NOT_SPECIFIED][0]['target_id']), 'Node was successfully saved without any files.');
+      $this->assertTrue(empty($node->{$field_name}->target_id), 'Node was successfully saved without any files.');
     }
   }
 
@@ -216,7 +216,7 @@ function testPrivateFileSetting() {
     $this->drupalPost("admin/structure/types/manage/$type_name/fields/$instance->id/field", $edit, t('Save field settings'));
     $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
     $node = node_load($nid, TRUE);
-    $node_file = file_load($node->{$field_name}[Language::LANGCODE_NOT_SPECIFIED][0]['target_id']);
+    $node_file = file_load($node->{$field_name}->target_id);
     $this->assertFileExists($node_file, 'New file saved to disk on node creation.');
 
     // Ensure the private file is available to the user who uploaded it.
diff --git a/core/modules/file/lib/Drupal/file/Tests/FileListingTest.php b/core/modules/file/lib/Drupal/file/Tests/FileListingTest.php
index 693f23693176..69842eb3465a 100644
--- a/core/modules/file/lib/Drupal/file/Tests/FileListingTest.php
+++ b/core/modules/file/lib/Drupal/file/Tests/FileListingTest.php
@@ -82,7 +82,7 @@ function testFileListingPages() {
         'files[file_' . Language::LANGCODE_NOT_SPECIFIED . '_' . 0 . ']' => drupal_realpath($file->getFileUri()),
       );
       $this->drupalPost(NULL, $edit, t('Save'));
-      $node = entity_load('node', $node->id())->getNGEntity();
+      $node = entity_load('node', $node->id());
     }
 
     $this->drupalGet('admin/content/files');
diff --git a/core/modules/file/lib/Drupal/file/Tests/FilePrivateTest.php b/core/modules/file/lib/Drupal/file/Tests/FilePrivateTest.php
index 1b8a82f651e9..cfeee72f2c22 100644
--- a/core/modules/file/lib/Drupal/file/Tests/FilePrivateTest.php
+++ b/core/modules/file/lib/Drupal/file/Tests/FilePrivateTest.php
@@ -50,7 +50,7 @@ function testPrivateFile() {
     $test_file = $this->getTestFile('text');
     $nid = $this->uploadNodeFile($test_file, $field_name, $type_name, TRUE, array('private' => TRUE));
     $node = node_load($nid, TRUE);
-    $node_file = file_load($node->{$field_name}[Language::LANGCODE_NOT_SPECIFIED][0]['target_id']);
+    $node_file = file_load($node->{$field_name}->target_id);
     // Ensure the file can be downloaded.
     $this->drupalGet(file_create_url($node_file->getFileUri()));
     $this->assertResponse(200, 'Confirmed that the generated URL is correct by downloading the shipped file.');
@@ -62,7 +62,7 @@ function testPrivateFile() {
     $this->drupalLogin($this->admin_user);
     $nid = $this->uploadNodeFile($test_file, $no_access_field_name, $type_name, TRUE, array('private' => TRUE));
     $node = node_load($nid, TRUE);
-    $node_file = file_load($node->{$no_access_field_name}[Language::LANGCODE_NOT_SPECIFIED][0]['target_id']);
+    $node_file = file_load($node->{$no_access_field_name}->target_id);
     // Ensure the file cannot be downloaded.
     $this->drupalGet(file_create_url($node_file->getFileUri()));
     $this->assertResponse(403, 'Confirmed that access is denied for the file without view field access permission.');
diff --git a/core/modules/file/lib/Drupal/file/Tests/FileTokenReplaceTest.php b/core/modules/file/lib/Drupal/file/Tests/FileTokenReplaceTest.php
index 36d323d156e8..5036c3ec11fd 100644
--- a/core/modules/file/lib/Drupal/file/Tests/FileTokenReplaceTest.php
+++ b/core/modules/file/lib/Drupal/file/Tests/FileTokenReplaceTest.php
@@ -47,7 +47,7 @@ function testFileTokenReplacement() {
 
     // Load the node and the file.
     $node = node_load($nid, TRUE);
-    $file = file_load($node->{$field_name}[Language::LANGCODE_NOT_SPECIFIED][0]['target_id']);
+    $file = file_load($node->{$field_name}->target_id);
 
     // Generate and test sanitized tokens.
     $tests = array();
diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterSecurityTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterSecurityTest.php
index 75af0b0310d4..2023434f846e 100644
--- a/core/modules/filter/lib/Drupal/filter/Tests/FilterSecurityTest.php
+++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterSecurityTest.php
@@ -72,8 +72,8 @@ function setUp() {
   function testDisableFilterModule() {
     // Create a new node.
     $node = $this->drupalCreateNode(array('promote' => 1));
-    $body_raw = $node->body[Language::LANGCODE_NOT_SPECIFIED][0]['value'];
-    $format_id = $node->body[Language::LANGCODE_NOT_SPECIFIED][0]['format'];
+    $body_raw = $node->body->value;
+    $format_id = $node->body->format;
     $this->drupalGet('node/' . $node->id());
     $this->assertText($body_raw, 'Node body found.');
 
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index 41d26040ead3..9352e0dd2c47 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -278,17 +278,17 @@ function forum_node_validate(EntityInterface $node, $form) {
   if (_forum_node_check_node_type($node)) {
     $langcode = $form['taxonomy_forums']['#language'];
     // vocabulary is selected, not a "container" term.
-    if (!empty($node->taxonomy_forums[$langcode])) {
+    if (!$node->taxonomy_forums->isEmpty()) {
       // Extract the node's proper topic ID.
       $containers = Drupal::config('forum.settings')->get('containers');
-      foreach ($node->taxonomy_forums[$langcode] as $delta => $item) {
+      foreach ($node->taxonomy_forums as $delta => $item) {
         // If no term was selected (e.g. when no terms exist yet), remove the
         // item.
-        if (empty($item['target_id'])) {
-          unset($node->taxonomy_forums[$langcode][$delta]);
+        if (empty($item->target_id)) {
+          unset($node->taxonomy_forums[$delta]);
           continue;
         }
-        $term = entity_load('taxonomy_term', $item['target_id']);
+        $term = $item->entity;
         if (!$term) {
           form_set_error('taxonomy_forums', t('Select a forum.'));
           continue;
@@ -311,19 +311,18 @@ function forum_node_validate(EntityInterface $node, $form) {
  * Assigns the forum taxonomy when adding a topic from within a forum.
  */
 function forum_node_presave(EntityInterface $node) {
+
   if (_forum_node_check_node_type($node)) {
     // Make sure all fields are set properly:
     $node->icon = !empty($node->icon) ? $node->icon : '';
-    reset($node->taxonomy_forums);
-    $langcode = key($node->taxonomy_forums);
-    if (!empty($node->taxonomy_forums[$langcode])) {
-      $node->forum_tid = $node->taxonomy_forums[$langcode][0]['target_id'];
+    if (!$node->taxonomy_forums->isEmpty()) {
+      $node->forum_tid = $node->taxonomy_forums->target_id;
       // Only do a shadow copy check if this is not a new node.
       if (!$node->isNew()) {
         $old_tid = db_query_range("SELECT f.tid FROM {forum} f INNER JOIN {node} n ON f.vid = n.vid WHERE n.nid = :nid ORDER BY f.vid DESC", 0, 1, array(':nid' => $node->id()))->fetchField();
         if ($old_tid && isset($node->forum_tid) && ($node->forum_tid != $old_tid) && !empty($node->shadow)) {
           // A shadow copy needs to be created. Retain new term and add old term.
-          $node->taxonomy_forums[$langcode][] = array('target_id' => $old_tid);
+          $node->taxonomy_forums[count($node->taxonomy_forums)] = array('target_id' => $old_tid);
         }
       }
     }
@@ -501,6 +500,7 @@ function forum_comment_delete($comment) {
  * Implements hook_field_storage_pre_insert().
  */
 function forum_field_storage_pre_insert(EntityInterface $entity, &$skip_fields) {
+  $entity = $entity->getNGEntity();
   if ($entity->entityType() == 'node' && $entity->isPublished() && _forum_node_check_node_type($entity)) {
     $query = db_insert('forum_index')->fields(array('nid', 'title', 'tid', 'sticky', 'created', 'comment_count', 'last_comment_timestamp'));
     foreach ($entity->getTranslationLanguages() as $langcode => $language) {
@@ -525,6 +525,7 @@ function forum_field_storage_pre_insert(EntityInterface $entity, &$skip_fields)
 function forum_field_storage_pre_update(EntityInterface $entity, &$skip_fields) {
   $first_call = &drupal_static(__FUNCTION__, array());
 
+  $entity = $entity->getNGEntity();
   if ($entity->entityType() == 'node' && _forum_node_check_node_type($entity)) {
 
     // If the node is published, update the forum index.
@@ -1126,11 +1127,15 @@ function template_preprocess_forum_topic_list(&$variables) {
       }
       else {
         $variables['topics'][$id]->moved = FALSE;
-        $variables['topics'][$id]->title = l($topic->getTitle(), 'node/' . $topic->id());
+        $variables['topics'][$id]->title_link = l($topic->getTitle(), 'node/' . $topic->id());
         $variables['topics'][$id]->message = '';
       }
-      $forum_submitted = array('#theme' => 'forum_submitted', '#topic' => $topic);
-      $variables['topics'][$id]->created = drupal_render($forum_submitted);
+      $forum_submitted = array('#theme' => 'forum_submitted', '#topic' => (object) array(
+        'uid' => $topic->getAuthorId(),
+        'name' => $topic->getAuthor()->getUsername(),
+        'created' => $topic->getCreatedTime(),
+      ));
+      $variables['topics'][$id]->submitted = drupal_render($forum_submitted);
       $forum_submitted = array(
         '#theme' => 'forum_submitted',
         '#topic' => isset($topic->last_reply) ? $topic->last_reply : NULL,
@@ -1140,7 +1145,7 @@ function template_preprocess_forum_topic_list(&$variables) {
       $variables['topics'][$id]->new_text = '';
       $variables['topics'][$id]->new_url = '';
       if ($topic->new_replies) {
-        $variables['topics'][$id]->new_text = format_plural($topic->new_replies, '1 new post<span class="visually-hidden"> in topic %title</span>', '@count new posts<span class="visually-hidden"> in topic %title</span>', array('%title' => $variables['topics'][$id]->title));
+        $variables['topics'][$id]->new_text = format_plural($topic->new_replies, '1 new post<span class="visually-hidden"> in topic %title</span>', '@count new posts<span class="visually-hidden"> in topic %title</span>', array('%title' => $variables['topics'][$id]->label()));
         $variables['topics'][$id]->new_url = url('node/' . $topic->id(), array('query' => comment_new_page_count($topic->comment_count, $topic->new_replies, $topic), 'fragment' => 'new'));
       }
 
diff --git a/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php b/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php
index c6f25332a9d3..b8e41edac714 100644
--- a/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php
+++ b/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php
@@ -529,7 +529,7 @@ function createForumTopic($forum, $container = FALSE) {
     // Retrieve node object, ensure that the topic was created and in the proper forum.
     $node = $this->drupalGetNodeByTitle($title);
     $this->assertTrue($node != NULL, format_string('Node @title was loaded', array('@title' => $title)));
-    $this->assertEqual($node->taxonomy_forums[Language::LANGCODE_NOT_SPECIFIED][0]['target_id'], $tid, 'Saved forum topic was in the expected forum');
+    $this->assertEqual($node->taxonomy_forums->target_id, $tid, 'Saved forum topic was in the expected forum');
 
     // View forum topic.
     $this->drupalGet('node/' . $node->id());
diff --git a/core/modules/forum/templates/forum-topic-list.html.twig b/core/modules/forum/templates/forum-topic-list.html.twig
index 85888bae163d..0e8cd1651e1f 100644
--- a/core/modules/forum/templates/forum-topic-list.html.twig
+++ b/core/modules/forum/templates/forum-topic-list.html.twig
@@ -13,7 +13,7 @@
  *   - icon: The icon to display.
  *   - moved: A flag to indicate whether the topic has been moved to another
  *     forum.
- *   - title: The title of the topic. Safe to output.
+ *   - title_link: The title of the topic. Safe to output.
  *   - message: If the topic has been moved, this contains an explanation and a
  *     link.
  *   - zebra: 'even' or 'odd', used for row class.
@@ -21,7 +21,7 @@
  *   - new_replies: A flag to indicate whether there are unread comments.
  *   - new_url: If there are unread replies, this is a link to them.
  *   - new_text: Text containing the translated, properly pluralized count.
- *   - created: Text representing when the topic was posted. Safe to output.
+ *   - submitted: Text representing when the topic was posted. Safe to output.
  *   - last_reply: Text representing when the topic was last replied to.
  *   - timestamp: The raw timestamp this topic was posted.
  * - topic_id: Numeric ID for the current forum topic.
@@ -42,10 +42,10 @@
         {{ topic.icon }}
         <div class="title">
           <div>
-            {{ topic.title }}
+            {{ topic.title_link }}
           </div>
           <div>
-            {{ topic.created }}
+            {{ topic.submitted }}
           </div>
         </div>
       </td>
diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageFieldDisplayTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageFieldDisplayTest.php
index e7f8d72544cf..b7d1b158e677 100644
--- a/core/modules/image/lib/Drupal/image/Tests/ImageFieldDisplayTest.php
+++ b/core/modules/image/lib/Drupal/image/Tests/ImageFieldDisplayTest.php
@@ -58,7 +58,7 @@ function _testImageFieldFormatters($scheme) {
     $node = node_load($nid, TRUE);
 
     // Test that the default formatter is being used.
-    $image_uri = file_load($node->{$field_name}[Language::LANGCODE_NOT_SPECIFIED][0]['target_id'])->getFileUri();
+    $image_uri = file_load($node->{$field_name}->target_id)->getFileUri();
     $image = array(
       '#theme' => 'image',
       '#uri' => $image_uri,
@@ -182,7 +182,7 @@ function testImageFieldSettings() {
     $node = node_load($nid, TRUE);
     $image_style = array(
       '#theme' => 'image_style',
-      '#uri' => file_load($node->{$field_name}[Language::LANGCODE_NOT_SPECIFIED][0]['target_id'])->getFileUri(),
+      '#uri' => file_load($node->{$field_name}->target_id)->getFileUri(),
       '#width' => 220,
       '#height' => 110,
       '#style_name' => 'medium',
@@ -193,7 +193,7 @@ function testImageFieldSettings() {
     // Add alt/title fields to the image and verify that they are displayed.
     $image = array(
       '#theme' => 'image',
-      '#uri' => file_load($node->{$field_name}[Language::LANGCODE_NOT_SPECIFIED][0]['target_id'])->getFileUri(),
+      '#uri' => file_load($node->{$field_name}->target_id)->getFileUri(),
       '#alt' => $this->randomName(),
       '#title' => $this->randomName(),
       '#width' => 40,
@@ -266,7 +266,7 @@ function testImageFieldDefaultImage() {
     $node = node_load($nid, TRUE);
     $image = array(
       '#theme' => 'image',
-      '#uri' => file_load($node->{$field_name}[Language::LANGCODE_NOT_SPECIFIED][0]['target_id'])->getFileUri(),
+      '#uri' => file_load($node->{$field_name}->target_id)->getFileUri(),
       '#width' => 40,
       '#height' => 20,
     );
diff --git a/core/modules/node/lib/Drupal/node/Entity/Node.php b/core/modules/node/lib/Drupal/node/Entity/Node.php
index 0452e995ceda..b0d33dff3caa 100644
--- a/core/modules/node/lib/Drupal/node/Entity/Node.php
+++ b/core/modules/node/lib/Drupal/node/Entity/Node.php
@@ -12,7 +12,6 @@
 use Drupal\Core\Entity\Annotation\EntityType;
 use Drupal\Core\Annotation\Translation;
 use Drupal\node\NodeInterface;
-use Drupal\node\NodeBCDecorator;
 
 /**
  * Defines the node entity class.
@@ -104,7 +103,7 @@ public function preSaveRevision(EntityStorageControllerInterface $storage_contro
       // need to make sure $entity->log is reset whenever it is empty.
       // Therefore, this code allows us to avoid clobbering an existing log
       // entry with an empty one.
-      $record->log = $this->original->log;
+      $record->log = $this->original->log->value;
     }
   }
 
@@ -116,21 +115,10 @@ public function postSave(EntityStorageControllerInterface $storage_controller, $
     // default revision. There's no need to delete existing records if the node
     // is new.
     if ($this->isDefaultRevision()) {
-      \Drupal::entityManager()->getAccessController('node')->writeGrants($this->getBCEntity(), $update);
+      \Drupal::entityManager()->getAccessController('node')->writeGrants($this, $update);
     }
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function getBCEntity() {
-    if (!isset($this->bcEntity)) {
-      $this->getPropertyDefinitions();
-      $this->bcEntity = new NodeBCDecorator($this, $this->fieldDefinitions);
-    }
-    return $this->bcEntity;
-  }
-
   /**
    * {@inheritdoc}
    */
diff --git a/core/modules/node/lib/Drupal/node/NodeBCDecorator.php b/core/modules/node/lib/Drupal/node/NodeBCDecorator.php
deleted file mode 100644
index b65bc4e15038..000000000000
--- a/core/modules/node/lib/Drupal/node/NodeBCDecorator.php
+++ /dev/null
@@ -1,148 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\node\NodeBCDecorator.
- */
-
-namespace Drupal\node;
-
-use Drupal\Core\Entity\EntityBCDecorator;
-
-/**
- * Defines the node specific entity BC decorator.
- */
-class NodeBCDecorator extends EntityBCDecorator implements NodeInterface {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getType() {
-    return $this->decorated->getType();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setTitle($title) {
-    return $this->decorated->setTitle($title);
-  }
-  /**
-   * {@inheritdoc}
-   */
-  public function getTitle() {
-    return $this->decorated->getTitle();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getCreatedTime() {
-    return $this->decorated->getCreatedTime();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setCreatedTime($timestamp) {
-    return $this->decorated->setCreatedTime($timestamp);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getChangedTime() {
-    return $this->decorated->getChangedTime();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function isPromoted() {
-    return $this->decorated->isPromoted();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setPromoted($promoted) {
-    $this->decorated->setPromoted($promoted);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function isSticky() {
-    return $this->decorated->isSticky();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setSticky($sticky) {
-    $this->decorated->setSticky($sticky);
-  }
-  /**
-   * {@inheritdoc}
-   */
-  public function getAuthor() {
-    return $this->decorated->getAuthor();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getAuthorId() {
-    return $this->decorated->getAuthorId();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setAuthorId($uid) {
-    $this->decorated->setAuthorId($uid);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function isPublished() {
-    return $this->decorated->isPublished();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setPublished($published) {
-    $this->decorated->setPublished($published);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getRevisionCreationTime() {
-    return $this->decorated->getRevisionCreationTime();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setRevisionCreationTime($timestamp) {
-    return $this->decorated->setRevisionCreationTime($timestamp);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getRevisionAuthor() {
-    return $this->decorated->getRevisionAuthor();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setRevisionAuthorId($uid) {
-    return $this->decorated->setRevisionAuthorId($uid);
-  }
-
-}
diff --git a/core/modules/node/lib/Drupal/node/NodeFormController.php b/core/modules/node/lib/Drupal/node/NodeFormController.php
index 08f54d159568..0609e9c03749 100644
--- a/core/modules/node/lib/Drupal/node/NodeFormController.php
+++ b/core/modules/node/lib/Drupal/node/NodeFormController.php
@@ -9,13 +9,13 @@
 
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Datetime\DrupalDateTime;
-use Drupal\Core\Entity\EntityFormController;
+use Drupal\Core\Entity\EntityFormControllerNG;
 use Drupal\Core\Language\Language;
 
 /**
  * Form controller for the node edit forms.
  */
-class NodeFormController extends EntityFormController {
+class NodeFormController extends EntityFormControllerNG {
 
   /**
    * Default settings for this content/node type.
@@ -42,7 +42,7 @@ protected function prepareEntity() {
     if ($node->isNew()) {
       foreach (array('status', 'promote', 'sticky') as $key) {
         // Multistep node forms might have filled in something already.
-        if (!isset($node->$key)) {
+        if ($node->$key->isEmpty()) {
           $node->$key = (int) in_array($key, $this->settings['options']);
         }
       }
@@ -105,7 +105,7 @@ public function form(array $form, array &$form_state) {
         '#type' => 'textfield',
         '#title' => check_plain($node_type->title_label),
         '#required' => TRUE,
-        '#default_value' => $node->title,
+        '#default_value' => $node->title->value,
         '#maxlength' => 255,
         '#weight' => -5,
       );
@@ -155,7 +155,7 @@ public function form(array $form, array &$form_state) {
       '#type' => 'textarea',
       '#title' => t('Revision log message'),
       '#rows' => 4,
-      '#default_value' => !empty($node->log) ? $node->log : '',
+      '#default_value' => !empty($node->log->value) ? $node->log->value : '',
       '#description' => t('Briefly describe the changes you have made.'),
       '#states' => array(
         'visible' => array(
@@ -191,7 +191,7 @@ public function form(array $form, array &$form_state) {
       '#title' => t('Authored by'),
       '#maxlength' => 60,
       '#autocomplete_route_name' => 'user_autocomplete',
-      '#default_value' => !empty($node->name) ? $node->name : '',
+      '#default_value' => $node->getAuthorId()? $node->getAuthor()->getUsername() : '',
       '#weight' => -1,
       '#description' => t('Leave blank for %anonymous.', array('%anonymous' => $user_config->get('anonymous'))),
     );
@@ -332,11 +332,11 @@ public function validate(array $form, array &$form_state) {
     }
 
     // Validate the "authored by" field.
-    if (!empty($node->name) && !($account = user_load_by_name($node->name))) {
+    if (!empty($form_state['values']['name']) && !($account = user_load_by_name($form_state['values']['name']))) {
       // The use of empty() is mandatory in the context of usernames
       // as the empty string denotes the anonymous user. In case we
       // are dealing with an anonymous user we set the user ID to 0.
-      form_set_error('name', t('The username %name does not exist.', array('%name' => $node->name)));
+      form_set_error('name', t('The username %name does not exist.', array('%name' => $form_state['values']['name'])));
     }
 
     // Validate the "authored on" field.
@@ -373,9 +373,13 @@ public function submit(array $form, array &$form_state) {
     // Save as a new revision if requested to do so.
     if (!empty($form_state['values']['revision'])) {
       $node->setNewRevision();
+      // If a new revision is created, save the current user as revision author.
+      $node->setRevisionCreationTime(REQUEST_TIME);
+      global $user;
+      $node->setRevisionAuthorId($user->id());
     }
 
-    node_submit($node);
+    $node->validated = TRUE;
     foreach (\Drupal::moduleHandler()->getImplementations('node_submit') as $module) {
       $function = $module . '_node_submit';
       $function($node, $form, $form_state);
@@ -429,6 +433,30 @@ public function unpublish(array $form, array &$form_state) {
     return $node;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function buildEntity(array $form, array &$form_state) {
+    $entity = parent::buildEntity($form, $form_state);
+    // A user might assign the node author by entering a user name in the node
+    // form, which we then need to translate to a user ID.
+    if (!empty($form_state['values']['name']) && $account = user_load_by_name($form_state['values']['name'])) {
+      $entity->setAuthorId($account->id());
+    }
+    else {
+      $entity->setAuthorId(0);
+    }
+
+    if (!empty($form_state['values']['date']) && $form_state['values']['date'] instanceOf DrupalDateTime) {
+      $entity->setCreatedTime($form_state['values']['date']->getTimestamp());
+    }
+    else {
+      $entity->setCreatedTime(REQUEST_TIME);
+    }
+    return $entity;
+  }
+
+
   /**
    * Overrides Drupal\Core\Entity\EntityFormController::save().
    */
diff --git a/core/modules/node/lib/Drupal/node/NodeGrantDatabaseStorage.php b/core/modules/node/lib/Drupal/node/NodeGrantDatabaseStorage.php
index b274a1f798b8..094c2626738e 100644
--- a/core/modules/node/lib/Drupal/node/NodeGrantDatabaseStorage.php
+++ b/core/modules/node/lib/Drupal/node/NodeGrantDatabaseStorage.php
@@ -213,7 +213,7 @@ public function write(NodeInterface $node, array $grants, $realm = NULL, $delete
             $grant['nid'] = $node->id();
             $grant['langcode'] = $grant_langcode;
             // The record with the original langcode is used as the fallback.
-            if ($grant['langcode'] == $node->langcode) {
+            if ($grant['langcode'] == $node->language()->id) {
               $grant['fallback'] = 1;
             }
             else {
diff --git a/core/modules/node/lib/Drupal/node/NodeStorageController.php b/core/modules/node/lib/Drupal/node/NodeStorageController.php
index 1a874fe56620..ee282d8b1563 100644
--- a/core/modules/node/lib/Drupal/node/NodeStorageController.php
+++ b/core/modules/node/lib/Drupal/node/NodeStorageController.php
@@ -26,22 +26,21 @@ public function create(array $values) {
     if (empty($values['created'])) {
       $values['created'] = REQUEST_TIME;
     }
-    return parent::create($values)->getBCEntity();
+    return parent::create($values);
   }
 
   /**
    * Overrides Drupal\Core\Entity\DatabaseStorageControllerNG::attachLoad().
    */
   protected function attachLoad(&$queried_entities, $load_revision = FALSE) {
-    $nodes = $this->mapFromStorageRecords($queried_entities, $load_revision);
+    $queried_entities = $this->mapFromStorageRecords($queried_entities, $load_revision);
 
     // Create an array of nodes for each content type and pass this to the
     // object type specific callback. To preserve backward-compatibility we
     // pass on BC decorators to node-specific hooks, while we pass on the
     // regular entity objects else.
     $typed_nodes = array();
-    foreach ($nodes as $id => $node) {
-      $queried_entities[$id] = $node->getBCEntity();
+    foreach ($queried_entities as $id => $node) {
       $typed_nodes[$node->bundle()][$id] = $queried_entities[$id];
     }
 
@@ -71,31 +70,6 @@ protected function attachLoad(&$queried_entities, $load_revision = FALSE) {
     }
   }
 
-  /**
-   * Overrides Drupal\Core\Entity\DatabaseStorageController::invokeHook().
-   */
-  protected function invokeHook($hook, EntityInterface $node) {
-    $node = $node->getUntranslated()->getBCEntity();
-
-    // Inline parent::invokeHook() to pass on BC-entities to node-specific
-    // hooks.
-
-    $function = 'field_attach_' . $hook;
-    // @todo: field_attach_delete_revision() is named the wrong way round,
-    // consider renaming it.
-    if ($function == 'field_attach_revision_delete') {
-      $function = 'field_attach_delete_revision';
-    }
-    if (!empty($this->entityInfo['fieldable']) && function_exists($function)) {
-      $function($node);
-    }
-
-    // Invoke the hook.
-    module_invoke_all($this->entityType . '_' . $hook, $node);
-    // Invoke the respective entity-level hook.
-    module_invoke_all('entity_' . $hook, $node, $this->entityType);
-  }
-
   /**
    * {@inheritdoc}
    */
diff --git a/core/modules/node/lib/Drupal/node/Plugin/views/row/Rss.php b/core/modules/node/lib/Drupal/node/Plugin/views/row/Rss.php
index 124f0bb7337c..7d69106b11d6 100644
--- a/core/modules/node/lib/Drupal/node/Plugin/views/row/Rss.php
+++ b/core/modules/node/lib/Drupal/node/Plugin/views/row/Rss.php
@@ -122,7 +122,7 @@ public function render($row) {
       ),
       array(
         'key' => 'dc:creator',
-        'value' => $node->name,
+        'value' => $node->getAuthor()->getUsername(),
       ),
       array(
         'key' => 'guid',
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeFieldMultilingualTestCase.php b/core/modules/node/lib/Drupal/node/Tests/NodeFieldMultilingualTestCase.php
index 6df2698557e3..c66865e9de2c 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeFieldMultilingualTestCase.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeFieldMultilingualTestCase.php
@@ -82,7 +82,7 @@ function testMultilingualNodeForm() {
     $this->drupalPost('node/add/page', $edit, t('Save'));
 
     // Check that the node exists in the database.
-    $node = $this->drupalGetNodeByTitle($edit[$title_key])->getNGEntity();
+    $node = $this->drupalGetNodeByTitle($edit[$title_key]);
     $this->assertTrue($node, 'Node found in database.');
     $this->assertTrue($node->language()->id == $langcode && $node->body->value == $body_value, 'Field language correctly set.');
 
@@ -94,7 +94,7 @@ function testMultilingualNodeForm() {
       'langcode' => $langcode,
     );
     $this->drupalPost(NULL, $edit, t('Save'));
-    $node = $this->drupalGetNodeByTitle($edit[$title_key], TRUE)->getNGEntity();
+    $node = $this->drupalGetNodeByTitle($edit[$title_key], TRUE);
     $this->assertTrue($node, 'Node found in database.');
     $this->assertTrue($node->language()->id == $langcode && $node->body->value == $body_value, 'Field language correctly changed.');
 
@@ -136,7 +136,7 @@ function testMultilingualDisplaySettings() {
       ':id' => 'node-' . $node->id(),
       ':class' => 'content',
     ));
-    $this->assertEqual(current($body), $node->body['en'][0]['value'], 'Node body found.');
+    $this->assertEqual(current($body), $node->body->value, 'Node body found.');
   }
 
 }
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeRevisionsAllTestCase.php b/core/modules/node/lib/Drupal/node/Tests/NodeRevisionsAllTestCase.php
index d6ddc3b15072..c98515f94645 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeRevisionsAllTestCase.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeRevisionsAllTestCase.php
@@ -59,7 +59,7 @@ function setUp() {
 
       // Create revision with a random title and body and update variables.
       $node->title = $this->randomName();
-      $node->body[$node->language()->id][0] = array(
+      $node->body = array(
         'value' => $this->randomName(32),
         'format' => filter_default_format(),
       );
@@ -98,7 +98,7 @@ function testRevisions() {
 
     // Confirm the correct revision text appears on "view revisions" page.
     $this->drupalGet("node/" . $node->id() . "/revisions/" . $node->getRevisionId() . "/view");
-    $this->assertText($node->body[Language::LANGCODE_NOT_SPECIFIED][0]['value'], 'Correct text displays for version.');
+    $this->assertText($node->body->value, 'Correct text displays for version.');
 
     // Confirm the correct log message appears on "revisions overview" page.
     $this->drupalGet("node/" . $node->id() . "/revisions");
@@ -119,7 +119,7 @@ function testRevisions() {
       )),
       'Revision reverted.');
     $reverted_node = node_load($node->id(), TRUE);
-    $this->assertTrue(($nodes[1]->body[Language::LANGCODE_NOT_SPECIFIED][0]['value'] == $reverted_node->body[Language::LANGCODE_NOT_SPECIFIED][0]['value']), 'Node reverted correctly.');
+    $this->assertTrue(($nodes[1]->body->value == $reverted_node->body->value), 'Node reverted correctly.');
 
     // Confirm that this is not the current version.
     $node = node_revision_load($node->getRevisionId());
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeRevisionsTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeRevisionsTest.php
index 1580f8629ec8..7a58feda7b21 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeRevisionsTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeRevisionsTest.php
@@ -59,7 +59,7 @@ function setUp() {
 
       // Create revision with a random title and body and update variables.
       $node->title = $this->randomName();
-      $node->body[$node->language()->id][0] = array(
+      $node->body = array(
         'value' => $this->randomName(32),
         'format' => filter_default_format(),
       );
@@ -86,7 +86,7 @@ function testRevisions() {
 
     // Confirm the correct revision text appears on "view revisions" page.
     $this->drupalGet("node/" . $node->id() . "/revisions/" . $node->getRevisionId() . "/view");
-    $this->assertText($node->body[Language::LANGCODE_NOT_SPECIFIED][0]['value'], 'Correct text displays for version.');
+    $this->assertText($node->body->value, 'Correct text displays for version.');
 
     // Confirm the correct log message appears on "revisions overview" page.
     $this->drupalGet("node/" . $node->id() . "/revisions");
@@ -103,7 +103,7 @@ function testRevisions() {
                         array('@type' => 'Basic page', '%title' => $nodes[1]->label(),
                               '%revision-date' => format_date($nodes[1]->getRevisionCreationTime()))), 'Revision reverted.');
     $reverted_node = node_load($node->id(), TRUE);
-    $this->assertTrue(($nodes[1]->body[Language::LANGCODE_NOT_SPECIFIED][0]['value'] == $reverted_node->body[Language::LANGCODE_NOT_SPECIFIED][0]['value']), 'Node reverted correctly.');
+    $this->assertTrue(($nodes[1]->body->value == $reverted_node->body->value), 'Node reverted correctly.');
 
     // Confirm that this is not the default version.
     $node = node_revision_load($node->getRevisionId());
@@ -136,7 +136,7 @@ function testRevisions() {
     // This will create a new revision that is not "front facing".
     $new_node_revision = clone $node;
     $new_body = $this->randomName();
-    $new_node_revision->body[Language::LANGCODE_NOT_SPECIFIED][0]['value'] = $new_body;
+    $new_node_revision->body->value = $new_body;
     // Save this as a non-default revision.
     $new_node_revision->setNewRevision();
     $new_node_revision->isDefaultRevision = FALSE;
@@ -183,7 +183,7 @@ function testNodeRevisionWithoutLogMessage() {
     $this->drupalGet('node/' . $node->id());
     $this->assertText($new_title, 'New node title appears on the page.');
     $node_revision = node_load($node->id(), TRUE);
-    $this->assertEqual($node_revision->log, $log, 'After an existing node revision is re-saved without a log message, the original log message is preserved.');
+    $this->assertEqual($node_revision->log->value, $log, 'After an existing node revision is re-saved without a log message, the original log message is preserved.');
 
     // Create another node with an initial log message.
     $node = $this->drupalCreateNode(array('log' => $log));
@@ -201,6 +201,6 @@ function testNodeRevisionWithoutLogMessage() {
     $this->drupalGet('node/' . $node->id());
     $this->assertText($new_title, 'New node title appears on the page.');
     $node_revision = node_load($node->id(), TRUE);
-    $this->assertTrue(empty($node_revision->log), 'After a new node revision is saved with an empty log message, the log message for the node is empty.');
+    $this->assertTrue(empty($node_revision->log->value), 'After a new node revision is saved with an empty log message, the log message for the node is empty.');
   }
 }
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeSaveTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeSaveTest.php
index 64f109a605fc..a3f16f869999 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeSaveTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeSaveTest.php
@@ -56,7 +56,7 @@ function testImport() {
       'type' => 'article',
       'nid' => $test_nid,
     );
-    $node = node_submit(entity_create('node', $node));
+    $node = entity_create('node', $node);
     $node->enforceIsNew();
 
     // Verify that node_submit did not overwrite the user ID.
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeTokenReplaceTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeTokenReplaceTest.php
index 0713d751dbdf..1e55f29610fd 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeTokenReplaceTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeTokenReplaceTest.php
@@ -50,12 +50,12 @@ function testNodeTokenReplacement() {
     $tests = array();
     $tests['[node:nid]'] = $node->id();
     $tests['[node:vid]'] = $node->getRevisionId();
-    $tests['[node:tnid]'] = $node->tnid;
+    $tests['[node:tnid]'] = $node->tnid->value;
     $tests['[node:type]'] = 'article';
     $tests['[node:type-name]'] = 'Article';
     $tests['[node:title]'] = check_plain($node->getTitle());
-    $tests['[node:body]'] = text_sanitize($instance['settings']['text_processing'], $node->language()->id, $node->body[$node->language()->id][0], 'value');
-    $tests['[node:summary]'] = text_sanitize($instance['settings']['text_processing'], $node->language()->id, $node->body[$node->language()->id][0], 'summary');
+    $tests['[node:body]'] = text_sanitize($instance['settings']['text_processing'], $node->language()->id, $node->body[0]->getValue(), 'value');
+    $tests['[node:summary]'] = text_sanitize($instance['settings']['text_processing'], $node->language()->id, $node->body[0]->getValue(), 'summary');
     $tests['[node:langcode]'] = check_plain($node->language()->id);
     $tests['[node:url]'] = url('node/' . $node->id(), $url_options);
     $tests['[node:edit-url]'] = url('node/' . $node->id() . '/edit', $url_options);
@@ -75,8 +75,9 @@ function testNodeTokenReplacement() {
 
     // Generate and test unsanitized tokens.
     $tests['[node:title]'] = $node->getTitle();
-    $tests['[node:body]'] = $node->body[$node->language()->id][0]['value'];
-    $tests['[node:summary]'] = $node->body[$node->language()->id][0]['summary'];
+    $tests['[node:title]'] = $node->label();
+    $tests['[node:body]'] = $node->body->value;
+    $tests['[node:summary]'] = $node->body->summary;
     $tests['[node:langcode]'] = $node->language()->id;
     $tests['[node:author:name]'] = user_format_name($account);
 
@@ -96,7 +97,7 @@ function testNodeTokenReplacement() {
 
     // Generate and test sanitized token - use full body as expected value.
     $tests = array();
-    $tests['[node:summary]'] = text_sanitize($instance['settings']['text_processing'], $node->language()->id, $node->body[$node->language()->id][0], 'value');
+    $tests['[node:summary]'] = text_sanitize($instance['settings']['text_processing'], $node->language()->id, $node->body[0]->getValue(), 'value');
 
     // Test to make sure that we generated something for each token.
     $this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated for node without a summary.');
@@ -107,7 +108,7 @@ function testNodeTokenReplacement() {
     }
 
     // Generate and test unsanitized tokens.
-    $tests['[node:summary]'] = $node->body[$node->language()->id][0]['value'];
+    $tests['[node:summary]'] = $node->body->value;
 
     foreach ($tests as $input => $expected) {
       $output = $token_service->replace($input, array('node' => $node), array('language' => $language_interface, 'sanitize' => FALSE));
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php b/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php
index dac3575112e5..6861c52dcc60 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php
@@ -69,7 +69,7 @@ protected function getNewEntityValues($langcode) {
    * Overrides \Drupal\content_translation\Tests\ContentTranslationUITest::getFormSubmitAction().
    */
   protected function getFormSubmitAction(EntityInterface $entity) {
-    if ($entity->status) {
+    if ($entity->isPublished()) {
       return t('Save and unpublish');
     }
     return t('Save and keep unpublished');
diff --git a/core/modules/node/lib/Drupal/node/Tests/Views/RowPluginTest.php b/core/modules/node/lib/Drupal/node/Tests/Views/RowPluginTest.php
index f5c59aeee985..6d347f9c047f 100644
--- a/core/modules/node/lib/Drupal/node/Tests/Views/RowPluginTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/Views/RowPluginTest.php
@@ -93,7 +93,7 @@ public function drupalCreateComment(array $settings = array()) {
     $node = node_load($settings['nid']);
     $settings += array(
       'subject' => $this->randomName(),
-      'node_type' => "comment_node_{$node->bundle()}",
+      'node_type' => 'comment_node_' . $node->bundle(),
       'comment_body' => $this->randomName(40),
     );
 
@@ -116,11 +116,8 @@ public function testRowPlugin() {
     $output = $view->preview();
     $output = drupal_render($output);
     foreach ($this->nodes as $node) {
-      $body = $node->body;
-      $teaser = $body[Language::LANGCODE_NOT_SPECIFIED][0]['summary'];
-      $full = $body[Language::LANGCODE_NOT_SPECIFIED][0]['value'];
-      $this->assertFalse(strpos($output, $teaser) !== FALSE, 'Make sure the teaser appears in the output of the view.');
-      $this->assertTrue(strpos($output, $full) !== FALSE, 'Make sure the full text appears in the output of the view.');
+      $this->assertFalse(strpos($output, $node->body->summary) !== FALSE, 'Make sure the teaser appears in the output of the view.');
+      $this->assertTrue(strpos($output, $node->body->value) !== FALSE, 'Make sure the full text appears in the output of the view.');
     }
 
     // Test with teasers.
@@ -128,11 +125,8 @@ public function testRowPlugin() {
     $output = $view->preview();
     $output = drupal_render($output);
     foreach ($this->nodes as $node) {
-      $body = $node->body;
-      $teaser = $body[Language::LANGCODE_NOT_SPECIFIED][0]['summary'];
-      $full = $body[Language::LANGCODE_NOT_SPECIFIED][0]['value'];
-      $this->assertTrue(strpos($output, $teaser) !== FALSE, 'Make sure the teaser appears in the output of the view.');
-      $this->assertFalse(strpos($output, $full) !== FALSE, 'Make sure the full text does not appears in the output of the view if teaser is set as viewmode.');
+      $this->assertTrue(strpos($output, $node->body->summary) !== FALSE, 'Make sure the teaser appears in the output of the view.');
+      $this->assertFalse(strpos($output, $node->body->value) !== FALSE, 'Make sure the full text does not appears in the output of the view if teaser is set as viewmode.');
     }
 
     // Test with links disabled.
diff --git a/core/modules/node/node.api.php b/core/modules/node/node.api.php
index a6ec9ef4ae4c..4616ab46f44d 100644
--- a/core/modules/node/node.api.php
+++ b/core/modules/node/node.api.php
@@ -260,7 +260,7 @@ function hook_node_grants($account, $op) {
 function hook_node_access_records(\Drupal\node\NodeInterface $node) {
   // We only care about the node if it has been marked private. If not, it is
   // treated just like any other node and we completely ignore it.
-  if ($node->private) {
+  if ($node->private->value) {
     $grants = array();
     // Only published Catalan translations of private nodes should be viewable
     // to all users. If we fail to check $node->isPublished(), all users would be able
@@ -611,7 +611,7 @@ function hook_node_access(\Drupal\node\NodeInterface $node, $op, $account, $lang
  * @ingroup node_api_hooks
  */
 function hook_node_prepare_form(\Drupal\node\NodeInterface $node, $form_display, $operation, array &$form_state) {
-  if (!isset($node->comment)) {
+  if (!isset($node->comment->value)) {
     $node->comment = variable_get('comment_' . $node->getType(), COMMENT_NODE_OPEN);
   }
 }
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 2353be3f3e69..44f60e7b9284 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -520,12 +520,7 @@ function node_type_update_nodes($old_id, $new_id) {
  * @see Drupal\Core\Entity\Query\EntityQueryInterface
  */
 function node_load_multiple(array $nids = NULL, $reset = FALSE) {
-  $entities = entity_load_multiple('node', $nids, $reset);
-  // Return BC-entities.
-  foreach ($entities as $id => $entity) {
-    $entities[$id] = $entity->getBCEntity();
-  }
-  return $entities;
+  return entity_load_multiple('node', $nids, $reset);
 }
 
 /**
@@ -541,8 +536,7 @@ function node_load_multiple(array $nids = NULL, $reset = FALSE) {
  *   A fully-populated node entity, or NULL if the node is not found.
  */
 function node_load($nid = NULL, $reset = FALSE) {
-  $entity = entity_load('node', $nid, $reset);
-  return $entity ? $entity->getBCEntity() : NULL;
+  return entity_load('node', $nid, $reset);
 }
 
 /**
@@ -558,41 +552,6 @@ function node_revision_load($vid = NULL) {
   return entity_revision_load('node', $vid);
 }
 
-/**
- * Prepares a node for saving by populating the author and creation date.
- *
- * @param \Drupal\Core\Entity\EntityInterface $node
- *   A node object.
- *
- * @return Drupal\node\Node
- *   An updated node object.
- */
-function node_submit(EntityInterface $node) {
-  global $user;
-
-  // A user might assign the node author by entering a user name in the node
-  // form, which we then need to translate to a user ID.
-  if (isset($node->name)) {
-    if ($account = user_load_by_name($node->name)) {
-      $node->setAuthorId($account->id());
-    }
-    else {
-      $node->setAuthorId(0);
-    }
-  }
-
-  // If a new revision is created, save the current user as revision author.
-  if ($node->isNewRevision()) {
-    $node->setRevisionAuthorId($user->id());
-    $node->setRevisionCreationTime(REQUEST_TIME);
-  }
-
-  $node->setCreatedTime(!empty($node->date) && $node->date instanceOf DrupalDateTime ? $node->date->getTimestamp() : REQUEST_TIME);
-  $node->validated = TRUE;
-
-  return $node;
-}
-
 /**
  * Deletes a node revision.
  *
diff --git a/core/modules/node/node.pages.inc b/core/modules/node/node.pages.inc
index 4cb383b34805..d80898e9ff4b 100644
--- a/core/modules/node/node.pages.inc
+++ b/core/modules/node/node.pages.inc
@@ -91,7 +91,7 @@ function node_add($node_type) {
     'name' => $user->getUsername(),
     'type' => $type,
     'langcode' => $langcode ? $langcode : language_default()->id,
-  ))->getBCEntity();
+  ));
   drupal_set_title(t('Create @name', array('@name' => $node_type->name)), PASS_THROUGH);
   return Drupal::entityManager()->getForm($node);
 }
@@ -109,21 +109,6 @@ function node_add($node_type) {
  */
 function node_preview(NodeInterface $node) {
   if (node_access('create', $node) || node_access('update', $node)) {
-    // Load the user's name when needed.
-    if (isset($node->name)) {
-      // The use of isset() is mandatory in the context of user IDs, because
-      // user ID 0 denotes the anonymous user.
-      if ($user = user_load_by_name($node->name)) {
-        $node->setAuthorId($user->id());
-      }
-      else {
-        $node->setAuthorId(0); // anonymous user
-      }
-    }
-    elseif ($node->getAuthorId()) {
-      $user = $node->getAuthor();
-      $node->name = $user->getUsername();
-    }
 
     $node->changed = REQUEST_TIME;
 
diff --git a/core/modules/node/node.tokens.inc b/core/modules/node/node.tokens.inc
index 3094ae6830e9..92404974eded 100644
--- a/core/modules/node/node.tokens.inc
+++ b/core/modules/node/node.tokens.inc
@@ -119,7 +119,7 @@ function node_tokens($type, $tokens, array $data = array(), array $options = arr
           break;
 
         case 'tnid':
-          $replacements[$original] = $node->tnid;
+          $replacements[$original] = $node->tnid->value;
           break;
 
         case 'type':
diff --git a/core/modules/node/node.views.inc b/core/modules/node/node.views.inc
index b2d00a670b3c..deacf4d98260 100644
--- a/core/modules/node/node.views.inc
+++ b/core/modules/node/node.views.inc
@@ -635,7 +635,7 @@ function node_row_node_view_preprocess_node(&$variables) {
     unset($variables['content']['links']);
   }
 
-  if (!empty($options['comments']) && user_access('access comments') && $node->comment) {
+  if (!empty($options['comments']) && user_access('access comments') && $node->comment->value) {
     $variables['content']['comments'] = comment_node_page_additions($node);
   }
 }
diff --git a/core/modules/node/tests/modules/node_access_test/node_access_test.module b/core/modules/node/tests/modules/node_access_test/node_access_test.module
index c200908b964d..abc18c6a635d 100644
--- a/core/modules/node/tests/modules/node_access_test/node_access_test.module
+++ b/core/modules/node/tests/modules/node_access_test/node_access_test.module
@@ -36,7 +36,7 @@ function node_access_test_node_grants($account, $op) {
 function node_access_test_node_access_records(NodeInterface $node) {
   $grants = array();
   // For NodeAccessBaseTableTestCase, only set records for private nodes.
-  if (!Drupal::state()->get('node_access_test.private') || $node->private) {
+  if (!Drupal::state()->get('node_access_test.private') || $node->private->value) {
     $grants[] = array(
       'realm' => 'node_access_test',
       'gid' => 8888,
@@ -77,6 +77,21 @@ function node_access_test_permission() {
   return array('node test view' => array('title' => 'View content'));
 }
 
+/**
+ * Implements hook_entity_field_info().
+ */
+function node_access_test_entity_field_info($entity_type) {
+  if ($entity_type === 'node') {
+    $info['definitions']['private'] = array(
+      'type' => 'boolean_field',
+      'label' => t('Private'),
+      'computed' => TRUE,
+      'list' => TRUE,
+    );
+    return $info;
+  }
+}
+
 /**
  * Implements hook_form_BASE_FORM_ID_alter().
  */
@@ -88,7 +103,7 @@ function node_access_test_form_node_form_alter(&$form, $form_state) {
       '#type' => 'checkbox',
       '#title' => t('Private'),
       '#description' => t('Check here if this content should be set private and only shown to privileged users.'),
-      '#default_value' => isset($node->private) ? $node->private : FALSE,
+      '#default_value' => $node->private->value,
     );
   }
 }
@@ -129,12 +144,10 @@ function node_access_test_node_update(EntityInterface $node) {
  * Helper for node insert/update.
  */
 function _node_access_test_node_write(EntityInterface $node) {
-  if (isset($node->private)) {
-    db_merge('node_access_test')
-      ->key(array('nid' => $node->id()))
-      ->fields(array('private' => (int) $node->private))
-      ->execute();
-  }
+  db_merge('node_access_test')
+    ->key(array('nid' => $node->id()))
+    ->fields(array('private' => (int) $node->private->value))
+    ->execute();
 }
 
 /**
diff --git a/core/modules/node/tests/modules/node_access_test_language/node_access_test_language.module b/core/modules/node/tests/modules/node_access_test_language/node_access_test_language.module
index 1baa48e7548f..fb217f2997d0 100644
--- a/core/modules/node/tests/modules/node_access_test_language/node_access_test_language.module
+++ b/core/modules/node/tests/modules/node_access_test_language/node_access_test_language.module
@@ -29,10 +29,11 @@ function node_access_test_language_node_access_records(EntityInterface $node) {
   // Create grants for each translation of the node.
   foreach ($node->getTranslationLanguages() as $langcode => $language) {
     // If the translation is not marked as private, grant access.
+    $translation = $node->getTranslation($langcode);
     $grants[] = array(
       'realm' => 'node_access_language_test',
       'gid' => 7888,
-      'grant_view' => empty($node->field_private[$langcode][0]['value']) ? 1 : 0,
+      'grant_view' => empty($translation->field_private->value) ? 1 : 0,
       'grant_update' => 0,
       'grant_delete' => 0,
       'priority' => 0,
diff --git a/core/modules/node/tests/modules/node_test/node_test.module b/core/modules/node/tests/modules/node_test/node_test.module
index 3da1eea03517..835f82cf97c4 100644
--- a/core/modules/node/tests/modules/node_test/node_test.module
+++ b/core/modules/node/tests/modules/node_test/node_test.module
@@ -139,7 +139,7 @@ function node_test_node_presave(EntityInterface $node) {
   // Determine changes.
   if (!empty($node->original) && $node->original->getTitle() == 'test_changes') {
     if ($node->original->getTitle() != $node->getTitle()) {
-      $node->title .= '_presave';
+      $node->title->value .= '_presave';
     }
   }
 }
@@ -151,7 +151,7 @@ function node_test_node_update(EntityInterface $node) {
   // Determine changes on update.
   if (!empty($node->original) && $node->original->getTitle() == 'test_changes') {
     if ($node->original->getTitle() != $node->getTitle()) {
-      $node->title .= '_update';
+      $node->title->value .= '_update';
     }
   }
 }
@@ -177,7 +177,7 @@ function node_test_entity_view_mode_alter(&$view_mode, Drupal\Core\Entity\Entity
 function node_test_node_insert(EntityInterface $node) {
   // Set the node title to the node ID and save.
   if ($node->getTitle() == 'new') {
-    $node->title = 'Node '. $node->id();
+    $node->setTitle('Node '. $node->id());
     $node->save();
   }
 }
diff --git a/core/modules/path/path.module b/core/modules/path/path.module
index 9a52a9853129..38ebaa2cc6dd 100644
--- a/core/modules/path/path.module
+++ b/core/modules/path/path.module
@@ -6,8 +6,7 @@
  */
 
 use Drupal\Core\Language\Language;
-use Drupal\node\NodeInterface;
-use Drupal\taxonomy\Entity\Term;
+use Drupal\Core\Entity\EntityInterface;
 
 /**
  * Implements hook_help().
@@ -180,51 +179,6 @@ function path_form_element_validate($element, &$form_state, $complete_form) {
   }
 }
 
-/**
- * Implements hook_node_insert().
- */
-function path_node_insert(NodeInterface $node) {
-  if (isset($node->path)) {
-    $alias = trim($node->path['alias']);
-    // Only save a non-empty alias.
-    if (!empty($alias)) {
-      // Ensure fields for programmatic executions.
-      $source = 'node/' . $node->id();
-      $langcode = $node->language()->id;
-      Drupal::service('path.crud')->save($source, $alias, $langcode);
-    }
-  }
-}
-
-/**
- * Implements hook_node_update().
- */
-function path_node_update(NodeInterface $node) {
-  if (isset($node->path)) {
-    $path = $node->path;
-    $alias = trim($path['alias']);
-    // Delete old alias if user erased it.
-    if (!empty($path['pid']) && empty($path['alias'])) {
-      Drupal::service('path.crud')->delete(array('pid' => $path['pid']));
-    }
-    // Only save a non-empty alias.
-    if (!empty($path['alias'])) {
-      // Ensure fields for programmatic executions.
-      $source = 'node/' . $node->id();
-      $langcode = $node->language()->id;
-      Drupal::service('path.crud')->save($source, $alias, $langcode, $path['pid']);
-    }
-  }
-}
-
-/**
- * Implements hook_node_predelete().
- */
-function path_node_predelete(NodeInterface $node) {
-  // Delete all aliases associated with this node.
-  Drupal::service('path.crud')->delete(array('source' => 'node/' . $node->id()));
-}
-
 /**
  * Implements hook_form_FORM_ID_alter() for taxonomy_term_form().
  */
@@ -265,7 +219,7 @@ function path_form_taxonomy_term_form_alter(&$form, $form_state) {
  * Implements hook_entity_field_info().
  */
 function path_entity_field_info($entity_type) {
-  if ($entity_type === 'taxonomy_term') {
+  if ($entity_type === 'taxonomy_term' || $entity_type === 'node') {
     $info['definitions']['path'] = array(
       'type' => 'path_field',
       'label' => t('The path alias'),
@@ -277,48 +231,53 @@ function path_entity_field_info($entity_type) {
 }
 
 /**
- * Implements hook_taxonomy_term_insert().
+ * Implements hook_entity_insert().
+ *
+ * @todo: Move this to methods on the FieldItem class.
  */
-function path_taxonomy_term_insert(Term $term) {
-  if (isset($term->path)) {
-    $term->path->alias = trim($term->path->alias);
+function path_entity_insert(EntityInterface $entity) {
+  if ($entity->getPropertyDefinition('path')) {
+    $entity->path->alias = trim($entity->path->alias);
     // Only save a non-empty alias.
-    if (!empty($term->path->alias)) {
+    if (!empty($entity->path->alias)) {
       // Ensure fields for programmatic executions.
-      $source = 'taxonomy/term/' . $term->id();
-      $langcode = Language::LANGCODE_NOT_SPECIFIED;
-      Drupal::service('path.crud')->save($source, $term->path->alias, $langcode);
+      $uri = $entity->uri();
+      $langcode = $entity->language()->id;
+      Drupal::service('path.crud')->save($uri['path'], $entity->path->alias, $langcode);
     }
   }
 }
 
 /**
- * Implements hook_taxonomy_term_update().
+ * Implements hook_entity_update().
  */
-function path_taxonomy_term_update(Term $term) {
-  if (isset($term->path)) {
-    $term->path->alias = trim($term->path->alias);
+function path_entity_update(EntityInterface $entity) {
+  if ($entity->getPropertyDefinition('path')) {
+    $entity->path->alias = trim($entity->path->alias);
     // Delete old alias if user erased it.
-    if (!empty($term->path->pid) && empty($term->path->alias)) {
-      Drupal::service('path.crud')->delete(array('pid' => $term->path->pid));
+    if ($entity->path->pid && !$entity->path->alias) {
+      Drupal::service('path.crud')->delete(array('pid' => $entity->path->pid));
     }
     // Only save a non-empty alias.
-    if ($term->path->alias) {
-      $pid = (!empty($term->path->pid) ? $term->path->pid  : NULL);
+    if ($entity->path->alias) {
+      $pid = $entity->path->pid;
       // Ensure fields for programmatic executions.
-      $source = 'taxonomy/term/' . $term->id();
-      $langcode = Language::LANGCODE_NOT_SPECIFIED;
-      Drupal::service('path.crud')->save($source, $term->path->alias, $langcode, $pid);
+      $uri = $entity->uri();
+      $langcode = $entity->language()->id;
+      Drupal::service('path.crud')->save($uri['path'], $entity->path->alias, $langcode, $pid);
     }
   }
 }
 
 /**
- * Implements hook_taxonomy_term_delete().
+ * Implements hook_entity_predelete().
  */
-function path_taxonomy_term_delete(Term $term) {
-  // Delete all aliases associated with this term.
-  Drupal::service('path.crud')->delete(array('source' => 'taxonomy/term/' . $term->id()));
+function path_entity_predelete(EntityInterface $entity) {
+  if ($entity->getPropertyDefinition('path')) {
+    // Delete all aliases associated with this term.
+    $uri = $entity->uri();
+    Drupal::service('path.crud')->delete(array('source' => $uri['path']));
+  }
 }
 
 /**
diff --git a/core/modules/picture/lib/Drupal/picture/Tests/PictureFieldDisplayTest.php b/core/modules/picture/lib/Drupal/picture/Tests/PictureFieldDisplayTest.php
index 6b3c18f89d64..1e1598ac1202 100644
--- a/core/modules/picture/lib/Drupal/picture/Tests/PictureFieldDisplayTest.php
+++ b/core/modules/picture/lib/Drupal/picture/Tests/PictureFieldDisplayTest.php
@@ -123,7 +123,7 @@ public function _testPictureFieldFormatters($scheme) {
     $node = node_load($nid, TRUE);
 
     // Test that the default formatter is being used.
-    $image_uri = file_load($node->{$field_name}[Language::LANGCODE_NOT_SPECIFIED][0]['target_id'])->getFileUri();
+    $image_uri = file_load($node->{$field_name}->target_id)->getFileUri();
     $image = array(
       '#theme' => 'image',
       '#uri' => $image_uri,
diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/FileFieldAttributesTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/FileFieldAttributesTest.php
index 11176dc54523..f1a69cce87d5 100644
--- a/core/modules/rdf/lib/Drupal/rdf/Tests/FileFieldAttributesTest.php
+++ b/core/modules/rdf/lib/Drupal/rdf/Tests/FileFieldAttributesTest.php
@@ -73,7 +73,7 @@ public function setUp() {
     $nid = $this->uploadNodeFile($test_file, $this->fieldName, $type_name);
 
     $this->node = node_load($nid, TRUE);
-    $this->file = file_load($this->node->{$this->fieldName}[Language::LANGCODE_NOT_SPECIFIED][0]['target_id']);
+    $this->file = file_load($this->node->{$this->fieldName}->target_id);
 
   }
 
diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/ImageFieldAttributesTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/ImageFieldAttributesTest.php
index a622a8c2e870..14af8f672dec 100644
--- a/core/modules/rdf/lib/Drupal/rdf/Tests/ImageFieldAttributesTest.php
+++ b/core/modules/rdf/lib/Drupal/rdf/Tests/ImageFieldAttributesTest.php
@@ -73,7 +73,7 @@ public function setUp() {
     // Save a node with the image.
     $nid = $this->uploadNodeImage($image, $this->fieldName, 'article');
     $this->node = node_load($nid);
-    $this->file = file_load($this->node->{$this->fieldName}[Language::LANGCODE_NOT_SPECIFIED][0]['target_id']);
+    $this->file = file_load($this->node->{$this->fieldName}->target_id);
   }
 
   /**
diff --git a/core/modules/search/lib/Drupal/search/Tests/SearchMultilingualEntityTest.php b/core/modules/search/lib/Drupal/search/Tests/SearchMultilingualEntityTest.php
index e1021b4abf41..95f51b80aaea 100644
--- a/core/modules/search/lib/Drupal/search/Tests/SearchMultilingualEntityTest.php
+++ b/core/modules/search/lib/Drupal/search/Tests/SearchMultilingualEntityTest.php
@@ -118,10 +118,8 @@ function testSearchingMultilingualFieldValues() {
     search_update_totals();
     foreach ($this->searchable_nodes as $node) {
       // Each searchable node that we created contains values in the body field
-      // in one or more languages. Let's pick the last language variant from the
-      // body array and execute a search using that as a search keyword.
-      $body_language_variant = end($node->body);
-      $search_result = node_search_execute($body_language_variant[0]['value']);
+      // in one or more languages.
+      $search_result = node_search_execute($node->body->value);
       // See whether we get the same node as a result.
       $this->assertEqual($search_result[0]['node']->id(), $node->id(), 'The search has resulted the correct node.');
     }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Plugin/PluginTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Plugin/PluginTestBase.php
index c58033893e24..c73fd671d660 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Plugin/PluginTestBase.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Plugin/PluginTestBase.php
@@ -89,7 +89,7 @@ public function setUp() {
         'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockComplexContextBlock',
         'context' => array(
           'user' => array('class' => 'Drupal\user\UserInterface'),
-          'node' => array('class' => 'Drupal\Core\Entity\EntityBCDecorator'),
+          'node' => array('class' => 'Drupal\node\NodeInterface'),
         ),
       ),
     );
diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php
index 66a95c0ef429..0f660e36b484 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php
@@ -6,7 +6,8 @@
  */
 
 namespace Drupal\system\Tests\Upgrade;
-use Drupal\Core\Language\Language;
+
+use Drupal\field\Field;
 
 /**
  * Tests upgrade of system variables.
@@ -209,16 +210,10 @@ function testFieldUpgradeToConfig() {
     // The deleted field uuid and deleted instance field_uuid must match.
     $this->assertEqual($deleted_field['uuid'], $deleted_instance['field_uuid']);
 
-    // Check that pre-existing deleted field values are read correctly.
-    $entity = _field_create_entity_from_ids((object) array(
-      'entity_type' => 'node',
-      'bundle' => 'article',
-      'entity_id' => 2,
-      'revision_id' => 2,
-    ));
-    field_attach_load('node', array(2 => $entity), FIELD_LOAD_CURRENT, array('instance' => entity_create('field_instance', $deleted_instance)));
-    $deleted_value = $entity->get('test_deleted_field');
-    $this->assertEqual($deleted_value[Language::LANGCODE_NOT_SPECIFIED][0]['value'], 'Some deleted value');
+    // Check that pre-existing deleted field table is renamed correctly.
+    $field_entity = new Field($deleted_field);
+    $table_name = _field_sql_storage_tablename($deleted_field);
+    $this->assertEqual("field_deleted_data_" . substr(hash('sha256', $deleted_field['uuid']), 0, 10), $table_name);
 
     // Check that creation of a new node works as expected.
     $value = $this->randomName();
diff --git a/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/MockBlockManager.php b/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/MockBlockManager.php
index a89716089d64..386841bc9a15 100644
--- a/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/MockBlockManager.php
+++ b/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/MockBlockManager.php
@@ -91,7 +91,7 @@ public function __construct() {
       'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockComplexContextBlock',
       'context' => array(
         'user' => array('class' => 'Drupal\user\UserInterface'),
-        'node' => array('class' => 'Drupal\Core\Entity\EntityBCDecorator'),
+        'node' => array('class' => 'Drupal\node\NodeInterface'),
       ),
     ));
 
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermIndexTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermIndexTest.php
index 991f274ef869..239e927d7467 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermIndexTest.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermIndexTest.php
@@ -170,7 +170,7 @@ function testTaxonomyIndex() {
     $this->assertEqual(1, $index_count, 'Term 2 is indexed once.');
 
     // Update the article to change one term.
-    $node->{$this->field_name_1}[$langcode] = array(array('target_id' => $term_1->id()));
+    $node->{$this->field_name_1} = array(array('target_id' => $term_1->id()));
     $node->save();
 
     // Check that both terms are indexed.
@@ -186,7 +186,7 @@ function testTaxonomyIndex() {
     $this->assertEqual(1, $index_count, 'Term 2 is indexed.');
 
     // Update the article to change another term.
-    $node->{$this->field_name_2}[$langcode] = array(array('target_id' => $term_1->id()));
+    $node->{$this->field_name_2} = array(array('target_id' => $term_1->id()));
     $node->save();
 
     // Check that only one term is indexed.
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index f4258cd5b7aa..2d08f286dc6f 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -1133,21 +1133,10 @@ function taxonomy_build_node_index($node) {
       $field_name = $instance['field_name'];
       $field = field_info_field($field_name);
       if ($field['module'] == 'taxonomy' && $field['storage']['type'] == 'field_sql_storage') {
-        // If a field value is not set in the node object when $node->save() is
-        // called, the old value from $node->original is used.
-        if (isset($node->{$field_name})) {
-          $items = $node->{$field_name};
-        }
-        elseif (isset($node->original->{$field_name})) {
-          $items = $node->original->{$field_name};
-        }
-        else {
-          continue;
-        }
-        foreach (field_available_languages('node', $field) as $langcode) {
-          if (!empty($items[$langcode])) {
-            foreach ($items[$langcode] as $item) {
-              $tid_all[$item['target_id']] = $item['target_id'];
+        foreach ($node->getTranslationLanguages() as $language) {
+          foreach ($node->getTranslation($language->id)->$field_name as $item) {
+            if (!$item->isEmpty()) {
+              $tid_all[$item->target_id] = $item->target_id;
             }
           }
         }
diff --git a/core/modules/translation/lib/Drupal/translation/Tests/TranslationTest.php b/core/modules/translation/lib/Drupal/translation/Tests/TranslationTest.php
index 434464e98447..072b3df59aaa 100644
--- a/core/modules/translation/lib/Drupal/translation/Tests/TranslationTest.php
+++ b/core/modules/translation/lib/Drupal/translation/Tests/TranslationTest.php
@@ -112,11 +112,11 @@ function testContentTranslation() {
     $edit["body[$langcode][0][value]"] = $this->randomName();
     $this->drupalPost('node/add/page', $edit, t('Save'), array('query' => array('translation' => $node->id(), 'language' => 'es')));
     $duplicate = $this->drupalGetNodeByTitle($edit["title"]);
-    $this->assertEqual($duplicate->tnid, 0, 'The node does not have a tnid.');
+    $this->assertEqual($duplicate->tnid->value, 0, 'The node does not have a tnid.');
 
     // Update original and mark translation as outdated.
     $node_body = $this->randomName();
-    $node->body[Language::LANGCODE_NOT_SPECIFIED][0]['value'] = $node_body;
+    $node->body->value = $node_body;
     $edit = array();
     $edit["body[$langcode][0][value]"] = $node_body;
     $edit['translation[retranslate]'] = TRUE;
@@ -139,7 +139,7 @@ function testContentTranslation() {
     $this->drupalGet('node/add/page');
     $this->assertFieldByXPath('//select[@name="langcode"]//option', Language::LANGCODE_NOT_SPECIFIED, 'Language neutral is available in language selection with disabled languages.');
     $node2 = $this->createPage($this->randomName(), $this->randomName(), Language::LANGCODE_NOT_SPECIFIED);
-    $this->assertRaw($node2->body[Language::LANGCODE_NOT_SPECIFIED][0]['value'], 'Language neutral content created with disabled languages available.');
+    $this->assertRaw($node2->body->value, 'Language neutral content created with disabled languages available.');
 
     // Leave just one language installed and check that the translation overview
     // page is still accessible.
@@ -171,7 +171,7 @@ function testLanguageSwitchLinks() {
     // Unpublish the Spanish translation to check that the related language
     // switch link is not shown.
     $this->drupalLogin($this->admin_user);
-    $this->drupalPost("node/$translation_es->nid/edit", array(), t('Save and unpublish'));
+    $this->drupalPost('node/' . $translation_es->id() . '/edit', array(), t('Save and unpublish'));
     $this->drupalLogin($this->translator);
     $this->assertLanguageSwitchLinks($node, $translation_es, FALSE);
 
@@ -181,7 +181,7 @@ function testLanguageSwitchLinks() {
     $edit = array('language_interface[enabled][language-url]' => FALSE);
     $this->drupalPost('admin/config/regional/language/detection', $edit, t('Save settings'));
     $this->resetCaches();
-    $this->drupalPost("node/$translation_es->nid/edit", array(), t('Save and publish'));
+    $this->drupalPost('node/' . $translation_es->id() . '/edit', array(), t('Save and publish'));
     $this->drupalLogin($this->translator);
     $this->assertLanguageSwitchLinks($node, $translation_es, TRUE, 'node');
   }
@@ -396,7 +396,7 @@ function createTranslation(EntityInterface $node, $title, $body, $langcode) {
     // Check to make sure that translation was successful.
     $translation = $this->drupalGetNodeByTitle($title);
     $this->assertTrue($translation, 'Node found in database.');
-    $this->assertTrue($translation->tnid == $node->id(), 'Translation set id correctly stored.');
+    $this->assertTrue($translation->tnid->value == $node->id(), 'Translation set id correctly stored.');
 
     return $translation;
   }
@@ -440,7 +440,7 @@ function assertContentByXPath($xpath, array $arguments = array(), $value = NULL,
    * @return
    *   TRUE if the language switch links are found, FALSE if not.
    */
-  function assertLanguageSwitchLinks(NodeInterface $node, $translation, $find = TRUE, $types = NULL) {
+  function assertLanguageSwitchLinks(NodeInterface $node, NodeInterface $translation, $find = TRUE, $types = NULL) {
     if (empty($types)) {
       $types = array('node', 'block-language');
     }
@@ -449,10 +449,9 @@ function assertLanguageSwitchLinks(NodeInterface $node, $translation, $find = TR
     }
 
     $result = TRUE;
-    $languages = language_list();
-    $page_language = $languages[$node->language()->id];
-    $translation_language = $languages[$translation->langcode];
-    $url = url("node/$translation->nid", array('language' => $translation_language));
+    $page_language = $node->language();
+    $translation_language = $translation->language();
+    $url = url('node/' . $translation->id(), array('language' => $translation_language));
 
     $this->drupalGet('node/' . $node->id(), array('language' => $page_language));
 
@@ -468,7 +467,7 @@ function assertLanguageSwitchLinks(NodeInterface $node, $translation, $find = TR
       // node uses the article tag.
       $tag = $type == 'node' ? 'article' : 'div';
 
-      if ($translation->nid) {
+      if ($translation->id()) {
         $xpath = '//' . $tag . '[contains(@class, :type)]//a[@href=:url]';
       }
       else {
diff --git a/core/modules/translation/translation.module b/core/modules/translation/translation.module
index 398fd4f65e41..9843f069d0c0 100644
--- a/core/modules/translation/translation.module
+++ b/core/modules/translation/translation.module
@@ -201,31 +201,29 @@ function translation_form_node_form_alter(&$form, &$form_state) {
   $node = $form_state['controller']->getEntity();
   if (translation_supported_type($node->getType())) {
     if (!empty($node->translation_source)) {
-      // We are creating a translation. Add values and lock language field.
-      $form['translation_source'] = array('#type' => 'value', '#value' => $node->translation_source);
+      // We are creating a translation. Lock language field.
       $form['langcode']['#disabled'] = TRUE;
     }
-    elseif (!$node->isNew() && !empty($node->tnid)) {
+    elseif (!$node->isNew() && $node->tnid->value) {
       // Disable languages for existing translations, so it is not possible
       // to switch this node to some language which is already in the
       // translation set. Also remove the language neutral option.
       unset($form['langcode']['#options'][Language::LANGCODE_NOT_SPECIFIED]);
-      foreach (translation_node_get_translations($node->tnid) as $langcode => $translation) {
+      foreach (translation_node_get_translations($node->tnid->value) as $langcode => $translation) {
         if ($translation->nid != $node->id()) {
           unset($form['langcode']['#options'][$langcode]);
         }
       }
       // Add translation values and workflow options.
-      $form['tnid'] = array('#type' => 'value', '#value' => $node->tnid);
       $form['translation'] = array(
         '#type' => 'details',
         '#title' => t('Translation settings'),
         '#access' => translation_user_can_translate_node($node),
-        '#collapsed' => !$node->translate,
+        '#collapsed' => !$node->translate->value,
         '#tree' => TRUE,
         '#weight' => 30,
       );
-      if ($node->tnid == $node->id()) {
+      if ($node->tnid->value == $node->id()) {
         // This is the source node of the translation.
         $form['translation']['retranslate'] = array(
           '#type' => 'checkbox',
@@ -239,14 +237,26 @@ function translation_form_node_form_alter(&$form, &$form_state) {
         $form['translation']['status'] = array(
           '#type' => 'checkbox',
           '#title' => t('This translation needs to be updated'),
-          '#default_value' => $node->translate,
+          '#default_value' => $node->translate->value,
           '#description' => t('When this option is checked, this translation needs to be updated because the source post has changed. Uncheck when the translation is up to date again.'),
         );
       }
+      $form['#entity_builders'][] = 'translation_node_builder';
     }
   }
 }
 
+/**
+ * Entity form builder to add translation metadata to the node.
+ *
+ * @todo: Remove this in favor of an entity field.
+ */
+function translation_node_builder($entity_type, $entity, &$form, &$form_state) {
+  if (isset($form_state['values']['translation'])) {
+    $entity->translation = $form_state['values']['translation'];
+  }
+}
+
 /**
  * Implements hook_node_view().
  *
@@ -257,7 +267,7 @@ function translation_form_node_form_alter(&$form, &$form_state) {
 function translation_node_view(EntityInterface $node, EntityDisplay $display, $view_mode) {
   // If the site has no translations or is not multilingual we have no content
   // translation links to display.
-  if (isset($node->tnid) && language_multilingual() && $translations = translation_node_get_translations($node->tnid)) {
+  if ($node->tnid->value && language_multilingual() && $translations = translation_node_get_translations($node->tnid->value)) {
     $languages = language_list(Language::STATE_ALL);
 
     // There might be a language provider enabled defining custom language
@@ -331,8 +341,8 @@ function translation_node_prepare_form(NodeInterface $node, $form_display, $oper
     }
 
     // Ensure we don't have an existing translation in this language.
-    if (!empty($source_node->tnid)) {
-      $translations = translation_node_get_translations($source_node->tnid);
+    if (!empty($source_node->tnid->value)) {
+      $translations = translation_node_get_translations($source_node->tnid->value);
       if (isset($translations[$langcode])) {
         drupal_set_message(t('A translation of %title in %language already exists, a new %type will be created instead of a translation.', array('%title' => $source_node->label(), '%language' => $language_list[$langcode]->name, '%type' => $node->getType())), 'error');
         return;
@@ -353,9 +363,9 @@ function translation_node_insert(EntityInterface $node) {
   // Only act if we are dealing with a content type supporting translations.
   if (translation_supported_type($node->getType())) {
     if (!empty($node->translation_source)) {
-      if ($node->translation_source->tnid) {
+      if ($node->translation_source->tnid->value) {
         // Add node to existing translation set.
-        $tnid = $node->translation_source->tnid;
+        $tnid = $node->translation_source->tnid->value;
       }
       else {
         // Create new translation set, using nid from the source node.
@@ -387,11 +397,11 @@ function translation_node_insert(EntityInterface $node) {
 function translation_node_update(NodeInterface $node) {
   // Only act if we are dealing with a content type supporting translations.
   if (translation_supported_type($node->getType())) {
-    if (isset($node->translation) && $node->translation && $node->tnid) {
+    if (isset($node->translation) && $node->translation && $node->tnid->value) {
       // Update translation information.
       db_update('node')
         ->fields(array(
-          'tnid' => $node->tnid,
+          'tnid' => $node->tnid->value,
           'translate' => $node->translation['status'],
         ))
         ->condition('nid', $node->id())
@@ -401,7 +411,7 @@ function translation_node_update(NodeInterface $node) {
         db_update('node')
           ->fields(array('translate' => 1))
           ->condition('nid', $node->id(), '<>')
-          ->condition('tnid', $node->tnid)
+          ->condition('tnid', $node->tnid->value)
           ->execute();
       }
     }
@@ -413,11 +423,11 @@ function translation_node_update(NodeInterface $node) {
  *
  * Ensures that duplicate translations can't be created for the same source.
  */
-function translation_node_validate(EntityInterface $node, $form, &$form_state) {
+function translation_node_validate(NodeInterface $node, $form, &$form_state) {
   // Only act on translatable nodes with a tnid or translation_source.
   $form_node = $form_state['controller']->getEntity();
-  if (translation_supported_type($node->getType()) && (!empty($node->tnid) || ($form_node->translation_source && $form_node->translation_source->id()))) {
-    $tnid = !empty($node->tnid) ? $node->tnid : $form_node->translation_source->id();
+  if (translation_supported_type($node->getType()) && ($node->tnid->value || ($form_node->translation_source && $form_node->translation_source->id()))) {
+    $tnid = $node->tnid->value ?: $form_node->translation_source->id();
     $translations = translation_node_get_translations($tnid);
     if (isset($translations[$node->language()->id]) && $translations[$node->language()->id]->nid != $node->id()) {
       form_set_error('langcode', t('There is already a translation in this language.'));
@@ -428,7 +438,7 @@ function translation_node_validate(EntityInterface $node, $form, &$form_state) {
 /**
  * Implements hook_node_predelete().
  */
-function translation_node_predelete(EntityInterface $node) {
+function translation_node_predelete(NodeInterface $node) {
   // Only act if we are dealing with a content type supporting translations.
   if (translation_supported_type($node->getType())) {
     translation_remove_from_set($node);
@@ -441,17 +451,17 @@ function translation_node_predelete(EntityInterface $node) {
  * @param $node
  *   A node entity.
  */
-function translation_remove_from_set($node) {
-  if (isset($node->tnid)) {
+function translation_remove_from_set(NodeInterface $node) {
+  if ($node->tnid->value) {
     $query = db_update('node')
       ->fields(array(
         'tnid' => 0,
         'translate' => 0,
       ));
-    if (db_query('SELECT COUNT(*) FROM {node} WHERE tnid = :tnid', array(':tnid' => $node->tnid))->fetchField() == 1) {
+    if (db_query('SELECT COUNT(*) FROM {node} WHERE tnid = :tnid', array(':tnid' => $node->tnid->value))->fetchField() == 1) {
       // There is only one node left in the set: remove the set altogether.
       $query
-        ->condition('tnid', $node->tnid)
+        ->condition('tnid', $node->tnid->value)
         ->execute();
     }
     else {
@@ -461,11 +471,11 @@ function translation_remove_from_set($node) {
 
       // If the node being removed was the source of the translation set,
       // we pick a new source - preferably one that is up to date.
-      if ($node->tnid == $node->id()) {
-        $new_tnid = db_query('SELECT nid FROM {node} WHERE tnid = :tnid ORDER BY translate ASC, nid ASC', array(':tnid' => $node->tnid))->fetchField();
+      if ($node->tnid->value == $node->id()) {
+        $new_tnid = db_query('SELECT nid FROM {node} WHERE tnid = :tnid ORDER BY translate ASC, nid ASC', array(':tnid' => $node->tnid->value))->fetchField();
         db_update('node')
           ->fields(array('tnid' => $new_tnid))
-          ->condition('tnid', $node->tnid)
+          ->condition('tnid', $node->tnid->value)
           ->execute();
       }
     }
@@ -531,8 +541,8 @@ function translation_supported_type($type) {
 function translation_path_get_translations($path) {
   $paths = array();
   // Check for a node related path, and for its translations.
-  if ((preg_match("!^node/(\d+)(/.+|)$!", $path, $matches)) && ($node = node_load((int) $matches[1])) && !empty($node->tnid)) {
-    foreach (translation_node_get_translations($node->tnid) as $language => $translation_node) {
+  if ((preg_match("!^node/(\d+)(/.+|)$!", $path, $matches)) && ($node = node_load((int) $matches[1])) && $node->tnid->value) {
+    foreach (translation_node_get_translations($node->tnid->value) as $language => $translation_node) {
       $paths[$language] = 'node/' . $translation_node->id() . $matches[2];
     }
   }
@@ -549,7 +559,7 @@ function translation_language_switch_links_alter(array &$links, $type, $path) {
   if ($type == $language_type && preg_match("!^node/(\d+)(/.+|)!", $path, $matches)) {
     $node = node_load((int) $matches[1]);
 
-    if (empty($node->tnid)) {
+    if (empty($node->tnid->value)) {
       // If the node cannot be found nothing needs to be done. If it does not
       // have translations it might be a language neutral node, in which case we
       // must leave the language switch links unaltered. This is true also for
@@ -560,13 +570,13 @@ function translation_language_switch_links_alter(array &$links, $type, $path) {
       $translations = array($node->language()->id => $node);
     }
     else {
-      $translations = translation_node_get_translations($node->tnid);
+      $translations = translation_node_get_translations($node->tnid->value);
     }
 
     foreach ($links as $langcode => $link) {
       if (isset($translations[$langcode]) && $translations[$langcode]->status) {
         // Translation in a different node.
-        $nid = $translations[$langcode]->nid;
+        $nid = $translations[$langcode] instanceof EntityInterface ? $translations[$langcode]->id() : $translations[$langcode]->nid;
         $links[$langcode]['href'] = 'node/' . $nid  . $matches[2];
       }
       else {
diff --git a/core/modules/translation/translation.pages.inc b/core/modules/translation/translation.pages.inc
index 2b787bf9b02d..b5ec442cb39b 100644
--- a/core/modules/translation/translation.pages.inc
+++ b/core/modules/translation/translation.pages.inc
@@ -22,14 +22,14 @@
 function translation_node_overview(EntityInterface $node) {
   include_once DRUPAL_ROOT . '/core/includes/language.inc';
 
-  if ($node->tnid) {
+  if ($node->tnid->value) {
     // Already part of a set, grab that set.
-    $tnid = $node->tnid;
-    $translations = translation_node_get_translations($node->tnid);
+    $tnid = $node->tnid->value;
+    $translations = translation_node_get_translations($node->tnid->value);
   }
   else {
     // We have no translation source nid, this could be a new set, emulate that.
-    $tnid = $node->nid;
+    $tnid = $node->id();
     $translations = array($node->language()->id => $node);
   }
 
@@ -42,7 +42,8 @@ function translation_node_overview(EntityInterface $node) {
     if (isset($translations[$langcode])) {
       // Existing translation in the translation set: display status.
       // We load the full node to check whether the user can edit it.
-      $translation_node = node_load($translations[$langcode]->nid);
+      $nid = $translations[$langcode] instanceof EntityInterface ? $translations[$langcode]->id() : $translations[$langcode]->nid;
+      $translation_node = node_load($nid);
       $path = 'node/' . $translation_node->id();
       $links = language_negotiation_get_switch_links($type, $path);
       $title = empty($links->links[$langcode]['href']) ? l($translation_node->label(), $path) : l($translation_node->label(), $links->links[$langcode]['href'], $links->links[$langcode]);
@@ -56,7 +57,7 @@ function translation_node_overview(EntityInterface $node) {
         }
       }
       $status = $translation_node->isPublished() ? t('Published') : t('Not published');
-      $status .= $translation_node->translate ? ' - <span class="marker">' . t('outdated') . '</span>' : '';
+      $status .= $translation_node->translate->value ? ' - <span class="marker">' . t('outdated') . '</span>' : '';
       if ($translation_node->id() == $tnid) {
         $language_name = t('<strong>@language_name</strong> (source)', array('@language_name' => $language_name));
       }
diff --git a/core/modules/views/lib/Drupal/views/Tests/DefaultViewsTest.php b/core/modules/views/lib/Drupal/views/Tests/DefaultViewsTest.php
index 066f5187c377..3306755b410f 100644
--- a/core/modules/views/lib/Drupal/views/Tests/DefaultViewsTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/DefaultViewsTest.php
@@ -97,7 +97,7 @@ protected function setUp() {
 
       $node = $this->drupalCreateNode($values);
 
-      search_index($node->id(), 'node', $node->body[Language::LANGCODE_NOT_SPECIFIED][0]['value'], Language::LANGCODE_NOT_SPECIFIED);
+      search_index($node->id(), 'node', $node->body->value, Language::LANGCODE_NOT_SPECIFIED);
 
       $comment = array(
         'uid' => $user->id(),
-- 
GitLab