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 use Joomla\String\StringHelper;
13
14 /**
15 * Allows for quoting in language .ini files.
16 */
17 define('_QQ_', '"');
18
19 /**
20 * Languages/translation handler class
21 *
22 * @since 11.1
23 */
24 class JLanguage
25 {
26 /**
27 * Array of JLanguage objects
28 *
29 * @var JLanguage[]
30 * @since 11.1
31 */
32 protected static $languages = array();
33
34 /**
35 * Debug language, If true, highlights if string isn't found.
36 *
37 * @var boolean
38 * @since 11.1
39 */
40 protected $debug = false;
41
42 /**
43 * The default language, used when a language file in the requested language does not exist.
44 *
45 * @var string
46 * @since 11.1
47 */
48 protected $default = 'en-GB';
49
50 /**
51 * An array of orphaned text.
52 *
53 * @var array
54 * @since 11.1
55 */
56 protected $orphans = array();
57
58 /**
59 * Array holding the language metadata.
60 *
61 * @var array
62 * @since 11.1
63 */
64 protected $metadata = null;
65
66 /**
67 * Array holding the language locale or boolean null if none.
68 *
69 * @var array|boolean
70 * @since 11.1
71 */
72 protected $locale = null;
73
74 /**
75 * The language to load.
76 *
77 * @var string
78 * @since 11.1
79 */
80 protected $lang = null;
81
82 /**
83 * A nested array of language files that have been loaded
84 *
85 * @var array
86 * @since 11.1
87 */
88 protected $paths = array();
89
90 /**
91 * List of language files that are in error state
92 *
93 * @var array
94 * @since 11.1
95 */
96 protected $errorfiles = array();
97
98 /**
99 * Translations
100 *
101 * @var array
102 * @since 11.1
103 */
104 protected $strings = array();
105
106 /**
107 * An array of used text, used during debugging.
108 *
109 * @var array
110 * @since 11.1
111 */
112 protected $used = array();
113
114 /**
115 * Counter for number of loads.
116 *
117 * @var integer
118 * @since 11.1
119 */
120 protected $counter = 0;
121
122 /**
123 * An array used to store overrides.
124 *
125 * @var array
126 * @since 11.1
127 */
128 protected $override = array();
129
130 /**
131 * Name of the transliterator function for this language.
132 *
133 * @var string
134 * @since 11.1
135 */
136 protected $transliterator = null;
137
138 /**
139 * Name of the pluralSuffixesCallback function for this language.
140 *
141 * @var callable
142 * @since 11.1
143 */
144 protected $pluralSuffixesCallback = null;
145
146 /**
147 * Name of the ignoredSearchWordsCallback function for this language.
148 *
149 * @var callable
150 * @since 11.1
151 */
152 protected $ignoredSearchWordsCallback = null;
153
154 /**
155 * Name of the lowerLimitSearchWordCallback function for this language.
156 *
157 * @var callable
158 * @since 11.1
159 */
160 protected $lowerLimitSearchWordCallback = null;
161
162 /**
163 * Name of the upperLimitSearchWordCallback function for this language.
164 *
165 * @var callable
166 * @since 11.1
167 */
168 protected $upperLimitSearchWordCallback = null;
169
170 /**
171 * Name of the searchDisplayedCharactersNumberCallback function for this language.
172 *
173 * @var callable
174 * @since 11.1
175 */
176 protected $searchDisplayedCharactersNumberCallback = null;
177
178 /**
179 * Constructor activating the default information of the language.
180 *
181 * @param string $lang The language
182 * @param boolean $debug Indicates if language debugging is enabled.
183 *
184 * @since 11.1
185 */
186 public function __construct($lang = null, $debug = false)
187 {
188 $this->strings = array();
189
190 if ($lang == null)
191 {
192 $lang = $this->default;
193 }
194
195 $this->lang = $lang;
196 $this->metadata = JLanguageHelper::getMetadata($this->lang);
197 $this->setDebug($debug);
198
199 $filename = JPATH_BASE . "/language/overrides/$lang.override.ini";
200
201 if (file_exists($filename) && $contents = $this->parse($filename))
202 {
203 if (is_array($contents))
204 {
205 // Sort the underlying heap by key values to optimize merging
206 ksort($contents, SORT_STRING);
207 $this->override = $contents;
208 }
209
210 unset($contents);
211 }
212
213 // Look for a language specific localise class
214 $class = str_replace('-', '_', $lang . 'Localise');
215 $paths = array();
216
217 if (defined('JPATH_SITE'))
218 {
219 // Note: Manual indexing to enforce load order.
220 $paths[0] = JPATH_SITE . "/language/overrides/$lang.localise.php";
221 $paths[2] = JPATH_SITE . "/language/$lang/$lang.localise.php";
222 }
223
224 if (defined('JPATH_ADMINISTRATOR'))
225 {
226 // Note: Manual indexing to enforce load order.
227 $paths[1] = JPATH_ADMINISTRATOR . "/language/overrides/$lang.localise.php";
228 $paths[3] = JPATH_ADMINISTRATOR . "/language/$lang/$lang.localise.php";
229 }
230
231 ksort($paths);
232 $path = reset($paths);
233
234 while (!class_exists($class) && $path)
235 {
236 if (file_exists($path))
237 {
238 require_once $path;
239 }
240
241 $path = next($paths);
242 }
243
244 if (class_exists($class))
245 {
246 /* Class exists. Try to find
247 * -a transliterate method,
248 * -a getPluralSuffixes method,
249 * -a getIgnoredSearchWords method
250 * -a getLowerLimitSearchWord method
251 * -a getUpperLimitSearchWord method
252 * -a getSearchDisplayCharactersNumber method
253 */
254 if (method_exists($class, 'transliterate'))
255 {
256 $this->transliterator = array($class, 'transliterate');
257 }
258
259 if (method_exists($class, 'getPluralSuffixes'))
260 {
261 $this->pluralSuffixesCallback = array($class, 'getPluralSuffixes');
262 }
263
264 if (method_exists($class, 'getIgnoredSearchWords'))
265 {
266 $this->ignoredSearchWordsCallback = array($class, 'getIgnoredSearchWords');
267 }
268
269 if (method_exists($class, 'getLowerLimitSearchWord'))
270 {
271 $this->lowerLimitSearchWordCallback = array($class, 'getLowerLimitSearchWord');
272 }
273
274 if (method_exists($class, 'getUpperLimitSearchWord'))
275 {
276 $this->upperLimitSearchWordCallback = array($class, 'getUpperLimitSearchWord');
277 }
278
279 if (method_exists($class, 'getSearchDisplayedCharactersNumber'))
280 {
281 $this->searchDisplayedCharactersNumberCallback = array($class, 'getSearchDisplayedCharactersNumber');
282 }
283 }
284
285 $this->load();
286 }
287
288 /**
289 * Returns a language object.
290 *
291 * @param string $lang The language to use.
292 * @param boolean $debug The debug mode.
293 *
294 * @return JLanguage The Language object.
295 *
296 * @since 11.1
297 */
298 public static function getInstance($lang, $debug = false)
299 {
300 if (!isset(self::$languages[$lang . $debug]))
301 {
302 self::$languages[$lang . $debug] = new JLanguage($lang, $debug);
303 }
304
305 return self::$languages[$lang . $debug];
306 }
307
308 /**
309 * Translate function, mimics the php gettext (alias _) function.
310 *
311 * The function checks if $jsSafe is true, then if $interpretBackslashes is true.
312 *
313 * @param string $string The string to translate
314 * @param boolean $jsSafe Make the result javascript safe
315 * @param boolean $interpretBackSlashes Interpret \t and \n
316 *
317 * @return string The translation of the string
318 *
319 * @since 11.1
320 */
321 public function _($string, $jsSafe = false, $interpretBackSlashes = true)
322 {
323 // Detect empty string
324 if ($string == '')
325 {
326 return '';
327 }
328
329 $key = strtoupper($string);
330
331 if (isset($this->strings[$key]))
332 {
333 $string = $this->debug ? '**' . $this->strings[$key] . '**' : $this->strings[$key];
334
335 // Store debug information
336 if ($this->debug)
337 {
338 $caller = $this->getCallerInfo();
339
340 if (!array_key_exists($key, $this->used))
341 {
342 $this->used[$key] = array();
343 }
344
345 $this->used[$key][] = $caller;
346 }
347 }
348 else
349 {
350 if ($this->debug)
351 {
352 $caller = $this->getCallerInfo();
353 $caller['string'] = $string;
354
355 if (!array_key_exists($key, $this->orphans))
356 {
357 $this->orphans[$key] = array();
358 }
359
360 $this->orphans[$key][] = $caller;
361
362 $string = '??' . $string . '??';
363 }
364 }
365
366 if ($jsSafe)
367 {
368 // Javascript filter
369 $string = addslashes($string);
370 }
371 elseif ($interpretBackSlashes)
372 {
373 if (strpos($string, '\\') !== false)
374 {
375 // Interpret \n and \t characters
376 $string = str_replace(array('\\\\', '\t', '\n'), array("\\", "\t", "\n"), $string);
377 }
378 }
379
380 return $string;
381 }
382
383 /**
384 * Transliterate function
385 *
386 * This method processes a string and replaces all accented UTF-8 characters by unaccented
387 * ASCII-7 "equivalents".
388 *
389 * @param string $string The string to transliterate.
390 *
391 * @return string The transliteration of the string.
392 *
393 * @since 11.1
394 */
395 public function transliterate($string)
396 {
397 if ($this->transliterator !== null)
398 {
399 return call_user_func($this->transliterator, $string);
400 }
401
402 $string = JLanguageTransliterate::utf8_latin_to_ascii($string);
403 $string = StringHelper::strtolower($string);
404
405 return $string;
406 }
407
408 /**
409 * Getter for transliteration function
410 *
411 * @return callable The transliterator function
412 *
413 * @since 11.1
414 */
415 public function getTransliterator()
416 {
417 return $this->transliterator;
418 }
419
420 /**
421 * Set the transliteration function.
422 *
423 * @param callable $function Function name or the actual function.
424 *
425 * @return callable The previous function.
426 *
427 * @since 11.1
428 */
429 public function setTransliterator($function)
430 {
431 $previous = $this->transliterator;
432 $this->transliterator = $function;
433
434 return $previous;
435 }
436
437 /**
438 * Returns an array of suffixes for plural rules.
439 *
440 * @param integer $count The count number the rule is for.
441 *
442 * @return array The array of suffixes.
443 *
444 * @since 11.1
445 */
446 public function getPluralSuffixes($count)
447 {
448 if ($this->pluralSuffixesCallback !== null)
449 {
450 return call_user_func($this->pluralSuffixesCallback, $count);
451 }
452 else
453 {
454 return array((string) $count);
455 }
456 }
457
458 /**
459 * Getter for pluralSuffixesCallback function.
460 *
461 * @return callable Function name or the actual function.
462 *
463 * @since 11.1
464 */
465 public function getPluralSuffixesCallback()
466 {
467 return $this->pluralSuffixesCallback;
468 }
469
470 /**
471 * Set the pluralSuffixes function.
472 *
473 * @param callable $function Function name or actual function.
474 *
475 * @return callable The previous function.
476 *
477 * @since 11.1
478 */
479 public function setPluralSuffixesCallback($function)
480 {
481 $previous = $this->pluralSuffixesCallback;
482 $this->pluralSuffixesCallback = $function;
483
484 return $previous;
485 }
486
487 /**
488 * Returns an array of ignored search words
489 *
490 * @return array The array of ignored search words.
491 *
492 * @since 11.1
493 */
494 public function getIgnoredSearchWords()
495 {
496 if ($this->ignoredSearchWordsCallback !== null)
497 {
498 return call_user_func($this->ignoredSearchWordsCallback);
499 }
500 else
501 {
502 return array();
503 }
504 }
505
506 /**
507 * Getter for ignoredSearchWordsCallback function.
508 *
509 * @return callable Function name or the actual function.
510 *
511 * @since 11.1
512 */
513 public function getIgnoredSearchWordsCallback()
514 {
515 return $this->ignoredSearchWordsCallback;
516 }
517
518 /**
519 * Setter for the ignoredSearchWordsCallback function
520 *
521 * @param callable $function Function name or actual function.
522 *
523 * @return callable The previous function.
524 *
525 * @since 11.1
526 */
527 public function setIgnoredSearchWordsCallback($function)
528 {
529 $previous = $this->ignoredSearchWordsCallback;
530 $this->ignoredSearchWordsCallback = $function;
531
532 return $previous;
533 }
534
535 /**
536 * Returns a lower limit integer for length of search words
537 *
538 * @return integer The lower limit integer for length of search words (3 if no value was set for a specific language).
539 *
540 * @since 11.1
541 */
542 public function getLowerLimitSearchWord()
543 {
544 if ($this->lowerLimitSearchWordCallback !== null)
545 {
546 return call_user_func($this->lowerLimitSearchWordCallback);
547 }
548 else
549 {
550 return 3;
551 }
552 }
553
554 /**
555 * Getter for lowerLimitSearchWordCallback function
556 *
557 * @return callable Function name or the actual function.
558 *
559 * @since 11.1
560 */
561 public function getLowerLimitSearchWordCallback()
562 {
563 return $this->lowerLimitSearchWordCallback;
564 }
565
566 /**
567 * Setter for the lowerLimitSearchWordCallback function.
568 *
569 * @param callable $function Function name or actual function.
570 *
571 * @return callable The previous function.
572 *
573 * @since 11.1
574 */
575 public function setLowerLimitSearchWordCallback($function)
576 {
577 $previous = $this->lowerLimitSearchWordCallback;
578 $this->lowerLimitSearchWordCallback = $function;
579
580 return $previous;
581 }
582
583 /**
584 * Returns an upper limit integer for length of search words
585 *
586 * @return integer The upper limit integer for length of search words (200 if no value was set or if default value is < 200).
587 *
588 * @since 11.1
589 */
590 public function getUpperLimitSearchWord()
591 {
592 if ($this->upperLimitSearchWordCallback !== null && call_user_func($this->upperLimitSearchWordCallback) > 200)
593 {
594 return call_user_func($this->upperLimitSearchWordCallback);
595 }
596
597 return 200;
598 }
599
600 /**
601 * Getter for upperLimitSearchWordCallback function
602 *
603 * @return callable Function name or the actual function.
604 *
605 * @since 11.1
606 */
607 public function getUpperLimitSearchWordCallback()
608 {
609 return $this->upperLimitSearchWordCallback;
610 }
611
612 /**
613 * Setter for the upperLimitSearchWordCallback function
614 *
615 * @param callable $function Function name or the actual function.
616 *
617 * @return callable The previous function.
618 *
619 * @since 11.1
620 */
621 public function setUpperLimitSearchWordCallback($function)
622 {
623 $previous = $this->upperLimitSearchWordCallback;
624 $this->upperLimitSearchWordCallback = $function;
625
626 return $previous;
627 }
628
629 /**
630 * Returns the number of characters displayed in search results.
631 *
632 * @return integer The number of characters displayed (200 if no value was set for a specific language).
633 *
634 * @since 11.1
635 */
636 public function getSearchDisplayedCharactersNumber()
637 {
638 if ($this->searchDisplayedCharactersNumberCallback !== null)
639 {
640 return call_user_func($this->searchDisplayedCharactersNumberCallback);
641 }
642 else
643 {
644 return 200;
645 }
646 }
647
648 /**
649 * Getter for searchDisplayedCharactersNumberCallback function
650 *
651 * @return callable Function name or the actual function.
652 *
653 * @since 11.1
654 */
655 public function getSearchDisplayedCharactersNumberCallback()
656 {
657 return $this->searchDisplayedCharactersNumberCallback;
658 }
659
660 /**
661 * Setter for the searchDisplayedCharactersNumberCallback function.
662 *
663 * @param callable $function Function name or the actual function.
664 *
665 * @return callable The previous function.
666 *
667 * @since 11.1
668 */
669 public function setSearchDisplayedCharactersNumberCallback($function)
670 {
671 $previous = $this->searchDisplayedCharactersNumberCallback;
672 $this->searchDisplayedCharactersNumberCallback = $function;
673
674 return $previous;
675 }
676
677 /**
678 * Checks if a language exists.
679 *
680 * This is a simple, quick check for the directory that should contain language files for the given user.
681 *
682 * @param string $lang Language to check.
683 * @param string $basePath Optional path to check.
684 *
685 * @return boolean True if the language exists.
686 *
687 * @since 11.1
688 * @deprecated 3.7.0, use JLanguageHelper::exists() instead.
689 */
690 public static function exists($lang, $basePath = JPATH_BASE)
691 {
692 JLog::add(__METHOD__ . '() is deprecated, use JLanguageHelper::exists() instead.', JLog::WARNING, 'deprecated');
693
694 return JLanguageHelper::exists($lang, $basePath);
695 }
696
697 /**
698 * Loads a single language file and appends the results to the existing strings
699 *
700 * @param string $extension The extension for which a language file should be loaded.
701 * @param string $basePath The basepath to use.
702 * @param string $lang The language to load, default null for the current language.
703 * @param boolean $reload Flag that will force a language to be reloaded if set to true.
704 * @param boolean $default Flag that force the default language to be loaded if the current does not exist.
705 *
706 * @return boolean True if the file has successfully loaded.
707 *
708 * @since 11.1
709 */
710 public function load($extension = 'joomla', $basePath = JPATH_BASE, $lang = null, $reload = false, $default = true)
711 {
712 // If language is null set as the current language.
713 if (!$lang)
714 {
715 $lang = $this->lang;
716 }
717
718 // Load the default language first if we're not debugging and a non-default language is requested to be loaded
719 // with $default set to true
720 if (!$this->debug && ($lang != $this->default) && $default)
721 {
722 $this->load($extension, $basePath, $this->default, false, true);
723 }
724
725 $path = JLanguageHelper::getLanguagePath($basePath, $lang);
726
727 $internal = $extension == 'joomla' || $extension == '';
728 $filename = $internal ? $lang : $lang . '.' . $extension;
729 $filename = "$path/$filename.ini";
730
731 if (isset($this->paths[$extension][$filename]) && !$reload)
732 {
733 // This file has already been tested for loading.
734 $result = $this->paths[$extension][$filename];
735 }
736 else
737 {
738 // Load the language file
739 $result = $this->loadLanguage($filename, $extension);
740
741 // Check whether there was a problem with loading the file
742 if ($result === false && $default)
743 {
744 // No strings, so either file doesn't exist or the file is invalid
745 $oldFilename = $filename;
746
747 // Check the standard file name
748 if (!$this->debug)
749 {
750 $path = JLanguageHelper::getLanguagePath($basePath, $this->default);
751
752 $filename = $internal ? $this->default : $this->default . '.' . $extension;
753 $filename = "$path/$filename.ini";
754
755 // If the one we tried is different than the new name, try again
756 if ($oldFilename != $filename)
757 {
758 $result = $this->loadLanguage($filename, $extension, false);
759 }
760 }
761 }
762 }
763
764 return $result;
765 }
766
767 /**
768 * Loads a language file.
769 *
770 * This method will not note the successful loading of a file - use load() instead.
771 *
772 * @param string $filename The name of the file.
773 * @param string $extension The name of the extension.
774 *
775 * @return boolean True if new strings have been added to the language
776 *
777 * @see JLanguage::load()
778 * @since 11.1
779 */
780 protected function loadLanguage($filename, $extension = 'unknown')
781 {
782 $this->counter++;
783
784 $result = false;
785 $strings = false;
786
787 if (file_exists($filename))
788 {
789 $strings = $this->parse($filename);
790 }
791
792 if ($strings)
793 {
794 if (is_array($strings) && count($strings))
795 {
796 $this->strings = array_replace($this->strings, $strings, $this->override);
797 $result = true;
798 }
799 }
800
801 // Record the result of loading the extension's file.
802 if (!isset($this->paths[$extension]))
803 {
804 $this->paths[$extension] = array();
805 }
806
807 $this->paths[$extension][$filename] = $result;
808
809 return $result;
810 }
811
812 /**
813 * Parses a language file.
814 *
815 * @param string $filename The name of the file.
816 *
817 * @return array The array of parsed strings.
818 *
819 * @since 11.1
820 */
821 protected function parse($filename)
822 {
823 // Capture hidden PHP errors from the parsing.
824 if ($this->debug)
825 {
826 // See https://secure.php.net/manual/en/reserved.variables.phperrormsg.php
827 $php_errormsg = null;
828
829 $trackErrors = ini_get('track_errors');
830 ini_set('track_errors', true);
831 }
832
833 if (!function_exists('parse_ini_file'))
834 {
835 $contents = file_get_contents($filename);
836 $contents = str_replace('_QQ_', '"\""', $contents);
837 $strings = @parse_ini_string($contents);
838 }
839 else
840 {
841 $strings = @parse_ini_file($filename);
842 }
843
844 if (!is_array($strings))
845 {
846 $strings = array();
847 }
848
849 // Restore error tracking to what it was before.
850 if ($this->debug)
851 {
852 ini_set('track_errors', $trackErrors);
853
854 $this->debugFile($filename);
855 }
856
857 return $strings;
858 }
859
860 /**
861 * Debugs a language file
862 *
863 * @param string $filename Absolute path to the file to debug
864 *
865 * @return integer A count of the number of parsing errors
866 *
867 * @since 3.6.3
868 * @throws InvalidArgumentException
869 */
870 public function debugFile($filename)
871 {
872 // Make sure our file actually exists
873 if (!file_exists($filename))
874 {
875 throw new InvalidArgumentException(
876 sprintf('Unable to locate file "%s" for debugging', $filename)
877 );
878 }
879
880 // Initialise variables for manually parsing the file for common errors.
881 $blacklist = array('YES', 'NO', 'NULL', 'FALSE', 'ON', 'OFF', 'NONE', 'TRUE');
882 $debug = $this->getDebug();
883 $this->debug = false;
884 $errors = array();
885 $php_errormsg = null;
886
887 // Open the file as a stream.
888 $file = new SplFileObject($filename);
889
890 foreach ($file as $lineNumber => $line)
891 {
892 // Avoid BOM error as BOM is OK when using parse_ini.
893 if ($lineNumber == 0)
894 {
895 $line = str_replace("\xEF\xBB\xBF", '', $line);
896 }
897
898 $line = trim($line);
899
900 // Ignore comment lines.
901 if (!strlen($line) || $line['0'] == ';')
902 {
903 continue;
904 }
905
906 // Ignore grouping tag lines, like: [group]
907 if (preg_match('#^\[[^\]]*\](\s*;.*)?$#', $line))
908 {
909 continue;
910 }
911
912 // Remove the "_QQ_" from the equation
913 $line = str_replace('"_QQ_"', '', $line);
914 $realNumber = $lineNumber + 1;
915
916 // Check for any incorrect uses of _QQ_.
917 if (strpos($line, '_QQ_') !== false)
918 {
919 $errors[] = $realNumber;
920 continue;
921 }
922
923 // Check for odd number of double quotes.
924 if (substr_count($line, '"') % 2 != 0)
925 {
926 $errors[] = $realNumber;
927 continue;
928 }
929
930 // Check that the line passes the necessary format.
931 if (!preg_match('#^[A-Z][A-Z0-9_\*\-\.]*\s*=\s*".*"(\s*;.*)?$#', $line))
932 {
933 $errors[] = $realNumber;
934 continue;
935 }
936
937 // Check that the key is not in the blacklist.
938 $key = strtoupper(trim(substr($line, 0, strpos($line, '='))));
939
940 if (in_array($key, $blacklist))
941 {
942 $errors[] = $realNumber;
943 }
944 }
945
946 // Check if we encountered any errors.
947 if (count($errors))
948 {
949 $this->errorfiles[$filename] = $filename . ' : error(s) in line(s) ' . implode(', ', $errors);
950 }
951 elseif ($php_errormsg)
952 {
953 // We didn't find any errors but there's probably a parse notice.
954 $this->errorfiles['PHP' . $filename] = 'PHP parser errors :' . $php_errormsg;
955 }
956
957 $this->debug = $debug;
958
959 return count($errors);
960 }
961
962 /**
963 * Get a metadata language property.
964 *
965 * @param string $property The name of the property.
966 * @param mixed $default The default value.
967 *
968 * @return mixed The value of the property.
969 *
970 * @since 11.1
971 */
972 public function get($property, $default = null)
973 {
974 if (isset($this->metadata[$property]))
975 {
976 return $this->metadata[$property];
977 }
978
979 return $default;
980 }
981
982 /**
983 * Determine who called JLanguage or JText.
984 *
985 * @return array Caller information.
986 *
987 * @since 11.1
988 */
989 protected function getCallerInfo()
990 {
991 // Try to determine the source if none was provided
992 if (!function_exists('debug_backtrace'))
993 {
994 return;
995 }
996
997 $backtrace = debug_backtrace();
998 $info = array();
999
1000 // Search through the backtrace to our caller
1001 $continue = true;
1002
1003 while ($continue && next($backtrace))
1004 {
1005 $step = current($backtrace);
1006 $class = @ $step['class'];
1007
1008 // We're looking for something outside of language.php
1009 if ($class != 'JLanguage' && $class != 'JText')
1010 {
1011 $info['function'] = @ $step['function'];
1012 $info['class'] = $class;
1013 $info['step'] = prev($backtrace);
1014
1015 // Determine the file and name of the file
1016 $info['file'] = @ $step['file'];
1017 $info['line'] = @ $step['line'];
1018
1019 $continue = false;
1020 }
1021 }
1022
1023 return $info;
1024 }
1025
1026 /**
1027 * Getter for Name.
1028 *
1029 * @return string Official name element of the language.
1030 *
1031 * @since 11.1
1032 */
1033 public function getName()
1034 {
1035 return $this->metadata['name'];
1036 }
1037
1038 /**
1039 * Get a list of language files that have been loaded.
1040 *
1041 * @param string $extension An optional extension name.
1042 *
1043 * @return array
1044 *
1045 * @since 11.1
1046 */
1047 public function getPaths($extension = null)
1048 {
1049 if (isset($extension))
1050 {
1051 if (isset($this->paths[$extension]))
1052 {
1053 return $this->paths[$extension];
1054 }
1055
1056 return;
1057 }
1058 else
1059 {
1060 return $this->paths;
1061 }
1062 }
1063
1064 /**
1065 * Get a list of language files that are in error state.
1066 *
1067 * @return array
1068 *
1069 * @since 11.1
1070 */
1071 public function getErrorFiles()
1072 {
1073 return $this->errorfiles;
1074 }
1075
1076 /**
1077 * Getter for the language tag (as defined in RFC 3066)
1078 *
1079 * @return string The language tag.
1080 *
1081 * @since 11.1
1082 */
1083 public function getTag()
1084 {
1085 return $this->metadata['tag'];
1086 }
1087
1088 /**
1089 * Getter for the calendar type
1090 *
1091 * @return string The calendar type.
1092 *
1093 * @since 3.7.0
1094 */
1095 public function getCalendar()
1096 {
1097 if (isset($this->metadata['calendar']))
1098 {
1099 return $this->metadata['calendar'];
1100 }
1101 else
1102 {
1103 return 'gregorian';
1104 }
1105 }
1106
1107 /**
1108 * Get the RTL property.
1109 *
1110 * @return boolean True is it an RTL language.
1111 *
1112 * @since 11.1
1113 */
1114 public function isRtl()
1115 {
1116 return (bool) $this->metadata['rtl'];
1117 }
1118
1119 /**
1120 * Set the Debug property.
1121 *
1122 * @param boolean $debug The debug setting.
1123 *
1124 * @return boolean Previous value.
1125 *
1126 * @since 11.1
1127 */
1128 public function setDebug($debug)
1129 {
1130 $previous = $this->debug;
1131 $this->debug = (boolean) $debug;
1132
1133 return $previous;
1134 }
1135
1136 /**
1137 * Get the Debug property.
1138 *
1139 * @return boolean True is in debug mode.
1140 *
1141 * @since 11.1
1142 */
1143 public function getDebug()
1144 {
1145 return $this->debug;
1146 }
1147
1148 /**
1149 * Get the default language code.
1150 *
1151 * @return string Language code.
1152 *
1153 * @since 11.1
1154 */
1155 public function getDefault()
1156 {
1157 return $this->default;
1158 }
1159
1160 /**
1161 * Set the default language code.
1162 *
1163 * @param string $lang The language code.
1164 *
1165 * @return string Previous value.
1166 *
1167 * @since 11.1
1168 */
1169 public function setDefault($lang)
1170 {
1171 $previous = $this->default;
1172 $this->default = $lang;
1173
1174 return $previous;
1175 }
1176
1177 /**
1178 * Get the list of orphaned strings if being tracked.
1179 *
1180 * @return array Orphaned text.
1181 *
1182 * @since 11.1
1183 */
1184 public function getOrphans()
1185 {
1186 return $this->orphans;
1187 }
1188
1189 /**
1190 * Get the list of used strings.
1191 *
1192 * Used strings are those strings requested and found either as a string or a constant.
1193 *
1194 * @return array Used strings.
1195 *
1196 * @since 11.1
1197 */
1198 public function getUsed()
1199 {
1200 return $this->used;
1201 }
1202
1203 /**
1204 * Determines is a key exists.
1205 *
1206 * @param string $string The key to check.
1207 *
1208 * @return boolean True, if the key exists.
1209 *
1210 * @since 11.1
1211 */
1212 public function hasKey($string)
1213 {
1214 $key = strtoupper($string);
1215
1216 return isset($this->strings[$key]);
1217 }
1218
1219 /**
1220 * Returns an associative array holding the metadata.
1221 *
1222 * @param string $lang The name of the language.
1223 *
1224 * @return mixed If $lang exists return key/value pair with the language metadata, otherwise return NULL.
1225 *
1226 * @since 11.1
1227 * @deprecated 3.7.0, use JLanguageHelper::getMetadata() instead.
1228 */
1229 public static function getMetadata($lang)
1230 {
1231 JLog::add(__METHOD__ . '() is deprecated, use JLanguageHelper::getMetadata() instead.', JLog::WARNING, 'deprecated');
1232
1233 return JLanguageHelper::getMetadata($lang);
1234 }
1235
1236 /**
1237 * Returns a list of known languages for an area
1238 *
1239 * @param string $basePath The basepath to use
1240 *
1241 * @return array key/value pair with the language file and real name.
1242 *
1243 * @since 11.1
1244 * @deprecated 3.7.0, use JLanguageHelper::getKnownLanguages() instead.
1245 */
1246 public static function getKnownLanguages($basePath = JPATH_BASE)
1247 {
1248 JLog::add(__METHOD__ . '() is deprecated, use JLanguageHelper::getKnownLanguages() instead.', JLog::WARNING, 'deprecated');
1249
1250 return JLanguageHelper::getKnownLanguages($basePath);
1251 }
1252
1253 /**
1254 * Get the path to a language
1255 *
1256 * @param string $basePath The basepath to use.
1257 * @param string $language The language tag.
1258 *
1259 * @return string language related path or null.
1260 *
1261 * @since 11.1
1262 * @deprecated 3.7.0, use JLanguageHelper::getLanguagePath() instead.
1263 */
1264 public static function getLanguagePath($basePath = JPATH_BASE, $language = null)
1265 {
1266 JLog::add(__METHOD__ . '() is deprecated, use JLanguageHelper::getLanguagePath() instead.', JLog::WARNING, 'deprecated');
1267
1268 return JLanguageHelper::getLanguagePath($basePath, $language);
1269 }
1270
1271 /**
1272 * Set the language attributes to the given language.
1273 *
1274 * Once called, the language still needs to be loaded using JLanguage::load().
1275 *
1276 * @param string $lang Language code.
1277 *
1278 * @return string Previous value.
1279 *
1280 * @since 11.1
1281 * @deprecated 4.0 (CMS) - Instantiate a new JLanguage object instead
1282 */
1283 public function setLanguage($lang)
1284 {
1285 JLog::add(__METHOD__ . ' is deprecated. Instantiate a new JLanguage object instead.', JLog::WARNING, 'deprecated');
1286
1287 $previous = $this->lang;
1288 $this->lang = $lang;
1289 $this->metadata = JLanguageHelper::getMetadata($this->lang);
1290
1291 return $previous;
1292 }
1293
1294 /**
1295 * Get the language locale based on current language.
1296 *
1297 * @return array The locale according to the language.
1298 *
1299 * @since 11.1
1300 */
1301 public function getLocale()
1302 {
1303 if (!isset($this->locale))
1304 {
1305 $locale = str_replace(' ', '', isset($this->metadata['locale']) ? $this->metadata['locale'] : '');
1306
1307 if ($locale)
1308 {
1309 $this->locale = explode(',', $locale);
1310 }
1311 else
1312 {
1313 $this->locale = false;
1314 }
1315 }
1316
1317 return $this->locale;
1318 }
1319
1320 /**
1321 * Get the first day of the week for this language.
1322 *
1323 * @return integer The first day of the week according to the language
1324 *
1325 * @since 11.1
1326 */
1327 public function getFirstDay()
1328 {
1329 return (int) (isset($this->metadata['firstDay']) ? $this->metadata['firstDay'] : 0);
1330 }
1331
1332 /**
1333 * Get the weekends days for this language.
1334 *
1335 * @return string The weekend days of the week separated by a comma according to the language
1336 *
1337 * @since 3.2
1338 */
1339 public function getWeekEnd()
1340 {
1341 return (isset($this->metadata['weekEnd']) && $this->metadata['weekEnd']) ? $this->metadata['weekEnd'] : '0,6';
1342 }
1343
1344 /**
1345 * Searches for language directories within a certain base dir.
1346 *
1347 * @param string $dir directory of files.
1348 *
1349 * @return array Array holding the found languages as filename => real name pairs.
1350 *
1351 * @since 11.1
1352 * @deprecated 3.7.0, use JLanguageHelper::parseLanguageFiles() instead.
1353 */
1354 public static function parseLanguageFiles($dir = null)
1355 {
1356 JLog::add(__METHOD__ . '() is deprecated, use JLanguageHelper::parseLanguageFiles() instead.', JLog::WARNING, 'deprecated');
1357
1358 return JLanguageHelper::parseLanguageFiles($dir);
1359 }
1360
1361 /**
1362 * Parse XML file for language information.
1363 *
1364 * @param string $path Path to the XML files.
1365 *
1366 * @return array Array holding the found metadata as a key => value pair.
1367 *
1368 * @since 11.1
1369 * @throws RuntimeException
1370 * @deprecated 3.7.0, use JLanguageHelper::parseXMLLanguageFile() instead.
1371 */
1372 public static function parseXMLLanguageFile($path)
1373 {
1374 JLog::add(__METHOD__ . '() is deprecated, use JLanguageHelper::parseXMLLanguageFile() instead.', JLog::WARNING, 'deprecated');
1375
1376 return JLanguageHelper::parseXMLLanguageFile($path);
1377 }
1378 }
1379