diff --git a/core/config/schema/core.data_types.schema.yml b/core/config/schema/core.data_types.schema.yml
index 3edb8057..7b658e0 100644
--- a/core/config/schema/core.data_types.schema.yml
+++ b/core/config/schema/core.data_types.schema.yml
@@ -650,12 +650,9 @@ field.field_settings.integer:
     max:
       type: integer
       label: 'Maximum'
-    prefix:
+    format_plural_string:
       type: label
-      label: 'Prefix'
-    suffix:
-      type: label
-      label: 'Suffix'
+      label: 'Plural formatted prefix/suffix'
 
 field.value.integer:
   type: mapping
@@ -688,12 +685,9 @@ field.field_settings.decimal:
     max:
       type: float
       label: 'Maximum'
-    prefix:
-      type: label
-      label: 'Prefix'
-    suffix:
+    format_plural_string:
       type: label
-      label: 'Suffix'
+      label: 'Plural formatted prefix/suffix'
 
 field.value.decimal:
    type: mapping
@@ -719,12 +713,9 @@ field.field_settings.float:
     max:
       type: float
       label: 'Maximum'
-    prefix:
-      type: label
-      label: 'Prefix'
-    suffix:
+    format_plural_string:
       type: label
-      label: 'Suffix'
+      label: 'Plural formatted prefix/suffix'
 
 field.value.float:
   type: mapping
diff --git a/core/config/schema/core.entity.schema.yml b/core/config/schema/core.entity.schema.yml
index 71b7ccd..984056f 100644
--- a/core/config/schema/core.entity.schema.yml
+++ b/core/config/schema/core.entity.schema.yml
@@ -202,6 +202,9 @@ field.widget.settings.number:
     placeholder:
       type: label
       label: 'Placeholder'
+    format_plural:
+      type: boolean
+      label: 'Whether or not to display prefix/suffix'
 
 field.widget.settings.checkbox:
   type: mapping
@@ -292,9 +295,12 @@ field.formatter.settings.number_decimal:
     scale:
       type: integer
       label: 'Scale'
-    prefix_suffix:
-      type: boolean
-      label: 'Display prefix and suffix.'
+    format_plural:
+      type: string
+      label: 'Which prefix/suffix to display, if any'
+    format_plural_string:
+      type: label
+      label: 'Override for prefix/suffix'
 
 field.formatter.settings.number_integer:
   type: mapping
@@ -303,9 +309,12 @@ field.formatter.settings.number_integer:
     thousand_separator:
       type: string
       label: 'Thousand marker'
-    prefix_suffix:
-      type: boolean
-      label: 'Display prefix and suffix.'
+    format_plural:
+      type: string
+      label: 'Which prefix/suffix to display, if any'
+    format_plural_string:
+      type: label
+      label: 'Override for prefix/suffix'
 
 field.formatter.settings.number_unformatted:
   type: mapping
diff --git a/core/lib/Drupal/Core/Entity/Entity/EntityFormDisplay.php b/core/lib/Drupal/Core/Entity/Entity/EntityFormDisplay.php
index bee0144..9760f29 100644
--- a/core/lib/Drupal/Core/Entity/Entity/EntityFormDisplay.php
+++ b/core/lib/Drupal/Core/Entity/Entity/EntityFormDisplay.php
@@ -141,6 +141,11 @@ public function getRenderer($field_name) {
 
     // Instantiate the widget object from the stored display properties.
     if (($configuration = $this->getComponent($field_name)) && isset($configuration['type']) && ($definition = $this->getFieldDefinition($field_name))) {
+      // Pass the language of the display configuration down to the widget,
+      // which otherwise doesn't have access to it because it is not a
+      // stand-alone config entity. It may be relevant for some operations,
+      // such as using plural formatting as a field prefix.
+      $configuration['settings']['_settings_langcode'] = $this->langcode;
       $widget = $this->pluginManager->getInstance(array(
         'field_definition' => $definition,
         'form_mode' => $this->originalMode,
diff --git a/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php b/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php
index 27884cd..f7d1f3c 100644
--- a/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php
+++ b/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php
@@ -197,6 +197,11 @@ public function getRenderer($field_name) {
 
     // Instantiate the formatter object from the stored display properties.
     if (($configuration = $this->getComponent($field_name)) && isset($configuration['type']) && ($definition = $this->getFieldDefinition($field_name))) {
+      // Pass the language of the display configuration down to the formatter,
+      // which otherwise doesn't have access to it because it is not a
+      // stand-alone config entity. It may be relevant for some operations,
+      // such as plural formatting.
+      $configuration['settings']['_settings_langcode'] = $this->langcode;
       $formatter = $this->pluginManager->getInstance(array(
         'field_definition' => $definition,
         'view_mode' => $this->originalMode,
diff --git a/core/lib/Drupal/Core/Entity/EntityDisplayBase.php b/core/lib/Drupal/Core/Entity/EntityDisplayBase.php
index b835327..5b374b8 100644
--- a/core/lib/Drupal/Core/Entity/EntityDisplayBase.php
+++ b/core/lib/Drupal/Core/Entity/EntityDisplayBase.php
@@ -292,6 +292,11 @@ public function toArray() {
       if (!$definition->isDisplayConfigurable($this->displayContext)) {
         unset($properties['content'][$field_name]);
         unset($properties['hidden'][$field_name]);
+        // If _settings_langcode is in the settings, remove it. It should never
+        // be saved in the configuration.
+        if (isset($properties['content'][$field_name]['settings']['_settings_langcode'])) {
+          unset($properties['content'][$field_name]['settings']['_settings_langcode']);
+        }
       }
     }
 
@@ -339,6 +344,12 @@ public function setComponent($name, array $options = array()) {
     // Ensure we always have an empty settings and array.
     $options += ['settings' => [], 'third_party_settings' => []];
 
+    // If _settings_langcode is in the settings, remove it. It should never
+    // be saved in the configuration.
+    if (isset($options['settings']['_settings_langcode'])) {
+      unset($options['settings']['_settings_langcode']);
+    }
+
     $this->content[$name] = $options;
     unset($this->hidden[$name]);
     unset($this->plugins[$name]);
diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
index d212474..12091df 100644
--- a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
+++ b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
@@ -13,6 +13,7 @@
 use Drupal\Core\Field\FieldItemInterface;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\Language\LanguageInterface;
 use Drupal\Core\Render\Element;
 use Drupal\Core\TypedData\TranslatableInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -453,10 +454,21 @@ protected function getSingleFieldDisplay($entity, $field_name, $display_options)
       $bundle = $entity->bundle();
       $key = $entity_type_id . ':' . $bundle . ':' . $field_name . ':' . hash('crc32b', serialize($display_options));
       if (!isset($this->singleFieldDisplays[$key])) {
+        // Assign the incoming '_settings_langcode' as the langcode of the
+        // EntityViewDisplay, or default to the current interface language.
+        if (isset($display_options['settings']['_settings_langcode'])) {
+          $langcode = $display_options['settings']['_settings_langcode'];
+          unset($display_options['settings']['_settings_langcode']);
+        }
+        else {
+          $langcode = $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_INTERFACE)->getId();
+        }
+
         $this->singleFieldDisplays[$key] = EntityViewDisplay::create(array(
           'targetEntityType' => $entity_type_id,
           'bundle' => $bundle,
           'status' => TRUE,
+          'langcode' => $langcode,
         ))->setComponent($field_name, $display_options);
       }
       $display = $this->singleFieldDisplays[$key];
diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php b/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php
index eff6516..42f8248 100644
--- a/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php
@@ -118,7 +118,11 @@ public function resetCache(array $entities = NULL);
    *       'default_formatter' for the field type. The default formatter will
    *       also be used if the requested formatter is not available.
    *     - settings: (array) Settings specific to the formatter. Defaults to the
-   *       formatter's default settings.
+   *       formatter's default settings. A '_settings_langcode' entry should be
+   *       present to indicate the language of the settings that are being
+   *       provided (typically, the language of the configuration entity that
+   *       contains the formatter, such as an entity view mode). If absent, the
+   *       current interface language will be assumed.
    *     - weight: (float) The weight to assign to the renderable element.
    *       Defaults to 0.
    *
diff --git a/core/lib/Drupal/Core/Field/FormatterPluginManager.php b/core/lib/Drupal/Core/Field/FormatterPluginManager.php
index e6054a9..9136c13 100644
--- a/core/lib/Drupal/Core/Field/FormatterPluginManager.php
+++ b/core/lib/Drupal/Core/Field/FormatterPluginManager.php
@@ -73,7 +73,7 @@ public function createInstance($plugin_id, array $configuration = array()) {
   }
 
   /**
-   * Overrides PluginManagerBase::getInstance().
+   * Creates a formatter instance with the provided options.
    *
    * @param array $options
    *   An array with the following key/value pairs:
@@ -92,6 +92,11 @@ public function createInstance($plugin_id, array $configuration = array()) {
    *       also be used if the requested formatter is not available.
    *     - settings: (array) Settings specific to the formatter. Each setting
    *       defaults to the default value specified in the formatter definition.
+   *       A '_settings_langcode' entry should be present to indicate the
+   *       language of the settings that are being provided (typically, the
+   *       language of the configuration entity that contains the formatter,
+   *       such as an entity view mode). If absent, the current interface
+   *       language will be assumed.
    *     - third_party_settings: (array) Settings provided by other extensions
    *       through hook_field_formatter_third_party_settings_form().
    *
@@ -116,7 +121,7 @@ public function getInstance(array $options) {
     // - the formatter is not applicable to the field definition.
     $definition = $this->getDefinition($configuration['type'], FALSE);
     if (!isset($definition['class']) || !in_array($field_type, $definition['field_types']) || !$definition['class']::isApplicable($field_definition)) {
-      // Grab the default widget for the field type.
+      // Grab the default formatter for the field type.
       $field_type_definition = $this->fieldTypeManager->getDefinition($field_type);
       if (empty($field_type_definition['default_formatter'])) {
         return NULL;
@@ -128,6 +133,7 @@ public function getInstance(array $options) {
       'field_definition' => $field_definition,
       'view_mode' => $options['view_mode'],
     );
+
     return $this->createInstance($plugin_id, $configuration);
   }
 
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/DecimalFormatter.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/DecimalFormatter.php
index 0e43ac1..7c9a17b 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/DecimalFormatter.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/DecimalFormatter.php
@@ -32,10 +32,8 @@ class DecimalFormatter extends NumericFormatterBase {
    */
   public static function defaultSettings() {
     return array(
-      'thousand_separator' => '',
       'decimal_separator' => '.',
       'scale' => 2,
-      'prefix_suffix' => TRUE,
     ) + parent::defaultSettings();
   }
 
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/IntegerFormatter.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/IntegerFormatter.php
index 8768474..20e5d4b 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/IntegerFormatter.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/IntegerFormatter.php
@@ -27,16 +27,6 @@ class IntegerFormatter extends NumericFormatterBase {
   /**
    * {@inheritdoc}
    */
-  public static function defaultSettings() {
-    return array(
-      'thousand_separator' => '',
-      'prefix_suffix' => TRUE,
-    ) + parent::defaultSettings();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   protected function numberFormat($number) {
     return number_format($number, 0, '', $this->getSetting('thousand_separator'));
   }
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/NumericFormatterBase.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/NumericFormatterBase.php
index 28b0159..6b3d81e 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/NumericFormatterBase.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/NumericFormatterBase.php
@@ -11,6 +11,7 @@
 use Drupal\Core\Field\FormatterBase;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\PluralTranslatableMarkup;
 
 /**
  * Parent plugin for decimal and integer formatters.
@@ -20,6 +21,32 @@
   use AllowedTagsXssTrait;
 
   /**
+   * Indicates plural formatting using the setting from the field.
+   */
+  const FORMAT_PLURAL_USE_FIELD_SETTING = 'field';
+
+  /**
+   * Indicates plural formatting using the setting from the formatter.
+   */
+  const FORMAT_PLURAL_USE_FORMATTER_SETTING = 'formatter';
+
+  /**
+   * Indicates no plural formatting.
+   */
+  const FORMAT_PLURAL_NONE = 'none';
+
+   /**
+    * {@inheritdoc}
+    */
+  public static function defaultSettings() {
+    return array(
+      'thousand_separator' => '',
+      'format_plural' => NumericFormatterBase::FORMAT_PLURAL_NONE,
+      'format_plural_string' => '',
+    ) + parent::defaultSettings();
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function settingsForm(array $form, FormStateInterface $form_state) {
@@ -39,16 +66,80 @@ public function settingsForm(array $form, FormStateInterface $form_state) {
       '#weight' => 0,
     );
 
-    $elements['prefix_suffix'] = array(
-      '#type' => 'checkbox',
-      '#title' => t('Display prefix and suffix'),
-      '#default_value' => $this->getSetting('prefix_suffix'),
-      '#weight' => 10,
+    $elements['format_plural'] = array(
+      '#type' => 'radios',
+      '#title' => $this->t('Display prefix/suffix'),
+      '#default_value' => $this->getSetting('format_plural'),
+      '#options' => array(
+        NumericFormatterBase::FORMAT_PLURAL_NONE => $this->t('No prefix/suffix'),
+        NumericFormatterBase::FORMAT_PLURAL_USE_FIELD_SETTING => $this->t('Use setting from the field settings'),
+        NumericFormatterBase::FORMAT_PLURAL_USE_FORMATTER_SETTING => $this->t('Override the setting from the field settings'),
+      ),
+    );
+
+    // @todo This field needs to have some kind of #states magic so that it
+    // only shows if the option on format_plural is
+    // NumericFormatterBase::FORMAT_PLURAL_USE_FORMATTER_SETTING.
+    // Need to do this before this patch is finalized!
+    $elements['format_plural_values'] = array(
+      '#type' => 'fieldset',
+      '#title' => $this->t('Prefix/suffix override'),
+      '#after_build' => [[get_class($this), 'formatPluralValuesAfterBuild']],
     );
 
+    $langcode = $this->getSetting('_settings_langcode');
+    $plurals = $this->getNumberOfPlurals($langcode);
+    $labels = $this->getPluralLabels($langcode);
+    $plural_string = $this->getSetting('format_plural_string');
+    if (!$plural_string) {
+      $settings = $this->getFieldSettings();
+      if (isset($settings['format_plural_string'])) {
+        $plural_string = $settings['format_plural_string'];
+      }
+    }
+    $plural_array = explode(LOCALE_PLURAL_DELIMITER, $plural_string);
+    for ($i = 0; $i < $plurals; $i++) {
+      $elements['format_plural_values'][$i] = array(
+        '#type' => 'textfield',
+        '#title' => $labels[$i]['label'],
+        '#description' => $labels[$i]['description'],
+        '#default_value' => isset($plural_array[$i]) ? $plural_array[$i] : '',
+      );
+    }
+
     return $elements;
   }
 
+ /**
+   * After-build callback: Sets the value for the format_plural_string property.
+   *
+   * This method is assigned as an #after_build function in the settings form.
+   *
+   * The user interface for setting up singular/plural forms is an array of text
+   * fields, for usability, but for translation purposes, the values have to be
+   * stored as a single string in the field settings. This function implodes the
+   * values.
+   *
+   * @param array $element
+   *   Textfield element where the plural format should be set.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   *
+   * @return array
+   *   Textfield element with plural string set.
+   */
+  public static function formatPluralValuesAfterBuild($element, FormStateInterface $form_state) {
+    if ($form_state->isProcessingInput()) {
+      $keys = $element['#parents'];
+      $array_value = $form_state->getValue($keys);
+      $string_value = implode(LOCALE_PLURAL_DELIMITER, $array_value);
+      array_pop($keys);
+      $keys[] = 'format_plural_string';
+      $form_state->setValue($keys, $string_value);
+    }
+    return $element;
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -56,8 +147,11 @@ public function settingsSummary() {
     $summary = array();
 
     $summary[] = $this->numberFormat(1234.1234567890);
-    if ($this->getSetting('prefix_suffix')) {
-      $summary[] = t('Display with prefix and suffix.');
+    if ($this->getSetting('format_plural') == NumericFormatterBase::FORMAT_PLURAL_USE_FIELD_SETTING) {
+      $summary[] = $this->t('Display prefix/suffix from the field settings.');
+    }
+    elseif ($this->getSetting('format_plural') == NumericFormatterBase::FORMAT_PLURAL_USE_FORMATTER_SETTING) {
+      $summary[] = $this->t('Display overridden prefix/suffix.');
     }
 
     return $summary;
@@ -68,19 +162,24 @@ public function settingsSummary() {
    */
   public function viewElements(FieldItemListInterface $items, $langcode) {
     $elements = array();
-    $settings = $this->getFieldSettings();
+    $field_settings = $this->getFieldSettings();
 
     foreach ($items as $delta => $item) {
       $output = $this->numberFormat($item->value);
 
       // Account for prefix and suffix.
-      if ($this->getSetting('prefix_suffix')) {
-        $prefixes = isset($settings['prefix']) ? array_map(array('Drupal\Core\Field\FieldFilteredMarkup', 'create'), explode('|', $settings['prefix'])) : array('');
-        $suffixes = isset($settings['suffix']) ? array_map(array('Drupal\Core\Field\FieldFilteredMarkup', 'create'), explode('|', $settings['suffix'])) : array('');
-        $prefix = (count($prefixes) > 1) ? $this->formatPlural($item->value, $prefixes[0], $prefixes[1]) : $prefixes[0];
-        $suffix = (count($suffixes) > 1) ? $this->formatPlural($item->value, $suffixes[0], $suffixes[1]) : $suffixes[0];
-        $output = $prefix . $output . $suffix;
+      $format_string = '';
+      if ($this->getSetting('format_plural') == NumericFormatterBase::FORMAT_PLURAL_USE_FIELD_SETTING) {
+        $format_string = $field_settings['format_plural_string'];
+      }
+      elseif ($this->getSetting('format_plural') == NumericFormatterBase::FORMAT_PLURAL_USE_FORMATTER_SETTING) {
+        $format_string = $this->getSetting('format_plural_string');
       }
+
+      if ($format_string) {
+        $output = PluralTranslatableMarkup::createFromTranslatedString($item->value, $format_string, ['@count' => $output], ['langcode' => $this->getSetting('_settings_langcode')]);
+      }
+
       // Output the raw value in a content attribute if the text of the HTML
       // element differs from the raw value (for example when a prefix is used).
       if (isset($item->_attributes) && $item->value != $output) {
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php
index 78d5d9e..2c3377a 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php
@@ -40,18 +40,6 @@ public static function defaultStorageSettings() {
   /**
    * {@inheritdoc}
    */
-  public static function defaultFieldSettings() {
-    return array(
-      'min' => '',
-      'max' => '',
-      'prefix' => '',
-      'suffix' => '',
-    ) + parent::defaultFieldSettings();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
     $properties['value'] = DataDefinition::create('integer')
       ->setLabel(t('Integer value'))
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/NumericItemBase.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/NumericItemBase.php
index 9f9bd0b..96d2617 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/NumericItemBase.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/NumericItemBase.php
@@ -22,8 +22,7 @@ public static function defaultFieldSettings() {
     return array(
       'min' => '',
       'max' => '',
-      'prefix' => '',
-      'suffix' => '',
+      'format_plural_string' => '',
     ) + parent::defaultFieldSettings();
   }
 
@@ -36,31 +35,65 @@ public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
 
     $element['min'] = array(
       '#type' => 'number',
-      '#title' => t('Minimum'),
+      '#title' => $this->t('Minimum'),
       '#default_value' => $settings['min'],
-      '#description' => t('The minimum value that should be allowed in this field. Leave blank for no minimum.'),
+      '#description' => $this->t('The minimum value that should be allowed in this field. Leave blank for no minimum.'),
     );
     $element['max'] = array(
       '#type' => 'number',
-      '#title' => t('Maximum'),
+      '#title' => $this->t('Maximum'),
       '#default_value' => $settings['max'],
-      '#description' => t('The maximum value that should be allowed in this field. Leave blank for no maximum.'),
+      '#description' => $this->t('The maximum value that should be allowed in this field. Leave blank for no maximum.'),
     );
-    $element['prefix'] = array(
-      '#type' => 'textfield',
-      '#title' => t('Prefix'),
-      '#default_value' => $settings['prefix'],
-      '#size' => 60,
-      '#description' => t("Define a string that should be prefixed to the value, like '$ ' or '&euro; '. Leave blank for none. Separate singular and plural values with a pipe ('pound|pounds')."),
-    );
-    $element['suffix'] = array(
-      '#type' => 'textfield',
-      '#title' => t('Suffix'),
-      '#default_value' => $settings['suffix'],
-      '#size' => 60,
-      '#description' => t("Define a string that should be suffixed to the value, like ' m', ' kb/s'. Leave blank for none. Separate singular and plural values with a pipe ('pound|pounds')."),
+
+    $element['format_plural_values'] = array(
+      '#type' => 'fieldset',
+      '#title' => $this->t('Plural formatted prefix/suffix'),
+      '#after_build' => [[get_class($this), 'formatPluralValuesAfterBuild']],
     );
+    $langcode = $this->getFieldDefinition()->get('langcode');
+    $plurals = $this->getNumberOfPlurals($langcode);
+    $labels = $this->getPluralLabels($langcode);
+    $plural_array = explode(LOCALE_PLURAL_DELIMITER, $settings['format_plural_string']);
+    for ($i = 0; $i < $plurals; $i++) {
+      $element['format_plural_values'][$i] = array(
+        '#type' => 'textfield',
+        '#title' => $labels[$i]['label'],
+        '#description' => $labels[$i]['description'],
+        '#default_value' => isset($plural_array[$i]) ? $plural_array[$i] : '',
+      );
+    }
+
+    return $element;
+  }
 
+ /**
+   * After-build callback: Sets the value for the format_plural_string property.
+   *
+   * This method is assigned as an #after_build function in the settings form.
+   *
+   * The user interface for setting up singular/plural forms is an array of text
+   * fields, for usability, but for translation purposes, the values have to be
+   * stored as a single string in the field settings. This function implodes the
+   * values.
+   *
+   * @param array $element
+   *   Textfield element where the plural format should be set.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   *
+   * @return array
+   *   Textfield element with plural string set.
+   */
+  public static function formatPluralValuesAfterBuild($element, FormStateInterface $form_state) {
+    if ($form_state->isProcessingInput()) {
+      $keys = $element['#parents'];
+      $array_value = $form_state->getValue($keys);
+      $string_value = implode(LOCALE_PLURAL_DELIMITER, $array_value);
+      array_pop($keys);
+      $keys[] = 'format_plural_string';
+      $form_state->setValue($keys, $string_value);
+    }
     return $element;
   }
 
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/NumberWidget.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/NumberWidget.php
index 57abc60..f7f6420 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/NumberWidget.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/NumberWidget.php
@@ -34,6 +34,7 @@ class NumberWidget extends WidgetBase {
   public static function defaultSettings() {
     return array(
       'placeholder' => '',
+      'format_plural' => TRUE,
     ) + parent::defaultSettings();
   }
 
@@ -47,6 +48,14 @@ public function settingsForm(array $form, FormStateInterface $form_state) {
       '#default_value' => $this->getSetting('placeholder'),
       '#description' => t('Text that will be shown inside the field until a value is entered. This hint is usually a sample value or a brief description of the expected format.'),
     );
+
+    $element['format_plural'] = array(
+      '#type' => 'checkbox',
+      '#title' => $this->t('Display prefix/suffix'),
+      '#description' => $this->t('If checked, prefix/suffix from the field will be displayed.'),
+      '#default_value' => $this->getSetting('format_plural'),
+    );
+
     return $element;
   }
 
@@ -58,10 +67,14 @@ public function settingsSummary() {
 
     $placeholder = $this->getSetting('placeholder');
     if (!empty($placeholder)) {
-      $summary[] = t('Placeholder: @placeholder', array('@placeholder' => $placeholder));
+      $summary[] = $this->t('Placeholder: @placeholder', array('@placeholder' => $placeholder));
     }
     else {
-      $summary[] = t('No placeholder');
+      $summary[] = $this->t('No placeholder');
+    }
+
+    if ($this->getSetting('format_plural')) {
+      $summary[] = $this->t('Prefix/suffix from the field are displayed');
     }
 
     return $summary;
@@ -100,13 +113,19 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
     }
 
     // Add prefix and suffix.
-    if ($field_settings['prefix']) {
-      $prefixes = explode('|', $field_settings['prefix']);
-      $element['#field_prefix'] = FieldFilteredMarkup::create(array_pop($prefixes));
-    }
-    if ($field_settings['suffix']) {
-      $suffixes = explode('|', $field_settings['suffix']);
-      $element['#field_suffix'] = FieldFilteredMarkup::create(array_pop($suffixes));
+    if ($field_settings['format_plural_string'] && $this->getSetting('format_plural')) {
+      // The field setting is a string containing all plural variants, suitable
+      // for use in formatPlural() methods, but already translated into the
+      // UI language. Pop off the last plural variant, and use the parts
+      // before/after @count in that string as the prefix/suffix for this field.
+      $values = explode(LOCALE_PLURAL_DELIMITER, $field_settings['format_plural_string']);
+      $labels = explode('@count', array_pop($values));
+      if (isset($labels[0])) {
+        $element['#field_prefix'] = FieldFilteredMarkup::create(trim($labels[0]));
+      }
+      if (isset($labels[1])) {
+        $element['#field_suffix'] = FieldFilteredMarkup::create(trim($labels[1]));
+      }
     }
 
     return array('value' => $element);
diff --git a/core/lib/Drupal/Core/Field/WidgetPluginManager.php b/core/lib/Drupal/Core/Field/WidgetPluginManager.php
index 2bc41f1..510c685 100644
--- a/core/lib/Drupal/Core/Field/WidgetPluginManager.php
+++ b/core/lib/Drupal/Core/Field/WidgetPluginManager.php
@@ -55,7 +55,7 @@ public function __construct(\Traversable $namespaces, CacheBackendInterface $cac
   }
 
   /**
-   * Overrides PluginManagerBase::getInstance().
+   * Creates a widget instance with the provided options.
    *
    * @param array $options
    *   An array with the following key/value pairs:
@@ -71,6 +71,11 @@ public function __construct(\Traversable $namespaces, CacheBackendInterface $cac
    *       used if the requested widget is not available.
    *     - settings: (array) Settings specific to the widget. Each setting
    *       defaults to the default value specified in the widget definition.
+   *       A '_settings_langcode' entry should be present to indicate the
+   *       language of the settings that are being provided (typically, the
+   *       language of the configuration entity that contains the widget,
+   *       such as an entity form mode). If absent, the current interface
+   *       language will be assumed.
    *     - third_party_settings: (array) Settings provided by other extensions
    *       through hook_field_formatter_third_party_settings_form().
    *
diff --git a/core/lib/Drupal/Core/StringTranslation/PluralTranslatableMarkup.php b/core/lib/Drupal/Core/StringTranslation/PluralTranslatableMarkup.php
index 583ab18..4bcccd2 100644
--- a/core/lib/Drupal/Core/StringTranslation/PluralTranslatableMarkup.php
+++ b/core/lib/Drupal/Core/StringTranslation/PluralTranslatableMarkup.php
@@ -63,7 +63,9 @@ class PluralTranslatableMarkup extends TranslatableMarkup {
    *   See \Drupal\Component\Render\FormattableMarkup::placeholderFormat() for
    *   additional information about placeholders. Note that you do not need to
    *   include @count in this array; this replacement is done automatically
-   *   for the plural cases.
+   *   for the plural cases. But you can include @count to override the
+   *   replacement value (for instance, to use a formatted number instead of
+   *   the raw number).
    * @param array $options
    *   (optional) An associative array of additional options. See t() for
    *   allowed keys.
@@ -95,7 +97,9 @@ public function __construct($count, $singular, $plural, array $args = [], array
    *   Based on the first character of the key, the value is escaped and/or
    *   themed. See \Drupal\Component\Utility\SafeMarkup::format(). Note that you
    *   do not need to include @count in this array; this replacement is done
-   *   automatically for the plural cases.
+   *   automatically for the plural cases. But you can include @count to
+   *   override the replacement value (for instance, to use a formatted number
+   *   instead of the raw number).
    * @param array $options
    *   An associative array of additional options. See t() for allowed keys.
    *
@@ -123,7 +127,10 @@ public function render() {
     }
 
     $arguments = $this->getArguments();
-    $arguments['@count'] = $this->count;
+    // Allow overrides of what gets substituted in for @count.
+    if (!isset($arguments['@count'])) {
+      $arguments['@count'] = $this->count;
+    }
     $translated_array = explode(static::DELIMITER, $this->translatedString);
 
     if ($this->count == 1) {
diff --git a/core/lib/Drupal/Core/StringTranslation/StringTranslationTrait.php b/core/lib/Drupal/Core/StringTranslation/StringTranslationTrait.php
index f4b4bd3..cfe8167 100644
--- a/core/lib/Drupal/Core/StringTranslation/StringTranslationTrait.php
+++ b/core/lib/Drupal/Core/StringTranslation/StringTranslationTrait.php
@@ -84,6 +84,59 @@ protected function getNumberOfPlurals($langcode = NULL) {
   }
 
   /**
+   * Returns the labels and descriptions for plural variants in a language.
+   *
+   * @param string $langcode
+   *   The language code.
+   *
+   * @return array[]
+   *  An array of labels and descriptions to use in forms for the plural
+   *  variants of $langcode, translated into the current interface language.
+   *  Each item in the returned array is an array with elements 'label' and
+   *  'description' for the corresponding plural variant.
+   */
+  protected function getPluralLabels($langcode) {
+    $plurals = $this->getNumberOfPlurals($langcode);
+
+    // @todo The return values for both cases should be more relevant to the
+    // actual plural rules of the language; see
+    // https://www.drupal.org/node/2499639.
+
+    if ($plurals == 1) {
+      return [
+        [
+          'label' => $this->t('Form for all numbers'),
+          'description' => $this->t('Text to use for all numbers; @count will be replaced with the value.'),
+        ],
+      ];
+    }
+
+    if ($plurals == 2) {
+      return [
+        [
+          'label' => $this->t('Singular form'),
+          'description' => $this->t('Text to use for the singular form; @count will be replaced with the value.'),
+        ],
+        [
+          'label' => $this->t('Plural form'),
+          'description' => $this->t('Text to use for the plural form; @count will be replaced with the value.'),
+        ],
+      ];
+    }
+
+    // For the != 2 case, generate labels.
+    $labels = [];
+    for ($i = 0; $i < $plurals; $i++) {
+      $labels[] = [
+        'label' => ($i == 0 ? $this->t('Singular form') : $this->formatPlural($i, 'First plural form', '@count. plural form')),
+        'description' => $this->t('Text to use for this variant; @count will be replaced with the value.'),
+      ];
+    }
+
+    return $labels;
+  }
+
+  /**
    * Gets the string translation service.
    *
    * @return \Drupal\Core\StringTranslation\TranslationInterface
diff --git a/core/modules/field/src/Tests/Migrate/d6/MigrateFieldFormatterSettingsTest.php b/core/modules/field/src/Tests/Migrate/d6/MigrateFieldFormatterSettingsTest.php
index 435c83c..191629c 100644
--- a/core/modules/field/src/Tests/Migrate/d6/MigrateFieldFormatterSettingsTest.php
+++ b/core/modules/field/src/Tests/Migrate/d6/MigrateFieldFormatterSettingsTest.php
@@ -74,7 +74,8 @@ public function testEntityDisplaySettings() {
     $expected['type'] = 'number_integer';
     $expected['settings'] = array(
       'thousand_separator' => ',',
-      'prefix_suffix' => TRUE,
+      'format_plural' => 'none',
+      'format_plural_string' => '',
     );
     $component = $display->getComponent('field_test_two');
     $this->assertIdentical($expected, $component);
@@ -84,7 +85,8 @@ public function testEntityDisplaySettings() {
        'scale' => 2,
        'decimal_separator' => '.',
        'thousand_separator' => ',',
-       'prefix_suffix' => TRUE,
+       'format_plural' => 'none',
+       'format_plural_string' => '',
     );
     $component = $display->getComponent('field_test_three');
     $this->assertIdentical($expected, $component);
diff --git a/core/modules/field/src/Tests/Migrate/d6/MigrateFieldInstanceTest.php b/core/modules/field/src/Tests/Migrate/d6/MigrateFieldInstanceTest.php
index afc6703..f98dc8a 100644
--- a/core/modules/field/src/Tests/Migrate/d6/MigrateFieldInstanceTest.php
+++ b/core/modules/field/src/Tests/Migrate/d6/MigrateFieldInstanceTest.php
@@ -45,8 +45,7 @@ public function testFieldInstanceSettings() {
     $expected = array(
       'min' => 10,
       'max' => 100,
-      'prefix' => 'pref',
-      'suffix' => 'suf',
+      'format_plural_string' => 'pref@count suf',
       'unsigned' => FALSE,
       'size' => 'normal',
     );
@@ -57,8 +56,7 @@ public function testFieldInstanceSettings() {
     $expected = array(
       'min' => 100.0,
       'max' => 200.0,
-      'prefix' => 'id-',
-      'suffix' => '',
+      'format_plural_string' => 'id-@count';
     );
     $this->assertIdentical($expected, $field->getSettings());
 
diff --git a/core/modules/field/src/Tests/Migrate/d6/MigrateFieldWidgetSettingsTest.php b/core/modules/field/src/Tests/Migrate/d6/MigrateFieldWidgetSettingsTest.php
index 891eb69..4cfe61c 100644
--- a/core/modules/field/src/Tests/Migrate/d6/MigrateFieldWidgetSettingsTest.php
+++ b/core/modules/field/src/Tests/Migrate/d6/MigrateFieldWidgetSettingsTest.php
@@ -36,7 +36,7 @@ public function testWidgetSettings() {
     // Text field.
     $component = $form_display->getComponent('field_test');
     $expected = array('weight' => 1, 'type' => 'text_textfield');
-    $expected['settings'] = array('size' => 60, 'placeholder' => '');
+    $expected['settings'] = array('size' => 60, 'placeholder' => '', 'format_plural' => TRUE);
     $expected['third_party_settings'] = array();
     $this->assertIdentical($expected, $component, 'Text field settings are correct.');
 
@@ -44,7 +44,7 @@ public function testWidgetSettings() {
     $component = $form_display->getComponent('field_test_two');
     $expected['type'] = 'number';
     $expected['weight'] = 1;
-    $expected['settings'] = array('placeholder' => '');
+    $expected['settings'] = array('placeholder' => '', 'format_plural' => TRUE);
     $this->assertIdentical($expected, $component);
 
     // Float field.
@@ -82,7 +82,7 @@ public function testWidgetSettings() {
     $component = $form_display->getComponent('field_test_phone');
     $expected['type'] = 'telephone_default';
     $expected['weight'] = 13;
-    $expected['settings'] = array('placeholder' => '');
+    $expected['settings'] = array('placeholder' => '', 'format_plural' => TRUE);
     $this->assertIdentical($expected, $component);
 
     // Date fields.
diff --git a/core/modules/field_ui/src/Tests/EntityDisplayTest.php b/core/modules/field_ui/src/Tests/EntityDisplayTest.php
index 7c7a48b..91a526f 100644
--- a/core/modules/field_ui/src/Tests/EntityDisplayTest.php
+++ b/core/modules/field_ui/src/Tests/EntityDisplayTest.php
@@ -214,6 +214,7 @@ public function testFieldComponent() {
       'type' => $default_formatter,
       'settings' => $formatter_settings,
       'third_party_settings' => array(),
+      '_settings_langcode' => 'en',
     );
     $this->assertEqual($display->getComponent($field_name), $expected);
 
diff --git a/core/modules/node/tests/modules/node_test_views/test_views/views.view.test_nid_argument.yml b/core/modules/node/tests/modules/node_test_views/test_views/views.view.test_nid_argument.yml
index b61f1ad..c6b5b1c 100644
--- a/core/modules/node/tests/modules/node_test_views/test_views/views.view.test_nid_argument.yml
+++ b/core/modules/node/tests/modules/node_test_views/test_views/views.view.test_nid_argument.yml
@@ -106,7 +106,7 @@ display:
           type: number_integer
           settings:
             thousand_separator: ''
-            prefix_suffix: true
+            format_plural: 'field'
           group_column: value
           group_columns: {  }
           group_rows: true
diff --git a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_term_relationship.yml b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_term_relationship.yml
index 96d0559..a99eb9a 100644
--- a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_term_relationship.yml
+++ b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_term_relationship.yml
@@ -140,7 +140,7 @@ display:
           type: number_integer
           settings:
             thousand_separator: ''
-            prefix_suffix: true
+            format_plural: 'field'
           group_column: value
           group_columns: {  }
           group_rows: true
diff --git a/core/modules/views/src/Entity/Render/EntityFieldRenderer.php b/core/modules/views/src/Entity/Render/EntityFieldRenderer.php
index 9e94a63..0b63fd3 100644
--- a/core/modules/views/src/Entity/Render/EntityFieldRenderer.php
+++ b/core/modules/views/src/Entity/Render/EntityFieldRenderer.php
@@ -196,6 +196,7 @@ protected function buildFields(array $values) {
 
     if ($values && ($field_ids = $this->getRenderableFieldIds())) {
       $entity_type_id = $this->getEntityTypeId();
+      $settings_langcode = $this->view->storage->language()->getId();
 
       // Collect the entities for the relationship, fetch the right translation,
       // and group by bundle. For each result row, the corresponding entity can
@@ -232,6 +233,7 @@ protected function buildFields(array $values) {
             'targetEntityType' => $entity_type_id,
             'bundle' => $bundle,
             'status' => TRUE,
+            'langcode' => $settings_langcode,
           ]);
           foreach ($display_fields['field_ids'] as $field) {
             $display->setComponent($field->definition['field_name'], [
diff --git a/core/modules/views/src/Plugin/views/field/Field.php b/core/modules/views/src/Plugin/views/field/Field.php
index 33253d1..9d0b6ec 100644
--- a/core/modules/views/src/Plugin/views/field/Field.php
+++ b/core/modules/views/src/Plugin/views/field/Field.php
@@ -471,6 +471,8 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) {
     $format = $this->options['type'];
 
     $settings = $this->options['settings'] + $this->formatterPluginManager->getDefaultSettings($format);
+    $settings_langcode = $this->view->storage->language()->getId();
+    $settings['_settings_langcode'] = $settings_langcode;
 
     $options = array(
       'field_definition' => $field,
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache.yml
index ec19b13..c391c25 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache.yml
@@ -180,7 +180,7 @@ display:
           type: number_integer
           settings:
             thousand_separator: ''
-            prefix_suffix: true
+            format_plural: 'field'
           group_column: value
           group_columns: {  }
           group_rows: true
