diff --git a/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php b/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php
index 2831fd9..5bdbfc8 100644
--- a/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php
+++ b/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php
@@ -176,6 +176,9 @@ public function buildForm(array $form, FormStateInterface $form_state, Request $
       if ($form_element = $this->createFormElement($schema)) {
         $parents = array('config_names', $name);
         $form['config_names'][$name] += $form_element->getTranslationBuild($this->sourceLanguage, $this->language, $source_config, $translation_config, $parents);
+        if ($attributes = $form_element->getFormAttributes()) {
+          $form = array_merge_recursive($form, $attributes);
+        }
       }
     }
 
diff --git a/core/modules/config_translation/src/FormElement/DateFormat.php b/core/modules/config_translation/src/FormElement/DateFormat.php
index 729afee..ea947af 100644
--- a/core/modules/config_translation/src/FormElement/DateFormat.php
+++ b/core/modules/config_translation/src/FormElement/DateFormat.php
@@ -29,44 +29,47 @@ public function getTranslationElement(LanguageInterface $translation_language, $
       '#type' => 'textfield',
       '#description' => $description,
       '#field_suffix' => ' <div class="edit-date-format-suffix"><small id="edit-date-format-suffix">' . $format . '</small></div>',
-      '#ajax' => array(
-        'callback' => 'Drupal\config_translation\FormElement\DateFormat::ajaxSample',
-        'event' => 'keyup',
-        'progress' => array('type' => 'throbber', 'message' => NULL),
+      '#attributes' => array(
+        'class' => array('date-formatter'),
       ),
+      '#attached' => array(
+        'drupalSettings' => array('dateFormats' => $this->getSampleDateFormats($translation_language->getId())),
+      ),
+
     ) + parent::getTranslationElement($translation_language, $source_config, $translation_config);
   }
 
   /**
-   * Ajax callback to render a sample of the input date format.
-   *
-   * @param array $form
-   *   Form API array structure.
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   Form state information.
-   *
-   * @return AjaxResponse
-   *   Ajax response with the rendered sample date using the given format. If
-   *   the given format cannot be identified or was empty, the response will
-   *   be empty as well.
+   * {@inheritdoc}
    */
-  public static function ajaxSample(array $form, FormStateInterface $form_state) {
-    $response = new AjaxResponse();
-
-    $format_value = NestedArray::getValue($form_state->getValues(), $form_state->getTriggeringElement()['#array_parents']);
-    if (!empty($format_value)) {
-      // Format the date with a custom date format with the given pattern.
-      // The object is not instantiated in an Ajax context, so $this->t()
-      // cannot be used here.
-      $format = t('Displayed as %date_format', array('%date_format' => \Drupal::service('date.formatter')->format(REQUEST_TIME, 'custom', $format_value)));
-
-      // Return a command instead of a string, since the Ajax framework
-      // automatically prepends an additional empty DIV element for a string,
-      // which breaks the layout.
-      $response->addCommand(new ReplaceCommand('#edit-date-format-suffix', '<small id="edit-date-format-suffix">' . $format . '</small>'));
-    }
+  public function getFormAttributes() {
+    return array(
+      '#attached' => array(
+        'library' => array('system/drupal.system.date'),
+      )
+    );
+  }
 
-    return $response;
+  /**
+   * Provides all date format characters for date format preview on client side.
+   *
+   * @param int|null $timestamp
+   * @return array
+   *   An array of date character values keyed by characters.
+   */
+  protected function getSampleDateFormats($langcode, $timestamp = NULL, $timezone = NULL) {
+    $tz = $timezone ?: date_default_timezone_get();
+    $ts = $timestamp ?: REQUEST_TIME;
+    // All date format characters in PHP.
+    $date_characters = 'dDjlNSwzWFmMntLoYyaABgGhHisueIOPTZcrU';
+    $date_chars = str_split($date_characters);
+    $date_elements = array_combine($date_chars, $date_chars);
+    /** @var \Drupal\Core\Datetime\DateFormatter $date_formatter */
+    $date_formatter = \Drupal::service('date.formatter');
+    array_walk($date_elements, function(&$character) use ($ts, $date_formatter, $langcode, $tz) {
+      $character = $date_formatter->format($ts, 'custom', $character, $tz, $langcode);
+    });
+    return $date_elements;
   }
 
 }
diff --git a/core/modules/config_translation/src/FormElement/ElementInterface.php b/core/modules/config_translation/src/FormElement/ElementInterface.php
index 286eeba..22ab0ef 100644
--- a/core/modules/config_translation/src/FormElement/ElementInterface.php
+++ b/core/modules/config_translation/src/FormElement/ElementInterface.php
@@ -70,4 +70,12 @@ public function getTranslationBuild(LanguageInterface $source_language, Language
    */
   public function setConfig(Config $base_config, LanguageConfigOverride $config_translation, $config_values, $base_key = NULL);
 
+
+  /**
+   * Builds a form attributes for the configuration form.
+   *
+   * @return mixed
+   */
+  public function getFormAttributes();
+
 }
diff --git a/core/modules/config_translation/src/FormElement/FormElementBase.php b/core/modules/config_translation/src/FormElement/FormElementBase.php
index 7db01e1..b8438d7 100644
--- a/core/modules/config_translation/src/FormElement/FormElementBase.php
+++ b/core/modules/config_translation/src/FormElement/FormElementBase.php
@@ -74,6 +74,10 @@ public function getTranslationBuild(LanguageInterface $source_language, Language
     return $build;
   }
 
+  public function getFormAttributes() {
+    return array();
+  }
+
   /**
    * Returns the source element for a given configuration definition.
    *
diff --git a/core/modules/config_translation/src/FormElement/ListElement.php b/core/modules/config_translation/src/FormElement/ListElement.php
index 9365fc2..8ec68a6 100644
--- a/core/modules/config_translation/src/FormElement/ListElement.php
+++ b/core/modules/config_translation/src/FormElement/ListElement.php
@@ -97,6 +97,23 @@ public function setConfig(Config $base_config, LanguageConfigOverride $config_tr
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function getFormAttributes() {
+    $attributes = array();
+    foreach ($this->element as $key => $element) {
+      if ($form_element = ConfigTranslationFormBase::createFormElement($element)) {
+        $form_attributes = $form_element->getFormAttributes();
+        if (empty($form_attributes)) {
+          continue;
+        }
+        $attributes +=  $form_attributes;
+      }
+    }
+    return $attributes;
+  }
+
+  /**
    * Returns the title for the 'details' element of a group of schema elements.
    *
    * For some configuration elements the same element structure can be repeated
diff --git a/core/modules/system/js/system.date.js b/core/modules/system/js/system.date.js
new file mode 100644
index 0000000..7ba36e0
--- /dev/null
+++ b/core/modules/system/js/system.date.js
@@ -0,0 +1,67 @@
+(function ($) {
+
+  "use strict";
+
+  /**
+   * Display the preview for date format entered.
+   */
+  Drupal.behaviors.dateFormat = {
+    attach: function (context, settings) {
+      var $context = $(context);
+      var $source = $context.find('.date-formatter');
+      var $target = $context.find('#edit-date-format-suffix');
+      // All elements have to exist.
+      if (!$source.length || !$target.length) {
+        return;
+      }
+      var dateFormats = settings.dateFormats;
+      var eventData = {
+        $source: $source,
+        $target: $target
+      };
+
+
+      /**
+       * Callback to check if the given character has a date value.
+       *
+       * @param key
+       * @param value
+       *
+       * @returns {*}
+       */
+      function replaceCallback(key, value) {
+        return dateFormats[key] ? dateFormats[key] : value;
+      }
+
+      /**
+       * Event handler that replaces date characters with value.
+       *
+       * @param e
+       */
+      function dateFormatHandler(e) {
+        var data = e.data;
+        var baseValue = $(e.target).val();
+        var dateString = baseValue.replace(/\\?(.?)/gi, replaceCallback);
+
+        if (dateString.length) {
+          $(data.$target).show();
+          if ($('em', $(data.$target)).length) {
+            $('em', $(data.$target)).html(dateString);
+          }
+        }
+        else {
+          $(data.$target).hide();
+        }
+
+      }
+
+      /**
+       * On given event triggers the date character replacement.
+       */
+      $source.on('keyup.dateFormat change.dateFormat input.dateFormat', eventData, dateFormatHandler)
+        // Initialize preview.
+        .trigger('keyup');
+    }
+  };
+
+})(jQuery);
diff --git a/core/modules/system/system.js b/core/modules/system/js/system.js
similarity index 100%
rename from core/modules/system/system.js
rename to core/modules/system/js/system.js
diff --git a/core/modules/system/system.modules.js b/core/modules/system/js/system.modules.js
similarity index 100%
rename from core/modules/system/system.modules.js
rename to core/modules/system/js/system.modules.js
diff --git a/core/modules/system/src/Form/DateFormatFormBase.php b/core/modules/system/src/Form/DateFormatFormBase.php
index c843ba2..0c46500 100644
--- a/core/modules/system/src/Form/DateFormatFormBase.php
+++ b/core/modules/system/src/Form/DateFormatFormBase.php
@@ -80,30 +80,6 @@ public function exists($entity_id, array $element) {
   }
 
   /**
-   * Returns the date for a given format string.
-   *
-   * @param array $form
-   *   An associative array containing the structure of the form.
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   The current state of the form.
-   *
-   * @return \Drupal\Core\Ajax\AjaxResponse
-   *   An AJAX Response to update the date-time value of the date format.
-   */
-  public static function dateTimeLookup(array $form, FormStateInterface $form_state) {
-    $format = '';
-    if (!$form_state->isValueEmpty('date_format_pattern')) {
-      $format = t('Displayed as %date_format', array('%date_format' => \Drupal::service('date.formatter')->format(REQUEST_TIME, 'custom', $form_state->getValue('date_format_pattern'))));
-    }
-    // Return a command instead of a string, since the Ajax framework
-    // automatically prepends an additional empty DIV element for a string, which
-    // breaks the layout.
-    $response = new AjaxResponse();
-    $response->addCommand(new ReplaceCommand('#edit-date-format-suffix', '<small id="edit-date-format-suffix">' . $format . '</small>'));
-    return $response;
-  }
-
-  /**
    * {@inheritdoc}
    */
   public function form(array $form, FormStateInterface $form_state) {
@@ -126,20 +102,16 @@ public function form(array $form, FormStateInterface $form_state) {
         'error' => $this->t('The machine-readable name must be unique, and can only contain lowercase letters, numbers, and underscores. Additionally, it can not be the reserved word "custom".'),
       ),
     );
-
     $form['date_format_pattern'] = array(
       '#type' => 'textfield',
       '#title' => t('Format string'),
       '#maxlength' => 100,
       '#description' => $this->t('A user-defined date format. See the <a href="@url">PHP manual</a> for available options.', array('@url' => 'http://php.net/manual/function.date.php')),
-      '#default_value' => '',
-      '#field_suffix' => ' <small id="edit-date-format-suffix"></small>',
-      '#ajax' => array(
-        'callback' => '::dateTimeLookup',
-        'event' => 'keyup',
-        'progress' => array('type' => 'throbber', 'message' => NULL),
-      ),
       '#required' => TRUE,
+      '#attributes' => array(
+        'class' => array('date-formatter'),
+      ),
+      '#field_suffix' => ' <small id="edit-date-format-suffix">Displayed as <em></em></small>',
     );
 
     $form['langcode'] = array(
@@ -148,7 +120,8 @@ public function form(array $form, FormStateInterface $form_state) {
       '#languages' => LanguageInterface::STATE_ALL,
       '#default_value' => $this->entity->language()->getId(),
     );
-
+    $form['#attached']['drupalSettings']['dateFormats'] = $this->getSampleDateFormats();
+    $form['#attached']['library'][] = 'system/drupal.system.date';
     return parent::form($form, $form_state);
   }
 
@@ -192,4 +165,23 @@ public function save(array $form, FormStateInterface $form_state) {
     $form_state->setRedirectUrl($this->entity->urlInfo('collection'));
   }
 
+  /**
+   * Provides all date format characters for date format preview on client side.
+   *
+   * @param int|null $timestamp
+   * @return array
+   *   An array of date character values keyed by characters.
+   */
+  protected function getSampleDateFormats($timestamp = NULL) {
+    $ts = $timestamp ?: REQUEST_TIME;
+    // All date format characters in PHP.
+    $date_characters = 'dDjlNSwzWFmMntLoYyaABgGhHisueIOPTZcrU';
+    $date_chars = str_split($date_characters);
+    $date_elements = array_combine($date_chars, $date_chars);
+    array_walk($date_elements, function(&$character) use ($ts) {
+      $character = $this->dateFormatter->format($ts, 'custom', $character);
+    });
+    return $date_elements;
+  }
+
 }
diff --git a/core/modules/system/system.libraries.yml b/core/modules/system/system.libraries.yml
index 34c90dd..a1cf34e 100644
--- a/core/modules/system/system.libraries.yml
+++ b/core/modules/system/system.libraries.yml
@@ -29,7 +29,7 @@ maintenance:
 drupal.system:
   version: VERSION
   js:
-    system.js: {}
+    js/system.js: {}
   dependencies:
     - core/jquery
     - core/drupal
@@ -39,7 +39,7 @@ drupal.system:
 drupal.system.modules:
   version: VERSION
   js:
-    system.modules.js: {}
+    js/system.modules.js: {}
   dependencies:
     - core/jquery
     - core/drupal
@@ -50,3 +50,12 @@ diff:
   css:
     component:
       css/system.diff.css: {}
+
+drupal.system.date:
+  version: VERSION
+  js:
+    js/system.date.js: {}
+  dependencies:
+    - core/jquery
+    - core/jquery.once
+    - core/drupal
