Viewing file: xml.php (15.57 KB) -rw-rw-rw- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?php /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/** * Contains the Translation2_Container_xml class * * PHP versions 4 and 5 * * LICENSE: Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * @category Internationalization * @package Translation2 * @author Lorenzo Alberton <l.alberton@quipo.it> * @author Olivier Guilyardi <olivier@samalyse.com> * @copyright 2004-2008 Lorenzo Alberton, Olivier Guilyardi * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause) * @version CVS: $Id: xml.php 305985 2010-12-05 22:55:33Z clockwerx $ * @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', "<!ELEMENT translation2 (languages,pages)>\n" . "<!ELEMENT languages (lang*)>\n" . "<!ELEMENT lang (name?,meta?,error_text?,encoding?)>\n" . "<!ATTLIST lang id ID #REQUIRED>\n" . "<!ELEMENT name (#PCDATA)>\n" . "<!ELEMENT meta (#PCDATA)>\n" . "<!ELEMENT error_text (#PCDATA)>\n" . "<!ELEMENT encoding (#PCDATA)>\n" . "<!ELEMENT pages (page*)>\n" . "<!ELEMENT page (string*)>\n" . "<!ATTLIST page key CDATA #REQUIRED>\n" . "<!ELEMENT string (tr*)>\n" . "<!ATTLIST string key CDATA #REQUIRED>\n" . "<!ELEMENT tr (#PCDATA)>\n" . "<!ATTLIST tr lang IDREF #REQUIRED>\n" );
/** * Storage driver for fetching data from a XML file * * Example file : * <pre> * <?xml version="1.0" encoding="iso-8859-1"?> * <translation2> * <languages> * <lang id='fr_FR'> * <name> English </name> * <meta> Custom meta data</meta> * <error_text> Non disponible en français </error_text> * <encoding> iso-8859-1 </encoding> * </lang> * <!-- some more <lang>...</lang> --> * </languages> * <pages> * <page key='pets'> * <string key='cat'> * <tr lang='fr_FR'> Chat </tr> * <!-- some more <tr>...</tr> --> * </string> * <!-- some more <string>...</string> --> * </page> * <!-- some more <page>...</page> --> * </pages> * </translation2> * </pre> * * @category Internationalization * @package Translation2 * @author Lorenzo Alberton <l.alberton@quipo.it> * @author Olivier Guilyardi <olivier@samalyse.com> * @copyright 2004-2008 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: // // <lang key="fr"/> // <lang key="en"/>
$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 $string string * @param string $pageID page/group ID * * @return string */ function getStringID($string, $pageID = null) { $pageID = (is_null($pageID)) ? '#NULL' : $pageID; foreach ($this->_data['pages'][$pageID] as $stringID => $translations) { if (array_search($string, $translations) !== false) { return $stringID; } }
return ''; } // }}} } ?>
|