3 namespace Drupal\Core\StringTranslation;
5 use Drupal\Component\Render\FormattableMarkup;
6 use Drupal\Component\Utility\ToStringTrait;
7 use Drupal\Component\Utility\Unicode;
10 * Provides translatable markup class.
12 * This object, when cast to a string, will return the formatted, translated
13 * string. Avoid casting it to a string yourself, because it is preferable to
14 * let the rendering system do the cast as late as possible in the rendering
15 * process, so that this object itself can be put, untranslated, into render
16 * caches and thus the cache can be shared between different language contexts.
18 * @see \Drupal\Component\Render\FormattableMarkup
19 * @see \Drupal\Core\StringTranslation\TranslationManager::translateString()
20 * @see \Drupal\Core\Annotation\Translation
22 class TranslatableMarkup extends FormattableMarkup {
27 * The translated markup without placeholder replacements.
31 protected $translatedMarkup;
34 * The translation options.
41 * The string translation service.
43 * @var \Drupal\Core\StringTranslation\TranslationInterface
45 protected $stringTranslation;
48 * Constructs a new class instance.
50 * When possible, use the
51 * \Drupal\Core\StringTranslation\StringTranslationTrait $this->t(). Otherwise
52 * create a new \Drupal\Core\StringTranslation\TranslatableMarkup object
55 * Calling the trait's t() method or instantiating a new TranslatableMarkup
56 * object serves two purposes:
57 * - At run-time it translates user-visible text into the appropriate
59 * - Static analyzers detect calls to t() and new TranslatableMarkup, and add
60 * the first argument (the string to be translated) to the database of
61 * strings that need translation. These strings are expected to be in
62 * English, so the first argument should always be in English.
63 * To allow the site to be localized, it is important that all human-readable
64 * text that will be displayed on the site or sent to a user is made available
65 * in one of the ways supported by the
66 * @link https://www.drupal.org/node/322729 Localization API @endlink.
67 * See the @link https://www.drupal.org/node/322729 Localization API @endlink
68 * pages for more information, including recommendations on how to break up or
69 * not break up strings for translation.
71 * @section sec_translating_vars Translating Variables
72 * $string should always be an English literal string.
74 * $string should never contain a variable, such as:
76 * new TranslatableMarkup($text)
78 * There are several reasons for this:
79 * - Using a variable for $string that is user input is a security risk.
80 * - Using a variable for $string that has even guaranteed safe text (for
81 * example, user interface text provided literally in code), will not be
82 * picked up by the localization static text processor. (The parameter could
83 * be a variable if the entire string in $text has been passed into t() or
84 * new TranslatableMarkup() elsewhere as the first argument, but that
85 * strategy is not recommended.)
87 * It is especially important never to call new TranslatableMarkup($user_text)
88 * or t($user_text) where $user_text is some text that a user entered -- doing
89 * that can lead to cross-site scripting and other security problems. However,
90 * you can use variable substitution in your string, to put variable text such
91 * as user names or link URLs into translated text. Variable substitution
94 * new TranslatableMarkup("@name's blog", array('@name' => $account->getDisplayName()));
96 * Basically, you can put placeholders like @name into your string, and the
97 * method will substitute the sanitized values at translation time. (See the
98 * Localization API pages referenced above and the documentation of
99 * \Drupal\Component\Render\FormattableMarkup::placeholderFormat()
100 * for details about how to safely and correctly define variables in your
101 * string.) Translators can then rearrange the string as necessary for the
102 * language (e.g., in Spanish, it might be "blog de @name").
104 * @param string $string
105 * A string containing the English text to translate.
106 * @param array $arguments
107 * (optional) An associative array of replacements to make after
108 * translation. Based on the first character of the key, the value is
109 * escaped and/or themed. See
110 * \Drupal\Component\Render\FormattableMarkup::placeholderFormat() for
112 * @param array $options
113 * (optional) An associative array of additional options, with the following
115 * - 'langcode' (defaults to the current language): A language code, to
116 * translate to a language other than what is used to display the page.
117 * - 'context' (defaults to the empty context): The context the source
119 * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
120 * (optional) The string translation service.
122 * @throws \InvalidArgumentException
123 * Exception thrown when $string is not a string.
125 * @see \Drupal\Component\Render\FormattableMarkup::placeholderFormat()
126 * @see \Drupal\Core\StringTranslation\StringTranslationTrait::t()
128 * @ingroup sanitization
130 public function __construct($string, array $arguments = [], array $options = [], TranslationInterface $string_translation = NULL) {
131 if (!is_string($string)) {
132 $message = $string instanceof TranslatableMarkup ? '$string ("' . $string->getUntranslatedString() . '") must be a string.' : '$string ("' . (string) $string . '") must be a string.';
133 throw new \InvalidArgumentException($message);
135 parent::__construct($string, $arguments);
136 $this->options = $options;
137 $this->stringTranslation = $string_translation;
141 * Gets the untranslated string value stored in this translated string.
144 * The string stored in this wrapper.
146 public function getUntranslatedString() {
147 return $this->string;
151 * Gets a specific option from this translated string.
153 * @param string $name
157 * The value of this option or empty string of option is not set.
159 public function getOption($name) {
160 return isset($this->options[$name]) ? $this->options[$name] : '';
164 * Gets all options from this translated string.
167 * The array of options.
169 public function getOptions() {
170 return $this->options;
174 * Gets all arguments from this translated string.
177 * The array of arguments.
179 public function getArguments() {
180 return $this->arguments;
184 * Renders the object as a string.
187 * The translated string.
189 public function render() {
190 if (!isset($this->translatedMarkup)) {
191 $this->translatedMarkup = $this->getStringTranslation()->translateString($this);
194 // Handle any replacements.
195 if ($args = $this->getArguments()) {
196 return $this->placeholderFormat($this->translatedMarkup, $args);
198 return $this->translatedMarkup;
202 * Magic __sleep() method to avoid serializing the string translator.
204 public function __sleep() {
205 return ['string', 'arguments', 'options'];
209 * Gets the string translation service.
211 * @return \Drupal\Core\StringTranslation\TranslationInterface
212 * The string translation service.
214 protected function getStringTranslation() {
215 if (!$this->stringTranslation) {
216 $this->stringTranslation = \Drupal::service('string_translation');
219 return $this->stringTranslation;
223 * Returns the string length.
226 * The length of the string.
228 public function count() {
229 return Unicode::strlen($this->render());