diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/LanguageItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/LanguageItem.php
index d0791e9..aa9b0d5 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/LanguageItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/LanguageItem.php
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Contains \Drupal\Core\Entity\Plugin\Field\FieldType\LanguageItem.
+ * Contains \Drupal\Core\Field\Plugin\Field\FieldType\LanguageItem.
  */
 
 namespace Drupal\Core\Field\Plugin\Field\FieldType;
@@ -25,10 +25,16 @@
  *   no_ui = TRUE,
  *   constraints = {
  *     "ComplexData" = {
- *       "value" = {"Length" = {"max" = 12}}
+ *       "value" = {
+ *         "Length" = {"max" = 12},
+ *         "AllowedValues" = {"callback" = "\Drupal\Core\Field\Plugin\Field\FieldType\LanguageItem::getAllowedLanguageCodes" }
+ *       }
  *     }
  *   }
  * )
+ *
+ * @todo Define the AllowedValues constraint via an options provider once
+ *   https://www.drupal.org/node/2329937 is completed.
  */
 class LanguageItem extends FieldItemBase {
 
@@ -51,6 +57,15 @@ public static function propertyDefinitions(FieldStorageDefinitionInterface $fiel
   }
 
   /**
+   * Defines allowed language codes for the field's AllowedValues constraint.
+   *
+   * @return string[]
+   */
+  public static function getAllowedLanguageCodes() {
+    return array_keys(\Drupal::languageManager()->getLanguages(LanguageInterface::STATE_ALL));
+  }
+
+  /**
    * {@inheritdoc}
    */
   public static function schema(FieldStorageDefinitionInterface $field_definition) {
diff --git a/core/modules/system/src/Tests/Entity/EntityValidationTest.php b/core/modules/system/src/Tests/Entity/EntityValidationTest.php
index 719b783..ef42036 100644
--- a/core/modules/system/src/Tests/Entity/EntityValidationTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityValidationTest.php
@@ -113,7 +113,8 @@ protected function checkValidation($entity_type) {
     $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode');
     $test_entity->{$langcode_key}->value = $this->randomString(13);
     $violations = $test_entity->validate();
-    $this->assertEqual($violations->count(), 1, 'Validation failed.');
+    // This should fail on AllowedValues and Length constraints.
+    $this->assertEqual($violations->count(), 2, 'Validation failed.');
     $this->assertEqual($violations[0]->getMessage(), t('This value is too long. It should have %limit characters or less.', array('%limit' => '12')));
 
     $test_entity = clone $entity;
diff --git a/core/modules/user/src/AccountForm.php b/core/modules/user/src/AccountForm.php
index 7ed3cbd..fc168df 100644
--- a/core/modules/user/src/AccountForm.php
+++ b/core/modules/user/src/AccountForm.php
@@ -359,7 +359,21 @@ public function buildEntity(array $form, FormStateInterface $form_state) {
     if (is_string(key($form_state->getValue('roles')))) {
       $form_state->setValue('roles', array_keys(array_filter($form_state->getValue('roles'))));
     }
-    return parent::buildEntity($form, $form_state);
+
+    /** @var \Drupal\user\UserInterface $account */
+    $account = parent::buildEntity($form, $form_state);
+
+    $signature = $form_state->getValue('signature');
+    $account->setSignature($signature['value']);
+    $account->setSignatureFormat($signature['format']);
+
+    // Translate the empty value '' of language selects to an unset field.
+    foreach (array('preferred_langcode', 'preferred_admin_langcode') as $field_name) {
+      if ($form_state->getValue($field_name) === '') {
+        $account->$field_name = NULL;
+      }
+    }
+    return $account;
   }
 
   /**
@@ -368,63 +382,24 @@ public function buildEntity(array $form, FormStateInterface $form_state) {
   public function validate(array $form, FormStateInterface $form_state) {
     parent::validate($form, $form_state);
 
-    $account = $this->entity;
-    // Validate new or changing username.
-    if ($form_state->hasValue('name')) {
-      if ($error = user_validate_name($form_state->getValue('name'))) {
-        $form_state->setErrorByName('name', $error);
-      }
-      // Cast the user ID as an integer. It might have been set to NULL, which
-      // could lead to unexpected results.
-      else {
-        $name_taken = (bool) $this->entityQuery->get('user')
-          ->condition('uid', (int) $account->id(), '<>')
-          ->condition('name', $form_state->getValue('name'))
-          ->range(0, 1)
-          ->count()
-          ->execute();
-
-        if ($name_taken) {
-          $form_state->setErrorByName('name', $this->t('The username %name is already taken.', array('%name' => $form_state->getValue('name'))));
-        }
-      }
-    }
-
-    $mail = $form_state->getValue('mail');
-
-    if (!empty($mail)) {
-      $mail_taken = (bool) $this->entityQuery->get('user')
-        ->condition('uid', (int) $account->id(), '<>')
-        ->condition('mail', $mail)
-        ->range(0, 1)
-        ->count()
-        ->execute();
-
-      if ($mail_taken) {
-        // Format error message dependent on whether the user is logged in or not.
-        if (\Drupal::currentUser()->isAuthenticated()) {
-          $form_state->setErrorByName('mail', $this->t('The email address %email is already taken.', array('%email' => $mail)));
-        }
-        else {
-          $form_state->setErrorByName('mail', $this->t('The email address %email is already registered. <a href="@password">Have you forgotten your password?</a>', array('%email' => $mail, '@password' => $this->url('user.pass'))));
-        }
-      }
-    }
-
-    // Make sure the signature isn't longer than the size of the database field.
-    // Signatures are disabled by default, so make sure it exists first.
-    if ($signature = $form_state->getValue('signature')) {
-      // Move text format for user signature into 'signature_format'.
-      $form_state->setValue('signature_format', $signature['format']);
-      // Move text value for user signature into 'signature'.
-      $form_state->setValue('signature', $signature['value']);
-
-      // @todo Make the user signature field use a widget to benefit from
-      //   automatic typed data validation in https://drupal.org/node/2227381.
-      $field_definitions = $this->entityManager->getFieldDefinitions('user', $this->getEntity()->bundle());
-      $max_length = $field_definitions['signature']->getSetting('max_length');
-      if (Unicode::strlen($form_state->getValue('signature')) > $max_length) {
-        $form_state->setErrorByName('signature', $this->t('The signature is too long: it must be %max characters or less.', array('%max' => $max_length)));
+    /** @var \Drupal\user\UserInterface $account */
+    $account = $this->buildEntity($form, $form_state);
+    // Customly trigger validation of manually added fields and add in
+    // violations.
+    $field_names = array(
+      'name',
+      'mail',
+      'signature',
+      'signature_format',
+      'timezone',
+      'langcode',
+      'preferred_langcode',
+      'preferred_admin_langcode'
+    );
+    foreach ($field_names as $field_name) {
+      $violations = $account->$field_name->validate();
+      foreach ($violations as $violation) {
+        $form_state->setErrorByName($field_name, $violation->getMessage());
       }
     }
   }
diff --git a/core/modules/user/src/Entity/User.php b/core/modules/user/src/Entity/User.php
index 22303e5..df0f353 100644
--- a/core/modules/user/src/Entity/User.php
+++ b/core/modules/user/src/Entity/User.php
@@ -12,6 +12,7 @@
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\Core\Language\LanguageInterface;
 use Drupal\user\UserInterface;
 
 /**
@@ -291,6 +292,14 @@ public function getSignature() {
   /**
    * {@inheritdoc}
    */
+  public function setSignature($signature) {
+    $this->get('signature')->value = $signature;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function getSignatureFormat() {
     return $this->get('signature_format')->value;
   }
@@ -298,6 +307,14 @@ public function getSignatureFormat() {
   /**
    * {@inheritdoc}
    */
+  public function setSignatureFormat($signature_format) {
+    $this->get('signature_format')->value = $signature_format;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function getCreatedTime() {
     return $this->get('created')->value;
   }
@@ -462,12 +479,25 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
 
     $fields['preferred_langcode'] = BaseFieldDefinition::create('language')
       ->setLabel(t('Preferred language code'))
-      ->setDescription(t("The user's preferred language code for receiving emails and viewing the site."));
+      ->setDescription(t("The user's preferred language code for receiving emails and viewing the site."))
+      // @todo: Define this via an options provider once
+      // https://www.drupal.org/node/2329937 is completed.
+      ->addPropertyConstraints('value', array(
+        'AllowedValues' => array('callback' => __CLASS__ . '::getAllowedConfigurableLanguageCodes'),
+      ));
 
     $fields['preferred_admin_langcode'] = BaseFieldDefinition::create('language')
       ->setLabel(t('Preferred admin language code'))
       ->setDescription(t("The user's preferred language code for viewing administration pages."))
-      ->setDefaultValue('');
+      // @todo: A default value of NULL is ignored, so we have to specify
+      // an empty field item structure instead. Fix this in
+      // https://www.drupal.org/node/2318605.
+      ->setDefaultValue(array(0 => array ('value' => NULL)))
+      // @todo: Define this via an options provider once
+      // https://www.drupal.org/node/2329937 is completed.
+      ->addPropertyConstraints('value', array(
+        'AllowedValues' => array('callback' => __CLASS__ . '::getAllowedConfigurableLanguageCodes'),
+      ));
 
     // The name should not vary per language. The username is the visual
     // identifier for a user and needs to be consistent in all languages.
@@ -499,12 +529,22 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ->setTranslatable(TRUE);
     $fields['signature_format'] = BaseFieldDefinition::create('string')
       ->setLabel(t('Signature format'))
-      ->setDescription(t('The signature format of this user.'));
+      ->setDescription(t('The signature format of this user.'))
+      // @todo: Define this via an options provider once
+      // https://www.drupal.org/node/2329937 is completed.
+      ->addPropertyConstraints('value', array(
+        'AllowedValues' => array('callback' => __CLASS__ . '::getAllowedSignatureFormats'),
+      ));
 
     $fields['timezone'] = BaseFieldDefinition::create('string')
       ->setLabel(t('Timezone'))
       ->setDescription(t('The timezone of this user.'))
-      ->setSetting('max_length', 32);
+      ->setSetting('max_length', 32)
+      // @todo: Define this via an options provider once
+      // https://www.drupal.org/node/2329937 is completed.
+      ->addPropertyConstraints('value', array(
+        'AllowedValues' => array('callback' => __CLASS__ . '::getAllowedTimezones'),
+      ));
 
     $fields['status'] = BaseFieldDefinition::create('boolean')
       ->setLabel(t('User status'))
@@ -553,4 +593,35 @@ protected function getRoleStorage() {
     return \Drupal::entityManager()->getStorage('user_role');
   }
 
+  /**
+   * Defines allowed signature formats for the field's AllowedValues constraint.
+   *
+   * @return string[]
+   */
+  public static function getAllowedSignatureFormats() {
+    if (\Drupal::moduleHandler()->moduleExists('filter')) {
+      return array_keys(filter_formats());
+    }
+    // If filter.module is disabled, no value may be assigned.
+    return array();
+  }
+
+  /**
+   * Defines allowed timezones for the field's AllowedValues constraint.
+   *
+   * @return string[]
+   */
+  public static function getAllowedTimezones() {
+    return array_keys(system_time_zones());
+  }
+
+  /**
+   * Defines allowed configurable language codes for AllowedValues constraints.
+   *
+   * @return string[]
+   */
+  public static function getAllowedConfigurableLanguageCodes() {
+    return array_keys(\Drupal::languageManager()->getLanguages(LanguageInterface::STATE_CONFIGURABLE));
+  }
+
 }
diff --git a/core/modules/user/src/Tests/UserEditTest.php b/core/modules/user/src/Tests/UserEditTest.php
index 308fad9..0ee9599 100644
--- a/core/modules/user/src/Tests/UserEditTest.php
+++ b/core/modules/user/src/Tests/UserEditTest.php
@@ -16,12 +16,43 @@
  */
 class UserEditTest extends WebTestBase {
 
+  public static $modules = array('filter');
+
+  protected function setUp() {
+    parent::setUp();
+
+    $this->config('user.settings')->set('signatures', TRUE)->save();
+
+    // Prefetch and create text formats.
+    $this->filtered_html_format = entity_create('filter_format', array(
+      'format' => 'filtered_html_format',
+      'name' => 'Filtered HTML',
+      'weight' => -1,
+      'filters' => array(
+        'filter_html' => array(
+          'module' => 'filter',
+          'status' => TRUE,
+          'settings' => array(
+            'allowed_html' => '<a> <em> <strong>',
+          ),
+        ),
+      ),
+    ));
+    $this->filtered_html_format->save();
+
+    $this->full_html_format = entity_create('filter_format', array(
+      'format' => 'full_html',
+      'name' => 'Full HTML',
+    ));
+    $this->full_html_format->save();
+  }
+
   /**
    * Test user edit page.
    */
   function testUserEdit() {
     // Test user edit functionality.
-    $user1 = $this->drupalCreateUser(array('change own username'));
+    $user1 = $this->drupalCreateUser(array('change own username', $this->full_html_format->getPermissionName(), $this->filtered_html_format->getPermissionName()));
     $user2 = $this->drupalCreateUser(array());
     $this->drupalLogin($user1);
 
@@ -85,6 +116,11 @@ function testUserEdit() {
     $config->set('password_strength', FALSE)->save();
     $this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
     $this->assertNoRaw(t('Password strength:'), 'The password strength indicator is not displayed.');
+
+    // Test user signature
+    $edit = array('signature[format]' => $this->full_html_format->id(), 'signature[value]' => $this->randomString(256));
+    $this->drupalPostForm('user/' . $user1->id() . '/edit', $edit, t('Save'));
+    $this->assertRaw(t("%name: may not be longer than @max characters.", array('%name' => t('Signature'), '@max' => 255)));
   }
 
   /**
diff --git a/core/modules/user/src/Tests/UserRegistrationTest.php b/core/modules/user/src/Tests/UserRegistrationTest.php
index 853891a..221667f 100644
--- a/core/modules/user/src/Tests/UserRegistrationTest.php
+++ b/core/modules/user/src/Tests/UserRegistrationTest.php
@@ -138,13 +138,13 @@ function testRegistrationEmailDuplicates() {
 
     // Attempt to create a new account using an existing email address.
     $this->drupalPostForm('user/register', $edit, t('Create new account'));
-    $this->assertText(t('The email address @email is already registered.', array('@email' => $duplicate_user->getEmail())), 'Supplying an exact duplicate email address displays an error message');
+    $this->assertText(t('The email address @email is already taken.', array('@email' => $duplicate_user->getEmail())), 'Supplying an exact duplicate email address displays an error message');
 
     // Attempt to bypass duplicate email registration validation by adding spaces.
     $edit['mail'] = '   ' . $duplicate_user->getEmail() . '   ';
 
     $this->drupalPostForm('user/register', $edit, t('Create new account'));
-    $this->assertText(t('The email address @email is already registered.', array('@email' => $duplicate_user->getEmail())), 'Supplying a duplicate email address with added whitespace displays an error message');
+    $this->assertText(t('The email address @email is already taken.', array('@email' => $duplicate_user->getEmail())), 'Supplying a duplicate email address with added whitespace displays an error message');
   }
 
   function testRegistrationDefaultValues() {
diff --git a/core/modules/user/src/Tests/UserValidationTest.php b/core/modules/user/src/Tests/UserValidationTest.php
index f5ae202..25ef677 100644
--- a/core/modules/user/src/Tests/UserValidationTest.php
+++ b/core/modules/user/src/Tests/UserValidationTest.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Field\Plugin\Field\FieldType\EmailItem;
+use Drupal\Core\Language\Language;
 use Drupal\Core\Render\Element\Email;
 use Drupal\simpletest\KernelTestBase;
 use Drupal\user\Entity\Role;
@@ -133,15 +134,33 @@ function testValidation() {
     $this->assertLengthViolation($user, 'signature', 255);
     $user->set('signature', NULL);
 
+    $user->set('signature_format', $this->randomString(32));
+    $this->assertAllowedValuesViolation($user, 'signature_format');
+    $user->set('signature_format', NULL);
+
     $user->set('timezone', $this->randomString(33));
-    $this->assertLengthViolation($user, 'timezone', 32);
+    $this->assertLengthViolation($user, 'timezone', 32, 2, 1);
+    $user->set('timezone', 'invalid zone');
+    $this->assertAllowedValuesViolation($user, 'timezone');
     $user->set('timezone', NULL);
 
     $user->set('init', 'invalid');
     $violations = $user->validate();
     $this->assertEqual(count($violations), 1, 'Violation found when init email is invalid');
-    $this->assertEqual($violations[0]->getPropertyPath(), 'init.0.value');
-    $this->assertEqual($violations[0]->getMessage(), t('This value is not a valid email address.'));
+    $user->set('init', NULL);
+
+    $user->set('langcode', 'invalid');
+    $this->assertAllowedValuesViolation($user, 'langcode');
+    $user->set('langcode', NULL);
+
+    // Only configurable langcodes are allowed for preferred languages.
+    $user->set('preferred_langcode', Language::LANGCODE_NOT_SPECIFIED);
+    $this->assertAllowedValuesViolation($user, 'preferred_langcode');
+    $user->set('preferred_langcode', NULL);
+
+    $user->set('preferred_admin_langcode', Language::LANGCODE_NOT_SPECIFIED);
+    $this->assertAllowedValuesViolation($user, 'preferred_admin_langcode');
+    $user->set('preferred_admin_langcode', NULL);
 
     Role::create(array('id' => 'role1'))->save();
     Role::create(array('id' => 'role2'))->save();
@@ -170,13 +189,32 @@ function testValidation() {
    *   The field that violates the maximum length.
    * @param int $length
    *   Number of characters that was exceeded.
+   * @param int $count
+   *   (optional) The number of expected violations. Defaults to 1.
+   * @param int $expected_index
+   *   (optional) The index at which to expect the violation. Defaults to 0.
    */
-  protected function assertLengthViolation(EntityInterface $entity, $field_name, $length) {
+  protected function assertLengthViolation(EntityInterface $entity, $field_name, $length, $count = 1, $expected_index = 0) {
     $violations = $entity->validate();
-    $this->assertEqual(count($violations), 1, "Violation found when $field_name is too long.");
-    $this->assertEqual($violations[0]->getPropertyPath(), "$field_name.0.value");
+    $this->assertEqual(count($violations), $count, "Violation found when $field_name is too long.");
+    $this->assertEqual($violations[$expected_index]->getPropertyPath(), "$field_name.0.value");
     $field_label = $entity->get($field_name)->getFieldDefinition()->getLabel();
-    $this->assertEqual($violations[0]->getMessage(), t('%name: may not be longer than @max characters.', array('%name' => $field_label, '@max' => $length)));
+    $this->assertEqual($violations[$expected_index]->getMessage(), t('%name: may not be longer than @max characters.', array('%name' => $field_label, '@max' => $length)));
+  }
+
+  /**
+   * Verifies that a AllowedValues violation exists for the given field.
+   *
+   * @param \Drupal\core\Entity\EntityInterface $entity
+   *   The entity object to validate.
+   * @param string $field_name
+   *   The name of the field to verify.
+   */
+  protected function assertAllowedValuesViolation(EntityInterface $entity, $field_name) {
+    $violations = $entity->validate();
+    $this->assertEqual(count($violations), 1, "Allowed values violation for $field_name found.");
+    $this->assertEqual($violations[0]->getPropertyPath(), "$field_name.0.value");
+    $this->assertEqual($violations[0]->getMessage(), t('The value you selected is not a valid choice.'));
   }
 
 }
diff --git a/core/modules/user/src/UserInterface.php b/core/modules/user/src/UserInterface.php
index e2c44d0..c6f3b2a 100644
--- a/core/modules/user/src/UserInterface.php
+++ b/core/modules/user/src/UserInterface.php
@@ -97,6 +97,16 @@ public function setEmail($mail);
   public function getSignature();
 
   /**
+   * Sets the user signature.
+   *
+   * @param string $signature
+   *   The new signature text of the user.
+   *
+   * @return $this
+   */
+  public function setSignature($signature);
+
+  /**
    * Returns the signature format.
    *
    * @return string
@@ -105,6 +115,16 @@ public function getSignature();
   public function getSignatureFormat();
 
   /**
+   * Sets the signature format.
+   *
+   * @param string $signature_format
+   *   The name of the new filter format.
+   *
+   * @return $this
+   */
+  public function setSignatureFormat($signature_format);
+
+  /**
    * Returns the creation time of the user as a UNIX timestamp.
    *
    * @return int
