Source for file language.php

Documentation is available at language.php

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

Documentation generated on Tue, 19 Nov 2013 15:06:25 +0100 by phpDocumentor 1.4.3