diff --git a/core/lib/Drupal/Core/OutputFormatter/DynamicStringInterface.php b/core/lib/Drupal/Core/OutputFormatter/DynamicStringInterface.php
new file mode 100644
index 0000000..f96029f
--- /dev/null
+++ b/core/lib/Drupal/Core/OutputFormatter/DynamicStringInterface.php
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\OutputFormatter\DynamicStringInterface.
+ */
+
+namespace Drupal\Core\OutputFormatter;
+
+/**
+ * Common interface for strings containing dynamic parts.
+ */
+interface DynamicStringInterface {
+
+  /**
+   * Gets the raw string pattern stored in this string wrapper.
+   *
+   * @return string
+   *   The string pattern stored in this wrapper with the placeholder values not
+   *   replaced.
+   */
+  public function getString();
+
+  /**
+   * Gets all arguments from this string wrapper.
+   *
+   * @return mixed[]
+   *   The array of arguments.
+   */
+  public function getArguments();
+
+  /**
+   * Renders the object as a string.
+   *
+   * @param string $content_type
+   *   (optional) The output content type to be used to format the string. If no
+   *   value is provided 'text/html' is assumed.
+   *
+   * @return string
+   *   The translated string.
+   */
+  public function render($content_type = NULL);
+
+}
diff --git a/core/lib/Drupal/Core/OutputFormatter/HtmlAttributeOutputFormatter.php b/core/lib/Drupal/Core/OutputFormatter/HtmlAttributeOutputFormatter.php
new file mode 100644
index 0000000..463f808
--- /dev/null
+++ b/core/lib/Drupal/Core/OutputFormatter/HtmlAttributeOutputFormatter.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\OutputFormatter\HtmlAttributeOutputFormatter.
+ */
+
+namespace Drupal\Core\OutputFormatter;
+
+use Drupal\Component\Utility\Html;
+
+/**
+ * HTML attribute output formatter.
+ */
+class HtmlAttributeOutputFormatter extends PlainTextOutputFormatter {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function format(DynamicStringInterface $string) {
+    // TODO We need a better handling of URLs.
+    $output = parent::format($string);
+    return Html::escape($output);
+  }
+
+}
diff --git a/core/lib/Drupal/Core/OutputFormatter/HtmlMarkupOutputFormatter.php b/core/lib/Drupal/Core/OutputFormatter/HtmlMarkupOutputFormatter.php
new file mode 100644
index 0000000..364bcb0
--- /dev/null
+++ b/core/lib/Drupal/Core/OutputFormatter/HtmlMarkupOutputFormatter.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\OutputFormatter\HtmlMarkupOutputFormatter.
+ */
+
+namespace Drupal\Core\OutputFormatter;
+
+use Drupal\Component\Utility\PlaceholderTrait;
+
+/**
+ * HTML markup output formatter.
+ */
+class HtmlMarkupOutputFormatter implements OutputFormatterInterface {
+
+  use PlaceholderTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function format(DynamicStringInterface $string) {
+    $output = $string->getString();
+    $args = $string->getArguments();
+    if (!empty($args)) {
+      $output = static::placeholderFormat($output, $args);
+    }
+    return $output;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/OutputFormatter/OutputFormatterInterface.php b/core/lib/Drupal/Core/OutputFormatter/OutputFormatterInterface.php
new file mode 100644
index 0000000..53e15c5
--- /dev/null
+++ b/core/lib/Drupal/Core/OutputFormatter/OutputFormatterInterface.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\OutputFormatter\OutputFormatterInterface.
+ */
+
+namespace Drupal\Core\OutputFormatter;
+
+/**
+ * Common interface for output formatters.
+ */
+interface OutputFormatterInterface {
+
+  /**
+   * Formats a dynamic string.
+   *
+   * @param \Drupal\Core\OutputFormatter\DynamicStringInterface $string
+   *   A dynamic string wrapper.
+   *
+   * @return string
+   *   A string encoded in the content type assumed by the current formatter.
+   */
+  public function format(DynamicStringInterface $string);
+
+}
diff --git a/core/lib/Drupal/Core/OutputFormatter/PlainTextOutputFormatter.php b/core/lib/Drupal/Core/OutputFormatter/PlainTextOutputFormatter.php
new file mode 100644
index 0000000..b44e1e4
--- /dev/null
+++ b/core/lib/Drupal/Core/OutputFormatter/PlainTextOutputFormatter.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\OutputFormatter\PlainTextOutputFormatter.
+ */
+
+namespace Drupal\Core\OutputFormatter;
+
+use Drupal\Component\Utility\Html;
+
+/**
+ * Plain text output formatter.
+ */
+class PlainTextOutputFormatter extends HtmlMarkupOutputFormatter {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function format(DynamicStringInterface $string) {
+    // String patterns are assumed to be HTML markup.
+    $output = parent::format($string);
+    return $this->toPlainText($output);
+  }
+
+  /**
+   * Converts the HTML output to plain text.
+   *
+   * @param string $output
+   *   The HTML output string.
+   *
+   * @return string
+   *   The plain text content.
+   */
+  protected function toPlainText($output) {
+    // TODO Make this logic swappable/injectable.
+    return Html::decodeEntities(strip_tags($output));
+  }
+
+}
diff --git a/core/lib/Drupal/Core/StringTranslation/TranslationWrapper.php b/core/lib/Drupal/Core/StringTranslation/TranslationWrapper.php
index 7b4abfc..37a5ffa 100644
--- a/core/lib/Drupal/Core/StringTranslation/TranslationWrapper.php
+++ b/core/lib/Drupal/Core/StringTranslation/TranslationWrapper.php
@@ -10,6 +10,10 @@
 use Drupal\Component\Utility\PlaceholderTrait;
 use Drupal\Component\Utility\SafeStringInterface;
 use Drupal\Component\Utility\ToStringTrait;
+use Drupal\Core\OutputFormatter\DynamicStringInterface;
+use Drupal\Core\OutputFormatter\HtmlAttributeOutputFormatter;
+use Drupal\Core\OutputFormatter\HtmlMarkupOutputFormatter;
+use Drupal\Core\OutputFormatter\PlainTextOutputFormatter;
 
 /**
  * Provides translatable string class.
@@ -23,7 +27,7 @@
  * @see \Drupal\Core\StringTranslation\TranslationManager::translateString()
  * @see \Drupal\Core\Annotation\Translation
  */
-class TranslationWrapper implements SafeStringInterface {
+class TranslationWrapper implements SafeStringInterface, DynamicStringInterface {
 
   use PlaceholderTrait;
   use ToStringTrait;
@@ -96,6 +100,13 @@ public function getUntranslatedString() {
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function getString() {
+    return $this->getStringTranslation()->translateString($this);
+  }
+
+  /**
    * Gets a specific option from this translation wrapper.
    *
    * @param $name
@@ -119,34 +130,37 @@ public function getOptions() {
   }
 
   /**
-   * Gets all argments from this translation wrapper.
-   *
-   * @return mixed[]
-   *   The array of arguments.
+   * {@inheritdoc}
    */
   public function getArguments() {
     return $this->arguments;
   }
 
   /**
-   * Renders the object as a string.
+   * {@inheritdoc}
+   */
+  public function render($content_type = NULL) {
+    return $this->string === '' ? '' : $this->getOutputFormatter($content_type)->format($this);
+  }
+
+  /**
+   * TODO
    *
-   * @return string
-   *   The translated string.
+   * This should be an injected factory, likely a plugin manager.
+   *
+   * @return \Drupal\Core\OutputFormatter\OutputFormatterInterface
    */
-  public function render() {
-    if (!isset($this->translatedString)) {
-      $this->translatedString = $this->getStringTranslation()->translateString($this);
-    }
+  protected function getOutputFormatter($content_type) {
+    switch ($content_type) {
+      case 'text/plain':
+        return new PlainTextOutputFormatter();
+
+      case 'text/html; x-drupal-context: attribute':
+        return new HtmlAttributeOutputFormatter();
 
-    // Handle any replacements.
-    // @todo https://www.drupal.org/node/2509218 Note that the argument
-    //   replacement is not stored so that different sanitization strategies can
-    //   be used in different contexts.
-    if ($args = $this->getArguments()) {
-      return $this->placeholderFormat($this->translatedString, $args);
+      default:
+        return new HtmlMarkupOutputFormatter();
     }
-    return $this->translatedString;
   }
 
   /**
diff --git a/core/tests/Drupal/Tests/Core/OutputFormatter/HtmlAttributeOutputFormatterTest.php b/core/tests/Drupal/Tests/Core/OutputFormatter/HtmlAttributeOutputFormatterTest.php
new file mode 100644
index 0000000..b69408c
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/OutputFormatter/HtmlAttributeOutputFormatterTest.php
@@ -0,0 +1,43 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\OutputFormatter\HtmlAttributeOutputFormatterTest.
+ */
+
+namespace Drupal\Tests\Core\OutputFormatter;
+
+use Drupal\Core\OutputFormatter\HtmlAttributeOutputFormatter;
+use Drupal\Core\Render\SafeString;
+
+/**
+ * @coversDefaultClass \Drupal\Core\OutputFormatter\HtmlAttributeOutputFormatter
+ * @group OutputFormatter
+ */
+class HtmlAttributeOutputFormatterTest extends OutputFormatterTestBase {
+
+  /**
+   * {@inheritdoc}
+   *
+   * @dataProvider providerTestFormat
+   *
+   * @covers ::format
+   */
+  public function testFormat($string, array $arguments, $expected) {
+    $dynamic_string = $this->getDynamicString($string, $arguments);
+    $formatter = new HtmlAttributeOutputFormatter();
+    $this->assertEquals($expected, $formatter->format($dynamic_string));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function providerTestFormat() {
+    $expected = 'The &lt;em&gt; tag makes your text look like &quot;this&quot;.';
+    return [
+      ['The &lt;em&gt; tag makes your text look like <em>"this"</em>.', [], $expected],
+      ['The @tag tag makes your text look like @result.', ['@tag' =>'<em>', '@result' => SafeString::create('<em>"this"</em>')], $expected],
+    ];
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/OutputFormatter/HtmlMarkupOutputFormatterTest.php b/core/tests/Drupal/Tests/Core/OutputFormatter/HtmlMarkupOutputFormatterTest.php
new file mode 100644
index 0000000..d7b896d
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/OutputFormatter/HtmlMarkupOutputFormatterTest.php
@@ -0,0 +1,43 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\OutputFormatter\HtmlMarkupOutputFormatterTest.
+ */
+
+namespace Drupal\Tests\Core\OutputFormatter;
+
+use Drupal\Core\OutputFormatter\HtmlMarkupOutputFormatter;
+use Drupal\Core\Render\SafeString;
+
+/**
+ * @coversDefaultClass \Drupal\Core\OutputFormatter\HtmlMarkupOutputFormatter
+ * @group OutputFormatter
+ */
+class HtmlMarkupOutputFormatterTest extends OutputFormatterTestBase {
+
+  /**
+   * {@inheritdoc}
+   *
+   * @dataProvider providerTestFormat
+   *
+   * @covers ::format
+   */
+  public function testFormat($string, array $arguments, $expected) {
+    $dynamic_string = $this->getDynamicString($string, $arguments);
+    $formatter = new HtmlMarkupOutputFormatter();
+    $this->assertEquals($expected, $formatter->format($dynamic_string));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function providerTestFormat() {
+    $expected = 'The &lt;em&gt; tag makes your text look like <em>"this"</em>.';
+    return [
+      [$expected, [], $expected],
+      ['The @tag tag makes your text look like @result.', ['@tag' =>'<em>', '@result' => SafeString::create('<em>"this"</em>')], $expected],
+    ];
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/OutputFormatter/OutputFormatterTestBase.php b/core/tests/Drupal/Tests/Core/OutputFormatter/OutputFormatterTestBase.php
new file mode 100644
index 0000000..1e21350
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/OutputFormatter/OutputFormatterTestBase.php
@@ -0,0 +1,59 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\OutputFormatter\OutputFormatterTestBase.
+ */
+
+namespace Drupal\Tests\Core\OutputFormatter;
+
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * Base class for output formatter tests.
+ */
+abstract class OutputFormatterTestBase extends UnitTestCase {
+
+  /**
+   * Tests output formatting.
+   *
+   * @param string $string
+   *   The raw string with placeholders.
+   * @param mixed[] $arguments
+   *   The placeholder values.
+   * @param $expected
+   *   The expected encoded string.
+   */
+  abstract public function testFormat($string, array $arguments, $expected);
+
+  /**
+   * Data provider for ::testFormat().
+   */
+  abstract public function providerTestFormat();
+
+  /**
+   * Returns a dynamic string wrapper instance.
+   *
+   * @param string $string
+   *   The raw string with placeholders.
+   * @param mixed[] $arguments
+   *   The placeholder values.
+   *
+   * @return \Drupal\Core\OutputFormatter\DynamicStringInterface
+   *   The dynamic string wrapper.
+   */
+  protected function getDynamicString($string, $arguments) {
+    $dynamic_string = $this->getMock('\Drupal\Core\OutputFormatter\DynamicStringInterface');
+
+    $dynamic_string->expects($this->any())
+      ->method('getString')
+      ->willReturn($string);
+
+    $dynamic_string->expects($this->any())
+      ->method('getArguments')
+      ->willReturn($arguments);
+
+    return $dynamic_string;
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/OutputFormatter/PlainTextOutputFormatterTest.php b/core/tests/Drupal/Tests/Core/OutputFormatter/PlainTextOutputFormatterTest.php
new file mode 100644
index 0000000..1354ab7
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/OutputFormatter/PlainTextOutputFormatterTest.php
@@ -0,0 +1,43 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\OutputFormatter\PlainTextOutputFormatterTest.
+ */
+
+namespace Drupal\Tests\Core\OutputFormatter;
+
+use Drupal\Core\OutputFormatter\PlainTextOutputFormatter;
+use Drupal\Core\Render\SafeString;
+
+/**
+ * @coversDefaultClass \Drupal\Core\OutputFormatter\PlainTextOutputFormatter
+ * @group OutputFormatter
+ */
+class PlainTextOutputFormatterTest extends OutputFormatterTestBase {
+
+  /**
+   * {@inheritdoc}
+   *
+   * @dataProvider providerTestFormat
+   *
+   * @covers ::format
+   */
+  public function testFormat($string, array $arguments, $expected) {
+    $dynamic_string = $this->getDynamicString($string, $arguments);
+    $formatter = new PlainTextOutputFormatter();
+    $this->assertEquals($expected, $formatter->format($dynamic_string));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function providerTestFormat() {
+    $expected = 'The <em> tag makes your text look like "this".';
+    return [
+      ['The &lt;em&gt; tag makes your text look like <em>"this"</em>.', [], $expected],
+      ['The @tag tag makes your text look like @result.', ['@tag' =>'<em>', '@result' => SafeString::create('<em>"this"</em>')], $expected],
+    ];
+  }
+
+}
