Patched to Drupal 8.4.8 level. See https://www.drupal.org/sa-core-2018-004 and patch...
[yaffs-website] / web / core / lib / Drupal / Core / StringTranslation / TranslatableMarkup.php
1 <?php
2
3 namespace Drupal\Core\StringTranslation;
4
5 use Drupal\Component\Render\FormattableMarkup;
6 use Drupal\Component\Utility\ToStringTrait;
7 use Drupal\Component\Utility\Unicode;
8
9 /**
10  * Provides translatable markup class.
11  *
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.
17  *
18  * @see \Drupal\Component\Render\FormattableMarkup
19  * @see \Drupal\Core\StringTranslation\TranslationManager::translateString()
20  * @see \Drupal\Core\Annotation\Translation
21  */
22 class TranslatableMarkup extends FormattableMarkup {
23
24   use ToStringTrait;
25
26   /**
27    * The string to be translated.
28    *
29    * @var string
30    */
31   protected $string;
32
33   /**
34    * The translated markup without placeholder replacements.
35    *
36    * @var string
37    */
38   protected $translatedMarkup;
39
40   /**
41    * The translation options.
42    *
43    * @var array
44    */
45   protected $options;
46
47   /**
48    * The string translation service.
49    *
50    * @var \Drupal\Core\StringTranslation\TranslationInterface
51    */
52   protected $stringTranslation;
53
54   /**
55    * Constructs a new class instance.
56    *
57    * When possible, use the
58    * \Drupal\Core\StringTranslation\StringTranslationTrait $this->t(). Otherwise
59    * create a new \Drupal\Core\StringTranslation\TranslatableMarkup object
60    * directly.
61    *
62    * Calling the trait's t() method or instantiating a new TranslatableMarkup
63    * object serves two purposes:
64    * - At run-time it translates user-visible text into the appropriate
65    *   language.
66    * - Static analyzers detect calls to t() and new TranslatableMarkup, and add
67    *   the first argument (the string to be translated) to the database of
68    *   strings that need translation. These strings are expected to be in
69    *   English, so the first argument should always be in English.
70    * To allow the site to be localized, it is important that all human-readable
71    * text that will be displayed on the site or sent to a user is made available
72    * in one of the ways supported by the
73    * @link https://www.drupal.org/node/322729 Localization API @endlink.
74    * See the @link https://www.drupal.org/node/322729 Localization API @endlink
75    * pages for more information, including recommendations on how to break up or
76    * not break up strings for translation.
77    *
78    * @section sec_translating_vars Translating Variables
79    * $string should always be an English literal string.
80    *
81    * $string should never contain a variable, such as:
82    * @code
83    * new TranslatableMarkup($text)
84    * @endcode
85    * There are several reasons for this:
86    * - Using a variable for $string that is user input is a security risk.
87    * - Using a variable for $string that has even guaranteed safe text (for
88    *   example, user interface text provided literally in code), will not be
89    *   picked up by the localization static text processor. (The parameter could
90    *   be a variable if the entire string in $text has been passed into t() or
91    *   new TranslatableMarkup() elsewhere as the first argument, but that
92    *   strategy is not recommended.)
93    *
94    * It is especially important never to call new TranslatableMarkup($user_text)
95    * or t($user_text) where $user_text is some text that a user entered -- doing
96    * that can lead to cross-site scripting and other security problems. However,
97    * you can use variable substitution in your string, to put variable text such
98    * as user names or link URLs into translated text. Variable substitution
99    * looks like this:
100    * @code
101    * new TranslatableMarkup("@name's blog", array('@name' => $account->getDisplayName()));
102    * @endcode
103    * Basically, you can put placeholders like @name into your string, and the
104    * method will substitute the sanitized values at translation time. (See the
105    * Localization API pages referenced above and the documentation of
106    * \Drupal\Component\Render\FormattableMarkup::placeholderFormat()
107    * for details about how to safely and correctly define variables in your
108    * string.) Translators can then rearrange the string as necessary for the
109    * language (e.g., in Spanish, it might be "blog de @name").
110    *
111    * @param string $string
112    *   A string containing the English text to translate.
113    * @param array $arguments
114    *   (optional) An associative array of replacements to make after
115    *   translation. Based on the first character of the key, the value is
116    *   escaped and/or themed. See
117    *   \Drupal\Component\Render\FormattableMarkup::placeholderFormat() for
118    *   details.
119    * @param array $options
120    *   (optional) An associative array of additional options, with the following
121    *   elements:
122    *   - 'langcode' (defaults to the current language): A language code, to
123    *     translate to a language other than what is used to display the page.
124    *   - 'context' (defaults to the empty context): The context the source
125    *     string belongs to.
126    * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
127    *   (optional) The string translation service.
128    *
129    * @throws \InvalidArgumentException
130    *   Exception thrown when $string is not a string.
131    *
132    * @see \Drupal\Component\Render\FormattableMarkup::placeholderFormat()
133    * @see \Drupal\Core\StringTranslation\StringTranslationTrait::t()
134    *
135    * @ingroup sanitization
136    */
137   public function __construct($string, array $arguments = [], array $options = [], TranslationInterface $string_translation = NULL) {
138     if (!is_string($string)) {
139       $message = $string instanceof TranslatableMarkup ? '$string ("' . $string->getUntranslatedString() . '") must be a string.' : '$string ("' . (string) $string . '") must be a string.';
140       throw new \InvalidArgumentException($message);
141     }
142     $this->string = $string;
143     $this->arguments = $arguments;
144     $this->options = $options;
145     $this->stringTranslation = $string_translation;
146   }
147
148   /**
149    * Gets the untranslated string value stored in this translated string.
150    *
151    * @return string
152    *   The string stored in this wrapper.
153    */
154   public function getUntranslatedString() {
155     return $this->string;
156   }
157
158   /**
159    * Gets a specific option from this translated string.
160    *
161    * @param string $name
162    *   Option name.
163    *
164    * @return mixed
165    *   The value of this option or empty string of option is not set.
166    */
167   public function getOption($name) {
168     return isset($this->options[$name]) ? $this->options[$name] : '';
169   }
170
171   /**
172    * Gets all options from this translated string.
173    *
174    * @return mixed[]
175    *   The array of options.
176    */
177   public function getOptions() {
178     return $this->options;
179   }
180
181   /**
182    * Gets all arguments from this translated string.
183    *
184    * @return mixed[]
185    *   The array of arguments.
186    */
187   public function getArguments() {
188     return $this->arguments;
189   }
190
191   /**
192    * Renders the object as a string.
193    *
194    * @return string
195    *   The translated string.
196    */
197   public function render() {
198     if (!isset($this->translatedMarkup)) {
199       $this->translatedMarkup = $this->getStringTranslation()->translateString($this);
200     }
201
202     // Handle any replacements.
203     if ($args = $this->getArguments()) {
204       return $this->placeholderFormat($this->translatedMarkup, $args);
205     }
206     return $this->translatedMarkup;
207   }
208
209   /**
210    * Magic __sleep() method to avoid serializing the string translator.
211    */
212   public function __sleep() {
213     return ['string', 'arguments', 'options'];
214   }
215
216   /**
217    * Gets the string translation service.
218    *
219    * @return \Drupal\Core\StringTranslation\TranslationInterface
220    *   The string translation service.
221    */
222   protected function getStringTranslation() {
223     if (!$this->stringTranslation) {
224       $this->stringTranslation = \Drupal::service('string_translation');
225     }
226
227     return $this->stringTranslation;
228   }
229
230   /**
231    * Returns the string length.
232    *
233    * @return int
234    *   The length of the string.
235    */
236   public function count() {
237     return Unicode::strlen($this->render());
238   }
239
240 }