Most Drupal output is HTML markup, however there are situations where non-HTML output is required. A typical example are plain-text emails.
Before this change the ! placeholder was used to skip escaping/sanitization for the specified value and thus obtain a string usable in a non-HTML context. The main drawback of this approach, aside from preventing the removal of the ! placeholder, is that the same Welcome, %username! string may be used in different contexts and each one would require slightly different placeholders, for instance:
- in a welcome HTML page:
Welcome, %username!becomesWelcome, <em class="placeholder">plach</a>! - in a welcome plain-text email:
Welcome, !username!becomesWelcome, plach! - in a
titleattribute:Welcome, @username!becomesWelcome, plach!(HTML-escaped)
This scenario would force translators to translate the source string three times when the actual content is always the same, only its "encoding" is different.
Ideally the source string should be context-agnostic, which implies converting the source string from a source format to a context-specific output format. Since most of the Drupal's output is HTML, this has been chosen as the source format. Now every source string passed to SafeMarkup::format() or t() is always interpreted as HTML markup ready for output, i.e. already sanitized/escaped. The resulting string will be safe HTML markup again.
If needing to use such a string in a non-HTML context, a dedicated output strategy will need to be applied to the HTML string returned by SafeMarkup::format() or t():
// $safe_string is 'Welcome, <em class="placeholder">plach</em>!'
$safe_string = t('Welcome, %username!', ['%user_name' => 'plach']);
// $plain_text is 'Welcome, plach!'
$plain_text = PlainTextOutput::renderFromHtml($safe_string);
Dynamic strings used in attributes need to be escaped (see the related change record for more information on what use-cases are supported). To avoid double-escaping the Attribute object automatically converts safe markup to plain text:
$element = array(
'#type' => 'submit',
'#value' => $this->t('Welcome, %user_name!', ['%user_name' => 'plach']),
);
With this approach in both cases the source safe string is the same.
Output strategy classes should implement OutputStrategyInterface to ensure a consistent developer experience. For instance PlainTextFormattedOutput will implement the same interface to provide an output strategy converting HTML to properly-formatted plain text output, typically used to provide an alternative text-only version of an HTML email (see #2573009: Provide a PlainTextFormattedOutput output strategy).