diff --git a/core/lib/Drupal/Component/Utility/FormattableString.php b/core/lib/Drupal/Component/Utility/FormattableString.php
index 2039584..a15cadd 100644
--- a/core/lib/Drupal/Component/Utility/FormattableString.php
+++ b/core/lib/Drupal/Component/Utility/FormattableString.php
@@ -13,7 +13,7 @@
  * When cast to a string, this object replaces variable placeholders in the
  * string with the arguments passed in during construction and escapes the
  * values so they can be safely displayed as HTML. See the documentation of
- * \Drupal\Component\Utility\PlaceholderTrait::placeholderFormat() for
+ * \Drupal\Component\Utility\FormattableString::placeholderFormat() for
  * details on the supported placeholders and how to use them securely. Incorrect
  * use of this class can result in security vulnerabilities.
  *
@@ -37,12 +37,10 @@
  *
  * @see \Drupal\Core\StringTranslation\TranslatableString
  * @see \Drupal\Core\StringTranslation\PluralTranslatableString
- * @see \Drupal\Component\Utility\PlaceholderTrait::placeholderFormat()
+ * @see \Drupal\Component\Utility\FormattableString::placeholderFormat()
  */
 class FormattableString implements SafeStringInterface {
 
-  use PlaceholderTrait;
-
   /**
    * The arguments to replace placeholders with.
    *
@@ -56,12 +54,12 @@ class FormattableString implements SafeStringInterface {
    * @param string $string
    *   A string containing placeholders. The string itself will not be escaped,
    *   any unsafe content must be in $args and inserted via placeholders.
-   * @param array $args
+   * @param array $arguments
    *   An array with placeholder replacements, keyed by placeholder. See
-   *   \Drupal\Component\Utility\PlaceholderTrait::placeholderFormat() for
+   *   \Drupal\Component\Utility\FormattableString::placeholderFormat() for
    *   additional information about placeholders.
    *
-   * @see \Drupal\Component\Utility\PlaceholderTrait::placeholderFormat()
+   * @see \Drupal\Component\Utility\FormattableString::placeholderFormat()
    */
   public function __construct($string, array $arguments) {
     $this->string = (string) $string;
@@ -95,4 +93,166 @@ public function jsonSerialize() {
     return $this->__toString();
   }
 
+  /**
+   * Formats a string by replacing variable placeholders.
+   *
+   * This trait is designed for formatting messages that are mostly text, not as
+   * an HTML template language. As such:
+   * - The passed in string should contain no (or minimal) HTML.
+   * - Variable placeholders should not be used within the "<" and ">" of an
+   *   HTML tag, such as in HTML attribute values. This would be a security
+   *   risk. Examples:
+   *   @code
+   *     // Insecure (placeholder within "<" and ">"):
+   *     $this->placeholderFormat('<@variable>text</@variable>', ['@variable' => $variable]);
+   *     // Insecure (placeholder within "<" and ">"):
+   *     $this->placeholderFormat('<a @variable>link text</a>', ['@variable' => $variable]);
+   *     // Insecure (placeholder within "<" and ">"):
+   *     $this->placeholderFormat('<a title="@variable">link text</a>', ['@variable' => $variable]);
+   *   @endcode
+   *   Only the "href" attribute is supported via the special ":variable"
+   *   placeholder, to allow simple links to be inserted:
+   *   @code
+   *     // Secure (usage of ":variable" placeholder for href attribute):
+   *     $this->placeholderFormat('<a href=":variable">link text</a>', [':variable' , $variable]);
+   *     // Secure (usage of ":variable" placeholder for href attribute):
+   *     $this->placeholderFormat('<a href=":variable" title="static text">link text</a>', [':variable' => $variable]);
+   *     // Insecure (the "@variable" placeholder does not filter dangerous
+   *     // protocols):
+   *     $this->placeholderFormat('<a href="@variable">link text</a>', ['@variable' => $variable]);
+   *     // Insecure ("@variable" placeholder within "<" and ">"):
+   *     $this->placeholderFormat('<a href=":url" title="@variable">link text</a>', [':url' => $url, '@variable' => $variable]);
+   *   @endcode
+   * To build non-minimal HTML, use an HTML template language such as Twig,
+   * rather than this trait.
+   *
+   * @param string $string
+   *   A string containing placeholders. The string itself is expected to be
+   *   safe and correct HTML. Any unsafe content must be in $args and
+   *   inserted via placeholders.
+   * @param array $args
+   *   An associative array of replacements. Each array key should be the same
+   *   as a placeholder in $string. The corresponding value should be a string
+   *   or an object that implements
+   *   \Drupal\Component\Utility\SafeStringInterface. The value replaces the
+   *   placeholder in $string. Sanitization and formatting will be done before
+   *   replacement. The type of sanitization and formatting depends on the first
+   *   character of the key:
+   *   - @variable: When the placeholder replacement value is:
+   *     - A string, the replaced value in the returned string will be sanitized
+   *       using \Drupal\Component\Utility\Html::escape().
+   *     - A SafeStringInterface object, the replaced value in the returned
+   *       string will not be sanitized.
+   *     - A SafeStringInterface object cast to a string, the replaced value in
+   *       the returned string be forcibly sanitized using
+   *       \Drupal\Component\Utility\Html::escape().
+   *       @code
+   *         $this->placeholderFormat('This will force HTML-escaping of the replacement value: @text', ['@text' => (string) $safe_string_interface_object));
+   *       @endcode
+   *     Use this placeholder as the default choice for anything displayed on
+   *     the site, but not within HTML attributes, JavaScript, or CSS. Doing so
+   *     is a security risk.
+   *   - %variable: Use when the replacement value is to be wrapped in <em>
+   *     tags.
+   *     A call like:
+   *     @code
+   *       $string = "%output_text";
+   *       $arguments = ['output_text' => 'text output here.'];
+   *       $this->placeholderFormat($string, $arguments);
+   *     @endcode
+   *     makes the following HTML code:
+   *     @code
+   *       <em class="placeholder">text output here.</em>
+   *     @endcode
+   *     As with @variable, do not use this within HTML attributes, JavaScript,
+   *     or CSS. Doing so is a security risk.
+   *   - :variable: Return value is escaped with
+   *     \Drupal\Component\Utility\Html::escape() and filtered for dangerous
+   *     protocols using UrlHelper::stripDangerousProtocols(). Use this when
+   *     using the "href" attribute, ensuring the attribute value is always
+   *     wrapped in quotes:
+   *     @code
+   *     // Secure (with quotes):
+   *     $this->placeholderFormat('<a href=":url">@variable</a>', [':url' => $url, @variable => $variable]);
+   *     // Insecure (without quotes):
+   *     $this->placeholderFormat('<a href=:url>@variable</a>', [':url' => $url, @variable => $variable]);
+   *     @endcode
+   *     When ":variable" comes from arbitrary user input, the result is secure,
+   *     but not guaranteed to be a valid URL (which means the resulting output
+   *     could fail HTML validation). To guarantee a valid URL, use
+   *     Url::fromUri($user_input)->toString() (which either throws an exception
+   *     or returns a well-formed URL) before passing the result into a
+   *     ":variable" placeholder.
+   *
+   * @return string
+   *   A formatted HTML string with the placeholders replaced.
+   *
+   * @ingroup sanitization
+   *
+   * @see \Drupal\Core\StringTranslation\TranslatableString
+   * @see \Drupal\Core\StringTranslation\PluralTranslatableString
+   * @see \Drupal\Component\Utility\Html::escape()
+   * @see \Drupal\Component\Utility\UrlHelper::stripDangerousProtocols()
+   * @see \Drupal\Core\Url::fromUri()
+   */
+  protected static function placeholderFormat($string, array $args) {
+    // Transform arguments before inserting them.
+    foreach ($args as $key => $value) {
+      switch ($key[0]) {
+        case '@':
+          // Escape if the value is not an object from a class that implements
+          // \Drupal\Component\Utility\SafeStringInterface, for example strings
+          // will be escaped.
+          // \Drupal\Component\Utility\SafeMarkup\SafeMarkup::isSafe() may
+          // return TRUE for content that is safe within HTML fragments, but not
+          // within other contexts, so this placeholder type must not be used
+          // within HTML attributes, JavaScript, or CSS. See
+          // \Drupal\Component\Utility\SafeMarkup::format().
+          $args[$key] = static::placeholderEscape($value);
+          break;
+
+        case ':':
+          // Strip URL protocols that can be XSS vectors.
+          $value = UrlHelper::stripDangerousProtocols($value);
+          // Escape unconditionally, without checking
+          // \Drupal\Component\Utility\SafeMarkup\SafeMarkup::isSafe(). This
+          // forces characters that are unsafe for use in an "href" HTML
+          // attribute to be encoded. If a caller wants to pass a value that is
+          // extracted from HTML and therefore is already HTML encoded, it must
+          // invoke
+          // \Drupal\Component\Utility\OutputStrategyInterface::renderFromHtml()
+          // on it prior to passing it in as a placeholder value of this type.
+          // @todo Add some advice and stronger warnings.
+          //   https://www.drupal.org/node/2569041.
+          $args[$key] = Html::escape($value);
+          break;
+
+        case '%':
+        default:
+          // Similarly to @, escape non-safe values. Also, add wrapping markup
+          // in order to render as a placeholder. Not for use within attributes,
+          // per the warning above about
+          // \Drupal\Component\Utility\SafeMarkup\SafeMarkup::isSafe() and also
+          // due to the wrapping markup.
+          $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/PlaceholderTrait.php b/core/lib/Drupal/Component/Utility/PlaceholderTrait.php
deleted file mode 100644
index 0eb3437..0000000
--- a/core/lib/Drupal/Component/Utility/PlaceholderTrait.php
+++ /dev/null
@@ -1,178 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Component\Utility\PlaceholderTrait.
- */
-
-namespace Drupal\Component\Utility;
-
-/**
- * Offers functionality for formatting strings using placeholders.
- */
-trait PlaceholderTrait {
-
-  /**
-   * Formats a string by replacing variable placeholders.
-   *
-   * This trait is designed for formatting messages that are mostly text, not as
-   * an HTML template language. As such:
-   * - The passed in string should contain no (or minimal) HTML.
-   * - Variable placeholders should not be used within the "<" and ">" of an
-   *   HTML tag, such as in HTML attribute values. This would be a security
-   *   risk. Examples:
-   *   @code
-   *     // Insecure (placeholder within "<" and ">"):
-   *     $this->placeholderFormat('<@variable>text</@variable>', ['@variable' => $variable]);
-   *     // Insecure (placeholder within "<" and ">"):
-   *     $this->placeholderFormat('<a @variable>link text</a>', ['@variable' => $variable]);
-   *     // Insecure (placeholder within "<" and ">"):
-   *     $this->placeholderFormat('<a title="@variable">link text</a>', ['@variable' => $variable]);
-   *   @endcode
-   *   Only the "href" attribute is supported via the special ":variable"
-   *   placeholder, to allow simple links to be inserted:
-   *   @code
-   *     // Secure (usage of ":variable" placeholder for href attribute):
-   *     $this->placeholderFormat('<a href=":variable">link text</a>', [':variable' , $variable]);
-   *     // Secure (usage of ":variable" placeholder for href attribute):
-   *     $this->placeholderFormat('<a href=":variable" title="static text">link text</a>', [':variable' => $variable]);
-   *     // Insecure (the "@variable" placeholder does not filter dangerous
-   *     // protocols):
-   *     $this->placeholderFormat('<a href="@variable">link text</a>', ['@variable' => $variable]);
-   *     // Insecure ("@variable" placeholder within "<" and ">"):
-   *     $this->placeholderFormat('<a href=":url" title="@variable">link text</a>', [':url' => $url, '@variable' => $variable]);
-   *   @endcode
-   * To build non-minimal HTML, use an HTML template language such as Twig,
-   * rather than this trait.
-   *
-   * @param string $string
-   *   A string containing placeholders. The string itself is expected to be
-   *   safe and correct HTML. Any unsafe content must be in $args and
-   *   inserted via placeholders.
-   * @param array $args
-   *   An associative array of replacements. Each array key should be the same
-   *   as a placeholder in $string. The corresponding value should be a string
-   *   or an object that implements
-   *   \Drupal\Component\Utility\SafeStringInterface. The value replaces the
-   *   placeholder in $string. Sanitization and formatting will be done before
-   *   replacement. The type of sanitization and formatting depends on the first
-   *   character of the key:
-   *   - @variable: When the placeholder replacement value is:
-   *     - A string, the replaced value in the returned string will be sanitized
-   *       using \Drupal\Component\Utility\Html::escape().
-   *     - A SafeStringInterface object, the replaced value in the returned
-   *       string will not be sanitized.
-   *     - A SafeStringInterface object cast to a string, the replaced value in
-   *       the returned string be forcibly sanitized using
-   *       \Drupal\Component\Utility\Html::escape().
-   *       @code
-   *         $this->placeholderFormat('This will force HTML-escaping of the replacement value: @text', ['@text' => (string) $safe_string_interface_object));
-   *       @endcode
-   *     Use this placeholder as the default choice for anything displayed on
-   *     the site, but not within HTML attributes, JavaScript, or CSS. Doing so
-   *     is a security risk.
-   *   - %variable: Use when the replacement value is to be wrapped in <em>
-   *     tags.
-   *     A call like:
-   *     @code
-   *       $string = "%output_text";
-   *       $arguments = ['output_text' => 'text output here.'];
-   *       $this->placeholderFormat($string, $arguments);
-   *     @endcode
-   *     makes the following HTML code:
-   *     @code
-   *       <em class="placeholder">text output here.</em>
-   *     @endcode
-   *     As with @variable, do not use this within HTML attributes, JavaScript,
-   *     or CSS. Doing so is a security risk.
-   *   - :variable: Return value is escaped with
-   *     \Drupal\Component\Utility\Html::escape() and filtered for dangerous
-   *     protocols using UrlHelper::stripDangerousProtocols(). Use this when
-   *     using the "href" attribute, ensuring the attribute value is always
-   *     wrapped in quotes:
-   *     @code
-   *     // Secure (with quotes):
-   *     $this->placeholderFormat('<a href=":url">@variable</a>', [':url' => $url, @variable => $variable]);
-   *     // Insecure (without quotes):
-   *     $this->placeholderFormat('<a href=:url>@variable</a>', [':url' => $url, @variable => $variable]);
-   *     @endcode
-   *     When ":variable" comes from arbitrary user input, the result is secure,
-   *     but not guaranteed to be a valid URL (which means the resulting output
-   *     could fail HTML validation). To guarantee a valid URL, use
-   *     Url::fromUri($user_input)->toString() (which either throws an exception
-   *     or returns a well-formed URL) before passing the result into a
-   *     ":variable" placeholder.
-   *
-   * @return string
-   *   A formatted HTML string with the placeholders replaced.
-   *
-   * @ingroup sanitization
-   *
-   * @see \Drupal\Component\Utility\FormattableString
-   * @see \Drupal\Core\StringTranslation\TranslatableString
-   * @see \Drupal\Core\StringTranslation\PluralTranslatableString
-   * @see \Drupal\Component\Utility\Html::escape()
-   * @see \Drupal\Component\Utility\UrlHelper::stripDangerousProtocols()
-   * @see \Drupal\Core\Url::fromUri()
-   */
-  protected static function placeholderFormat($string, array $args) {
-    // Transform arguments before inserting them.
-    foreach ($args as $key => $value) {
-      switch ($key[0]) {
-        case '@':
-          // Escape if the value is not an object from a class that implements
-          // \Drupal\Component\Utility\SafeStringInterface, for example strings
-          // will be escaped.
-          // \Drupal\Component\Utility\SafeMarkup\SafeMarkup::isSafe() may
-          // return TRUE for content that is safe within HTML fragments, but not
-          // within other contexts, so this placeholder type must not be used
-          // within HTML attributes, JavaScript, or CSS. See
-          // \Drupal\Component\Utility\SafeMarkup::format().
-          $args[$key] = static::placeholderEscape($value);
-          break;
-
-        case ':':
-          // Strip URL protocols that can be XSS vectors.
-          $value = UrlHelper::stripDangerousProtocols($value);
-          // Escape unconditionally, without checking
-          // \Drupal\Component\Utility\SafeMarkup\SafeMarkup::isSafe(). This
-          // forces characters that are unsafe for use in an "href" HTML
-          // attribute to be encoded. If a caller wants to pass a value that is
-          // extracted from HTML and therefore is already HTML encoded, it must
-          // invoke
-          // \Drupal\Component\Utility\OutputStrategyInterface::renderFromHtml()
-          // on it prior to passing it in as a placeholder value of this type.
-          // @todo Add some advice and stronger warnings.
-          //   https://www.drupal.org/node/2569041.
-          $args[$key] = Html::escape($value);
-          break;
-
-        case '%':
-        default:
-          // Similarly to @, escape non-safe values. Also, add wrapping markup
-          // in order to render as a placeholder. Not for use within attributes,
-          // per the warning above about
-          // \Drupal\Component\Utility\SafeMarkup\SafeMarkup::isSafe() and also
-          // due to the wrapping markup.
-          $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 6f42d13..c6f2468 100644
--- a/core/lib/Drupal/Component/Utility/SafeMarkup.php
+++ b/core/lib/Drupal/Component/Utility/SafeMarkup.php
@@ -31,7 +31,6 @@
  * @see theme_render
  */
 class SafeMarkup {
-  use PlaceholderTrait;
 
   /**
    * The list of safe strings.
@@ -167,7 +166,7 @@ public static function checkPlain($text) {
    *   any unsafe content must be in $args and inserted via placeholders.
    * @param array $args
    *   An array with placeholder replacements, keyed by placeholder. See
-   *   \Drupal\Component\Utility\PlaceholderTrait::placeholderFormat() for
+   *   \Drupal\Component\Utility\FormattableString::placeholderFormat() for
    *   additional information about placeholders.
    *
    * @return string|\Drupal\Component\Utility\SafeStringInterface
@@ -176,7 +175,7 @@ public static function checkPlain($text) {
    *
    * @ingroup sanitization
    *
-   * @see \Drupal\Component\Utility\PlaceholderTrait::placeholderFormat()
+   * @see \Drupal\Component\Utility\FormattableString::placeholderFormat()
    * @see \Drupal\Component\Utility\FormattableString
    *
    * @deprecated in Drupal 8.0.0, will be removed before Drupal 9.0.0.
diff --git a/core/lib/Drupal/Core/StringTranslation/PluralTranslatableString.php b/core/lib/Drupal/Core/StringTranslation/PluralTranslatableString.php
index 46225df..c7d8d58 100644
--- a/core/lib/Drupal/Core/StringTranslation/PluralTranslatableString.php
+++ b/core/lib/Drupal/Core/StringTranslation/PluralTranslatableString.php
@@ -7,13 +7,10 @@
 
 namespace Drupal\Core\StringTranslation;
 
-use Drupal\Component\Utility\PlaceholderTrait;
-
 /**
  * A class to hold plural translatable strings.
  */
 class PluralTranslatableString extends TranslatableString {
-  use PlaceholderTrait;
 
   /**
    * The delimiter used to split plural strings.
@@ -63,7 +60,7 @@ class PluralTranslatableString extends TranslatableString {
    *   "@count new comments".
    * @param array $args
    *   (optional) An array with placeholder replacements, keyed by placeholder.
-   *   See \Drupal\Component\Utility\PlaceholderTrait::placeholderFormat() for
+   *   See \Drupal\Component\Utility\FormattableString::placeholderFormat() for
    *   additional information about placeholders. Note that you do not need to
    *   include @count in this array; this replacement is done automatically
    *   for the plural cases.
@@ -73,7 +70,7 @@ class PluralTranslatableString extends TranslatableString {
    * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
    *   (optional) The string translation service.
    *
-   * @see \Drupal\Component\Utility\PlaceholderTrait::placeholderFormat()
+   * @see \Drupal\Component\Utility\FormattableString:placeholderFormat()
    */
   public function __construct($count, $singular, $plural, array $args = [], array $options = [], TranslationInterface $string_translation = NULL) {
     $this->count = $count;
diff --git a/core/lib/Drupal/Core/StringTranslation/TranslatableString.php b/core/lib/Drupal/Core/StringTranslation/TranslatableString.php
index 6d587a1..01e8969 100644
--- a/core/lib/Drupal/Core/StringTranslation/TranslatableString.php
+++ b/core/lib/Drupal/Core/StringTranslation/TranslatableString.php
@@ -7,8 +7,7 @@
 
 namespace Drupal\Core\StringTranslation;
 
-use Drupal\Component\Utility\PlaceholderTrait;
-use Drupal\Component\Utility\SafeStringInterface;
+use Drupal\Component\Utility\FormattableString;
 use Drupal\Component\Utility\ToStringTrait;
 
 /**
@@ -19,14 +18,13 @@
  * This is useful for using translation in very low level subsystems like entity
  * definition and stream wrappers.
  *
- * @see \Drupal\Component\Utility\PlaceholderTrait::placeholderFormat()
+ * @see \Drupal\Component\Utility\FormattableString::placeholderFormat()
  * @see \Drupal\Core\StringTranslation\TranslationManager::translate()
  * @see \Drupal\Core\StringTranslation\TranslationManager::translateString()
  * @see \Drupal\Core\Annotation\Translation
  */
-class TranslatableString implements SafeStringInterface {
+class TranslatableString extends FormattableString {
 
-  use PlaceholderTrait;
   use ToStringTrait;
 
   /**
@@ -74,14 +72,14 @@ class TranslatableString implements SafeStringInterface {
    *   The string that is to be translated.
    * @param array $arguments
    *   (optional) An array with placeholder replacements, keyed by placeholder.
-   *   See \Drupal\Component\Utility\PlaceholderTrait::placeholderFormat() for
+   *   See \Drupal\Component\Utility\FormattableString::placeholderFormat() for
    *   additional information about placeholders.
    * @param array $options
    *   (optional) An array of additional options.
    * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
    *   (optional) The string translation service.
    *
-   * @see \Drupal\Component\Utility\PlaceholderTrait::placeholderFormat()
+   * @see \Drupal\Component\Utility\FormattableString::placeholderFormat()
    */
   public function __construct($string, array $arguments = array(), array $options = array(), TranslationInterface $string_translation = NULL) {
     $this->string = $string;
diff --git a/core/tests/Drupal/Tests/UnitTestCase.php b/core/tests/Drupal/Tests/UnitTestCase.php
index 5b950d4..63098bb 100644
--- a/core/tests/Drupal/Tests/UnitTestCase.php
+++ b/core/tests/Drupal/Tests/UnitTestCase.php
@@ -12,7 +12,6 @@
 use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
-use Drupal\Component\Utility\PlaceholderTrait;
 use Drupal\Core\StringTranslation\TranslatableString;
 use Drupal\Core\StringTranslation\PluralTranslatableString;
 
@@ -24,8 +23,6 @@
  */
 abstract class UnitTestCase extends \PHPUnit_Framework_TestCase {
 
-  use PlaceholderTrait;
-
   /**
    * The random generator.
    *
