summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoreffulgentsia2016-04-05 22:01:34 (GMT)
committereffulgentsia2016-04-05 22:01:34 (GMT)
commit517efbde7856dd78b52a3f41e763db539be61c09 (patch)
tree9bd25da7164476a103e3558954401dd491628c6d
parentf3f3800fe4f6dd27784caf9fb0302d46a5351b8d (diff)
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
-rw-r--r--core/lib/Drupal/Core/Annotation/PluralTranslation.php110
-rw-r--r--core/lib/Drupal/Core/Entity/EntityType.php58
-rw-r--r--core/lib/Drupal/Core/Entity/EntityTypeInterface.php27
-rw-r--r--core/modules/node/src/Entity/Node.php6
-rw-r--r--core/modules/system/src/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php5
-rw-r--r--core/modules/system/src/Tests/Plugin/Discovery/CustomDirectoryAnnotatedClassDiscoveryTest.php5
-rw-r--r--core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Banana.php6
-rw-r--r--core/tests/Drupal/Tests/Core/Annotation/PluralTranslationTest.php71
-rw-r--r--core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php60
-rw-r--r--core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php4
10 files changed, 350 insertions, 2 deletions
diff --git a/core/lib/Drupal/Core/Annotation/PluralTranslation.php b/core/lib/Drupal/Core/Annotation/PluralTranslation.php
new file mode 100644
index 0000000..ba60d87
--- /dev/null
+++ b/core/lib/Drupal/Core/Annotation/PluralTranslation.php
@@ -0,0 +1,110 @@
+<?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,
+ ];
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/EntityType.php b/core/lib/Drupal/Core/Entity/EntityType.php
index a7d1134..2fd5984 100644
--- a/core/lib/Drupal/Core/Entity/EntityType.php
+++ b/core/lib/Drupal/Core/Entity/EntityType.php
@@ -10,6 +10,7 @@ namespace Drupal\Core\Entity;
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.
@@ -186,6 +187,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.
*
* @var callable|null
@@ -290,7 +314,6 @@ class EntityType implements EntityTypeInterface {
if (empty($this->list_cache_tags)) {
$this->list_cache_tags = [$definition['id'] . '_list'];
}
-
}
/**
@@ -723,6 +746,39 @@ class EntityType implements EntityTypeInterface {
/**
* {@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}
+ */
public function getUriCallback() {
return $this->uri_callback;
}
diff --git a/core/lib/Drupal/Core/Entity/EntityTypeInterface.php b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
index 1120eff..f2d693a 100644
--- a/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
@@ -631,6 +631,33 @@ interface EntityTypeInterface extends PluginDefinitionInterface {
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.
*
* This is only called if there is no matching link template for the link
diff --git a/core/modules/node/src/Entity/Node.php b/core/modules/node/src/Entity/Node.php
index 7b20a3b..090ddd9 100644
--- a/core/modules/node/src/Entity/Node.php
+++ b/core/modules/node/src/Entity/Node.php
@@ -22,6 +22,12 @@ use Drupal\user\UserInterface;
* @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",
diff --git a/core/modules/system/src/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php b/core/modules/system/src/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php
index d038dbf..d79db4d 100644
--- a/core/modules/system/src/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php
+++ b/core/modules/system/src/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php
@@ -32,6 +32,11 @@ class AnnotatedClassDiscoveryTest extends DiscoveryTestBase {
'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',
diff --git a/core/modules/system/src/Tests/Plugin/Discovery/CustomDirectoryAnnotatedClassDiscoveryTest.php b/core/modules/system/src/Tests/Plugin/Discovery/CustomDirectoryAnnotatedClassDiscoveryTest.php
index 39e7caf..2d33070 100644
--- a/core/modules/system/src/Tests/Plugin/Discovery/CustomDirectoryAnnotatedClassDiscoveryTest.php
+++ b/core/modules/system/src/Tests/Plugin/Discovery/CustomDirectoryAnnotatedClassDiscoveryTest.php
@@ -46,6 +46,11 @@ class CustomDirectoryAnnotatedClassDiscoveryTest extends DiscoveryTestBase {
'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',
diff --git a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Banana.php b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Banana.php
index 5e4d180..467db5f 100644
--- a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Banana.php
+++ b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Banana.php
@@ -13,7 +13,11 @@ namespace Drupal\plugin_test\Plugin\plugin_test\fruit;
* label = "Banana",
* color = "yellow",
* uses = {
- * "bread" = @Translation("Banana bread")
+ * "bread" = @Translation("Banana bread"),
+ * "loaf" = @PluralTranslation(
+ * singular = "@count loaf",
+ * plural = "@count loaves"
+ * )
* }
* )
*/
diff --git a/core/tests/Drupal/Tests/Core/Annotation/PluralTranslationTest.php b/core/tests/Drupal/Tests/Core/Annotation/PluralTranslationTest.php
new file mode 100644
index 0000000..e62c9b8
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Annotation/PluralTranslationTest.php
@@ -0,0 +1,71 @@
+<?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;
+ }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php
index 41dccee..b650ddc 100644
--- a/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php
@@ -315,6 +315,66 @@ class EntityTypeTest extends UnitTestCase {
}
/**
+ * @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.
*
* @return string
diff --git a/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php b/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php
index 180fd88..dab993a 100644
--- a/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php
@@ -49,6 +49,10 @@ class DefaultPluginManagerTest extends UnitTestCase {
'color' => 'yellow',
'uses' => array(
'bread' => 'Banana bread',
+ 'loaf' => array(
+ 'singular' => '@count loaf',
+ 'plural' => '@count loaves',
+ ),
),
'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\Banana',
),