Revision 1690
Added by darkviper over 13 years ago
| ModLanguage.php | ||
|---|---|---|
| 7 | 7 |
* @license http://www.gnu.org/licenses/gpl.html |
| 8 | 8 |
* @version $Id$ |
| 9 | 9 |
* @filesource $HeadURL$ |
| 10 |
* @since Datei vorhanden seit Release 2.8.2
|
|
| 10 |
* @since Datei vorhanden seit Release 2.8.4
|
|
| 11 | 11 |
* @lastmodified $Date$ |
| 12 | 12 |
*/ |
| 13 | 13 |
class ModLanguage {
|
| 14 | 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 |
|
| 15 | 20 |
private $_sCurrentLanguage = ''; |
| 16 |
private $_sDefaultLanguage = '';
|
|
| 21 |
// @var string full directory with trailing slash to search language files in
|
|
| 17 | 22 |
private $_sLanguageDirectory = ''; |
| 18 |
private $_sLanguageFile = ''; |
|
| 19 |
private $_LanguageTable = array(); |
|
| 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 |
|
| 20 | 29 |
private $_bLoaded = false; |
| 30 |
// @object hold the Singleton instance |
|
| 31 |
private static $_oInstance = null; |
|
| 21 | 32 |
|
| 22 |
private static $_oInstance = null; |
|
| 23 |
/* prevent from public instancing */ |
|
| 24 |
protected function __construct() { }
|
|
| 25 |
/* prevent from cloning */ |
|
| 26 |
private function __clone() {}
|
|
| 27 | 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 |
/** |
|
| 28 | 42 |
* get a valid instance of this class |
| 29 | 43 |
* @return object |
| 30 | 44 |
*/ |
| ... | ... | |
| 36 | 50 |
return self::$_oInstance; |
| 37 | 51 |
} |
| 38 | 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 |
/** |
|
| 39 | 82 |
* set language and load needed language file |
| 40 | 83 |
* @param string $sDirectory full path to the language files |
| 41 |
* @param string $sLanguage 2-letters language code |
|
| 42 |
* @param string $sDefault 2-letters default-language code |
|
| 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] |
|
| 43 | 88 |
*/ |
| 44 |
public function setLanguage($sDirectory, $sLanguage, $sDefault = 'EN')
|
|
| 89 |
public function setLanguage($sDirectory, $sCurrentLanguage, $sDefaultLanguage = 'EN')
|
|
| 45 | 90 |
{
|
| 91 |
// sanitize arguments |
|
| 46 | 92 |
$sBasePath = realpath(dirname(dirname(__FILE__))); |
| 47 |
$sLangDir = realpath($sDirectory); |
|
| 48 |
if(!preg_match('/^'.preg_quote($sBasePath, '/').'/', $sLangDir)) {
|
|
| 49 |
throw new SecDirectoryTraversalException(); |
|
| 50 |
} |
|
| 51 |
$sLangDir = str_replace('\\', '/', $sLangDir);
|
|
| 52 |
$sLangDir = rtrim($sLangDir, '/').'/'; |
|
| 53 |
$sLanguage = strtoupper($sLanguage); |
|
| 54 |
$sLanguage = strtoupper($sDefault); |
|
| 55 |
if($this->_sLanguageDirectory != $sLangDir || |
|
| 56 |
$this->_sCurrentLanguage != $sLanguage || |
|
| 57 |
$this->_sDefaultLanguage != $sDefault) |
|
| 58 |
{
|
|
| 59 |
// only load language if not already loaded |
|
| 60 |
$this->_sLanguageDirectory = rtrim($sLangDir, '/').'/'; |
|
| 61 |
$this->_sCurrentLanguage = $sLanguage; |
|
| 62 |
$this->_sDefaultLanguage = $sDefault; |
|
| 63 |
|
|
| 64 |
if(!$this->_findLanguageFile()) {
|
|
| 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 search SYSTEM_LANGUAGE |
|
| 106 |
$this->_loadLanguage($sLangDir, $this->_sSystemLanguage); |
|
| 107 |
// at second merge DEFAULT_LANGUAGE |
|
| 108 |
if(!in_array($sDefaultLanguage, $this->_aLoadedLanguages)) {
|
|
| 109 |
$this->_loadLanguage($sLangDir, $sDefaultLanguage); |
|
| 110 |
} |
|
| 111 |
// at third merge CURRENT_LANGUAGE |
|
| 112 |
if(!in_array($sCurrentLanguage, $this->_aLoadedLanguages)) {
|
|
| 113 |
$this->_loadLanguage($sLangDir, $sCurrentLanguage); |
|
| 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))) {
|
|
| 65 | 128 |
$msg = 'unable to find valid language definition file in<br />'; |
| 66 | 129 |
$msg .= '"'.str_replace($sBasePath, '', $this->_sLanguageDirectory).'"'; |
| 67 | 130 |
throw new TranslationException($msg); |
| 68 | 131 |
} |
| 69 |
$this->_importArrays(); |
|
| 132 |
$this->_bLoaded = true; |
|
| 133 |
}else {
|
|
| 134 |
throw new SecDirectoryTraversalException($sLangDir); |
|
| 70 | 135 |
} |
| 71 |
$this->_bLoaded = (sizeof($this->_LanguageTable) > 0); |
|
| 72 | 136 |
} |
| 73 | 137 |
/** |
| 74 |
* return requested translation for a key
|
|
| 75 |
* @param string $sLanguageKey 2-uppercase letters language code
|
|
| 76 |
* @return string found translation or empty string
|
|
| 138 |
* load language from given directory
|
|
| 139 |
* @param string $sLangDir
|
|
| 140 |
* @param string $sLanguage
|
|
| 77 | 141 |
*/ |
| 78 |
public function __get($sLanguageKey)
|
|
| 142 |
private function _loadLanguage($sLangDir, $sLanguage)
|
|
| 79 | 143 |
{
|
| 80 |
$sRetval = (isset($this->_LanguageTable[$sLanguageKey]) |
|
| 81 |
? $this->_LanguageTable[$sLanguageKey] : '{missing: '.$sLanguageKey.'}');
|
|
| 82 |
return $sRetval; |
|
| 144 |
if(is_readable($sLangDir.$sLanguage.'.php')) {
|
|
| 145 |
$this->_aLanguageTable = array_merge($this->_aLanguageTable, |
|
| 146 |
$this->_importArrays($sLangDir.$sLanguage.'.php')); |
|
| 147 |
$this->_aLoadedLanguages[] = $sLanguage; |
|
| 148 |
} |
|
| 83 | 149 |
} |
| 84 | 150 |
/** |
| 85 |
* returns the whoole language array for use in templateengine |
|
| 86 |
* @return array |
|
| 151 |
* find first available language in given directory |
|
| 152 |
* @param string $sLangDir the directory to scan for language files |
|
| 153 |
* @return string returns the 2 char language code or FALSE if search fails |
|
| 87 | 154 |
*/ |
| 88 |
public function getLangArray()
|
|
| 155 |
private function _findFirstLanguage($sLangDir)
|
|
| 89 | 156 |
{
|
| 90 |
return $this->_LanguageTable; |
|
| 91 |
} |
|
| 92 |
/** |
|
| 93 |
* search language file in order: LANGUAGE - DEFAULT_LANGUAGE - FIRST_FOUND |
|
| 94 |
* @return boolean |
|
| 95 |
*/ |
|
| 96 |
private function _findLanguageFile() |
|
| 97 |
{
|
|
| 98 |
$bMatch = false; |
|
| 99 |
$dir = $this->_sLanguageDirectory; |
|
| 100 |
if(is_readable($dir.$this->_sCurrentLanguage.'.php')) {
|
|
| 101 |
// check actual language |
|
| 102 |
$this->_sLanguageFile = $dir.$this->_sCurrentLanguage.'.php'; |
|
| 103 |
$bMatch = true; |
|
| 104 |
}else {
|
|
| 105 |
if(is_readable($dir.$this->_sDefaultLanguage.'.php')) {
|
|
| 106 |
// check default language |
|
| 107 |
$this->_sLanguageFile = $dir.$this->_sDefaultLanguage.'.php'; |
|
| 108 |
$bMatch = true; |
|
| 109 |
}else {
|
|
| 110 |
// search for first available and readable language file |
|
| 111 |
if(is_readable($dir)) {
|
|
| 112 |
$iterator = new DirectoryIterator($dir); |
|
| 113 |
foreach ($iterator as $fileinfo) {
|
|
| 114 |
if(!preg_match('/^[A-Z]{2}\.php$/', $fileinfo->getBasename())) { continue; }
|
|
| 115 |
$sLanguageFile = str_replace('\\', '/', $fileinfo->getPathname());
|
|
| 116 |
if(is_readable($sLanguageFile)) {
|
|
| 117 |
$this->_sLanguageFile = $sLanguageFile; |
|
| 118 |
$bMatch = true; |
|
| 119 |
break; |
|
| 120 |
} |
|
| 121 |
} |
|
| 157 |
// search for first available and readable language file |
|
| 158 |
$sRetval = false; |
|
| 159 |
if(is_readable($sLangDir)) {
|
|
| 160 |
$iterator = new DirectoryIterator($sLangDir); |
|
| 161 |
foreach ($iterator as $fileinfo) {
|
|
| 162 |
if(!preg_match('/^[A-Z]{2}\.php$/', $fileinfo->getBasename())) { continue; }
|
|
| 163 |
$sLanguageFile = $fileinfo->getPathname(); |
|
| 164 |
if(is_readable($sLanguageFile)) {
|
|
| 165 |
$sRetval = basename($sLanguageFile, '.php'); |
|
| 166 |
break; |
|
| 122 | 167 |
} |
| 123 | 168 |
} |
| 124 | 169 |
} |
| 125 |
return $bMatch;
|
|
| 170 |
return $sRetval;
|
|
| 126 | 171 |
} |
| 127 | 172 |
/** |
| 128 | 173 |
* import key-values from language file |
| 174 |
* @param string $sLanguageFile |
|
| 175 |
* @return array of language definitions |
|
| 129 | 176 |
*/ |
| 130 |
private function _importArrays() |
|
| 177 |
private function _importArrays($sLanguageFile)
|
|
| 131 | 178 |
{
|
| 132 |
include($this->_sLanguageFile); |
|
| 133 |
$aLangSections = array('HEADING', 'TEXT', 'MESSAGE', 'MENU', 'OVERVIEW', 'GENERIC');
|
|
| 179 |
include($sLanguageFile); |
|
| 180 |
$aAllVars = get_defined_vars(); |
|
| 181 |
$aLangSections = array(); |
|
| 182 |
$aLanguageTable = array(); |
|
| 183 |
foreach($aAllVars as $key=>$value) {
|
|
| 184 |
// extract the names of arrays from language file |
|
| 185 |
if(is_array($value)) {
|
|
| 186 |
$aLangSections[] = $key; |
|
| 187 |
} |
|
| 188 |
} |
|
| 134 | 189 |
foreach($aLangSections as $sSection) {
|
| 135 |
if(isset(${$sSection}) && is_array(${$sSection})) {
|
|
| 136 |
foreach(${$sSection} as $key => $value) {
|
|
| 137 |
$this->_LanguageTable[$sSection.'_'.$key] = $value;
|
|
| 138 |
}
|
|
| 190 |
// walk through all arrays
|
|
| 191 |
foreach(${$sSection} as $key => $value) {
|
|
| 192 |
// and import all found translations
|
|
| 193 |
$aLanguageTable[$sSection.'_'.$key] = $value;
|
|
| 139 | 194 |
} |
| 140 | 195 |
} |
| 196 |
return $aLanguageTable; |
|
| 141 | 197 |
} |
| 142 | 198 |
} // end class Translate |
| 143 | 199 |
/** |
| 144 | 200 |
* Exception class for Translation |
| 145 | 201 |
*/ |
| 146 | 202 |
class TranslationException extends AppException {}
|
| 147 |
|
|
Also available in: Unified diff
ModLanguage modified for auto fallback to DEFAULT_LANGUAGE
globalExceptionHandler added AppException, SecurityException, SecDirectoryTraversalException
Errormessage for old class.database modified