1 <?php
2 /**
3 * @package Joomla.Platform
4 * @subpackage Language
5 *
6 * @copyright Copyright (C) 2005 - 2017 Open Source Matters, Inc. All rights reserved.
7 * @license GNU General Public License version 2 or later; see LICENSE
8 */
9
10 defined('JPATH_PLATFORM') or die;
11
12 /**
13 * Text handling class.
14 *
15 * @since 11.1
16 */
17 class JText
18 {
19 /**
20 * JavaScript strings
21 *
22 * @var array
23 * @since 11.1
24 */
25 protected static $strings = array();
26
27 /**
28 * Translates a string into the current language.
29 *
30 * Examples:
31 * `<script>alert(Joomla.JText._('<?php echo JText::_("JDEFAULT", array("script"=>true)); ?>'));</script>`
32 * will generate an alert message containing 'Default'
33 * `<?php echo JText::_("JDEFAULT"); ?>` will generate a 'Default' string
34 *
35 * @param string $string The string to translate.
36 * @param mixed $jsSafe Boolean: Make the result javascript safe.
37 * @param boolean $interpretBackSlashes To interpret backslashes (\\=\, \n=carriage return, \t=tabulation)
38 * @param boolean $script To indicate that the string will be push in the javascript language store
39 *
40 * @return string The translated string or the key if $script is true
41 *
42 * @since 11.1
43 */
44 public static function _($string, $jsSafe = false, $interpretBackSlashes = true, $script = false)
45 {
46 if (is_array($jsSafe))
47 {
48 if (array_key_exists('interpretBackSlashes', $jsSafe))
49 {
50 $interpretBackSlashes = (boolean) $jsSafe['interpretBackSlashes'];
51 }
52
53 if (array_key_exists('script', $jsSafe))
54 {
55 $script = (boolean) $jsSafe['script'];
56 }
57
58 $jsSafe = array_key_exists('jsSafe', $jsSafe) ? (boolean) $jsSafe['jsSafe'] : false;
59 }
60
61 if (self::passSprintf($string, $jsSafe, $interpretBackSlashes, $script))
62 {
63 return $string;
64 }
65
66 $lang = JFactory::getLanguage();
67
68 if ($script)
69 {
70 static::$strings[$string] = $lang->_($string, $jsSafe, $interpretBackSlashes);
71
72 return $string;
73 }
74
75 return $lang->_($string, $jsSafe, $interpretBackSlashes);
76 }
77
78 /**
79 * Checks the string if it should be interpreted as sprintf and runs sprintf over it.
80 *
81 * @param string &$string The string to translate.
82 * @param mixed $jsSafe Boolean: Make the result javascript safe.
83 * @param boolean $interpretBackSlashes To interpret backslashes (\\=\, \n=carriage return, \t=tabulation)
84 * @param boolean $script To indicate that the string will be push in the javascript language store
85 *
86 * @return boolean Whether the string be interpreted as sprintf
87 *
88 * @since 3.4.4
89 */
90 private static function passSprintf(&$string, $jsSafe = false, $interpretBackSlashes = true, $script = false)
91 {
92 // Check if string contains a comma
93 if (strpos($string, ',') === false)
94 {
95 return false;
96 }
97
98 $lang = JFactory::getLanguage();
99 $string_parts = explode(',', $string);
100
101 // Pass all parts through the JText translator
102 foreach ($string_parts as $i => $str)
103 {
104 $string_parts[$i] = $lang->_($str, $jsSafe, $interpretBackSlashes);
105 }
106
107 $first_part = array_shift($string_parts);
108
109 // Replace custom named placeholders with sprinftf style placeholders
110 $first_part = preg_replace('/\[\[%([0-9]+):[^\]]*\]\]/', '%\1$s', $first_part);
111
112 // Check if string contains sprintf placeholders
113 if (!preg_match('/%([0-9]+\$)?s/', $first_part))
114 {
115 return false;
116 }
117
118 $final_string = vsprintf($first_part, $string_parts);
119
120 // Return false if string hasn't changed
121 if ($first_part === $final_string)
122 {
123 return false;
124 }
125
126 $string = $final_string;
127
128 if ($script)
129 {
130 foreach ($string_parts as $i => $str)
131 {
132 static::$strings[$str] = $string_parts[$i];
133 }
134 }
135
136 return true;
137 }
138
139 /**
140 * Translates a string into the current language.
141 *
142 * Examples:
143 * `<?php echo JText::alt('JALL', 'language'); ?>` will generate a 'All' string in English but a "Toutes" string in French
144 * `<?php echo JText::alt('JALL', 'module'); ?>` will generate a 'All' string in English but a "Tous" string in French
145 *
146 * @param string $string The string to translate.
147 * @param string $alt The alternate option for global string
148 * @param mixed $jsSafe Boolean: Make the result javascript safe.
149 * @param boolean $interpretBackSlashes To interpret backslashes (\\=\, \n=carriage return, \t=tabulation)
150 * @param boolean $script To indicate that the string will be pushed in the javascript language store
151 *
152 * @return string The translated string or the key if $script is true
153 *
154 * @since 11.1
155 */
156 public static function alt($string, $alt, $jsSafe = false, $interpretBackSlashes = true, $script = false)
157 {
158 if (JFactory::getLanguage()->hasKey($string . '_' . $alt))
159 {
160 $string .= '_' . $alt;
161 }
162
163 return static::_($string, $jsSafe, $interpretBackSlashes, $script);
164 }
165
166 /**
167 * Like JText::sprintf but tries to pluralise the string.
168 *
169 * Note that this method can take a mixed number of arguments as for the sprintf function.
170 *
171 * The last argument can take an array of options:
172 *
173 * array('jsSafe'=>boolean, 'interpretBackSlashes'=>boolean, 'script'=>boolean)
174 *
175 * where:
176 *
177 * jsSafe is a boolean to generate a javascript safe strings.
178 * interpretBackSlashes is a boolean to interpret backslashes \\->\, \n->new line, \t->tabulation.
179 * script is a boolean to indicate that the string will be push in the javascript language store.
180 *
181 * Examples:
182 * `<script>alert(Joomla.JText._('<?php echo JText::plural("COM_PLUGINS_N_ITEMS_UNPUBLISHED", 1, array("script"=>true)); ?>'));</script>`
183 * will generate an alert message containing '1 plugin successfully disabled'
184 * `<?php echo JText::plural('COM_PLUGINS_N_ITEMS_UNPUBLISHED', 1); ?>` will generate a '1 plugin successfully disabled' string
185 *
186 * @param string $string The format string.
187 * @param integer $n The number of items
188 *
189 * @return string The translated strings or the key if 'script' is true in the array of options
190 *
191 * @since 11.1
192 */
193 public static function plural($string, $n)
194 {
195 $lang = JFactory::getLanguage();
196 $args = func_get_args();
197 $count = count($args);
198
199 if ($count < 1)
200 {
201 return '';
202 }
203
204 if ($count == 1)
205 {
206 // Default to the normal sprintf handling.
207 $args[0] = $lang->_($string);
208
209 return call_user_func_array('sprintf', $args);
210 }
211
212 // Try the key from the language plural potential suffixes
213 $found = false;
214 $suffixes = $lang->getPluralSuffixes((int) $n);
215 array_unshift($suffixes, (int) $n);
216
217 foreach ($suffixes as $suffix)
218 {
219 $key = $string . '_' . $suffix;
220
221 if ($lang->hasKey($key))
222 {
223 $found = true;
224 break;
225 }
226 }
227
228 if (!$found)
229 {
230 // Not found so revert to the original.
231 $key = $string;
232 }
233
234 if (is_array($args[$count - 1]))
235 {
236 $args[0] = $lang->_(
237 $key, array_key_exists('jsSafe', $args[$count - 1]) ? $args[$count - 1]['jsSafe'] : false,
238 array_key_exists('interpretBackSlashes', $args[$count - 1]) ? $args[$count - 1]['interpretBackSlashes'] : true
239 );
240
241 if (array_key_exists('script', $args[$count - 1]) && $args[$count - 1]['script'])
242 {
243 static::$strings[$key] = call_user_func_array('sprintf', $args);
244
245 return $key;
246 }
247 }
248 else
249 {
250 $args[0] = $lang->_($key);
251 }
252
253 return call_user_func_array('sprintf', $args);
254 }
255
256 /**
257 * Passes a string thru a sprintf.
258 *
259 * Note that this method can take a mixed number of arguments as for the sprintf function.
260 *
261 * The last argument can take an array of options:
262 *
263 * array('jsSafe'=>boolean, 'interpretBackSlashes'=>boolean, 'script'=>boolean)
264 *
265 * where:
266 *
267 * jsSafe is a boolean to generate a javascript safe strings.
268 * interpretBackSlashes is a boolean to interpret backslashes \\->\, \n->new line, \t->tabulation.
269 * script is a boolean to indicate that the string will be push in the javascript language store.
270 *
271 * @param string $string The format string.
272 *
273 * @return string The translated strings or the key if 'script' is true in the array of options.
274 *
275 * @since 11.1
276 */
277 public static function sprintf($string)
278 {
279 $lang = JFactory::getLanguage();
280 $args = func_get_args();
281 $count = count($args);
282
283 if ($count < 1)
284 {
285 return '';
286 }
287
288 if (is_array($args[$count - 1]))
289 {
290 $args[0] = $lang->_(
291 $string, array_key_exists('jsSafe', $args[$count - 1]) ? $args[$count - 1]['jsSafe'] : false,
292 array_key_exists('interpretBackSlashes', $args[$count - 1]) ? $args[$count - 1]['interpretBackSlashes'] : true
293 );
294
295 if (array_key_exists('script', $args[$count - 1]) && $args[$count - 1]['script'])
296 {
297 static::$strings[$string] = call_user_func_array('sprintf', $args);
298
299 return $string;
300 }
301 }
302 else
303 {
304 $args[0] = $lang->_($string);
305 }
306
307 // Replace custom named placeholders with sprintf style placeholders
308 $args[0] = preg_replace('/\[\[%([0-9]+):[^\]]*\]\]/', '%\1$s', $args[0]);
309
310 return call_user_func_array('sprintf', $args);
311 }
312
313 /**
314 * Passes a string thru an printf.
315 *
316 * Note that this method can take a mixed number of arguments as for the sprintf function.
317 *
318 * @param format $string The format string.
319 *
320 * @return mixed
321 *
322 * @since 11.1
323 */
324 public static function printf($string)
325 {
326 $lang = JFactory::getLanguage();
327 $args = func_get_args();
328 $count = count($args);
329
330 if ($count < 1)
331 {
332 return '';
333 }
334
335 if (is_array($args[$count - 1]))
336 {
337 $args[0] = $lang->_(
338 $string, array_key_exists('jsSafe', $args[$count - 1]) ? $args[$count - 1]['jsSafe'] : false,
339 array_key_exists('interpretBackSlashes', $args[$count - 1]) ? $args[$count - 1]['interpretBackSlashes'] : true
340 );
341 }
342 else
343 {
344 $args[0] = $lang->_($string);
345 }
346
347 return call_user_func_array('printf', $args);
348 }
349
350 /**
351 * Translate a string into the current language and stores it in the JavaScript language store.
352 *
353 * @param string $string The JText key.
354 * @param boolean $jsSafe Ensure the output is JavaScript safe.
355 * @param boolean $interpretBackSlashes Interpret \t and \n.
356 *
357 * @return string
358 *
359 * @since 11.1
360 */
361 public static function script($string = null, $jsSafe = false, $interpretBackSlashes = true)
362 {
363 if ($string === null)
364 {
365 JLog::add(
366 sprintf(
367 'As of 3.7.0, passing a null value for the first argument of %1$s() is deprecated and will not be supported in 4.0.'
368 . ' Use the %2$s::getScriptStrings() method to get the strings from the JavaScript language store instead.',
369 __METHOD__,
370 __CLASS__
371 ),
372 JLog::WARNING,
373 'deprecated'
374 );
375 }
376
377 if (is_array($jsSafe))
378 {
379 if (array_key_exists('interpretBackSlashes', $jsSafe))
380 {
381 $interpretBackSlashes = (boolean) $jsSafe['interpretBackSlashes'];
382 }
383
384 if (array_key_exists('jsSafe', $jsSafe))
385 {
386 $jsSafe = (boolean) $jsSafe['jsSafe'];
387 }
388 else
389 {
390 $jsSafe = false;
391 }
392 }
393
394 // Add the string to the array if not null.
395 if ($string !== null)
396 {
397 // Normalize the key and translate the string.
398 static::$strings[strtoupper($string)] = JFactory::getLanguage()->_($string, $jsSafe, $interpretBackSlashes);
399
400 // Load core.js dependency
401 JHtml::_('behavior.core');
402
403 // Update Joomla.JText script options
404 JFactory::getDocument()->addScriptOptions('joomla.jtext', static::$strings, false);
405 }
406
407 return static::getScriptStrings();
408 }
409
410 /**
411 * Get the strings that have been loaded to the JavaScript language store.
412 *
413 * @return array
414 *
415 * @since 3.7.0
416 */
417 public static function getScriptStrings()
418 {
419 return static::$strings;
420 }
421 }
422