diff --git a/core/lib/Drupal/Core/Database/Install/Tasks.php b/core/lib/Drupal/Core/Database/Install/Tasks.php
index fed2c5e..c82a560 100644
--- a/core/lib/Drupal/Core/Database/Install/Tasks.php
+++ b/core/lib/Drupal/Core/Database/Install/Tasks.php
@@ -92,14 +92,16 @@ protected function hasPdoDriver() {
    * Assert test as failed.
    */
   protected function fail($message) {
-    $this->results[$message] = FALSE;
+    // @todo fix this
+    $this->results[(string) $message] = FALSE;
   }
 
   /**
    * Assert test as a pass.
    */
   protected function pass($message) {
-    $this->results[$message] = TRUE;
+    // @todo fix this
+    $this->results[(string) $message] = TRUE;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/StringTranslation/TranslationManager.php b/core/lib/Drupal/Core/StringTranslation/TranslationManager.php
index 87ff63e..279e8f4 100644
--- a/core/lib/Drupal/Core/StringTranslation/TranslationManager.php
+++ b/core/lib/Drupal/Core/StringTranslation/TranslationManager.php
@@ -140,16 +140,7 @@ 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)) {
-      // This is assumed to be safe because translate should only be called
-      // with strings defined in code.
-      // @see \Drupal\Core\StringTranslation\TranslationInterface::translate()
-      return SafeMarkup::set($string);
-    }
-    else {
-      return SafeMarkup::format($string, $args);
-    }
+    return new TranslationWrapper($string, $args, $options);
   }
 
   /**
@@ -165,8 +156,10 @@ public function translate($string, array $args = array(), array $options = array
    *
    * @return string
    *   The translated string.
+   *
+   * @internal
    */
-  protected function doTranslate($string, array $options = array()) {
+  public function doTranslate($string, array $options = array()) {
     // Merge in defaults.
     if (empty($options['langcode'])) {
       $options['langcode'] = $this->defaultLangcode;
diff --git a/core/lib/Drupal/Core/StringTranslation/TranslationWrapper.php b/core/lib/Drupal/Core/StringTranslation/TranslationWrapper.php
index 6bf591a..e346e99 100644
--- a/core/lib/Drupal/Core/StringTranslation/TranslationWrapper.php
+++ b/core/lib/Drupal/Core/StringTranslation/TranslationWrapper.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\Core\StringTranslation;
 
+use Drupal\Component\Utility\Html;
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\SafeStringInterface;
 
 /**
@@ -18,7 +20,7 @@
  *
  * @see \Drupal\Core\Annotation\Translation
  */
-class TranslationWrapper implements SafeStringInterface {
+class TranslationWrapper implements SafeStringInterface, \JsonSerializable {
   use StringTranslationTrait;
 
   /**
@@ -108,7 +110,35 @@ public function __toString() {
    *   The translated string.
    */
   public function render() {
-    return $this->t($this->string, $this->arguments, $this->options);
+    $string = $this->getStringTranslation()->doTranslate($this->string, $this->options);
+    if (!empty($this->arguments)) {
+      // Transform arguments before inserting them.
+      $args = $this->arguments;
+      foreach ($args as $key => $value) {
+        switch ($key[0]) {
+          case '@':
+            if (!SafeMarkup::isSafe($value)) {
+              // Escaped only.
+              $args[$key] = Html::escape($value);
+            }
+            break;
+
+          case '%':
+          default:
+            // Escaped and placeholder.
+            if (!SafeMarkup::isSafe($value)) {
+              $value = Html::escape($value);
+            }
+            $args[$key] = '<em class="placeholder">' . SafeMarkup::escape($value) . '</em>';
+            break;
+
+          case '!':
+            // Pass-through. This should be safe.
+        }
+      }
+      $string = strtr($string, $args);
+    }
+    return $string;
   }
 
   /**
@@ -118,4 +148,8 @@ public function __sleep() {
     return array('string', 'arguments', 'options');
   }
 
+  public function jsonSerialize() {
+    return $this->render();
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Template/Attribute.php b/core/lib/Drupal/Core/Template/Attribute.php
index 8fe6788..687f026 100644
--- a/core/lib/Drupal/Core/Template/Attribute.php
+++ b/core/lib/Drupal/Core/Template/Attribute.php
@@ -109,7 +109,7 @@ protected function createAttributeValue($name, $value) {
     elseif (is_bool($value)) {
       $value = new AttributeBoolean($name, $value);
     }
-    elseif (!is_object($value)) {
+    elseif (!is_object($value) || $value instanceof SafeStringInterface) {
       $value = new AttributeString($name, $value);
     }
     return $value;
