diff --git a/core/lib/Drupal/Component/Utility/PlaceholderTrait.php b/core/lib/Drupal/Component/Utility/PlaceholderTrait.php
index f5bcb399666a59e28fcb451ce20c8dd3bff21a4d..e2db26b3ecb17234752cc1909c04ff1559703df5 100644
--- a/core/lib/Drupal/Component/Utility/PlaceholderTrait.php
+++ b/core/lib/Drupal/Component/Utility/PlaceholderTrait.php
@@ -50,15 +50,6 @@ trait PlaceholderTrait {
    *     Url::fromUri($user_input)->toString() (which either throws an exception
    *     or returns a well-formed URL) before passing the result into a
    *     ":variable" placeholder.
-   *   - !variable: Inserted as is, with no sanitization or formatting. Only
-   *     use this when the resulting string is being generated for one of:
-   *     - Non-HTML usage, such as a plain-text email.
-   *     - Non-direct HTML output, such as a plain-text variable that will be
-   *       printed as an HTML attribute value and therefore formatted with
-   *       self::checkPlain() as part of that.
-   *     - Some other special reason for suppressing sanitization.
-   * @param bool &$safe
-   *   A boolean indicating whether the string is safe or not (optional).
    *
    * @return string
    *   The string with the placeholders replaced.
@@ -72,24 +63,13 @@ trait PlaceholderTrait {
    * @see \Drupal\Component\Utility\UrlHelper::stripDangerousProtocols()
    * @see \Drupal\Core\Url::fromUri()
    */
-  protected static function placeholderFormat($string, array $args, &$safe = TRUE) {
+  protected static function placeholderFormat($string, array $args) {
     // 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] = '<em class="placeholder">' . $value . '</em>';
+          $args[$key] = static::placeholderEscape($value);
           break;
 
         case ':':
@@ -100,14 +80,28 @@ protected static function placeholderFormat($string, array $args, &$safe = TRUE)
           $args[$key] = Html::escape(UrlHelper::stripDangerousProtocols($value));
           break;
 
-        case '!':
-          // Pass-through.
-          if (!SafeMarkup::isSafe($value)) {
-            $safe = FALSE;
-          }
+        case '%':
+        default:
+          // Escaped and placeholder.
+          $args[$key] = '<em class="placeholder">' . static::placeholderEscape($value) . '</em>';
+          break;
       }
     }
+
     return strtr($string, $args);
   }
 
+  /**
+   * Escapes a placeholder replacement value if needed.
+   *
+   * @param string|\Drupal\Component\Utility\SafeStringInterface $value
+   *   A placeholder replacement value.
+   *
+   * @return string
+   *   The properly escaped replacement value.
+   */
+  protected static function placeholderEscape($value) {
+    return SafeMarkup::isSafe($value) ? (string) $value : Html::escape($value);
+  }
+
 }
diff --git a/core/lib/Drupal/Component/Utility/SafeMarkup.php b/core/lib/Drupal/Component/Utility/SafeMarkup.php
index a8c95764041f0747b078dfcbdb8f1f4ca5b73448..6f42d13020c61992f53b3ff048961335acadb43d 100644
--- a/core/lib/Drupal/Component/Utility/SafeMarkup.php
+++ b/core/lib/Drupal/Component/Utility/SafeMarkup.php
@@ -183,20 +183,7 @@ public static function checkPlain($text) {
    *   Use \Drupal\Component\Utility\FormattableString.
    */
   public static function format($string, array $args) {
-    // If the string has arguments that start with '!' we consider it unsafe
-    // and return a string instead of an object for backward compatibility
-    // purposes.
-    // @todo https://www.drupal.org/node/2571695 remove this temporary
-    //   workaround.
-    $safe = TRUE;
-    foreach ($args as $key => $value) {
-      if ($key[0] == '!' && !static::isSafe($value)) {
-        $safe = FALSE;
-      }
-    }
-    $safe_string = new FormattableString($string, $args);
-
-    return $safe ? $safe_string : (string) $safe_string;
+    return new FormattableString($string, $args);
   }
 
 }
diff --git a/core/lib/Drupal/Core/StringTranslation/PluralTranslatableString.php b/core/lib/Drupal/Core/StringTranslation/PluralTranslatableString.php
index b9e79e5a3e16bdfe618da3017779ba6c4447b169..46225df6d4cad53ca1c1d61a55a38f20072a75bb 100644
--- a/core/lib/Drupal/Core/StringTranslation/PluralTranslatableString.php
+++ b/core/lib/Drupal/Core/StringTranslation/PluralTranslatableString.php
@@ -8,7 +8,6 @@
 namespace Drupal\Core\StringTranslation;
 
 use Drupal\Component\Utility\PlaceholderTrait;
-use Drupal\Component\Utility\SafeMarkup;
 
 /**
  * A class to hold plural translatable strings.
@@ -107,21 +106,9 @@ public function __construct($count, $singular, $plural, array $args = [], array
    *   A PluralTranslatableString object.
    */
   public static function createFromTranslatedString($count, $translated_string, array $args = [], array $options = []) {
-    $safe = TRUE;
-    foreach (array_keys($args) as $arg_key) {
-      // If the string has arguments that start with '!' we consider it unsafe
-      // and return the translation as a string for backward compatibility
-      // purposes.
-      // @todo https://www.drupal.org/node/2570037 remove this temporary
-      // workaround.
-      if (0 === strpos($arg_key, '!') && !SafeMarkup::isSafe($args[$arg_key])) {
-        $safe = FALSE;
-        break;
-      }
-    }
     $plural = new static($count, '', '', $args, $options);
     $plural->translatedString = $translated_string;
-    return $safe ? $plural : (string) $plural;
+    return $plural;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/StringTranslation/TranslationInterface.php b/core/lib/Drupal/Core/StringTranslation/TranslationInterface.php
index f9ff9ef8fb0d491a569f65732cc709adb213fe59..195a5a1d58ae1c94e4920808c3419a53cc2d0db0 100644
--- a/core/lib/Drupal/Core/StringTranslation/TranslationInterface.php
+++ b/core/lib/Drupal/Core/StringTranslation/TranslationInterface.php
@@ -33,7 +33,7 @@ interface TranslationInterface {
    *      what is used to display the page.
    *   - 'context': The context the source string belongs to.
    *
-   * @return string|\Drupal\Core\StringTranslation\TranslatableString
+   * @return \Drupal\Core\StringTranslation\TranslatableString
    *   The translated string.
    *
    * @see \Drupal\Component\Utility\SafeMarkup::format()
diff --git a/core/lib/Drupal/Core/StringTranslation/TranslationManager.php b/core/lib/Drupal/Core/StringTranslation/TranslationManager.php
index c7bad70ee1fc2b2a4dbf43aa6d66b0bc43c659bc..ec16db217ba6d699512d466663af11f2cccc2527 100644
--- a/core/lib/Drupal/Core/StringTranslation/TranslationManager.php
+++ b/core/lib/Drupal/Core/StringTranslation/TranslationManager.php
@@ -110,20 +110,7 @@ public function getStringTranslation($langcode, $string, $context) {
    * {@inheritdoc}
    */
   public function translate($string, array $args = array(), array $options = array()) {
-    $safe = TRUE;
-    foreach (array_keys($args) as $arg_key) {
-      // If the string has arguments that start with '!' we consider it unsafe
-      // and return the translation as a string for backward compatibility
-      // purposes.
-      // @todo https://www.drupal.org/node/2570037 remove this temporary
-      // workaround.
-      if (0 === strpos($arg_key, '!') && !SafeMarkup::isSafe($args[$arg_key])) {
-        $safe = FALSE;
-        break;
-      }
-    }
-    $wrapper = new TranslatableString($string, $args, $options, $this);
-    return $safe ? $wrapper : (string) $wrapper;
+    return new TranslatableString($string, $args, $options, $this);
   }
 
   /**
@@ -161,20 +148,7 @@ protected function doTranslate($string, array $options = array()) {
    * {@inheritdoc}
    */
   public function formatPlural($count, $singular, $plural, array $args = array(), array $options = array()) {
-    $safe = TRUE;
-    foreach (array_keys($args) as $arg_key) {
-      // If the string has arguments that start with '!' we consider it unsafe
-      // and return the translation as a string for backward compatibility
-      // purposes.
-      // @todo https://www.drupal.org/node/2570037 remove this temporary
-      // workaround.
-      if (0 === strpos($arg_key, '!') && !SafeMarkup::isSafe($args[$arg_key])) {
-        $safe = FALSE;
-        break;
-      }
-    }
-    $plural = new PluralTranslatableString($count, $singular, $plural, $args, $options, $this);
-    return $safe ? $plural : (string) $plural;
+    return new PluralTranslatableString($count, $singular, $plural, $args, $options, $this);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Template/TwigExtension.php b/core/lib/Drupal/Core/Template/TwigExtension.php
index 360eed8d19432cb9c577e1c52284dcf535af5775..552ef547ed033bd33931d525e42738d4a12c33cd 100644
--- a/core/lib/Drupal/Core/Template/TwigExtension.php
+++ b/core/lib/Drupal/Core/Template/TwigExtension.php
@@ -154,7 +154,6 @@ public function getFilters() {
       // "raw" filter and give it identifiable names. These filters should only
       // be used in "trans" tags.
       // @see TwigNodeTrans::compileString()
-      new \Twig_SimpleFilter('passthrough', 'twig_raw_filter', array('is_safe' => array('html'))),
       new \Twig_SimpleFilter('placeholder', [$this, 'escapePlaceholder'], array('is_safe' => array('html'), 'needs_environment' => TRUE)),
 
       // Replace twig's escape filter with our own.
diff --git a/core/lib/Drupal/Core/Template/TwigNodeTrans.php b/core/lib/Drupal/Core/Template/TwigNodeTrans.php
index a7b723ad05839ed42dd041deef76b867bebfad22..757b94b4cc2a7cc2a1357c2a46e7911274b32b95 100644
--- a/core/lib/Drupal/Core/Template/TwigNodeTrans.php
+++ b/core/lib/Drupal/Core/Template/TwigNodeTrans.php
@@ -136,9 +136,6 @@ protected function compileString(\Twig_NodeInterface $body) {
           $argPrefix = '@';
           while ($args instanceof \Twig_Node_Expression_Filter) {
             switch ($args->getNode('filter')->getAttribute('value')) {
-              case 'passthrough':
-                $argPrefix = '!';
-                break;
               case 'placeholder':
                 $argPrefix = '%';
                 break;
diff --git a/core/modules/book/tests/src/Unit/BookUninstallValidatorTest.php b/core/modules/book/tests/src/Unit/BookUninstallValidatorTest.php
index d3748c263d0d435ced5cf3277267af99f17931ea..651c6ce909588fca7d2d5f112bba0ac443e4ac8f 100644
--- a/core/modules/book/tests/src/Unit/BookUninstallValidatorTest.php
+++ b/core/modules/book/tests/src/Unit/BookUninstallValidatorTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Tests\book\Unit;
 
+use Drupal\simpletest\AssertHelperTrait;
 use Drupal\Tests\UnitTestCase;
 
 /**
@@ -15,6 +16,8 @@
  */
 class BookUninstallValidatorTest extends UnitTestCase {
 
+  use AssertHelperTrait;
+
   /**
    * @var \Drupal\book\BookUninstallValidator|\PHPUnit_Framework_MockObject_MockObject
    */
@@ -44,7 +47,7 @@ public function testValidateNotBook() {
     $module = 'not_book';
     $expected = [];
     $reasons = $this->bookUninstallValidator->validate($module);
-    $this->assertSame($expected, $reasons);
+    $this->assertSame($expected, $this->castSafeStrings($reasons));
   }
 
   /**
@@ -61,7 +64,7 @@ public function testValidateEntityQueryWithoutResults() {
     $module = 'book';
     $expected = [];
     $reasons = $this->bookUninstallValidator->validate($module);
-    $this->assertSame($expected, $reasons);
+    $this->assertSame($expected, $this->castSafeStrings($reasons));
   }
 
   /**
@@ -78,7 +81,7 @@ public function testValidateEntityQueryWithResults() {
     $module = 'book';
     $expected = ['To uninstall Book, delete all content that has the Book content type'];
     $reasons = $this->bookUninstallValidator->validate($module);
-    $this->assertSame($expected, $reasons);
+    $this->assertSame($expected, $this->castSafeStrings($reasons));
   }
 
   /**
@@ -94,7 +97,7 @@ public function testValidateOutlineStorage() {
     $module = 'book';
     $expected = ['To uninstall Book, delete all content that is part of a book'];
     $reasons = $this->bookUninstallValidator->validate($module);
-    $this->assertSame($expected, $reasons);
+    $this->assertSame($expected, $this->castSafeStrings($reasons));
   }
 
 }
diff --git a/core/modules/config_translation/tests/src/Unit/ConfigNamesMapperTest.php b/core/modules/config_translation/tests/src/Unit/ConfigNamesMapperTest.php
index 796012fbc624898b40a608992ee574f41ccb21ae..fce0d674bc8e1f40af67a33045a21c2075acc693 100644
--- a/core/modules/config_translation/tests/src/Unit/ConfigNamesMapperTest.php
+++ b/core/modules/config_translation/tests/src/Unit/ConfigNamesMapperTest.php
@@ -140,7 +140,7 @@ protected function setUp() {
    */
   public function testGetTitle() {
     $result = $this->configNamesMapper->getTitle();
-    $this->assertSame($this->pluginDefinition['title'], $result);
+    $this->assertSame($this->pluginDefinition['title'], (string) $result);
   }
 
   /**
@@ -397,7 +397,7 @@ public function testPopulateFromRequest() {
    */
   public function testGetTypeLabel() {
     $result = $this->configNamesMapper->getTypeLabel();
-    $this->assertSame($this->pluginDefinition['title'], $result);
+    $this->assertSame($this->pluginDefinition['title'], (string) $result);
   }
 
   /**
@@ -624,7 +624,7 @@ public function providerTestHasTranslation() {
    */
   public function testGetTypeName() {
     $result = $this->configNamesMapper->getTypeName();
-    $this->assertSame('Settings', $result);
+    $this->assertSame('Settings', (string) $result);
   }
 
   /**
diff --git a/core/modules/field/tests/src/Unit/FieldUninstallValidatorTest.php b/core/modules/field/tests/src/Unit/FieldUninstallValidatorTest.php
index ea9e0c89f6843a657c894ab22e2837b02e07e0e0..5f7a16d6f4e819ee3eead617366253ff7f9da9db 100644
--- a/core/modules/field/tests/src/Unit/FieldUninstallValidatorTest.php
+++ b/core/modules/field/tests/src/Unit/FieldUninstallValidatorTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Tests\field\Unit;
 
+use Drupal\simpletest\AssertHelperTrait;
 use Drupal\Tests\UnitTestCase;
 
 /**
@@ -15,6 +16,8 @@
  */
 class FieldUninstallValidatorTest extends UnitTestCase {
 
+  use AssertHelperTrait;
+
   /**
    * @var \Drupal\field\FieldUninstallValidator|\PHPUnit_Framework_MockObject_MockObject
    */
@@ -43,7 +46,7 @@ public function testValidateNoStorages() {
     $module = $this->randomMachineName();
     $expected = [];
     $reasons = $this->fieldUninstallValidator->validate($module);
-    $this->assertSame($expected, $reasons);
+    $this->assertSame($expected, $this->castSafeStrings($reasons));
   }
 
   /**
@@ -63,7 +66,7 @@ public function testValidateDeleted() {
     $module = $this->randomMachineName();
     $expected = ['Fields pending deletion'];
     $reasons = $this->fieldUninstallValidator->validate($module);
-    $this->assertSame($expected, $reasons);
+    $this->assertSame($expected, $this->castSafeStrings($reasons));
   }
 
   /**
@@ -83,7 +86,7 @@ public function testValidateNoDeleted() {
     $module = $this->randomMachineName();
     $expected = ['Fields type(s) in use'];
     $reasons = $this->fieldUninstallValidator->validate($module);
-    $this->assertSame($expected, $reasons);
+    $this->assertSame($expected, $this->castSafeStrings($reasons));
   }
 
 }
diff --git a/core/modules/filter/tests/src/Unit/FilterUninstallValidatorTest.php b/core/modules/filter/tests/src/Unit/FilterUninstallValidatorTest.php
index 4a4f7bac36d8fd65b6243ab38958c588674dd19b..8416d7e619525fd5d5eecae69b0f80159d6f829b 100644
--- a/core/modules/filter/tests/src/Unit/FilterUninstallValidatorTest.php
+++ b/core/modules/filter/tests/src/Unit/FilterUninstallValidatorTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Tests\filter\Unit;
 
+use Drupal\simpletest\AssertHelperTrait;
 use Drupal\Tests\UnitTestCase;
 
 /**
@@ -15,6 +16,8 @@
  */
 class FilterUninstallValidatorTest extends UnitTestCase {
 
+  use AssertHelperTrait;
+
   /**
    * @var \Drupal\filter\FilterUninstallValidator|\PHPUnit_Framework_MockObject_MockObject
    */
@@ -45,7 +48,7 @@ public function testValidateNoPlugins() {
     $module = $this->randomMachineName();
     $expected = [];
     $reasons = $this->filterUninstallValidator->validate($module);
-    $this->assertSame($expected, $reasons);
+    $this->assertSame($expected, $this->castSafeStrings($reasons));
   }
 
   /**
@@ -67,7 +70,7 @@ public function testValidateNoFormats() {
     $module = $this->randomMachineName();
     $expected = [];
     $reasons = $this->filterUninstallValidator->validate($module);
-    $this->assertSame($expected, $reasons);
+    $this->assertSame($expected, $this->castSafeStrings($reasons));
   }
 
   /**
@@ -162,7 +165,7 @@ public function testValidateNoMatchingFormats() {
       'Provides a filter plugin that is in use in the following filter formats: <em class="placeholder">Filter Format 1 Label, Filter Format 2 Label</em>'
     ];
     $reasons = $this->filterUninstallValidator->validate($this->randomMachineName());
-    $this->assertSame($expected, $reasons);
+    $this->assertSame($expected, $this->castSafeStrings($reasons));
   }
 
 }
diff --git a/core/modules/forum/tests/src/Unit/ForumUninstallValidatorTest.php b/core/modules/forum/tests/src/Unit/ForumUninstallValidatorTest.php
index 560a872834e48d5581688e9519bbde1aa5281a7b..11639bc95799359f4fe6e36119f8594ea6bab846 100644
--- a/core/modules/forum/tests/src/Unit/ForumUninstallValidatorTest.php
+++ b/core/modules/forum/tests/src/Unit/ForumUninstallValidatorTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Tests\forum\Unit;
 
+use Drupal\simpletest\AssertHelperTrait;
 use Drupal\Tests\UnitTestCase;
 
 /**
@@ -15,6 +16,8 @@
  */
 class ForumUninstallValidatorTest extends UnitTestCase {
 
+  use AssertHelperTrait;
+
   /**
    * @var \Drupal\forum\ForumUninstallValidator|\PHPUnit_Framework_MockObject_MockObject
    */
@@ -46,7 +49,7 @@ public function testValidateNotForum() {
     $module = 'not_forum';
     $expected = [];
     $reasons = $this->forumUninstallValidator->validate($module);
-    $this->assertSame($expected, $reasons);
+    $this->assertSame($expected, $this->castSafeStrings($reasons));
   }
 
   /**
@@ -69,7 +72,7 @@ public function testValidate() {
     $module = 'forum';
     $expected = [];
     $reasons = $this->forumUninstallValidator->validate($module);
-    $this->assertSame($expected, $reasons);
+    $this->assertSame($expected, $this->castSafeStrings($reasons));
   }
 
   /**
@@ -94,7 +97,7 @@ public function testValidateHasForumNodes() {
       'To uninstall Forum, first delete all <em>Forum</em> content',
     ];
     $reasons = $this->forumUninstallValidator->validate($module);
-    $this->assertSame($expected, $reasons);
+    $this->assertSame($expected, $this->castSafeStrings($reasons));
   }
 
   /**
@@ -129,7 +132,7 @@ public function testValidateHasTermsForVocabularyWithNodesAccess() {
       'To uninstall Forum, first delete all <a href="/path/to/vocabulary/overview"><em class="placeholder">Vocabulary label</em></a> terms',
     ];
     $reasons = $this->forumUninstallValidator->validate($module);
-    $this->assertSame($expected, $reasons);
+    $this->assertSame($expected, $this->castSafeStrings($reasons));
   }
 
   /**
@@ -163,7 +166,7 @@ public function testValidateHasTermsForVocabularyWithNodesNoAccess() {
       'To uninstall Forum, first delete all <em class="placeholder">Vocabulary label</em> terms',
     ];
     $reasons = $this->forumUninstallValidator->validate($module);
-    $this->assertSame($expected, $reasons);
+    $this->assertSame($expected, $this->castSafeStrings($reasons));
   }
 
   /**
@@ -197,7 +200,7 @@ public function testValidateHasTermsForVocabularyAccess() {
       'To uninstall Forum, first delete all <a href="/path/to/vocabulary/overview"><em class="placeholder">Vocabulary label</em></a> terms',
     ];
     $reasons = $this->forumUninstallValidator->validate($module);
-    $this->assertSame($expected, $reasons);
+    $this->assertSame($expected, $this->castSafeStrings($reasons));
   }
 
   /**
@@ -230,7 +233,7 @@ public function testValidateHasTermsForVocabularyNoAccess() {
       'To uninstall Forum, first delete all <em class="placeholder">Vocabulary label</em> terms',
     ];
     $reasons = $this->forumUninstallValidator->validate($module);
-    $this->assertSame($expected, $reasons);
+    $this->assertSame($expected, $this->castSafeStrings($reasons));
   }
 
 }
diff --git a/core/modules/system/src/Tests/Common/XssUnitTest.php b/core/modules/system/src/Tests/Common/XssUnitTest.php
index 2890f5537c6f9fbea192b7e28c71cf564039d24a..a02bada701ef021b6b5b100c4bfd60d13b75c9f0 100644
--- a/core/modules/system/src/Tests/Common/XssUnitTest.php
+++ b/core/modules/system/src/Tests/Common/XssUnitTest.php
@@ -40,8 +40,6 @@ function testT() {
     $this->assertEqual($text, 'Escaped text: &lt;script&gt;', 't replaces and escapes string.');
     $text = t('Placeholder text: %value', array('%value' => '<script>'));
     $this->assertEqual($text, 'Placeholder text: <em class="placeholder">&lt;script&gt;</em>', 't replaces, escapes and themes string.');
-    $text = t('Verbatim text: !value', array('!value' => '<script>'));
-    $this->assertEqual($text, 'Verbatim text: <script>', 't replaces verbatim string as-is.');
   }
 
   /**
diff --git a/core/modules/system/src/Tests/Theme/TwigTransTest.php b/core/modules/system/src/Tests/Theme/TwigTransTest.php
index 21aef18c0c22acb976c1a3459c12e254ffd5bf46..ac970cd267c5db796a5cca46d4343a34a9fd9605 100644
--- a/core/modules/system/src/Tests/Theme/TwigTransTest.php
+++ b/core/modules/system/src/Tests/Theme/TwigTransTest.php
@@ -138,18 +138,13 @@ protected function assertTwigTransTags() {
       '{{ token }} was successfully translated and prefixed with "@".'
     );
 
-    $this->assertRaw(
-      'PAS-THRU: &"<>',
-      '{{ token|passthrough }} was successfully translated and prefixed with "!".'
-    );
-
     $this->assertRaw(
       'PLAYSHOLDR: <em class="placeholder">&amp;&quot;&lt;&gt;</em>',
       '{{ token|placeholder }} was successfully translated and prefixed with "%".'
     );
 
     $this->assertRaw(
-      'DIS complex token HAZ LENGTH OV: 3. IT CONTAYNZ: <em class="placeholder">12345</em> AN &amp;&quot;&lt;&gt;. LETS PAS TEH BAD TEXT THRU: &"<>.',
+      'DIS complex token HAZ LENGTH OV: 3. IT CONTAYNZ: <em class="placeholder">12345</em> AN &amp;&quot;&lt;&gt;.',
       '{{ complex.tokens }} were successfully translated with appropriate prefixes.'
     );
 
@@ -253,14 +248,11 @@ protected function poFileContents($langcode) {
 msgid "Escaped: @string"
 msgstr "ESCAPEE: @string"
 
-msgid "Pass-through: !string"
-msgstr "PAS-THRU: !string"
-
 msgid "Placeholder: %string"
 msgstr "PLAYSHOLDR: %string"
 
-msgid "This @token.name has a length of: @count. It contains: %token.numbers and @token.bad_text. Lets pass the bad text through: !token.bad_text."
-msgstr "DIS @token.name HAZ LENGTH OV: @count. IT CONTAYNZ: %token.numbers AN @token.bad_text. LETS PAS TEH BAD TEXT THRU: !token.bad_text."
+msgid "This @token.name has a length of: @count. It contains: %token.numbers and @token.bad_text."
+msgstr "DIS @token.name HAZ LENGTH OV: @count. IT CONTAYNZ: %token.numbers AN @token.bad_text."
 
 msgctxt "Lolspeak"
 msgid "I have context."
diff --git a/core/modules/system/tests/modules/twig_theme_test/templates/twig_theme_test.trans.html.twig b/core/modules/system/tests/modules/twig_theme_test/templates/twig_theme_test.trans.html.twig
index 9623ba546d21c94b27025f5f3d7bc14459d1d0d7..dc08db39d13f98af083577ae2bde74ff7698f096 100644
--- a/core/modules/system/tests/modules/twig_theme_test/templates/twig_theme_test.trans.html.twig
+++ b/core/modules/system/tests/modules/twig_theme_test/templates/twig_theme_test.trans.html.twig
@@ -47,11 +47,6 @@
     Escaped: {{ string }}
   {% endtrans %}
 </div>
-<div>
-  {% trans %}
-    Pass-through: {{ string|passthrough }}
-  {% endtrans %}
-</div>
 <div>
   {% trans %}
     Placeholder: {{ string|placeholder }}
@@ -63,7 +58,7 @@
 {% set count = token|length %}
 <div>
   {% trans %}
-    This {{ token.name }} has a length of: {{ count }}. It contains: {{ token.numbers|placeholder }} and {{ token.bad_text }}. Lets pass the bad text through: {{ token.bad_text|passthrough }}.
+    This {{ token.name }} has a length of: {{ count }}. It contains: {{ token.numbers|placeholder }} and {{ token.bad_text }}.
   {% endtrans %}
 </div>
 
diff --git a/core/modules/user/src/Plugin/Validation/Constraint/UserMailRequired.php b/core/modules/user/src/Plugin/Validation/Constraint/UserMailRequired.php
index 57f5ced2ba1525a43ac28b936d60d501dcb14264..ae4973ca9b55dbaafe24d63559e15d136b2a937d 100644
--- a/core/modules/user/src/Plugin/Validation/Constraint/UserMailRequired.php
+++ b/core/modules/user/src/Plugin/Validation/Constraint/UserMailRequired.php
@@ -34,7 +34,7 @@ class UserMailRequired extends Constraint implements ConstraintValidatorInterfac
    *
    * @var string
    */
-  public $message = '!name field is required.';
+  public $message = '@name field is required.';
 
   /**
    * @var \Symfony\Component\Validator\ExecutionContextInterface
@@ -73,7 +73,7 @@ public function validate($items, Constraint $constraint) {
     $required = !(!$existing_value && \Drupal::currentUser()->hasPermission('administer users'));
 
     if ($required && (!isset($items) || $items->isEmpty())) {
-      $this->context->addViolation($this->message, ['!name' => Html::escape($account->getFieldDefinition('mail')->getLabel())]);
+      $this->context->addViolation($this->message, ['@name' => $account->getFieldDefinition('mail')->getLabel()]);
     }
   }
 
diff --git a/core/modules/user/tests/src/Unit/PermissionHandlerTest.php b/core/modules/user/tests/src/Unit/PermissionHandlerTest.php
index 5ac0f1be1081395ffd661fea0ff68f26ee713931..1b155e0a716b20550799700d150f36c637aed130 100644
--- a/core/modules/user/tests/src/Unit/PermissionHandlerTest.php
+++ b/core/modules/user/tests/src/Unit/PermissionHandlerTest.php
@@ -8,6 +8,9 @@
 namespace Drupal\Tests\user\Unit;
 
 use Drupal\Core\Extension\Extension;
+use Drupal\Core\StringTranslation\PluralTranslatableString;
+use Drupal\Core\StringTranslation\TranslatableString;
+use Drupal\Core\StringTranslation\TranslationInterface;
 use Drupal\Tests\UnitTestCase;
 use Drupal\user\PermissionHandler;
 use org\bovigo\vfs\vfsStream;
@@ -40,7 +43,7 @@ class PermissionHandlerTest extends UnitTestCase {
   /**
    * The mocked string translation.
    *
-   * @var \Drupal\Core\StringTranslation\TranslationInterface|\PHPUnit_Framework_MockObject_MockObject
+   * @var \Drupal\Tests\user\Unit\TestTranslationManager
    */
   protected $stringTranslation;
 
@@ -57,7 +60,7 @@ class PermissionHandlerTest extends UnitTestCase {
   protected function setUp() {
     parent::setUp();
 
-    $this->stringTranslation = $this->getStringTranslationStub();
+    $this->stringTranslation = new TestTranslationManager();
     $this->controllerResolver = $this->getMock('Drupal\Core\Controller\ControllerResolverInterface');
   }
 
@@ -407,3 +410,31 @@ public function titleDescriptionRestrictAccess() {
   }
 
 }
+
+/**
+ * Implements a translation manager in tests.
+ */
+class TestTranslationManager implements TranslationInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function translate($string, array $args = array(), array $options = array()) {
+    return new TranslatableString($string, $args, $options, $this);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function translateString(TranslatableString $translated_string) {
+    return $translated_string->getUntranslatedString();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function formatPlural($count, $singular, $plural, array $args = array(), array $options = array()) {
+    return new PluralTranslatableString($count, $singular, $plural, $args, $options, $this);
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php b/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php
index 12f201256f90b303d7327d34d2be4a999a668cf3..279bdd49806d685f5a209fa5873d3669f40ae725 100644
--- a/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php
+++ b/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php
@@ -231,8 +231,6 @@ function providerFormat() {
     $tests[] = array('Escaped text: @value', array('@value' => SafeMarkupTestSafeString::create('<span>Safe HTML</span>')), 'Escaped text: <span>Safe HTML</span>', 'SafeMarkup::format does not escape an already safe string.', TRUE);
     $tests[] = array('Placeholder text: %value', array('%value' => '<script>'), 'Placeholder text: <em class="placeholder">&lt;script&gt;</em>', 'SafeMarkup::format replaces, escapes and themes string.', TRUE);
     $tests[] = array('Placeholder text: %value', array('%value' => SafeMarkupTestSafeString::create('<span>Safe HTML</span>')), 'Placeholder text: <em class="placeholder"><span>Safe HTML</span></em>', 'SafeMarkup::format does not escape an already safe string themed as a placeholder.', TRUE);
-    $tests[] = array('Verbatim text: !value', array('!value' => '<script>'), 'Verbatim text: <script>', 'SafeMarkup::format replaces verbatim string as-is.', FALSE);
-    $tests[] = array('Verbatim text: !value', array('!value' => SafeMarkupTestSafeString::create('<span>Safe HTML</span>')), 'Verbatim text: <span>Safe HTML</span>', 'SafeMarkup::format replaces verbatim string as-is.', TRUE);
 
     $tests['javascript-protocol-url'] = ['Simple text <a href=":url">giraffe</a>', [':url' => 'javascript://example.com?foo&bar'], 'Simple text <a href="//example.com?foo&amp;bar">giraffe</a>', 'Support for filtering bad protocols', TRUE];
     $tests['external-url'] = ['Simple text <a href=":url">giraffe</a>', [':url' => 'http://example.com?foo&bar'], 'Simple text <a href="http://example.com?foo&amp;bar">giraffe</a>', 'Support for filtering bad protocols', TRUE];
diff --git a/core/tests/Drupal/Tests/Core/Annotation/TranslationTest.php b/core/tests/Drupal/Tests/Core/Annotation/TranslationTest.php
index f06254c2abbe6601d316f8f43a8ad234f4cc50eb..b0379a18989920d8c6dda7c68bf54731806dcc30 100644
--- a/core/tests/Drupal/Tests/Core/Annotation/TranslationTest.php
+++ b/core/tests/Drupal/Tests/Core/Annotation/TranslationTest.php
@@ -66,9 +66,9 @@ public function providerTestGet() {
     $random_html_entity = '&' . $random;
     $data[] = array(
       array(
-        'value' => 'Foo !bar @baz %qux',
+        'value' => 'Foo @bar @baz %qux',
         'arguments' => array(
-          '!bar' => $random,
+          '@bar' => $random,
           '@baz' => $random_html_entity,
           '%qux' => $random_html_entity,
         ),
diff --git a/core/tests/Drupal/Tests/Core/Extension/RequiredModuleUninstallValidatorTest.php b/core/tests/Drupal/Tests/Core/Extension/RequiredModuleUninstallValidatorTest.php
index a5f6fbba7f7a99e49bc69e5dcde4491ae16a3dd8..74ea1ddb8fcbbbfa0a719ac5a3186a46600ca8cf 100644
--- a/core/tests/Drupal/Tests/Core/Extension/RequiredModuleUninstallValidatorTest.php
+++ b/core/tests/Drupal/Tests/Core/Extension/RequiredModuleUninstallValidatorTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Tests\Core\Extension;
 
+use Drupal\simpletest\AssertHelperTrait;
 use Drupal\Tests\UnitTestCase;
 
 /**
@@ -15,6 +16,8 @@
  */
 class RequiredModuleUninstallValidatorTest extends UnitTestCase {
 
+  use AssertHelperTrait;
+
   /**
    * @var \Drupal\Core\Extension\RequiredModuleUninstallValidator|\PHPUnit_Framework_MockObject_MockObject
    */
@@ -73,7 +76,7 @@ public function testValidateRequired() {
 
     $expected = ["The $module module is required"];
     $reasons = $this->uninstallValidator->validate($module);
-    $this->assertSame($expected, $reasons);
+    $this->assertSame($expected, $this->castSafeStrings($reasons));
   }
 
 }
diff --git a/core/tests/Drupal/Tests/Core/StringTranslation/TranslationManagerTest.php b/core/tests/Drupal/Tests/Core/StringTranslation/TranslationManagerTest.php
index 7eaa99dc63664966b770aa8bbd1b4277b0a69600..730437b560af5becf86dfbdd4fffea554bef1ef4 100644
--- a/core/tests/Drupal/Tests/Core/StringTranslation/TranslationManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/StringTranslation/TranslationManagerTest.php
@@ -25,6 +25,9 @@ class TranslationManagerTest extends UnitTestCase {
    */
   protected $translationManager;
 
+  /**
+   * {@inheritdoc}
+   */
   protected function setUp() {
     $this->translationManager = new TestTranslationManager();
   }
@@ -35,19 +38,18 @@ protected function setUp() {
    */
   public function providerTestFormatPlural() {
     return array(
-      array(1, 'Singular', '@count plural', array(), array(), 'Singular', TRUE),
-      array(2, 'Singular', '@count plural', array(), array(), '2 plural', TRUE),
+      [1, 'Singular', '@count plural', array(), array(), 'Singular'],
+      [2, 'Singular', '@count plural', array(), array(), '2 plural'],
       // @todo support locale_get_plural
-      array(2, 'Singular', '@count @arg', array('@arg' => '<script>'), array(), '2 &lt;script&gt;', TRUE),
-      array(2, 'Singular', '@count %arg', array('%arg' => '<script>'), array(), '2 <em class="placeholder">&lt;script&gt;</em>', TRUE),
-      array(2, 'Singular', '@count !arg', array('!arg' => '<script>'), array(), '2 <script>', FALSE),
+      [2, 'Singular', '@count @arg', array('@arg' => '<script>'), array(), '2 &lt;script&gt;'],
+      [2, 'Singular', '@count %arg', array('%arg' => '<script>'), array(), '2 <em class="placeholder">&lt;script&gt;</em>'],
     );
   }
 
   /**
    * @dataProvider providerTestFormatPlural
    */
-  public function testFormatPlural($count, $singular, $plural, array $args = array(), array $options = array(), $expected, $safe) {
+  public function testFormatPlural($count, $singular, $plural, array $args = array(), array $options = array(), $expected) {
     $translator = $this->getMock('\Drupal\Core\StringTranslation\Translator\TranslatorInterface');
     $translator->expects($this->once())
       ->method('getStringTranslation')
@@ -57,7 +59,7 @@ public function testFormatPlural($count, $singular, $plural, array $args = array
     $this->translationManager->addTranslator($translator);
     $result = $this->translationManager->formatPlural($count, $singular, $plural, $args, $options);
     $this->assertEquals($expected, $result);
-    $this->assertEquals(SafeMarkup::isSafe($result), $safe);
+    $this->assertTrue(SafeMarkup::isSafe($result));
   }
 
   /**
@@ -69,20 +71,13 @@ public function testFormatPlural($count, $singular, $plural, array $args = array
    *   An associative array of replacements to make after translation.
    * @param string $expected_string
    *   The expected translated string value.
-   * @param bool $returns_translation_wrapper
-   *   Whether we are expecting a TranslatableString object to be returned.
    *
    * @dataProvider providerTestTranslatePlaceholder
    */
-  public function testTranslatePlaceholder($string, array $args = array(), $expected_string, $returns_translation_wrapper) {
+  public function testTranslatePlaceholder($string, array $args = array(), $expected_string) {
     $actual = $this->translationManager->translate($string, $args);
-    if ($returns_translation_wrapper) {
-      $this->assertInstanceOf(SafeStringInterface::class, $actual);
-    }
-    else {
-      $this->assertInternalType('string', $actual);
-    }
-    $this->assertEquals($expected_string, $actual);
+    $this->assertInstanceOf(SafeStringInterface::class, $actual);
+    $this->assertEquals($expected_string, (string) $actual);
   }
 
   /**
@@ -92,10 +87,10 @@ public function testTranslatePlaceholder($string, array $args = array(), $expect
    */
   public function providerTestTranslatePlaceholder() {
     return [
-      ['foo @bar', ['@bar' => 'bar'], 'foo bar', TRUE],
-      ['bar !baz', ['!baz' => 'baz'], 'bar baz', FALSE],
-      ['bar @bar !baz', ['@bar' => 'bar', '!baz' => 'baz'], 'bar bar baz', FALSE],
-      ['bar !baz @bar', ['!baz' => 'baz', '@bar' => 'bar'], 'bar baz bar', FALSE],
+      ['foo @bar', ['@bar' => 'bar'], 'foo bar'],
+      ['bar %baz', ['%baz' => 'baz'], 'bar <em class="placeholder">baz</em>'],
+      ['bar @bar %baz', ['@bar' => 'bar', '%baz' => 'baz'], 'bar bar <em class="placeholder">baz</em>'],
+      ['bar %baz @bar', ['%baz' => 'baz', '@bar' => 'bar'], 'bar <em class="placeholder">baz</em> bar'],
     ];
   }
 }
diff --git a/core/tests/Drupal/Tests/UnitTestCase.php b/core/tests/Drupal/Tests/UnitTestCase.php
index 9d805be233693533c0ee7debc6643ac17e8449b7..5b950d47a9581469f7082c4c9c6e843a77544e29 100644
--- a/core/tests/Drupal/Tests/UnitTestCase.php
+++ b/core/tests/Drupal/Tests/UnitTestCase.php
@@ -221,10 +221,7 @@ public function getStringTranslationStub() {
     $translation->expects($this->any())
       ->method('translate')
       ->willReturnCallback(function ($string, array $args = array(), array $options = array()) use ($translation) {
-        $wrapper = new TranslatableString($string, $args, $options, $translation);
-        // Pretend everything is not safe.
-        // @todo https://www.drupal.org/node/2570037 return the wrapper instead.
-        return (string) $wrapper;
+        return new TranslatableString($string, $args, $options, $translation);
       });
     $translation->expects($this->any())
       ->method('translateString')