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