diff --git a/core/lib/Drupal/Component/Utility/PlaceholderTrait.php b/core/lib/Drupal/Component/Utility/PlaceholderTrait.php
new file mode 100644
index 0000000..072393c
--- /dev/null
+++ b/core/lib/Drupal/Component/Utility/PlaceholderTrait.php
@@ -0,0 +1,65 @@
+ $value) {
+ switch ($key[0]) {
+ case '@':
+ // Escaped only.
+ if (!SafeMarkup::isSafe($value)) {
+ $args[$key] = Html::escape($value);
+ }
+ break;
+
+ case '%':
+ default:
+ // Escaped and placeholder.
+ if (!SafeMarkup::isSafe($value)) {
+ $value = Html::escape($value);
+ }
+ $args[$key] = '' . $value . '';
+ break;
+
+ case '!':
+ // Pass-through.
+ if (!SafeMarkup::isSafe($value)) {
+ $safe = FALSE;
+ }
+ }
+ }
+
+ $output = strtr($string, $args);
+ return [$output, $safe];
+ }
+
+}
diff --git a/core/lib/Drupal/Component/Utility/SafeMarkup.php b/core/lib/Drupal/Component/Utility/SafeMarkup.php
index 3020bbe..dc91b41 100644
--- a/core/lib/Drupal/Component/Utility/SafeMarkup.php
+++ b/core/lib/Drupal/Component/Utility/SafeMarkup.php
@@ -31,6 +31,7 @@
* @see theme_render
*/
class SafeMarkup {
+ use PlaceholderTrait;
/**
* The list of safe strings.
@@ -203,41 +204,12 @@ public static function checkPlain($text) {
* @see t()
*/
public static function format($string, array $args) {
- $safe = TRUE;
-
- // Transform arguments before inserting them.
- foreach ($args as $key => $value) {
- switch ($key[0]) {
- case '@':
- // Escaped only.
- if (!SafeMarkup::isSafe($value)) {
- $args[$key] = Html::escape($value);
- }
- break;
-
- case '%':
- default:
- // Escaped and placeholder.
- if (!SafeMarkup::isSafe($value)) {
- $value = Html::escape($value);
- }
- $args[$key] = '' . $value . '';
- break;
-
- case '!':
- // Pass-through.
- if (!static::isSafe($value)) {
- $safe = FALSE;
- }
- }
- }
-
- $output = strtr($string, $args);
+ list($output, $safe) = static::placeholderFormat($string, $args);
if ($safe) {
static::$safeStrings[$output]['html'] = TRUE;
}
-
return $output;
+
}
}
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index dd3c8f0..9d749fb 100644
--- a/core/lib/Drupal/Core/Entity/EntityManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -945,9 +945,6 @@ public function getEntityTypeLabels($group = FALSE) {
foreach ($definitions as $entity_type_id => $definition) {
if ($group) {
- // We cast the optgroup label to string as array keys must not be
- // objects and t() may return a TranslationWrapper once issue #2557113
- // lands.
$options[(string) $definition->getGroupLabel()][$entity_type_id] = $definition->getLabel();
}
else {
@@ -963,8 +960,6 @@ public function getEntityTypeLabels($group = FALSE) {
// Make sure that the 'Content' group is situated at the top.
$content = $this->t('Content', array(), array('context' => 'Entity type group'));
- // We cast the optgroup label to string as array keys must not be objects
- // and t() may return a TranslationWrapper once issue #2557113 lands.
$options = array((string) $content => $options[(string) $content]) + $options;
}
diff --git a/core/lib/Drupal/Core/StringTranslation/TranslationInterface.php b/core/lib/Drupal/Core/StringTranslation/TranslationInterface.php
index c3e2f68..b0b04d9 100644
--- a/core/lib/Drupal/Core/StringTranslation/TranslationInterface.php
+++ b/core/lib/Drupal/Core/StringTranslation/TranslationInterface.php
@@ -41,6 +41,24 @@
public function translate($string, array $args = array(), array $options = array());
/**
+ * Translates a string to the current language or to a given language.
+ *
+ * @param string $string
+ * A string containing the English string to translate.
+ * @param array $options
+ * An associative array of additional options, with the following elements:
+ * - 'langcode': The language code to translate to a language other than
+ * what is used to display the page.
+ * - 'context': The context the source string belongs to.
+ *
+ * @return string
+ * The translated string.
+ *
+ * @internal This method is for internal use only. Use ::translate() instead.
+ */
+ public function translateToString($string, array $options = array());
+
+ /**
* Formats a string containing a count of items.
*
* This function ensures that the string is pluralized correctly. Since t() is
diff --git a/core/lib/Drupal/Core/StringTranslation/TranslationManager.php b/core/lib/Drupal/Core/StringTranslation/TranslationManager.php
index af6aa2b..fcd3b56 100644
--- a/core/lib/Drupal/Core/StringTranslation/TranslationManager.php
+++ b/core/lib/Drupal/Core/StringTranslation/TranslationManager.php
@@ -140,38 +140,13 @@ public function getStringTranslation($langcode, $string, $context) {
* {@inheritdoc}
*/
public function translate($string, array $args = array(), array $options = array()) {
- $string = $this->doTranslate($string, $options);
- if (empty($args)) {
- // We add the string to the safe list as opposed to making it an object
- // implementing SafeStringInterface as we may need to call __toString()
- // on the object before render time, at which point the string ceases to
- // be safe, and working around this would require significant rework.
- // Adding this string to the safe list is assumed to be safe because
- // translate() should only be called with strings defined in code.
- // @see \Drupal\Core\StringTranslation\TranslationInterface::translate()
- SafeMarkup::setMultiple([$string => ['html' => TRUE]]);
- return $string;
- }
- else {
- return SafeMarkup::format($string, $args);
- }
+ return new TranslationWrapper($string, $args, $options);
}
/**
- * Translates a string to the current language or to a given language.
- *
- * @param string $string
- * A string containing the English string to translate.
- * @param array $options
- * An associative array of additional options, with the following elements:
- * - 'langcode': The language code to translate to a language other than
- * what is used to display the page.
- * - 'context': The context the source string belongs to.
- *
- * @return string
- * The translated string.
+ * {@inheritdoc}
*/
- protected function doTranslate($string, array $options = array()) {
+ public function translateToString($string, array $options = array()) {
// Merge in defaults.
if (empty($options['langcode'])) {
$options['langcode'] = $this->defaultLangcode;
@@ -188,7 +163,7 @@ protected function doTranslate($string, array $options = array()) {
*/
public function formatPlural($count, $singular, $plural, array $args = array(), array $options = array()) {
$translatable_string = implode(LOCALE_PLURAL_DELIMITER, array($singular, $plural));
- $translated_strings = $this->doTranslate($translatable_string, $options);
+ $translated_strings = $this->translateToString($translatable_string, $options);
return $this->formatPluralTranslated($count, $translated_strings, $args, $options);
}
diff --git a/core/lib/Drupal/Core/StringTranslation/TranslationWrapper.php b/core/lib/Drupal/Core/StringTranslation/TranslationWrapper.php
index 10e9b12..536778c 100644
--- a/core/lib/Drupal/Core/StringTranslation/TranslationWrapper.php
+++ b/core/lib/Drupal/Core/StringTranslation/TranslationWrapper.php
@@ -7,8 +7,11 @@
namespace Drupal\Core\StringTranslation;
+use Drupal\Component\Utility\Html;
+use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\SafeStringInterface;
use Drupal\Component\Utility\ToStringTrait;
+use Drupal\Component\Utility\PlaceholderTrait;
/**
* Provides a class to wrap a translatable string.
@@ -21,6 +24,7 @@
*/
class TranslationWrapper implements SafeStringInterface {
+ use PlaceholderTrait;
use StringTranslationTrait;
use ToStringTrait;
@@ -103,7 +107,12 @@ public function getOptions() {
* The translated string.
*/
public function render() {
- return $this->t($this->string, $this->arguments, $this->options);
+ $string = $this->getStringTranslation()->translateToString($this->string, $this->options);
+ if (!empty($this->arguments)) {
+ list($string, $safe) = static::placeholderFormat($string, $this->arguments);
+ // @todo make sure the output from placeholderFormat() is always safe
+ }
+ return $string;
}
/**
@@ -123,5 +132,4 @@ public function jsonSerialize() {
return $this->__toString();
}
-
}
diff --git a/core/lib/Drupal/Core/Template/Attribute.php b/core/lib/Drupal/Core/Template/Attribute.php
index cc0d591..76d7c6a 100644
--- a/core/lib/Drupal/Core/Template/Attribute.php
+++ b/core/lib/Drupal/Core/Template/Attribute.php
@@ -109,7 +109,8 @@ protected function createAttributeValue($name, $value) {
elseif (is_bool($value)) {
$value = new AttributeBoolean($name, $value);
}
- elseif (!is_object($value)) {
+ // As a development aid, we allow the value to be a safe string object.
+ elseif (!is_object($value) || $value instanceof SafeStringInterface) {
$value = new AttributeString($name, $value);
}
return $value;
diff --git a/core/lib/Drupal/Core/Validation/DrupalTranslator.php b/core/lib/Drupal/Core/Validation/DrupalTranslator.php
index a2bbf5b..dfbef68 100644
--- a/core/lib/Drupal/Core/Validation/DrupalTranslator.php
+++ b/core/lib/Drupal/Core/Validation/DrupalTranslator.php
@@ -7,6 +7,8 @@
namespace Drupal\Core\Validation;
+use Drupal\Component\Utility\SafeStringInterface;
+
/**
* Translates strings using Drupal's translation system.
*
@@ -73,8 +75,13 @@ public function getLocale() {
protected function processParameters(array $parameters) {
$return = array();
foreach ($parameters as $key => $value) {
+ // We allow the values in the parameters to be safe string objects. This can be
+ // useful when we want to use parameter values that are TranslationWrappers.
+ if ($value instanceof SafeStringInterface) {
+ $value = (string) $value;
+ }
if (is_object($value)) {
- // t() does not work will objects being passed as replacement strings.
+ // t() does not work with objects being passed as replacement strings.
}
// Check for symfony replacement patterns in the form "{{ name }}".
elseif (strpos($key, '{{ ') === 0 && strrpos($key, ' }}') == strlen($key) - 3) {
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/PrimitiveTypeConstraintValidator.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/PrimitiveTypeConstraintValidator.php
index d873820..6fa8771 100644
--- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/PrimitiveTypeConstraintValidator.php
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/PrimitiveTypeConstraintValidator.php
@@ -16,6 +16,7 @@
use Drupal\Core\TypedData\Type\StringInterface;
use Drupal\Core\TypedData\Type\UriInterface;
use Drupal\Core\TypedData\Validation\TypedDataAwareValidatorTrait;
+use Drupal\Component\Utility\SafeStringInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
@@ -49,7 +50,7 @@ public function validate($value, Constraint $constraint) {
if ($typed_data instanceof IntegerInterface && filter_var($value, FILTER_VALIDATE_INT) === FALSE) {
$valid = FALSE;
}
- if ($typed_data instanceof StringInterface && !is_scalar($value)) {
+ if ($typed_data instanceof StringInterface && !is_scalar($value) && !($value instanceof SafeStringInterface)) {
$valid = FALSE;
}
// Ensure that URIs comply with http://tools.ietf.org/html/rfc3986, which
diff --git a/core/modules/ckeditor/ckeditor.admin.inc b/core/modules/ckeditor/ckeditor.admin.inc
index d5bb690..9120763 100644
--- a/core/modules/ckeditor/ckeditor.admin.inc
+++ b/core/modules/ckeditor/ckeditor.admin.inc
@@ -117,8 +117,6 @@ function template_preprocess_ckeditor_settings_toolbar(&$variables) {
$variables['active_buttons'] = array();
foreach ($active_buttons as $row_number => $button_row) {
foreach ($button_groups[$row_number] as $group_name) {
- // We cast the group name to string as array keys must not be objects
- // and t() may return a TranslationWrapper once issue #2557113 lands.
$group_name = (string) $group_name;
$variables['active_buttons'][$row_number][$group_name] = array(
'group_name_class' => Html::getClass($group_name),
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index 596542f..4720e36 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -299,8 +299,6 @@ function comment_form_field_ui_field_storage_add_form_alter(&$form, FormStateInt
$form['#title'] = \Drupal::service('comment.manager')->getFieldUIPageTitle($route_match->getParameter('commented_entity_type'), $route_match->getParameter('field_name'));
}
if (!_comment_entity_uses_integer_id($form_state->get('entity_type_id'))) {
- // We cast the optgroup label to string as array keys must not be objects
- // and t() will return a TranslationWrapper once issue #2557113 lands.
$optgroup = (string) t('General');
// You cannot use comment fields on entity types with non-integer IDs.
unset($form['add']['new_storage_type']['#options'][$optgroup]['comment']);
diff --git a/core/modules/entity_reference/entity_reference.module b/core/modules/entity_reference/entity_reference.module
index 908e0e5..ae4cc03 100644
--- a/core/modules/entity_reference/entity_reference.module
+++ b/core/modules/entity_reference/entity_reference.module
@@ -126,8 +126,6 @@ function entity_reference_field_config_presave(FieldConfigInterface $field) {
* Implements hook_form_FORM_ID_alter() for 'field_ui_field_storage_add_form'.
*/
function entity_reference_form_field_ui_field_storage_add_form_alter(array &$form) {
- // We cast the optgroup label to string as array keys must not be objects
- // and t() may return a TranslationWrapper once issue #2557113 lands.
$optgroup = (string) t('Reference');
// Move the "Entity reference" option to the end of the list and rename it to
// "Other".
diff --git a/core/modules/language/src/Form/NegotiationBrowserForm.php b/core/modules/language/src/Form/NegotiationBrowserForm.php
index 111f35a..5fa186a 100644
--- a/core/modules/language/src/Form/NegotiationBrowserForm.php
+++ b/core/modules/language/src/Form/NegotiationBrowserForm.php
@@ -82,8 +82,6 @@ public function buildForm(array $form, FormStateInterface $form_state) {
}
else {
$language_options = array(
- // We cast the optgroup labels to string as array keys must not be objects
- // and t() may return a TranslationWrapper once issue #2557113 lands.
(string) $this->t('Existing languages') => $existing_languages,
(string) $this->t('Languages not yet added') => $this->languageManager->getStandardLanguageListWithoutConfigured(),
);
diff --git a/core/modules/locale/src/Form/ImportForm.php b/core/modules/locale/src/Form/ImportForm.php
index 86aecc3..f3007f6 100644
--- a/core/modules/locale/src/Form/ImportForm.php
+++ b/core/modules/locale/src/Form/ImportForm.php
@@ -94,8 +94,6 @@ public function buildForm(array $form, FormStateInterface $form_state) {
else {
$default = key($existing_languages);
$language_options = array(
- // We cast the optgroup labels to string as array keys must not be objects
- // and t() may return a TranslationWrapper once issue #2557113 lands.
(string) $this->t('Existing languages') => $existing_languages,
(string) $this->t('Languages not yet added') => $this->languageManager->getStandardLanguageListWithoutConfigured(),
);
diff --git a/core/modules/locale/src/Tests/LocaleImportFunctionalTest.php b/core/modules/locale/src/Tests/LocaleImportFunctionalTest.php
index 05af294..02e364b 100644
--- a/core/modules/locale/src/Tests/LocaleImportFunctionalTest.php
+++ b/core/modules/locale/src/Tests/LocaleImportFunctionalTest.php
@@ -238,8 +238,10 @@ public function testLanguageContext() {
'langcode' => 'hr',
));
- $this->assertIdentical(t('May', array(), array('langcode' => 'hr', 'context' => 'Long month name')), 'Svibanj', 'Long month name context is working.');
- $this->assertIdentical(t('May', array(), array('langcode' => 'hr')), 'Svi.', 'Default context is working.');
+ // We cast the return value of t() to string so as to retrieve the translated
+ // value, rendered as a string.
+ $this->assertIdentical((string) t('May', array(), array('langcode' => 'hr', 'context' => 'Long month name')), 'Svibanj', 'Long month name context is working.');
+ $this->assertIdentical((string) t('May', array(), array('langcode' => 'hr')), 'Svi.', 'Default context is working.');
}
/**
@@ -254,7 +256,7 @@ public function testEmptyMsgstr() {
));
$this->assertRaw(t('One translation file imported. %number translations were added, %update translations were updated and %delete translations were removed.', array('%number' => 1, '%update' => 0, '%delete' => 0)), 'The translation file was successfully imported.');
- $this->assertIdentical(t('Operations', array(), array('langcode' => $langcode)), 'Műveletek', 'String imported and translated.');
+ $this->assertIdentical((string) t('Operations', array(), array('langcode' => $langcode)), 'Műveletek', 'String imported and translated.');
// Try importing a .po file.
$this->importPoFile($this->getPoFileWithEmptyMsgstr(), array(
diff --git a/core/modules/locale/src/Tests/LocaleTranslationUiTest.php b/core/modules/locale/src/Tests/LocaleTranslationUiTest.php
index 6c35b18..a708311 100644
--- a/core/modules/locale/src/Tests/LocaleTranslationUiTest.php
+++ b/core/modules/locale/src/Tests/LocaleTranslationUiTest.php
@@ -64,7 +64,7 @@ public function testStringTranslation() {
);
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add custom language'));
// Add string.
- t($name, array(), array('langcode' => $langcode));
+ (string) t($name, array(), array('langcode' => $langcode));
// Reset locale cache.
$this->container->get('string_translation')->reset();
$this->assertRaw('"edit-languages-' . $langcode . '-weight"', 'Language code found.');
@@ -302,7 +302,7 @@ public function testStringValidation() {
);
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add custom language'));
// Add string.
- t($name, array(), array('langcode' => $langcode));
+ (string) t($name, array(), array('langcode' => $langcode));
// Reset locale cache.
$search = array(
'string' => $name,
@@ -361,7 +361,7 @@ public function testStringSearch() {
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add custom language'));
// Add string.
- t($name, array(), array('langcode' => $langcode));
+ (string) t($name, array(), array('langcode' => $langcode));
// Reset locale cache.
$this->container->get('string_translation')->reset();
$this->drupalLogout();
diff --git a/core/modules/simpletest/src/TestBase.php b/core/modules/simpletest/src/TestBase.php
index 62e4c9f..285865b 100644
--- a/core/modules/simpletest/src/TestBase.php
+++ b/core/modules/simpletest/src/TestBase.php
@@ -658,10 +658,6 @@ protected function assertNotNull($value, $message = '', $group = 'Other') {
* TRUE if the assertion succeeded, FALSE otherwise.
*/
protected function assertEqual($first, $second, $message = '', $group = 'Other') {
- // We cast objects implementing SafeStringInterface to string ourself so as
- // to not rely on PHP casting them to string depending on what we're
- // comparing with.
- $this->castSafeStrings($first, $second);
return $this->assert($first == $second, $message ? $message : SafeMarkup::format('Value @first is equal to value @second.', array('@first' => var_export($first, TRUE), '@second' => var_export($second, TRUE))), $group);
}
diff --git a/core/modules/system/src/Tests/Form/FormTest.php b/core/modules/system/src/Tests/Form/FormTest.php
index e89b78f..b02cbdc 100644
--- a/core/modules/system/src/Tests/Form/FormTest.php
+++ b/core/modules/system/src/Tests/Form/FormTest.php
@@ -144,7 +144,7 @@ function testRequiredFields() {
// Select elements are going to have validation errors with empty
// input, since those are illegal choices. Just make sure the
// error is not "field is required".
- $this->assertTrue((empty($errors[$element]) || strpos('field is required', $errors[$element]) === FALSE), "Optional '$type' field '$element' is not treated as a required element");
+ $this->assertTrue((empty($errors[$element]) || strpos('field is required', (string) $errors[$element]) === FALSE), "Optional '$type' field '$element' is not treated as a required element");
}
else {
// Make sure there is *no* form error for this element.
diff --git a/core/modules/system/tests/modules/plugin_test/src/Plugin/MockBlockManager.php b/core/modules/system/tests/modules/plugin_test/src/Plugin/MockBlockManager.php
index e435c0a..df66b7f 100644
--- a/core/modules/system/tests/modules/plugin_test/src/Plugin/MockBlockManager.php
+++ b/core/modules/system/tests/modules/plugin_test/src/Plugin/MockBlockManager.php
@@ -78,7 +78,7 @@ public function __construct() {
'label' => t('User name'),
'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockUserNameBlock',
'context' => array(
- 'user' => new ContextDefinition('entity:user', t('User')),
+ 'user' => $this->createContextDefinitionWithStringLabel('entity:user', t('User')),
),
));
@@ -87,7 +87,7 @@ public function __construct() {
'label' => t('User name optional'),
'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockUserNameBlock',
'context' => array(
- 'user' => new ContextDefinition('entity:user', t('User'), FALSE),
+ 'user' => $this->createContextDefinitionWithStringLabel('entity:user', t('User'), FALSE),
),
));
@@ -102,8 +102,8 @@ public function __construct() {
'label' => t('Complex context'),
'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockComplexContextBlock',
'context' => array(
- 'user' => new ContextDefinition('entity:user', t('User')),
- 'node' => new ContextDefinition('entity:node', t('Node')),
+ 'user' => $this->createContextDefinitionWithStringLabel('entity:user', t('User')),
+ 'node' => $this->createContextDefinitionWithStringLabel('entity:node', t('Node')),
),
));
@@ -118,4 +118,17 @@ public function __construct() {
// specified), so we provide it the discovery object.
$this->factory = new ReflectionFactory($this->discovery);
}
+
+ /**
+ * Creates a new context definition with a label that is cast to string.
+ *
+ * @return \Drupal\Core\Plugin\Context\ContextDefinition
+ */
+ protected function createContextDefinitionWithStringLabel($data_type, $label, $required = TRUE) {
+ // We cast the label to string for testing purposes only, as it may be
+ // a TranslationWrapper and we will do assertEqual() checks on arrays that
+ // include ContextDefinition objects, and var_export() has problems
+ // printing TranslationWrapper objects.
+ return new ContextDefinition($data_type, (string) $label, $required);
+ }
}
diff --git a/core/modules/user/src/Tests/UserCancelTest.php b/core/modules/user/src/Tests/UserCancelTest.php
index 5357013..648f56f 100644
--- a/core/modules/user/src/Tests/UserCancelTest.php
+++ b/core/modules/user/src/Tests/UserCancelTest.php
@@ -535,7 +535,7 @@ function testMassUserCancelByAdmin() {
$this->drupalPostForm(NULL, NULL, t('Cancel accounts'));
$status = TRUE;
foreach ($users as $account) {
- $status = $status && (strpos($this->content, t('%name has been deleted.', array('%name' => $account->getUsername()))) !== FALSE);
+ $status = $status && (strpos($this->content, $account->getUsername() . ' has been deleted.') !== FALSE);
$user_storage->resetCache(array($account->id()));
$status = $status && !$user_storage->load($account->id());
}
diff --git a/core/modules/views/src/Plugin/views/PluginBase.php b/core/modules/views/src/Plugin/views/PluginBase.php
index 49b7f1d..66661df 100644
--- a/core/modules/views/src/Plugin/views/PluginBase.php
+++ b/core/modules/views/src/Plugin/views/PluginBase.php
@@ -15,6 +15,7 @@
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Plugin\PluginBase as ComponentPluginBase;
use Drupal\Core\Render\Element;
+use Drupal\Core\StringTranslation\TranslationWrapper;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
use Drupal\views\ViewExecutable;
use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -552,7 +553,14 @@ protected function listLanguages($flags = LanguageInterface::STATE_ALL, array $c
// Since this is not a real language, surround it by '***LANGUAGE_...***',
// like the negotiated languages below.
if ($flags & LanguageInterface::STATE_SITE_DEFAULT) {
- $list[PluginBase::VIEWS_QUERY_LANGUAGE_SITE_DEFAULT] = $this->t($languages[LanguageInterface::LANGCODE_SITE_DEFAULT]->getName());
+ $name = $languages[LanguageInterface::LANGCODE_SITE_DEFAULT]->getName();
+ // The language name may have already been translated, no need to
+ // translate it again.
+ // @see Drupal\Core\Language::filterLanguages().
+ if (!$name instanceof TranslationWrapper) {
+ $name = $this->t($name);
+ }
+ $list[PluginBase::VIEWS_QUERY_LANGUAGE_SITE_DEFAULT] = $name;
// Remove site default language from $languages so it's not added
// twice with the real languages below.
unset($languages[LanguageInterface::LANGCODE_SITE_DEFAULT]);
diff --git a/core/modules/views/src/Plugin/views/area/TokenizeAreaPluginBase.php b/core/modules/views/src/Plugin/views/area/TokenizeAreaPluginBase.php
index 7c26932..75d6529 100644
--- a/core/modules/views/src/Plugin/views/area/TokenizeAreaPluginBase.php
+++ b/core/modules/views/src/Plugin/views/area/TokenizeAreaPluginBase.php
@@ -52,8 +52,6 @@ public function tokenForm(&$form, FormStateInterface $form_state) {
// Get a list of the available fields and arguments for token replacement.
$options = array();
- // We cast the optgroup labels to string as array keys must not be objects
- // and t() may return a TranslationWrapper once issue #2557113 lands.
$optgroup_arguments = (string) t('Arguments');
$optgroup_fields = (string) t('Fields');
foreach ($this->view->display_handler->getHandlers('field') as $field => $handler) {
diff --git a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
index 4ed8f55..1d089c8 100644
--- a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
+++ b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
@@ -1726,9 +1726,6 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) {
$options = array();
$count = 0; // This lets us prepare the key as we want it printed.
- // We cast the optgroup label to string as array keys must not be
- // objects and t() may return a TranslationWrapper once issue #2557113
- // lands.
$optgroup_arguments = (string) t('Arguments');
foreach ($this->view->display_handler->getHandlers('argument') as $handler) {
$options[$optgroup_arguments]['%' . ++$count] = $this->t('@argument title', array('@argument' => $handler->adminLabel()));
diff --git a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php
index baa11ab..e22cab3 100644
--- a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php
+++ b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php
@@ -862,8 +862,6 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) {
// Setup the tokens for fields.
$previous = $this->getPreviousFieldLabels();
- // We cast the optgroup labels to string as array keys must not be objects
- // and t() may return a TranslationWrapper once issue #2557113 lands.
$optgroup_arguments = (string) t('Arguments');
$optgroup_fields = (string) t('Fields');
foreach ($previous as $id => $label) {
diff --git a/core/tests/Drupal/Tests/Core/Annotation/TranslationTest.php b/core/tests/Drupal/Tests/Core/Annotation/TranslationTest.php
index be8cca3..f9dfc4f 100644
--- a/core/tests/Drupal/Tests/Core/Annotation/TranslationTest.php
+++ b/core/tests/Drupal/Tests/Core/Annotation/TranslationTest.php
@@ -46,8 +46,8 @@ public function testGet(array $values, $expected) {
'context' => $values['context'],
) : array();
$this->translationManager->expects($this->once())
- ->method('translate')
- ->with($values['value'], $arguments, $options);
+ ->method('translateToString')
+ ->with($values['value'], $options);
$annotation = new Translation($values);
diff --git a/core/tests/Drupal/Tests/Core/Menu/ContextualLinkDefaultTest.php b/core/tests/Drupal/Tests/Core/Menu/ContextualLinkDefaultTest.php
index ac92088..837ed8b 100644
--- a/core/tests/Drupal/Tests/Core/Menu/ContextualLinkDefaultTest.php
+++ b/core/tests/Drupal/Tests/Core/Menu/ContextualLinkDefaultTest.php
@@ -74,8 +74,8 @@ public function testGetTitle() {
$this->pluginDefinition['title'] = (new TranslationWrapper($title))
->setStringTranslation($this->stringTranslation);
$this->stringTranslation->expects($this->once())
- ->method('translate')
- ->with($title, array(), array())
+ ->method('translateToString')
+ ->with($title, array())
->will($this->returnValue('Example translated'));
$this->setupContextualLinkDefault();
@@ -90,8 +90,8 @@ public function testGetTitleWithContext() {
$this->pluginDefinition['title'] = (new TranslationWrapper($title, array(), array('context' => 'context')))
->setStringTranslation($this->stringTranslation);
$this->stringTranslation->expects($this->once())
- ->method('translate')
- ->with($title, array(), array('context' => 'context'))
+ ->method('translateToString')
+ ->with($title, array('context' => 'context'))
->will($this->returnValue('Example translated with context'));
$this->setupContextualLinkDefault();
@@ -106,8 +106,8 @@ public function testGetTitleWithTitleArguments() {
$this->pluginDefinition['title'] = (new TranslationWrapper($title, array('@test' => 'value')))
->setStringTranslation($this->stringTranslation);
$this->stringTranslation->expects($this->once())
- ->method('translate')
- ->with($title, array('@test' => 'value'), array())
+ ->method('translateToString')
+ ->with($title, array())
->will($this->returnValue('Example value'));
$this->setupContextualLinkDefault();
diff --git a/core/tests/Drupal/Tests/Core/Menu/LocalActionDefaultTest.php b/core/tests/Drupal/Tests/Core/Menu/LocalActionDefaultTest.php
index 02d4578..52d49e4 100644
--- a/core/tests/Drupal/Tests/Core/Menu/LocalActionDefaultTest.php
+++ b/core/tests/Drupal/Tests/Core/Menu/LocalActionDefaultTest.php
@@ -86,8 +86,8 @@ public function testGetTitle() {
$this->pluginDefinition['title'] = (new TranslationWrapper('Example'))
->setStringTranslation($this->stringTranslation);
$this->stringTranslation->expects($this->once())
- ->method('translate')
- ->with('Example', array(), array())
+ ->method('translateToString')
+ ->with('Example', array())
->will($this->returnValue('Example translated'));
$this->setupLocalActionDefault();
@@ -103,8 +103,8 @@ public function testGetTitleWithContext() {
$this->pluginDefinition['title'] = (new TranslationWrapper('Example', array(), array('context' => 'context')))
->setStringTranslation($this->stringTranslation);
$this->stringTranslation->expects($this->once())
- ->method('translate')
- ->with('Example', array(), array('context' => 'context'))
+ ->method('translateToString')
+ ->with('Example', array('context' => 'context'))
->will($this->returnValue('Example translated with context'));
$this->setupLocalActionDefault();
@@ -118,8 +118,8 @@ public function testGetTitleWithTitleArguments() {
$this->pluginDefinition['title'] = (new TranslationWrapper('Example @test', array('@test' => 'value')))
->setStringTranslation($this->stringTranslation);
$this->stringTranslation->expects($this->once())
- ->method('translate')
- ->with('Example @test', array('@test' => 'value'), array())
+ ->method('translateToString')
+ ->with('Example @test', array())
->will($this->returnValue('Example value'));
$this->setupLocalActionDefault();
diff --git a/core/tests/Drupal/Tests/Core/Menu/LocalTaskDefaultTest.php b/core/tests/Drupal/Tests/Core/Menu/LocalTaskDefaultTest.php
index 6e393a8..59732ff 100644
--- a/core/tests/Drupal/Tests/Core/Menu/LocalTaskDefaultTest.php
+++ b/core/tests/Drupal/Tests/Core/Menu/LocalTaskDefaultTest.php
@@ -236,8 +236,8 @@ public function testGetTitle() {
$this->pluginDefinition['title'] = (new TranslationWrapper('Example'))
->setStringTranslation($this->stringTranslation);
$this->stringTranslation->expects($this->once())
- ->method('translate')
- ->with('Example', array(), array())
+ ->method('translateToString')
+ ->with('Example', array())
->will($this->returnValue('Example translated'));
$this->setupLocalTaskDefault();
@@ -252,8 +252,8 @@ public function testGetTitleWithContext() {
$this->pluginDefinition['title'] = (new TranslationWrapper($title, array(), array('context' => 'context')))
->setStringTranslation($this->stringTranslation);
$this->stringTranslation->expects($this->once())
- ->method('translate')
- ->with($title, array(), array('context' => 'context'))
+ ->method('translateToString')
+ ->with($title, array('context' => 'context'))
->will($this->returnValue('Example translated with context'));
$this->setupLocalTaskDefault();
@@ -268,8 +268,8 @@ public function testGetTitleWithTitleArguments() {
$this->pluginDefinition['title'] = (new TranslationWrapper('Example @test', array('@test' => 'value')))
->setStringTranslation($this->stringTranslation);
$this->stringTranslation->expects($this->once())
- ->method('translate')
- ->with($title, array('@test' => 'value'), array())
+ ->method('translateToString')
+ ->with($title, array())
->will($this->returnValue('Example value'));
$this->setupLocalTaskDefault();
diff --git a/core/tests/Drupal/Tests/Core/StringTranslation/TranslationWrapperTest.php b/core/tests/Drupal/Tests/Core/StringTranslation/TranslationWrapperTest.php
index 3facf38..7b051cd 100644
--- a/core/tests/Drupal/Tests/Core/StringTranslation/TranslationWrapperTest.php
+++ b/core/tests/Drupal/Tests/Core/StringTranslation/TranslationWrapperTest.php
@@ -66,7 +66,7 @@ public function testToString() {
->willReturn('');
$translation = $this->prophesize(TranslationInterface::class);
- $translation->translate($string, [], [])->will(function () {
+ $translation->translateToString($string, [])->will(function () {
throw new \Exception('Yes you may.');
});
$text->setStringTranslation($translation->reveal());
diff --git a/core/tests/Drupal/Tests/Core/Validation/Plugin/Validation/Constraint/PrimitiveTypeConstraintValidatorTest.php b/core/tests/Drupal/Tests/Core/Validation/Plugin/Validation/Constraint/PrimitiveTypeConstraintValidatorTest.php
index 2bcb318..0707fa3 100644
--- a/core/tests/Drupal/Tests/Core/Validation/Plugin/Validation/Constraint/PrimitiveTypeConstraintValidatorTest.php
+++ b/core/tests/Drupal/Tests/Core/Validation/Plugin/Validation/Constraint/PrimitiveTypeConstraintValidatorTest.php
@@ -16,6 +16,7 @@
use Drupal\Core\TypedData\PrimitiveInterface;
use Drupal\Core\Validation\Plugin\Validation\Constraint\PrimitiveTypeConstraint;
use Drupal\Core\Validation\Plugin\Validation\Constraint\PrimitiveTypeConstraintValidator;
+use Drupal\Core\StringTranslation\TranslationWrapper;
use Drupal\Tests\UnitTestCase;
/**
@@ -63,6 +64,7 @@ public function provideTestValidate() {
$data[] = [new IntegerData(DataDefinition::create('integer')), 1.5, FALSE];
$data[] = [new IntegerData(DataDefinition::create('integer')), 'test', FALSE];
$data[] = [new StringData(DataDefinition::create('string')), 'test', TRUE];
+ $data[] = [new StringData(DataDefinition::create('string')), new TranslationWrapper('test'), TRUE];
// It is odd that 1 is a valid string.
// $data[] = [$this->getMock('Drupal\Core\TypedData\Type\StringInterface'), 1, FALSE];
$data[] = [new StringData(DataDefinition::create('string')), [], FALSE];
diff --git a/core/tests/Drupal/Tests/UnitTestCase.php b/core/tests/Drupal/Tests/UnitTestCase.php
index 3433c94..11dd14d 100644
--- a/core/tests/Drupal/Tests/UnitTestCase.php
+++ b/core/tests/Drupal/Tests/UnitTestCase.php
@@ -220,6 +220,11 @@ public function getStringTranslationStub() {
->willReturnCallback(function ($count, $singular, $plural, array $args = [], array $options = []) {
return $count === 1 ? SafeMarkup::format($singular, $args) : SafeMarkup::format($plural, $args + ['@count' => $count]);
});
+ $translation->expects($this->any())
+ ->method('translateToString')
+ ->willReturnCallback(function ($string, array $options = []) {
+ return $string;
+ });
return $translation;
}