* '[LOCALE].xml' )
* );
*
* $b = new ezcTranslationManager( $a );
* ?>
*
*
* The reader capabilities of this class (the implementation of
* ezcTranslationContextRead) can be used it two different ways, where the
* second one is the more elegant approach.
*
* Reader Example 1:
*
* options->format = '[LOCALE].xml';
* $backend->initReader( 'nb-no' );
* $backend->next();
*
* $contexts = array();
* while ( $backend->valid() )
* {
* $contextName = $backend->key();
* $contextData = $backend->current();
* // do something with the data
* $backend->next();
* }
* ?>
*
*
* Reader Example 2:
*
* options->format = '[LOCALE].xml';
* $backend->initReader( 'nb-no' );
*
* $contexts = array();
* foreach ( $backend as $contextName => $contextData )
* {
* // do something with the data
* }
* ?>
*
*
* For a more extensive example see {@link ezcTranslationManager}.
*
* @property ezcTranslationTsBackendOptions $options
* Contains the options for this class.
*
* @package Translation
* @version 1.1.5
* @mainclass
*/
class ezcTranslationTsBackend implements ezcTranslationBackend, ezcTranslationContextRead
{
/**
* The last read context, as read by next() method.
*
* The next() method is a part of the {@link ezcTranslationContextRead}
* interface. The first element is the name, the second an array with
* {@link ezcTranslationData} objects. An example of such an array is:
*
*
* array(
* 'design/admin/class/classlist',
* array(
* new ezcTranslationData( 'Edit', 'Rediger', false, ezcTranslationData::TRANSLATED ),
* new ezcTranslationData( 'Create a copy of the <%class_name> class.', 'Lag en kopi av klassen <%class_name>.', false, ezcTranslationData::TRANSLATED ),
* ),
* );
*
*
* @var array
*/
private $currentContext = null;
/**
* Handle for the XML parser used as part of the ezcTranslationContextRead interface.
*
* @var resource
*/
private $xmlParser = null;
/**
* Container to hold the properties
*
* @var array(string=>mixed)
*/
protected $properties;
/**
* Constructs a new ezcTranslationTsBackend that will use the file specified by $location.
*
* You can specify additional options through the $options parameter. See
* the documentation for the {@link ezcTranslationTsBackend::setOptions()}
* method for supported options.
*
* @throws ezcTranslationNotConfiguredException if $location is not set or is empty.
* @param string $location
* @param array(string=>mixed) $options
*/
function __construct( $location, array $options = array() )
{
if ( !$location || !strlen( $location ) )
{
throw new ezcTranslationNotConfiguredException( $location );
}
$this->properties['options'] = new ezcTranslationTsBackendOptions( $options );
$this->properties['options']->location = $location;
}
/**
* Set new options.
* This method allows you to change the options of the translation backend.
*
* @param ezcTranslationTsBackendOptions $options The options to set.
*
* @throws ezcBaseSettingNotFoundException
* If you tried to set a non-existent option value.
* @throws ezcBaseSettingValueException
* If the value is not valid for the desired option.
* @throws ezcBaseValueException
* If you submit neither an array nor an instance of
* ezcTranslationTsBackendOptions.
*/
public function setOptions( $options )
{
if ( is_array( $options ) )
{
$this->options->merge( $options );
}
else if ( $options instanceof ezcTranslationTsBackendOptions )
{
$this->options = $options;
}
else
{
throw new ezcBaseValueException( "options", $options, "instance of ezcTranslationTsBackendOptions" );
}
}
/**
* Returns the current options.
* Returns the options currently set for this backend.
*
* @return ezcTranslationTsBackendOptions The current options.
*/
public function getOptions()
{
return $this->options;
}
/**
* Returns the filename for the translation file using the locale $locale.
*
* This function uses the location and format options,
* combined with the $locale parameter to form a filename that contains the
* translation belonging to the specified locale.
*
* @param string $locale
* @return string
*/
public function buildTranslationFileName( $locale )
{
$filename = $this->options->location . $this->options->format;
$filename = str_replace( '[LOCALE]', $locale, $filename );
return $filename;
}
/**
* Creates an SimpleXML parser object for the locale $locale..
*
* You can set the class of the returned object through the $returnClass
* parameter. That class should extend the SimpleXMLElement class.
*
* This function checks if the location setting is made, if the file
* with the filename as returned by buildTranslationFileName() exists and
* creates a SimpleXML parser object for this file. If either the setting
* is not made, or the file doesn't exists it throws an exception.
*
* @throws ezcTranslationMissingTranslationFileException if the translation
* could not be opened.
* @param string $locale
* @param string $returnClass The class of the returned XML Parser Object.
* @return object The created parser. The parameter $returnClass
* determines what type of class gets returned. The
* classname that you specify should be inherited from
* SimpleXMLElement.
*/
public function openTranslationFile( $locale, $returnClass = 'SimpleXMLElement' )
{
$filename = $this->buildTranslationFileName( $locale );
if ( !file_exists( $filename ) )
{
throw new ezcTranslationMissingTranslationFileException( $filename );
}
return simplexml_load_file( $filename, $returnClass );
}
/**
* Returns the data from the XML element $message as an
* ezcTranslationData object.
*
* @param SimpleXMLElement $message
* @return ezcTranslationData
*/
private function parseSimpleXMLMessage( SimpleXMLElement $message )
{
$status = ezcTranslationData::TRANSLATED;
if ( $message->translation['type'] == 'unfinished' )
{
$status = ezcTranslationData::UNFINISHED;
}
else if ( $message->translation['type'] == 'obsolete' )
{
return null;
}
$source = trim( (string) $message->source );
$translation = trim( (string) $message->translation );
$comment = trim( (string) $message->comment );
$source = strlen( $source ) ? $source : false;
$translation = strlen( $translation ) ? $translation : false;
$comment = strlen( $comment ) ? $comment : false;
return new ezcTranslationData( $source, $translation, $comment, $status );
}
/**
* Returns a array containing a translation map for the locale $locale
* and the context $context.
*
* This method returns an array containing the translation map for the
* specified $locale and $context. It uses the location and
* format options to locate the file, unless caching is
* enabled.
*
* @throws ezcTranslationContextNotAvailableException if a context is not
* available
* @throws ezcTranslationMissingTranslationFileException if the translation
* file does not exist.
* @throws ezcTranslationNotConfiguredException if the option format
* is not set before this method is called.
* @param string $locale
* @param string $context
* @return array(ezcTranslationData)
*/
public function getContext( $locale, $context )
{
$ts = $this->openTranslationFile( $locale );
$contextElements = array();
foreach ( $ts as $trContext )
{
if ( (string) $trContext->name == $context )
{
foreach ( $trContext as $message )
{
if ( $message->source != '' )
{
$element = $this->parseSimpleXMLMessage( $message );
if ( is_null( $element ) )
{
continue;
}
$contextElements[] = $element;
}
}
return $contextElements;
}
}
throw new ezcTranslationContextNotAvailableException( $context );
}
/**
* Initializes the reader to read from locale $locale.
*
* Opens the translation file.
*
* @throws ezcTranslationNotConfiguredException if the option format
* is not set before this method is called.
*
* @param string $locale
* @return void
*/
public function initReader( $locale )
{
$this->xmlParser = $this->openTranslationFile( $locale, 'SimpleXMLIterator' );
$this->xmlParser->rewind();
}
/**
* Deinitializes the reader
*
* This method should be called after the haveMore() method returns false
* to cleanup resources.
*
* @throws ezcTranslationException when the reader is not initialized with
* initReader().
* @return void
*/
public function deinitReader()
{
$this->xmlParser = null;
}
/**
* Advanced to the next context.
*
* This method parses a little bit more of the XML file to be able to
* return the next context. If no more contexts are available it sets the
* $currentContext member variable to null, so that the valid() method can
* pick this up. If there are more contexts available it reads the context
* from the file and stores it into the $currentContext member variable.
* This method is used for iteration as part of the Iterator interface.
*
* @throws ezcTranslationReaderNotInitializedException when the reader is
* not initialized with initReader().
* @return void
*/
public function next()
{
if ( is_null( $this->xmlParser ) )
{
throw new ezcTranslationReaderNotInitializedException();
}
$valid = $this->xmlParser->valid();
if ( $valid )
{
$newContext = array( trim( $this->xmlParser->getChildren()->name), array() );
foreach ( $this->xmlParser->getChildren()->message as $data )
{
$translationItem = $this->parseSimpleXMLMessage( $data );
if ( !is_null( $translationItem ) )
{
$newContext[1][] = $translationItem;
}
}
$this->currentContext = $newContext;
$this->xmlParser->next();
}
else
{
$this->currentContext = null;
}
}
/**
* Returns whether there is a new context available.
*
* This method checks whether a valid context was read. It checks the
* $currentContext member variable for the status.
* This method is used for iteration as part of the Iterator interface.
*
* @throws ezcTranslationReaderNotInitializedException when the reader is
* not initialized with initReader().
* @return bool
*/
public function valid()
{
return $this->currentContext != null;
}
/**
* Returns the current context
*
* This method returns the latest read context, that the haveMore() method
* put into the $currentContext property. See
* {@link ezcTranslationTsBackend::$currentContext} for the format of this
* array.
* This method is used for iteration as part of the Iterator interface.
*
* @throws ezcTranslationReaderNotInitializedException when the reader is
* not initialized with initReader().
* @return array The current context's translation map
*/
public function currentContext()
{
if ( is_null( $this->xmlParser ) )
{
throw new ezcTranslationReaderNotInitializedException();
}
return $this->currentContext;
}
/**
* Returns the current context's data.
*
* This method returns the latest read context, that the next() method
* put into the $currentContext property. See
* {@link ezcTranslationTsBackend::$currentContext} for the format of this
* array.
* This method is used for iteration as part of the Iterator interface.
*
* @throws ezcTranslationReaderNotInitializedException when the reader is
* not initialized with initReader().
* @return array The current context's translation map
*/
public function current()
{
$context = $this->currentContext();
return $context[1];
}
/**
* Returns the current context's name.
*
* This method returns the latest read context, that the next() method
* put into the $currentContext property. See
* {@link ezcTranslationTsBackend::$currentContext} for the format of this
* array.
* This method is used for iteration as part of the Iterator interface.
*
* @throws ezcTranslationReaderNotInitializedException when the reader is
* not initialized with initReader().
* @return string The current context's name
*/
public function key()
{
$context = $this->currentContext();
return $context[0];
}
/**
* Empty function to satisfy the Iterator interface.
*
* The iterator interface expects this method to rewind to the start of
* the array. As we do not support rewinding actually, the only thing that
* the rewind() implementation does is reading the first element from the
* translation file. There are no side effects either if you just use the
* foreach or while methods. (See class introduction for an example).
* This method is used for iteration as part of the Iterator interface.
*
* @throws ezcTranslationReaderNotInitializedException when the reader is
* not initialized with initReader().
* @return void
*/
public function rewind()
{
$this->next();
}
/**
* Property read access.
*
* @throws ezcBasePropertyNotFoundException
* If the the desired property is not found.
* @param string $propertyName Name of the property.
* @return mixed Value of the property or null.
* @ignore
*/
public function __get( $propertyName )
{
switch ( $propertyName )
{
case 'options':
return $this->properties['options'];
default:
break;
}
throw new ezcBasePropertyNotFoundException( $propertyName );
}
/**
* Property write access.
*
* @throws ezcBaseValueException
* If a the value for the property options is not an instance of
* ezcTranslationTsBackendOptions.
* @throws ezcBasePropertyNotFoundException
* If the the desired property is not found.
* @param string $propertyName Name of the property.
* @param mixed $val The value for the property.
* @ignore
*/
public function __set( $propertyName, $val )
{
switch ( $propertyName )
{
case 'options':
if ( !( $val instanceof ezcTranslationTsBackendOptions ) )
{
throw new ezcBaseValueException( $propertyName, $val, 'instance of ezcTranslationTsBackendOptions' );
}
$this->properties['options'] = $val;
return;
default:
break;
}
throw new ezcBasePropertyNotFoundException( $propertyName );
}
/**
* Property isset access.
*
* @param string $propertyName Name of the property.
* @return bool True is the property is set, otherwise false.
* @ignore
*/
public function __isset( $propertyName )
{
switch ( $propertyName )
{
case 'options':
return true;
}
return false;
}
}
?>