PageRenderTime 96ms CodeModel.GetById 12ms app.highlight 51ms RepoModel.GetById 26ms app.codeStats 1ms

/libraries/joomla/language/language.php

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