Project

General

Profile

1 1860 darkviper
<?php
2
3
/**
4
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
 */
19
20
/**
21
 * Translate.php
22
 *
23
 * @category     Core
24
 * @package      Core_Translation
25 1873 darkviper
 * @copyright    Werner v.d.Decken <wkl@isteam.de>
26 1860 darkviper
 * @author       Werner v.d.Decken <wkl@isteam.de>
27
 * @license      http://www.gnu.org/licenses/gpl.html   GPL License
28
 * @version      0.0.1
29
 * @revision     $Revision$
30
 * @link         $HeadURL$
31
 * @lastmodified $Date$
32
 * @since        File available since 12.01.2013
33 1873 darkviper
 * @description  basic class of the Translate package
34 1860 darkviper
 */
35
class Translate {
36
37 1873 darkviper
/** holds the active singleton instance */
38 1864 darkviper
	private static $_oInstance     = null;
39 1873 darkviper
/** name of the default adaptor */
40 1864 darkviper
	protected $sAdaptor            = 'WbOldStyle';
41 1873 darkviper
/** setting for the system language (default: en) */
42 1864 darkviper
	protected $sSystemLanguage     = 'en';
43 1873 darkviper
/** setting for the default runtime language (default: en) */
44 1864 darkviper
	protected $sDefaultLanguage    = 'en';
45 1873 darkviper
/** setting for the user defined language (default: en) */
46 1864 darkviper
	protected $sUserLanguage       = 'en';
47 1873 darkviper
/** switch cache on/off */
48 1864 darkviper
	protected $bUseCache           = true;
49 1873 darkviper
/** defines if the keys of undefines translation should be preserved */
50
	protected $bRemoveMissing      = false;
51
/** can hold up to 31 boolean option settings */
52
	protected $iOptions            = 0;
53
/** List of loaded translation tables */
54 1864 darkviper
	protected $aLoadedAddons       = array();
55
/** TranslationTable object of the core and additional one activated addon */
56
	protected $aActiveTranslations = array();
57 2096 darkviper
// **  */
58
    protected $aPrivatePriorities = array();
59 1873 darkviper
/** possible option flags */
60
	const CACHE_DISABLED = 1; // ( 2^0 )
61
	const KEEP_MISSING   = 2; // ( 2^1 )
62 2096 darkviper
/** types of translation tables */
63
    const TABLE_CORE               = 0;
64
    const TABLE_ADDON              = 1;
65
    const TABLE_THEME              = 2;
66
    const TABLE_TEMPLATE           = 2;
67
    const FIRST_USER_DEFINED_TABLE = 10;
68
    const STEP_USER_DEFINED_TABLES = 5;
69 1860 darkviper
/** prevent class from public instancing and get an object to hold extensions */
70
	protected function  __construct() {}
71
/** prevent from cloning existing instance */
72
	private function __clone() {}
73
/**
74
 * get a valid instance of this class
75
 * @return object
76
 */
77
	static public function getInstance() {
78
		if( is_null(self::$_oInstance) ) {
79
            $c = __CLASS__;
80
            self::$_oInstance = new $c;
81
		}
82
		return self::$_oInstance;
83
	}
84
/**
85
 * Initialize the Translations
86 1873 darkviper
 * @param string SystemLanguage code ('de' || 'de_CH' || 'de_CH_uri')
87 1860 darkviper
 * @param string DefaultLanguage code ('de' || 'de_CH' || 'de_CH_uri')
88
 * @param string UserLanguage code ('de' || 'de_CH' || 'de_CH_uri')
89 1873 darkviper
 * @param string Name of the adaptor to use
90
 * @param integer possible options (DISABLE_CACHE | KEEP_MISSING)
91 1860 darkviper
 * @throws TranslationException
92
 */
93 1873 darkviper
	public function initialize($sSystemLanguage,
94
	                           $sDefaultLanguage,
95
	                           $sUserLanguage = '',
96
	                           $sAdaptor = 'WbOldStyle',
97
	                           $iOptions = 0)
98 1860 darkviper
	{
99
		if(!class_exists('TranslateAdaptor'.$sAdaptor)) {
100
			throw new TranslationException('unable to load adaptor: '.$sAdaptor);
101
		}
102 1873 darkviper
		$this->sAdaptor       = 'TranslateAdaptor'.$sAdaptor;
103 1902 darkviper
		$this->bUseCache      = (!($iOptions & self::CACHE_DISABLED));
104
		$this->bRemoveMissing = (!($iOptions & self::KEEP_MISSING));
105 1864 darkviper
		// if no system language is set then use language 'en'
106
		$this->sSystemLanguage = (trim($sSystemLanguage) == '' ? 'en' : $sSystemLanguage);
107
		// if no default language is set then use system language
108
		$this->sDefaultLanguage = (trim($sDefaultLanguage) == ''
109
		                            ? $this->sSystemLanguage
110
		                            : $sDefaultLanguage);
111 1860 darkviper
		// if no user language is set then use default language
112 1864 darkviper
		$this->sUserLanguage = (trim($sUserLanguage) == ''
113 1873 darkviper
		                         ? $this->sDefaultLanguage
114 1864 darkviper
		                         : $sUserLanguage);
115 1873 darkviper
		$sPattern = '/^[a-z]{2,3}(?:(?:[_-][a-z]{2})?(?:[_-][a-z0-9]{2,4})?)$/siU';
116 1860 darkviper
		// validate language codes
117 1864 darkviper
		if(preg_match($sPattern, $this->sSystemLanguage) &&
118
		   preg_match($sPattern, $this->sDefaultLanguage) &&
119
		   preg_match($sPattern, $this->sUserLanguage))
120 1860 darkviper
		{
121
		// load core translations and activate it
122 1864 darkviper
			$oTmp = new TranslationTable('',
123
			                             $this->sSystemLanguage,
124
			                             $this->sDefaultLanguage,
125
			                             $this->sUserLanguage,
126
			                             $this->bUseCache);
127
			$this->aLoadedAddons['core'] = $oTmp->load($this->sAdaptor);
128 2096 darkviper
			$this->aActiveTranslations[self::TABLE_CORE] = $this->aLoadedAddons['core'];
129 1864 darkviper
			if(sizeof($this->aLoadedAddons['core']) == 0) {
130 1860 darkviper
			// throw an exception for missing translations
131
				throw new TranslationException('missing core translations');
132
			}
133
		}else {
134
		// throw an exception for invalid or missing language codes
135 1864 darkviper
			$sMsg = 'Invalid language codes: ['.$this->sSystemLanguage.'] or ['
136
			      . $this->sDefaultLanguage.'] or ['.$this->sUserLanguage.']';
137 1860 darkviper
			throw new TranslationException($sMsg);
138
		}
139
	}
140
/**
141
 * Add new addon
142
 * @param string Addon descriptor (i.e. 'modules\myAddon')
143 1902 darkviper
 * @param string (optional)Adaptor name (default: $this->sAdaptor)
144 1860 darkviper
 * @return bool
145
 */
146 1902 darkviper
	public function addAddon($sAddon, $sAdaptor = null)
147 1860 darkviper
	{
148 1902 darkviper
		if($sAdaptor) {
149
			if(!class_exists('TranslateAdaptor'.$sAdaptor)) {
150
				throw new TranslationException('unable to load adaptor: '.$sAdaptor);
151
			}
152
			$sAdaptor = 'TranslateAdaptor'.$sAdaptor;
153
		}else {
154
			$sAdaptor = $this->sAdaptor;
155
		}
156 1882 darkviper
		$sAddon = str_replace('/', '\\', $sAddon);
157 1864 darkviper
		if(!(strtolower($sAddon) == 'core' || $sAddon == '' || isset($this->aLoadedAddons[$sAddon]))) {
158 1860 darkviper
		// load requested addon translations if needed and possible
159 1864 darkviper
			$oTmp = new TranslationTable($sAddon,
160
			                             $this->sSystemLanguage,
161
			                             $this->sDefaultLanguage,
162
			                             $this->sUserLanguage,
163
			                             $this->bUseCache);
164 1902 darkviper
			$this->aLoadedAddons[$sAddon] = $oTmp->load($sAdaptor);
165 1860 darkviper
		}
166
	}
167
/**
168
 * Activate Addon
169
 * @param string Addon descriptor (i.e. 'modules\myAddon')
170 1902 darkviper
 * @param string (optional)Adaptor name (default: $this->sAdaptor)
171 1860 darkviper
 */
172 1902 darkviper
	public function enableAddon($sAddon, $sAdaptor = null)
173 1860 darkviper
	{
174 1882 darkviper
		$sAddon = str_replace('/', '\\', $sAddon);
175 1860 darkviper
		if(!(strtolower($sAddon) == 'core' || $sAddon == '')) {
176 1864 darkviper
			if(!isset($this->aLoadedAddons[$sAddon])) {
177 1902 darkviper
				$this->addAddon($sAddon, $sAdaptor);
178 1860 darkviper
			}
179 2096 darkviper
			$this->aActiveTranslations[self::TABLE_ADDON] = $this->aLoadedAddons[$sAddon];
180 1860 darkviper
		}
181
182
	}
183 1864 darkviper
/**
184
 * Dissable active addon
185
 */
186 1860 darkviper
	public function disableAddon()
187
	{
188 2096 darkviper
		if(isset($this->aActiveTranslations[self::TABLE_ADDON])) {
189
			unset($this->aActiveTranslations[self::TABLE_ADDON]);
190 1860 darkviper
		}
191
	}
192
/**
193 2096 darkviper
 * Activate additional translation table
194
 * @param string $sAddon    Addon descriptor (i.e. 'modules\myAddon')
195
 * @param int    $iPriority (default: self::FIRST_USER_DEFINED_TABLE)
196
 * @param string $sAdaptor  (optional)Adaptor name (default: $this->sAdaptor)
197
 */
198
	public function enablePrivateTable($sAddon, $iPriority = self::FIRST_USER_DEFINED_TABLE, $sAdaptor = null)
199
	{
200
        switch (($iPriority = intval($iPriority))) :
201
            case self::TABLE_THEME:
202
            case self::TABLE_TEMPLATE:
203
                break;
204
            default:
205
                $iPriority = ($iPriority < self::FIRST_USER_DEFINED_TABLE ? self::FIRST_USER_DEFINED_TABLE : $iPriority);
206
            // search for first free priority position
207
                while (array_key_exists($iPriority, $this->aActiveTranslations)) {
208
                    $iPriority += self::STEP_USER_DEFINED_TABLES;
209
                }
210
                break;
211
        endswitch;
212
    // sanitize Addon descriptor
213
		$sAddon = str_replace('/', '\\', $sAddon);
214
		if(!(strtolower($sAddon) == 'core' || $sAddon == '')) {
215
        // if addon is not already in list then add it now
216
			if(!isset($this->aLoadedAddons[$sAddon])) {
217
				$this->addAddon($sAddon, $sAdaptor);
218
			}
219
            // copy table into activate list
220
			$this->aActiveTranslations[$iPriority] = $this->aLoadedAddons[$sAddon];
221
            // save dependency of addon<->priority
222
            $this->aPrivatePriorities[$sAddon] = $iPriority;
223
            // sort active list ascending by priority
224
            ksort($this->aActiveTranslations);
225
		}
226
227
	}
228
/**
229
 * Remove private table from ActiveTranslations table
230
 * @param string $sAddon    Addon descriptor (i.e. 'modules\myAddon')
231
 */
232
	public function disablePrivateTable($sAddon)
233
    {
234
    // sanitize addon descriptor
235
		$sAddon = str_replace('/', '\\', $sAddon);
236
        if (isset($this->aPrivatePriorities[$sAddon])) {
237
        // get priority for this addon if it's loaded
238
            $iPriority = $this->aPrivatePriorities[$sAddon];
239
        // check if addon is activated
240
            if (isset($this->aActiveTranslations[$iPriority]) && $iPriority >= self::FIRST_USER_DEFINED_TABLE) {
241
            // deactivate addon and remove it from priority list
242
                unset($this->aActiveTranslations[$iPriority]);
243
                unset($this->aPrivatePriorities[$sAddon]);
244
            // sort active translation list for ascending priority
245
                ksort($this->aActiveTranslations);
246
            }
247
        }
248
    }
249
/**
250 1860 darkviper
 * Is key available
251
 * @param string Language key
252
 * @return bool
253
 */
254
	public function __isset($sKey)
255
	{
256 1864 darkviper
		foreach($this->aActiveTranslations as $oAddon) {
257 1860 darkviper
			if(isset($oAddon->$sKey)) {
258
			// is true if at least one translation is found
259
				return true;
260
			}
261
		}
262
		return false;
263
	}
264
/**
265
 * Get translation text
266
 * @param string Language key
267
 * @return string Translation text
268
 */
269
	public function __get($sKey)
270
	{
271 1873 darkviper
		$sRetval = ($this->bRemoveMissing ? '' : '#'.$sKey.'#');
272 1864 darkviper
		foreach($this->aActiveTranslations as $oAddon) {
273 1860 darkviper
			if(isset($oAddon->$sKey)) {
274
			// search the last matching translation (Core -> Addon)
275
				$sRetval = $oAddon->$sKey;
276
			}
277
		}
278
		return $sRetval;
279
	}
280
/**
281 1882 darkviper
 * Protect class from property injections
282
 * @param string name of property
283
 * @param mixed value
284
 * @throws TranslationException
285
 */
286
	public function __set($name, $value) {
287
		throw new TranslationException('tried to set a readonly or nonexisting property ['.$name.']!! ');
288
	}
289
/**
290 1929 darkviper
 * Get translation text with replaced placeholders
291
 * @param type $sKey
292
 * @param type $aArguments
293
 * @return type
294
 */
295
    public function __call($sKey, $aArguments)
296
    {
297
		$sRetval = '';
298
		foreach($this->aActiveTranslations as $oAddon) {
299
			if(isset($oAddon->$sKey)) {
300
			// search the last matching translation (Core -> Addon)
301
				$sRetval = $oAddon->$sKey;
302
			}
303
		}
304
		if(sizeof($aArguments) > 0 && $sRetval != '') {
305
			foreach($aArguments as $iKey=>$sArgument) {
306
				if(is_string($sArgument)) {
307
					$sRetval = preg_replace('/\{'.$iKey.'\}/', $sArgument, $sRetval);
308
				}
309
			}
310
		}
311
		return ($sRetval != '' ? $sRetval : ($this->bRemoveMissing ? '' : '#'.$sKey.'#'));
312
    }
313
314
/**
315 1860 darkviper
 * Return complete table of translations
316
 * @return array
317
 * @deprecated for backward compatibility only. Will be removed shortly
318
 */
319
	public function getLangArray()
320
	{
321 1864 darkviper
		$aSumTranslations = array();
322
		foreach($this->aActiveTranslations as $oTranslationTable) {
323
			if(get_class($oTranslationTable) == 'TranslationTable') {
324
				$aSumTranslations = array_merge($aSumTranslations, $oTranslationTable->getArray());
325
			}
326 1860 darkviper
		}
327 1864 darkviper
		return $aSumTranslations;
328 1860 darkviper
	}
329
330
} // end of class Translate
331
// //////////////////////////////////////////////////////////////////////////////////// //
332
/**
333
 * TranslationException
334
 *
335
 * @category     Core
336
 * @package      Core_Translation
337
 * @author       Werner v.d.Decken <wkl@isteam.de>
338
 * @copyright    Werner v.d.Decken <wkl@isteam.de>
339
 * @license      http://www.gnu.org/licenses/gpl.html   GPL License
340
 * @version      2.9.0
341
 * @revision     $Revision$
342
 * @lastmodified $Date$
343
 * @description  Exceptionhandler for the Translation class and depending classes
344
 */
345
class TranslationException extends AppException {}