Source for file xml.php
Documentation is available at xml.php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
* Contains the Translation2_Container_xml class
* 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
* @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,v 1.17 2008/02/02 18:05:06 quipo Exp $
* @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
"<!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
* <?xml version="1.0" encoding="iso-8859-1"?>
* <meta> Custom meta data</meta>
* <error_text> Non disponible en français </error_text>
* <encoding> iso-8859-1 </encoding>
* <!-- some more <lang>...</lang> -->
* <tr lang='fr_FR'> Chat </tr>
* <!-- some more <tr>...</tr> -->
* <!-- some more <string>...</string> -->
* <!-- some more <page>...</page> -->
* @category Internationalization
* @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
* Initialize the container
* @param array $options - 'filename': Path to the XML file
* @return boolean|PEAR_Errorobject if something went wrong
$this->_filename = $options['filename'];
unset ($options['filename']);
$this->_setDefaultOptions ();
return $this->_loadFile ();
* Load an XML file into memory, and eventually decode the strings from UTF-8
* @return boolean|PEAR_Error
if (!$fp = @fopen($this->_filename, 'r')) {
return new PEAR_Error (" Can\'t read from the XML source: {$this->_filename}" );
$unserializer = &new XML_Unserializer (array('keyAttribute' => $keyAttr));
if (PEAR::isError($status = $unserializer->unserialize ($this->_filename, true ))) {
$this->_data = $unserializer->getUnserializedData ();
$this->_fixDuplicateEntries ();
// Handle default language settings.
// This allows, for example, to rapidly write the meta data as:
'encoding' => 'iso-8859-1'
foreach ($this->_data['languages'] as $lang_id => $settings) {
$this->_data['languages'][$lang_id] = $defaults;
$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))) {
// 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';
$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') {
strtoupper($data['languages'][$lang]['encoding']);
strtoupper($data['languages'][$lang]['encoding']);
if ($target_encoding != $source_encoding) {
$res = iconv($source_encoding, $target_encoding, $str);
$msg = 'Encoding conversion error ' .
"(source encoding: $source_encoding, " .
"target encoding: $target_encoding, " .
"processed string: \" $str\"" ;
TRANSLATION2_ERROR_ENCODING_CONVERSION,
$data['pages'][$page_id][$str_id][$lang] = $res;
// {{{ _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';
$target_encoding = 'UTF-8';
foreach ($data['languages'] as $lang_id => $lang) {
if ($direction == 'from_xml') {
$target_encoding = strtoupper($lang['encoding']);
$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]);
$msg = 'Encoding conversion error ' .
"(source encoding: $source_encoding, " .
"target encoding: $target_encoding, " .
"processed string: \" $lang[$field]\"" ;
TRANSLATION2_ERROR_ENCODING_CONVERSION,
$data['languages'][$lang_id][$field] = $res;
// {{{ _fixDuplicateEntries()
* Remove duplicate entries from the xml data
function _fixDuplicateEntries()
foreach ($this->_data['pages'] as $pagename => $pagedata) {
foreach ($pagedata as $stringname => $stringvalues) {
$this->_data['pages'][$pagename][$stringname] =
* 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
if (PEAR::isError($this->_data) && ($this->_data->code == XML_UNSERIALIZER_ERROR_NO_UNSERIALIZATION)) {
//empty file... create skeleton
if (is_string($data['languages']) and trim($data['languages']) == '') {
$data['languages'] = array();
$data['pages'] = array();
foreach ($data['pages'] as $pageName => $strings) {
//if (is_string($strings) and trim($strings) == '') {
$data['pages'][$pageName] = array();
foreach ($strings as $stringName => $translations) {
$data['pages'][$pageName][$stringName] = array();
* Wrapper for array_merge()
* @param array $arr1 reference
// {{{ _setDefaultOptions()
* Set some default options
function _setDefaultOptions()
//save changes on shutdown or in real time?
$this->options['save_on_shutdown'] = true;
* Fetch the available langs
foreach ($this->_data['languages'] as $id => $spec) {
* Returns an array of the strings in the selected page
* @param string $pageID page/group ID
* @param string $langID language ID
function getPage($pageID = null, $langID = null)
$langID = $this->_getLangID($langID);
if (PEAR::isError($langID)) {
$pageID = (is_null($pageID)) ? '#NULL' : $pageID;
$pageID = (empty($pageID) && (0 !== $pageID)) ? '#EMPTY' : $pageID;
foreach ($this->_data['pages'][$pageID] as $str_id => $translations) {
$result[$str_id] = isset($translations[$langID])
* Get a single item from the container
* @param string $stringID string ID
* @param string $pageID page/group ID
* @param string $langID language ID
function getOne($stringID, $pageID = null, $langID = null)
$langID = $this->_getLangID($langID);
if (PEAR::isError($langID)) {
$pageID = (is_null($pageID)) ? '#NULL' : $pageID;
return isset($this->_data['pages'][$pageID][$stringID][$langID])
? $this->_data['pages'][$pageID][$stringID][$langID]
* Get the stringID for the given string
* @param string $string string
* @param string $pageID page/group ID
$pageID = (is_null($pageID)) ? '#NULL' : $pageID;
foreach ($this->_data['pages'][$pageID] as $stringID => $translations) {
Documentation generated on Fri, 14 Nov 2008 11:30:38 -0500 by phpDocumentor 1.4.0. PEAR Logo Copyright © PHP Group 2004.
|