diff --git a/core/config/schema/core.entity.schema.yml b/core/config/schema/core.entity.schema.yml
index bf0e12d..406118a 100644
--- a/core/config/schema/core.entity.schema.yml
+++ b/core/config/schema/core.entity.schema.yml
@@ -356,3 +356,19 @@ field.formatter.settings.entity_reference_label:
       type: boolean
       label: 'Link label to the referenced entity'
 
+bundle:
+  type: config_entity
+  label: 'Bundle'
+  mapping:
+    label_singular:
+      type: label
+      label: 'Indefinite singular name'
+    label_plural:
+      type: label
+      label: 'Indefinite plural name'
+    label_count:
+      type: sequence
+      label: 'Count label'
+      sequence:
+        type: string
+        label: 'Plural variant'
diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBundleBase.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBundleBase.php
index dc17e05..131af79 100644
--- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBundleBase.php
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBundleBase.php
@@ -2,8 +2,12 @@
 
 namespace Drupal\Core\Config\Entity;
 
+use Drupal\Component\Render\FormattableMarkup;
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Config\ConfigNameException;
 use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\StringTranslation\PluralTranslatableMarkup;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 
 /**
  * A base class for config entity types that act as bundles.
@@ -11,7 +15,28 @@
  * Entity types that want to use this base class must use bundle_of in their
  * annotation to specify for which entity type they are providing bundles for.
  */
-abstract class ConfigEntityBundleBase extends ConfigEntityBase {
+abstract class ConfigEntityBundleBase extends ConfigEntityBase implements ConfigEntityBundleInterface {
+
+  /**
+   * The indefinite singular name of the bundle.
+   */
+  protected $label_singular;
+
+  /**
+   * The indefinite plural name of the bundle.
+   *
+   * @var string
+   */
+  protected $label_plural;
+
+  /**
+   * A definite singular/plural count label.
+   *
+   * The label is an array having the plural variants as items.
+   *
+   * @var string[]
+   */
+  protected $label_count = [];
 
   /**
    * Deletes display if a bundle is deleted.
@@ -66,6 +91,39 @@ public static function postDelete(EntityStorageInterface $storage, array $entiti
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function getSingularLabel() {
+    return $this->label_singular = $this->label_singular ?: Unicode::strtolower($this->label());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPluralLabel() {
+    if (empty($this->label_plural)) {
+      $arguments = ['@label' => Unicode::strtolower($this->label())];
+      $this->label_plural = new TranslatableMarkup('@label items', $arguments);
+    }
+    return $this->label_plural;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCountLabel($count) {
+    $index = $this->getPluralIndex($count);
+    if (isset($this->label_count[$index])) {
+      return new FormattableMarkup($this->label_count[$index], ['@count' => $count]);
+    }
+    $arguments = [
+      '@label_singular' => $this->getSingularLabel(),
+      '@label_plural' => $this->getPluralLabel(),
+    ];
+    return new PluralTranslatableMarkup($count, '1 @label_singular', '@count @label_plural', $arguments);
+  }
+
+  /**
    * Acts on an entity before the presave hook is invoked.
    *
    * Used before the entity is saved and before invoking the presave hook.
@@ -112,4 +170,31 @@ protected function loadDisplays($entity_type_id) {
     return array();
   }
 
+  /**
+   * Gets the plural index through the gettext formula.
+   *
+   * @param int $count
+   *   Number to return plural for.
+   *
+   * @return int
+   *   The numeric index of the plural variant to use for this $langcode and
+   *   $count combination or -1 if the language was not found or does not have a
+   *   plural formula.
+   *
+   * @todo Inspired by PluralTranslatableMarkup::getPluralIndex(). Move it to a
+   *   utility class or make it public static on PluralTranslatableMarkup.
+   */
+  protected function getPluralIndex($count) {
+    // We have to test both if the function and the service exist since in
+    // certain situations it is possible that locale code might be loaded but
+    // the service does not exist. For example, where the parent test site has
+    // locale installed but the child site does not.
+    // @todo Refactor in https://www.drupal.org/node/2660338 so this code does
+    // not depend on knowing that the Locale module exists.
+    if (function_exists('locale_get_plural') && \Drupal::hasService('locale.plural.formula')) {
+      return locale_get_plural($count);
+    }
+    return -1;
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBundleInterface.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBundleInterface.php
new file mode 100644
index 0000000..f13751b
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBundleInterface.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace Drupal\Core\Config\Entity;
+
+/**
+ * Provides an interface for bundle configuration entities.
+ */
+interface ConfigEntityBundleInterface {
+
+  /**
+   * Gets the singular label of the bundle.
+   *
+   * @return string
+   *   The singular label.
+   */
+  public function getSingularLabel();
+
+  /**
+   * Gets the plural label of the bundle.
+   *
+   * @return string
+   *   The plural label.
+   */
+  public function getPluralLabel();
+
+  /**
+   * Gets the count label of the bundle.
+   *
+   * @param int $count
+   *   The item count to display if the plural form was requested.
+   *
+   * @return string
+   *   The count label.
+   */
+  public function getCountLabel($count);
+
+}
diff --git a/core/modules/node/config/schema/node.schema.yml b/core/modules/node/config/schema/node.schema.yml
index 84e4d12..4cc1b1f 100644
--- a/core/modules/node/config/schema/node.schema.yml
+++ b/core/modules/node/config/schema/node.schema.yml
@@ -9,7 +9,7 @@ node.settings:
       label: 'Use admin theme when editing or creating content'
 
 node.type.*:
-  type: config_entity
+  type: bundle
   label: 'Content type'
   mapping:
     name:
diff --git a/core/modules/node/src/NodeTypeForm.php b/core/modules/node/src/NodeTypeForm.php
index d5fb998..0d92b92 100644
--- a/core/modules/node/src/NodeTypeForm.php
+++ b/core/modules/node/src/NodeTypeForm.php
@@ -46,7 +46,8 @@ public static function create(ContainerInterface $container) {
   public function form(array $form, FormStateInterface $form_state) {
     $form = parent::form($form, $form_state);
 
-    $type = $this->entity;
+    /** @var \Drupal\node\NodeTypeInterface $type */
+    $type = $this->getEntity();
     if ($this->operation == 'add') {
       $form['#title'] = $this->t('Add content type');
       $fields = $this->entityManager->getBaseFieldDefinitions('node');
@@ -182,6 +183,66 @@ public function form(array $form, FormStateInterface $form_state) {
       '#default_value' => $type->displaySubmitted(),
       '#description' => t('Author username and publish date will be displayed.'),
     );
+    $form['display']['plural'] = [
+      '#type' => 'fieldset',
+      '#title' => $this->t('Label variants'),
+      '#description' => $this->t('These are alternatives to the node type label for singular and plural cases.'),
+    ];
+    $form['display']['plural']['label_singular'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Singular label'),
+      '#description' => $this->t("Enter a lowercase label for the singular case. Examples: 'article', 'page'."),
+      '#default_value' => $type->getSingularLabel(),
+    ];
+    $form['display']['plural']['label_plural'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Plural label'),
+      '#description' => $this->t("Enter a lowercase label for the plural case. Examples: 'articles', 'pages'."),
+      '#default_value' => $type->getPluralLabel(),
+    ];
+
+    if (\Drupal::hasService('locale.plural.formula')) {
+      $langcode = \Drupal::languageManager()->getCurrentLanguage()->getId();
+      $plurals = \Drupal::service('locale.plural.formula')->getNumberOfPlurals($langcode);
+    }
+    else {
+      // We assume 2 plurals if Locale's services are not available.
+      $plurals = 2;
+    }
+    $label_count = $type->get('label_count');
+
+    $form['display']['label_count'] = [
+      '#type' => 'fieldset',
+      '#title' => $this->t('Count labels'),
+      '#description' => $this->t('Token <code>@count</code> is available.'),
+      '#tree' => TRUE,
+    ];
+    for ($i = 0; $i < $plurals; $i++) {
+      if ($i == 0) {
+        $title = $this->t('Singular');
+      }
+      elseif ($plurals == 2 && $i == 1) {
+        $title = $this->t('Plural');
+      }
+      else {
+        $title = $this->t('Plural variant #@variant', ['@variant' => $i]);
+      }
+      if (isset($label_count[$i])) {
+        $default_value = $label_count[$i];
+      }
+      else {
+        $arguments = [
+          '@label_singular' => $type->getSingularLabel(),
+          '@label_plural' => $type->getPluralLabel(),
+        ];
+        $default_value = $i == 0 ? $this->t('1 @label_singular', $arguments) : $this->t('@count @label_plural', $arguments);
+      }
+      $form['display']['label_count'][$i] = [
+        '#type' => 'textfield',
+        '#title' => $title,
+        '#default_value' => $default_value,
+      ];
+    }
 
     return $this->protectBundleIdElement($form);
   }
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 738b52e..b33e043 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -9,6 +9,7 @@
 use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Asset\AttachedAssetsInterface;
 use Drupal\Core\Cache\Cache;
+use Drupal\Core\Config\Entity\ConfigEntityBundleInterface;
 use Drupal\Core\Queue\QueueGarbageCollectionInterface;
 use Drupal\Core\Database\Query\AlterableInterface;
 use Drupal\Core\Extension\Extension;
@@ -1409,6 +1410,22 @@ function system_entity_type_build(array &$entity_types) {
 }
 
 /**
+ * Implements hook_entity_type_alter().
+ */
+function system_entity_type_alter(array &$entity_types) {
+  /** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */
+  foreach ($entity_types as $entity_type_id => &$entity_type) {
+    if (is_subclass_of($entity_type->getClass(), ConfigEntityBundleInterface::class)) {
+      $config_export = $entity_type->get('config_export');
+      $config_export[] = 'label_singular';
+      $config_export[] = 'label_plural';
+      $config_export[] = 'label_count';
+      $entity_type->set('config_export', $config_export);
+    }
+  }
+}
+
+/**
  * Implements hook_block_view_BASE_BLOCK_ID_alter().
  */
 function system_block_view_system_main_block_alter(array &$build, BlockPluginInterface $block) {
diff --git a/core/profiles/standard/config/install/node.type.article.yml b/core/profiles/standard/config/install/node.type.article.yml
index 1fd439c..2a13b53 100644
--- a/core/profiles/standard/config/install/node.type.article.yml
+++ b/core/profiles/standard/config/install/node.type.article.yml
@@ -8,3 +8,8 @@ help: ''
 new_revision: true
 preview_mode: 1
 display_submitted: true
+label_singular: article
+label_plural: articles
+label_count:
+  - '1 article'
+  - '@count articles'
diff --git a/core/profiles/standard/config/install/node.type.page.yml b/core/profiles/standard/config/install/node.type.page.yml
index 57dcc0c..60f4101 100644
--- a/core/profiles/standard/config/install/node.type.page.yml
+++ b/core/profiles/standard/config/install/node.type.page.yml
@@ -8,3 +8,8 @@ help: ''
 new_revision: true
 preview_mode: 1
 display_submitted: false
+label_singular: page
+label_plural: pages
+label_count:
+  - '1 page'
+  - '@count pages'
