diff --git a/core/lib/Drupal/Core/Form/FormErrorHandler.php b/core/lib/Drupal/Core/Form/FormErrorHandler.php
index df736b0..385ef62 100644
--- a/core/lib/Drupal/Core/Form/FormErrorHandler.php
+++ b/core/lib/Drupal/Core/Form/FormErrorHandler.php
@@ -83,8 +83,9 @@ protected function displayErrorMessages(array $form, FormStateInterface $form_st
       }
       elseif ($is_visible_element && $has_title && $has_id) {
         // We need to pass this through SafeMarkup::escape() so
-        // drupal_set_message() does not encode the links.
-        $error_links[] = SafeMarkup::escape($this->l($title, Url::fromRoute('<none>', [], ['fragment' => $form_element['#id'], 'external' => TRUE])));
+        // drupal_set_message() does not encode the links. The element title,
+        // however, can contain HTML so we need to mark it as safe.
+        $error_links[] = SafeMarkup::escape($this->l(['#markup' => $title], Url::fromRoute('<none>', [], ['fragment' => $form_element['#id'], 'external' => TRUE])));
         unset($errors[$name]);
       }
     }
@@ -98,6 +99,7 @@ protected function displayErrorMessages(array $form, FormStateInterface $form_st
       $message = $this->formatPlural(count($error_links), '1 error has been found: !errors', '@count errors have been found: !errors', [
         '!errors' => SafeMarkup::set(implode(', ', $error_links)),
       ]);
+
       $this->drupalSetMessage($message, 'error');
     }
   }
diff --git a/core/modules/config_translation/src/ConfigEntityMapper.php b/core/modules/config_translation/src/ConfigEntityMapper.php
index 269bfac..4a6d275 100644
--- a/core/modules/config_translation/src/ConfigEntityMapper.php
+++ b/core/modules/config_translation/src/ConfigEntityMapper.php
@@ -8,8 +8,8 @@
 namespace Drupal\config_translation;
 
 use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Config\Entity\ConfigEntityInterface;
 use Drupal\Core\Config\TypedConfigManagerInterface;
-use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\Routing\RouteMatch;
@@ -43,7 +43,7 @@ class ConfigEntityMapper extends ConfigNamesMapper {
   /**
    * Loaded entity instance to help produce the translation interface.
    *
-   * @var \Drupal\Core\Entity\EntityInterface
+   * @var \Drupal\Core\Config\Entity\ConfigEntityInterface
    */
   protected $entity;
 
@@ -125,13 +125,13 @@ public function populateFromRequest(Request $request) {
    * configuration names to use to check permissions or display a translation
    * screen.
    *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity to set.
+   * @param \Drupal\Core\Config\Entity\ConfigEntityInterface $entity
+   *   The configuration entity to set.
    *
    * @return bool
    *   TRUE, if the entity was set successfully; FALSE otherwise.
    */
-  public function setEntity(EntityInterface $entity) {
+  public function setEntity(ConfigEntityInterface $entity) {
     if (isset($this->entity)) {
       return FALSE;
     }
@@ -143,6 +143,7 @@ public function setEntity(EntityInterface $entity) {
     // page with more names if form altering added more configuration to an
     // entity. This is not a Drupal 8 best practice (ideally the configuration
     // would have pluggable components), but this may happen as well.
+    /** @var \Drupal\Core\Config\Entity\ConfigEntityTypeInterface $entity_type_info */
     $entity_type_info = $this->entityManager->getDefinition($this->entityType);
     $this->addConfigName($entity_type_info->getConfigPrefix() . '.' . $entity->id());
 
diff --git a/core/modules/config_translation/src/ConfigFieldMapper.php b/core/modules/config_translation/src/ConfigFieldMapper.php
index e9d48e4..a593fff 100644
--- a/core/modules/config_translation/src/ConfigFieldMapper.php
+++ b/core/modules/config_translation/src/ConfigFieldMapper.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\config_translation;
 
+use Drupal\Core\Config\Entity\ConfigEntityInterface;
+
 /**
  * Configuration mapper for fields.
  *
@@ -49,4 +51,22 @@ public function getTypeLabel() {
     return $this->t('@label fields', array('@label' => $base_entity_info->getLabel()));
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function setEntity(ConfigEntityInterface $entity) {
+    if (parent::setEntity($entity)) {
+
+      // Field storage config can also contain translatable values. Add the name
+      // of the config as well to the list of configs for this entity.
+      /** @var \Drupal\field\FieldStorageConfigInterface $field_storage */
+      $field_storage = $this->entity->getFieldStorageDefinition();
+      /** @var \Drupal\Core\Config\Entity\ConfigEntityTypeInterface $entity_type_info */
+      $entity_type_info = $this->entityManager->getDefinition($field_storage->getEntityTypeId());
+      $this->addConfigName($entity_type_info->getConfigPrefix() . '.' . $field_storage->id());
+      return TRUE;
+    }
+    return FALSE;
+  }
+
 }
diff --git a/core/modules/config_translation/src/FormElement/FormElementBase.php b/core/modules/config_translation/src/FormElement/FormElementBase.php
index e13cc31..77fe96d 100644
--- a/core/modules/config_translation/src/FormElement/FormElementBase.php
+++ b/core/modules/config_translation/src/FormElement/FormElementBase.php
@@ -8,6 +8,7 @@
 namespace Drupal\config_translation\FormElement;
 
 use Drupal\Core\Config\Config;
+use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Language\LanguageInterface;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\Core\TypedData\TypedDataInterface;
@@ -147,6 +148,12 @@ protected function getSourceElement(LanguageInterface $source_language, $source_
    * translation of complex data, similar access logic must be implemented
    * manually.
    *
+   * Note that because configuration values might be output on the page the form
+   * element is also responsible for validating that the input does not contain
+   * malicious HTML input. FormElementBase::validateInput(), which uses
+   * locale_string_is_safe() for the validation, is provided for that purpose
+   * and is used for the default form element.
+   *
    * @param \Drupal\Core\Language\LanguageInterface $translation_language
    *   The language to display the translation form for.
    * @param mixed $source_config
@@ -159,6 +166,8 @@ protected function getSourceElement(LanguageInterface $source_language, $source_
    *
    * @see \Drupal\config_translation\FormElement\TextFormat
    * @see filter_process_format()
+   * @see \Drupal\config_translation\FormElement\FormElementBase::validateInput()
+   * @see locale_string_is_safe()
    */
   protected function getTranslationElement(LanguageInterface $translation_language, $source_config, $translation_config) {
     // Add basic properties that apply to all form elements.
@@ -170,10 +179,25 @@ protected function getTranslationElement(LanguageInterface $translation_language
       )),
       '#default_value' => $translation_config,
       '#attributes' => array('lang' => $translation_language->getId()),
+      '#element_validate' => [[get_class($this), 'validateInput']],
     );
   }
 
   /**
+   * Validates that the form element does not contain malicious HTML input.
+   *
+   * @param $element
+   *   The form element to validate.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The form state.
+   */
+  public static function validateInput($element, FormStateInterface $form_state) {
+    if (isset($element['#value']) && !locale_string_is_safe($element['#value'])) {
+      $form_state->setError($element, t('The submitted string contains disallowed HTML.'));
+    }
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function setConfig(Config $base_config, LanguageConfigOverride $config_translation, $config_values, $base_key = NULL) {
diff --git a/core/modules/config_translation/src/Tests/ConfigTranslationUiTest.php b/core/modules/config_translation/src/Tests/ConfigTranslationUiTest.php
index c7218df..18b5a21 100644
--- a/core/modules/config_translation/src/Tests/ConfigTranslationUiTest.php
+++ b/core/modules/config_translation/src/Tests/ConfigTranslationUiTest.php
@@ -13,6 +13,8 @@
 use Drupal\Core\Config\FileStorage;
 use Drupal\Core\Language\Language;
 use Drupal\Core\Language\LanguageInterface;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
 use Drupal\language\Entity\ConfigurableLanguage;
 use Drupal\simpletest\WebTestBase;
 
@@ -28,7 +30,21 @@ class ConfigTranslationUiTest extends WebTestBase {
    *
    * @var array
    */
-  public static $modules = array('node', 'contact', 'contact_test', 'config_translation', 'config_translation_test', 'views', 'views_ui', 'contextual', 'filter', 'filter_test');
+  public static $modules = [
+    'config_translation',
+    'config_translation_test',
+    'contact',
+    'contact_test',
+    'contextual',
+    'entity_test',
+    'field_test',
+    'field_ui',
+    'filter',
+    'filter_test',
+    'node',
+    'views',
+    'views_ui',
+  ];
 
   /**
    * Languages to enable.
@@ -610,6 +626,46 @@ public function testViewsTranslationUI() {
   }
 
   /**
+   * Tests the translation of field and field storage configuration.
+   */
+  public function testFieldConfigTranslation() {
+    // Add a test field which has a translatable field setting and a
+    // translatable field storage setting.
+    $field_name = strtolower($this->randomMachineName());
+    $field_storage = FieldStorageConfig::create([
+      'field_name' => $field_name,
+      'entity_type' => 'entity_test',
+      'type' => 'test_field',
+    ]);
+
+    $translatable_storage_setting = $this->randomString();
+    $field_storage->setSetting('translatable_storage_setting', $translatable_storage_setting);
+    $field_storage->save();
+
+    $bundle = strtolower($this->randomMachineName());
+    entity_test_create_bundle($bundle);
+    $field = FieldConfig::create([
+      'field_name' => $field_name,
+      'entity_type' => 'entity_test',
+      'bundle' => $bundle,
+    ]);
+
+    $translatable_field_setting = $this->randomString();
+    $field->setSetting('translatable_field_setting', $translatable_field_setting);
+    $field->save();
+
+    $this->drupalLogin($this->translatorUser);
+
+    $this->drupalGet("/entity_test/structure/$bundle/fields/entity_test.$bundle.$field_name/translate");
+    $this->clickLink('Add');
+
+    $this->assertText('Translatable field setting');
+    $this->assertText(SafeMarkup::checkPlain($translatable_field_setting));
+    $this->assertText('Translatable storage setting');
+    $this->assertText(SafeMarkup::checkPlain($translatable_storage_setting));
+  }
+
+  /**
    * Test translation storage in locale storage.
    */
   public function testLocaleDBStorage() {
diff --git a/core/modules/config_translation/tests/src/Unit/ConfigEntityMapperTest.php b/core/modules/config_translation/tests/src/Unit/ConfigEntityMapperTest.php
index 8a2fc59..8041931 100644
--- a/core/modules/config_translation/tests/src/Unit/ConfigEntityMapperTest.php
+++ b/core/modules/config_translation/tests/src/Unit/ConfigEntityMapperTest.php
@@ -57,7 +57,7 @@ class ConfigEntityMapperTest extends UnitTestCase {
   protected function setUp() {
     $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
 
-    $this->entity = $this->getMock('Drupal\Core\Entity\EntityInterface');
+    $this->entity = $this->getMock('Drupal\Core\Config\Entity\ConfigEntityInterface');
 
     $this->routeProvider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface');
 
@@ -109,6 +109,10 @@ public function testSetEntity() {
       ->will($this->returnValue('entity_id'));
 
     $entity_type = $this->getMock('Drupal\Core\Config\Entity\ConfigEntityTypeInterface');
+    $entity_type
+      ->expects($this->any())
+      ->method('getConfigPrefix')
+      ->will($this->returnValue('config_prefix'));
     $this->entityManager
       ->expects($this->once())
       ->method('getDefinition')
@@ -118,6 +122,10 @@ public function testSetEntity() {
     $result = $this->configEntityMapper->setEntity($this->entity);
     $this->assertTrue($result);
 
+    // Ensure that the configuration name was added to the mapper.
+    $plugin_definition = $this->configEntityMapper->getPluginDefinition();
+    $this->assertTrue(in_array('config_prefix.entity_id', $plugin_definition['names']));
+
     // Make sure setEntity() returns FALSE when called a second time.
     $result = $this->configEntityMapper->setEntity($this->entity);
     $this->assertFalse($result);
diff --git a/core/modules/config_translation/tests/src/Unit/ConfigFieldMapperTest.php b/core/modules/config_translation/tests/src/Unit/ConfigFieldMapperTest.php
new file mode 100644
index 0000000..740b722
--- /dev/null
+++ b/core/modules/config_translation/tests/src/Unit/ConfigFieldMapperTest.php
@@ -0,0 +1,116 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\config_translation\Unit\ConfigFieldMapperTest.
+ */
+
+namespace Drupal\Tests\config_translation\Unit;
+
+use Drupal\config_translation\ConfigFieldMapper;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * Tests the functionality provided by the configuration field mapper.
+ *
+ * @group config_translation
+ *
+ * @coversDefaultClass \Drupal\config_translation\ConfigFieldMapper
+ */
+class ConfigFieldMapperTest extends UnitTestCase {
+
+  /**
+   * The configuration field mapper to test.
+   *
+   * @var \Drupal\config_translation\ConfigFieldMapper
+   */
+  protected $configFieldMapper;
+
+  /**
+   * The field config instance used for testing.
+   *
+   * @var \Drupal\field\FieldConfigInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $entity;
+
+  /**
+   * The entity manager used for testing.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $entityManager;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
+    $this->entity = $this->getMock('Drupal\field\FieldConfigInterface');
+
+    $definition = array(
+      'class' => '\Drupal\config_translation\ConfigFieldMapper',
+      'base_route_name' => 'entity.field_config.node_field_edit_form',
+      'title' => '!label field',
+      'names' => array(),
+      'entity_type' => 'field_config',
+    );
+
+    $locale_config_manager = $this->getMockBuilder('Drupal\locale\LocaleConfigManager')
+      ->disableOriginalConstructor()
+      ->getMock();
+
+    $this->configFieldMapper = new ConfigFieldMapper(
+      'node_fields',
+      $definition,
+      $this->getConfigFactoryStub(),
+      $this->getMock('Drupal\Core\Config\TypedConfigManagerInterface'),
+      $locale_config_manager,
+      $this->getMock('Drupal\config_translation\ConfigMapperManagerInterface'),
+      $this->getMock('Drupal\Core\Routing\RouteProviderInterface'),
+      $this->getStringTranslationStub(),
+      $this->entityManager,
+      $this->getMock('Drupal\Core\Language\LanguageManagerInterface')
+    );
+  }
+
+  /**
+   * Tests ConfigFieldMapper::setEntity().
+   *
+   * @covers ::setEntity
+   */
+  public function testSetEntity() {
+    $entity_type = $this->getMock('Drupal\Core\Config\Entity\ConfigEntityTypeInterface');
+    $entity_type
+      ->expects($this->any())
+      ->method('getConfigPrefix')
+      ->will($this->returnValue('config_prefix'));
+
+    $this->entityManager
+      ->expects($this->any())
+      ->method('getDefinition')
+      ->will($this->returnValue($entity_type));
+
+    $field_storage = $this->getMock('Drupal\field\FieldStorageConfigInterface');
+    $field_storage
+      ->expects($this->any())
+      ->method('id')
+      ->will($this->returnValue('field_storage_id'));
+
+    $this->entity
+      ->expects($this->any())
+      ->method('getFieldStorageDefinition')
+      ->will($this->returnValue($field_storage));
+
+    $result = $this->configFieldMapper->setEntity($this->entity);
+    $this->assertTrue($result);
+
+    // Ensure that the configuration name was added to the mapper.
+    $plugin_definition = $this->configFieldMapper->getPluginDefinition();
+    $this->assertTrue(in_array('config_prefix.field_storage_id', $plugin_definition['names']));
+
+    // Make sure setEntity() returns FALSE when called a second time.
+    $result = $this->configFieldMapper->setEntity($this->entity);
+    $this->assertFalse($result);
+  }
+
+}
diff --git a/core/modules/field/tests/modules/field_test/config/schema/field_test.schema.yml b/core/modules/field/tests/modules/field_test/config/schema/field_test.schema.yml
index 48f5dcf..8e06db1 100644
--- a/core/modules/field/tests/modules/field_test/config/schema/field_test.schema.yml
+++ b/core/modules/field/tests/modules/field_test/config/schema/field_test.schema.yml
@@ -65,6 +65,9 @@ field.storage_settings.test_field:
     config_data_from_storage_setting:
       type: boolean
       label: 'Test FieldItemInterface::storageSettingsToConfigData()'
+    translatable_storage_setting:
+      type: label
+      label: 'Translatable storage setting'
 
 field.storage_settings.test_field_with_dependencies:
   type: field.storage_settings.test_field
@@ -88,6 +91,9 @@ field.field_settings.test_field:
     config_data_from_field_setting:
       type: boolean
       label: 'Test FieldItemInterface::fieldSettingsToConfigData()'
+    translatable_field_setting:
+      type: label
+      label: 'Translatable field setting'
 
 field.field_settings.test_field_with_dependencies:
   type: field.field_settings.test_field
diff --git a/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItem.php b/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItem.php
index 9886b03..42eb11e 100644
--- a/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItem.php
+++ b/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItem.php
@@ -33,6 +33,7 @@ public static function defaultStorageSettings() {
       'test_field_storage_setting' => 'dummy test string',
       'changeable' => 'a changeable field storage setting',
       'unchangeable' => 'an unchangeable field storage setting',
+      'translatable_storage_setting' => 'a translatable field storage setting',
     ) + parent::defaultStorageSettings();
   }
 
@@ -42,6 +43,7 @@ public static function defaultStorageSettings() {
   public static function defaultFieldSettings() {
     return array(
       'test_field_setting' => 'dummy test string',
+      'translatable_field_setting' => 'a translatable field setting',
     ) + parent::defaultFieldSettings();
   }
 
diff --git a/core/tests/Drupal/Tests/Core/Form/FormErrorHandlerTest.php b/core/tests/Drupal/Tests/Core/Form/FormErrorHandlerTest.php
index 53d7e76..86367ef 100644
--- a/core/tests/Drupal/Tests/Core/Form/FormErrorHandlerTest.php
+++ b/core/tests/Drupal/Tests/Core/Form/FormErrorHandlerTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Tests\Core\Form;
 
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Core\Form\FormState;
 use Drupal\Tests\UnitTestCase;
 
@@ -24,7 +25,11 @@ public function testDisplayErrorMessages() {
     $link_generator = $this->getMock('Drupal\Core\Utility\LinkGeneratorInterface');
     $link_generator->expects($this->any())
       ->method('generate')
-      ->willReturnArgument(0);
+      ->willReturnCallback(function (array $build) {
+        // @todo This should not be necessary after
+        //   https://www.drupal.org/node/2501975
+        return SafeMarkup::set($build['#markup']);
+      });
     $form_error_handler = $this->getMockBuilder('Drupal\Core\Form\FormErrorHandler')
       ->setConstructorArgs([$this->getStringTranslationStub(), $link_generator])
       ->setMethods(['drupalSetMessage'])
@@ -41,7 +46,7 @@ public function testDisplayErrorMessages() {
       ->with('this missing element is invalid', 'error');
     $form_error_handler->expects($this->at(3))
       ->method('drupalSetMessage')
-      ->with('3 errors have been found: Test 1, Test 2 &amp; a half, Test 3', 'error');
+      ->with('3 errors have been found: Test 1, Test 2 & a half, Test 3', 'error');
 
     $form = [
       '#parents' => [],
