| 1 | <?php
 | 
  
    | 2 | /**
 | 
  
    | 3 |  * @category     WebsiteBaker
 | 
  
    | 4 |  * @package      WebsiteBaker_Core
 | 
  
    | 5 |  * @author       Werner v.d.Decken
 | 
  
    | 6 |  * @copyright    WebsiteBaker Org e.V.
 | 
  
    | 7 |  * @license      http://www.gnu.org/licenses/gpl.html
 | 
  
    | 8 |  * @version      $Id: ModLanguage.php 1691 2012-06-10 16:13:24Z darkviper $
 | 
  
    | 9 |  * @filesource   $HeadURL: svn://isteam.dynxs.de/wb-archiv/branches/2.8.x/wb/framework/ModLanguage.php $
 | 
  
    | 10 |  * @since        Datei vorhanden seit Release 2.8.4
 | 
  
    | 11 |  * @lastmodified $Date: 2012-06-10 18:13:24 +0200 (Sun, 10 Jun 2012) $
 | 
  
    | 12 |  */
 | 
  
    | 13 | class ModLanguage {
 | 
  
    | 14 | 
 | 
  
    | 15 | // @var string 2 upercase chars for hardcoded system default language
 | 
  
    | 16 | 	private $_sSystemLanguage    = 'EN';
 | 
  
    | 17 | // @var string 2 upercase chars for default fallback language
 | 
  
    | 18 | 	private $_sDefaultLanguage   = '';
 | 
  
    | 19 | // @var string 2 upercase chars for current active language
 | 
  
    | 20 | 	private $_sCurrentLanguage   = '';
 | 
  
    | 21 | // @var string full directory with trailing slash to search language files in
 | 
  
    | 22 | 	private $_sLanguageDirectory = '';
 | 
  
    | 23 | // @array list to hold the complete resulting translation table
 | 
  
    | 24 | 	private $_aLanguageTable     = array();
 | 
  
    | 25 | // @array list of all loaded/merged languages
 | 
  
    | 26 | 	private $_aLoadedLanguages   = array();
 | 
  
    | 27 | 
 | 
  
    | 28 | // @boolean set to TRUE if language is successfully loaded
 | 
  
    | 29 | 	private $_bLoaded            = false;
 | 
  
    | 30 | // @object hold the Singleton instance
 | 
  
    | 31 | 	private static $_oInstance   = null;
 | 
  
    | 32 | 
 | 
  
    | 33 | /**
 | 
  
    | 34 |  *  prevent class from public instancing
 | 
  
    | 35 |  */
 | 
  
    | 36 | 	final protected function  __construct() { }
 | 
  
    | 37 | /**
 | 
  
    | 38 |  *  prevent from cloning existing instance
 | 
  
    | 39 |  */
 | 
  
    | 40 | 	final private function __clone() {}
 | 
  
    | 41 | /**
 | 
  
    | 42 |  * get a valid instance of this class
 | 
  
    | 43 |  * @return object
 | 
  
    | 44 |  */
 | 
  
    | 45 | 	static public function getInstance() {
 | 
  
    | 46 | 		if( is_null(self::$_oInstance) ) {
 | 
  
    | 47 |             $c = __CLASS__;
 | 
  
    | 48 |             self::$_oInstance = new $c;
 | 
  
    | 49 | 		}
 | 
  
    | 50 | 		return self::$_oInstance;
 | 
  
    | 51 | 	}
 | 
  
    | 52 | /**
 | 
  
    | 53 |  * return requested translation for a key
 | 
  
    | 54 |  * @param string $sLanguageKey 2-uppercase letters language code
 | 
  
    | 55 |  * @return string found translation or empty string
 | 
  
    | 56 |  * @throws TranslationException
 | 
  
    | 57 |  */
 | 
  
    | 58 | 	public function __get($sLanguageKey)
 | 
  
    | 59 | 	{
 | 
  
    | 60 | 		if($this->_bLoaded) {
 | 
  
    | 61 | 			$sRetval = (isset($this->_aLanguageTable[$sLanguageKey])
 | 
  
    | 62 | 						? $this->_aLanguageTable[$sLanguageKey] : '{missing: '.$sLanguageKey.'}');
 | 
  
    | 63 | 			return $sRetval;
 | 
  
    | 64 | 		}
 | 
  
    | 65 | 		$msg = 'No translation table loaded';
 | 
  
    | 66 | 		throw new TranslationException($msg);
 | 
  
    | 67 | 	}
 | 
  
    | 68 | /**
 | 
  
    | 69 |  * returns the whoole language array for use in templateengine
 | 
  
    | 70 |  * @return array
 | 
  
    | 71 |  * @throws TranslationException
 | 
  
    | 72 |  */
 | 
  
    | 73 | 	public function getLangArray()
 | 
  
    | 74 | 	{
 | 
  
    | 75 | 		if($this->_bLoaded) {
 | 
  
    | 76 | 			return $this->_aLanguageTable;
 | 
  
    | 77 | 		}
 | 
  
    | 78 | 		$msg = 'No translation table loaded';
 | 
  
    | 79 | 		throw new TranslationException($msg);
 | 
  
    | 80 | 	}
 | 
  
    | 81 | /**
 | 
  
    | 82 |  * set language and load needed language file
 | 
  
    | 83 |  * @param string $sDirectory full path to the language files
 | 
  
    | 84 |  * @param string $sLanguage 2 chars current active language code
 | 
  
    | 85 |  * @param string $sDefault 2 chars default fallback language code
 | 
  
    | 86 |  * @throws SecDirectoryTraversalException [global exception]
 | 
  
    | 87 |  * @throws TranslationException [private exception]
 | 
  
    | 88 |  */
 | 
  
    | 89 | 	public function setLanguage($sDirectory, $sCurrentLanguage, $sDefaultLanguage = 'EN')
 | 
  
    | 90 | 	{
 | 
  
    | 91 | 		// sanitize arguments
 | 
  
    | 92 | 		$sBasePath = realpath(dirname(dirname(__FILE__)));
 | 
  
    | 93 | 		$sLangDir  = realpath($sDirectory);
 | 
  
    | 94 | 		if(preg_match('/^'.preg_quote($sBasePath, '/').'/', $sLangDir)) {
 | 
  
    | 95 | 			$sLangDir  = rtrim(str_replace('\\', '/', $sLangDir), '/').'/';
 | 
  
    | 96 | 			$sCurrentLanguage = strtoupper($sCurrentLanguage);
 | 
  
    | 97 | 			$sDefaultLanguage = strtoupper($sDefaultLanguage);
 | 
  
    | 98 | 			// check if the requested language is not already loaded
 | 
  
    | 99 | 			if($this->_sLanguageDirectory != $sLangDir ||
 | 
  
    | 100 | 			   $this->_sCurrentLanguage   != $sCurrentLanguage ||
 | 
  
    | 101 | 			   $this->_sDefaultLanguage   != $sDefaultLanguage)
 | 
  
    | 102 | 			{
 | 
  
    | 103 | 			// now load and merge the files in order SYSTEM - DEFAULT - CURRENT
 | 
  
    | 104 | 				$this->_aLanguageTable = array();
 | 
  
    | 105 | 				// at first load DEFAULT_LANGUAGE
 | 
  
    | 106 | 				$this->_loadLanguage($sLangDir, $sDefaultLanguage);
 | 
  
    | 107 | 				// at second merge CURRENT_LANGUAGE to front if not already loaded
 | 
  
    | 108 | 				if(!in_array($sCurrentLanguage, $this->_aLoadedLanguages)) {
 | 
  
    | 109 | 					$this->_loadLanguage($sLangDir, $sCurrentLanguage);
 | 
  
    | 110 | 				}
 | 
  
    | 111 | 				// at last merge SYSTEM_LANGUAGE to background if not already loaded
 | 
  
    | 112 | 				if(!in_array($this->_sSystemLanguage, $this->_aLoadedLanguages)) {
 | 
  
    | 113 | 					$this->_loadLanguage($sLangDir, $this->_sSystemLanguage, true);
 | 
  
    | 114 | 				}
 | 
  
    | 115 | 				// if no predefined language was fond, search for first available language
 | 
  
    | 116 | 				if(sizeof($this->_aLanguageTable) == 0) {
 | 
  
    | 117 | 					// if absolutely no language was fond, throw an exception
 | 
  
    | 118 | 					if(false !== ($sRandomLanguage = $this->_findFirstLanguage($sLangDir))) {
 | 
  
    | 119 | 						$this->_loadLanguage($sLangDir, $sRandomLanguage);
 | 
  
    | 120 | 					}
 | 
  
    | 121 | 				}
 | 
  
    | 122 | 				// remember last settings
 | 
  
    | 123 | 				$this->_sLanguageDirectory = $sLangDir;
 | 
  
    | 124 | 				$this->_sCurrentLanguage   = $sCurrentLanguage;
 | 
  
    | 125 | 				$this->_sDefaultLanguage   = $sDefaultLanguage;
 | 
  
    | 126 | 			}
 | 
  
    | 127 | 			if(!($this->_bLoaded = (sizeof($this->_aLanguageTable) != 0))) {
 | 
  
    | 128 | 				$msg  = 'unable to find valid language definition file in<br />';
 | 
  
    | 129 | 				$msg .= '"'.str_replace($sBasePath, '', $this->_sLanguageDirectory).'"';
 | 
  
    | 130 | 				throw new TranslationException($msg);
 | 
  
    | 131 | 			}
 | 
  
    | 132 | 			$this->_bLoaded = true;
 | 
  
    | 133 | 		}else {
 | 
  
    | 134 | 			throw new SecDirectoryTraversalException($sLangDir);
 | 
  
    | 135 | 		}
 | 
  
    | 136 | 	}
 | 
  
    | 137 | /**
 | 
  
    | 138 |  * load language from given directory
 | 
  
    | 139 |  * @param string $sLangDir
 | 
  
    | 140 |  * @param string $sLanguage
 | 
  
    | 141 |  */
 | 
  
    | 142 | 	private function _loadLanguage($sLangDir, $sLanguage, $bLoadSystemLanguage = false)
 | 
  
    | 143 | 	{
 | 
  
    | 144 | 		if(is_readable($sLangDir.$sLanguage.'.php')) {
 | 
  
    | 145 | 			$aTemp = $this->_importArrays($sLangDir.$sLanguage.'.php');
 | 
  
    | 146 | 			if($bLoadSystemLanguage) {
 | 
  
    | 147 | 				$this->_aLanguageTable = array_merge($aTemp, $this->_aLanguageTable);
 | 
  
    | 148 | 			}else {
 | 
  
    | 149 | 				$this->_aLanguageTable = array_merge($this->_aLanguageTable, $aTemp);
 | 
  
    | 150 | 			}
 | 
  
    | 151 | 			$this->_aLoadedLanguages[] = $sLanguage;
 | 
  
    | 152 | 		}
 | 
  
    | 153 | 	}
 | 
  
    | 154 | /**
 | 
  
    | 155 |  * find first available language in given directory
 | 
  
    | 156 |  * @param  string $sLangDir the directory to scan for language files
 | 
  
    | 157 |  * @return string returns the 2 char language code or FALSE if search fails
 | 
  
    | 158 |  */
 | 
  
    | 159 | 	private function _findFirstLanguage($sLangDir)
 | 
  
    | 160 | 	{
 | 
  
    | 161 | 	// search for first available and readable language file
 | 
  
    | 162 | 		$sRetval = false;
 | 
  
    | 163 | 		if(is_readable($sLangDir)) {
 | 
  
    | 164 | 			$iterator = new DirectoryIterator($sLangDir);
 | 
  
    | 165 | 			foreach ($iterator as $fileinfo) {
 | 
  
    | 166 | 				if(!preg_match('/^[A-Z]{2}\.php$/', $fileinfo->getBasename())) { continue; }
 | 
  
    | 167 | 				$sLanguageFile = $fileinfo->getPathname();
 | 
  
    | 168 | 				if(is_readable($sLanguageFile)) {
 | 
  
    | 169 | 					$sRetval = basename($sLanguageFile, '.php');
 | 
  
    | 170 | 					break;
 | 
  
    | 171 | 				}
 | 
  
    | 172 | 			}
 | 
  
    | 173 | 		}
 | 
  
    | 174 | 		return $sRetval;
 | 
  
    | 175 | 	}
 | 
  
    | 176 | /**
 | 
  
    | 177 |  * import key-values from language file
 | 
  
    | 178 |  * @param  string $sLanguageFile
 | 
  
    | 179 |  * @return array of language definitions
 | 
  
    | 180 |  */
 | 
  
    | 181 | 	private function _importArrays($sLanguageFile)
 | 
  
    | 182 | 	{
 | 
  
    | 183 | 		include($sLanguageFile);
 | 
  
    | 184 | 		$aAllVars = get_defined_vars();
 | 
  
    | 185 | 		$aLangSections = array();
 | 
  
    | 186 | 		$aLanguageTable = array();
 | 
  
    | 187 | 		foreach($aAllVars as $key=>$value) {
 | 
  
    | 188 | 		// extract the names of arrays from language file
 | 
  
    | 189 | 			if(is_array($value)) {
 | 
  
    | 190 | 				$aLangSections[] = $key;
 | 
  
    | 191 | 			}
 | 
  
    | 192 | 		}
 | 
  
    | 193 | 		foreach($aLangSections as $sSection) {
 | 
  
    | 194 | 		// walk through all arrays
 | 
  
    | 195 | 			foreach(${$sSection} as $key => $value) {
 | 
  
    | 196 | 			// and import all found translations
 | 
  
    | 197 | 				$aLanguageTable[$sSection.'_'.$key] = $value;
 | 
  
    | 198 | 			}
 | 
  
    | 199 | 		}
 | 
  
    | 200 | 		return $aLanguageTable;
 | 
  
    | 201 | 	}
 | 
  
    | 202 | } // end class Translate
 | 
  
    | 203 | /**
 | 
  
    | 204 |  *  Exception class for Translation
 | 
  
    | 205 |  */
 | 
  
    | 206 | class TranslationException extends AppException {}
 |