2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
5 * Contains the Translation2 base class
9 * LICENSE: Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
20 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 * @category Internationalization
31 * @package Translation2
32 * @author Lorenzo Alberton <l.alberton@quipo.it>
33 * @copyright 2004-2008 Lorenzo Alberton
34 * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
35 * @version CVS: $Id: Translation2.php 268999 2008-11-14 16:18:50Z quipo $
36 * @link http://pear.php.net/package/Translation2
40 * require PEAR base class
42 require_once 'PEAR.php';
45 * Allows redefinition of the default pageID.
46 * This constant is needed to allow both NULL and EMPTY pageID values
47 * and to have them match
49 if (!defined('TRANSLATION2_DEFAULT_PAGEID')) {
50 define('TRANSLATION2_DEFAULT_PAGEID', 'translation2_default_pageID');
55 define('TRANSLATION2_ERROR', -1);
56 define('TRANSLATION2_ERROR_METHOD_NOT_SUPPORTED', -2);
57 define('TRANSLATION2_ERROR_CANNOT_CONNECT', -3);
58 define('TRANSLATION2_ERROR_CANNOT_FIND_FILE', -4);
59 define('TRANSLATION2_ERROR_DOMAIN_NOT_SET', -5);
60 define('TRANSLATION2_ERROR_INVALID_PATH', -6);
61 define('TRANSLATION2_ERROR_CANNOT_CREATE_DIR', -7);
62 define('TRANSLATION2_ERROR_CANNOT_WRITE_FILE', -8);
63 define('TRANSLATION2_ERROR_UNKNOWN_LANG', -9);
64 define('TRANSLATION2_ERROR_ENCODING_CONVERSION', -10);
65 define('TRANSLATION2_ERROR_UNSUPPORTED', -11);
68 * Translation2 base class
70 * This class provides an easy way to retrieve all the strings
71 * for a multilingual site or application from a data source
72 * (i.e. a db, an xml file or a gettext file).
74 * @category Internationalization
75 * @package Translation2
76 * @author Lorenzo Alberton <l.alberton@quipo.it>
77 * @copyright 2004-2008 Lorenzo Alberton
78 * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
79 * @link http://pear.php.net/package/Translation2
96 var $options = array();
110 var $currentPageID = null;
113 * Array of parameters for the adapter class
117 var $params = array();
125 function __construct()
127 if (func_num_args()) {
128 $msg = '<b>Translation2 error:</b>'
129 .' Don\'t use the constructor - use factory()';
130 trigger_error($msg, E_USER_ERROR);
138 * Return a Translation2 instance already initialized
140 * @param string $driver Type of the storage driver
141 * @param mixed $options Additional options for the storage driver
142 * (example: if you are using DB as the storage
143 * driver, you have to pass the dsn string here)
144 * @param array $params Array of parameters for the adapter class
145 * (i.e. you can set here the mappings between your
146 * table/field names and the ones used by this class)
148 * @return object Translation2 instance or PEAR_Error on failure
151 static function & factory($driver, $options = '', $params = array())
153 $tr = new Translation2;
154 $tr->storage = Translation2::_storageFactory($driver, $options);
155 if (PEAR::isError($tr->storage)) {
158 $tr->_setDefaultOptions();
159 $tr->_parseOptions($params);
160 $tr->storage->_parseOptions($params);
165 // {{{ _storageFactory()
168 * Return a storage driver based on $driver and $options
170 * @param string $driver Type of storage class to return
171 * @param string $options Optional parameters for the storage class
173 * @return object Object Storage object
177 static function & _storageFactory($driver, $options = '')
179 $storage_path = 'Translation2/Container/'.strtolower($driver).'.php';
180 $storage_class = 'Translation2_Container_'.strtolower($driver);
181 include_once $storage_path;
182 $storage = new $storage_class;
183 $err = $storage->init($options);
184 if (PEAR::isError($err)) {
191 // {{{ setContainerOptions()
194 * Set some storage driver options
196 * @param array $options array of options
201 function setContainerOptions($options)
203 $this->storage->_parseOptions($options);
207 // {{{ _setDefaultOptions()
210 * Set some default options
215 function _setDefaultOptions()
217 $this->options['ParameterPrefix'] = '&&';
218 $this->options['ParameterPostfix'] = '&&';
219 $this->options['ParameterAutoFree'] = true;
220 $this->options['prefetch'] = true;
224 // {{{ _parseOptions()
227 * Parse options passed to the base class
229 * @param array $array options
234 function _parseOptions($array)
236 foreach ($array as $key => $value) {
237 if (isset($this->options[$key])) {
238 $this->options[$key] = $value;
244 // {{{ getDecorator()
247 * Return an instance of a decorator
249 * This method is used to get a decorator instance.
250 * A decorator can be seen as a filter, i.e. something that can change
251 * or handle the values of the objects/vars that pass through.
253 * @param string $decorator Name of the decorator
255 * @return object Decorator object reference
257 function & getDecorator($decorator)
259 $decorator_path = 'Translation2/Decorator/'.$decorator.'.php';
260 $decorator_class = 'Translation2_Decorator_'.$decorator;
261 include_once $decorator_path;
262 if (func_num_args() > 1) {
263 $obj = func_get_arg(1);
264 $new_decorator = new $decorator_class($obj);
266 $new_decorator = new $decorator_class($this);
268 return $new_decorator;
275 * Set charset used to read/store the translations
277 * @param string $charset character set (encoding)
279 * @return void|PEAR_Error
281 function setCharset($charset)
283 $res = $this->storage->setCharset($charset);
284 if (PEAR::isError($res)) {
295 * Set the language that shall be used when retrieving strings.
297 * @param string $langID language code (for instance, 'en' or 'it')
299 * @return true|PEAR_Error
301 function setLang($langID)
303 $res = $this->storage->setLang($langID);
304 if (PEAR::isError($res)) {
312 // {{{ setPageID($pageID)
317 * Set the page (aka "group of strings") that shall be used when retrieving strings.
318 * If you set it, you don't have to state it in each get() call.
320 * @param string $pageID ID of the default page
324 function setPageID($pageID = null)
326 $this->currentPageID = $pageID;
336 * Get some extra information about the language (its full name,
337 * the localized error text, ...)
339 * @param string $langID language ID
340 * @param string $format ['name', 'meta', 'error_text', 'array']
342 * @return mixed [string | array], depending on $format
344 function getLang($langID = null, $format = 'name')
346 if (is_null($langID)) {
347 if (!isset($this->lang['id'])) {
348 $msg = 'Translation2::getLang(): unknown language "'.$langID.'".'
349 .' Use Translation2::setLang() to set a default language.';
350 return $this->storage->raiseError($msg, TRANSLATION2_ERROR_UNKNOWN_LANG);
352 $langID = $this->lang['id'];
354 $lang = $this->storage->getLangData($langID);
355 if ($format == 'array') {
357 } elseif (isset($lang[$format])) {
358 return $lang[$format];
359 } elseif (isset($lang['name'])) {
360 return $lang['name'];
362 $msg = 'Translation2::getLang(): unknown language "'.$langID.'".'
363 .' Use Translation2::setLang() to set a default language.';
364 return $this->storage->raiseError($msg, TRANSLATION2_ERROR_UNKNOWN_LANG);
373 * Get some extra information about the languages (their full names,
374 * the localized error text, their codes, ...)
376 * @param string $format ['ids', 'names', 'array']
378 * @return array|PEAR_Error
380 function getLangs($format = 'name')
382 return $this->storage->getLangs($format);
389 * Set parameters for next string
391 * Set the replacement for the parameters in the string(s).
392 * Parameter delimiters are customizable.
394 * @param array $params array of replacement parameters
398 function setParams($params = null)
400 if (empty($params)) {
401 $this->params = array();
402 } elseif (is_array($params)) {
403 $this->params = $params;
405 $this->params = array($params);
411 // {{{ _replaceParams()
414 * Replace parameters in strings
416 * @param mixed $strings strings where the replacements must occur
421 function _replaceParams($strings)
423 if (empty($strings) || is_object($strings) || !count($this->params)) {
426 if (is_array($strings)) {
427 foreach ($strings as $key => $string) {
428 $strings[$key] = $this->_replaceParams($string);
431 if (strpos($strings, $this->options['ParameterPrefix']) !== false) {
432 foreach ($this->params as $name => $value) {
433 $strings = str_replace($this->options['ParameterPrefix']
434 . $name . $this->options['ParameterPostfix'],
438 if ($this->options['ParameterAutoFree']) {
439 $this->params = array();
447 // {{{ replaceEmptyStringsWithKeys()
450 * Replace empty strings with their stringID
452 * @param array $strings array of strings to be replaced if empty
457 function replaceEmptyStringsWithKeys($strings)
459 if (!is_array($strings)) {
462 foreach ($strings as $key => $string) {
463 if (empty($string)) {
464 $strings[$key] = $key;
474 * Get translated string (as-is)
476 * @param string $stringID ID of the string to be translated
477 * @param string $pageID ID of the page/group containing the string
478 * @param string $langID ID of the language
479 * @param string $defaultText Text to display when the string is empty
481 * @return string|PEAR_Error
483 function getRaw($stringID, $pageID = TRANSLATION2_DEFAULT_PAGEID, $langID = null, $defaultText = '')
485 $pageID = ($pageID == TRANSLATION2_DEFAULT_PAGEID ? $this->currentPageID : $pageID);
486 $str = $this->storage->getOne($stringID, $pageID, $langID);
497 * Get translated string
499 * First check if the string is cached, if not => fetch the page
500 * from the container and cache it for later use.
501 * If the string is empty, check the fallback language; if
502 * the latter is empty too, then return the $defaultText.
504 * @param string $stringID ID of the string
505 * @param string $pageID ID of the page/group containing the string
506 * @param string $langID ID of the language
507 * @param string $defaultText Text to display when the string is empty
508 * NB: This parameter is only used in the DefaultText decorator
512 function get($stringID, $pageID = TRANSLATION2_DEFAULT_PAGEID, $langID = null, $defaultText = '')
514 $str = $this->getRaw($stringID, $pageID, $langID);
515 if (PEAR::isError($str)) {
518 return $this->_replaceParams($str);
525 * Get the array of strings in a page
527 * Fetch the page (aka "group of strings) from the container,
528 * without applying any formatting and without replacing the parameters
530 * @param string $pageID ID of the page/group containing the string
531 * @param string $langID ID of the language
535 function getRawPage($pageID = TRANSLATION2_DEFAULT_PAGEID, $langID = null)
537 $pageID = ($pageID == TRANSLATION2_DEFAULT_PAGEID ? $this->currentPageID : $pageID);
538 return $this->storage->getPage($pageID, $langID);
545 * Get an entire group of strings
547 * Same as getRawPage, but resort to fallback language and
548 * replace parameters when needed
550 * @param string $pageID ID of the page/group containing the string
551 * @param string $langID ID of the language
555 function getPage($pageID = TRANSLATION2_DEFAULT_PAGEID, $langID = null)
557 $pageData = $this->getRawPage($pageID, $langID);
558 return $this->_replaceParams($pageData);
565 * Get the stringID for the given string. This method is the reverse of get().
567 * @param string $string This is NOT the stringID, this is a real string.
568 * The method will search for its matching stringID, and then
569 * it will return the associate string in the selected language.
570 * @param string $pageID ID of the page/group containing the string
574 function getStringID($string, $pageID = TRANSLATION2_DEFAULT_PAGEID)
576 $pageID = ($pageID == TRANSLATION2_DEFAULT_PAGEID ? $this->currentPageID : $pageID);
577 return $this->storage->getStringID($string, $pageID);
584 * Clone internal object references
586 * This method is called automatically by PHP5
593 $this->storage = clone($this->storage);