diff --git a/core/modules/migrate_drupal/tests/fixtures/drupal7.php b/core/modules/migrate_drupal/tests/fixtures/drupal7.php
index e8c006b..aacbc2b 100644
--- a/core/modules/migrate_drupal/tests/fixtures/drupal7.php
+++ b/core/modules/migrate_drupal/tests/fixtures/drupal7.php
@@ -41044,11 +41044,29 @@
   'login' => '0',
   'status' => '1',
   'timezone' => 'America/Chicago',
-  'language' => 'en',
+  'language' => 'is',
   'picture' => '0',
   'init' => 'odo@local.host',
   'data' => 'a:1:{s:7:"contact";i:1;}',
 ))
+->values(array(
+  'uid' => '3',
+  'name' => 'Bob',
+  'pass' => '$S$DGFZUE.FhrXbe4y52eC7p0ZVRGD/gOPtVctDlmC89qkujnBokAlJ',
+  'mail' => 'bob@local.host',
+  'theme' => '',
+  'signature' => '',
+  'signature_format' => 'filtered_html',
+  'created' => '1440532218',
+  'access' => '0',
+  'login' => '0',
+  'status' => '1',
+  'timezone' => 'America/New_York',
+  'language' => 'fr',
+  'picture' => '0',
+  'init' => 'bob@local.host',
+  'data' => 'a:1:{s:7:"contact";i:1;}',
+))
 ->execute();
 
 $connection->schema()->createTable('users_roles', array(
@@ -41084,6 +41102,14 @@
   'uid' => '1',
   'rid' => '3',
 ))
+->values(array(
+  'uid' => '2',
+  'rid' => '3',
+))
+->values(array(
+  'uid' => '3',
+  'rid' => '3',
+))
 ->execute();
 
 $connection->schema()->createTable('variable', array(
diff --git a/core/modules/migrate_drupal_ui/src/Tests/d7/MigrateUpgrade7Test.php b/core/modules/migrate_drupal_ui/src/Tests/d7/MigrateUpgrade7Test.php
index bac70e5..eef7b90 100644
--- a/core/modules/migrate_drupal_ui/src/Tests/d7/MigrateUpgrade7Test.php
+++ b/core/modules/migrate_drupal_ui/src/Tests/d7/MigrateUpgrade7Test.php
@@ -61,7 +61,7 @@ protected function getEntityCounts() {
       'taxonomy_term' => 18,
       'taxonomy_vocabulary' => 3,
       'tour' => 4,
-      'user' => 3,
+      'user' => 4,
       'user_role' => 4,
       'menu_link_content' => 9,
       'view' => 12,
diff --git a/core/modules/user/migration_templates/d6_user.yml b/core/modules/user/migration_templates/d6_user.yml
index bf6ec2a..fa6326e 100644
--- a/core/modules/user/migration_templates/d6_user.yml
+++ b/core/modules/user/migration_templates/d6_user.yml
@@ -16,7 +16,18 @@ process:
   timezone:
     plugin: user_update_7002
     source: timezone
-  preferred_langcode: language
+  langcode:
+    plugin: user_langcode
+    source: language
+    fallback_to_site_default: false
+  preferred_langcode:
+    plugin: user_langcode
+    source: language
+    fallback_to_site_default: true
+  preferred_admin_langcode:
+    plugin: user_langcode
+    source: language
+    fallback_to_site_default: true
   init: init
   roles:
     plugin: migration
@@ -34,6 +45,8 @@ migration_dependencies:
   required:
     - d6_user_role
   optional:
+    - language
+    - default_language
     - d6_user_picture_file
     - user_picture_entity_display
     - user_picture_entity_form_display
diff --git a/core/modules/user/migration_templates/d7_user.yml b/core/modules/user/migration_templates/d7_user.yml
index d68fddf..b72e8c7 100644
--- a/core/modules/user/migration_templates/d7_user.yml
+++ b/core/modules/user/migration_templates/d7_user.yml
@@ -15,9 +15,18 @@ process:
   login: login
   status: status
   timezone: timezone
-  langcode: language
-  preferred_langcode: language
-  preferred_admin_langcode: language
+  langcode:
+    plugin: user_langcode
+    source: language
+    fallback_to_site_default: false
+  preferred_langcode:
+    plugin: user_langcode
+    source: language
+    fallback_to_site_default: true
+  preferred_admin_langcode:
+    plugin: user_langcode
+    source: language
+    fallback_to_site_default: true
   init: init
   roles:
     plugin: migration
@@ -38,6 +47,8 @@ migration_dependencies:
     - d7_user_role
   optional:
     - d7_file
+    - language
+    - default_language
     - user_picture_field_instance
     - user_picture_entity_display
     - user_picture_entity_form_display
diff --git a/core/modules/user/src/Plugin/migrate/process/UserLangcode.php b/core/modules/user/src/Plugin/migrate/process/UserLangcode.php
new file mode 100644
index 0000000..80f2078
--- /dev/null
+++ b/core/modules/user/src/Plugin/migrate/process/UserLangcode.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace Drupal\user\Plugin\migrate\process;
+
+use Drupal\Core\Language\LanguageManager;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\migrate\MigrateExecutableInterface;
+use Drupal\migrate\ProcessPluginBase;
+use Drupal\migrate\Row;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides a process plugin for the user langcode.
+ *
+ * @MigrateProcessPlugin(
+ *   id = "user_langcode"
+ * )
+ */
+class UserLangcode extends ProcessPluginBase implements ContainerFactoryPluginInterface {
+
+  /**
+   * The language manager.
+   *
+   * @var \Drupal\Core\Language\LanguageManager
+   */
+  protected $languageManager;
+
+  /**
+   * Constructs a UserLangcode object.
+   *
+   * @param array $configuration
+   *   Plugin configuration.
+   * @param string $plugin_id
+   *   The plugin ID.
+   * @param mixed $plugin_definition
+   *   The plugin definiiton.
+   * @param \Drupal\Core\Language\LanguageManager $language_manager
+   *   The language manager service.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, LanguageManager $language_manager) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->languageManager = $language_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('language_manager')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
+    if (!isset($this->configuration['fallback_to_site_default'])) {
+      $this->configuration['fallback_to_site_default'] = TRUE;
+    }
+
+    // If the user's language is empty, it means the locale module was not
+    // installed, so the user's langcode should be English and the user's
+    // preferred_langcode and preferred_admin_langcode should fallback to the
+    // default language.
+    if (empty($value)) {
+      if ($this->configuration['fallback_to_site_default']) {
+        return $this->languageManager->getDefaultLanguage()->getId();
+      }
+      else {
+        return 'en';
+      }
+    }
+    // If the user's language does not exists, use the default language.
+    elseif ($this->languageManager->getLanguage($value) === NULL) {
+      return $this->languageManager->getDefaultLanguage()->getId();
+    }
+
+    // If the langcode is a valid one, just return it.
+    return $value;
+  }
+
+}
diff --git a/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserProfileValuesTest.php b/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserProfileValuesTest.php
index 978c0bf..e734517 100644
--- a/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserProfileValuesTest.php
+++ b/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserProfileValuesTest.php
@@ -15,10 +15,16 @@ class MigrateUserProfileValuesTest extends MigrateDrupal6TestBase {
   /**
    * {@inheritdoc}
    */
+  public static $modules = ['language'];
+
+  /**
+   * {@inheritdoc}
+   */
   protected function setUp() {
     parent::setUp();
 
     $this->executeMigrations([
+      'language',
       'user_profile_field',
       'user_profile_field_instance',
       'user_profile_entity_display',
diff --git a/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserTest.php b/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserTest.php
index 9317834..33952d1 100644
--- a/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserTest.php
+++ b/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserTest.php
@@ -22,6 +22,11 @@ class MigrateUserTest extends MigrateDrupal6TestBase {
   /**
    * {@inheritdoc}
    */
+  public static $modules = ['language'];
+
+  /**
+   * {@inheritdoc}
+   */
   protected function setUp() {
     parent::setUp();
 
@@ -60,6 +65,7 @@ protected function setUp() {
     file_put_contents($file->getFileUri(), file_get_contents('core/modules/simpletest/files/image-2.jpg'));
     $file->save();
 
+    $this->executeMigration('language');
     $this->migrateUsers();
   }
 
@@ -91,28 +97,47 @@ public function testUser() {
 
       /** @var \Drupal\user\UserInterface $user */
       $user = User::load($source->uid);
-      $this->assertIdentical($source->uid, $user->id());
-      $this->assertIdentical($source->name, $user->label());
-      $this->assertIdentical($source->mail, $user->getEmail());
-      $this->assertIdentical($source->created, $user->getCreatedTime());
-      $this->assertIdentical($source->access, $user->getLastAccessedTime());
-      $this->assertIdentical($source->login, $user->getLastLoginTime());
+      $this->assertSame($source->uid, $user->id());
+      $this->assertSame($source->name, $user->label());
+      $this->assertSame($source->mail, $user->getEmail());
+      $this->assertSame($source->created, $user->getCreatedTime());
+      $this->assertSame($source->access, $user->getLastAccessedTime());
+      $this->assertSame($source->login, $user->getLastLoginTime());
       $is_blocked = $source->status == 0;
-      $this->assertIdentical($is_blocked, $user->isBlocked());
+      $this->assertSame($is_blocked, $user->isBlocked());
+      $expected_timezone_name = $source->timezone_name ?: $this->config('system.date')->get('timezone.default');
+      $this->assertSame($expected_timezone_name, $user->getTimeZone());
+      $this->assertSame($source->init, $user->getInitialEmail());
+      $this->assertSame($roles, $user->getRoles());
+
+      // Ensure the user's langcode, preferred_langcode and
+      // preferred_admin_langcode are valid.
       // $user->getPreferredLangcode() might fallback to default language if the
       // user preferred language is not configured on the site. We just want to
       // test if the value was imported correctly.
-      $this->assertIdentical($source->language, $user->preferred_langcode->value);
-      $expected_timezone_name = $source->timezone_name ?: $this->config('system.date')->get('timezone.default');
-      $this->assertIdentical($expected_timezone_name, $user->getTimeZone());
-      $this->assertIdentical($source->init, $user->getInitialEmail());
-      $this->assertIdentical($roles, $user->getRoles());
+      $language_manager = $this->container->get('language_manager');
+      $default_langcode = $language_manager->getDefaultLanguage()->getId();
+      if (empty($source->language)) {
+        $this->assertSame('en', $user->langcode->value);
+        $this->assertSame($default_langcode, $user->preferred_langcode->value);
+        $this->assertSame($default_langcode, $user->preferred_admin_langcode->value);
+      }
+      elseif ($language_manager->getLanguage($source->language) === NULL) {
+        $this->assertSame($default_langcode, $user->langcode->value);
+        $this->assertSame($default_langcode, $user->preferred_langcode->value);
+        $this->assertSame($default_langcode, $user->preferred_admin_langcode->value);
+      }
+      else {
+        $this->assertSame($source->language, $user->langcode->value);
+        $this->assertSame($source->language, $user->preferred_langcode->value);
+        $this->assertSame($source->language, $user->preferred_admin_langcode->value);
+      }
 
       // We have one empty picture in the data so don't try load that.
       if (!empty($source->picture)) {
         // Test the user picture.
         $file = File::load($user->user_picture->target_id);
-        $this->assertIdentical(basename($source->picture), $file->getFilename());
+        $this->assertSame(basename($source->picture), $file->getFilename());
       }
       else {
         // Ensure the user does not have a picture.
diff --git a/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserTest.php b/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserTest.php
index 33bc5fe..c21856e 100644
--- a/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserTest.php
+++ b/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserTest.php
@@ -3,6 +3,7 @@
 namespace Drupal\Tests\user\Kernel\Migrate\d7;
 
 use Drupal\comment\Entity\CommentType;
+use Drupal\Core\Database\Database;
 use Drupal\node\Entity\NodeType;
 use Drupal\taxonomy\Entity\Vocabulary;
 use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
@@ -25,6 +26,7 @@ class MigrateUserTest extends MigrateDrupal7TestBase {
     'datetime',
     'file',
     'image',
+    'language',
     'link',
     'node',
     'system',
@@ -49,6 +51,7 @@ protected function setUp() {
     $this->createType('test_content_type');
     Vocabulary::create(['vid' => 'test_vocabulary'])->save();
     $this->executeMigrations([
+      'language',
       'user_picture_field',
       'user_picture_field_instance',
       'd7_user_role',
@@ -88,6 +91,8 @@ protected function createType($id) {
    *   The user's email address.
    * @param string $password
    *   The password for this user.
+   * @param int $created
+   *   The user's creation time.
    * @param int $access
    *   The last access time.
    * @param int $login
@@ -96,37 +101,59 @@ protected function createType($id) {
    *   Whether or not the account is blocked.
    * @param string $langcode
    *   The user account's language code.
+   * @param string $timezone
+   *   The user account's timezone name.
    * @param string $init
    *   The user's initial email address.
    * @param string[] $roles
    *   Role IDs the user account is expected to have.
-   * @param bool $has_picture
-   *   Whether the user is expected to have a picture attached.
    * @param int $field_integer
    *   The value of the integer field.
+   * @param bool $has_picture
+   *   Whether the user is expected to have a picture attached.
    */
-  protected function assertEntity($id, $label, $mail, $password, $access, $login, $blocked, $langcode, $init, array $roles = [RoleInterface::AUTHENTICATED_ID], $has_picture = FALSE, $field_integer = NULL) {
+  protected function assertEntity($id, $label, $mail, $password, $created, $access, $login, $blocked, $langcode, $timezone, $init, $roles, $field_integer, $has_picture = FALSE) {
     /** @var \Drupal\user\UserInterface $user */
     $user = User::load($id);
     $this->assertTrue($user instanceof UserInterface);
-    $this->assertIdentical($label, $user->label());
-    $this->assertIdentical($mail, $user->getEmail());
-    $this->assertIdentical($access, $user->getLastAccessedTime());
-    $this->assertIdentical($login, $user->getLastLoginTime());
-    $this->assertIdentical($blocked, $user->isBlocked());
+    $this->assertSame($label, $user->label());
+    $this->assertSame($mail, $user->getEmail());
+    $this->assertSame($password, $user->getPassword());
+    $this->assertSame($created, $user->getCreatedTime());
+    $this->assertSame($access, $user->getLastAccessedTime());
+    $this->assertSame($login, $user->getLastLoginTime());
+    $this->assertNotSame($blocked, $user->isBlocked());
+
+    // Ensure the user's langcode, preferred_langcode and
+    // preferred_admin_langcode are valid.
     // $user->getPreferredLangcode() might fallback to default language if the
     // user preferred language is not configured on the site. We just want to
     // test if the value was imported correctly.
-    $this->assertIdentical($langcode, $user->langcode->value);
-    $this->assertIdentical($langcode, $user->preferred_langcode->value);
-    $this->assertIdentical($langcode, $user->preferred_admin_langcode->value);
-    $this->assertIdentical($init, $user->getInitialEmail());
-    $this->assertIdentical($roles, $user->getRoles());
-    $this->assertIdentical($has_picture, !$user->user_picture->isEmpty());
-    $this->assertIdentical($password, $user->getPassword());
+    $language_manager = $this->container->get('language_manager');
+    $default_langcode = $language_manager->getDefaultLanguage()->getId();
+    if ($langcode == '') {
+      $this->assertSame('en', $user->langcode->value);
+      $this->assertSame($default_langcode, $user->preferred_langcode->value);
+      $this->assertSame($default_langcode, $user->preferred_admin_langcode->value);
+    }
+    elseif ($language_manager->getLanguage($langcode) === NULL) {
+      $this->assertSame($default_langcode, $user->langcode->value);
+      $this->assertSame($default_langcode, $user->preferred_langcode->value);
+      $this->assertSame($default_langcode, $user->preferred_admin_langcode->value);
+    }
+    else {
+      $this->assertSame($langcode, $user->langcode->value);
+      $this->assertSame($langcode, $user->preferred_langcode->value);
+      $this->assertSame($langcode, $user->preferred_admin_langcode->value);
+    }
+
+    $this->assertSame($timezone, $user->getTimeZone());
+    $this->assertSame($init, $user->getInitialEmail());
+    $this->assertSame($roles, $user->getRoles());
+    $this->assertSame($has_picture, !$user->user_picture->isEmpty());
     if (!is_null($field_integer)) {
       $this->assertTrue($user->hasField('field_integer'));
-      $this->assertEquals($field_integer, $user->field_integer->value);
+      $this->assertEquals($field_integer[0], $user->field_integer->value);
     }
   }
 
@@ -134,22 +161,65 @@ protected function assertEntity($id, $label, $mail, $password, $access, $login,
    * Tests the Drupal 7 user to Drupal 8 migration.
    */
   public function testUser() {
-    $password = '$S$DGFZUE.FhrXbe4y52eC7p0ZVRGD/gOPtVctDlmC89qkujnBokAlJ';
-    $this->assertEntity(2, 'Odo', 'odo@local.host', $password, '0', '0', FALSE, 'en', 'odo@local.host', [RoleInterface::AUTHENTICATED_ID], FALSE, 99);
-
-    // Ensure that the user can authenticate.
-    $this->assertEquals(2, \Drupal::service('user.auth')->authenticate('Odo', 'a password'));
-    // After authenticating the password will be rehashed because the password
-    // stretching iteration count has changed from 15 in Drupal 7 to 16 in
-    // Drupal 8.
-    $user = User::load(2);
-    $rehash = $user->getPassword();
-    $this->assertNotEquals($password, $rehash);
-
-    // Authenticate again and there should be no re-hash.
-    $this->assertEquals(2, \Drupal::service('user.auth')->authenticate('Odo', 'a password'));
-    $user = User::load(2);
-    $this->assertEquals($rehash, $user->getPassword());
+    $users = Database::getConnection('default', 'migrate')
+      ->select('users', 'u')
+      ->fields('u')
+      ->condition('uid', 1, '>')
+      ->execute()
+      ->fetchAll();
+
+    foreach ($users as $source) {
+      $rids = Database::getConnection('default', 'migrate')
+        ->select('users_roles', 'ur')
+        ->fields('ur', array('rid'))
+        ->condition('ur.uid', $source->uid)
+        ->execute()
+        ->fetchCol();
+      $roles = array(RoleInterface::AUTHENTICATED_ID);
+      $id_map = $this->getMigration('d7_user_role')->getIdMap();
+      foreach ($rids as $rid) {
+        $role = $id_map->lookupDestinationId(array($rid));
+        $roles[] = reset($role);
+      }
+
+      $field_integer = Database::getConnection('default', 'migrate')
+        ->select('field_data_field_integer', 'fi')
+        ->fields('fi', array('field_integer_value'))
+        ->condition('fi.entity_id', $source->uid)
+        ->execute()
+        ->fetchCol();
+      $field_integer = !empty($field_integer) ? $field_integer : NULL;
+
+      $this->assertEntity(
+        $source->uid,
+        $source->name,
+        $source->mail,
+        $source->pass,
+        $source->created,
+        $source->access,
+        $source->login,
+        $source->status,
+        $source->language,
+        $source->timezone,
+        $source->init,
+        $roles,
+        $field_integer
+      );
+
+      // Ensure that the user can authenticate.
+      $this->assertEquals($source->uid, $this->container->get('user.auth')->authenticate($source->name, 'a password'));
+      // After authenticating the password will be rehashed because the password
+      // stretching iteration count has changed from 15 in Drupal 7 to 16 in
+      // Drupal 8.
+      $user = User::load($source->uid);
+      $rehash = $user->getPassword();
+      $this->assertNotEquals($source->pass, $rehash);
+
+      // Authenticate again and there should be no re-hash.
+      $this->assertEquals($source->uid, $this->container->get('user.auth')->authenticate($source->name, 'a password'));
+      $user = User::load($source->uid);
+      $this->assertEquals($rehash, $user->getPassword());
+    }
   }
 
 }
