diff --git a/core/lib/Drupal/Core/Config/Entity/EntityBundleWithPluralLabelsInterface.php b/core/lib/Drupal/Core/Config/Entity/EntityBundleWithPluralLabelsInterface.php index 8eb3bce418..d75644c623 100644 --- a/core/lib/Drupal/Core/Config/Entity/EntityBundleWithPluralLabelsInterface.php +++ b/core/lib/Drupal/Core/Config/Entity/EntityBundleWithPluralLabelsInterface.php @@ -8,18 +8,18 @@ interface EntityBundleWithPluralLabelsInterface { /** - * Gets the singular label of the bundle. + * Returns the singular label of the bundle. * * @return string|null - * The singular label or NULL if it hasn't been set. + * The singular label or NULL if it cannot be computed. */ public function getSingularLabel(); /** - * Gets the plural label of the bundle. + * Returns the plural label of the bundle. * * @return string|null - * The plural label or NULL if it hasn't been set. + * The singular label or NULL if it cannot be computed. */ public function getPluralLabel(); @@ -36,8 +36,8 @@ public function getPluralLabel(); * context if there's only one version of the definite singular/plural count * label. Defaults to NULL. * - * @return string - * The count label. + * @return string|null + * The count label or NULL if it cannot be computed. */ public function getCountLabel($count, $context = NULL); diff --git a/core/lib/Drupal/Core/Config/Entity/EntityBundleWithPluralLabelsTrait.php b/core/lib/Drupal/Core/Config/Entity/EntityBundleWithPluralLabelsTrait.php index aff03d6365..4f2e1d93bd 100644 --- a/core/lib/Drupal/Core/Config/Entity/EntityBundleWithPluralLabelsTrait.php +++ b/core/lib/Drupal/Core/Config/Entity/EntityBundleWithPluralLabelsTrait.php @@ -85,6 +85,7 @@ public function getPluralLabel() { * {@inheritdoc} */ public function getCountLabel($count, $context = NULL) { + $result = NULL; $label_count_versions = (array) $this->label_count; // If the context was not passed, pickup the first version of count label. @@ -98,13 +99,13 @@ public function getCountLabel($count, $context = NULL) { $label_count = empty($label_count_versions[$context]) ? [] : explode(PluralTranslatableMarkup::DELIMITER, $label_count_versions[$context]); if (!empty($label_count[$index])) { - return new FormattableMarkup($label_count[$index], ['@count' => $count]); + $result = new FormattableMarkup($label_count[$index], ['@count' => $count]); } - $arguments = [ - '@singular' => $this->getSingularLabel(), - '@plural' => $this->getPluralLabel(), - ]; - return new PluralTranslatableMarkup($count, '1 @singular', '@count @plural', $arguments); + elseif (($singular = $this->getSingularLabel()) && ($plural = $this->getPluralLabel())) { + $arguments = ['@singular' => $singular, '@plural' => $plural]; + $result = new PluralTranslatableMarkup($count, '1 @singular', '@count @plural', $arguments); + } + return $result; } /** diff --git a/core/modules/node/src/NodeTypeForm.php b/core/modules/node/src/NodeTypeForm.php index 3544cd9026..9eec40c700 100644 --- a/core/modules/node/src/NodeTypeForm.php +++ b/core/modules/node/src/NodeTypeForm.php @@ -6,7 +6,6 @@ use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\StringTranslation\PluralTranslatableMarkup; use Drupal\language\Entity\ContentLanguageSettings; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -185,39 +184,6 @@ 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('Singular and plural labels'), - '#description' => $this->t('These are lowercase 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(), - ]; - $arguments = ['@plural' => $type->getPluralLabel() ?: $this->t('items')]; - $form['display']['label_count'] = [ - '#type' => 'fieldset', - '#title' => $this->t('Count labels'), - '#description' => $this->t('Count labels are used to build a text representation of a certain number of @plural. The @count placeholder is available and will be replaced with the number of @plural.', $arguments), - '#tree' => TRUE, - ]; - $plurals = $this->getNumberOfPlurals($type->language()->getId()); - for ($i = 0; $i < $plurals; $i++) { - $form['display']['label_count'][0][$i] = [ - '#type' => 'textfield', - '#title' => $this->getPluralVariantLabel($plurals, $i), - '#description' => $this->t('Text to use for this variant, @count will be replaced with the number of @plural.', $arguments), - '#default_value' => $this->getPluralVariantDefaultValue($i), - ]; - } return $this->protectBundleIdElement($form); } @@ -243,13 +209,6 @@ public function validateForm(array &$form, FormStateInterface $form_state) { if ($id == '0') { $form_state->setErrorByName('type', $this->t("Invalid machine-readable name. Enter a name other than %invalid.", ['%invalid' => $id])); } - - // Pack the label_count values as a plural_label. - $label_count = []; - foreach ($form_state->getValue('label_count') as $plural_variants) { - $label_count[] = implode(PluralTranslatableMarkup::DELIMITER, $plural_variants); - } - $form_state->setValue('label_count', $label_count); } /** @@ -297,63 +256,4 @@ public function save(array $form, FormStateInterface $form_state) { $form_state->setRedirectUrl($type->urlInfo('collection')); } - /** - * Returns a plural variant label given the variant delta and variants count. - * - * @param int $plurals - * The total number of plural variants. - * @param int $delta - * The plural variant delta. - * - * @return \Drupal\Component\Render\MarkupInterface - * The variant label. - */ - protected function getPluralVariantLabel($plurals, $delta) { - if ($delta == 0) { - return $this->t('Singular form'); - } - elseif ($plurals == 2 && $delta == 1) { - return $this->t('Plural form'); - } - return $this->formatPlural($delta, 'First plural form', '@count. plural form'); - } - - /** - * Retrieves a plural variant from the backend or assures a decent fallback. - * - * @param int $delta - * The plural variant delta. - * - * @return \Drupal\Component\Render\MarkupInterface|string - * The plural variant default value. - */ - protected function getPluralVariantDefaultValue($delta) { - static $label_count; - - /** @var \Drupal\node\NodeTypeInterface $type */ - $type = $this->getEntity(); - - if (!isset($label_count)) { - $label_count = isset($type->get('label_count')[0]) ? explode(PluralTranslatableMarkup::DELIMITER, $type->get('label_count')[0]) : []; - } - - if (isset($label_count[$delta])) { - // A value was stored in the backend. - return $label_count[$delta]; - } - - $singular = $type->getSingularLabel(); - $plural = $type->getPluralLabel(); - - if ($type->isNew() || ($delta == 0 && empty($singular) || ($delta > 0 && empty($plural)))) { - // Either the node type is not yet saved (we don't even know the entity - // label) or we cannot assure a decent fallback. - return ''; - } - - // Provide a fallback default value. - $arguments = ['@singular' => $singular, '@plural' => $plural]; - return $delta == 0 ? $this->t('1 @singular', $arguments) : $this->t('@count @plural', $arguments); - } - } diff --git a/core/modules/node/src/Tests/NodeTypeTest.php b/core/modules/node/src/Tests/NodeTypeTest.php index e501eabd8c..70d3ac40b5 100644 --- a/core/modules/node/src/Tests/NodeTypeTest.php +++ b/core/modules/node/src/Tests/NodeTypeTest.php @@ -256,42 +256,4 @@ public function testNodeTypeNoContentType() { $this->assertEqual(0, count($bundle_info->getBundleInfo('node')), 'The bundle information service has 0 bundles for the Node entity type.'); } - /** - * Tests singular/plural labels. - */ - public function testPluralLabels() { - $account = $this->drupalCreateUser(['administer content types', 'administer node fields']); - $this->drupalLogin($account); - - $edit = [ - 'name' => 'Mouse', - 'type' => 'mouse', - ]; - $this->drupalPostForm('admin/structure/types/add', $edit, 'Save and manage fields'); - - $type = NodeType::load('mouse'); - - // Test fallback values. - $this->assertEqual($type->getSingularLabel(), 'mouse'); - $this->assertEqual($type->getPluralLabel(), 'mouse items'); - $this->assertEqual($type->getCountLabel(1), '1 mouse'); - $this->assertEqual($type->getCountLabel(5), '5 mouse items'); - - $edit = [ - 'label_singular' => 'mouse', - 'label_plural' => 'mice', - 'label_count[0][0]' => '1 mouse', - 'label_count[0][1]' => '@count mice', - ]; - $this->drupalPostForm('admin/structure/types/manage/mouse', $edit, 'Save content type'); - - $type = NodeType::load('mouse'); - - // Test user entered values. - $this->assertEqual($type->getSingularLabel(), 'mouse'); - $this->assertEqual($type->getPluralLabel(), 'mice'); - $this->assertEqual($type->getCountLabel(1), '1 mouse'); - $this->assertEqual($type->getCountLabel(5), '5 mice'); - } - } diff --git a/core/modules/node/tests/src/Functional/NodeTypePluralLabelsTranslationTest.php b/core/modules/node/tests/src/Functional/NodeTypePluralLabelsTranslationTest.php deleted file mode 100644 index 56258e44ac..0000000000 --- a/core/modules/node/tests/src/Functional/NodeTypePluralLabelsTranslationTest.php +++ /dev/null @@ -1,136 +0,0 @@ -save(); - - // Create and log in user. - $account = $this->drupalCreateUser([ - 'administer content types', - 'administer node fields', - 'translate configuration', - ]); - $this->drupalLogin($account); - - /** @var \Drupal\locale\PluralFormulaInterface $plural_formula */ - $plural_formula = $this->container->get('locale.plural.formula'); - - // Workaround to register the Romanian and English language formulae in - // state. Without this, the number of plurals is not correctly parsed. - $po_header = new PoHeader('ro'); - $plural = $po_header->parsePluralForms('nplurals=3; plural=((n==1)?(0):(((n==0)||(((n%100)>0)&&((n%100)<20)))?(1):2));\n'); - $plural_formula->setPluralFormula('ro', $plural[0], $plural[1]); - $plural = $po_header->parsePluralForms('nplurals=2; plural=(n > 1);\n'); - $plural_formula->setPluralFormula('en', $plural[0], $plural[1]); - } - - /** - * {@inheritdoc} - */ - protected function installParameters() { - $parameters = parent::installParameters(); - // Install the site in Romanian language. - $parameters['parameters']['langcode'] = 'ro'; - return $parameters; - } - - /** - * Tests the node type translation. - */ - public function testNodeTypeTranslation() { - /** @var \Drupal\Core\Language\LanguageManagerInterface $language_manager */ - $language_manager = $this->container->get('language_manager'); - - $this->drupalGet('admin/structure/types/add'); - - // Check that 3 plural variants are exposed for the Romanian language. - $this->assertSession()->fieldExists('label_count[0][0]'); - $this->assertSession()->fieldExists('label_count[0][1]'); - $this->assertSession()->fieldExists('label_count[0][2]'); - $this->assertSession()->fieldNotExists('label_count[0][3]'); - - $edit = [ - 'name' => 'Copil', - 'type' => 'child', - 'label_singular' => 'copil', - 'label_plural' => 'copii', - 'label_count[0][0]' => '1 copil', - 'label_count[0][1]' => '@count copii', - 'label_count[0][2]' => '@count de copii', - ]; - $this->drupalPostForm(NULL, $edit, 'Save and manage fields'); - - $edit = [ - 'translation[config_names][node.type.child][name]' => 'Child', - 'translation[config_names][node.type.child][label_singular]' => 'child', - 'translation[config_names][node.type.child][label_plural]' => 'children', - 'translation[config_names][node.type.child][label_count][0][0]' => '1 child', - 'translation[config_names][node.type.child][label_count][0][1]' => '@count children', - ]; - $this->drupalPostForm('admin/structure/types/manage/child/translate/en/add', $edit, 'Save translation'); - - /** @var \Drupal\node\NodeTypeInterface $node_type */ - $node_type = NodeType::load('child'); - - // Check that the default language (Romanian) original labels were stored. - self::assertEquals($node_type->label(), 'Copil'); - self::assertEquals($node_type->getSingularLabel(), 'copil'); - self::assertEquals($node_type->getPluralLabel(), 'copii'); - self::assertEquals($node_type->getCountLabel(1), '1 copil'); - self::assertEquals($node_type->getCountLabel(5), '5 copii'); - self::assertEquals($node_type->getCountLabel(20), '20 de copii'); - - // Load the English version of the 'child' node-type. - $original_language = $language_manager->getConfigOverrideLanguage(); - $language_manager->setConfigOverrideLanguage(ConfigurableLanguage::load('en')); - /** @var \Drupal\node\NodeTypeInterface $node_type */ - $node_type = NodeType::load('child'); - $language_manager->setConfigOverrideLanguage($original_language); - - // Check that the English translated labels were stored. - self::assertEquals($node_type->label(), 'Child'); - self::assertEquals($node_type->getSingularLabel(), 'child'); - self::assertEquals($node_type->getPluralLabel(), 'children'); - self::assertEquals($node_type->getCountLabel(1), '1 child'); - self::assertEquals($node_type->getCountLabel(5), '5 children'); - self::assertEquals($node_type->getCountLabel(20), '20 children'); - - // Clear the count label second variant to test variant delta preserving. - $edit = ['label_count[0][1]' => '']; - $this->drupalPostForm($node_type->toUrl('edit-form'), $edit, 'Save content type'); - - $node_type = NodeType::load('child'); - - // Check that the variants delta has been preserved. - $variants = explode(PluralTranslatableMarkup::DELIMITER, $node_type->get('label_count')[0]); - self::assertEquals($variants[0], '1 copil'); - self::assertEquals($variants[1], ''); - self::assertEquals($variants[2], '@count de copii'); - } - -} diff --git a/core/modules/node/tests/src/Unit/NodeTypePluralLabelTest.php b/core/modules/node/tests/src/Unit/NodeTypePluralLabelTest.php new file mode 100644 index 0000000000..0ca811fede --- /dev/null +++ b/core/modules/node/tests/src/Unit/NodeTypePluralLabelTest.php @@ -0,0 +1,238 @@ +set('string_translation', $this->getStringTranslationStub()); + \Drupal::setContainer($container); + + $this->language = $this->getMockBuilder(LanguageInterface::class) + ->disableOriginalConstructor() + ->getMock() + ->expects($this->any()) + ->method('getId') + ->will($this->returnValue('en')); + } + + /** + * @covers ::getSingularLabel + * @covers ::getPluralLabel + * @dataProvider providerForTestGetSingularAndPLuralLabel + * + * @param string|null $entity_label + * The entity label. + * @param string|null $singular_label + * The singular label. + * @param string|null $expected_singular + * The expected singular label. + * @param string|null $plural_label + * The plural label. + * @param string|null $expected_plural + * The expected plural label. + */ + public function testGetSingularAndPLuralLabel($entity_label, $singular_label, $expected_singular, $plural_label, $expected_plural) { + $bundle_entity_mock = new TestingBundleMock($entity_label, $singular_label, $plural_label, NULL); + $this->assertEquals($expected_singular, $bundle_entity_mock->getSingularLabel()); + $this->assertEquals($expected_plural, $bundle_entity_mock->getPluralLabel()); + } + + /** + * Provides test cases for self::testGetSingularAndPLuralLabel(). + * + * @return array + * Test cases. + */ + public function providerForTestGetSingularAndPLuralLabel() { + return [ + // No singular/plural labels and a fallback cannot be build. + 'no labels' => [NULL, NULL, NULL, NULL, NULL], + // No singular/plural labels but a fallback label could be build. + 'entity label only' => ['Eye', NULL, 'eye', NULL, 'eye items'], + // No singular label but a fallback singular label could be build. + 'no singular label' => ['Eye', NULL, 'eye', 'eyes', 'eyes'], + // No plural label but a fallback plural label could be build. + 'no plural label' => ['Eye', 'eye', 'eye', NULL, 'eye items'], + // Singular and plural labels were provided. + 'singular/plural labels' => ['Eye', 'eye', 'eye', 'eyes', 'eyes'], + ]; + } + + /** + * @covers ::getCountLabel + * @dataProvider providerForTestGetCountLabel + * + * @param string[]|null $count_labels + * The count label array. + * @param string|null $entity_label + * The entity label. + * @param string|null $singular_label + * The singular label. + * @param string|null $plural_label + * The plural label. + * @param array $expectation + * An associative array keyed where each value is the expected result given + * a count integer which is the item key. + */ + public function testGetCountLabel(array $count_labels = NULL, $entity_label, $singular_label, $plural_label, array $expectation) { + $count_labels = $count_labels ?: [NULL]; + $bundle_entity_mock = new TestingBundleMock($entity_label, $singular_label, $plural_label, $count_labels); + foreach ($count_labels as $context => $count_label) { + foreach ($expectation[$context] as $count => $expected) { + // Count label doesn't have a context. + if (!$context) { + $this->assertEquals($expected, $bundle_entity_mock->getCountLabel($count)); + } + // Multiple contextualised count labels. + else { + $this->assertEquals($expected, $bundle_entity_mock->getCountLabel($count, $context)); + } + } + } + } + + /** + * Provides test cases for self::testGetCountLabel(). + * + * @return array + * Test cases. + */ + public function providerForTestGetCountLabel() { + return [ + // No singular/plural labels and a fallback cannot be build. + 'no labels' => [NULL, NULL, NULL, NULL, [[1 => NULL, 2 => NULL]]], + // The entity label is used to create fallback singular & plural labels + // and these are used to create the count label fallback. + 'entity label only' => [ + NULL, + 'Eye', + NULL, + NULL, + [[1 => '1 eye', 2 => '2 eye items']], + ], + // In there's no count label set and one of singular or plural is missed, + // it's not possible to create a count fallback label. + 'singular label only' => [ + NULL, + NULL, + 'eye', + NULL, + [[1 => NULL, 2 => NULL]], + ], + 'only singular/plural labels' => [ + NULL, + NULL, + 'blue eye', + 'blue eyes', + [[1 => '1 blue eye', 2 => '2 blue eyes']], + ], + 'count label' => [ + [ + "1 blue eye\x3@count blue eyes", + ], + NULL, + NULL, + NULL, + [[1 => '1 blue eye', 2 => '2 blue eyes']], + ], + // This count label lacks the singular variant. + 'broken count label, no singular variant' => [ + [ + "\x3@count blue eyes", + ], + NULL, + NULL, + NULL, + [[1 => NULL, 2 => '2 blue eyes']], + ], + // This count label lacks the plural variant but is able to compute a + // fallback from the entity label. + 'broken count label, no plural variant but with entity label' => [ + [ + "1 blue eye", + ], + 'Eye', + NULL, + NULL, + [[1 => '1 blue eye', 2 => '2 eye items']], + ], + // This count label lacks the plural variant but is able to compute a + // fallback from the singular label. + 'broken count label, no plural variant but with singular label' => [ + [ + "1 blue eye", + ], + 'Eye', + 'blue eye', + 'blue eyes', + [[1 => '1 blue eye', 2 => '2 blue eyes']], + ], + // Multiple count labels. + 'contextualized count labels' => [ + [ + 'default' => "1 blue eye\x3@count blue eyes", + 'no count' => "blue eye\x3" . 'blue eyes', + 'with markup' => "1 blue eye\x3@count blue eyes", + 'with parenthesis' => "blue eye\x3" . 'blue eyes (@count)', + ], + NULL, + NULL, + NULL, + [ + 'default' => [1 => '1 blue eye', 2 => '2 blue eyes'], + 'no count' => [1 => 'blue eye', 2 => 'blue eyes'], + 'with markup' => [1 => '1 blue eye', 2 => '2 blue eyes'], + 'with parenthesis' => [1 => 'blue eye', 2 => 'blue eyes (2)'], + ], + ], + ]; + } + +} + +// @codingStandardsIgnoreStart + +/** + * Mocks a bundle config entity class that uses the tested trait. + */ +class TestingBundleMock implements EntityBundleWithPluralLabelsInterface { + + use EntityBundleWithPluralLabelsTrait; + + protected $label; + + public function __construct($entity_label, $singular_label, $plural_label, $count_label) { + $this->label = $entity_label; + $this->label_singular = $singular_label; + $this->label_plural = $plural_label; + $this->label_count = $count_label; + } + + public function label() { return $this->label; } + public function language() { return new Language(); } +} + +// @codingStandardsIgnoreEnd