PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.8.16
VikBooking Hotel Booking Engine & PMS v1.8.16
1.8.16 1.8.15 1.8.14 1.8.13 1.8.12 1.8.11 1.8.10 1.8.9 1.8.6 1.8.7 1.8.8 trunk 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 All 37 releases
vikbooking / libraries / adapter / language / language.php

language.php in VikBooking Hotel Booking Engine & PMS 1.8.16, at libraries/adapter/language/language.php

569 lines 13.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package VikWP - Libraries
4 * @subpackage adapter.language
5 * @author E4J s.r.l.
6 * @copyright Copyright (C) 2023 E4J s.r.l. All Rights Reserved.
7 * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
8 * @link https://vikwp.com
9 */
10
11 // No direct access
12 defined('ABSPATH') or die('No script kiddies please!');
13
14 JLoader::import('adapter.language.handler');
15
16 /**
17 * Languages/translation adapter class.
18 *
19 * @since 10.0
20 */
21 #[\AllowDynamicProperties]
22 class JLanguage
23 {
24 /**
25 * Array of Language objects.
26 *
27 * @var array
28 */
29 protected static $instances = array();
30
31 /**
32 * The current language tag.
33 *
34 * @var string
35 */
36 private $tag = null;
37
38 /**
39 * A list of handlers to switch the lang keys.
40 *
41 * @var array
42 */
43 private $handlers = array();
44
45 /**
46 * Name of the transliterator function for this language.
47 *
48 * @var Callable
49 */
50 private $transliterator = null;
51
52 /**
53 * Class constructor.
54 *
55 * @param string $lang The language to use.
56 *
57 * @uses load()
58 */
59 public function __construct($lang = null)
60 {
61 $this->tag = $lang;
62 }
63
64 /**
65 * Returns a language object.
66 *
67 * @param string $lang The language to use.
68 *
69 * @return self The Language object.
70 */
71 public static function getInstance($lang = null)
72 {
73 $key = (string) $lang;
74
75 if (!isset(static::$instances[$key]))
76 {
77 static::$instances[$key] = new static($lang);
78 }
79
80 return static::$instances[$key];
81 }
82
83 /**
84 * Loads a single language file. This method doesn't update the
85 * current user locale.
86 *
87 * @param string $extension The extension for which a language file should be loaded.
88 * @param string $basePath The basepath to use.
89 * @param string $lang The language to load, default null for the current language.
90 *
91 * @return boolean True if the file has been successfully loaded.
92 *
93 * @uses getLocaleFilter()
94 */
95 public function load($extension, $basePath = '', $lang = null)
96 {
97 // make sure we are not using the Joomla tag standards
98 $lang = is_null($lang) ? null : str_replace('-', '_', $lang);
99
100 // make sure the name of the plugin is correct
101 $extension = preg_replace("/^com_/", '', $extension);
102
103 $this->localeFilter = $lang;
104
105 /**
106 * Action triggered before loading the text domain.
107 *
108 * @param string $extension The plugin text domain to look for.
109 * @param string $basePath The base path containing the languages.
110 * @param mixed $lang An optional language tag to use.
111 *
112 * @return void
113 *
114 * @since 10.1.30
115 */
116 do_action('vik_plugin_before_load_language', $extension, $basePath, $lang);
117
118 /**
119 * In case the base path was not provided, or a default Joomla constant was passed,
120 * we need to try to auto-detect the standard folder where the language files
121 * should be placed.
122 *
123 * @since 10.1.24
124 */
125 if (!$basePath || $basePath == 'JPATH_ADMINISTRATOR' || $basePath == 'JPATH_SITE')
126 {
127 // create constant name to retrieve standard languages folder
128 $const = strtoupper($extension) . '_LANG';
129
130 // try to retrieve language path from constant, if defined
131 $basePath = defined($const) ? constant($const) : '';
132 }
133
134 // check if the lang tag has been specified,
135 // otherwise use the default locale
136 if (!is_null($lang))
137 {
138 // create a filter to override the 'plugin_locale'
139 add_filter('plugin_locale', array($this, 'getLocaleFilter'));
140 }
141
142 // we need to attach an action to 'load_textdomain' to unset the
143 // cache related to the existing language, if any
144 add_action('load_textdomain', array($this, 'refreshDomain'));
145
146 /**
147 * Register 2 hooks to prevent the hack defined by Polylang to support the
148 * lazy loading of the translations. Since our plugin might run before
149 * Polylang, we need to by pass this limitation and always load the translations
150 * without waiting the latter is ready.
151 *
152 * @since 10.1.33
153 */
154 add_filter('load_textdomain_mofile', array($this, 'storeDefaultMofile'), 1, 2);
155 add_filter('load_textdomain_mofile', array($this, 'preventPolylangHack'), 100, 2);
156
157 // init language
158 $loaded = load_plugin_textdomain($extension, false, $basePath);
159
160 /**
161 * Hook used to load plugin translations from different folders.
162 *
163 * @param boolean $loaded True if a language translation has been already loaded.
164 * @param string $extension The plugin text domain to look for.
165 *
166 * @return boolean True if a new translation is loaded.
167 *
168 * @since 10.1.28
169 */
170 $loaded = apply_filters('vik_plugin_load_language', $loaded, $extension);
171
172 // remove the action to avoid affecting other plugins
173 remove_action('load_textdomain', array($this, 'refreshDomain'));
174
175 // remove the filter to avoid affecting other plugins
176 remove_filter('plugin_locale', array($this, 'getLocaleFilter'));
177
178 return $loaded;
179 }
180
181 /**
182 * Returns the lang tag to load for 'locale' filter.
183 *
184 * @return string The locale.
185 */
186 public function getLocaleFilter()
187 {
188 /**
189 * @var $localeFilter is declared in self::load()
190 */
191 return isset($this->localeFilter) ? $this->localeFilter : null;
192 }
193
194 /**
195 * Used to unset the cache related to a language already loaded.
196 *
197 * @param string $domain The language domain to unset.
198 *
199 * @return void
200 */
201 public function refreshDomain()
202 {
203 $args = func_get_args();
204 $domain = array_shift($args);
205
206 if (!$domain)
207 {
208 return;
209 }
210
211 // the global $l10n var contains all the cached languages
212 global $l10n;
213
214 // if the domain is set in the cache, unset it
215 if (isset($l10n[$domain]))
216 {
217 unset($l10n[$domain]);
218 }
219 }
220
221 /**
222 * Every time Gettext tries to fetch the path of a MO file,
223 * we need to internally save the default given path.
224 *
225 * This method should be executed as soon as possible.
226 *
227 * @param string $mofile Path to the MO file.
228 * @param string $domain Unique identifier for retrieving translated strings.
229 *
230 * @return string The updated MO file.
231 *
232 * @since 10.1.33
233 */
234 public function storeDefaultMofile($mofile, $domain)
235 {
236 // always track the default MO file path
237 $this->defaultMoFile = $mofile;
238
239 return $mofile;
240 }
241
242 /**
243 * Polylang always unset the given MO files to load them all together when
244 * this latter already loaded all the resources. In order to prevent this
245 * behavior/hack, we need to reset the empty path with the previously
246 * registered one (@see storeDefaultMofile).
247 *
248 * This method should be executed as late as possible.
249 *
250 * @param string $mofile Path to the MO file.
251 * @param string $domain Unique identifier for retrieving translated strings.
252 *
253 * @return string The updated MO file.
254 *
255 * @since 10.1.33
256 */
257 public function preventPolylangHack($mofile, $domain)
258 {
259 if (!$mofile)
260 {
261 // the path has been probably emptied by Polylang, reset it
262 $mofile = $this->defaultMoFile;
263 }
264
265 return $mofile;
266 }
267
268 /**
269 * Translates a string into the current language.
270 *
271 * @param string $string The string to translate.
272 * @param boolean $jsSafe Make the result javascript safe.
273 * @param boolean $interpretBackSlashes To interpret backslashes (\\=\, \n=carriage return, \t=tabulation)
274 *
275 * @return string The translated string.
276 *
277 * @uses findTranslation()
278 */
279 public function _($string, $jsSafe = false, $interpretBackSlashes = true)
280 {
281 $return = $this->findTranslation($string);
282
283 if (empty($return))
284 {
285 return $string;
286 }
287
288 if ($jsSafe)
289 {
290 // javascript filter
291 $return = addslashes($return);
292 }
293 else if ($interpretBackSlashes)
294 {
295 if (strpos($return, '\\') !== false)
296 {
297 // interpret \n and \t characters
298 $return = str_replace(array('\\\\', '\t', '\n'), array("\\", "\t", "\n"), $return);
299 }
300 }
301
302 return $return;
303 }
304
305 /**
306 * Dispatches the attached handlers to find the specified string.
307 *
308 * @param string $string The string to translate.
309 *
310 * @return string The translated string, otherwise null.
311 */
312 protected function findTranslation($string)
313 {
314 foreach ($this->handlers as $handler)
315 {
316 $result = $handler->translate($string);
317
318 if ($result !== null)
319 {
320 return $result;
321 }
322 }
323
324 return null;
325 }
326
327 /**
328 * Getter for the language tag (as defined in RFC 3066).
329 *
330 * @return string The language tag.
331 */
332 public function getTag()
333 {
334 $tag = $this->tag;
335
336 // if no tag set, return the current one
337 if (is_null($tag))
338 {
339 /**
340 * Take the locale specified by the user.
341 * In case of missing locale, the function
342 * always fallback to the default one.
343 *
344 * @since 10.1.31
345 */
346 $tag = get_user_locale();
347 }
348
349 // replace the underscore with an hyphen
350 return str_replace('_', '-', $tag);
351 }
352
353 /**
354 * Determines is a key exists.
355 *
356 * @param string $string The key to check.
357 *
358 * @return boolean True if the key exists, otherwise false.
359 *
360 * @uses _()
361 */
362 public function hasKey($string)
363 {
364 /**
365 * Fixed return value, which was exactly a negation of
366 * the expected boolean.
367 *
368 * @since 10.1.35
369 */
370 return strcmp($this->_($string), $string) === 0 ? false : true;
371 }
372
373 /**
374 * Attaches an handler to evaluate the key to translate.
375 *
376 * @param mixed $handler Either a file path or an handler (@since 10.1.46 added support to JLanguageHandler).
377 * @param string $domain The plugin domain name.
378 *
379 * @return self This object to support chaining.
380 */
381 public function attachHandler($handler, $domain)
382 {
383 /**
384 * Strip any unexpected characters from domain.
385 *
386 * @since 10.1.29
387 */
388 $domain = preg_replace("/[^a-z0-9_]+/i", '', $domain);
389
390 $sign = serialize([$handler, $domain]);
391
392 if (!isset($this->handlers[$sign]))
393 {
394 /**
395 * Auto-load the class handler only in case a file path is provided.
396 *
397 * @since 10.1.46
398 */
399 if (is_string($handler))
400 {
401 require_once $handler;
402
403 $name = basename($handler);
404 $name = substr($name, 0, strrpos($name, '.'));
405
406 $classname = ucwords($domain) . 'Language' . ucwords($name);
407 $handler = new $classname();
408 }
409
410 if ($handler instanceof JLanguageHandler)
411 {
412 $this->handlers[$sign] = $handler;
413 }
414 }
415
416 return $this;
417 }
418
419 /**
420 * Transliterate function.
421 * This method processes a string and replaces all accented UTF-8 characters by unaccented
422 * ASCII-7 "equivalents".
423 *
424 * @param string $string The string to transliterate.
425 *
426 * @return string The transliteration of the string.
427 */
428 public function transliterate($string)
429 {
430 if ($this->transliterator !== null)
431 {
432 return call_user_func($this->transliterator, $string);
433 }
434
435 JLoader::import('adapter.language.transliterate');
436
437 $string = Transliterate::utf8_latin_to_ascii($string);
438
439 return $string;
440 }
441
442 /**
443 * Getter for transliteration function.
444 *
445 * @return callable The transliterator function.
446 */
447 public function getTransliterator()
448 {
449 return $this->transliterator;
450 }
451
452 /**
453 * Set the transliteration function.
454 *
455 * @param callable $function Function name or the actual function.
456 *
457 * @return callable The previous function.
458 */
459 public function setTransliterator($function)
460 {
461 $previous = $this->transliterator;
462 $this->transliterator = $function;
463
464 return $previous;
465 }
466
467 /**
468 * Get the first day of the week for this language.
469 *
470 * @return integer The first day of the week according to the language
471 *
472 * @since 10.1.28
473 */
474 public function getFirstDay()
475 {
476 // since we have not enough information to know
477 // the first day of the week for this region,
478 // we should rely on the global configuration
479 return get_option('start_of_week', 0);
480 }
481
482 /**
483 * Get the RTL property.
484 *
485 * @return boolean True is it an RTL language.
486 *
487 * @since 10.1.28
488 */
489 public function isRtl()
490 {
491 // checks whether the current locale is RTL
492 return is_rtl();
493 }
494
495 /**
496 * Returns a list of known languages.
497 *
498 * @return array Key/value pair with the language file and related metadata.
499 *
500 * @since 10.1.9
501 */
502 public static function getKnownLanguages()
503 {
504 /**
505 * Get installed languages.
506 *
507 * @since 10.1.55 Get rid of the keys because third-party plugins might attach
508 * custom languages by using non-integer keys.
509 */
510 $list = array_values(get_available_languages());
511
512 // if the default US lang is in the array, remove it
513 if ($index = array_search('en_US', $list))
514 {
515 array_splice($list, $index, 1);
516 }
517
518 // insert en_US default lang at the beginning of the list
519 array_unshift($list, 'en_US');
520
521 // replace all the underscores with an hyphen
522 $list = array_map(function($elem)
523 {
524 return str_replace('_', '-', $elem);
525 }, $list);
526
527 // obtain WP available translations
528 require_once ABSPATH . 'wp-admin/includes/translation-install.php';
529 $translations = wp_get_available_translations();
530
531 $map = array();
532
533 foreach ($list as $lang)
534 {
535 // get the original locale
536 $key = str_replace('-', '_', $lang);
537
538 if (isset($translations[$key]))
539 {
540 $name = $translations[$key]['english_name'];
541 $native = $translations[$key]['native_name'];
542 $locale = implode(', ', $translations[$key]['iso']);
543
544 unset($translations[$key]);
545 }
546 else
547 {
548 $name = $native = $lang;
549 $locale = substr($lang, 0, 2);
550 }
551
552 $map[$lang] = array(
553 'name' => $name,
554 'nativeName' => $native,
555 'tag' => $lang,
556 'locale' => $locale,
557 'rtl' => 0,
558 'firstDay' => 0,
559 );
560 }
561
562 // fix en_US property because its details don't exist
563 $map['en-US']['name'] = 'English (United States)';
564 $map['en-US']['nativeName'] = 'English (United States)';
565
566 return $map;
567 }
568 }
569