diff --git a/core/lib/Drupal/Core/Plugin/Validation/Constraint/LengthConstraint.php b/core/lib/Drupal/Core/Plugin/Validation/Constraint/LengthConstraint.php new file mode 100644 index 0000000..5b23c98 --- /dev/null +++ b/core/lib/Drupal/Core/Plugin/Validation/Constraint/LengthConstraint.php @@ -0,0 +1,39 @@ +validator)) { $this->validator = Validation::createValidatorBuilder() ->setMetadataFactory(new MetadataFactory()) + ->setTranslator(new DrupalTranslator()) ->getValidator(); } return $this->validator; diff --git a/core/lib/Drupal/Core/Validation/ConstraintManager.php b/core/lib/Drupal/Core/Validation/ConstraintManager.php index 96c4306..dd3ece7 100644 --- a/core/lib/Drupal/Core/Validation/ConstraintManager.php +++ b/core/lib/Drupal/Core/Validation/ConstraintManager.php @@ -50,16 +50,6 @@ public function __construct() { $this->factory = new DefaultFactory($this->discovery); // Add in definitions for constraints shipped with Symfony. - $this->discovery->setDefinition('Range', array( - 'label' => t('Range'), - 'class' => '\Symfony\Component\Validator\Constraints\Range', - 'type' => array('integer', 'float'), - )); - $this->discovery->setDefinition('Length', array( - 'label' => t('Length'), - 'class' => '\Symfony\Component\Validator\Constraints\Length', - 'type' => 'string', - )); $this->discovery->setDefinition('Null', array( 'label' => t('Null'), 'class' => '\Symfony\Component\Validator\Constraints\Null', diff --git a/core/lib/Drupal/Core/Validation/DrupalTranslator.php b/core/lib/Drupal/Core/Validation/DrupalTranslator.php new file mode 100644 index 0000000..266d935 --- /dev/null +++ b/core/lib/Drupal/Core/Validation/DrupalTranslator.php @@ -0,0 +1,92 @@ +processParameters($parameters), $this->getOptions($domain, $locale)); + } + + /** + * Implements \Symfony\Component\Translation\TranslatorInterface::transChoice(). + */ + public function transChoice($id, $number, array $parameters = array(), $domain = NULL, $locale = NULL) { + // Violation messages can separated singular and plural versions by "|". + $ids = explode('|', $id); + + if (!isset($ids[1])) { + throw new InvalidArgumentException(sprintf('The message "%s" cannot be pluralized, because it is missing a plural (e.g. "There is one apple|There are @count apples").', $id)); + } + return format_plural($number, $ids[0], $ids[1], $this->processParameters($parameters), $this->getOptions($domain, $locale)); + } + + /** + * Implements \Symfony\Component\Translation\TranslatorInterface::setLocale(). + */ + public function setLocale($locale) { + $this->locale = $locale; + } + + /** + * Implements \Symfony\Component\Translation\TranslatorInterface::getLocale(). + */ + public function getLocale() { + return $this->locale ? $this->locale : language(LANGUAGE_TYPE_INTERFACE)->langcode; + } + + /** + * Processes the parameters array for use with t(). + */ + protected function processParameters(array $parameters) { + $return = array(); + foreach ($parameters as $key => $value) { + if (is_object($value)) { + // t() does not work will objects being passed as replacement strings. + } + // Check for symfony replacement patterns in the form "{{ name }}". + elseif (strpos($key, '{{ ') === 0 && strrpos($key, ' }}') == strlen($key) - 3) { + // Transform it into a Drupal pattern using the format %name. + $key = '%' . substr($key, 3, strlen($key) - 6); + $return[$key] = $value; + } + else { + $return[$key] = $value; + } + } + return $return; + } + + /** + * Returns options suitable for use with t(). + */ + protected function getOptions($domain = NULL, $locale = NULL) { + // We do not support domains, so we ignore this parameter. + // If locale is left NULL, t() will default to the interface language. + $locale = isset($locale) ? $locale : $this->locale; + return array('langcode' => $locale); + } +} diff --git a/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php b/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php index 3d2c256..61f2546 100644 --- a/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php @@ -214,16 +214,24 @@ public function testTypedDataValidation() { $violations = $integer->validate(); $this->assertEqual($violations->count(), 1); - $message = t('This value should be {{ limit }} or more.', array('{{ limit }}' => 5)); - // Right now there is no support for getting translated messages, so test - // doing it manually for now. - // @todo: Replace this to use the API for getting translations. - $translation = t($violations[0]->getMessageTemplate(), $violations[0]->getMessageParameters()); - $this->assertEqual($translation, $message, 'Translated violation message retrieved.'); - $this->assertEqual($violations[0]->getPropertyPath(), ''); + // Test translating violation messages. + $message = t('This value should be %limit or more.', array('%limit' => 5)); + $this->assertEqual($violations[0]->getMessage(), $message, 'Translated violation message retrieved.'); $this->assertEqual($violations[0]->getPropertyPath(), ''); $this->assertIdentical($violations[0]->getRoot(), $integer, 'Root object returned.'); + // Test translating violation messages when pluralization is used. + $definition = array( + 'type' => 'string', + 'constraints' => array( + 'Length' => array('min' => 10), + ), + ); + $violations = $this->typedData->create($definition, "short")->validate(); + $this->assertEqual($violations->count(), 1); + $message = t('This value is too short. It should have %limit characters or more.', array('%limit' => 10)); + $this->assertEqual($violations[0]->getMessage(), $message, 'Translated violation message retrieved.'); + // Test having multiple violations. $definition = array( 'type' => 'integer',