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