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
← All changes | libraries/adapter/language/language.php +568 -0 1.6.2 → 1.8.16 View file →
@@ -1,0 +1,568 @@
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 +}