Skip to content
Snippets Groups Projects
Commit 1215e51e authored by Alex Bronstein's avatar Alex Bronstein
Browse files

Issue #1850080 by amateescu, dawehner, Xano, bojanz, martin107, Pancho,...

Issue #1850080 by amateescu, dawehner, Xano, bojanz, martin107, Pancho, alexpott, tim.plunkett, icseh., stefank, Gábor Hojtsy, jhodgdon, tstoeckler: Entity type labels lack plurality, cannot generate UI text based on label if plural is needed

(cherry picked from commit 517efbde)
parent 3389d32e
No related branches found
No related tags found
No related merge requests found
Showing
with 350 additions and 2 deletions
<?php
/**
* @file
* Contains \Drupal\Core\Annotation\PluralTranslation.
*/
namespace Drupal\Core\Annotation;
use Drupal\Component\Annotation\AnnotationBase;
/**
* Defines an annotation object for strings that require plural forms.
*
* Note that the return values for both 'singular' and 'plural' keys needs to be
* passed to
* \Drupal\Core\StringTranslation\TranslationInterface::formatPlural().
*
* For example, the annotation can look like this:
* @code
* label_count = @ PluralTranslation(
* singular = "@count item",
* plural = "@count items",
* context = "cart_items",
* ),
* @endcode
* Remove spaces after @ in your actual plugin - these are put into this sample
* code so that it is not recognized as annotation.
*
* Code samples that make use of this annotation class and the definition sample
* above:
* @code
* // Returns: 1 item
* $entity_type->getCountLabel(1);
*
* // Returns: 5 items
* $entity_type->getCountLabel(5);
* @endcode
*
* @see \Drupal\Core\Entity\EntityType::getSingularLabel()
* @see \Drupal\Core\Entity\EntityType::getPluralLabel()
* @see \Drupal\Core\Entity\EntityType::getCountLabel()
*
* @ingroup plugin_translatable
*
* @Annotation
*/
class PluralTranslation extends AnnotationBase {
/**
* The string for the singular case.
*
* @var string
*/
protected $singular;
/**
* The string for the plural case.
*
* @var string
*/
protected $plural;
/**
* The context the source strings belong to.
*
* @var string
*/
protected $context;
/**
* Constructs a new class instance.
*
* @param array $values
* An associative array with the following keys:
* - singular: The string for the singular case.
* - plural: The string for the plural case.
* - context: The context the source strings belong to.
*
* @throws \InvalidArgumentException
* Thrown when the keys 'singular' or 'plural' are missing from the $values
* array.
*/
public function __construct(array $values) {
if (!isset($values['singular'])) {
throw new \InvalidArgumentException('Missing "singular" value in the PluralTranslation annotation');
}
if (!isset($values['plural'])) {
throw new \InvalidArgumentException('Missing "plural" value in the PluralTranslation annotation');
}
$this->singular = $values['singular'];
$this->plural = $values['plural'];
if (isset($values['context'])) {
$this->context = $values['context'];
}
}
/**
* {@inheritdoc}
*/
public function get() {
return [
'singular' => $this->singular,
'plural' => $this->plural,
'context' => $this->context,
];
}
}
......@@ -10,6 +10,7 @@
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Entity\Exception\EntityTypeIdLengthException;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslatableMarkup;
/**
* Provides an implementation of an entity type and its metadata.
......@@ -185,6 +186,29 @@ class EntityType implements EntityTypeInterface {
*/
protected $label = '';
/**
* The indefinite singular name of the type.
*
* @var string
*/
protected $label_singular = '';
/**
* The indefinite plural name of the type.
*
* @var string
*/
protected $label_plural = '';
/**
* A definite singular/plural name of the type.
*
* Needed keys: "singular" and "plural".
*
* @var string[]
*/
protected $label_count = [];
/**
* A callable that can be used to provide the entity URI.
*
......@@ -290,7 +314,6 @@ public function __construct($definition) {
if (empty($this->list_cache_tags)) {
$this->list_cache_tags = [$definition['id'] . '_list'];
}
}
/**
......@@ -720,6 +743,39 @@ public function getLowercaseLabel() {
return Unicode::strtolower($this->getLabel());
}
/**
* {@inheritdoc}
*/
public function getSingularLabel() {
if (empty($this->label_singular)) {
$lowercase_label = $this->getLowercaseLabel();
$this->label_singular = $lowercase_label;
}
return $this->label_singular;
}
/**
* {@inheritdoc}
*/
public function getPluralLabel() {
if (empty($this->label_plural)) {
$lowercase_label = $this->getLowercaseLabel();
$this->label_plural = new TranslatableMarkup('@label entities', ['@label' => $lowercase_label], [], $this->getStringTranslation());
}
return $this->label_plural;
}
/**
* {@inheritdoc}
*/
public function getCountLabel($count) {
if (empty($this->label_count)) {
return $this->formatPlural($count, '@count @label', '@count @label entities', ['@label' => $this->getLowercaseLabel()], ['context' => 'Entity type label']);
}
$context = isset($this->label_count['context']) ? $this->label_count['context'] : 'Entity type label';
return $this->formatPlural($count, $this->label_count['singular'], $this->label_count['plural'], ['context' => $context]);
}
/**
* {@inheritdoc}
*/
......
......@@ -630,6 +630,33 @@ public function getLabel();
*/
public function getLowercaseLabel();
/**
* Gets the singular label of the entity type.
*
* @return string
* The singular label.
*/
public function getSingularLabel();
/**
* Gets the plural label of the entity type.
*
* @return string
* The plural label.
*/
public function getPluralLabel();
/**
* Gets the count label of the entity type
*
* @param int $count
* The item count to display if the plural form was requested.
*
* @return string
* The count label.
*/
public function getCountLabel($count);
/**
* Gets a callable that can be used to provide the entity URI.
*
......
......@@ -22,6 +22,12 @@
* @ContentEntityType(
* id = "node",
* label = @Translation("Content"),
* label_singular = @Translation("content item"),
* label_plural = @Translation("content items"),
* label_count = @PluralTranslation(
* singular = "@count content item",
* plural = "@count content items"
* ),
* bundle_label = @Translation("Content type"),
* handlers = {
* "storage" = "Drupal\node\NodeStorage",
......
......@@ -32,6 +32,11 @@ protected function setUp() {
'color' => 'yellow',
'uses' => array(
'bread' => t('Banana bread'),
'loaf' => array(
'singular' => '@count loaf',
'plural' => '@count loaves',
'context' => NULL,
),
),
'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\Banana',
'provider' => 'plugin_test',
......
......@@ -46,6 +46,11 @@ protected function setUp() {
'color' => 'yellow',
'uses' => array(
'bread' => t('Banana bread'),
'loaf' => array(
'singular' => '@count loaf',
'plural' => '@count loaves',
'context' => NULL,
),
),
'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\Banana',
'provider' => 'plugin_test',
......
......@@ -13,7 +13,11 @@
* label = "Banana",
* color = "yellow",
* uses = {
* "bread" = @Translation("Banana bread")
* "bread" = @Translation("Banana bread"),
* "loaf" = @PluralTranslation(
* singular = "@count loaf",
* plural = "@count loaves"
* )
* }
* )
*/
......
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Annotation\PluralTranslationTest.
*/
namespace Drupal\Tests\Core\Annotation;
use Drupal\Core\Annotation\PluralTranslation;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\Annotation\PluralTranslation
* @group Annotation
*/
class PluralTranslationTest extends UnitTestCase {
/**
* @covers ::get
*
* @dataProvider providerTestGet
*/
public function testGet(array $values) {
$annotation = new PluralTranslation($values);
$default_values = [
'context' => NULL,
];
$this->assertEquals($values + $default_values, $annotation->get());
}
/**
* Provides data to self::testGet().
*/
public function providerTestGet() {
$data = [];
$data[] = [
[
'singular' => $this->randomMachineName(),
'plural' => $this->randomMachineName(),
'context' => $this->randomMachineName(),
],
];
$data[] = [
[
'singular' => $this->randomMachineName(),
'plural' => $this->randomMachineName(),
],
];
return $data;
}
/**
* @dataProvider providerTestMissingData
*/
public function testMissingData($data) {
$this->setExpectedException(\InvalidArgumentException::class);
new PluralTranslation($data);
}
public function providerTestMissingData() {
$data = [];
$data['all-missing'] = [[]];
$data['singular-missing'] = [['plural' => 'muh']];
$data['plural-missing'] = [['singular' => 'muh']];
return $data;
}
}
......@@ -314,6 +314,66 @@ public function testGetGroupLabel() {
$this->assertSame($default_label, $entity_type->getGroupLabel());
}
/**
* @covers ::getSingularLabel
*/
public function testGetSingularLabel() {
$translatable_label = new TranslatableMarkup('entity test singular', [], [], $this->getStringTranslationStub());
$entity_type = $this->setUpEntityType(['label_singular' => $translatable_label]);
$entity_type->setStringTranslation($this->getStringTranslationStub());
$this->assertEquals('entity test singular', $entity_type->getSingularLabel());
}
/**
* @covers ::getSingularLabel
*/
public function testGetSingularLabelDefault() {
$entity_type = $this->setUpEntityType(['label' => 'Entity test Singular']);
$entity_type->setStringTranslation($this->getStringTranslationStub());
$this->assertEquals('entity test singular', $entity_type->getSingularLabel());
}
/**
* @covers ::getPluralLabel
*/
public function testGetPluralLabel() {
$translatable_label = new TranslatableMarkup('entity test plural', [], [], $this->getStringTranslationStub());
$entity_type = $this->setUpEntityType(['label_plural' => $translatable_label]);
$entity_type->setStringTranslation($this->getStringTranslationStub());
$this->assertEquals('entity test plural', $entity_type->getPluralLabel());
}
/**
* @covers ::getPluralLabel
*/
public function testGetPluralLabelDefault() {
$entity_type = $this->setUpEntityType(['label' => 'Entity test Plural']);
$entity_type->setStringTranslation($this->getStringTranslationStub());
$this->assertEquals('entity test plural entities', $entity_type->getPluralLabel());
}
/**
* @covers ::getCountLabel
*/
public function testGetCountLabel() {
$entity_type = $this->setUpEntityType(['label_count' => ['singular' => 'one entity test', 'plural' => '@count entity test']]);
$entity_type->setStringTranslation($this->getStringTranslationStub());
$this->assertEquals('one entity test', $entity_type->getCountLabel(1));
$this->assertEquals('2 entity test', $entity_type->getCountLabel(2));
$this->assertEquals('200 entity test', $entity_type->getCountLabel(200));
}
/**
* @covers ::getCountLabel
*/
public function testGetCountLabelDefault() {
$entity_type = $this->setUpEntityType(['label' => 'Entity test Plural']);
$entity_type->setStringTranslation($this->getStringTranslationStub());
$this->assertEquals('1 entity test plural', $entity_type->getCountLabel(1));
$this->assertEquals('2 entity test plural entities', $entity_type->getCountLabel(2));
$this->assertEquals('200 entity test plural entities', $entity_type->getCountLabel(200));
}
/**
* Gets a mock controller class name.
*
......
......@@ -49,6 +49,10 @@ protected function setUp() {
'color' => 'yellow',
'uses' => array(
'bread' => 'Banana bread',
'loaf' => array(
'singular' => '@count loaf',
'plural' => '@count loaves',
),
),
'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\Banana',
),
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment