diff --git a/core/modules/field/migration_templates/d7_field.yml b/core/modules/field/migration_templates/d7_field.yml
index 069334322a40d67b3d4a93198eb31489488ae8ff..15079d88163dbf72bca613a0a849e9a05147f924 100644
--- a/core/modules/field/migration_templates/d7_field.yml
+++ b/core/modules/field/migration_templates/d7_field.yml
@@ -35,7 +35,10 @@ process:
       phone: telephone
       text_long: text_long
       text_with_summary: text_with_summary
-  translatable: translatable
+  # Translatable is not migrated and the Drupal 8 default of true is used.
+  # If translatable is false in field storage then the field can not be
+  # set to translatable via the UI.
+  #translatable: translatable
   cardinality: cardinality
   settings:
     plugin: d7_field_settings
diff --git a/core/modules/field/migration_templates/d7_field_instance.yml b/core/modules/field/migration_templates/d7_field_instance.yml
index da4ce0a458a01b81f45d6a85ca22f9eca96cb184..f3518c91a26057ed491a970716db92f0c316d2c9 100644
--- a/core/modules/field/migration_templates/d7_field_instance.yml
+++ b/core/modules/field/migration_templates/d7_field_instance.yml
@@ -28,6 +28,7 @@ process:
     source:
       - default_value
       - widget_settings
+  translatable: translatable
 destination:
   plugin: entity:field_config
 migration_dependencies:
diff --git a/core/modules/field/src/Plugin/migrate/source/d7/FieldInstance.php b/core/modules/field/src/Plugin/migrate/source/d7/FieldInstance.php
index 5d3d6cc066664fbf6936c781a4c2161eaebd00fb..3da02b92a0cb7a477b971169d9131e5efc69c12a 100644
--- a/core/modules/field/src/Plugin/migrate/source/d7/FieldInstance.php
+++ b/core/modules/field/src/Plugin/migrate/source/d7/FieldInstance.php
@@ -86,6 +86,25 @@ public function prepareRow(Row $row) {
     $field_data = unserialize($row->getSourceProperty('field_data'));
     $row->setSourceProperty('field_settings', $field_data['settings']);
 
+    $translatable = FALSE;
+    if ($row->getSourceProperty('entity_type') == 'node') {
+      // language_content_type_[bundle] may be
+      //   - 0: no language support
+      //   - 1: language assignment support
+      //   - 2: node translation support
+      //   - 4: entity translation support
+      if ($this->variableGet('language_content_type_' . $row->getSourceProperty('bundle'), 0) == 2) {
+        $translatable = TRUE;
+      }
+    }
+    else {
+      // This is not a node entity. Get the translatable value from the source
+      // field_config table.
+      $data = unserialize($row->getSourceProperty('field_data'));
+      $translatable = $data['translatable'];
+    }
+    $row->setSourceProperty('translatable', $translatable);
+
     return parent::prepareRow($row);
   }
 
diff --git a/core/modules/field/tests/src/Kernel/Migrate/d7/MigrateFieldInstanceTest.php b/core/modules/field/tests/src/Kernel/Migrate/d7/MigrateFieldInstanceTest.php
index 7c2115fbe535712fc6b90ccda24a7416ce4f6aed..c50c3241fbbdc323fa06cb33adb157cb667ed3b3 100644
--- a/core/modules/field/tests/src/Kernel/Migrate/d7/MigrateFieldInstanceTest.php
+++ b/core/modules/field/tests/src/Kernel/Migrate/d7/MigrateFieldInstanceTest.php
@@ -80,8 +80,10 @@ protected function createType($id) {
    *   The expected field type.
    * @param bool $is_required
    *   Whether or not the field is required.
+   * @param bool $expected_translatable
+   *   Whether or not the field is expected to be translatable.
    */
-  protected function assertEntity($id, $expected_label, $expected_field_type, $is_required) {
+  protected function assertEntity($id, $expected_label, $expected_field_type, $is_required, $expected_translatable) {
     list ($expected_entity_type, $expected_bundle, $expected_name) = explode('.', $id);
 
     /** @var \Drupal\field\FieldConfigInterface $field */
@@ -94,6 +96,7 @@ protected function assertEntity($id, $expected_label, $expected_field_type, $is_
     $this->assertIdentical($expected_name, $field->getName());
     $this->assertEqual($is_required, $field->isRequired());
     $this->assertIdentical($expected_entity_type . '.' . $expected_name, $field->getFieldStorageDefinition()->id());
+    $this->assertSame($expected_translatable, $field->isTranslatable());
   }
 
   /**
@@ -113,40 +116,38 @@ protected function assertLinkFields($id, $title_setting) {
    * Tests migrating D7 field instances to field_config entities.
    */
   public function testFieldInstances() {
-    $this->assertEntity('comment.comment_node_page.comment_body', 'Comment', 'text_long', TRUE);
-    $this->assertEntity('node.page.body', 'Body', 'text_with_summary', FALSE);
-    $this->assertEntity('comment.comment_node_article.comment_body', 'Comment', 'text_long', TRUE);
-    $this->assertEntity('node.article.body', 'Body', 'text_with_summary', FALSE);
-    $this->assertEntity('node.article.field_tags', 'Tags', 'entity_reference', FALSE);
-    $this->assertEntity('node.article.field_image', 'Image', 'image', FALSE);
-    $this->assertEntity('comment.comment_node_blog.comment_body', 'Comment', 'text_long', TRUE);
-    $this->assertEntity('node.blog.body', 'Body', 'text_with_summary', FALSE);
-    $this->assertEntity('comment.comment_node_book.comment_body', 'Comment', 'text_long', TRUE);
-    $this->assertEntity('node.book.body', 'Body', 'text_with_summary', FALSE);
-    $this->assertEntity('node.forum.taxonomy_forums', 'Forums', 'entity_reference', TRUE);
-    $this->assertEntity('comment.comment_node_forum.comment_body', 'Comment', 'text_long', TRUE);
-    $this->assertEntity('node.forum.body', 'Body', 'text_with_summary', FALSE);
-    $this->assertEntity('comment.comment_node_test_content_type.comment_body', 'Comment', 'text_long', TRUE);
-    $this->assertEntity('node.test_content_type.field_boolean', 'Boolean', 'boolean', FALSE);
-    $this->assertEntity('node.test_content_type.field_email', 'Email', 'email', FALSE);
-    $this->assertEntity('node.test_content_type.field_phone', 'Phone', 'telephone', TRUE);
-    $this->assertEntity('node.test_content_type.field_date', 'Date', 'datetime', FALSE);
-    $this->assertEntity('node.test_content_type.field_date_with_end_time', 'Date With End Time', 'datetime', FALSE);
-    $this->assertEntity('node.test_content_type.field_file', 'File', 'file', FALSE);
-    $this->assertEntity('node.test_content_type.field_float', 'Float', 'float', FALSE);
-    $this->assertEntity('node.test_content_type.field_images', 'Images', 'image', TRUE);
-    $this->assertEntity('node.test_content_type.field_integer', 'Integer', 'integer', TRUE);
-    $this->assertEntity('node.test_content_type.field_link', 'Link', 'link', FALSE);
-    $this->assertEntity('node.test_content_type.field_text_list', 'Text List', 'list_string', FALSE);
-    $this->assertEntity('node.test_content_type.field_integer_list', 'Integer List', 'list_integer', FALSE);
-    $this->assertEntity('node.test_content_type.field_long_text', 'Long text', 'text_with_summary', FALSE);
-    $this->assertEntity('node.test_content_type.field_term_reference', 'Term Reference', 'entity_reference', FALSE);
-    $this->assertEntity('node.test_content_type.field_node_entityreference', 'Node Entity Reference', 'entity_reference', FALSE);
-    $this->assertEntity('node.test_content_type.field_user_entityreference', 'User Entity Reference', 'entity_reference', FALSE);
-    $this->assertEntity('node.test_content_type.field_term_entityreference', 'Term Entity Reference', 'entity_reference', FALSE);
-    $this->assertEntity('node.test_content_type.field_text', 'Text', 'text', FALSE);
-    $this->assertEntity('comment.comment_node_test_content_type.field_integer', 'Integer', 'integer', FALSE);
-    $this->assertEntity('user.user.field_file', 'File', 'file', FALSE);
+    $this->assertEntity('comment.comment_node_page.comment_body', 'Comment', 'text_long', TRUE, FALSE);
+    $this->assertEntity('node.page.body', 'Body', 'text_with_summary', FALSE, FALSE);
+    $this->assertEntity('comment.comment_node_article.comment_body', 'Comment', 'text_long', TRUE, FALSE);
+    $this->assertEntity('node.article.body', 'Body', 'text_with_summary', FALSE, TRUE);
+    $this->assertEntity('node.article.field_tags', 'Tags', 'entity_reference', FALSE, TRUE);
+    $this->assertEntity('node.article.field_image', 'Image', 'image', FALSE, TRUE);
+    $this->assertEntity('comment.comment_node_blog.comment_body', 'Comment', 'text_long', TRUE, FALSE);
+    $this->assertEntity('node.blog.body', 'Body', 'text_with_summary', FALSE, TRUE);
+    $this->assertEntity('comment.comment_node_book.comment_body', 'Comment', 'text_long', TRUE, FALSE);
+    $this->assertEntity('node.book.body', 'Body', 'text_with_summary', FALSE, FALSE);
+    $this->assertEntity('node.forum.taxonomy_forums', 'Forums', 'entity_reference', TRUE, FALSE);
+    $this->assertEntity('comment.comment_node_forum.comment_body', 'Comment', 'text_long', TRUE, FALSE);
+    $this->assertEntity('node.forum.body', 'Body', 'text_with_summary', FALSE, FALSE);
+    $this->assertEntity('comment.comment_node_test_content_type.comment_body', 'Comment', 'text_long', TRUE, FALSE);
+    $this->assertEntity('node.test_content_type.field_boolean', 'Boolean', 'boolean', FALSE, FALSE);
+    $this->assertEntity('node.test_content_type.field_email', 'Email', 'email', FALSE, FALSE);
+    $this->assertEntity('node.test_content_type.field_phone', 'Phone', 'telephone', TRUE, FALSE);
+    $this->assertEntity('node.test_content_type.field_date', 'Date', 'datetime', FALSE, FALSE);
+    $this->assertEntity('node.test_content_type.field_date_with_end_time', 'Date With End Time', 'datetime', FALSE, FALSE);
+    $this->assertEntity('node.test_content_type.field_file', 'File', 'file', FALSE, FALSE);
+    $this->assertEntity('node.test_content_type.field_float', 'Float', 'float', FALSE, FALSE);
+    $this->assertEntity('node.test_content_type.field_images', 'Images', 'image', TRUE, FALSE);
+    $this->assertEntity('node.test_content_type.field_integer', 'Integer', 'integer', TRUE, FALSE);
+    $this->assertEntity('node.test_content_type.field_link', 'Link', 'link', FALSE, FALSE);
+    $this->assertEntity('node.test_content_type.field_text_list', 'Text List', 'list_string', FALSE, FALSE);
+    $this->assertEntity('node.test_content_type.field_integer_list', 'Integer List', 'list_integer', FALSE, FALSE);
+    $this->assertEntity('node.test_content_type.field_long_text', 'Long text', 'text_with_summary', FALSE, FALSE);
+    $this->assertEntity('node.test_content_type.field_term_reference', 'Term Reference', 'entity_reference', FALSE, FALSE);
+    $this->assertEntity('node.test_content_type.field_text', 'Text', 'text', FALSE, FALSE);
+    $this->assertEntity('comment.comment_node_test_content_type.field_integer', 'Integer', 'integer', FALSE, FALSE);
+    $this->assertEntity('user.user.field_file', 'File', 'file', FALSE, FALSE);
+
 
     $this->assertLinkFields('node.test_content_type.field_link', DRUPAL_OPTIONAL);
     $this->assertLinkFields('node.article.field_link', DRUPAL_DISABLED);
diff --git a/core/modules/field/tests/src/Kernel/Migrate/d7/MigrateFieldTest.php b/core/modules/field/tests/src/Kernel/Migrate/d7/MigrateFieldTest.php
index fb275c67ca7a5eb35f2de6f05095e35a413bd9f1..4a0f8cde5694f9b30ceac8ccbc2a99a4d03b6501 100644
--- a/core/modules/field/tests/src/Kernel/Migrate/d7/MigrateFieldTest.php
+++ b/core/modules/field/tests/src/Kernel/Migrate/d7/MigrateFieldTest.php
@@ -60,8 +60,6 @@ protected function assertEntity($id, $expected_type, $expected_translatable, $ex
     $this->assertTrue($field instanceof FieldStorageConfigInterface);
     $this->assertIdentical($expected_name, $field->getName());
     $this->assertIdentical($expected_type, $field->getType());
-    // FieldStorageConfig::$translatable is TRUE by default, so it is useful
-    // to test for FALSE here.
     $this->assertEqual($expected_translatable, $field->isTranslatable());
     $this->assertIdentical($expected_entity_type, $field->getTargetEntityTypeId());
 
@@ -78,31 +76,31 @@ protected function assertEntity($id, $expected_type, $expected_translatable, $ex
    * Tests migrating D7 fields to field_storage_config entities.
    */
   public function testFields() {
-    $this->assertEntity('node.body', 'text_with_summary', FALSE, 1);
-    $this->assertEntity('node.field_long_text', 'text_with_summary', FALSE, 1);
-    $this->assertEntity('comment.comment_body', 'text_long', FALSE, 1);
-    $this->assertEntity('node.field_file', 'file', FALSE, 1);
-    $this->assertEntity('user.field_file', 'file', FALSE, 1);
-    $this->assertEntity('node.field_float', 'float', FALSE, 1);
-    $this->assertEntity('node.field_image', 'image', FALSE, 1);
-    $this->assertEntity('node.field_images', 'image', FALSE, 1);
-    $this->assertEntity('node.field_integer', 'integer', FALSE, 1);
-    $this->assertEntity('comment.field_integer', 'integer', FALSE, 1);
-    $this->assertEntity('node.field_integer_list', 'list_integer', FALSE, 1);
-    $this->assertEntity('node.field_link', 'link', FALSE, 1);
-    $this->assertEntity('node.field_tags', 'entity_reference', FALSE, -1);
-    $this->assertEntity('node.field_term_reference', 'entity_reference', FALSE, 1);
-    $this->assertEntity('node.taxonomy_forums', 'entity_reference', FALSE, 1);
-    $this->assertEntity('node.field_text', 'text', FALSE, 1);
-    $this->assertEntity('node.field_text_list', 'list_string', FALSE, 3);
-    $this->assertEntity('node.field_boolean', 'boolean', FALSE, 1);
-    $this->assertEntity('node.field_email', 'email', FALSE, -1);
-    $this->assertEntity('node.field_phone', 'telephone', FALSE, 1);
-    $this->assertEntity('node.field_date', 'datetime', FALSE, 1);
-    $this->assertEntity('node.field_date_with_end_time', 'datetime', FALSE, 1);
-    $this->assertEntity('node.field_node_entityreference', 'entity_reference', FALSE, -1);
-    $this->assertEntity('node.field_user_entityreference', 'entity_reference', FALSE, 1);
-    $this->assertEntity('node.field_term_entityreference', 'entity_reference', FALSE, -1);
+    $this->assertEntity('node.body', 'text_with_summary', TRUE, 1);
+    $this->assertEntity('node.field_long_text', 'text_with_summary', TRUE, 1);
+    $this->assertEntity('comment.comment_body', 'text_long', TRUE, 1);
+    $this->assertEntity('node.field_file', 'file', TRUE, 1);
+    $this->assertEntity('user.field_file', 'file', TRUE, 1);
+    $this->assertEntity('node.field_float', 'float', TRUE, 1);
+    $this->assertEntity('node.field_image', 'image', TRUE, 1);
+    $this->assertEntity('node.field_images', 'image', TRUE, 1);
+    $this->assertEntity('node.field_integer', 'integer', TRUE, 1);
+    $this->assertEntity('comment.field_integer', 'integer', TRUE, 1);
+    $this->assertEntity('node.field_integer_list', 'list_integer', TRUE, 1);
+    $this->assertEntity('node.field_link', 'link', TRUE, 1);
+    $this->assertEntity('node.field_tags', 'entity_reference', TRUE, -1);
+    $this->assertEntity('node.field_term_reference', 'entity_reference', TRUE, 1);
+    $this->assertEntity('node.taxonomy_forums', 'entity_reference', TRUE, 1);
+    $this->assertEntity('node.field_text', 'text', TRUE, 1);
+    $this->assertEntity('node.field_text_list', 'list_string', TRUE, 3);
+    $this->assertEntity('node.field_boolean', 'boolean', TRUE, 1);
+    $this->assertEntity('node.field_email', 'email', TRUE, -1);
+    $this->assertEntity('node.field_phone', 'telephone', TRUE, 1);
+    $this->assertEntity('node.field_date', 'datetime', TRUE, 1);
+    $this->assertEntity('node.field_date_with_end_time', 'datetime', TRUE, 1);
+    $this->assertEntity('node.field_node_entityreference', 'entity_reference', TRUE, -1);
+    $this->assertEntity('node.field_user_entityreference', 'entity_reference', TRUE, 1);
+    $this->assertEntity('node.field_term_entityreference', 'entity_reference', TRUE, -1);
 
     // Assert that the taxonomy term reference fields are referencing the
     // correct entity type.
@@ -123,7 +121,8 @@ public function testFields() {
     // Validate that the source count and processed count match up.
     /** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
     $migration = $this->getMigration('d7_field');
-    $this->assertIdentical($migration->getSourcePlugin()->count(), $migration->getIdMap()->processedCount());
+    $this->assertSame($migration->getSourcePlugin()
+      ->count(), $migration->getIdMap()->processedCount());
   }
 
 }
diff --git a/core/modules/field/tests/src/Kernel/Plugin/migrate/source/d7/FieldInstanceTest.php b/core/modules/field/tests/src/Kernel/Plugin/migrate/source/d7/FieldInstanceTest.php
index a0b8ec1dbcc2346aed8a23318d7c02e50b60d738..c9e93efd25a9ce83611daa7f783b264cc02cc6c9 100644
--- a/core/modules/field/tests/src/Kernel/Plugin/migrate/source/d7/FieldInstanceTest.php
+++ b/core/modules/field/tests/src/Kernel/Plugin/migrate/source/d7/FieldInstanceTest.php
@@ -94,6 +94,7 @@ public function providerSource() {
         ],
         'description' => '',
         'required' => FALSE,
+        'field_data' => 'a:6:{s:12:"entity_types";a:1:{i:0;s:4:"node";}s:12:"translatable";b:0;s:8:"settings";a:0:{}s:7:"storage";a:4:{s:4:"type";s:17:"field_sql_storage";s:8:"settings";a:0:{}s:6:"module";s:17:"field_sql_storage";s:6:"active";i:1;}s:12:"foreign keys";a:1:{s:6:"format";a:2:{s:5:"table";s:13:"filter_format";s:7:"columns";a:1:{s:6:"format";s:6:"format";}}}s:7:"indexes";a:1:{s:6:"format";a:1:{i:0;s:6:"format";}}}',
       ],
     ];
 
diff --git a/core/modules/migrate_drupal/tests/fixtures/drupal7.php b/core/modules/migrate_drupal/tests/fixtures/drupal7.php
index a388f8e5a18c6e5f4e812b4e5e945f9e31c77842..ea4ecdac1d2603bce582fdbbe167d95845c8bedd 100644
--- a/core/modules/migrate_drupal/tests/fixtures/drupal7.php
+++ b/core/modules/migrate_drupal/tests/fixtures/drupal7.php
@@ -6141,6 +6141,30 @@
   'body_summary' => '',
   'body_format' => 'filtered_html',
 ))
+->values(array(
+  'entity_type' => 'node',
+  'bundle' => 'article',
+  'deleted' => '0',
+  'entity_id' => '4',
+  'revision_id' => '4',
+  'language' => 'und',
+  'delta' => '0',
+  'body_value' => 'is - Is that is it awesome.',
+  'body_summary' => '',
+  'body_format' => 'filtered_html',
+))
+->values(array(
+  'entity_type' => 'node',
+  'bundle' => 'article',
+  'deleted' => '0',
+  'entity_id' => '5',
+  'revision_id' => '5',
+  'language' => 'und',
+  'delta' => '0',
+  'body_value' => 'en - Is that is it awesome.',
+  'body_summary' => '',
+  'body_format' => 'filtered_html',
+))
 ->execute();
 
 $connection->schema()->createTable('field_revision_comment_body', array(
@@ -30945,6 +30969,38 @@
   'tnid' => '2',
   'translate' => '0',
 ))
+->values(array(
+  'nid' => '4',
+  'vid' => '4',
+  'type' => 'article',
+  'language' => 'is',
+  'title' => 'is - The thing about Firefly',
+  'uid' => '1',
+  'status' => '1',
+  'created' => '1478755274',
+  'changed' => '1478755274',
+  'comment' => '2',
+  'promote' => '1',
+  'sticky' => '0',
+  'tnid' => '4',
+  'translate' => '0',
+))
+->values(array(
+  'nid' => '5',
+  'vid' => '5',
+  'type' => 'article',
+  'language' => 'en',
+  'title' => 'en - The thing about Firefly',
+  'uid' => '1',
+  'status' => '1',
+  'created' => '1478755314',
+  'changed' => '1478755314',
+  'comment' => '2',
+  'promote' => '1',
+  'sticky' => '0',
+  'tnid' => '4',
+  'translate' => '0',
+))
 ->execute();
 
 $connection->schema()->createTable('node_access', array(
@@ -31089,6 +31145,22 @@
   'last_comment_uid' => '1',
   'comment_count' => '0',
 ))
+->values(array(
+  'nid' => '4',
+  'cid' => '0',
+  'last_comment_timestamp' => '1478755274',
+  'last_comment_name' => NULL,
+  'last_comment_uid' => '1',
+  'comment_count' => '0',
+))
+->values(array(
+  'nid' => '5',
+  'cid' => '0',
+  'last_comment_timestamp' => '1478755314',
+  'last_comment_name' => NULL,
+  'last_comment_uid' => '1',
+  'comment_count' => '0',
+))
 ->execute();
 
 $connection->schema()->createTable('node_counter', array(
@@ -31143,15 +31215,27 @@
 ->values(array(
   'nid' => '2',
   'totalcount' => '1',
-  'daycount' => '1',
+  'daycount' => '0',
   'timestamp' => '1471428059',
 ))
 ->values(array(
   'nid' => '3',
   'totalcount' => '1',
-  'daycount' => '1',
+  'daycount' => '0',
   'timestamp' => '1471428153',
 ))
+->values(array(
+  'nid' => '4',
+  'totalcount' => '1',
+  'daycount' => '1',
+  'timestamp' => '1478755275',
+))
+->values(array(
+  'nid' => '5',
+  'totalcount' => '1',
+  'daycount' => '1',
+  'timestamp' => '1478755314',
+))
 ->execute();
 
 $connection->schema()->createTable('node_revision', array(
@@ -31272,6 +31356,30 @@
   'promote' => '1',
   'sticky' => '0',
 ))
+->values(array(
+  'nid' => '4',
+  'vid' => '4',
+  'uid' => '1',
+  'title' => 'is - The thing about Firefly',
+  'log' => '',
+  'timestamp' => '1478755274',
+  'status' => '1',
+  'comment' => '2',
+  'promote' => '1',
+  'sticky' => '0',
+))
+->values(array(
+  'nid' => '5',
+  'vid' => '5',
+  'uid' => '1',
+  'title' => 'en - The thing about Firefly',
+  'log' => '',
+  'timestamp' => '1478755314',
+  'status' => '1',
+  'comment' => '2',
+  'promote' => '1',
+  'sticky' => '0',
+))
 ->execute();
 
 $connection->schema()->createTable('node_type', array(
diff --git a/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php b/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php
index 90b0b61dde9cdd79bb426713c236a00268018747..0c12e3b9225fbb9e9e809c1da4b105f747966cf7 100644
--- a/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php
+++ b/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php
@@ -374,6 +374,10 @@ class MigrateUpgradeForm extends ConfirmFormBase {
       'source_module' => 'node',
       'destination_module' => 'node',
     ],
+    'd7_node_translation' => [
+      'source_module' => 'node',
+      'destination_module' => 'node',
+    ],
     'd7_node_title_label' => [
       'source_module' => 'node',
       'destination_module' => 'node',
diff --git a/core/modules/node/migration_templates/d7_node.yml b/core/modules/node/migration_templates/d7_node.yml
index 381f7ce70c97ab983153e703dc793700781fe3c1..5de3055882a5147e0939915de70188d1f351545d 100644
--- a/core/modules/node/migration_templates/d7_node.yml
+++ b/core/modules/node/migration_templates/d7_node.yml
@@ -8,7 +8,10 @@ source:
 process:
   # If you are using this file to build a custom migration consider removing
   # the nid and vid fields to allow incremental migrations.
-  nid: nid
+  # In D7, nodes always have a tnid, but it's zero for untranslated nodes.
+  # We normalize it to equal the nid in that case.
+  # @see \Drupal\node\Plugin\migrate\source\d7\Node::prepareRow().
+  nid: tnid
   vid: vid
   langcode:
     plugin: default_value
diff --git a/core/modules/node/migration_templates/d7_node_translation.yml b/core/modules/node/migration_templates/d7_node_translation.yml
new file mode 100644
index 0000000000000000000000000000000000000000..11dfbb792a1fa243e7d6b259e32de906fcb97e5c
--- /dev/null
+++ b/core/modules/node/migration_templates/d7_node_translation.yml
@@ -0,0 +1,42 @@
+id: d7_node_translation
+label: Node translations
+migration_tags:
+  - Drupal 7
+  - translation
+deriver: Drupal\node\Plugin\migrate\D7NodeDeriver
+source:
+  plugin: d7_node
+  translations: true
+process:
+  # If you are using this file to build a custom migration consider removing
+  # the nid field to allow incremental migrations.
+  nid: tnid
+  type: type
+  langcode:
+    plugin: default_value
+    source: language
+    default_value: "und"
+  title: title
+  uid: node_uid
+  status: status
+  created: created
+  changed: changed
+  promote: promote
+  sticky: sticky
+  revision_uid: revision_uid
+  revision_log: log
+  revision_timestamp: timestamp
+  content_translation_source: source_langcode
+destination:
+  plugin: entity:node
+  translations: true
+  content_translation_update_definitions:
+    - node
+migration_dependencies:
+  required:
+    - d7_user
+    - d7_node_type
+    - language
+  optional:
+    - d7_field_instance
+provider: migrate_drupal
diff --git a/core/modules/node/src/Plugin/migrate/D7NodeDeriver.php b/core/modules/node/src/Plugin/migrate/D7NodeDeriver.php
index 3fc268266c881e7bb59c5cbd2867368ecd946528..a35c3763a756d85a361818b3c883f17257c856a4 100644
--- a/core/modules/node/src/Plugin/migrate/D7NodeDeriver.php
+++ b/core/modules/node/src/Plugin/migrate/D7NodeDeriver.php
@@ -38,6 +38,13 @@ class D7NodeDeriver extends DeriverBase implements ContainerDeriverInterface {
    */
   protected $cckPluginManager;
 
+  /**
+   * Whether or not to include translations.
+   *
+   * @var bool
+   */
+  protected $includeTranslations;
+
   /**
    * D7NodeDeriver constructor.
    *
@@ -45,19 +52,24 @@ class D7NodeDeriver extends DeriverBase implements ContainerDeriverInterface {
    *   The base plugin ID for the plugin ID.
    * @param \Drupal\migrate_drupal\Plugin\MigrateCckFieldPluginManagerInterface $cck_manager
    *   The CCK plugin manager.
+   * @param bool $translations
+   *   Whether or not to include translations.
    */
-  public function __construct($base_plugin_id, MigrateCckFieldPluginManagerInterface $cck_manager) {
+  public function __construct($base_plugin_id, MigrateCckFieldPluginManagerInterface $cck_manager, $translations) {
     $this->basePluginId = $base_plugin_id;
     $this->cckPluginManager = $cck_manager;
+    $this->includeTranslations = $translations;
   }
 
   /**
    * {@inheritdoc}
    */
   public static function create(ContainerInterface $container, $base_plugin_id) {
+    // Translations don't make sense unless we have content_translation.
     return new static(
       $base_plugin_id,
-      $container->get('plugin.manager.migrate.cckfield')
+      $container->get('plugin.manager.migrate.cckfield'),
+      $container->get('module_handler')->moduleExists('content_translation')
     );
   }
 
@@ -65,6 +77,11 @@ public static function create(ContainerInterface $container, $base_plugin_id) {
    * {@inheritdoc}
    */
   public function getDerivativeDefinitions($base_plugin_definition) {
+    if (in_array('translation', $base_plugin_definition['migration_tags']) && !$this->includeTranslations) {
+      // Refuse to generate anything.
+      return $this->derivatives;
+    }
+
     $node_types = static::getSourcePlugin('d7_node_type');
     try {
       $node_types->checkRequirements();
@@ -105,6 +122,13 @@ public function getDerivativeDefinitions($base_plugin_definition) {
         $values['source']['node_type'] = $node_type;
         $values['destination']['default_bundle'] = $node_type;
 
+        // If this migration is based on the d7_node_revision migration or
+        // is for translations of nodes, it should explicitly depend on the
+        // corresponding d7_node variant.
+        if ($base_plugin_definition['id'] == ['d7_node_revision'] || in_array('translation', $base_plugin_definition['migration_tags'])) {
+          $values['migration_dependencies']['required'][] = 'd7_node:' . $node_type;
+        }
+
         $migration = \Drupal::service('plugin.manager.migration')->createStubMigration($values);
         if (isset($fields[$node_type])) {
           foreach ($fields[$node_type] as $field_name => $info) {
@@ -131,7 +155,6 @@ public function getDerivativeDefinitions($base_plugin_definition) {
       // MigrationPluginManager gathers up the migration definitions but we do
       // not actually have a Drupal 7 source database.
     }
-
     return $this->derivatives;
   }
 
diff --git a/core/modules/node/src/Plugin/migrate/source/d7/Node.php b/core/modules/node/src/Plugin/migrate/source/d7/Node.php
index 6286d5fa47d308c35f3bd9cbc1cd22f6f14f8c1b..85acee127c48fb546d21f523c8267bad30b3efee 100644
--- a/core/modules/node/src/Plugin/migrate/source/d7/Node.php
+++ b/core/modules/node/src/Plugin/migrate/source/d7/Node.php
@@ -2,8 +2,15 @@
 
 namespace Drupal\node\Plugin\migrate\source\d7;
 
+use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\migrate\Row;
 use Drupal\migrate_drupal\Plugin\migrate\source\d7\FieldableEntity;
+use Drupal\Core\Database\Query\SelectInterface;
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Extension\ModuleHandler;
+use Drupal\Core\State\StateInterface;
+use Drupal\migrate\Plugin\MigrationInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Drupal 7 node source from database.
@@ -14,6 +21,35 @@
  * )
  */
 class Node extends FieldableEntity {
+  /**
+   * The module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, StateInterface $state, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $state, $entity_manager);
+    $this->moduleHandler = $module_handler;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $migration,
+      $container->get('state'),
+      $container->get('entity.manager'),
+      $container->get('module_handler')
+    );
+  }
 
   /**
    * The join options between the node and the node_revisions table.
@@ -49,6 +85,14 @@ public function query() {
     $query->addField('nr', 'uid', 'revision_uid');
     $query->innerJoin('node', 'n', static::JOIN);
 
+    // If the content_translation module is enabled, get the source langcode
+    // to fill the content_translation_source field.
+    if ($this->moduleHandler->moduleExists('content_translation')) {
+      $query->leftJoin('node', 'nt', 'n.tnid = nt.nid');
+      $query->addField('nt', 'language', 'source_langcode');
+    }
+    $this->handleTranslations($query);
+
     if (isset($this->configuration['node_type'])) {
       $query->condition('n.type', $this->configuration['node_type']);
     }
@@ -66,6 +110,11 @@ public function prepareRow(Row $row) {
       $vid = $row->getSourceProperty('vid');
       $row->setSourceProperty($field, $this->getFieldValues('node', $field, $nid, $vid));
     }
+
+    // Make sure we always have a translation set.
+    if ($row->getSourceProperty('tnid') == 0) {
+      $row->setSourceProperty('tnid', $row->getSourceProperty('nid'));
+    }
     return parent::prepareRow($row);
   }
 
@@ -101,4 +150,21 @@ public function getIds() {
     return $ids;
   }
 
+  /**
+   * Adapt our query for translations.
+   *
+   * @param \Drupal\Core\Database\Query\SelectInterface $query
+   *   The generated query.
+   */
+  protected function handleTranslations(SelectInterface $query) {
+    // Check whether or not we want translations.
+    if (empty($this->configuration['translations'])) {
+      // No translations: Yield untranslated nodes, or default translations.
+      $query->where('n.tnid = 0 OR n.tnid = n.nid');
+    }
+    else {
+      // Translations: Yield only non-default translations.
+      $query->where('n.tnid <> 0 AND n.tnid <> n.nid');
+    }
+  }
 }
diff --git a/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeDeriverTest.php b/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeDeriverTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..569582dd8653ce288a9c36249abb7b80fbec95a5
--- /dev/null
+++ b/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeDeriverTest.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace Drupal\Tests\node\Kernel\Migrate\d7;
+
+use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
+
+/**
+ * Test D7NodeDeriver.
+ *
+ * @group migrate_drupal_7
+ */
+class MigrateNodeDeriverTest extends MigrateDrupal7TestBase {
+
+  /**
+   * The migration plugin manager.
+   *
+   * @var \Drupal\migrate\Plugin\MigrationPluginManagerInterface
+   */
+  protected $pluginManager;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+    $this->pluginManager = $this->container->get('plugin.manager.migration');
+  }
+
+  /**
+   * Test node translation migrations with translation disabled.
+   */
+  public function testNoTranslations() {
+    // Without content_translation, there should be no translation migrations.
+    $migrations = $this->pluginManager->createInstances('d7_node_translation');
+    $this->assertSame([], $migrations,
+      "No node translation migrations without content_translation");
+  }
+
+  /**
+   * Test node translation migrations with translation enabled.
+   */
+  public function testTranslations() {
+    // With content_translation, there should be translation migrations for
+    // each content type.
+    $this->enableModules(['language', 'content_translation', 'node', 'filter']);
+    $migrations = $this->pluginManager->createInstances('d7_node_translation');
+    $this->assertArrayHasKey('d7_node_translation:article', $migrations,
+      "Node translation migrations exist after content_translation installed");
+  }
+
+}
diff --git a/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeTest.php b/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeTest.php
index f7b98bd7fc37f66b4671122cf84d98e0e8481e54..8a962e77e5684f8aba66d26b81293c3ad378152c 100644
--- a/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeTest.php
+++ b/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeTest.php
@@ -13,11 +13,16 @@
  */
 class MigrateNodeTest extends MigrateDrupal7TestBase {
 
+  /**
+   * {@inheritdoc}
+   */
   public static $modules = array(
+    'content_translation',
     'comment',
     'datetime',
     'filter',
     'image',
+    'language',
     'link',
     'node',
     'taxonomy',
@@ -40,15 +45,17 @@ protected function setUp() {
     $this->installSchema('system', ['sequences']);
 
     $this->executeMigrations([
+      'language',
       'd7_user_role',
       'd7_user',
       'd7_node_type',
+      'd7_language_content_settings',
       'd7_comment_type',
       'd7_taxonomy_vocabulary',
       'd7_field',
       'd7_field_instance',
       'd7_node',
-      'd7_node:article',
+      'd7_node_translation',
     ]);
   }
 
@@ -144,9 +151,31 @@ public function testNode() {
     $this->assertIdentical('Click Here', $node->field_link->title);
 
     $node = Node::load(2);
+    $this->assertSame('en', $node->langcode->value);
     $this->assertIdentical("...is that it's the absolute best show ever. Trust me, I would know.", $node->body->value);
+    $this->assertSame('The thing about Deep Space 9', $node->label());
     $this->assertIdentical('internal:/', $node->field_link->uri);
     $this->assertIdentical('Home', $node->field_link->title);
+    $this->assertTrue($node->hasTranslation('is'), "Node 2 has an Icelandic translation");
+
+    $translation = $node->getTranslation('is');
+    $this->assertSame('is', $translation->langcode->value);
+    $this->assertSame("is - ...is that it's the absolute best show ever. Trust me, I would know.", $translation->body->value);
+    $this->assertSame('is - The thing about Deep Space 9', $translation->label());
+    $this->assertSame('internal:/', $translation->field_link->uri);
+    $this->assertSame('Home', $translation->field_link->title);
+
+    // Test that content_translation_source is set.
+    $manager = $this->container->get('content_translation.manager');
+    $this->assertSame('en', $manager->getTranslationMetadata($node->getTranslation('is'))->getSource());
+
+    // Node 3 is a translation of node 2, and should not be imported separately.
+    $this->assertNull(Node::load(3), "Node 3 doesn't exist in D8, it was a translation");
+
+    // Test that content_translation_source for a source other than English.
+    $node = Node::load(4);
+    $this->assertSame('is', $manager->getTranslationMetadata($node->getTranslation('en'))->getSource());
+
   }
 
 }
diff --git a/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d7/NodeTest.php b/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d7/NodeTest.php
index 6b19b9580dfe908728dc12703c17a9c27a37beaf..da4f57efebadc58bae1c7696b184ae865c41af17 100644
--- a/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d7/NodeTest.php
+++ b/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d7/NodeTest.php
@@ -27,7 +27,6 @@ public function providerSource() {
     // The source data.
     $tests[0]['source_data']['node'] = [
       [
-        // Node fields.
         'nid' => 1,
         'vid' => 1,
         'type' => 'page',
@@ -44,7 +43,6 @@ public function providerSource() {
         'translate' => 0,
       ],
       [
-        // Node fields.
         'nid' => 2,
         'vid' => 2,
         'type' => 'page',
@@ -61,7 +59,6 @@ public function providerSource() {
         'translate' => 0,
       ],
       [
-        // Node fields.
         'nid' => 5,
         'vid' => 5,
         'type' => 'article',
@@ -77,11 +74,41 @@ public function providerSource() {
         'tnid' => 0,
         'translate' => 0,
       ],
-
+      [
+        'nid' => 6,
+        'vid' => 6,
+        'type' => 'article',
+        'language' => 'en',
+        'title' => 'node title 5',
+        'uid' => 1,
+        'status' => 1,
+        'created' => 1279291908,
+        'changed' => 1279309993,
+        'comment' => 0,
+        'promote' => 1,
+        'sticky' => 0,
+        'tnid' => 6,
+        'translate' => 0,
+      ],
+      [
+        'nid' => 7,
+        'vid' => 7,
+        'type' => 'article',
+        'language' => 'fr',
+        'title' => 'fr - node title 5',
+        'uid' => 1,
+        'status' => 1,
+        'created' => 1279292908,
+        'changed' => 1279310993,
+        'comment' => 0,
+        'promote' => 1,
+        'sticky' => 0,
+        'tnid' => 6,
+        'translate' => 0,
+      ],
     ];
     $tests[0]['source_data']['node_revision'] = [
       [
-        // Node fields.
         'nid' => 1,
         'vid' => 1,
         'uid' => 2,
@@ -94,7 +121,6 @@ public function providerSource() {
         'sticky' => 0,
       ],
       [
-        // Node fields.
         'nid' => 2,
         'vid' => 2,
         'uid' => 2,
@@ -107,7 +133,6 @@ public function providerSource() {
         'sticky' => 0,
       ],
       [
-        // Node fields.
         'nid' => 5,
         'vid' => 5,
         'uid' => 2,
@@ -119,6 +144,31 @@ public function providerSource() {
         'promote' => 1,
         'sticky' => 0,
       ],
+      [
+        'nid' => 6,
+        'vid' => 6,
+        'uid' => 1,
+        'title' => 'node title 5',
+        'log' => '',
+        'timestamp' => 1279309993,
+        'status' => 1,
+        'comment' => 0,
+        'promote' => 1,
+        'sticky' => 0,
+
+      ],
+      [
+        'nid' => 7,
+        'vid' => 7,
+        'uid' => 1,
+        'title' => 'fr - node title 5',
+        'log' => '',
+        'timestamp' => 1279310993,
+        'status' => 1,
+        'comment' => 0,
+        'promote' => 1,
+        'sticky' => 0,
+      ],
     ];
     $tests[0]['source_data']['field_config_instance'] = [
       [
@@ -131,7 +181,7 @@ public function providerSource() {
         'deleted' => '0',
       ],
       [
-        'id' => '2',
+        'id' => '3',
         'field_id' => '2',
         'field_name' => 'body',
         'entity_type' => 'node',
@@ -153,12 +203,59 @@ public function providerSource() {
         'body_summary' => '',
         'body_format' => 'filtered_html',
       ],
+      [
+        'entity_type' => 'node',
+        'bundle' => 'page',
+        'deleted' => '0',
+        'entity_id' => '2',
+        'revision_id' => '2',
+        'language' => 'en',
+        'delta' => '0',
+        'body_value' => 'body 2',
+        'body_summary' => '',
+        'body_format' => 'filtered_html',
+      ],
+      [
+        'entity_type' => 'node',
+        'bundle' => 'page',
+        'deleted' => '0',
+        'entity_id' => '5',
+        'revision_id' => '5',
+        'language' => 'en',
+        'delta' => '0',
+        'body_value' => 'body 5',
+        'body_summary' => '',
+        'body_format' => 'filtered_html',
+      ],
+      [
+        'entity_type' => 'node',
+        'bundle' => 'page',
+        'deleted' => '0',
+        'entity_id' => '6',
+        'revision_id' => '6',
+        'language' => 'en',
+        'delta' => '0',
+        'body_value' => 'body 6',
+        'body_summary' => '',
+        'body_format' => 'filtered_html',
+      ],
+      [
+        'entity_type' => 'node',
+        'bundle' => 'page',
+        'deleted' => '0',
+        'entity_id' => '7',
+        'revision_id' => '7',
+        'language' => 'fr',
+        'delta' => '0',
+        'body_value' => 'fr - body 6',
+        'body_summary' => '',
+        'body_format' => 'filtered_html',
+      ],
     ];
 
     // The expected results.
     $tests[0]['expected_data'] = [
       [
-        // Node fields.
         'nid' => 1,
         'vid' => 1,
         'type' => 'page',
@@ -172,7 +269,7 @@ public function providerSource() {
         'comment' => 2,
         'promote' => 1,
         'sticky' => 0,
-        'tnid' => 0,
+        'tnid' => 1,
         'translate' => 0,
         'log' => '',
         'timestamp' => 1279051598,
@@ -185,7 +282,6 @@ public function providerSource() {
         ],
       ],
       [
-        // Node fields.
         'nid' => 2,
         'vid' => 2,
         'type' => 'page',
@@ -199,13 +295,19 @@ public function providerSource() {
         'comment' => 0,
         'promote' => 1,
         'sticky' => 0,
-        'tnid' => 0,
+        'tnid' => 2,
         'translate' => 0,
         'log' => '',
         'timestamp' => 1279308993,
+        'body' => [
+          [
+            'value' => 'body 2',
+            'summary' => '',
+            'format' => 'filtered_html',
+          ],
+        ],
       ],
       [
-        // Node fields.
         'nid' => 5,
         'vid' => 5,
         'type' => 'article',
@@ -219,10 +321,43 @@ public function providerSource() {
         'comment' => 0,
         'promote' => 1,
         'sticky' => 0,
-        'tnid' => 0,
+        'tnid' => 5,
         'translate' => 0,
         'log' => '',
         'timestamp' => 1279308993,
+        'body' => [
+          [
+            'value' => 'body 5',
+            'summary' => '',
+            'format' => 'filtered_html',
+          ],
+        ],
+      ],
+      [
+        'nid' => 6,
+        'vid' => 6,
+        'type' => 'article',
+        'language' => 'en',
+        'title' => 'node title 5',
+        'node_uid' => 1,
+        'revision_uid' => 1,
+        'status' => 1,
+        'created' => 1279291908,
+        'changed' => 1279309993,
+        'comment' => 0,
+        'promote' => 1,
+        'sticky' => 0,
+        'tnid' => 6,
+        'translate' => 0,
+        'log' => '',
+        'timestamp' => 1279309993,
+        'body' => [
+          [
+            'value' => 'body 6',
+            'summary' => '',
+            'format' => 'filtered_html',
+          ],
+        ],
       ],
     ];
 
diff --git a/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d7/NodeTranslationTest.php b/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d7/NodeTranslationTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..e46bd35a334cc00cd0b1d365b698a1fadb2b1ea4
--- /dev/null
+++ b/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d7/NodeTranslationTest.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace Drupal\Tests\node\Kernel\Plugin\migrate\source\d7;
+
+/**
+ * Tests D7 node translation source plugin.
+ *
+ * @covers \Drupal\node\Plugin\migrate\source\d7\Node
+ *
+ * @group node
+ */
+class NodeTranslationTest extends NodeTest {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['node', 'user', 'migrate_drupal'];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function providerSource() {
+    // Get the source data from parent.
+    $tests = parent::providerSource();
+
+    // The expected results.
+    $tests[0]['expected_data'] = [
+      [
+        // Node fields.
+        'nid' => 7,
+        'vid' => 7,
+        'type' => 'article',
+        'language' => 'fr',
+        'title' => 'fr - node title 5',
+        'node_uid' => 1,
+        'revision_uid' => 1,
+        'status' => 1,
+        'created' => 1279292908,
+        'changed' => 1279310993,
+        'comment' => 0,
+        'promote' => 1,
+        'sticky' => 0,
+        'tnid' => 6,
+        'translate' => 0,
+        // Node revision fields.
+        'log' => '',
+        'timestamp' => 1279310993,
+        'body' => [
+          [
+            'value' => 'fr - body 6',
+            'summary' => '',
+            'format' => 'filtered_html',
+          ],
+        ],
+      ],
+    ];
+
+    // Do an automatic count.
+    $tests[0]['expected_count'] = NULL;
+
+    // Set up source plugin configuration.
+    $tests[0]['configuration'] = [
+      'translations' => TRUE,
+    ];
+
+    return $tests;
+  }
+
+}