* @author Olivier Guilyardi * @copyright 2004-2007 Lorenzo Alberton, Olivier Guilyardi * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause) * @version CVS: $Id$ * @link http://pear.php.net/package/Translation2 */ /** * require Translation2_Container class */ require_once 'Translation2/Container.php'; /** * require XML_Unserializer class */ require_once 'XML/Unserializer.php'; /** * Document Type Definition */ define('TRANSLATION2_DTD', "\n" . "\n" . "\n" . "\n" . "\n" . "\n" . "\n" . "\n" . "\n" . "\n" . "\n" . "\n" . "\n" . "\n" . "\n" ); /** * Storage driver for fetching data from a XML file * * Example file : *
 * 
 * 
 *     
 *         
 *              English 
 *              Custom meta data
 *              Non disponible en français 
 *              iso-8859-1 
 *         
 *         
 *     
 *     
 *         
 *             
 *                  Chat 
 *                 
 *             
 *             
 *         
 *         
 *     
 * 
 * 
* * @category Internationalization * @package Translation2 * @author Lorenzo Alberton * @author Olivier Guilyardi * @copyright 2004-2007 Lorenzo Alberton, Olivier Guilyardi * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause) * @link http://pear.php.net/package/Translation2 */ class Translation2_Container_xml extends Translation2_Container { // {{{ class vars /** * Unserialized XML data * @var object */ var $_data = null; /** * XML file name * @var string */ var $_filename; // }}} // {{{ init /** * Initialize the container * * @param array $options - 'filename': Path to the XML file * * @return boolean|PEAR_Error object if something went wrong */ function init($options) { $this->_filename = $options['filename']; unset($options['filename']); $this->_setDefaultOptions(); $this->_parseOptions($options); return $this->_loadFile(); } // }}} // {{{ _loadFile() /** * Load an XML file into memory, and eventually decode the strings from UTF-8 * * @return boolean|PEAR_Error * @access private */ function _loadFile() { $keyAttr = array ( 'lang' => 'id', 'page' => 'key', 'string' => 'key', 'tr' => 'lang' ); if (!$fp = @fopen($this->_filename, 'r')) { return new PEAR_Error ("Can\'t read from the XML source: {$this->_filename}"); } @flock($fp, LOCK_SH); $unserializer = &new XML_Unserializer (array('keyAttribute' => $keyAttr)); if (PEAR::isError($status = $unserializer->unserialize($this->_filename, true))) { fclose($fp); return $status; } fclose($fp); // unserialize data $this->_data = $unserializer->getUnserializedData(); $this->fixEmptySets($this->_data); $this->_fixDuplicateEntries(); // Handle default language settings. // This allows, for example, to rapidly write the meta data as: // // // $defaults = array( 'name' => '', 'meta' => '', 'error_text' => '', 'encoding' => 'iso-8859-1' ); foreach ($this->_data['languages'] as $lang_id => $settings) { if (empty($settings)) { $this->_data['languages'][$lang_id] = $defaults; } else { $this->_data['languages'][$lang_id] = array_merge($defaults, $this->_data['languages'][$lang_id]); } } // convert lang metadata from UTF-8 if (PEAR::isError($e = $this->_convertLangEncodings('from_xml', $this->_data))) { return $e; } // convert encodings of the translated strings from xml (somehow heavy) return $this->_convertEncodings('from_xml', $this->_data); } // }}} // {{{ _convertEncodings() /** * Convert strings to/from XML unique charset (UTF-8) * * @param string $direction ['from_xml' | 'to_xml'] * @param array &$data Data buffer to operate on * * @return boolean|PEAR_Error */ function _convertEncodings($direction, &$data) { if ($direction == 'from_xml') { $source_encoding = 'UTF-8'; } else { $target_encoding = 'UTF-8'; } foreach ($data['pages'] as $page_id => $page_content) { foreach ($page_content as $str_id => $translations) { foreach ($translations as $lang => $str) { if ($direction == 'from_xml') { $target_encoding = strtoupper($data['languages'][$lang]['encoding']); } else { $source_encoding = strtoupper($data['languages'][$lang]['encoding']); } if ($target_encoding != $source_encoding) { $res = iconv($source_encoding, $target_encoding, $str); if ($res === false) { $msg = 'Encoding conversion error ' . "(source encoding: $source_encoding, ". "target encoding: $target_encoding, ". "processed string: \"$str\""; return $this->raiseError($msg, TRANSLATION2_ERROR_ENCODING_CONVERSION, PEAR_ERROR_RETURN, E_USER_WARNING); } $data['pages'][$page_id][$str_id][$lang] = $res; } } } } return true; } // }}} // {{{ _convertLangEncodings() /** * Convert lang data to/from XML unique charset (UTF-8) * * @param string $direction ['from_xml' | 'to_xml'] * @param array &$data Data buffer to operate on * * @return boolean|PEAR_Error */ function _convertLangEncodings($direction, &$data) { static $fields = array('name', 'meta', 'error_text'); if ($direction == 'from_xml') { $source_encoding = 'UTF-8'; } else { $target_encoding = 'UTF-8'; } foreach ($data['languages'] as $lang_id => $lang) { if ($direction == 'from_xml') { $target_encoding = strtoupper($lang['encoding']); } else { $source_encoding = strtoupper($lang['encoding']); } //foreach (array_keys($lang) as $field) { foreach ($fields as $field) { if ($target_encoding != $source_encoding && !empty($lang[$field])) { $res = iconv($source_encoding, $target_encoding, $lang[$field]); if ($res === false) { $msg = 'Encoding conversion error ' . "(source encoding: $source_encoding, ". "target encoding: $target_encoding, ". "processed string: \"$lang[$field]\""; return $this->raiseError($msg, TRANSLATION2_ERROR_ENCODING_CONVERSION, PEAR_ERROR_RETURN, E_USER_WARNING); } $data['languages'][$lang_id][$field] = $res; } } } return true; } // }}} // {{{ _fixDuplicateEntries() /** * Remove duplicate entries from the xml data * * @return void */ function _fixDuplicateEntries() { foreach ($this->_data['pages'] as $pagename => $pagedata) { foreach ($pagedata as $stringname => $stringvalues) { if (is_array(array_pop($stringvalues))) { $this->_data['pages'][$pagename][$stringname] = call_user_func_array(array($this, '_merge'), $stringvalues); } } } } // }}} // {{{ fixEmptySets() /** * Turn empty strings returned by XML_Unserializer into empty arrays * * Note: this method is public because called statically by the t2xmlchk.php * script. It is not meant to be called by user-space code. * * @param array &$data array of languages/pages * * @return void * @access public * @static */ function fixEmptySets(&$data) { if (PEAR::isError($this->_data) && ($this->_data->code == XML_UNSERIALIZER_ERROR_NO_UNSERIALIZATION)) { //empty file... create skeleton $this->_data = array( 'languages' => array(), 'pages' => array(), ); } if (is_string($data['languages']) and trim($data['languages']) == '') { $data['languages'] = array(); } if (is_string($data['pages']) and trim($data['pages']) == '') { $data['pages'] = array(); } else { foreach ($data['pages'] as $pageName => $strings) { //if (is_string($strings) and trim($strings) == '') { if (is_string($strings)) { $data['pages'][$pageName] = array(); } else { foreach ($strings as $stringName => $translations) { if (is_string($translations) and trim($translations) == '') { $data['pages'][$pageName][$stringName] = array(); } } } } } } // }}} // {{{ _merge() /** * Wrapper for array_merge() * * @param array $arr1 reference * * @return array */ function _merge() { $return = array(); foreach (func_get_args() as $arg) { $return = array_merge($return, $arg); } return $return; } // }}} // {{{ _setDefaultOptions() /** * Set some default options * * @return void * @access private */ function _setDefaultOptions() { //save changes on shutdown or in real time? $this->options['save_on_shutdown'] = true; } // }}} // {{{ fetchLangs() /** * Fetch the available langs * * @return void */ function fetchLangs() { $res = array(); foreach ($this->_data['languages'] as $id => $spec) { $spec['id'] = $id; $res[$id] = $spec; } $this->langs = $res; } // }}} // {{{ getPage() /** * Returns an array of the strings in the selected page * * @param string $pageID page/group ID * @param string $langID language ID * * @return array */ function getPage($pageID = null, $langID = null) { $langID = $this->_getLangID($langID); if (PEAR::isError($langID)) { return $langID; } $pageID = (is_null($pageID)) ? '#NULL' : $pageID; $pageID = (empty($pageID) && (0 !== $pageID)) ? '#EMPTY' : $pageID; $result = array(); foreach ($this->_data['pages'][$pageID] as $str_id => $translations) { $result[$str_id] = isset($translations[$langID]) ? $translations[$langID] : null; } return $result; } // }}} // {{{ getOne() /** * Get a single item from the container * * @param string $stringID string ID * @param string $pageID page/group ID * @param string $langID language ID * * @return string */ function getOne($stringID, $pageID = null, $langID = null) { $langID = $this->_getLangID($langID); if (PEAR::isError($langID)) { return $langID; } $pageID = (is_null($pageID)) ? '#NULL' : $pageID; return isset($this->_data['pages'][$pageID][$stringID][$langID]) ? $this->_data['pages'][$pageID][$stringID][$langID] : null; } // }}} // {{{ getStringID() /** * Get the stringID for the given string * * @param string $stringID string ID * @param string $pageID page/group ID * * @return string */ function getStringID($stringID, $pageID = null) { $pageID = (is_null($pageID)) ? '#NULL' : $pageID; foreach ($this->_data['pages'][$pageID] as $str_id => $translations) { if (array_search($string, $translations) !== false) { return $str_id; } } return ''; } // }}} } ?>