- * @version $Revision: 1.66 $ $Date: ${DATE} ${TIME} $
- * @package System.I18N
- * @since 3.0
- */
-class TGlobalization extends TModule
-{
- /**
- * Default character set is 'UTF-8'.
- * @var string
- */
- private $_defaultCharset = 'UTF-8';
-
- /**
- * Default culture is 'en'.
- * @var string
- */
- private $_defaultCulture = 'en';
-
- /**
- * The current charset.
- * @var string
- */
- private $_charset=null;
-
- /**
- * The current culture.
- * @var string
- */
- private $_culture=null;
-
- /**
- * Translation source parameters.
- * @var TMap
- */
- private $_translation;
-
- /**
- * @var boolean whether we should translate the default culture
- */
- private $_translateDefaultCulture=true;
-
- /**
- * Initialize the Culture and Charset for this application.
- * You should override this method if you want a different way of
- * setting the Culture and/or Charset for your application.
- * If you override this method, call parent::init($xml) first.
- * @param mixed application configuration
- */
- public function init($config)
- {
- if($this->_charset===null)
- $this->_charset=$this->getDefaultCharset();
- if($this->_culture===null)
- $this->_culture=$this->getDefaultCulture();
-
- if($config!==null)
- {
- if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP)
- $translation = isset($config['translate'])?$config['translate']:null;
- else
- {
- $t = $config->getElementByTagName('translation');
- $translation = ($t)?$t->getAttributes():null;
- }
- if($translation)
- $this->setTranslationConfiguration($translation);
- }
- $this->getApplication()->setGlobalization($this);
- }
-
- /**
- * @return string default culture
- */
- public function getTranslateDefaultCulture()
- {
- return $this->_translateDefaultCulture;
- }
-
- /**
- * @param bool default culture, e.g. en_US for American English
- */
- public function setTranslateDefaultCulture($value)
- {
- $this->_translateDefaultCulture = TPropertyValue::ensureBoolean($value);
- }
-
- /**
- * @return string default culture
- */
- public function getDefaultCulture()
- {
- return $this->_defaultCulture;
- }
-
- /**
- * @param string default culture, e.g. en_US for American English
- */
- public function setDefaultCulture($culture)
- {
- $this->_defaultCulture = str_replace('-','_',$culture);
- }
-
- /**
- * @return string default charset set
- */
- public function getDefaultCharset()
- {
- return $this->_defaultCharset;
- }
-
- /**
- * @param string default localization charset, e.g. UTF-8
- */
- public function setDefaultCharset($charset)
- {
- $this->_defaultCharset = $charset;
- }
-
- /**
- * @return string current application culture
- */
- public function getCulture()
- {
- return $this->_culture;
- }
-
- /**
- * @param string culture, e.g. en_US for American English
- */
- public function setCulture($culture)
- {
- $this->_culture = str_replace('-','_',$culture);
- }
-
- /**
- * @return string localization charset
- */
- public function getCharset()
- {
- return $this->_charset;
- }
-
- /**
- * @param string localization charset, e.g. UTF-8
- */
- public function setCharset($charset)
- {
- $this->_charset = $charset;
- }
-
- /**
- * @return TMap translation source configuration.
- */
- public function getTranslationConfiguration()
- {
- return (!$this->_translateDefaultCulture && ($this->getDefaultCulture() == $this->getCulture()))
- ? null
- : $this->_translation;
- }
-
- /**
- * Sets the translation configuration. Example configuration:
- *
- * $config['type'] = 'XLIFF'; //XLIFF, gettext, Database or MySQL (deprecated)
- * $config['source'] = 'Path.to.directory'; // for types XLIFF and gettext
- * $config['source'] = 'connectionId'; // for type Database
- * $config['source'] = 'mysql://user:pw@host/db'; // for type MySQL (deprecated)
- * $config['catalogue'] = 'messages'; //default catalog
- * $config['autosave'] = 'true'; //save untranslated message
- * $config['cache'] = 'true'; //cache translated message
- * $config['marker'] = '@@'; // surround untranslated text with '@@'
- *
- * Throws exception is source is not found.
- * @param TMap|array configuration options
- */
- protected function setTranslationConfiguration($config)
- {
- if($config['type'] == 'XLIFF' || $config['type'] == 'gettext')
- {
- if($config['source'])
- {
- $config['source'] = Prado::getPathOfNamespace($config['source']);
- if(!is_dir($config['source']))
- {
- if(@mkdir($config['source'])===false)
- throw new TConfigurationException('globalization_source_path_failed',
- $config['source']);
- chmod($config['source'], PRADO_CHMOD); //make it deletable
- }
- }
- else
- {
- throw new TConfigurationException("invalid source dir '{$config['source']}'");
- }
- }
- if($config['cache'])
- {
- $config['cache'] = $this->getApplication()->getRunTimePath().'/i18n';
- if(!is_dir($config['cache']))
- {
- if(@mkdir($config['cache'])===false)
- throw new TConfigurationException('globalization_cache_path_failed',
- $config['cache']);
- chmod($config['cache'], PRADO_CHMOD); //make it deletable
- }
- }
- $this->_translation = $config;
- }
-
- /**
- * @return string current translation catalogue.
- */
- public function getTranslationCatalogue()
- {
- return $this->_translation['catalogue'];
- }
-
- /**
- * @param string update the translation catalogue.
- */
- public function setTranslationCatalogue($value)
- {
- $this->_translation['catalogue'] = $value;
- }
-
- /**
- * Gets all the variants of a specific culture. If the parameter
- * $culture is null, the current culture is used.
- * @param string $culture the Culture string
- * @return array variants of the culture.
- */
- public function getCultureVariants($culture=null)
- {
- if($culture===null) $culture = $this->getCulture();
- $variants = explode('_', $culture);
- $result = array();
- for(; count($variants) > 0; array_pop($variants))
- $result[] = implode('_', $variants);
- return $result;
- }
-
- /**
- * Returns a list of possible localized files. Example
- *
- * $files = $app->getLocalizedResource("path/to/Home.page","en_US");
- *
- * will return
- *
- * array
- * 0 => 'path/to/en_US/Home.page'
- * 1 => 'path/to/en/Home.page'
- * 2 => 'path/to/Home.en_US.page'
- * 3 => 'path/to/Home.en.page'
- * 4 => 'path/to/Home.page'
- *
- * Note that you still need to verify the existance of these files.
- * @param string filename
- * @param string culture string, null to use current culture
- * @return array list of possible localized resource files.
- */
- public function getLocalizedResource($file,$culture=null)
- {
- $files = array();
- $variants = $this->getCultureVariants($culture);
- $path = pathinfo($file);
- foreach($variants as $variant)
- $files[] = $path['dirname'].DIRECTORY_SEPARATOR.$variant.DIRECTORY_SEPARATOR.$path['basename'];
- $filename = substr($path['basename'],0,strrpos($path['basename'],'.'));
- foreach($variants as $variant)
- $files[] = $path['dirname'].DIRECTORY_SEPARATOR.$filename.'.'.$variant.'.'.$path['extension'];
- $files[] = $file;
- return $files;
- }
-
-}
-
-?>
+
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.I18N
+ */
+
+
+/**
+ * TGlobalization contains settings for Culture, Charset
+ * and TranslationConfiguration.
+ *
+ * TGlobalization can be subclassed to change how the Culture, Charset
+ * are determined. See TGlobalizationAutoDetect for example of
+ * setting the Culture based on browser settings.
+ *
+ * @author Wei Zhuo
+ * @version $Revision: 1.66 $ $Date: ${DATE} ${TIME} $
+ * @package System.I18N
+ * @since 3.0
+ */
+class TGlobalization extends TModule
+{
+ /**
+ * Default character set is 'UTF-8'.
+ * @var string
+ */
+ private $_defaultCharset = 'UTF-8';
+
+ /**
+ * Default culture is 'en'.
+ * @var string
+ */
+ private $_defaultCulture = 'en';
+
+ /**
+ * The current charset.
+ * @var string
+ */
+ private $_charset=null;
+
+ /**
+ * The current culture.
+ * @var string
+ */
+ private $_culture=null;
+
+ /**
+ * Translation source parameters.
+ * @var TMap
+ */
+ private $_translation;
+
+ /**
+ * @var boolean whether we should translate the default culture
+ */
+ private $_translateDefaultCulture=true;
+
+ /**
+ * Initialize the Culture and Charset for this application.
+ * You should override this method if you want a different way of
+ * setting the Culture and/or Charset for your application.
+ * If you override this method, call parent::init($xml) first.
+ * @param mixed application configuration
+ */
+ public function init($config)
+ {
+ if($this->_charset===null)
+ $this->_charset=$this->getDefaultCharset();
+ if($this->_culture===null)
+ $this->_culture=$this->getDefaultCulture();
+
+ if($config!==null)
+ {
+ if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP)
+ $translation = isset($config['translate'])?$config['translate']:null;
+ else
+ {
+ $t = $config->getElementByTagName('translation');
+ $translation = ($t)?$t->getAttributes():null;
+ }
+ if($translation)
+ $this->setTranslationConfiguration($translation);
+ }
+ $this->getApplication()->setGlobalization($this);
+ }
+
+ /**
+ * @return string default culture
+ */
+ public function getTranslateDefaultCulture()
+ {
+ return $this->_translateDefaultCulture;
+ }
+
+ /**
+ * @param bool default culture, e.g. en_US for American English
+ */
+ public function setTranslateDefaultCulture($value)
+ {
+ $this->_translateDefaultCulture = TPropertyValue::ensureBoolean($value);
+ }
+
+ /**
+ * @return string default culture
+ */
+ public function getDefaultCulture()
+ {
+ return $this->_defaultCulture;
+ }
+
+ /**
+ * @param string default culture, e.g. en_US for American English
+ */
+ public function setDefaultCulture($culture)
+ {
+ $this->_defaultCulture = str_replace('-','_',$culture);
+ }
+
+ /**
+ * @return string default charset set
+ */
+ public function getDefaultCharset()
+ {
+ return $this->_defaultCharset;
+ }
+
+ /**
+ * @param string default localization charset, e.g. UTF-8
+ */
+ public function setDefaultCharset($charset)
+ {
+ $this->_defaultCharset = $charset;
+ }
+
+ /**
+ * @return string current application culture
+ */
+ public function getCulture()
+ {
+ return $this->_culture;
+ }
+
+ /**
+ * @param string culture, e.g. en_US for American English
+ */
+ public function setCulture($culture)
+ {
+ $this->_culture = str_replace('-','_',$culture);
+ }
+
+ /**
+ * @return string localization charset
+ */
+ public function getCharset()
+ {
+ return $this->_charset;
+ }
+
+ /**
+ * @param string localization charset, e.g. UTF-8
+ */
+ public function setCharset($charset)
+ {
+ $this->_charset = $charset;
+ }
+
+ /**
+ * @return TMap translation source configuration.
+ */
+ public function getTranslationConfiguration()
+ {
+ return (!$this->_translateDefaultCulture && ($this->getDefaultCulture() == $this->getCulture()))
+ ? null
+ : $this->_translation;
+ }
+
+ /**
+ * Sets the translation configuration. Example configuration:
+ *
+ * $config['type'] = 'XLIFF'; //XLIFF, gettext, Database or MySQL (deprecated)
+ * $config['source'] = 'Path.to.directory'; // for types XLIFF and gettext
+ * $config['source'] = 'connectionId'; // for type Database
+ * $config['source'] = 'mysql://user:pw@host/db'; // for type MySQL (deprecated)
+ * $config['catalogue'] = 'messages'; //default catalog
+ * $config['autosave'] = 'true'; //save untranslated message
+ * $config['cache'] = 'true'; //cache translated message
+ * $config['marker'] = '@@'; // surround untranslated text with '@@'
+ *
+ * Throws exception is source is not found.
+ * @param TMap|array configuration options
+ */
+ protected function setTranslationConfiguration($config)
+ {
+ if($config['type'] == 'XLIFF' || $config['type'] == 'gettext')
+ {
+ if($config['source'])
+ {
+ $config['source'] = Prado::getPathOfNamespace($config['source']);
+ if(!is_dir($config['source']))
+ {
+ if(@mkdir($config['source'])===false)
+ throw new TConfigurationException('globalization_source_path_failed',
+ $config['source']);
+ chmod($config['source'], PRADO_CHMOD); //make it deletable
+ }
+ }
+ else
+ {
+ throw new TConfigurationException("invalid source dir '{$config['source']}'");
+ }
+ }
+ if($config['cache'])
+ {
+ $config['cache'] = $this->getApplication()->getRunTimePath().'/i18n';
+ if(!is_dir($config['cache']))
+ {
+ if(@mkdir($config['cache'])===false)
+ throw new TConfigurationException('globalization_cache_path_failed',
+ $config['cache']);
+ chmod($config['cache'], PRADO_CHMOD); //make it deletable
+ }
+ }
+ $this->_translation = $config;
+ }
+
+ /**
+ * @return string current translation catalogue.
+ */
+ public function getTranslationCatalogue()
+ {
+ return $this->_translation['catalogue'];
+ }
+
+ /**
+ * @param string update the translation catalogue.
+ */
+ public function setTranslationCatalogue($value)
+ {
+ $this->_translation['catalogue'] = $value;
+ }
+
+ /**
+ * Gets all the variants of a specific culture. If the parameter
+ * $culture is null, the current culture is used.
+ * @param string $culture the Culture string
+ * @return array variants of the culture.
+ */
+ public function getCultureVariants($culture=null)
+ {
+ if($culture===null) $culture = $this->getCulture();
+ $variants = explode('_', $culture);
+ $result = array();
+ for(; count($variants) > 0; array_pop($variants))
+ $result[] = implode('_', $variants);
+ return $result;
+ }
+
+ /**
+ * Returns a list of possible localized files. Example
+ *
+ * $files = $app->getLocalizedResource("path/to/Home.page","en_US");
+ *
+ * will return
+ *
+ * array
+ * 0 => 'path/to/en_US/Home.page'
+ * 1 => 'path/to/en/Home.page'
+ * 2 => 'path/to/Home.en_US.page'
+ * 3 => 'path/to/Home.en.page'
+ * 4 => 'path/to/Home.page'
+ *
+ * Note that you still need to verify the existance of these files.
+ * @param string filename
+ * @param string culture string, null to use current culture
+ * @return array list of possible localized resource files.
+ */
+ public function getLocalizedResource($file,$culture=null)
+ {
+ $files = array();
+ $variants = $this->getCultureVariants($culture);
+ $path = pathinfo($file);
+ foreach($variants as $variant)
+ $files[] = $path['dirname'].DIRECTORY_SEPARATOR.$variant.DIRECTORY_SEPARATOR.$path['basename'];
+ $filename = substr($path['basename'],0,strrpos($path['basename'],'.'));
+ foreach($variants as $variant)
+ $files[] = $path['dirname'].DIRECTORY_SEPARATOR.$filename.'.'.$variant.'.'.$path['extension'];
+ $files[] = $file;
+ return $files;
+ }
+
+}
+
+?>
diff --git a/framework/I18N/TGlobalizationAutoDetect.php b/framework/I18N/TGlobalizationAutoDetect.php
index 36273867..5d8a7677 100644
--- a/framework/I18N/TGlobalizationAutoDetect.php
+++ b/framework/I18N/TGlobalizationAutoDetect.php
@@ -1,49 +1,49 @@
-
- * @link http://www.pradosoft.com/
+
+ * @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Revision: 1.66 $ $Date: ${DATE} ${TIME} $
- * @package System.I18N
- */
-
-/**
- * Import the HTTPNeogtiator
- */
-Prado::using('System.I18N.core.HTTPNegotiator');
-
-/**
- * TGlobalizationAutoDetect class will automatically try to resolve the default
- * culture using the user browser language settings.
- *
- * @author Wei Zhuo
- * @version $Revision: 1.66 $ $Date: ${DATE} ${TIME} $
- * @package System.I18N
- */
-class TGlobalizationAutoDetect extends TGlobalization
-{
- private $_detectedLanguage;
-
- public function init($xml)
- {
- parent::init($xml);
-
- //set the culture according to browser language settings
- $http = new HTTPNegotiator();
- $languages = $http->getLanguages();
- if(count($languages) > 0)
- {
- $this->_detectedLanguage=$languages[0];
- $this->setCulture($languages[0]);
- }
- }
-
- public function getDetectedLanguage()
- {
- return $this->_detectedLanguage;
- }
-}
-
+ * @license http://www.pradosoft.com/license/
+ * @version $Revision: 1.66 $ $Date: ${DATE} ${TIME} $
+ * @package System.I18N
+ */
+
+/**
+ * Import the HTTPNeogtiator
+ */
+Prado::using('System.I18N.core.HTTPNegotiator');
+
+/**
+ * TGlobalizationAutoDetect class will automatically try to resolve the default
+ * culture using the user browser language settings.
+ *
+ * @author Wei Zhuo
+ * @version $Revision: 1.66 $ $Date: ${DATE} ${TIME} $
+ * @package System.I18N
+ */
+class TGlobalizationAutoDetect extends TGlobalization
+{
+ private $_detectedLanguage;
+
+ public function init($xml)
+ {
+ parent::init($xml);
+
+ //set the culture according to browser language settings
+ $http = new HTTPNegotiator();
+ $languages = $http->getLanguages();
+ if(count($languages) > 0)
+ {
+ $this->_detectedLanguage=$languages[0];
+ $this->setCulture($languages[0]);
+ }
+ }
+
+ public function getDetectedLanguage()
+ {
+ return $this->_detectedLanguage;
+ }
+}
+
diff --git a/framework/I18N/TI18NControl.php b/framework/I18N/TI18NControl.php
index 7e8efe0a..79c7d5ed 100644
--- a/framework/I18N/TI18NControl.php
+++ b/framework/I18N/TI18NControl.php
@@ -1,90 +1,90 @@
-
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.I18N
- */
-
-
-/**
- * TI18NControl class.
- *
- * Base class for I18N components, providing Culture and Charset properties.
- * Namespace: System.I18N
- *
- * Properties
- * - Culture, string,
- *
Gets or sets the culture for formatting. If the Culture property
- * is not specified. The culture from the Application/Page is used.
- * - Charset, string,
- *
Gets or sets the charset for both input and output.
- * If the Charset property is not specified. The charset from the
- * Application/Page is used. The default is UTF-8.
- *
- * @author Xiang Wei Zhuo
- * @version v1.0, last update on Sat Dec 11 15:25:11 EST 2004
- * @package System.I18N
- */
-class TI18NControl extends TControl
-{
- /**
- * Gets the charset.
- * It is evaluated in the following order:
- * 1) application charset,
- * 2) the default charset in globalization
- * 3) UTF-8
- * @return string charset
- */
- public function getCharset()
- {
- $app = $this->getApplication()->getGlobalization(false);
-
- //instance charset
- $charset = $this->getViewState('Charset','');
-
- //fall back to globalization charset
- if(empty($charset))
- $charset = ($app===null) ? '' : $app->getCharset();
-
- //fall back to default charset
- if(empty($charset))
- $charset = ($app===null) ? 'UTF-8' : $app->getDefaultCharset();
-
- return $charset;
- }
-
- /**
- * Sets the charset for message output
- * @param string the charset, e.g. UTF-8
- */
- public function setCharset($value)
- {
- $this->setViewState('Charset',$value,'');
- }
-
-
- /**
- * Get the specific culture for this control.
- * @param parameter
- * @return string culture identifier.
- */
- public function getCulture()
- {
- return $this->getViewState('Culture','');
- }
-
- /**
- * Get the custom culture identifier.
- * @param string culture identifier.
- */
- public function setCulture($culture)
- {
- $this->setViewState('Culture',$culture,'');
- }
-}
-
+
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.I18N
+ */
+
+
+/**
+ * TI18NControl class.
+ *
+ * Base class for I18N components, providing Culture and Charset properties.
+ * Namespace: System.I18N
+ *
+ * Properties
+ * - Culture, string,
+ *
Gets or sets the culture for formatting. If the Culture property
+ * is not specified. The culture from the Application/Page is used.
+ * - Charset, string,
+ *
Gets or sets the charset for both input and output.
+ * If the Charset property is not specified. The charset from the
+ * Application/Page is used. The default is UTF-8.
+ *
+ * @author Xiang Wei Zhuo
+ * @version v1.0, last update on Sat Dec 11 15:25:11 EST 2004
+ * @package System.I18N
+ */
+class TI18NControl extends TControl
+{
+ /**
+ * Gets the charset.
+ * It is evaluated in the following order:
+ * 1) application charset,
+ * 2) the default charset in globalization
+ * 3) UTF-8
+ * @return string charset
+ */
+ public function getCharset()
+ {
+ $app = $this->getApplication()->getGlobalization(false);
+
+ //instance charset
+ $charset = $this->getViewState('Charset','');
+
+ //fall back to globalization charset
+ if(empty($charset))
+ $charset = ($app===null) ? '' : $app->getCharset();
+
+ //fall back to default charset
+ if(empty($charset))
+ $charset = ($app===null) ? 'UTF-8' : $app->getDefaultCharset();
+
+ return $charset;
+ }
+
+ /**
+ * Sets the charset for message output
+ * @param string the charset, e.g. UTF-8
+ */
+ public function setCharset($value)
+ {
+ $this->setViewState('Charset',$value,'');
+ }
+
+
+ /**
+ * Get the specific culture for this control.
+ * @param parameter
+ * @return string culture identifier.
+ */
+ public function getCulture()
+ {
+ return $this->getViewState('Culture','');
+ }
+
+ /**
+ * Get the custom culture identifier.
+ * @param string culture identifier.
+ */
+ public function setCulture($culture)
+ {
+ $this->setViewState('Culture',$culture,'');
+ }
+}
+
diff --git a/framework/I18N/TNumberFormat.php b/framework/I18N/TNumberFormat.php
index 30a1e638..1c2502be 100644
--- a/framework/I18N/TNumberFormat.php
+++ b/framework/I18N/TNumberFormat.php
@@ -1,251 +1,251 @@
-
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.I18N
- */
-
-/**
- * Get the NumberFormat class.
- */
-Prado::using('System.I18N.core.NumberFormat');
-
-/**
- * Get the parent control class.
- */
-Prado::using('System.I18N.TI18NControl');
-
-/**
- * To format numbers in locale sensitive manner use
- *
- *
- *
- *
- * Numbers can be formatted as currency, percentage, decimal or scientific
- * numbers by specifying the Type attribute. The known types are
- * "currency", "percentage", "decimal" and "scientific".
- *
- * If someone from US want to see sales figures from a store in
- * Germany (say using the EURO currency), formatted using the german
- * currency, you would need to use the attribute Culture="de_DE" to get
- * the currency right, e.g. 100,00. The decimal and grouping separator is
- * then also from the de_DE locale. This may lead to some confusion because
- * people from US know the "," as thousand separator. Therefore a "Currency"
- * attribute is available, so that the output from the following example
- * results in 100.00.
- *
- *
- *
- *
- * Namespace: System.I18N
- *
- * Properties
- * - Value, number,
- *
Gets or sets the number to format. The tag content is used as Value
- * if the Value property is not specified.
- * - Type, string,
- *
Gets or sets the formatting type. The valid types are
- * 'decimal', 'currency', 'percentage' and 'scientific'.
- * - Currency, string,
- *
Gets or sets the currency symbol for the currency format.
- * The default is 'USD' if the Currency property is not specified.
- * - Pattern, string,
- *
Gets or sets the custom number formatting pattern.
- * - DefaultText, string,
- *
Gets or sets the default text. If Value is not set, DefaultText will be
- * shown instead of the default currency Value/Pattern.
- *
- * @author Xiang Wei Zhuo
- * @version v1.0, last update on Sat Dec 11 17:49:56 EST 2004
- * @package System.I18N
- */
-class TNumberFormat extends TI18NControl implements IDataRenderer
-{
- /**
- * Default NumberFormat, set to the application culture.
- * @var NumberFormat
- */
- protected static $formatter;
-
- /**
- * Get the number formatting pattern.
- * @return string format pattern.
- */
- public function getPattern()
- {
- return $this->getViewState('Pattern','');
- }
-
- /**
- * Set the number format pattern.
- * @param string format pattern.
- */
- public function setPattern($pattern)
- {
- $this->setViewState('Pattern',$pattern,'');
- }
-
- /**
- * Get the numberic value for this control.
- * @return string number
- */
- public function getValue()
- {
- return $this->getViewState('Value','');
- }
-
- /**
- * Set the numberic value for this control.
- * @param string the number value
- */
- public function setValue($value)
- {
- $this->setViewState('Value',$value,'');
- }
-
- /**
- * Get the default text value for this control.
- * @return string default text value
- */
- public function getDefaultText()
- {
- return $this->getViewState('DefaultText','');
- }
-
- /**
- * Set the default text value for this control.
- * @param string default text value
- */
- public function setDefaultText($value)
- {
- $this->setViewState('DefaultText',$value,'');
- }
-
- /**
- * Get the numberic value for this control.
- * This method is required by {@link IDataRenderer}.
- * It is the same as {@link getValue()}.
- * @return string number
- * @see getValue
- * @since 3.1.2
- */
- public function getData()
- {
- return $this->getValue();
- }
-
- /**
- * Set the numberic value for this control.
- * This method is required by {@link IDataRenderer}.
- * It is the same as {@link setValue()}.
- * @param string the number value
- * @see setValue
- * @since 3.1.2
- */
- public function setData($value)
- {
- $this->setValue($value);
- }
-
- /**
- * Get the formatting type for this control.
- * @return string formatting type.
- */
- public function getType()
- {
- return $this->getViewState('Type','d');
- }
-
- /**
- * Set the formatting type for this control.
- * @param string formatting type, either "decimal", "currency","percentage"
- * or "scientific"
- * @throws TPropertyTypeInvalidException
- */
- public function setType($type)
- {
- $type = strtolower($type);
-
- switch($type)
- {
- case 'decimal':
- $this->setViewState('Type','d',''); break;
- case 'currency':
- $this->setViewState('Type','c',''); break;
- case 'percentage':
- $this->setViewState('Type','p',''); break;
- case 'scientific':
- $this->setViewState('Type','e',''); break;
- default:
- throw new TInvalidDataValueException('numberformat_type_invalid',$type);
- }
-
- }
-
- /**
- * @return string 3 letter currency code. Defaults to 'USD'.
- */
- public function getCurrency()
- {
- return $this->getViewState('Currency','USD');
- }
-
- /**
- * Set the 3-letter ISO 4217 code. For example, the code
- * "USD" represents the US Dollar and "EUR" represents the Euro currency.
- * @param string currency code.
- */
- public function setCurrency($currency)
- {
- $this->setViewState('Currency', $currency,'');
- }
-
- /**
- * Formats the localized number, be it currency or decimal, or percentage.
- * If the culture is not specified, the default application
- * culture will be used.
- * @return string formatted number
- */
- protected function getFormattedValue()
- {
- $value = $this->getValue();
- $defaultText = $this->getDefaultText();
- if(empty($value) && !empty($defaultText))
- return $this->getDefaultText();
-
- $app = $this->getApplication()->getGlobalization();
- //initialized the default class wide formatter
- if(self::$formatter===null)
- self::$formatter = new NumberFormat($app->getCulture());
-
- $pattern = strlen($this->getPattern()) > 0
- ? $this->getPattern() : $this->getType();
-
- $culture = $this->getCulture();
- //return the specific cultural formatted number
- if(!empty($culture) && $app->getCulture() != $culture)
- {
- $formatter = new NumberFormat($culture);
- return $formatter->format($this->getValue(),$pattern,
- $this->getCurrency(),
- $this->getCharset());
- }
-
- //return the application wide culture formatted number.
- return self::$formatter->format($this->getValue(),$pattern,
- $this->getCurrency(),
- $this->getCharset());
- }
-
- public function render($writer)
- {
- $writer->write($this->getFormattedValue());
- }
-}
-
-?>
+
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.I18N
+ */
+
+/**
+ * Get the NumberFormat class.
+ */
+Prado::using('System.I18N.core.NumberFormat');
+
+/**
+ * Get the parent control class.
+ */
+Prado::using('System.I18N.TI18NControl');
+
+/**
+ * To format numbers in locale sensitive manner use
+ *
+ *
+ *
+ *
+ * Numbers can be formatted as currency, percentage, decimal or scientific
+ * numbers by specifying the Type attribute. The known types are
+ * "currency", "percentage", "decimal" and "scientific".
+ *
+ * If someone from US want to see sales figures from a store in
+ * Germany (say using the EURO currency), formatted using the german
+ * currency, you would need to use the attribute Culture="de_DE" to get
+ * the currency right, e.g. 100,00. The decimal and grouping separator is
+ * then also from the de_DE locale. This may lead to some confusion because
+ * people from US know the "," as thousand separator. Therefore a "Currency"
+ * attribute is available, so that the output from the following example
+ * results in 100.00.
+ *
+ *
+ *
+ *
+ * Namespace: System.I18N
+ *
+ * Properties
+ * - Value, number,
+ *
Gets or sets the number to format. The tag content is used as Value
+ * if the Value property is not specified.
+ * - Type, string,
+ *
Gets or sets the formatting type. The valid types are
+ * 'decimal', 'currency', 'percentage' and 'scientific'.
+ * - Currency, string,
+ *
Gets or sets the currency symbol for the currency format.
+ * The default is 'USD' if the Currency property is not specified.
+ * - Pattern, string,
+ *
Gets or sets the custom number formatting pattern.
+ * - DefaultText, string,
+ *
Gets or sets the default text. If Value is not set, DefaultText will be
+ * shown instead of the default currency Value/Pattern.
+ *
+ * @author Xiang Wei Zhuo
+ * @version v1.0, last update on Sat Dec 11 17:49:56 EST 2004
+ * @package System.I18N
+ */
+class TNumberFormat extends TI18NControl implements IDataRenderer
+{
+ /**
+ * Default NumberFormat, set to the application culture.
+ * @var NumberFormat
+ */
+ protected static $formatter;
+
+ /**
+ * Get the number formatting pattern.
+ * @return string format pattern.
+ */
+ public function getPattern()
+ {
+ return $this->getViewState('Pattern','');
+ }
+
+ /**
+ * Set the number format pattern.
+ * @param string format pattern.
+ */
+ public function setPattern($pattern)
+ {
+ $this->setViewState('Pattern',$pattern,'');
+ }
+
+ /**
+ * Get the numberic value for this control.
+ * @return string number
+ */
+ public function getValue()
+ {
+ return $this->getViewState('Value','');
+ }
+
+ /**
+ * Set the numberic value for this control.
+ * @param string the number value
+ */
+ public function setValue($value)
+ {
+ $this->setViewState('Value',$value,'');
+ }
+
+ /**
+ * Get the default text value for this control.
+ * @return string default text value
+ */
+ public function getDefaultText()
+ {
+ return $this->getViewState('DefaultText','');
+ }
+
+ /**
+ * Set the default text value for this control.
+ * @param string default text value
+ */
+ public function setDefaultText($value)
+ {
+ $this->setViewState('DefaultText',$value,'');
+ }
+
+ /**
+ * Get the numberic value for this control.
+ * This method is required by {@link IDataRenderer}.
+ * It is the same as {@link getValue()}.
+ * @return string number
+ * @see getValue
+ * @since 3.1.2
+ */
+ public function getData()
+ {
+ return $this->getValue();
+ }
+
+ /**
+ * Set the numberic value for this control.
+ * This method is required by {@link IDataRenderer}.
+ * It is the same as {@link setValue()}.
+ * @param string the number value
+ * @see setValue
+ * @since 3.1.2
+ */
+ public function setData($value)
+ {
+ $this->setValue($value);
+ }
+
+ /**
+ * Get the formatting type for this control.
+ * @return string formatting type.
+ */
+ public function getType()
+ {
+ return $this->getViewState('Type','d');
+ }
+
+ /**
+ * Set the formatting type for this control.
+ * @param string formatting type, either "decimal", "currency","percentage"
+ * or "scientific"
+ * @throws TPropertyTypeInvalidException
+ */
+ public function setType($type)
+ {
+ $type = strtolower($type);
+
+ switch($type)
+ {
+ case 'decimal':
+ $this->setViewState('Type','d',''); break;
+ case 'currency':
+ $this->setViewState('Type','c',''); break;
+ case 'percentage':
+ $this->setViewState('Type','p',''); break;
+ case 'scientific':
+ $this->setViewState('Type','e',''); break;
+ default:
+ throw new TInvalidDataValueException('numberformat_type_invalid',$type);
+ }
+
+ }
+
+ /**
+ * @return string 3 letter currency code. Defaults to 'USD'.
+ */
+ public function getCurrency()
+ {
+ return $this->getViewState('Currency','USD');
+ }
+
+ /**
+ * Set the 3-letter ISO 4217 code. For example, the code
+ * "USD" represents the US Dollar and "EUR" represents the Euro currency.
+ * @param string currency code.
+ */
+ public function setCurrency($currency)
+ {
+ $this->setViewState('Currency', $currency,'');
+ }
+
+ /**
+ * Formats the localized number, be it currency or decimal, or percentage.
+ * If the culture is not specified, the default application
+ * culture will be used.
+ * @return string formatted number
+ */
+ protected function getFormattedValue()
+ {
+ $value = $this->getValue();
+ $defaultText = $this->getDefaultText();
+ if(empty($value) && !empty($defaultText))
+ return $this->getDefaultText();
+
+ $app = $this->getApplication()->getGlobalization();
+ //initialized the default class wide formatter
+ if(self::$formatter===null)
+ self::$formatter = new NumberFormat($app->getCulture());
+
+ $pattern = strlen($this->getPattern()) > 0
+ ? $this->getPattern() : $this->getType();
+
+ $culture = $this->getCulture();
+ //return the specific cultural formatted number
+ if(!empty($culture) && $app->getCulture() != $culture)
+ {
+ $formatter = new NumberFormat($culture);
+ return $formatter->format($this->getValue(),$pattern,
+ $this->getCurrency(),
+ $this->getCharset());
+ }
+
+ //return the application wide culture formatted number.
+ return self::$formatter->format($this->getValue(),$pattern,
+ $this->getCurrency(),
+ $this->getCharset());
+ }
+
+ public function render($writer)
+ {
+ $writer->write($this->getFormattedValue());
+ }
+}
+
+?>
diff --git a/framework/I18N/TTranslate.php b/framework/I18N/TTranslate.php
index 9ecb8a29..2976e8c3 100644
--- a/framework/I18N/TTranslate.php
+++ b/framework/I18N/TTranslate.php
@@ -1,255 +1,255 @@
-
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.I18N
- */
-
-/**
- * Get the parent control class.
- */
-Prado::using('System.I18N.TI18NControl');
-
-/**
- * TTranslate class.
- *
- * This component performs message/string translation. The translation
- * source is set in the TGlobalization handler. The following example
- * demonstrated a simple message translation.
- *
- *
- *
- *
- * Depending on the culture set on the page, the phrase "Goodbye" will
- * be translated.
- *
- * The {@link getParameters Parameters} property can be use to add name values pairs for
- * substitution. Substrings enclosed with "{" and "}" in the translation message are consider as the
- * parameter names during substitution lookup. The following example will substitute the substring
- * "{time}" with the value of the parameter attribute "Parameters.time=<%= time() %>. Note that
- * the value of the parameter named "time" is evaluated.
- *
- * >
- * The unix-time is "{time}".
- *
- *
- *
- * More complex string substitution can be applied using the
- * TTranslateParameter component.
- *
- * Namespace: System.I18N
- *
- * Properties
- * - Text, string,
- *
Gets or sets the string to translate.
- * - Catalogue, string,
- *
Gets or sets the catalogue for message translation. The
- * default catalogue can be set by the @Page directive.
- * - Key, string,
- *
Gets or sets the key used to message look up.
- * - Trim, boolean,
- *
Gets or sets an option to trim the contents.
- * Default is to trim the contents.
- *
- * @author Xiang Wei Zhuo
- * @version v1.0, last update on Fri Dec 24 21:38:49 EST 2004
- * @package System.I18N
- */
-class TTranslate extends TI18NControl
-{
- /**
- * @return string the text to be localized/translated.
- */
- public function getText()
- {
- return $this->getViewState('Text','');
- }
-
- /**
- * Sets the text for localization.
- * @param string the text for translation.
- */
- public function setText($value)
- {
- $this->setViewState('Text',$value,'');
- }
-
- /**
- * Set the key for message lookup.
- * @param string key
- */
- public function setKey($value)
- {
- $this->setViewState('Key',$value,'');
- }
-
- /**
- * Get the key for message lookup.
- * @return string key
- */
- public function getKey()
- {
- return $this->getViewState('Key','');
- }
-
- /**
- * Get the message catalogue.
- * @return string catalogue.
- */
- public function getCatalogue()
- {
- return $this->getViewState('Catalogue','');
- }
-
- /**
- * Set the message catalogue.
- * @param string catalogue.
- */
- public function setCatalogue($value)
- {
- $this->setViewState('Catalogue',$value,'');
- }
-
- /**
- * Set the option to trim the contents.
- * @param boolean trim or not.
- */
- public function setTrim($value)
- {
- $this->setViewState('Trim',TPropertyValue::ensureBoolean($value),true);
- }
-
- /**
- * Trim the content or not.
- * @return boolean trim or not.
- */
- public function getTrim()
- {
- return $this->getViewState('Trim',true);
- }
-
- /**
- * Returns the list of custom parameters.
- * Custom parameters are name-value pairs that may subsititute translation
- * place holders during rendering.
- * @return TAttributeCollection the list of custom parameters
- */
- public function getParameters()
- {
- if($parameters=$this->getViewState('Parameters',null))
- return $parameters;
- else
- {
- $parameters=new TAttributeCollection;
- $parameters->setCaseSensitive(true);
- $this->setViewState('Parameters',$parameters,null);
- return $parameters;
- }
- }
-
- /**
- * @return boolean whether the named parameter exists
- */
- public function hasParameter($name)
- {
- if($parameters=$this->getViewState('Parameters',null))
- return $parameters->contains($name);
- else
- return false;
- }
-
- /**
- * @return string parameter value, null if parameter does not exist
- */
- public function getParameter($name)
- {
- if($parameters=$this->getViewState('Parameters',null))
- return $parameters->itemAt($name);
- else
- return null;
- }
-
- /**
- * @param string parameter name
- * @param string value of the parameter
- */
- public function setParameter($name,$value)
- {
- $this->getParameters()->add($name,$value);
- }
-
- /**
- * Removes the named parameter.
- * @param string the name of the parameter to be removed.
- * @return string parameter value removed, null if parameter does not exist.
- */
- public function removeParameter($name)
- {
- if($parameters=$this->getViewState('Parameters',null))
- return $parameters->remove($name);
- else
- return null;
- }
-
- /**
- * renders the translated string.
- */
- public function render($writer)
- {
- $htmlWriter = Prado::createComponent($this->GetResponse()->getHtmlWriterType(), new TTextWriter());
- $subs = array();
- foreach($this->getParameters() as $key => $value)
- $subs['{'.$key.'}'] = $value;
- foreach($this->getControls() as $control)
- {
- if($control instanceof TTranslateParameter)
- $subs['{'.$control->getKey().'}'] = $control->getParameter();
- elseif($control instanceof TControl)
- $control->render($htmlWriter);
- elseif(is_string($control))
- $htmlWriter->write($control);
- }
-
- $text = $this->getText();
- if(strlen($text)==0)
- $text = $htmlWriter->flush();
- if($this->getTrim())
- $text = trim($text);
-
- $writer->write($this->translateText($text, $subs));
- }
-
- /**
- * Translates the text with subsititution.
- * @param string text for translation
- * @param array list of substitutions
- * @return string translated text
- */
- protected function translateText($text, $subs)
- {
- $app = $this->getApplication()->getGlobalization();
-
- //no translation handler provided
- if(($config = $app->getTranslationConfiguration())===null)
- return strtr($text, $subs);
-
- $catalogue = $this->getCatalogue();
- if(empty($catalogue) && isset($config['catalogue']))
- $catalogue = $config['catalogue'];
- if (empty($catalogue)) $catalogue='messages';
- Translation::init($catalogue);
-
- $key = $this->getKey();
- if(!empty($key)) $text = $key;
-
- //translate it
- return Translation::formatter($catalogue)->format($text,
- $subs, $catalogue, $this->getCharset());
- }
-}
-
+
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.I18N
+ */
+
+/**
+ * Get the parent control class.
+ */
+Prado::using('System.I18N.TI18NControl');
+
+/**
+ * TTranslate class.
+ *
+ * This component performs message/string translation. The translation
+ * source is set in the TGlobalization handler. The following example
+ * demonstrated a simple message translation.
+ *
+ *
+ *
+ *
+ * Depending on the culture set on the page, the phrase "Goodbye" will
+ * be translated.
+ *
+ * The {@link getParameters Parameters} property can be use to add name values pairs for
+ * substitution. Substrings enclosed with "{" and "}" in the translation message are consider as the
+ * parameter names during substitution lookup. The following example will substitute the substring
+ * "{time}" with the value of the parameter attribute "Parameters.time=<%= time() %>. Note that
+ * the value of the parameter named "time" is evaluated.
+ *
+ * >
+ * The unix-time is "{time}".
+ *
+ *
+ *
+ * More complex string substitution can be applied using the
+ * TTranslateParameter component.
+ *
+ * Namespace: System.I18N
+ *
+ * Properties
+ * - Text, string,
+ *
Gets or sets the string to translate.
+ * - Catalogue, string,
+ *
Gets or sets the catalogue for message translation. The
+ * default catalogue can be set by the @Page directive.
+ * - Key, string,
+ *
Gets or sets the key used to message look up.
+ * - Trim, boolean,
+ *
Gets or sets an option to trim the contents.
+ * Default is to trim the contents.
+ *
+ * @author Xiang Wei Zhuo
+ * @version v1.0, last update on Fri Dec 24 21:38:49 EST 2004
+ * @package System.I18N
+ */
+class TTranslate extends TI18NControl
+{
+ /**
+ * @return string the text to be localized/translated.
+ */
+ public function getText()
+ {
+ return $this->getViewState('Text','');
+ }
+
+ /**
+ * Sets the text for localization.
+ * @param string the text for translation.
+ */
+ public function setText($value)
+ {
+ $this->setViewState('Text',$value,'');
+ }
+
+ /**
+ * Set the key for message lookup.
+ * @param string key
+ */
+ public function setKey($value)
+ {
+ $this->setViewState('Key',$value,'');
+ }
+
+ /**
+ * Get the key for message lookup.
+ * @return string key
+ */
+ public function getKey()
+ {
+ return $this->getViewState('Key','');
+ }
+
+ /**
+ * Get the message catalogue.
+ * @return string catalogue.
+ */
+ public function getCatalogue()
+ {
+ return $this->getViewState('Catalogue','');
+ }
+
+ /**
+ * Set the message catalogue.
+ * @param string catalogue.
+ */
+ public function setCatalogue($value)
+ {
+ $this->setViewState('Catalogue',$value,'');
+ }
+
+ /**
+ * Set the option to trim the contents.
+ * @param boolean trim or not.
+ */
+ public function setTrim($value)
+ {
+ $this->setViewState('Trim',TPropertyValue::ensureBoolean($value),true);
+ }
+
+ /**
+ * Trim the content or not.
+ * @return boolean trim or not.
+ */
+ public function getTrim()
+ {
+ return $this->getViewState('Trim',true);
+ }
+
+ /**
+ * Returns the list of custom parameters.
+ * Custom parameters are name-value pairs that may subsititute translation
+ * place holders during rendering.
+ * @return TAttributeCollection the list of custom parameters
+ */
+ public function getParameters()
+ {
+ if($parameters=$this->getViewState('Parameters',null))
+ return $parameters;
+ else
+ {
+ $parameters=new TAttributeCollection;
+ $parameters->setCaseSensitive(true);
+ $this->setViewState('Parameters',$parameters,null);
+ return $parameters;
+ }
+ }
+
+ /**
+ * @return boolean whether the named parameter exists
+ */
+ public function hasParameter($name)
+ {
+ if($parameters=$this->getViewState('Parameters',null))
+ return $parameters->contains($name);
+ else
+ return false;
+ }
+
+ /**
+ * @return string parameter value, null if parameter does not exist
+ */
+ public function getParameter($name)
+ {
+ if($parameters=$this->getViewState('Parameters',null))
+ return $parameters->itemAt($name);
+ else
+ return null;
+ }
+
+ /**
+ * @param string parameter name
+ * @param string value of the parameter
+ */
+ public function setParameter($name,$value)
+ {
+ $this->getParameters()->add($name,$value);
+ }
+
+ /**
+ * Removes the named parameter.
+ * @param string the name of the parameter to be removed.
+ * @return string parameter value removed, null if parameter does not exist.
+ */
+ public function removeParameter($name)
+ {
+ if($parameters=$this->getViewState('Parameters',null))
+ return $parameters->remove($name);
+ else
+ return null;
+ }
+
+ /**
+ * renders the translated string.
+ */
+ public function render($writer)
+ {
+ $htmlWriter = Prado::createComponent($this->GetResponse()->getHtmlWriterType(), new TTextWriter());
+ $subs = array();
+ foreach($this->getParameters() as $key => $value)
+ $subs['{'.$key.'}'] = $value;
+ foreach($this->getControls() as $control)
+ {
+ if($control instanceof TTranslateParameter)
+ $subs['{'.$control->getKey().'}'] = $control->getParameter();
+ elseif($control instanceof TControl)
+ $control->render($htmlWriter);
+ elseif(is_string($control))
+ $htmlWriter->write($control);
+ }
+
+ $text = $this->getText();
+ if(strlen($text)==0)
+ $text = $htmlWriter->flush();
+ if($this->getTrim())
+ $text = trim($text);
+
+ $writer->write($this->translateText($text, $subs));
+ }
+
+ /**
+ * Translates the text with subsititution.
+ * @param string text for translation
+ * @param array list of substitutions
+ * @return string translated text
+ */
+ protected function translateText($text, $subs)
+ {
+ $app = $this->getApplication()->getGlobalization();
+
+ //no translation handler provided
+ if(($config = $app->getTranslationConfiguration())===null)
+ return strtr($text, $subs);
+
+ $catalogue = $this->getCatalogue();
+ if(empty($catalogue) && isset($config['catalogue']))
+ $catalogue = $config['catalogue'];
+ if (empty($catalogue)) $catalogue='messages';
+ Translation::init($catalogue);
+
+ $key = $this->getKey();
+ if(!empty($key)) $text = $key;
+
+ //translate it
+ return Translation::formatter($catalogue)->format($text,
+ $subs, $catalogue, $this->getCharset());
+ }
+}
+
diff --git a/framework/I18N/TTranslateParameter.php b/framework/I18N/TTranslateParameter.php
index c54f6ab1..6ac6617c 100644
--- a/framework/I18N/TTranslateParameter.php
+++ b/framework/I18N/TTranslateParameter.php
@@ -1,119 +1,119 @@
-
- * @link http://www.pradosoft.com/
+
+ * @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.I18N
- */
-
-/**
- * TTranslateParameter component should be used inside the TTranslate component to
- * allow parameter substitution.
- *
- * For example, the strings "{greeting}" and "{name}" will be replace
- * with the values of "Hello" and "World", respectively.
- * The substitution string must be enclose with "{" and "}".
- * The parameters can be further translated by using TTranslate.
- *
- *
- * {greeting} {name}!
- * World
- * Hello
- *
- *
- *
- * Namespace: System.I18N
- *
- * Properties
- * - Key, string, required.
- *
Gets or sets the string in TTranslate to substitute.
- * - Trim, boolean,
- *
Gets or sets an option to trim the contents of the TParam.
- * Default is to trim the contents.
- *
- * @author Xiang Wei Zhuo
- * @version v3.0, last update on Friday, 6 January 2006
- * @package System.I18N
- */
-class TTranslateParameter extends TControl
-{
- /**
- * The substitution key.
- * @var string
- */
- protected $key;
-
- /**
- * To trim or not to trim the contents.
- * @var boolean
- */
- protected $trim = true;
-
-
- /**
- * Get the parameter substitution key.
- * @return string substitution key.
- */
- public function getKey()
- {
- if(empty($this->key))
- throw new TException('The Key property must be specified.');
- return $this->key;
- }
-
- /**
- * Set the parameter substitution key.
- * @param string substitution key.
- */
- public function setKey($value)
- {
- $this->key = $value;
- }
-
- /**
- * Set the option to trim the contents.
- * @param boolean trim or not.
- */
- public function setTrim($value)
- {
- $this->trim = TPropertyValue::ensureBoolean($value);
- }
-
- /**
- * Trim the content or not.
- * @return boolean trim or not.
- */
- public function getTrim()
- {
- return $this->trim;
- }
-
- public function getValue()
- {
- return $this->getViewState('Value', '');
- }
-
- public function setValue($value)
- {
- $this->setViewState('Value', $value, '');
- }
-
- /**
- * @return string parameter contents.
- */
- public function getParameter()
- {
- $value = $this->getValue();
- if(strlen($value) > 0)
- return $value;
- $htmlWriter = Prado::createComponent($this->GetResponse()->getHtmlWriterType(), new TTextWriter());
- $this->renderControl($htmlWriter);
- return $this->getTrim() ?
- trim($htmlWriter->flush()) : $htmlWriter->flush();
- }
-}
-
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.I18N
+ */
+
+/**
+ * TTranslateParameter component should be used inside the TTranslate component to
+ * allow parameter substitution.
+ *
+ * For example, the strings "{greeting}" and "{name}" will be replace
+ * with the values of "Hello" and "World", respectively.
+ * The substitution string must be enclose with "{" and "}".
+ * The parameters can be further translated by using TTranslate.
+ *
+ *
+ * {greeting} {name}!
+ * World
+ * Hello
+ *
+ *
+ *
+ * Namespace: System.I18N
+ *
+ * Properties
+ * - Key, string, required.
+ *
Gets or sets the string in TTranslate to substitute.
+ * - Trim, boolean,
+ *
Gets or sets an option to trim the contents of the TParam.
+ * Default is to trim the contents.
+ *
+ * @author Xiang Wei Zhuo
+ * @version v3.0, last update on Friday, 6 January 2006
+ * @package System.I18N
+ */
+class TTranslateParameter extends TControl
+{
+ /**
+ * The substitution key.
+ * @var string
+ */
+ protected $key;
+
+ /**
+ * To trim or not to trim the contents.
+ * @var boolean
+ */
+ protected $trim = true;
+
+
+ /**
+ * Get the parameter substitution key.
+ * @return string substitution key.
+ */
+ public function getKey()
+ {
+ if(empty($this->key))
+ throw new TException('The Key property must be specified.');
+ return $this->key;
+ }
+
+ /**
+ * Set the parameter substitution key.
+ * @param string substitution key.
+ */
+ public function setKey($value)
+ {
+ $this->key = $value;
+ }
+
+ /**
+ * Set the option to trim the contents.
+ * @param boolean trim or not.
+ */
+ public function setTrim($value)
+ {
+ $this->trim = TPropertyValue::ensureBoolean($value);
+ }
+
+ /**
+ * Trim the content or not.
+ * @return boolean trim or not.
+ */
+ public function getTrim()
+ {
+ return $this->trim;
+ }
+
+ public function getValue()
+ {
+ return $this->getViewState('Value', '');
+ }
+
+ public function setValue($value)
+ {
+ $this->setViewState('Value', $value, '');
+ }
+
+ /**
+ * @return string parameter contents.
+ */
+ public function getParameter()
+ {
+ $value = $this->getValue();
+ if(strlen($value) > 0)
+ return $value;
+ $htmlWriter = Prado::createComponent($this->GetResponse()->getHtmlWriterType(), new TTextWriter());
+ $this->renderControl($htmlWriter);
+ return $this->getTrim() ?
+ trim($htmlWriter->flush()) : $htmlWriter->flush();
+ }
+}
+
diff --git a/framework/I18N/core/ChoiceFormat.php b/framework/I18N/core/ChoiceFormat.php
index b8eb69f0..dd8e48f8 100644
--- a/framework/I18N/core/ChoiceFormat.php
+++ b/framework/I18N/core/ChoiceFormat.php
@@ -1,226 +1,226 @@
-
- * @version $Revision: 1.1 $ $Date: 2005/01/11 07:19:39 $
- * @package System.I18N.core
- */
-
-
-/**
- * ChoiceFormat class.
- *
- * ChoiceFormat converts between ranges of numeric values and string
- * names for those ranges.
- *
- * A ChoiceFormat splits the real number line -Inf to +Inf into two or
- * more contiguous ranges. Each range is mapped to a string.
- * ChoiceFormat is generally used in a MessageFormat for displaying
- * grammatically correct plurals such as "There are 2 files."
- *
- *
- * $string = '[0] are no files |[1] is one file |(1,Inf] are {number} files';
- *
- * $formatter = new MessageFormat(...); //init for a source
- * $translated = $formatter->format($string);
- *
- * $choice = new ChoiceFormat();
- * echo $choice->format($translated, 0); //shows "are no files"
- *
- *
- * The message/string choices are separated by the pipe "|" followed
- * by a set notation of the form
- * # [1,2] -- accepts values between 1 and 2, inclusive.
- * # (1,2) -- accepts values between 1 and 2, excluding 1 and 2.
- * # {1,2,3,4} -- only values defined in the set are accepted.
- * # [-Inf,0) -- accepts value greater or equal to negative infinity
- * and strictly less than 0
- * Any non-empty combinations of the delimiters of square and round brackets
- * are acceptable.
- *
- * Since version 3.1.2 the following set notation is also possible.
- *
- * # {n: n % 10 > 1 && n % 10 < 5} -- matches numbers like 2, 3, 4, 22, 23, 24
- *
- * Where set is defined by the expression after n:. In particular, the expression
- * accepts the following mathematical/logical operators to form a set of logical conditions
- * on the value given by n:
- * # < -- less than.
- * # <= -- less than equals.
- * # > -- greater than.
- * # >= -- greater than equals.
- * # == -- of equal value.
- * # % -- modulo, e.g., 1 % 10 equals 1, 11 % 10 equals 1.
- * # - -- minus, negative.
- * # + -- addition.
- * # & -- conditional AND.
- * # && -- condition AND with short circuit.
- * # | -- conditional OR.
- * # || -- conditional OR with short circuit.
- * # ! -- negation.
- *
- * Additional round brackets can also be used to perform grouping.
- *
- * @author Xiang Wei Zhuo
- * @version v1.0, last update on Fri Dec 24 20:46:16 EST 2004
- * @package System.I18N.core
- */
-class ChoiceFormat
-{
- /**
- * The pattern to validate a set notation
- * @var string
- */
- protected $validate = '/[\(\[\{]|[-Inf\d:\s]+|,|[\+Inf\d\s:\?\-=!><%\|&\(\)]+|[\)\]\}]/ms';
-
- /**
- * The pattern to parse the formatting string.
- * @var string
- */
- protected $parse = '/\s*\|?([\(\[\{]([-Inf\d:\s]+,?[\+Inf\d\s:\?\-=!><%\|&\(\)]*)+[\)\]\}])\s*/';
-
- /**
- * The value for positive infinity.
- * @var float
- */
- protected $inf;
-
-
- /**
- * Constructor.
- */
- function __construct()
- {
- $this->inf = -log(0);
- }
-
-
- /**
- * Determine if the given number belongs to a given set
- * @param float the number to test.
- * @param string the set, in set notation.
- * @return boolean true if number is in the set, false otherwise.
- */
- function isValid($number, $set)
- {
- $n = preg_match_all($this->validate,$set,$matches,PREG_SET_ORDER);
-
- if($n < 3) throw new Exception("Invalid set \"{$set}\"");
-
- if(preg_match('/\{\s*n:([^\}]+)\}/', $set, $def))
- {
- return $this->isValidSetNotation($number, $def[1]);
- }
-
- $leftBracket = $matches[0][0];
- $rightBracket = $matches[$n-1][0];
-
- $i = 0;
- $elements = array();
- foreach($matches as $match)
- {
- $string = $match[0];
- if($i != 0 && $i != $n-1 && $string !== ',')
- {
- if($string == '-Inf')
- $elements[] = -1*$this->inf;
- else if ($string == '+Inf' || $string == 'Inf')
- $elements[] = $this->inf;
- else
- $elements[] = floatval($string);
- }
- $i++;
- }
- $total = count($elements);
- $number = floatval($number);
-
- if($leftBracket == '{' && $rightBracket == '}')
- return in_array($number, $elements);
-
- $left = false;
- if($leftBracket == '[')
- $left = $number >= $elements[0];
- else if ($leftBracket == '(')
- $left = $number > $elements[0];
-
- $right = false;
- if($rightBracket==']')
- $right = $number <= $elements[$total-1];
- else if($rightBracket == ')')
- $right = $number < $elements[$total-1];
-
- if($left && $right) return true;
-
- return false;
- }
-
- protected function isValidSetNotation($number, $set)
- {
- $str = '$result = '.str_replace('n', '$number', $set).';';
- try
- {
- eval($str);
- return $result;
- }
- catch(Exception $e)
- {
- return false;
- }
- }
-
- /**
- * Parse a choice string and get a list of sets and a list of strings
- * corresponding to the sets.
- * @param string the string containing the choices
- * @return array array($sets, $strings)
- */
- function parse($string)
- {
- $n = preg_match_all($this->parse,$string,$matches, PREG_OFFSET_CAPTURE);
- $sets = array();
- foreach($matches[1] as $match)
- $sets[] = $match[0];
- $offset = $matches[0];
- $strings = array();
- for($i = 0; $i < $n; $i++)
- {
- $len = strlen($offset[$i][0]);
- $begin = $i == 0? $len : $offset[$i][1] + $len;
- $end = $i == $n-1 ? strlen($string) : $offset[$i+1][1];
- $strings[] = substr($string, $begin, $end - $begin);
- }
- return array($sets, $strings);
- }
-
- /**
- * For the choice string, and a number, find and return the
- * string that satisfied the set within the choices.
- * @param string the choices string.
- * @param float the number to test.
- * @return string the choosen string.
- */
- public function format($string, $number)
- {
- list($sets, $strings) = $this->parse($string);
- $total = count($sets);
- for($i = 0; $i < $total; $i++)
- {
- if($this->isValid($number, $sets[$i]))
- return $strings[$i];
- }
- return false;
- }
-}
-
-?>
+
+ * @version $Revision: 1.1 $ $Date: 2005/01/11 07:19:39 $
+ * @package System.I18N.core
+ */
+
+
+/**
+ * ChoiceFormat class.
+ *
+ * ChoiceFormat converts between ranges of numeric values and string
+ * names for those ranges.
+ *
+ * A ChoiceFormat splits the real number line -Inf to +Inf into two or
+ * more contiguous ranges. Each range is mapped to a string.
+ * ChoiceFormat is generally used in a MessageFormat for displaying
+ * grammatically correct plurals such as "There are 2 files."
+ *
+ *
+ * $string = '[0] are no files |[1] is one file |(1,Inf] are {number} files';
+ *
+ * $formatter = new MessageFormat(...); //init for a source
+ * $translated = $formatter->format($string);
+ *
+ * $choice = new ChoiceFormat();
+ * echo $choice->format($translated, 0); //shows "are no files"
+ *
+ *
+ * The message/string choices are separated by the pipe "|" followed
+ * by a set notation of the form
+ * # [1,2] -- accepts values between 1 and 2, inclusive.
+ * # (1,2) -- accepts values between 1 and 2, excluding 1 and 2.
+ * # {1,2,3,4} -- only values defined in the set are accepted.
+ * # [-Inf,0) -- accepts value greater or equal to negative infinity
+ * and strictly less than 0
+ * Any non-empty combinations of the delimiters of square and round brackets
+ * are acceptable.
+ *
+ * Since version 3.1.2 the following set notation is also possible.
+ *
+ * # {n: n % 10 > 1 && n % 10 < 5} -- matches numbers like 2, 3, 4, 22, 23, 24
+ *
+ * Where set is defined by the expression after n:. In particular, the expression
+ * accepts the following mathematical/logical operators to form a set of logical conditions
+ * on the value given by n:
+ * # < -- less than.
+ * # <= -- less than equals.
+ * # > -- greater than.
+ * # >= -- greater than equals.
+ * # == -- of equal value.
+ * # % -- modulo, e.g., 1 % 10 equals 1, 11 % 10 equals 1.
+ * # - -- minus, negative.
+ * # + -- addition.
+ * # & -- conditional AND.
+ * # && -- condition AND with short circuit.
+ * # | -- conditional OR.
+ * # || -- conditional OR with short circuit.
+ * # ! -- negation.
+ *
+ * Additional round brackets can also be used to perform grouping.
+ *
+ * @author Xiang Wei Zhuo
+ * @version v1.0, last update on Fri Dec 24 20:46:16 EST 2004
+ * @package System.I18N.core
+ */
+class ChoiceFormat
+{
+ /**
+ * The pattern to validate a set notation
+ * @var string
+ */
+ protected $validate = '/[\(\[\{]|[-Inf\d:\s]+|,|[\+Inf\d\s:\?\-=!><%\|&\(\)]+|[\)\]\}]/ms';
+
+ /**
+ * The pattern to parse the formatting string.
+ * @var string
+ */
+ protected $parse = '/\s*\|?([\(\[\{]([-Inf\d:\s]+,?[\+Inf\d\s:\?\-=!><%\|&\(\)]*)+[\)\]\}])\s*/';
+
+ /**
+ * The value for positive infinity.
+ * @var float
+ */
+ protected $inf;
+
+
+ /**
+ * Constructor.
+ */
+ function __construct()
+ {
+ $this->inf = -log(0);
+ }
+
+
+ /**
+ * Determine if the given number belongs to a given set
+ * @param float the number to test.
+ * @param string the set, in set notation.
+ * @return boolean true if number is in the set, false otherwise.
+ */
+ function isValid($number, $set)
+ {
+ $n = preg_match_all($this->validate,$set,$matches,PREG_SET_ORDER);
+
+ if($n < 3) throw new Exception("Invalid set \"{$set}\"");
+
+ if(preg_match('/\{\s*n:([^\}]+)\}/', $set, $def))
+ {
+ return $this->isValidSetNotation($number, $def[1]);
+ }
+
+ $leftBracket = $matches[0][0];
+ $rightBracket = $matches[$n-1][0];
+
+ $i = 0;
+ $elements = array();
+ foreach($matches as $match)
+ {
+ $string = $match[0];
+ if($i != 0 && $i != $n-1 && $string !== ',')
+ {
+ if($string == '-Inf')
+ $elements[] = -1*$this->inf;
+ else if ($string == '+Inf' || $string == 'Inf')
+ $elements[] = $this->inf;
+ else
+ $elements[] = floatval($string);
+ }
+ $i++;
+ }
+ $total = count($elements);
+ $number = floatval($number);
+
+ if($leftBracket == '{' && $rightBracket == '}')
+ return in_array($number, $elements);
+
+ $left = false;
+ if($leftBracket == '[')
+ $left = $number >= $elements[0];
+ else if ($leftBracket == '(')
+ $left = $number > $elements[0];
+
+ $right = false;
+ if($rightBracket==']')
+ $right = $number <= $elements[$total-1];
+ else if($rightBracket == ')')
+ $right = $number < $elements[$total-1];
+
+ if($left && $right) return true;
+
+ return false;
+ }
+
+ protected function isValidSetNotation($number, $set)
+ {
+ $str = '$result = '.str_replace('n', '$number', $set).';';
+ try
+ {
+ eval($str);
+ return $result;
+ }
+ catch(Exception $e)
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Parse a choice string and get a list of sets and a list of strings
+ * corresponding to the sets.
+ * @param string the string containing the choices
+ * @return array array($sets, $strings)
+ */
+ function parse($string)
+ {
+ $n = preg_match_all($this->parse,$string,$matches, PREG_OFFSET_CAPTURE);
+ $sets = array();
+ foreach($matches[1] as $match)
+ $sets[] = $match[0];
+ $offset = $matches[0];
+ $strings = array();
+ for($i = 0; $i < $n; $i++)
+ {
+ $len = strlen($offset[$i][0]);
+ $begin = $i == 0? $len : $offset[$i][1] + $len;
+ $end = $i == $n-1 ? strlen($string) : $offset[$i+1][1];
+ $strings[] = substr($string, $begin, $end - $begin);
+ }
+ return array($sets, $strings);
+ }
+
+ /**
+ * For the choice string, and a number, find and return the
+ * string that satisfied the set within the choices.
+ * @param string the choices string.
+ * @param float the number to test.
+ * @return string the choosen string.
+ */
+ public function format($string, $number)
+ {
+ list($sets, $strings) = $this->parse($string);
+ $total = count($sets);
+ for($i = 0; $i < $total; $i++)
+ {
+ if($this->isValid($number, $sets[$i]))
+ return $strings[$i];
+ }
+ return false;
+ }
+}
+
+?>
diff --git a/framework/I18N/core/CultureInfo.php b/framework/I18N/core/CultureInfo.php
index 18aae74d..799ccdb4 100644
--- a/framework/I18N/core/CultureInfo.php
+++ b/framework/I18N/core/CultureInfo.php
@@ -1,632 +1,632 @@
-
- * @version $Id$
- * @package System.I18N.core
- */
-
-/**
- * CultureInfo class.
- *
- * Represents information about a specific culture including the
- * names of the culture, the calendar used, as well as access to
- * culture-specific objects that provide methods for common operations,
- * such as formatting dates, numbers, and currency.
- *
- * The CultureInfo class holds culture-specific information, such as the
- * associated language, sublanguage, country/region, calendar, and cultural
- * conventions. This class also provides access to culture-specific
- * instances of DateTimeFormatInfo and NumberFormatInfo. These objects
- * contain the information required for culture-specific operations,
- * such as formatting dates, numbers and currency.
- *
- * The culture names follow the format "_",
- * where is a lowercase two-letter code derived from ISO 639
- * codes. You can find a full list of the ISO-639 codes at
- * http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt
- *
- * The is an uppercase two-letter code derived from
- * ISO 3166. A copy of ISO-3166 can be found at
- * http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html
- *
- * For example, Australian English is "en_AU".
- *
- * @author Xiang Wei Zhuo
- * @version $Id$
- * @package System.I18N.core
- */
-class CultureInfo
-{
- /**
- * ICU data filename extension.
- * @var string
- */
- private $dataFileExt = '.dat';
-
- /**
- * The ICU data array.
- * @var array
- */
- private $data = array();
-
- /**
- * The current culture.
- * @var string
- */
- private $culture;
-
- /**
- * Directory where the ICU data is stored.
- * @var string
- */
- private $dataDir;
-
- /**
- * A list of ICU date files loaded.
- * @var array
- */
- private $dataFiles = array();
-
- /**
- * The current date time format info.
- * @var DateTimeFormatInfo
- */
- private $dateTimeFormat;
-
- /**
- * The current number format info.
- * @var NumberFormatInfo
- */
- private $numberFormat;
-
- /**
- * A list of properties that are accessable/writable.
- * @var array
- */
- protected $properties = array();
-
- /**
- * Culture type, all.
- * @see getCultures()
- * @var int
- */
- const ALL = 0;
-
- /**
- * Culture type, neutral.
- * @see getCultures()
- * @var int
- */
- const NEUTRAL = 1;
-
- /**
- * Culture type, specific.
- * @see getCultures()
- * @var int
- */
- const SPECIFIC = 2;
-
- /**
- * Display the culture name.
- * @return string the culture name.
- * @see getName()
- */
- function __toString()
- {
- return $this->getName();
- }
-
-
- /**
- * Allow functions that begins with 'set' to be called directly
- * as an attribute/property to retrieve the value.
- * @return mixed
- */
- function __get($name)
- {
- $getProperty = 'get'.$name;
- if(in_array($getProperty, $this->properties))
- return $this->$getProperty();
- else
- throw new Exception('Property '.$name.' does not exists.');
- }
-
- /**
- * Allow functions that begins with 'set' to be called directly
- * as an attribute/property to set the value.
- */
- function __set($name, $value)
- {
- $setProperty = 'set'.$name;
- if(in_array($setProperty, $this->properties))
- $this->$setProperty($value);
- else
- throw new Exception('Property '.$name.' can not be set.');
- }
-
-
- /**
- * Initializes a new instance of the CultureInfo class based on the
- * culture specified by name. E.g. new CultureInfo('en_AU');
- * The culture indentifier must be of the form
- * "language_(country/region/variant)".
- * @param string a culture name, e.g. "en_AU".
- * @return return new CultureInfo.
- */
- function __construct($culture='en')
- {
- $this->properties = get_class_methods($this);
-
- if(empty($culture))
- $culture = 'en';
-
- $this->dataDir = $this->dataDir();
- $this->dataFileExt = $this->fileExt();
-
- $this->setCulture($culture);
-
- $this->loadCultureData('root');
- $this->loadCultureData($culture);
- }
-
- /**
- * Get the default directory for the ICU data.
- * The default is the "data" directory for this class.
- * @return string directory containing the ICU data.
- */
- protected static function dataDir()
- {
- return dirname(__FILE__).'/data/';
- }
-
- /**
- * Get the filename extension for ICU data. Default is ".dat".
- * @return string filename extension for ICU data.
- */
- protected static function fileExt()
- {
- return '.dat';
- }
-
- /**
- * Gets the CultureInfo that for this culture string
- * @return CultureInfo invariant culture info is "en".
- */
- public static function getInstance($culture)
- {
- static $instances = array();
- if(!isset($instances[$culture]))
- $instances[$culture] = new CultureInfo($culture);
- return $instances[$culture];
- }
-
- /**
- * Determine if a given culture is valid. Simply checks that the
- * culture data exists.
- * @param string a culture
- * @return boolean true if valid, false otherwise.
- */
- public static function validCulture($culture)
- {
- if(preg_match('/^[_\\w]+$/', $culture))
- return is_file(self::dataDir().$culture.self::fileExt());
-
- return false;
- }
-
- /**
- * Set the culture for the current instance. The culture indentifier
- * must be of the form "_(country/region)".
- * @param string culture identifier, e.g. "fr_FR_EURO".
- */
- protected function setCulture($culture)
- {
- if(!empty($culture))
- {
- if (!preg_match('/^[_\\w]+$/', $culture))
- throw new Exception('Invalid culture supplied: ' . $culture);
- }
-
- $this->culture = $culture;
- }
-
- /**
- * Load the ICU culture data for the specific culture identifier.
- * @param string the culture identifier.
- */
- protected function loadCultureData($culture)
- {
- $file_parts = explode('_',$culture);
- $current_part = $file_parts[0];
-
- $files = array($current_part);
-
- for($i = 1, $k = count($file_parts); $i < $k; ++$i)
- {
- $current_part .= '_'.$file_parts[$i];
- $files[] = $current_part;
- }
-
- foreach($files as $file)
- {
- $filename = $this->dataDir.$file.$this->dataFileExt;
-
- if(is_file($filename) == false)
- throw new Exception('Data file for "'.$file.'" was not found.');
-
- if(in_array($filename, $this->dataFiles) === false)
- {
- array_unshift($this->dataFiles, $file);
-
- $data = &$this->getData($filename);
- $this->data[$file] = &$data;
-
- if(isset($data['__ALIAS']))
- $this->loadCultureData($data['__ALIAS'][0]);
- unset($data);
- }
- }
- }
-
- /**
- * Get the data by unserializing the ICU data from disk.
- * The data files are cached in a static variable inside
- * this function.
- * @param string the ICU data filename
- * @return array ICU data
- */
- protected function &getData($filename)
- {
- static $data = array();
- static $files = array();
-
- if(!in_array($filename, $files))
- {
- $data[$filename] = unserialize(file_get_contents($filename));
- $files[] = $filename;
- }
-
- return $data[$filename];
- }
-
- /**
- * Find the specific ICU data information from the data.
- * The path to the specific ICU data is separated with a slash "/".
- * E.g. To find the default calendar used by the culture, the path
- * "calendar/default" will return the corresponding default calendar.
- * Use merge=true to return the ICU including the parent culture.
- * E.g. The currency data for a variant, say "en_AU" contains one
- * entry, the currency for AUD, the other currency data are stored
- * in the "en" data file. Thus to retrieve all the data regarding
- * currency for "en_AU", you need to use findInfo("Currencies,true);.
- * @param string the data you want to find.
- * @param boolean merge the data from its parents.
- * @return mixed the specific ICU data.
- */
- protected function findInfo($path='/', $merge=false)
- {
- $result = array();
- foreach($this->dataFiles as $section)
- {
- $info = $this->searchArray($this->data[$section], $path);
-
- if($info)
- {
- if($merge)
- $result = array_merge($info,$result);
- else
- return $info;
- }
- }
-
- return $result;
- }
-
- /**
- * Search the array for a specific value using a path separated using
- * slash "/" separated path. e.g to find $info['hello']['world'],
- * the path "hello/world" will return the corresponding value.
- * @param array the array for search
- * @param string slash "/" separated array path.
- * @return mixed the value array using the path
- */
- private function searchArray($info, $path='/')
- {
- $index = explode('/',$path);
-
- $array = $info;
-
- for($i = 0, $k = count($index); $i < $k; ++$i)
- {
- $value = $index[$i];
- if($i < $k-1 && isset($array[$value]))
- $array = $array[$value];
- else if ($i == $k-1 && isset($array[$value]))
- return $array[$value];
- }
- }
-
- /**
- * Gets the culture name in the format
- * "_(country/regioncode2)".
- * @return string culture name.
- */
- function getName()
- {
- return $this->culture;
- }
-
- /**
- * Gets the DateTimeFormatInfo that defines the culturally appropriate
- * format of displaying dates and times.
- * @return DateTimeFormatInfo date time format information for the culture.
- */
- function getDateTimeFormat()
- {
- if($this->dateTimeFormat === null)
- {
- $calendar = $this->getCalendar();
- $info = $this->findInfo("calendar/{$calendar}", true);
- $this->setDateTimeFormat(new DateTimeFormatInfo($info));
- }
-
- return $this->dateTimeFormat;
- }
-
- /**
- * Set the date time format information.
- * @param DateTimeFormatInfo the new date time format info.
- */
- function setDateTimeFormat($dateTimeFormat)
- {
- $this->dateTimeFormat = $dateTimeFormat;
- }
-
- /**
- * Gets the default calendar used by the culture, e.g. "gregorian".
- * @return string the default calendar.
- */
- function getCalendar()
- {
- $info = $this->findInfo('calendar/default');
- return $info[0];
- }
-
- /**
- * Gets the culture name in the language that the culture is set
- * to display. Returns array('Language','Country');
- * 'Country' is omitted if the culture is neutral.
- * @return array array with language and country as elements, localized.
- */
- function getNativeName()
- {
- $lang = substr($this->culture,0,2);
- $reg = substr($this->culture,3,2);
- $language = $this->findInfo("Languages/{$lang}");
- $region = $this->findInfo("Countries/{$reg}");
- if($region)
- return $language[0].' ('.$region[0].')';
- else
- return $language[0];
- }
-
- /**
- * Gets the culture name in English.
- * Returns array('Language','Country');
- * 'Country' is omitted if the culture is neutral.
- * @return string language (country), it may locale code string if english name does not exist.
- */
- function getEnglishName()
- {
- $lang = substr($this->culture,0,2);
- $reg = substr($this->culture,3,2);
- $culture = $this->getInvariantCulture();
-
- $language = $culture->findInfo("Languages/{$lang}");
- if(count($language) == 0)
- return $this->culture;
-
- $region = $culture->findInfo("Countries/{$reg}");
- if($region)
- return $language[0].' ('.$region[0].')';
- else
- return $language[0];
- }
-
- /**
- * Gets the CultureInfo that is culture-independent (invariant).
- * Any changes to the invariant culture affects all other
- * instances of the invariant culture.
- * The invariant culture is assumed to be "en";
- * @return CultureInfo invariant culture info is "en".
- */
- static function getInvariantCulture()
- {
- static $invariant;
- if($invariant === null)
- $invariant = new CultureInfo();
- return $invariant;
- }
-
- /**
- * Gets a value indicating whether the current CultureInfo
- * represents a neutral culture. Returns true if the culture
- * only contains two characters.
- * @return boolean true if culture is neutral, false otherwise.
- */
- function getIsNeutralCulture()
- {
- return strlen($this->culture) == 2;
- }
-
- /**
- * Gets the NumberFormatInfo that defines the culturally appropriate
- * format of displaying numbers, currency, and percentage.
- * @return NumberFormatInfo the number format info for current culture.
- */
- function getNumberFormat()
- {
- if($this->numberFormat === null)
- {
- $elements = $this->findInfo('NumberElements');
- $patterns = $this->findInfo('NumberPatterns');
- $currencies = $this->getCurrencies();
- $data = array( 'NumberElements'=>$elements,
- 'NumberPatterns'=>$patterns,
- 'Currencies' => $currencies);
-
- $this->setNumberFormat(new NumberFormatInfo($data));
- }
- return $this->numberFormat;
- }
-
- /**
- * Set the number format information.
- * @param NumberFormatInfo the new number format info.
- */
- function setNumberFormat($numberFormat)
- {
- $this->numberFormat = $numberFormat;
- }
-
- /**
- * Gets the CultureInfo that represents the parent culture of the
- * current CultureInfo
- * @return CultureInfo parent culture information.
- */
- function getParent()
- {
- if(strlen($this->culture) == 2)
- return $this->getInvariantCulture();
-
- $lang = substr($this->culture,0,2);
- return new CultureInfo($lang);
- }
-
- /**
- * Gets the list of supported cultures filtered by the specified
- * culture type. This is an EXPENSIVE function, it needs to traverse
- * a list of ICU files in the data directory.
- * This function can be called statically.
- * @param int culture type, CultureInfo::ALL, CultureInfo::NEUTRAL
- * or CultureInfo::SPECIFIC.
- * @return array list of culture information available.
- */
- static function getCultures($type=CultureInfo::ALL)
- {
- $dataDir = CultureInfo::dataDir();
- $dataExt = CultureInfo::fileExt();
- $dir = dir($dataDir);
-
- $neutral = array();
- $specific = array();
-
- while (false !== ($entry = $dir->read()))
- {
- if(is_file($dataDir.$entry)
- && substr($entry,-4) == $dataExt
- && $entry != 'root'.$dataExt)
- {
- $culture = substr($entry,0,-4);
- if(strlen($culture) == 2)
- $neutral[] = $culture;
- else
- $specific[] = $culture;
- }
- }
- $dir->close();
-
- switch($type)
- {
- case CultureInfo::ALL :
- $all = array_merge($neutral, $specific);
- sort($all);
- return $all;
- break;
- case CultureInfo::NEUTRAL :
- return $neutral;
- break;
- case CultureInfo::SPECIFIC :
- return $specific;
- break;
- }
- }
-
- /**
- * Simplify a single element array into its own value.
- * E.g. array(0 => array('hello'), 1 => 'world');
- * becomes array(0 => 'hello', 1 => 'world');
- * @param array with single elements arrays
- * @return array simplified array.
- */
- private function simplify($array)
- {
- for($i = 0, $k = count($array); $i<$k; ++$i)
- {
- $key = key($array);
- if(is_array($array[$key])
- && count($array[$key]) == 1)
- $array[$key] = $array[$key][0];
- next($array);
- }
- return $array;
- }
-
- /**
- * Get a list of countries in the language of the localized version.
- * @return array a list of localized country names.
- */
- function getCountries()
- {
- return $this->simplify($this->findInfo('Countries',true));
- }
-
- /**
- * Get a list of currencies in the language of the localized version.
- * @return array a list of localized currencies.
- */
- function getCurrencies()
- {
- return $this->findInfo('Currencies',true);
- }
-
- /**
- * Get a list of languages in the language of the localized version.
- * @return array list of localized language names.
- */
- function getLanguages()
- {
- return $this->simplify($this->findInfo('Languages',true));
- }
-
- /**
- * Get a list of scripts in the language of the localized version.
- * @return array list of localized script names.
- */
- function getScripts()
- {
- return $this->simplify($this->findInfo('Scripts',true));
- }
-
- /**
- * Get a list of timezones in the language of the localized version.
- * @return array list of localized timezones.
- */
- function getTimeZones()
- {
- return $this->simplify($this->findInfo('zoneStrings',true));
- }
-}
-
+
+ * @version $Id$
+ * @package System.I18N.core
+ */
+
+/**
+ * CultureInfo class.
+ *
+ * Represents information about a specific culture including the
+ * names of the culture, the calendar used, as well as access to
+ * culture-specific objects that provide methods for common operations,
+ * such as formatting dates, numbers, and currency.
+ *
+ * The CultureInfo class holds culture-specific information, such as the
+ * associated language, sublanguage, country/region, calendar, and cultural
+ * conventions. This class also provides access to culture-specific
+ * instances of DateTimeFormatInfo and NumberFormatInfo. These objects
+ * contain the information required for culture-specific operations,
+ * such as formatting dates, numbers and currency.
+ *
+ * The culture names follow the format "_",
+ * where is a lowercase two-letter code derived from ISO 639
+ * codes. You can find a full list of the ISO-639 codes at
+ * http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt
+ *
+ * The is an uppercase two-letter code derived from
+ * ISO 3166. A copy of ISO-3166 can be found at
+ * http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html
+ *
+ * For example, Australian English is "en_AU".
+ *
+ * @author Xiang Wei Zhuo
+ * @version $Id$
+ * @package System.I18N.core
+ */
+class CultureInfo
+{
+ /**
+ * ICU data filename extension.
+ * @var string
+ */
+ private $dataFileExt = '.dat';
+
+ /**
+ * The ICU data array.
+ * @var array
+ */
+ private $data = array();
+
+ /**
+ * The current culture.
+ * @var string
+ */
+ private $culture;
+
+ /**
+ * Directory where the ICU data is stored.
+ * @var string
+ */
+ private $dataDir;
+
+ /**
+ * A list of ICU date files loaded.
+ * @var array
+ */
+ private $dataFiles = array();
+
+ /**
+ * The current date time format info.
+ * @var DateTimeFormatInfo
+ */
+ private $dateTimeFormat;
+
+ /**
+ * The current number format info.
+ * @var NumberFormatInfo
+ */
+ private $numberFormat;
+
+ /**
+ * A list of properties that are accessable/writable.
+ * @var array
+ */
+ protected $properties = array();
+
+ /**
+ * Culture type, all.
+ * @see getCultures()
+ * @var int
+ */
+ const ALL = 0;
+
+ /**
+ * Culture type, neutral.
+ * @see getCultures()
+ * @var int
+ */
+ const NEUTRAL = 1;
+
+ /**
+ * Culture type, specific.
+ * @see getCultures()
+ * @var int
+ */
+ const SPECIFIC = 2;
+
+ /**
+ * Display the culture name.
+ * @return string the culture name.
+ * @see getName()
+ */
+ function __toString()
+ {
+ return $this->getName();
+ }
+
+
+ /**
+ * Allow functions that begins with 'set' to be called directly
+ * as an attribute/property to retrieve the value.
+ * @return mixed
+ */
+ function __get($name)
+ {
+ $getProperty = 'get'.$name;
+ if(in_array($getProperty, $this->properties))
+ return $this->$getProperty();
+ else
+ throw new Exception('Property '.$name.' does not exists.');
+ }
+
+ /**
+ * Allow functions that begins with 'set' to be called directly
+ * as an attribute/property to set the value.
+ */
+ function __set($name, $value)
+ {
+ $setProperty = 'set'.$name;
+ if(in_array($setProperty, $this->properties))
+ $this->$setProperty($value);
+ else
+ throw new Exception('Property '.$name.' can not be set.');
+ }
+
+
+ /**
+ * Initializes a new instance of the CultureInfo class based on the
+ * culture specified by name. E.g. new CultureInfo('en_AU');
+ * The culture indentifier must be of the form
+ * "language_(country/region/variant)".
+ * @param string a culture name, e.g. "en_AU".
+ * @return return new CultureInfo.
+ */
+ function __construct($culture='en')
+ {
+ $this->properties = get_class_methods($this);
+
+ if(empty($culture))
+ $culture = 'en';
+
+ $this->dataDir = $this->dataDir();
+ $this->dataFileExt = $this->fileExt();
+
+ $this->setCulture($culture);
+
+ $this->loadCultureData('root');
+ $this->loadCultureData($culture);
+ }
+
+ /**
+ * Get the default directory for the ICU data.
+ * The default is the "data" directory for this class.
+ * @return string directory containing the ICU data.
+ */
+ protected static function dataDir()
+ {
+ return dirname(__FILE__).'/data/';
+ }
+
+ /**
+ * Get the filename extension for ICU data. Default is ".dat".
+ * @return string filename extension for ICU data.
+ */
+ protected static function fileExt()
+ {
+ return '.dat';
+ }
+
+ /**
+ * Gets the CultureInfo that for this culture string
+ * @return CultureInfo invariant culture info is "en".
+ */
+ public static function getInstance($culture)
+ {
+ static $instances = array();
+ if(!isset($instances[$culture]))
+ $instances[$culture] = new CultureInfo($culture);
+ return $instances[$culture];
+ }
+
+ /**
+ * Determine if a given culture is valid. Simply checks that the
+ * culture data exists.
+ * @param string a culture
+ * @return boolean true if valid, false otherwise.
+ */
+ public static function validCulture($culture)
+ {
+ if(preg_match('/^[_\\w]+$/', $culture))
+ return is_file(self::dataDir().$culture.self::fileExt());
+
+ return false;
+ }
+
+ /**
+ * Set the culture for the current instance. The culture indentifier
+ * must be of the form "_(country/region)".
+ * @param string culture identifier, e.g. "fr_FR_EURO".
+ */
+ protected function setCulture($culture)
+ {
+ if(!empty($culture))
+ {
+ if (!preg_match('/^[_\\w]+$/', $culture))
+ throw new Exception('Invalid culture supplied: ' . $culture);
+ }
+
+ $this->culture = $culture;
+ }
+
+ /**
+ * Load the ICU culture data for the specific culture identifier.
+ * @param string the culture identifier.
+ */
+ protected function loadCultureData($culture)
+ {
+ $file_parts = explode('_',$culture);
+ $current_part = $file_parts[0];
+
+ $files = array($current_part);
+
+ for($i = 1, $k = count($file_parts); $i < $k; ++$i)
+ {
+ $current_part .= '_'.$file_parts[$i];
+ $files[] = $current_part;
+ }
+
+ foreach($files as $file)
+ {
+ $filename = $this->dataDir.$file.$this->dataFileExt;
+
+ if(is_file($filename) == false)
+ throw new Exception('Data file for "'.$file.'" was not found.');
+
+ if(in_array($filename, $this->dataFiles) === false)
+ {
+ array_unshift($this->dataFiles, $file);
+
+ $data = &$this->getData($filename);
+ $this->data[$file] = &$data;
+
+ if(isset($data['__ALIAS']))
+ $this->loadCultureData($data['__ALIAS'][0]);
+ unset($data);
+ }
+ }
+ }
+
+ /**
+ * Get the data by unserializing the ICU data from disk.
+ * The data files are cached in a static variable inside
+ * this function.
+ * @param string the ICU data filename
+ * @return array ICU data
+ */
+ protected function &getData($filename)
+ {
+ static $data = array();
+ static $files = array();
+
+ if(!in_array($filename, $files))
+ {
+ $data[$filename] = unserialize(file_get_contents($filename));
+ $files[] = $filename;
+ }
+
+ return $data[$filename];
+ }
+
+ /**
+ * Find the specific ICU data information from the data.
+ * The path to the specific ICU data is separated with a slash "/".
+ * E.g. To find the default calendar used by the culture, the path
+ * "calendar/default" will return the corresponding default calendar.
+ * Use merge=true to return the ICU including the parent culture.
+ * E.g. The currency data for a variant, say "en_AU" contains one
+ * entry, the currency for AUD, the other currency data are stored
+ * in the "en" data file. Thus to retrieve all the data regarding
+ * currency for "en_AU", you need to use findInfo("Currencies,true);.
+ * @param string the data you want to find.
+ * @param boolean merge the data from its parents.
+ * @return mixed the specific ICU data.
+ */
+ protected function findInfo($path='/', $merge=false)
+ {
+ $result = array();
+ foreach($this->dataFiles as $section)
+ {
+ $info = $this->searchArray($this->data[$section], $path);
+
+ if($info)
+ {
+ if($merge)
+ $result = array_merge($info,$result);
+ else
+ return $info;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Search the array for a specific value using a path separated using
+ * slash "/" separated path. e.g to find $info['hello']['world'],
+ * the path "hello/world" will return the corresponding value.
+ * @param array the array for search
+ * @param string slash "/" separated array path.
+ * @return mixed the value array using the path
+ */
+ private function searchArray($info, $path='/')
+ {
+ $index = explode('/',$path);
+
+ $array = $info;
+
+ for($i = 0, $k = count($index); $i < $k; ++$i)
+ {
+ $value = $index[$i];
+ if($i < $k-1 && isset($array[$value]))
+ $array = $array[$value];
+ else if ($i == $k-1 && isset($array[$value]))
+ return $array[$value];
+ }
+ }
+
+ /**
+ * Gets the culture name in the format
+ * "_(country/regioncode2)".
+ * @return string culture name.
+ */
+ function getName()
+ {
+ return $this->culture;
+ }
+
+ /**
+ * Gets the DateTimeFormatInfo that defines the culturally appropriate
+ * format of displaying dates and times.
+ * @return DateTimeFormatInfo date time format information for the culture.
+ */
+ function getDateTimeFormat()
+ {
+ if($this->dateTimeFormat === null)
+ {
+ $calendar = $this->getCalendar();
+ $info = $this->findInfo("calendar/{$calendar}", true);
+ $this->setDateTimeFormat(new DateTimeFormatInfo($info));
+ }
+
+ return $this->dateTimeFormat;
+ }
+
+ /**
+ * Set the date time format information.
+ * @param DateTimeFormatInfo the new date time format info.
+ */
+ function setDateTimeFormat($dateTimeFormat)
+ {
+ $this->dateTimeFormat = $dateTimeFormat;
+ }
+
+ /**
+ * Gets the default calendar used by the culture, e.g. "gregorian".
+ * @return string the default calendar.
+ */
+ function getCalendar()
+ {
+ $info = $this->findInfo('calendar/default');
+ return $info[0];
+ }
+
+ /**
+ * Gets the culture name in the language that the culture is set
+ * to display. Returns array('Language','Country');
+ * 'Country' is omitted if the culture is neutral.
+ * @return array array with language and country as elements, localized.
+ */
+ function getNativeName()
+ {
+ $lang = substr($this->culture,0,2);
+ $reg = substr($this->culture,3,2);
+ $language = $this->findInfo("Languages/{$lang}");
+ $region = $this->findInfo("Countries/{$reg}");
+ if($region)
+ return $language[0].' ('.$region[0].')';
+ else
+ return $language[0];
+ }
+
+ /**
+ * Gets the culture name in English.
+ * Returns array('Language','Country');
+ * 'Country' is omitted if the culture is neutral.
+ * @return string language (country), it may locale code string if english name does not exist.
+ */
+ function getEnglishName()
+ {
+ $lang = substr($this->culture,0,2);
+ $reg = substr($this->culture,3,2);
+ $culture = $this->getInvariantCulture();
+
+ $language = $culture->findInfo("Languages/{$lang}");
+ if(count($language) == 0)
+ return $this->culture;
+
+ $region = $culture->findInfo("Countries/{$reg}");
+ if($region)
+ return $language[0].' ('.$region[0].')';
+ else
+ return $language[0];
+ }
+
+ /**
+ * Gets the CultureInfo that is culture-independent (invariant).
+ * Any changes to the invariant culture affects all other
+ * instances of the invariant culture.
+ * The invariant culture is assumed to be "en";
+ * @return CultureInfo invariant culture info is "en".
+ */
+ static function getInvariantCulture()
+ {
+ static $invariant;
+ if($invariant === null)
+ $invariant = new CultureInfo();
+ return $invariant;
+ }
+
+ /**
+ * Gets a value indicating whether the current CultureInfo
+ * represents a neutral culture. Returns true if the culture
+ * only contains two characters.
+ * @return boolean true if culture is neutral, false otherwise.
+ */
+ function getIsNeutralCulture()
+ {
+ return strlen($this->culture) == 2;
+ }
+
+ /**
+ * Gets the NumberFormatInfo that defines the culturally appropriate
+ * format of displaying numbers, currency, and percentage.
+ * @return NumberFormatInfo the number format info for current culture.
+ */
+ function getNumberFormat()
+ {
+ if($this->numberFormat === null)
+ {
+ $elements = $this->findInfo('NumberElements');
+ $patterns = $this->findInfo('NumberPatterns');
+ $currencies = $this->getCurrencies();
+ $data = array( 'NumberElements'=>$elements,
+ 'NumberPatterns'=>$patterns,
+ 'Currencies' => $currencies);
+
+ $this->setNumberFormat(new NumberFormatInfo($data));
+ }
+ return $this->numberFormat;
+ }
+
+ /**
+ * Set the number format information.
+ * @param NumberFormatInfo the new number format info.
+ */
+ function setNumberFormat($numberFormat)
+ {
+ $this->numberFormat = $numberFormat;
+ }
+
+ /**
+ * Gets the CultureInfo that represents the parent culture of the
+ * current CultureInfo
+ * @return CultureInfo parent culture information.
+ */
+ function getParent()
+ {
+ if(strlen($this->culture) == 2)
+ return $this->getInvariantCulture();
+
+ $lang = substr($this->culture,0,2);
+ return new CultureInfo($lang);
+ }
+
+ /**
+ * Gets the list of supported cultures filtered by the specified
+ * culture type. This is an EXPENSIVE function, it needs to traverse
+ * a list of ICU files in the data directory.
+ * This function can be called statically.
+ * @param int culture type, CultureInfo::ALL, CultureInfo::NEUTRAL
+ * or CultureInfo::SPECIFIC.
+ * @return array list of culture information available.
+ */
+ static function getCultures($type=CultureInfo::ALL)
+ {
+ $dataDir = CultureInfo::dataDir();
+ $dataExt = CultureInfo::fileExt();
+ $dir = dir($dataDir);
+
+ $neutral = array();
+ $specific = array();
+
+ while (false !== ($entry = $dir->read()))
+ {
+ if(is_file($dataDir.$entry)
+ && substr($entry,-4) == $dataExt
+ && $entry != 'root'.$dataExt)
+ {
+ $culture = substr($entry,0,-4);
+ if(strlen($culture) == 2)
+ $neutral[] = $culture;
+ else
+ $specific[] = $culture;
+ }
+ }
+ $dir->close();
+
+ switch($type)
+ {
+ case CultureInfo::ALL :
+ $all = array_merge($neutral, $specific);
+ sort($all);
+ return $all;
+ break;
+ case CultureInfo::NEUTRAL :
+ return $neutral;
+ break;
+ case CultureInfo::SPECIFIC :
+ return $specific;
+ break;
+ }
+ }
+
+ /**
+ * Simplify a single element array into its own value.
+ * E.g. array(0 => array('hello'), 1 => 'world');
+ * becomes array(0 => 'hello', 1 => 'world');
+ * @param array with single elements arrays
+ * @return array simplified array.
+ */
+ private function simplify($array)
+ {
+ for($i = 0, $k = count($array); $i<$k; ++$i)
+ {
+ $key = key($array);
+ if(is_array($array[$key])
+ && count($array[$key]) == 1)
+ $array[$key] = $array[$key][0];
+ next($array);
+ }
+ return $array;
+ }
+
+ /**
+ * Get a list of countries in the language of the localized version.
+ * @return array a list of localized country names.
+ */
+ function getCountries()
+ {
+ return $this->simplify($this->findInfo('Countries',true));
+ }
+
+ /**
+ * Get a list of currencies in the language of the localized version.
+ * @return array a list of localized currencies.
+ */
+ function getCurrencies()
+ {
+ return $this->findInfo('Currencies',true);
+ }
+
+ /**
+ * Get a list of languages in the language of the localized version.
+ * @return array list of localized language names.
+ */
+ function getLanguages()
+ {
+ return $this->simplify($this->findInfo('Languages',true));
+ }
+
+ /**
+ * Get a list of scripts in the language of the localized version.
+ * @return array list of localized script names.
+ */
+ function getScripts()
+ {
+ return $this->simplify($this->findInfo('Scripts',true));
+ }
+
+ /**
+ * Get a list of timezones in the language of the localized version.
+ * @return array list of localized timezones.
+ */
+ function getTimeZones()
+ {
+ return $this->simplify($this->findInfo('zoneStrings',true));
+ }
+}
+
diff --git a/framework/I18N/core/DateFormat.php b/framework/I18N/core/DateFormat.php
index 7724a480..27c9d3c6 100644
--- a/framework/I18N/core/DateFormat.php
+++ b/framework/I18N/core/DateFormat.php
@@ -1,652 +1,652 @@
-
- * @version $Revision: 1.8 $ $Date: 2005/12/15 07:14:49 $
- * @package System.I18N.core
- */
-
-/**
- * Get the DateTimeFormatInfo class.
- */
-require_once(dirname(__FILE__).'/DateTimeFormatInfo.php');
-
-/**
- * Get the encoding utilities
- */
-require_once(dirname(__FILE__).'/util.php');
-
-/**
- * DateFormat class.
- *
- * The DateFormat class allows you to format dates and times with
- * predefined styles in a locale-sensitive manner. Formatting times
- * with the DateFormat class is similar to formatting dates.
- *
- * Formatting dates with the DateFormat class is a two-step process.
- * First, you create a formatter with the getDateInstance method.
- * Second, you invoke the format method, which returns a string containing
- * the formatted date.
- *
- * DateTime values are formatted using standard or custom patterns stored
- * in the properties of a DateTimeFormatInfo.
- *
- * @author Xiang Wei Zhuo
- * @version v1.0, last update on Sat Dec 04 14:10:49 EST 2004
- * @package System.I18N.core
- */
-class DateFormat
-{
- /**
- * A list of tokens and their function call.
- * @var array
- */
- protected $tokens = array(
- 'G'=>'Era',
- 'y'=>'Year',
- 'M'=>'Month',
- 'd'=>'Day',
- 'h'=>'Hour12',
- 'H'=>'Hour24',
- 'm'=>'Minutes',
- 's'=>'Seconds',
- 'E'=>'DayInWeek',
- 'D'=>'DayInYear',
- 'F'=>'DayInMonth',
- 'w'=>'WeekInYear',
- 'W'=>'WeekInMonth',
- 'a'=>'AMPM',
- 'k'=>'HourInDay',
- 'K'=>'HourInAMPM',
- 'z'=>'TimeZone'
- );
-
- /**
- * A list of methods, to be used by the token function calls.
- * @var array
- */
- protected $methods = array();
-
- /**
- * The DateTimeFormatInfo, containing culture specific patterns and names.
- * @var DateTimeFormatInfo
- */
- protected $formatInfo;
-
- /**
- * Initialize a new DateFormat.
- * @param mixed either, null, a CultureInfo instance,
- * a DateTimeFormatInfo instance, or a locale.
- * @return DateFormat instance
- */
- function __construct($formatInfo=null)
- {
- if($formatInfo === null)
- $this->formatInfo = DateTimeFormatInfo::getInvariantInfo();
- else if($formatInfo instanceof CultureInfo)
- $this->formatInfo = $formatInfo->DateTimeFormat;
- else if($formatInfo instanceof DateTimeFormatInfo)
- $this->formatInfo = $formatInfo;
- else
- $this->formatInfo = DateTimeFormatInfo::getInstance($formatInfo);
-
- $this->methods = get_class_methods($this);
- }
-
- /**
- * Format a date according to the pattern.
- * @param mixed the time as integer or string in strtotime format.
- * @return string formatted date time.
- */
- public function format($time, $pattern='F', $charset='UTF-8')
- {
- if (is_numeric($time)) //assumes unix epoch
- $time = floatval($time);
- else if(is_string($time))
- $time = @strtotime($time);
-
- if($pattern === null)
- $pattern = 'F';
-
- $s = Prado::createComponent('System.Util.TDateTimeStamp');
-
- $date = $s->getDate($time);
-
- $pattern = $this->getPattern($pattern);
-
- $tokens = $this->getTokens($pattern);
-
- for($i = 0, $k = count($tokens); $i<$k; ++$i)
- {
- $pattern = $tokens[$i];
- if($pattern{0} == "'"
- && $pattern{strlen($pattern)-1} == "'")
- {
- $sub = preg_replace('/(^\')|(\'$)/','',$pattern);
- $tokens[$i] = str_replace('``````','\'',$sub);
- }
- else if($pattern == '``````')
- {
- $tokens[$i] = '\'';
- }
- else
- {
- $function = $this->getFunctionName($pattern);
- if($function != null)
- {
- $fName = 'get'.$function;
- if(in_array($fName, $this->methods))
- {
- $rs = $this->$fName($date, $pattern);
- $tokens[$i] = $rs;
- }
- else
- throw new
- Exception('function '.$function.' not found.');
- }
- }
- }
-
- return I18N_toEncoding(implode('',$tokens), $charset);
- }
-
- /**
- * For a particular token, get the corresponding function to call.
- * @param string token
- * @return mixed the function if good token, null otherwise.
- */
- protected function getFunctionName($token)
- {
- if(isset($this->tokens[$token{0}]))
- return $this->tokens[$token{0}];
- }
-
- /**
- * Get the pattern from DateTimeFormatInfo or some predefined patterns.
- * If the $pattern parameter is an array of 2 element, it will assume
- * that the first element is the date, and second the time
- * and try to find an appropriate pattern and apply
- * DateTimeFormatInfo::formatDateTime
- * See the tutorial documentation for futher details on the patterns.
- * @param mixed a pattern.
- * @return string a pattern.
- * @see DateTimeFormatInfo::formatDateTime()
- */
- protected function getPattern($pattern)
- {
- if(is_array($pattern) && count($pattern) == 2)
- {
- return $this->formatInfo->formatDateTime(
- $this->getPattern($pattern[0]),
- $this->getPattern($pattern[1]));
- }
-
- switch($pattern)
- {
- case 'd':
- return $this->formatInfo->ShortDatePattern;
- break;
- case 'D':
- return $this->formatInfo->LongDatePattern;
- break;
- case 'p':
- return $this->formatInfo->MediumDatePattern;
- break;
- case 'P':
- return $this->formatInfo->FullDatePattern;
- break;
- case 't':
- return $this->formatInfo->ShortTimePattern;
- break;
- case 'T':
- return $this->formatInfo->LongTimePattern;
- break;
- case 'q':
- return $this->formatInfo->MediumTimePattern;
- break;
- case 'Q':
- return $this->formatInfo->FullTimePattern;
- break;
- case 'f':
- return $this->formatInfo->formatDateTime(
- $this->formatInfo->LongDatePattern,
- $this->formatInfo->ShortTimePattern);
- break;
- case 'F':
- return $this->formatInfo->formatDateTime(
- $this->formatInfo->LongDatePattern,
- $this->formatInfo->LongTimePattern);
- break;
- case 'g':
- return $this->formatInfo->formatDateTime(
- $this->formatInfo->ShortDatePattern,
- $this->formatInfo->ShortTimePattern);
- break;
- case 'G':
- return $this->formatInfo->formatDateTime(
- $this->formatInfo->ShortDatePattern,
- $this->formatInfo->LongTimePattern);
- break;
- case 'M':
- case 'm':
- return 'MMMM dd';
- break;
- case 'R':
- case 'r':
- return 'EEE, dd MMM yyyy HH:mm:ss';
- break;
- case 's':
- return 'yyyy-MM-ddTHH:mm:ss';
- break;
- case 'u':
- return 'yyyy-MM-dd HH:mm:ss z';
- break;
- case 'U':
- return 'EEEE dd MMMM yyyy HH:mm:ss';
- break;
- case 'Y':
- case 'y':
- return 'yyyy MMMM';
- break;
- default :
- return $pattern;
- }
- }
-
- /**
- * Tokenize the pattern. The tokens are delimited by group of
- * similar characters, e.g. 'aabb' will form 2 tokens of 'aa' and 'bb'.
- * Any substrings, starting and ending with a single quote (')
- * will be treated as a single token.
- * @param string pattern.
- * @return array string tokens in an array.
- */
- protected function getTokens($pattern)
- {
- $char = null;
- $tokens = array();
- $token = null;
-
- $text = false;
- $pattern = preg_replace("/''/", '``````', $pattern);
-
- for($i = 0; $i < strlen($pattern); $i++)
- {
- if($char==null || $pattern{$i} == $char || $text)
- {
- $token .= $pattern{$i};
- }
- else
- {
- $tokens[] = str_replace("","'",$token);
- $token = $pattern{$i};
- }
-
- if($pattern{$i} == "'" && $text == false)
- $text = true;
- else if($text && $pattern{$i} == "'" && $char == "'")
- $text = true;
- else if($text && $char != "'" && $pattern{$i} == "'")
- $text = false;
-
- $char = $pattern{$i};
-
- }
- $tokens[] = $token;
- return $tokens;
- }
-
- /**
- * Get the year.
- * "yy" will return the last two digits of year.
- * "yyyy" will return the full integer year.
- * @param array getdate format.
- * @param string a pattern.
- * @return string year
- */
- protected function getYear($date, $pattern='yyyy')
- {
- $year = $date['year'];
- switch($pattern)
- {
- case 'yy':
- return substr($year,2);
- case 'yyyy':
- return $year;
- default:
- throw new Exception('The pattern for year is either "yy" or "yyyy".');
- }
- }
-
- /**
- * Get the month.
- * "M" will return integer 1 through 12
- * "MM" will return the narrow month name, e.g. "J"
- * "MMM" will return the abrreviated month name, e.g. "Jan"
- * "MMMM" will return the month name, e.g. "January"
- * @param array getdate format.
- * @param string a pattern.
- * @return string month name
- */
- protected function getMonth($date, $pattern='M')
- {
- $month = $date['mon'];
-
- switch($pattern)
- {
- case 'M':
- return $month;
- case 'MM':
- return str_pad($month, 2,'0',STR_PAD_LEFT);
- case 'MMM':
- return $this->formatInfo->AbbreviatedMonthNames[$month-1];
- break;
- case 'MMMM':
- return $this->formatInfo->MonthNames[$month-1];
- default:
- throw new Exception('The pattern for month '.
- 'is "M", "MM", "MMM", or "MMMM".');
- }
- }
-
- /**
- * Get the day of the week.
- * "E" will return integer 0 (for Sunday) through 6 (for Saturday).
- * "EE" will return the narrow day of the week, e.g. "M"
- * "EEE" will return the abrreviated day of the week, e.g. "Mon"
- * "EEEE" will return the day of the week, e.g. "Monday"
- * @param array getdate format.
- * @param string a pattern.
- * @return string day of the week.
- */
- protected function getDayInWeek($date, $pattern='EEEE')
- {
- $day = $date['wday'];
-
- switch($pattern)
- {
- case 'E':
- return $day;
- break;
- case 'EE':
- return $this->formatInfo->NarrowDayNames[$day];
- case 'EEE':
- return $this->formatInfo->AbbreviatedDayNames[$day];
- break;
- case 'EEEE':
- return $this->formatInfo->DayNames[$day];
- break;
- default:
- throw new Exception('The pattern for day of the week '.
- 'is "E", "EE", "EEE", or "EEEE".');
- }
- }
-
- /**
- * Get the day of the month.
- * "d" for non-padding, "dd" will always return 2 characters.
- * @param array getdate format.
- * @param string a pattern.
- * @return string day of the month
- */
- protected function getDay($date, $pattern='d')
- {
- $day = $date['mday'];
-
- switch($pattern)
- {
- case 'd':
- return $day;
- case 'dd':
- return str_pad($day, 2,'0',STR_PAD_LEFT);
- default:
- throw new Exception('The pattern for day of '.
- 'the month is "d" or "dd".');
- }
- }
-
-
- /**
- * Get the era. i.e. in gregorian, year > 0 is AD, else BC.
- * @todo How to support multiple Eras?, e.g. Japanese.
- * @param array getdate format.
- * @param string a pattern.
- * @return string era
- */
- protected function getEra($date, $pattern='G')
- {
-
- if($pattern != 'G')
- throw new Exception('The pattern for era is "G".');
-
- $year = $date['year'];
- if($year > 0)
- return $this->formatInfo->getEra(1);
- else
- return $this->formatInfo->getEra(0);
- }
-
- /**
- * Get the hours in 24 hour format, i.e. [0-23].
- * "H" for non-padding, "HH" will always return 2 characters.
- * @param array getdate format.
- * @param string a pattern.
- * @return string hours in 24 hour format.
- */
- protected function getHour24($date, $pattern='H')
- {
- $hour = $date['hours'];
-
- switch($pattern)
- {
- case 'H':
- return $hour;
- case 'HH':
- return str_pad($hour, 2,'0',STR_PAD_LEFT);
- default:
- throw new Exception('The pattern for 24 hour '.
- 'format is "H" or "HH".');
- }
- }
-
- /**
- * Get the AM/PM designator, 12 noon is PM, 12 midnight is AM.
- * @param array getdate format.
- * @param string a pattern.
- * @return string AM or PM designator
- */
- protected function getAMPM($date, $pattern='a')
- {
- if($pattern != 'a')
- throw new Exception('The pattern for AM/PM marker is "a".');
-
- $hour = $date['hours'];
- $ampm = (int)($hour/12);
- return $this->formatInfo->AMPMMarkers[$ampm];
- }
-
- /**
- * Get the hours in 12 hour format.
- * "h" for non-padding, "hh" will always return 2 characters.
- * @param array getdate format.
- * @param string a pattern.
- * @return string hours in 12 hour format.
- */
- protected function getHour12($date, $pattern='h')
- {
- $hour = $date['hours'];
- $hour = ($hour==12|$hour==0)?12:($hour)%12;
-
- switch($pattern)
- {
- case 'h':
- return $hour;
- case 'hh':
- return str_pad($hour, 2,'0',STR_PAD_LEFT);
- default:
- throw new Exception('The pattern for 24 hour '.
- 'format is "H" or "HH".');
- }
- }
-
- /**
- * Get the minutes.
- * "m" for non-padding, "mm" will always return 2 characters.
- * @param array getdate format.
- * @param string a pattern.
- * @return string minutes.
- */
- protected function getMinutes($date, $pattern='m')
- {
- $minutes = $date['minutes'];
-
- switch($pattern)
- {
- case 'm':
- return $minutes;
- case 'mm':
- return str_pad($minutes, 2,'0',STR_PAD_LEFT);
- default:
- throw new Exception('The pattern for minutes is "m" or "mm".');
- }
- }
-
- /**
- * Get the seconds.
- * "s" for non-padding, "ss" will always return 2 characters.
- * @param array getdate format.
- * @param string a pattern.
- * @return string seconds
- */
- protected function getSeconds($date, $pattern='s')
- {
- $seconds = $date['seconds'];
-
- switch($pattern)
- {
- case 's':
- return $seconds;
- case 'ss':
- return str_pad($seconds, 2,'0',STR_PAD_LEFT);
- default:
- throw new Exception('The pattern for seconds is "s" or "ss".');
- }
- }
-
- /**
- * Get the timezone from the server machine.
- * @todo How to get the timezone for a different region?
- * @param array getdate format.
- * @param string a pattern.
- * @return string time zone
- */
- protected function getTimeZone($date, $pattern='z')
- {
- if($pattern != 'z')
- throw new Exception('The pattern for time zone is "z".');
-
- return @date('T', @mktime($date['hours'], $date['minutes'], $date['seconds'], $date['mon'], $date['mday'], $date['year']));
- }
-
- /**
- * Get the day in the year, e.g. [1-366]
- * @param array getdate format.
- * @param string a pattern.
- * @return int hours in AM/PM format.
- */
- protected function getDayInYear($date, $pattern='D')
- {
- if($pattern != 'D')
- throw new Exception('The pattern for day in year is "D".');
-
- return $date['yday'];
- }
-
- /**
- * Get day in the month.
- * @param array getdate format.
- * @param string a pattern.
- * @return int day in month
- */
- protected function getDayInMonth($date, $pattern='FF')
- {
- switch ($pattern) {
- case 'F':
- return @date('j', @mktime(0, 0, 0, $date['mon'], $date['mday'], $date['year']));
- break;
- case 'FF':
- return @date('d', @mktime(0, 0, 0, $date['mon'], $date['mday'], $date['year']));
- break;
- default:
- throw new Exception('The pattern for day in month is "F" or "FF".');
- }
- }
-
- /**
- * Get the week in the year.
- * @param array getdate format.
- * @param string a pattern.
- * @return int week in year
- */
- protected function getWeekInYear($date, $pattern='w')
- {
- if($pattern != 'w')
- throw new Exception('The pattern for week in year is "w".');
-
- return @date('W', @mktime(0, 0, 0, $date['mon'], $date['mday'], $date['year']));
- }
-
- /**
- * Get week in the month.
- * @param array getdate format.
- * @return int week in month
- */
- protected function getWeekInMonth($date, $pattern='W')
- {
- if($pattern != 'W')
- throw new Exception('The pattern for week in month is "W".');
-
- return @date('W', @mktime(0, 0, 0, $date['mon'], $date['mday'], $date['year'])) - date('W', mktime(0, 0, 0, $date['mon'], 1, $date['year']));
- }
-
- /**
- * Get the hours [1-24].
- * @param array getdate format.
- * @param string a pattern.
- * @return int hours [1-24]
- */
- protected function getHourInDay($date, $pattern='k')
- {
- if($pattern != 'k')
- throw new Exception('The pattern for hour in day is "k".');
-
- return $date['hours']+1;
- }
-
- /**
- * Get the hours in AM/PM format, e.g [1-12]
- * @param array getdate format.
- * @param string a pattern.
- * @return int hours in AM/PM format.
- */
- protected function getHourInAMPM($date, $pattern='K')
- {
- if($pattern != 'K')
- throw new Exception('The pattern for hour in AM/PM is "K".');
-
- return ($date['hours']+1)%12;
- }
-
-}
-
-?>
+
+ * @version $Revision: 1.8 $ $Date: 2005/12/15 07:14:49 $
+ * @package System.I18N.core
+ */
+
+/**
+ * Get the DateTimeFormatInfo class.
+ */
+require_once(dirname(__FILE__).'/DateTimeFormatInfo.php');
+
+/**
+ * Get the encoding utilities
+ */
+require_once(dirname(__FILE__).'/util.php');
+
+/**
+ * DateFormat class.
+ *
+ * The DateFormat class allows you to format dates and times with
+ * predefined styles in a locale-sensitive manner. Formatting times
+ * with the DateFormat class is similar to formatting dates.
+ *
+ * Formatting dates with the DateFormat class is a two-step process.
+ * First, you create a formatter with the getDateInstance method.
+ * Second, you invoke the format method, which returns a string containing
+ * the formatted date.
+ *
+ * DateTime values are formatted using standard or custom patterns stored
+ * in the properties of a DateTimeFormatInfo.
+ *
+ * @author Xiang Wei Zhuo
+ * @version v1.0, last update on Sat Dec 04 14:10:49 EST 2004
+ * @package System.I18N.core
+ */
+class DateFormat
+{
+ /**
+ * A list of tokens and their function call.
+ * @var array
+ */
+ protected $tokens = array(
+ 'G'=>'Era',
+ 'y'=>'Year',
+ 'M'=>'Month',
+ 'd'=>'Day',
+ 'h'=>'Hour12',
+ 'H'=>'Hour24',
+ 'm'=>'Minutes',
+ 's'=>'Seconds',
+ 'E'=>'DayInWeek',
+ 'D'=>'DayInYear',
+ 'F'=>'DayInMonth',
+ 'w'=>'WeekInYear',
+ 'W'=>'WeekInMonth',
+ 'a'=>'AMPM',
+ 'k'=>'HourInDay',
+ 'K'=>'HourInAMPM',
+ 'z'=>'TimeZone'
+ );
+
+ /**
+ * A list of methods, to be used by the token function calls.
+ * @var array
+ */
+ protected $methods = array();
+
+ /**
+ * The DateTimeFormatInfo, containing culture specific patterns and names.
+ * @var DateTimeFormatInfo
+ */
+ protected $formatInfo;
+
+ /**
+ * Initialize a new DateFormat.
+ * @param mixed either, null, a CultureInfo instance,
+ * a DateTimeFormatInfo instance, or a locale.
+ * @return DateFormat instance
+ */
+ function __construct($formatInfo=null)
+ {
+ if($formatInfo === null)
+ $this->formatInfo = DateTimeFormatInfo::getInvariantInfo();
+ else if($formatInfo instanceof CultureInfo)
+ $this->formatInfo = $formatInfo->DateTimeFormat;
+ else if($formatInfo instanceof DateTimeFormatInfo)
+ $this->formatInfo = $formatInfo;
+ else
+ $this->formatInfo = DateTimeFormatInfo::getInstance($formatInfo);
+
+ $this->methods = get_class_methods($this);
+ }
+
+ /**
+ * Format a date according to the pattern.
+ * @param mixed the time as integer or string in strtotime format.
+ * @return string formatted date time.
+ */
+ public function format($time, $pattern='F', $charset='UTF-8')
+ {
+ if (is_numeric($time)) //assumes unix epoch
+ $time = floatval($time);
+ else if(is_string($time))
+ $time = @strtotime($time);
+
+ if($pattern === null)
+ $pattern = 'F';
+
+ $s = Prado::createComponent('System.Util.TDateTimeStamp');
+
+ $date = $s->getDate($time);
+
+ $pattern = $this->getPattern($pattern);
+
+ $tokens = $this->getTokens($pattern);
+
+ for($i = 0, $k = count($tokens); $i<$k; ++$i)
+ {
+ $pattern = $tokens[$i];
+ if($pattern{0} == "'"
+ && $pattern{strlen($pattern)-1} == "'")
+ {
+ $sub = preg_replace('/(^\')|(\'$)/','',$pattern);
+ $tokens[$i] = str_replace('``````','\'',$sub);
+ }
+ else if($pattern == '``````')
+ {
+ $tokens[$i] = '\'';
+ }
+ else
+ {
+ $function = $this->getFunctionName($pattern);
+ if($function != null)
+ {
+ $fName = 'get'.$function;
+ if(in_array($fName, $this->methods))
+ {
+ $rs = $this->$fName($date, $pattern);
+ $tokens[$i] = $rs;
+ }
+ else
+ throw new
+ Exception('function '.$function.' not found.');
+ }
+ }
+ }
+
+ return I18N_toEncoding(implode('',$tokens), $charset);
+ }
+
+ /**
+ * For a particular token, get the corresponding function to call.
+ * @param string token
+ * @return mixed the function if good token, null otherwise.
+ */
+ protected function getFunctionName($token)
+ {
+ if(isset($this->tokens[$token{0}]))
+ return $this->tokens[$token{0}];
+ }
+
+ /**
+ * Get the pattern from DateTimeFormatInfo or some predefined patterns.
+ * If the $pattern parameter is an array of 2 element, it will assume
+ * that the first element is the date, and second the time
+ * and try to find an appropriate pattern and apply
+ * DateTimeFormatInfo::formatDateTime
+ * See the tutorial documentation for futher details on the patterns.
+ * @param mixed a pattern.
+ * @return string a pattern.
+ * @see DateTimeFormatInfo::formatDateTime()
+ */
+ protected function getPattern($pattern)
+ {
+ if(is_array($pattern) && count($pattern) == 2)
+ {
+ return $this->formatInfo->formatDateTime(
+ $this->getPattern($pattern[0]),
+ $this->getPattern($pattern[1]));
+ }
+
+ switch($pattern)
+ {
+ case 'd':
+ return $this->formatInfo->ShortDatePattern;
+ break;
+ case 'D':
+ return $this->formatInfo->LongDatePattern;
+ break;
+ case 'p':
+ return $this->formatInfo->MediumDatePattern;
+ break;
+ case 'P':
+ return $this->formatInfo->FullDatePattern;
+ break;
+ case 't':
+ return $this->formatInfo->ShortTimePattern;
+ break;
+ case 'T':
+ return $this->formatInfo->LongTimePattern;
+ break;
+ case 'q':
+ return $this->formatInfo->MediumTimePattern;
+ break;
+ case 'Q':
+ return $this->formatInfo->FullTimePattern;
+ break;
+ case 'f':
+ return $this->formatInfo->formatDateTime(
+ $this->formatInfo->LongDatePattern,
+ $this->formatInfo->ShortTimePattern);
+ break;
+ case 'F':
+ return $this->formatInfo->formatDateTime(
+ $this->formatInfo->LongDatePattern,
+ $this->formatInfo->LongTimePattern);
+ break;
+ case 'g':
+ return $this->formatInfo->formatDateTime(
+ $this->formatInfo->ShortDatePattern,
+ $this->formatInfo->ShortTimePattern);
+ break;
+ case 'G':
+ return $this->formatInfo->formatDateTime(
+ $this->formatInfo->ShortDatePattern,
+ $this->formatInfo->LongTimePattern);
+ break;
+ case 'M':
+ case 'm':
+ return 'MMMM dd';
+ break;
+ case 'R':
+ case 'r':
+ return 'EEE, dd MMM yyyy HH:mm:ss';
+ break;
+ case 's':
+ return 'yyyy-MM-ddTHH:mm:ss';
+ break;
+ case 'u':
+ return 'yyyy-MM-dd HH:mm:ss z';
+ break;
+ case 'U':
+ return 'EEEE dd MMMM yyyy HH:mm:ss';
+ break;
+ case 'Y':
+ case 'y':
+ return 'yyyy MMMM';
+ break;
+ default :
+ return $pattern;
+ }
+ }
+
+ /**
+ * Tokenize the pattern. The tokens are delimited by group of
+ * similar characters, e.g. 'aabb' will form 2 tokens of 'aa' and 'bb'.
+ * Any substrings, starting and ending with a single quote (')
+ * will be treated as a single token.
+ * @param string pattern.
+ * @return array string tokens in an array.
+ */
+ protected function getTokens($pattern)
+ {
+ $char = null;
+ $tokens = array();
+ $token = null;
+
+ $text = false;
+ $pattern = preg_replace("/''/", '``````', $pattern);
+
+ for($i = 0; $i < strlen($pattern); $i++)
+ {
+ if($char==null || $pattern{$i} == $char || $text)
+ {
+ $token .= $pattern{$i};
+ }
+ else
+ {
+ $tokens[] = str_replace("","'",$token);
+ $token = $pattern{$i};
+ }
+
+ if($pattern{$i} == "'" && $text == false)
+ $text = true;
+ else if($text && $pattern{$i} == "'" && $char == "'")
+ $text = true;
+ else if($text && $char != "'" && $pattern{$i} == "'")
+ $text = false;
+
+ $char = $pattern{$i};
+
+ }
+ $tokens[] = $token;
+ return $tokens;
+ }
+
+ /**
+ * Get the year.
+ * "yy" will return the last two digits of year.
+ * "yyyy" will return the full integer year.
+ * @param array getdate format.
+ * @param string a pattern.
+ * @return string year
+ */
+ protected function getYear($date, $pattern='yyyy')
+ {
+ $year = $date['year'];
+ switch($pattern)
+ {
+ case 'yy':
+ return substr($year,2);
+ case 'yyyy':
+ return $year;
+ default:
+ throw new Exception('The pattern for year is either "yy" or "yyyy".');
+ }
+ }
+
+ /**
+ * Get the month.
+ * "M" will return integer 1 through 12
+ * "MM" will return the narrow month name, e.g. "J"
+ * "MMM" will return the abrreviated month name, e.g. "Jan"
+ * "MMMM" will return the month name, e.g. "January"
+ * @param array getdate format.
+ * @param string a pattern.
+ * @return string month name
+ */
+ protected function getMonth($date, $pattern='M')
+ {
+ $month = $date['mon'];
+
+ switch($pattern)
+ {
+ case 'M':
+ return $month;
+ case 'MM':
+ return str_pad($month, 2,'0',STR_PAD_LEFT);
+ case 'MMM':
+ return $this->formatInfo->AbbreviatedMonthNames[$month-1];
+ break;
+ case 'MMMM':
+ return $this->formatInfo->MonthNames[$month-1];
+ default:
+ throw new Exception('The pattern for month '.
+ 'is "M", "MM", "MMM", or "MMMM".');
+ }
+ }
+
+ /**
+ * Get the day of the week.
+ * "E" will return integer 0 (for Sunday) through 6 (for Saturday).
+ * "EE" will return the narrow day of the week, e.g. "M"
+ * "EEE" will return the abrreviated day of the week, e.g. "Mon"
+ * "EEEE" will return the day of the week, e.g. "Monday"
+ * @param array getdate format.
+ * @param string a pattern.
+ * @return string day of the week.
+ */
+ protected function getDayInWeek($date, $pattern='EEEE')
+ {
+ $day = $date['wday'];
+
+ switch($pattern)
+ {
+ case 'E':
+ return $day;
+ break;
+ case 'EE':
+ return $this->formatInfo->NarrowDayNames[$day];
+ case 'EEE':
+ return $this->formatInfo->AbbreviatedDayNames[$day];
+ break;
+ case 'EEEE':
+ return $this->formatInfo->DayNames[$day];
+ break;
+ default:
+ throw new Exception('The pattern for day of the week '.
+ 'is "E", "EE", "EEE", or "EEEE".');
+ }
+ }
+
+ /**
+ * Get the day of the month.
+ * "d" for non-padding, "dd" will always return 2 characters.
+ * @param array getdate format.
+ * @param string a pattern.
+ * @return string day of the month
+ */
+ protected function getDay($date, $pattern='d')
+ {
+ $day = $date['mday'];
+
+ switch($pattern)
+ {
+ case 'd':
+ return $day;
+ case 'dd':
+ return str_pad($day, 2,'0',STR_PAD_LEFT);
+ default:
+ throw new Exception('The pattern for day of '.
+ 'the month is "d" or "dd".');
+ }
+ }
+
+
+ /**
+ * Get the era. i.e. in gregorian, year > 0 is AD, else BC.
+ * @todo How to support multiple Eras?, e.g. Japanese.
+ * @param array getdate format.
+ * @param string a pattern.
+ * @return string era
+ */
+ protected function getEra($date, $pattern='G')
+ {
+
+ if($pattern != 'G')
+ throw new Exception('The pattern for era is "G".');
+
+ $year = $date['year'];
+ if($year > 0)
+ return $this->formatInfo->getEra(1);
+ else
+ return $this->formatInfo->getEra(0);
+ }
+
+ /**
+ * Get the hours in 24 hour format, i.e. [0-23].
+ * "H" for non-padding, "HH" will always return 2 characters.
+ * @param array getdate format.
+ * @param string a pattern.
+ * @return string hours in 24 hour format.
+ */
+ protected function getHour24($date, $pattern='H')
+ {
+ $hour = $date['hours'];
+
+ switch($pattern)
+ {
+ case 'H':
+ return $hour;
+ case 'HH':
+ return str_pad($hour, 2,'0',STR_PAD_LEFT);
+ default:
+ throw new Exception('The pattern for 24 hour '.
+ 'format is "H" or "HH".');
+ }
+ }
+
+ /**
+ * Get the AM/PM designator, 12 noon is PM, 12 midnight is AM.
+ * @param array getdate format.
+ * @param string a pattern.
+ * @return string AM or PM designator
+ */
+ protected function getAMPM($date, $pattern='a')
+ {
+ if($pattern != 'a')
+ throw new Exception('The pattern for AM/PM marker is "a".');
+
+ $hour = $date['hours'];
+ $ampm = (int)($hour/12);
+ return $this->formatInfo->AMPMMarkers[$ampm];
+ }
+
+ /**
+ * Get the hours in 12 hour format.
+ * "h" for non-padding, "hh" will always return 2 characters.
+ * @param array getdate format.
+ * @param string a pattern.
+ * @return string hours in 12 hour format.
+ */
+ protected function getHour12($date, $pattern='h')
+ {
+ $hour = $date['hours'];
+ $hour = ($hour==12|$hour==0)?12:($hour)%12;
+
+ switch($pattern)
+ {
+ case 'h':
+ return $hour;
+ case 'hh':
+ return str_pad($hour, 2,'0',STR_PAD_LEFT);
+ default:
+ throw new Exception('The pattern for 24 hour '.
+ 'format is "H" or "HH".');
+ }
+ }
+
+ /**
+ * Get the minutes.
+ * "m" for non-padding, "mm" will always return 2 characters.
+ * @param array getdate format.
+ * @param string a pattern.
+ * @return string minutes.
+ */
+ protected function getMinutes($date, $pattern='m')
+ {
+ $minutes = $date['minutes'];
+
+ switch($pattern)
+ {
+ case 'm':
+ return $minutes;
+ case 'mm':
+ return str_pad($minutes, 2,'0',STR_PAD_LEFT);
+ default:
+ throw new Exception('The pattern for minutes is "m" or "mm".');
+ }
+ }
+
+ /**
+ * Get the seconds.
+ * "s" for non-padding, "ss" will always return 2 characters.
+ * @param array getdate format.
+ * @param string a pattern.
+ * @return string seconds
+ */
+ protected function getSeconds($date, $pattern='s')
+ {
+ $seconds = $date['seconds'];
+
+ switch($pattern)
+ {
+ case 's':
+ return $seconds;
+ case 'ss':
+ return str_pad($seconds, 2,'0',STR_PAD_LEFT);
+ default:
+ throw new Exception('The pattern for seconds is "s" or "ss".');
+ }
+ }
+
+ /**
+ * Get the timezone from the server machine.
+ * @todo How to get the timezone for a different region?
+ * @param array getdate format.
+ * @param string a pattern.
+ * @return string time zone
+ */
+ protected function getTimeZone($date, $pattern='z')
+ {
+ if($pattern != 'z')
+ throw new Exception('The pattern for time zone is "z".');
+
+ return @date('T', @mktime($date['hours'], $date['minutes'], $date['seconds'], $date['mon'], $date['mday'], $date['year']));
+ }
+
+ /**
+ * Get the day in the year, e.g. [1-366]
+ * @param array getdate format.
+ * @param string a pattern.
+ * @return int hours in AM/PM format.
+ */
+ protected function getDayInYear($date, $pattern='D')
+ {
+ if($pattern != 'D')
+ throw new Exception('The pattern for day in year is "D".');
+
+ return $date['yday'];
+ }
+
+ /**
+ * Get day in the month.
+ * @param array getdate format.
+ * @param string a pattern.
+ * @return int day in month
+ */
+ protected function getDayInMonth($date, $pattern='FF')
+ {
+ switch ($pattern) {
+ case 'F':
+ return @date('j', @mktime(0, 0, 0, $date['mon'], $date['mday'], $date['year']));
+ break;
+ case 'FF':
+ return @date('d', @mktime(0, 0, 0, $date['mon'], $date['mday'], $date['year']));
+ break;
+ default:
+ throw new Exception('The pattern for day in month is "F" or "FF".');
+ }
+ }
+
+ /**
+ * Get the week in the year.
+ * @param array getdate format.
+ * @param string a pattern.
+ * @return int week in year
+ */
+ protected function getWeekInYear($date, $pattern='w')
+ {
+ if($pattern != 'w')
+ throw new Exception('The pattern for week in year is "w".');
+
+ return @date('W', @mktime(0, 0, 0, $date['mon'], $date['mday'], $date['year']));
+ }
+
+ /**
+ * Get week in the month.
+ * @param array getdate format.
+ * @return int week in month
+ */
+ protected function getWeekInMonth($date, $pattern='W')
+ {
+ if($pattern != 'W')
+ throw new Exception('The pattern for week in month is "W".');
+
+ return @date('W', @mktime(0, 0, 0, $date['mon'], $date['mday'], $date['year'])) - date('W', mktime(0, 0, 0, $date['mon'], 1, $date['year']));
+ }
+
+ /**
+ * Get the hours [1-24].
+ * @param array getdate format.
+ * @param string a pattern.
+ * @return int hours [1-24]
+ */
+ protected function getHourInDay($date, $pattern='k')
+ {
+ if($pattern != 'k')
+ throw new Exception('The pattern for hour in day is "k".');
+
+ return $date['hours']+1;
+ }
+
+ /**
+ * Get the hours in AM/PM format, e.g [1-12]
+ * @param array getdate format.
+ * @param string a pattern.
+ * @return int hours in AM/PM format.
+ */
+ protected function getHourInAMPM($date, $pattern='K')
+ {
+ if($pattern != 'K')
+ throw new Exception('The pattern for hour in AM/PM is "K".');
+
+ return ($date['hours']+1)%12;
+ }
+
+}
+
+?>
diff --git a/framework/I18N/core/DateTimeFormatInfo.php b/framework/I18N/core/DateTimeFormatInfo.php
index aebd094a..d4deee1f 100644
--- a/framework/I18N/core/DateTimeFormatInfo.php
+++ b/framework/I18N/core/DateTimeFormatInfo.php
@@ -1,516 +1,516 @@
-
- * @version $Revision: 1.2 $ $Date: 2005/01/05 03:15:14 $
- * @package System.I18N.core
- */
-
-/**
- * Get the CultureInfo class.
- */
-require_once(dirname(__FILE__).'/CultureInfo.php');
-
-
-/**
- * Defines how DateTime values are formatted and displayed, depending
- * on the culture.
- *
- * This class contains information, such as date patterns, time patterns,
- * and AM/PM designators.
- *
- * To create a DateTimeFormatInfo for a specific culture, create a
- * CultureInfo for that culture and retrieve the CultureInfo.DateTimeFormat
- * property. For example:
- *
- * $culture = new CultureInfo('en_AU');
- * $dtfi = $culture->DateTimeFormat;
- *
- *
- * To create a DateTimeFormatInfo for the invariant culture, use
- *
- * DateTimeFormatInfo::getInstance($culture=null);
- *
- * you may pass a CultureInfo parameter $culture to get the DateTimeFormatInfo
- * for a specific culture.
- *
- * DateTime values are formatted using standard or custom patterns stored in
- * the properties of a DateTimeFormatInfo.
- *
- * The standard patterns can be replaced with custom patterns by setting the
- * associated properties of DateTimeFormatInfo.
- *
- * The following table lists the standard format characters for each standard
- * pattern and the associated DateTimeFormatInfo property that can be set to
- * modify the standard pattern. The format characters are case-sensitive;
- * for example, 'g' and 'G' represent slightly different patterns.
- *
- *
- * Format Character Associated Property Example Format Pattern (en-US)
- * --------------------------------------------------------------------------
- * d ShortDatePattern MM/dd/yyyy
- * D LongDatePattern dddd, dd MMMM yyyy
- * F FullDateTimePattern dddd, dd MMMM yyyy HH:mm:ss
- * m, M MonthDayPattern MMMM dd
- * r, R RFC1123Pattern ddd, dd MMM yyyy HH':'mm':'ss 'GMT'
- * s SortableDateTimePattern yyyy'-'MM'-'dd'T'HH':'mm':'ss
- * t ShortTimePattern HH:mm
- * T LongTimePattern HH:mm:ss
- * Y YearMonthPattern yyyy MMMM
- * --------------------------------------------------------------------------
- *
- *
- * @author Xiang Wei Zhuo
- * @version v1.0, last update on Fri Dec 03 22:30:31 EST 2004
- * @package System.I18N.core
- */
-class DateTimeFormatInfo
-{
- /**
- * ICU date time formatting data.
- * @var array
- */
- private $data = array();
-
- /**
- * A list of properties that are accessable/writable.
- * @var array
- */
- protected $properties = array();
-
- /**
- * Allow functions that begins with 'set' to be called directly
- * as an attribute/property to retrieve the value.
- * @return mixed
- */
- function __get($name)
- {
- $getProperty = 'get'.$name;
- if(in_array($getProperty, $this->properties))
- return $this->$getProperty();
- else
- throw new Exception('Property '.$name.' does not exists.');
- }
-
- /**
- * Allow functions that begins with 'set' to be called directly
- * as an attribute/property to set the value.
- */
- function __set($name, $value)
- {
- $setProperty = 'set'.$name;
- if(in_array($setProperty, $this->properties))
- $this->$setProperty($value);
- else
- throw new Exception('Property '.$name.' can not be set.');
- }
-
- /**
- * Initializes a new writable instance of the DateTimeFormatInfo class
- * that is dependent on the ICU data for date time formatting
- * information. N.B.You should not initialize this class directly
- * unless you know what you are doing. Please use use
- * DateTimeFormatInfo::getInstance() to create an instance.
- * @param array ICU data for date time formatting.
- * @see getInstance()
- */
- function __construct($data=array())
- {
- $this->properties = get_class_methods($this);
-
- if(empty($data))
- throw new Exception('Please provide the ICU data to initialize.');
-
- $this->data = $data;
- }
-
- /**
- * Get the internal ICU data for date time formatting.
- * @return array ICU date time formatting data.
- */
- protected function getData()
- {
- return $this->data;
- }
-
- /**
- * Gets the default DateTimeFormatInfo that is culture-independent
- * (invariant).
- * @return DateTimeFormatInfo default DateTimeFormatInfo.
- */
- static function getInvariantInfo()
- {
- static $invariant;
- if($invariant === null)
- {
- $culture = CultureInfo::getInvariantCulture();
- $invariant = $culture->getDateTimeFormat();
- }
- return $invariant;
- }
-
- /**
- * Returns the DateTimeFormatInfo associated with the specified culture.
- * @param CultureInfo the culture that gets the DateTimeFormat property.
- * @return DateTimeFormatInfo DateTimeFormatInfo for the specified
- * culture.
- */
- static function getInstance($culture=null)
- {
-
- if ($culture instanceof CultureInfo)
- return $culture->getDateTimeFormat();
- else if(is_string($culture))
- {
- $cultureInfo = CultureInfo::getInstance($culture);
- return $cultureInfo->getDateTimeFormat();
- }
- else
- {
- $cultureInfo = CultureInfo::getInvariantCulture();
- return $cultureInfo->getDateTimeFormat();
- }
- }
-
- /**
- * A one-dimensional array of type String containing
- * the culture-specific abbreviated names of the days
- * of the week. The array for InvariantInfo contains
- * "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", and "Sat".
- * @return array abbreviated day names
- */
- function getAbbreviatedDayNames()
- {
- return $this->data['dayNames']['format']['abbreviated'];
- //return $this->data['dayNames/format/abbreviated'];
- }
-
- /**
- * Set the abbreviated day names. The value should be
- * an array of string starting with Sunday and ends in Saturady.
- * For example,
- * array("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat");
- * @param array abbreviated day names.
- */
- function setAbbreviatedDayNames($value)
- {
- $this->data['dayNames']['format']['abbreviated'] = $value;
- }
-
- /**
- * A one-dimensional array of type String containing
- * the culture-specific narrow names of the days
- * of the week. The array for InvariantInfo contains
- * "S", "M", "T", "W", "T", "F", and "S".
- * @return array narrow day names
- */
- function getNarrowDayNames()
- {
- return $this->data['dayNames']['format']['narrow'];
- }
-
- /**
- * Set the narrow day names. The value should be
- * an array of string starting with Sunday and ends in Saturady.
- * For example,
- * array("S", "M", "T", "W", "T", "F", "S");
- * @param array narrow day names.
- */
- function setNarrowDayNames($value)
- {
- $this->data['dayNames']['format']['narrow'] = $value;
- }
-
- /**
- * A one-dimensional array of type String containing the
- * culture-specific full names of the days of the week.
- * The array for InvariantInfo contains "Sunday", "Monday",
- * "Tuesday", "Wednesday", "Thursday", "Friday", and "Saturday".
- * @return array day names
- */
- function getDayNames()
- {
- return $this->data['dayNames']['format']['wide'];
- }
-
-
- /**
- * Set the day names. The value should be
- * an array of string starting with Sunday and ends in Saturady.
- * For example,
- * array("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
- * "Friday", "Saturday".);
- * @param array day names.
- */
- function setDayNames($value)
- {
- $this->data['dayNames']['format']['wide'] = $value;
- }
-
- /**
- * A one-dimensional array of type String containing the
- * culture-specific narrow names of the months. The array
- * for InvariantInfo contains "J", "F", "M", "A", "M", "J",
- * "J", "A", "S", "O", "N", and "D".
- * @return array narrow month names.
- */
- function getNarrowMonthNames()
- {
- return $this->data['monthNames']['format']['narrow'];
- }
-
- /**
- * Set the narrow month names. The value should be
- * an array of string starting with J and ends in D.
- * For example,
- * array("J","F","M","A","M","J","J","A","S","O","N","D");
- * @param array month names.
- */
- function setNarrowMonthNames($value)
- {
- $this->data['monthNames']['format']['narrow'] = $value;
- }
-
- /**
- * A one-dimensional array of type String containing the
- * culture-specific abbreviated names of the months. The array
- * for InvariantInfo contains "Jan", "Feb", "Mar", "Apr", "May",
- * "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", and "Dec".
- * Returns wide names if abbreviated names doesn't exist.
- * @return array abbreviated month names.
- */
- function getAbbreviatedMonthNames()
- {
- if (isset($this->data['monthNames']['format']['abbreviated']))
- return $this->data['monthNames']['format']['abbreviated'];
- else
- return $this->data['monthNames']['format']['wide'];
- }
-
- /**
- * Set the abbreviated month names. The value should be
- * an array of string starting with Jan and ends in Dec.
- * For example,
- * array("Jan", "Feb", "Mar", "Apr", "May", "Jun",
- * "Jul", "Aug", "Sep","Oct","Nov","Dec");
- * @param array month names.
- */
- function setAbbreviatedMonthNames($value)
- {
- $this->data['monthNames']['format']['abbreviated'] = $value;
- }
-
- /**
- * A one-dimensional array of type String containing the
- * culture-specific full names of the months. The array for
- * InvariantInfo contains "January", "February", "March", "April",
- * "May", "June", "July", "August", "September", "October", "November",
- * and "December"
- * @return array month names.
- */
- function getMonthNames()
- {
- return $this->data['monthNames']['format']['wide'];
- }
-
- /**
- * Set the month names. The value should be
- * an array of string starting with Janurary and ends in December.
- * For example,
- * array("January", "February", "March", "April", "May", "June",
- * "July", "August", "September","October","November","December");
- * @param array month names.
- */
- function setMonthNames($value)
- {
- $this->data['monthNames']['format']['wide'] = $value;
- }
-
- /**
- * A string containing the name of the era.
- * @param int era The integer representing the era.
- * @return string the era name.
- */
- function getEra($era)
- {
- $eraName = $this->data['eras']['abbreviated'];
- return $eraName[$era];
- }
-
- /**
- * The string designator for hours that are "ante meridiem" (before noon).
- * The default for InvariantInfo is "AM".
- * @return string AM designator.
- */
- function getAMDesignator()
- {
- $result = $this->getAMPMMarkers();
- return $result[0];
- }
-
- /**
- * Set the AM Designator. For example, 'AM'.
- * @param string AM designator.
- */
- function setAMDesignator($value)
- {
- $markers = $this->getAMPMMarkers();
- $markers[0] = $value;
- $this->setAMPMMarkers($markers);
- }
-
- /**
- * The string designator for hours that are "post meridiem" (after noon).
- * The default for InvariantInfo is "PM".
- * @return string PM designator.
- */
- function getPMDesignator()
- {
- $result = $this->getAMPMMarkers();
- return $result[1];
- }
-
- /**
- * Set the PM Designator. For example, 'PM'.
- * @param string PM designator.
- */
- function setPMDesignator($value)
- {
- $markers = $this->getAMPMMarkers();
- $markers[1] = $value;
- $this->setAMPMMarkers($markers);
- }
-
- /**
- * Get the AM and PM markers array.
- * Default InvariantInfo for AM and PM is array('AM','PM');
- * @return array AM and PM markers
- */
- function getAMPMMarkers()
- {
- return $this->data['AmPmMarkers'];
- }
-
- /**
- * Set the AM and PM markers array.
- * For example array('AM','PM');
- * @param array AM and PM markers
- */
- function setAMPMMarkers($value)
- {
- $this->data['AmPmMarkers'] = $value;
- }
-
- /**
- * Returns the full time pattern "HH:mm:ss z" (default).
- * This is culture sensitive.
- * @return string pattern "HH:mm:ss z".
- */
- function getFullTimePattern()
- {
- return $this->data['DateTimePatterns'][0];
- }
-
- /**
- * Returns the long time pattern "HH:mm:ss z" (default).
- * This is culture sensitive.
- * @return string pattern "HH:mm:ss z".
- */
- function getLongTimePattern()
- {
- return $this->data['DateTimePatterns'][1];
- }
-
- /**
- * Returns the medium time pattern "HH:mm:ss" (default).
- * This is culture sensitive.
- * @return string pattern "HH:mm:ss".
- */
- function getMediumTimePattern()
- {
- return $this->data['DateTimePatterns'][2];
- }
-
- /**
- * Returns the short time pattern "HH:mm" (default).
- * This is culture sensitive.
- * @return string pattern "HH:mm".
- */
- function getShortTimePattern()
- {
- return $this->data['DateTimePatterns'][3];
- }
-
- /**
- * Returns the full date pattern "EEEE, yyyy MMMM dd" (default).
- * This is culture sensitive.
- * @return string pattern "EEEE, yyyy MMMM dd".
- */
- function getFullDatePattern()
- {
- return $this->data['DateTimePatterns'][4];
- }
-
- /**
- * Returns the long date pattern "yyyy MMMM d" (default).
- * This is culture sensitive.
- * @return string pattern "yyyy MMMM d".
- */
- function getLongDatePattern()
- {
- return $this->data['DateTimePatterns'][5];
- }
-
- /**
- * Returns the medium date pattern "yyyy MMMM d" (default).
- * This is culture sensitive.
- * @return string pattern "yyyy MMM d".
- */
- function getMediumDatePattern()
- {
- return $this->data['DateTimePatterns'][6];
- }
-
- /**
- * Returns the short date pattern "yy/MM/dd" (default).
- * This is culture sensitive.
- * @return string pattern "yy/MM/dd".
- */
- function getShortDatePattern()
- {
- return $this->data['DateTimePatterns'][7];
- }
-
- /**
- * Returns the date time order pattern, "{1} {0}" (default).
- * This is culture sensitive.
- * @return string pattern "{1} {0}".
- */
- function getDateTimeOrderPattern()
- {
- return $this->data['DateTimePatterns'][8];
- }
-
- /**
- * Formats the date and time in a culture sensitive paterrn.
- * The default is "Date Time".
- * @return string date and time formated
- */
- function formatDateTime($date, $time)
- {
- $pattern = $this->getDateTimeOrderPattern();
- return str_replace(array('{0}','{1}'), array($time, $date), $pattern);
- }
-
-}
+
+ * @version $Revision: 1.2 $ $Date: 2005/01/05 03:15:14 $
+ * @package System.I18N.core
+ */
+
+/**
+ * Get the CultureInfo class.
+ */
+require_once(dirname(__FILE__).'/CultureInfo.php');
+
+
+/**
+ * Defines how DateTime values are formatted and displayed, depending
+ * on the culture.
+ *
+ * This class contains information, such as date patterns, time patterns,
+ * and AM/PM designators.
+ *
+ * To create a DateTimeFormatInfo for a specific culture, create a
+ * CultureInfo for that culture and retrieve the CultureInfo.DateTimeFormat
+ * property. For example:
+ *
+ * $culture = new CultureInfo('en_AU');
+ * $dtfi = $culture->DateTimeFormat;
+ *
+ *
+ * To create a DateTimeFormatInfo for the invariant culture, use
+ *
+ * DateTimeFormatInfo::getInstance($culture=null);
+ *
+ * you may pass a CultureInfo parameter $culture to get the DateTimeFormatInfo
+ * for a specific culture.
+ *
+ * DateTime values are formatted using standard or custom patterns stored in
+ * the properties of a DateTimeFormatInfo.
+ *
+ * The standard patterns can be replaced with custom patterns by setting the
+ * associated properties of DateTimeFormatInfo.
+ *
+ * The following table lists the standard format characters for each standard
+ * pattern and the associated DateTimeFormatInfo property that can be set to
+ * modify the standard pattern. The format characters are case-sensitive;
+ * for example, 'g' and 'G' represent slightly different patterns.
+ *
+ *
+ * Format Character Associated Property Example Format Pattern (en-US)
+ * --------------------------------------------------------------------------
+ * d ShortDatePattern MM/dd/yyyy
+ * D LongDatePattern dddd, dd MMMM yyyy
+ * F FullDateTimePattern dddd, dd MMMM yyyy HH:mm:ss
+ * m, M MonthDayPattern MMMM dd
+ * r, R RFC1123Pattern ddd, dd MMM yyyy HH':'mm':'ss 'GMT'
+ * s SortableDateTimePattern yyyy'-'MM'-'dd'T'HH':'mm':'ss
+ * t ShortTimePattern HH:mm
+ * T LongTimePattern HH:mm:ss
+ * Y YearMonthPattern yyyy MMMM
+ * --------------------------------------------------------------------------
+ *
+ *
+ * @author Xiang Wei Zhuo
+ * @version v1.0, last update on Fri Dec 03 22:30:31 EST 2004
+ * @package System.I18N.core
+ */
+class DateTimeFormatInfo
+{
+ /**
+ * ICU date time formatting data.
+ * @var array
+ */
+ private $data = array();
+
+ /**
+ * A list of properties that are accessable/writable.
+ * @var array
+ */
+ protected $properties = array();
+
+ /**
+ * Allow functions that begins with 'set' to be called directly
+ * as an attribute/property to retrieve the value.
+ * @return mixed
+ */
+ function __get($name)
+ {
+ $getProperty = 'get'.$name;
+ if(in_array($getProperty, $this->properties))
+ return $this->$getProperty();
+ else
+ throw new Exception('Property '.$name.' does not exists.');
+ }
+
+ /**
+ * Allow functions that begins with 'set' to be called directly
+ * as an attribute/property to set the value.
+ */
+ function __set($name, $value)
+ {
+ $setProperty = 'set'.$name;
+ if(in_array($setProperty, $this->properties))
+ $this->$setProperty($value);
+ else
+ throw new Exception('Property '.$name.' can not be set.');
+ }
+
+ /**
+ * Initializes a new writable instance of the DateTimeFormatInfo class
+ * that is dependent on the ICU data for date time formatting
+ * information. N.B.You should not initialize this class directly
+ * unless you know what you are doing. Please use use
+ * DateTimeFormatInfo::getInstance() to create an instance.
+ * @param array ICU data for date time formatting.
+ * @see getInstance()
+ */
+ function __construct($data=array())
+ {
+ $this->properties = get_class_methods($this);
+
+ if(empty($data))
+ throw new Exception('Please provide the ICU data to initialize.');
+
+ $this->data = $data;
+ }
+
+ /**
+ * Get the internal ICU data for date time formatting.
+ * @return array ICU date time formatting data.
+ */
+ protected function getData()
+ {
+ return $this->data;
+ }
+
+ /**
+ * Gets the default DateTimeFormatInfo that is culture-independent
+ * (invariant).
+ * @return DateTimeFormatInfo default DateTimeFormatInfo.
+ */
+ static function getInvariantInfo()
+ {
+ static $invariant;
+ if($invariant === null)
+ {
+ $culture = CultureInfo::getInvariantCulture();
+ $invariant = $culture->getDateTimeFormat();
+ }
+ return $invariant;
+ }
+
+ /**
+ * Returns the DateTimeFormatInfo associated with the specified culture.
+ * @param CultureInfo the culture that gets the DateTimeFormat property.
+ * @return DateTimeFormatInfo DateTimeFormatInfo for the specified
+ * culture.
+ */
+ static function getInstance($culture=null)
+ {
+
+ if ($culture instanceof CultureInfo)
+ return $culture->getDateTimeFormat();
+ else if(is_string($culture))
+ {
+ $cultureInfo = CultureInfo::getInstance($culture);
+ return $cultureInfo->getDateTimeFormat();
+ }
+ else
+ {
+ $cultureInfo = CultureInfo::getInvariantCulture();
+ return $cultureInfo->getDateTimeFormat();
+ }
+ }
+
+ /**
+ * A one-dimensional array of type String containing
+ * the culture-specific abbreviated names of the days
+ * of the week. The array for InvariantInfo contains
+ * "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", and "Sat".
+ * @return array abbreviated day names
+ */
+ function getAbbreviatedDayNames()
+ {
+ return $this->data['dayNames']['format']['abbreviated'];
+ //return $this->data['dayNames/format/abbreviated'];
+ }
+
+ /**
+ * Set the abbreviated day names. The value should be
+ * an array of string starting with Sunday and ends in Saturady.
+ * For example,
+ * array("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat");
+ * @param array abbreviated day names.
+ */
+ function setAbbreviatedDayNames($value)
+ {
+ $this->data['dayNames']['format']['abbreviated'] = $value;
+ }
+
+ /**
+ * A one-dimensional array of type String containing
+ * the culture-specific narrow names of the days
+ * of the week. The array for InvariantInfo contains
+ * "S", "M", "T", "W", "T", "F", and "S".
+ * @return array narrow day names
+ */
+ function getNarrowDayNames()
+ {
+ return $this->data['dayNames']['format']['narrow'];
+ }
+
+ /**
+ * Set the narrow day names. The value should be
+ * an array of string starting with Sunday and ends in Saturady.
+ * For example,
+ * array("S", "M", "T", "W", "T", "F", "S");
+ * @param array narrow day names.
+ */
+ function setNarrowDayNames($value)
+ {
+ $this->data['dayNames']['format']['narrow'] = $value;
+ }
+
+ /**
+ * A one-dimensional array of type String containing the
+ * culture-specific full names of the days of the week.
+ * The array for InvariantInfo contains "Sunday", "Monday",
+ * "Tuesday", "Wednesday", "Thursday", "Friday", and "Saturday".
+ * @return array day names
+ */
+ function getDayNames()
+ {
+ return $this->data['dayNames']['format']['wide'];
+ }
+
+
+ /**
+ * Set the day names. The value should be
+ * an array of string starting with Sunday and ends in Saturady.
+ * For example,
+ * array("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
+ * "Friday", "Saturday".);
+ * @param array day names.
+ */
+ function setDayNames($value)
+ {
+ $this->data['dayNames']['format']['wide'] = $value;
+ }
+
+ /**
+ * A one-dimensional array of type String containing the
+ * culture-specific narrow names of the months. The array
+ * for InvariantInfo contains "J", "F", "M", "A", "M", "J",
+ * "J", "A", "S", "O", "N", and "D".
+ * @return array narrow month names.
+ */
+ function getNarrowMonthNames()
+ {
+ return $this->data['monthNames']['format']['narrow'];
+ }
+
+ /**
+ * Set the narrow month names. The value should be
+ * an array of string starting with J and ends in D.
+ * For example,
+ * array("J","F","M","A","M","J","J","A","S","O","N","D");
+ * @param array month names.
+ */
+ function setNarrowMonthNames($value)
+ {
+ $this->data['monthNames']['format']['narrow'] = $value;
+ }
+
+ /**
+ * A one-dimensional array of type String containing the
+ * culture-specific abbreviated names of the months. The array
+ * for InvariantInfo contains "Jan", "Feb", "Mar", "Apr", "May",
+ * "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", and "Dec".
+ * Returns wide names if abbreviated names doesn't exist.
+ * @return array abbreviated month names.
+ */
+ function getAbbreviatedMonthNames()
+ {
+ if (isset($this->data['monthNames']['format']['abbreviated']))
+ return $this->data['monthNames']['format']['abbreviated'];
+ else
+ return $this->data['monthNames']['format']['wide'];
+ }
+
+ /**
+ * Set the abbreviated month names. The value should be
+ * an array of string starting with Jan and ends in Dec.
+ * For example,
+ * array("Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ * "Jul", "Aug", "Sep","Oct","Nov","Dec");
+ * @param array month names.
+ */
+ function setAbbreviatedMonthNames($value)
+ {
+ $this->data['monthNames']['format']['abbreviated'] = $value;
+ }
+
+ /**
+ * A one-dimensional array of type String containing the
+ * culture-specific full names of the months. The array for
+ * InvariantInfo contains "January", "February", "March", "April",
+ * "May", "June", "July", "August", "September", "October", "November",
+ * and "December"
+ * @return array month names.
+ */
+ function getMonthNames()
+ {
+ return $this->data['monthNames']['format']['wide'];
+ }
+
+ /**
+ * Set the month names. The value should be
+ * an array of string starting with Janurary and ends in December.
+ * For example,
+ * array("January", "February", "March", "April", "May", "June",
+ * "July", "August", "September","October","November","December");
+ * @param array month names.
+ */
+ function setMonthNames($value)
+ {
+ $this->data['monthNames']['format']['wide'] = $value;
+ }
+
+ /**
+ * A string containing the name of the era.
+ * @param int era The integer representing the era.
+ * @return string the era name.
+ */
+ function getEra($era)
+ {
+ $eraName = $this->data['eras']['abbreviated'];
+ return $eraName[$era];
+ }
+
+ /**
+ * The string designator for hours that are "ante meridiem" (before noon).
+ * The default for InvariantInfo is "AM".
+ * @return string AM designator.
+ */
+ function getAMDesignator()
+ {
+ $result = $this->getAMPMMarkers();
+ return $result[0];
+ }
+
+ /**
+ * Set the AM Designator. For example, 'AM'.
+ * @param string AM designator.
+ */
+ function setAMDesignator($value)
+ {
+ $markers = $this->getAMPMMarkers();
+ $markers[0] = $value;
+ $this->setAMPMMarkers($markers);
+ }
+
+ /**
+ * The string designator for hours that are "post meridiem" (after noon).
+ * The default for InvariantInfo is "PM".
+ * @return string PM designator.
+ */
+ function getPMDesignator()
+ {
+ $result = $this->getAMPMMarkers();
+ return $result[1];
+ }
+
+ /**
+ * Set the PM Designator. For example, 'PM'.
+ * @param string PM designator.
+ */
+ function setPMDesignator($value)
+ {
+ $markers = $this->getAMPMMarkers();
+ $markers[1] = $value;
+ $this->setAMPMMarkers($markers);
+ }
+
+ /**
+ * Get the AM and PM markers array.
+ * Default InvariantInfo for AM and PM is array('AM','PM');
+ * @return array AM and PM markers
+ */
+ function getAMPMMarkers()
+ {
+ return $this->data['AmPmMarkers'];
+ }
+
+ /**
+ * Set the AM and PM markers array.
+ * For example array('AM','PM');
+ * @param array AM and PM markers
+ */
+ function setAMPMMarkers($value)
+ {
+ $this->data['AmPmMarkers'] = $value;
+ }
+
+ /**
+ * Returns the full time pattern "HH:mm:ss z" (default).
+ * This is culture sensitive.
+ * @return string pattern "HH:mm:ss z".
+ */
+ function getFullTimePattern()
+ {
+ return $this->data['DateTimePatterns'][0];
+ }
+
+ /**
+ * Returns the long time pattern "HH:mm:ss z" (default).
+ * This is culture sensitive.
+ * @return string pattern "HH:mm:ss z".
+ */
+ function getLongTimePattern()
+ {
+ return $this->data['DateTimePatterns'][1];
+ }
+
+ /**
+ * Returns the medium time pattern "HH:mm:ss" (default).
+ * This is culture sensitive.
+ * @return string pattern "HH:mm:ss".
+ */
+ function getMediumTimePattern()
+ {
+ return $this->data['DateTimePatterns'][2];
+ }
+
+ /**
+ * Returns the short time pattern "HH:mm" (default).
+ * This is culture sensitive.
+ * @return string pattern "HH:mm".
+ */
+ function getShortTimePattern()
+ {
+ return $this->data['DateTimePatterns'][3];
+ }
+
+ /**
+ * Returns the full date pattern "EEEE, yyyy MMMM dd" (default).
+ * This is culture sensitive.
+ * @return string pattern "EEEE, yyyy MMMM dd".
+ */
+ function getFullDatePattern()
+ {
+ return $this->data['DateTimePatterns'][4];
+ }
+
+ /**
+ * Returns the long date pattern "yyyy MMMM d" (default).
+ * This is culture sensitive.
+ * @return string pattern "yyyy MMMM d".
+ */
+ function getLongDatePattern()
+ {
+ return $this->data['DateTimePatterns'][5];
+ }
+
+ /**
+ * Returns the medium date pattern "yyyy MMMM d" (default).
+ * This is culture sensitive.
+ * @return string pattern "yyyy MMM d".
+ */
+ function getMediumDatePattern()
+ {
+ return $this->data['DateTimePatterns'][6];
+ }
+
+ /**
+ * Returns the short date pattern "yy/MM/dd" (default).
+ * This is culture sensitive.
+ * @return string pattern "yy/MM/dd".
+ */
+ function getShortDatePattern()
+ {
+ return $this->data['DateTimePatterns'][7];
+ }
+
+ /**
+ * Returns the date time order pattern, "{1} {0}" (default).
+ * This is culture sensitive.
+ * @return string pattern "{1} {0}".
+ */
+ function getDateTimeOrderPattern()
+ {
+ return $this->data['DateTimePatterns'][8];
+ }
+
+ /**
+ * Formats the date and time in a culture sensitive paterrn.
+ * The default is "Date Time".
+ * @return string date and time formated
+ */
+ function formatDateTime($date, $time)
+ {
+ $pattern = $this->getDateTimeOrderPattern();
+ return str_replace(array('{0}','{1}'), array($time, $date), $pattern);
+ }
+
+}
diff --git a/framework/I18N/core/Gettext/MO.php b/framework/I18N/core/Gettext/MO.php
index 2a97aee7..4b34034e 100644
--- a/framework/I18N/core/Gettext/MO.php
+++ b/framework/I18N/core/Gettext/MO.php
@@ -1,355 +1,355 @@
-
- * @version $Revision: 1.3 $ $Date: 2005/08/27 03:21:12 $
- * @package System.I18N.core
- */
-
-
-// +----------------------------------------------------------------------+
-// | PEAR :: File :: Gettext :: MO |
-// +----------------------------------------------------------------------+
-// | This source file is subject to version 3.0 of the PHP license, |
-// | that is available at http://www.php.net/license/3_0.txt |
-// | If you did not receive a copy of the PHP license and are unable |
-// | to obtain it through the world-wide-web, please send a note to |
-// | license@php.net so we can mail you a copy immediately. |
-// +----------------------------------------------------------------------+
-// | Copyright (c) 2004 Michael Wallner |
-// +----------------------------------------------------------------------+
-//
-// $Id$
-
-/**
- * File::Gettext::MO
- *
- * @author Michael Wallner
- * @license PHP License
- */
-
-require_once dirname(__FILE__).'/TGettext.php';
-
-/**
- * File_Gettext_MO
- *
- * GNU MO file reader and writer.
- *
- * @author Michael Wallner
- * @version $Revision: 1.3 $
- * @access public
- * @package System.I18N.core
- */
-class TGettext_MO extends TGettext
-{
- /**
- * file handle
- *
- * @access private
- * @var resource
- */
- protected $_handle = null;
-
- /**
- * big endianess
- *
- * Whether to write with big endian byte order.
- *
- * @access public
- * @var bool
- */
- protected $writeBigEndian = false;
-
- /**
- * Constructor
- *
- * @access public
- * @return object File_Gettext_MO
- * @param string $file path to GNU MO file
- */
- function TGettext_MO($file = '')
- {
- $this->file = $file;
- }
-
- /**
- * _read
- *
- * @access private
- * @return mixed
- * @param int $bytes
- */
- function _read($bytes = 1)
- {
- if (0 < $bytes = abs($bytes)) {
- return fread($this->_handle, $bytes);
- }
- return null;
- }
-
- /**
- * _readInt
- *
- * @access private
- * @return int
- * @param bool $bigendian
- */
- function _readInt($bigendian = false)
- {
- //unpack returns a reference????
- $unpacked = unpack($bigendian ? 'N' : 'V', $this->_read(4));
- return array_shift($unpacked);
- }
-
- /**
- * _writeInt
- *
- * @access private
- * @return int
- * @param int $int
- */
- function _writeInt($int)
- {
- return $this->_write(pack($this->writeBigEndian ? 'N' : 'V', (int) $int));
- }
-
- /**
- * _write
- *
- * @access private
- * @return int
- * @param string $data
- */
- function _write($data)
- {
- return fwrite($this->_handle, $data);
- }
-
- /**
- * _writeStr
- *
- * @access private
- * @return int
- * @param string $string
- */
- function _writeStr($string)
- {
- return $this->_write($string . "\0");
- }
-
- /**
- * _readStr
- *
- * @access private
- * @return string
- * @param array $params associative array with offset and length
- * of the string
- */
- function _readStr($params)
- {
- fseek($this->_handle, $params['offset']);
- return $this->_read($params['length']);
- }
-
- /**
- * Load MO file
- *
- * @access public
- * @return mixed Returns true on success or PEAR_Error on failure.
- * @param string $file
- */
- function load($file = null)
- {
- if (!isset($file)) {
- $file = $this->file;
- }
-
- // open MO file
- if (!is_resource($this->_handle = @fopen($file, 'rb'))) {
- return false;
- }
- // lock MO file shared
- if (!@flock($this->_handle, LOCK_SH)) {
- @fclose($this->_handle);
- return false;
- }
-
- // read (part of) magic number from MO file header and define endianess
-
- //unpack returns a reference????
- $unpacked = unpack('c', $this->_read(4));
- switch ($magic = array_shift($unpacked))
- {
- case -34:
- $be = false;
- break;
-
- case -107:
- $be = true;
- break;
-
- default:
- return false;
- }
-
- // check file format revision - we currently only support 0
- if (0 !== ($_rev = $this->_readInt($be))) {
- return false;
- }
-
- // count of strings in this file
- $count = $this->_readInt($be);
-
- // offset of hashing table of the msgids
- $offset_original = $this->_readInt($be);
- // offset of hashing table of the msgstrs
- $offset_translat = $this->_readInt($be);
-
- // move to msgid hash table
- fseek($this->_handle, $offset_original);
- // read lengths and offsets of msgids
- $original = array();
- for ($i = 0; $i < $count; $i++) {
- $original[$i] = array(
- 'length' => $this->_readInt($be),
- 'offset' => $this->_readInt($be)
- );
- }
-
- // move to msgstr hash table
- fseek($this->_handle, $offset_translat);
- // read lengths and offsets of msgstrs
- $translat = array();
- for ($i = 0; $i < $count; $i++) {
- $translat[$i] = array(
- 'length' => $this->_readInt($be),
- 'offset' => $this->_readInt($be)
- );
- }
-
- // read all
- for ($i = 0; $i < $count; $i++) {
- $this->strings[$this->_readStr($original[$i])] =
- $this->_readStr($translat[$i]);
- }
-
- // done
- @flock($this->_handle, LOCK_UN);
- @fclose($this->_handle);
- $this->_handle = null;
-
- // check for meta info
- if (isset($this->strings[''])) {
- $this->meta = parent::meta2array($this->strings['']);
- unset($this->strings['']);
- }
-
- return true;
- }
-
- /**
- * Save MO file
- *
- * @access public
- * @return mixed Returns true on success or PEAR_Error on failure.
- * @param string $file
- */
- function save($file = null)
- {
- if (!isset($file)) {
- $file = $this->file;
- }
-
- // open MO file
- if (!is_resource($this->_handle = @fopen($file, 'wb'))) {
- return false;
- }
- // lock MO file exclusively
- if (!@flock($this->_handle, LOCK_EX)) {
- @fclose($this->_handle);
- return false;
- }
-
- // write magic number
- if ($this->writeBigEndian) {
- $this->_write(pack('c*', 0x95, 0x04, 0x12, 0xde));
- } else {
- $this->_write(pack('c*', 0xde, 0x12, 0x04, 0x95));
- }
-
- // write file format revision
- $this->_writeInt(0);
-
- $count = count($this->strings) + ($meta = (count($this->meta) ? 1 : 0));
- // write count of strings
- $this->_writeInt($count);
-
- $offset = 28;
- // write offset of orig. strings hash table
- $this->_writeInt($offset);
-
- $offset += ($count * 8);
- // write offset transl. strings hash table
- $this->_writeInt($offset);
-
- // write size of hash table (we currently ommit the hash table)
- $this->_writeInt(0);
-
- $offset += ($count * 8);
- // write offset of hash table
- $this->_writeInt($offset);
-
- // unshift meta info
- if ($this->meta) {
- $meta = '';
- foreach ($this->meta as $key => $val) {
- $meta .= $key . ': ' . $val . "\n";
- }
- $strings = array('' => $meta) + $this->strings;
- } else {
- $strings = $this->strings;
- }
-
- // write offsets for original strings
- foreach (array_keys($strings) as $o) {
- $len = strlen($o);
- $this->_writeInt($len);
- $this->_writeInt($offset);
- $offset += $len + 1;
- }
-
- // write offsets for translated strings
- foreach ($strings as $t) {
- $len = strlen($t);
- $this->_writeInt($len);
- $this->_writeInt($offset);
- $offset += $len + 1;
- }
-
- // write original strings
- foreach (array_keys($strings) as $o) {
- $this->_writeStr($o);
- }
-
- // write translated strings
- foreach ($strings as $t) {
- $this->_writeStr($t);
- }
-
- // done
- @flock($this->_handle, LOCK_UN);
- @fclose($this->_handle);
- chmod($file,PRADO_CHMOD);
- return true;
- }
-}
+
+ * @version $Revision: 1.3 $ $Date: 2005/08/27 03:21:12 $
+ * @package System.I18N.core
+ */
+
+
+// +----------------------------------------------------------------------+
+// | PEAR :: File :: Gettext :: MO |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is available at http://www.php.net/license/3_0.txt |
+// | If you did not receive a copy of the PHP license and are unable |
+// | to obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 2004 Michael Wallner |
+// +----------------------------------------------------------------------+
+//
+// $Id$
+
+/**
+ * File::Gettext::MO
+ *
+ * @author Michael Wallner
+ * @license PHP License
+ */
+
+require_once dirname(__FILE__).'/TGettext.php';
+
+/**
+ * File_Gettext_MO
+ *
+ * GNU MO file reader and writer.
+ *
+ * @author Michael Wallner
+ * @version $Revision: 1.3 $
+ * @access public
+ * @package System.I18N.core
+ */
+class TGettext_MO extends TGettext
+{
+ /**
+ * file handle
+ *
+ * @access private
+ * @var resource
+ */
+ protected $_handle = null;
+
+ /**
+ * big endianess
+ *
+ * Whether to write with big endian byte order.
+ *
+ * @access public
+ * @var bool
+ */
+ protected $writeBigEndian = false;
+
+ /**
+ * Constructor
+ *
+ * @access public
+ * @return object File_Gettext_MO
+ * @param string $file path to GNU MO file
+ */
+ function TGettext_MO($file = '')
+ {
+ $this->file = $file;
+ }
+
+ /**
+ * _read
+ *
+ * @access private
+ * @return mixed
+ * @param int $bytes
+ */
+ function _read($bytes = 1)
+ {
+ if (0 < $bytes = abs($bytes)) {
+ return fread($this->_handle, $bytes);
+ }
+ return null;
+ }
+
+ /**
+ * _readInt
+ *
+ * @access private
+ * @return int
+ * @param bool $bigendian
+ */
+ function _readInt($bigendian = false)
+ {
+ //unpack returns a reference????
+ $unpacked = unpack($bigendian ? 'N' : 'V', $this->_read(4));
+ return array_shift($unpacked);
+ }
+
+ /**
+ * _writeInt
+ *
+ * @access private
+ * @return int
+ * @param int $int
+ */
+ function _writeInt($int)
+ {
+ return $this->_write(pack($this->writeBigEndian ? 'N' : 'V', (int) $int));
+ }
+
+ /**
+ * _write
+ *
+ * @access private
+ * @return int
+ * @param string $data
+ */
+ function _write($data)
+ {
+ return fwrite($this->_handle, $data);
+ }
+
+ /**
+ * _writeStr
+ *
+ * @access private
+ * @return int
+ * @param string $string
+ */
+ function _writeStr($string)
+ {
+ return $this->_write($string . "\0");
+ }
+
+ /**
+ * _readStr
+ *
+ * @access private
+ * @return string
+ * @param array $params associative array with offset and length
+ * of the string
+ */
+ function _readStr($params)
+ {
+ fseek($this->_handle, $params['offset']);
+ return $this->_read($params['length']);
+ }
+
+ /**
+ * Load MO file
+ *
+ * @access public
+ * @return mixed Returns true on success or PEAR_Error on failure.
+ * @param string $file
+ */
+ function load($file = null)
+ {
+ if (!isset($file)) {
+ $file = $this->file;
+ }
+
+ // open MO file
+ if (!is_resource($this->_handle = @fopen($file, 'rb'))) {
+ return false;
+ }
+ // lock MO file shared
+ if (!@flock($this->_handle, LOCK_SH)) {
+ @fclose($this->_handle);
+ return false;
+ }
+
+ // read (part of) magic number from MO file header and define endianess
+
+ //unpack returns a reference????
+ $unpacked = unpack('c', $this->_read(4));
+ switch ($magic = array_shift($unpacked))
+ {
+ case -34:
+ $be = false;
+ break;
+
+ case -107:
+ $be = true;
+ break;
+
+ default:
+ return false;
+ }
+
+ // check file format revision - we currently only support 0
+ if (0 !== ($_rev = $this->_readInt($be))) {
+ return false;
+ }
+
+ // count of strings in this file
+ $count = $this->_readInt($be);
+
+ // offset of hashing table of the msgids
+ $offset_original = $this->_readInt($be);
+ // offset of hashing table of the msgstrs
+ $offset_translat = $this->_readInt($be);
+
+ // move to msgid hash table
+ fseek($this->_handle, $offset_original);
+ // read lengths and offsets of msgids
+ $original = array();
+ for ($i = 0; $i < $count; $i++) {
+ $original[$i] = array(
+ 'length' => $this->_readInt($be),
+ 'offset' => $this->_readInt($be)
+ );
+ }
+
+ // move to msgstr hash table
+ fseek($this->_handle, $offset_translat);
+ // read lengths and offsets of msgstrs
+ $translat = array();
+ for ($i = 0; $i < $count; $i++) {
+ $translat[$i] = array(
+ 'length' => $this->_readInt($be),
+ 'offset' => $this->_readInt($be)
+ );
+ }
+
+ // read all
+ for ($i = 0; $i < $count; $i++) {
+ $this->strings[$this->_readStr($original[$i])] =
+ $this->_readStr($translat[$i]);
+ }
+
+ // done
+ @flock($this->_handle, LOCK_UN);
+ @fclose($this->_handle);
+ $this->_handle = null;
+
+ // check for meta info
+ if (isset($this->strings[''])) {
+ $this->meta = parent::meta2array($this->strings['']);
+ unset($this->strings['']);
+ }
+
+ return true;
+ }
+
+ /**
+ * Save MO file
+ *
+ * @access public
+ * @return mixed Returns true on success or PEAR_Error on failure.
+ * @param string $file
+ */
+ function save($file = null)
+ {
+ if (!isset($file)) {
+ $file = $this->file;
+ }
+
+ // open MO file
+ if (!is_resource($this->_handle = @fopen($file, 'wb'))) {
+ return false;
+ }
+ // lock MO file exclusively
+ if (!@flock($this->_handle, LOCK_EX)) {
+ @fclose($this->_handle);
+ return false;
+ }
+
+ // write magic number
+ if ($this->writeBigEndian) {
+ $this->_write(pack('c*', 0x95, 0x04, 0x12, 0xde));
+ } else {
+ $this->_write(pack('c*', 0xde, 0x12, 0x04, 0x95));
+ }
+
+ // write file format revision
+ $this->_writeInt(0);
+
+ $count = count($this->strings) + ($meta = (count($this->meta) ? 1 : 0));
+ // write count of strings
+ $this->_writeInt($count);
+
+ $offset = 28;
+ // write offset of orig. strings hash table
+ $this->_writeInt($offset);
+
+ $offset += ($count * 8);
+ // write offset transl. strings hash table
+ $this->_writeInt($offset);
+
+ // write size of hash table (we currently ommit the hash table)
+ $this->_writeInt(0);
+
+ $offset += ($count * 8);
+ // write offset of hash table
+ $this->_writeInt($offset);
+
+ // unshift meta info
+ if ($this->meta) {
+ $meta = '';
+ foreach ($this->meta as $key => $val) {
+ $meta .= $key . ': ' . $val . "\n";
+ }
+ $strings = array('' => $meta) + $this->strings;
+ } else {
+ $strings = $this->strings;
+ }
+
+ // write offsets for original strings
+ foreach (array_keys($strings) as $o) {
+ $len = strlen($o);
+ $this->_writeInt($len);
+ $this->_writeInt($offset);
+ $offset += $len + 1;
+ }
+
+ // write offsets for translated strings
+ foreach ($strings as $t) {
+ $len = strlen($t);
+ $this->_writeInt($len);
+ $this->_writeInt($offset);
+ $offset += $len + 1;
+ }
+
+ // write original strings
+ foreach (array_keys($strings) as $o) {
+ $this->_writeStr($o);
+ }
+
+ // write translated strings
+ foreach ($strings as $t) {
+ $this->_writeStr($t);
+ }
+
+ // done
+ @flock($this->_handle, LOCK_UN);
+ @fclose($this->_handle);
+ chmod($file,PRADO_CHMOD);
+ return true;
+ }
+}
diff --git a/framework/I18N/core/Gettext/PO.php b/framework/I18N/core/Gettext/PO.php
index 54fe10e3..57028f6d 100644
--- a/framework/I18N/core/Gettext/PO.php
+++ b/framework/I18N/core/Gettext/PO.php
@@ -1,160 +1,160 @@
-
- * @version $Revision: 1.2 $ $Date: 2005/01/05 03:15:14 $
- * @package System.I18N.core
- */
-
-// +----------------------------------------------------------------------+
-// | PEAR :: File :: Gettext :: PO |
-// +----------------------------------------------------------------------+
-// | This source file is subject to version 3.0 of the PHP license, |
-// | that is available at http://www.php.net/license/3_0.txt |
-// | If you did not receive a copy of the PHP license and are unable |
-// | to obtain it through the world-wide-web, please send a note to |
-// | license@php.net so we can mail you a copy immediately. |
-// +----------------------------------------------------------------------+
-// | Copyright (c) 2004 Michael Wallner |
-// +----------------------------------------------------------------------+
-//
-// $Id$
-
-/**
- * File::Gettext::PO
- *
- * @author Michael Wallner
- * @license PHP License
- */
-
-require_once dirname(__FILE__).'/TGettext.php';
-
-/**
- * File_Gettext_PO
- *
- * GNU PO file reader and writer.
- *
- * @author Michael Wallner
- * @version $Revision: 1.2 $
- * @access public
- * @package System.I18N.core
- */
-class TGettext_PO extends TGettext
-{
- /**
- * Constructor
- *
- * @access public
- * @return object File_Gettext_PO
- * @param string path to GNU PO file
- */
- function TGettext_PO($file = '')
- {
- $this->file = $file;
- }
-
- /**
- * Load PO file
- *
- * @access public
- * @return mixed Returns true on success or PEAR_Error on failure.
- * @param string $file
- */
- function load($file = null)
- {
- if (!isset($file)) {
- $file = $this->file;
- }
-
- // load file
- if (!$contents = @file($file)) {
- return false;
- }
- $contents = implode('', $contents);
-
- // match all msgid/msgstr entries
- $matched = preg_match_all(
- '/(msgid\s+("([^"]|\\\\")*?"\s*)+)\s+' .
- '(msgstr\s+("([^"]|\\\\")*?"\s*)+)/',
- $contents, $matches
- );
- unset($contents);
-
- if (!$matched) {
- return false;
- }
-
- // get all msgids and msgtrs
- for ($i = 0; $i < $matched; $i++) {
- $msgid = preg_replace(
- '/\s*msgid\s*"(.*)"\s*/s', '\\1', $matches[1][$i]);
- $msgstr= preg_replace(
- '/\s*msgstr\s*"(.*)"\s*/s', '\\1', $matches[4][$i]);
- $this->strings[parent::prepare($msgid)] = parent::prepare($msgstr);
- }
-
- // check for meta info
- if (isset($this->strings[''])) {
- $this->meta = parent::meta2array($this->strings['']);
- unset($this->strings['']);
- }
-
- return true;
- }
-
- /**
- * Save PO file
- *
- * @access public
- * @return mixed Returns true on success or PEAR_Error on failure.
- * @param string $file
- */
- function save($file = null)
- {
- if (!isset($file)) {
- $file = $this->file;
- }
-
- // open PO file
- if (!is_resource($fh = @fopen($file, 'w'))) {
- return false;
- }
-
- // lock PO file exclusively
- if (!flock($fh, LOCK_EX)) {
- fclose($fh);
- return false;
- }
- // write meta info
- if (count($this->meta)) {
- $meta = 'msgid ""' . "\nmsgstr " . '""' . "\n";
- foreach ($this->meta as $k => $v) {
- $meta .= '"' . $k . ': ' . $v . '\n"' . "\n";
- }
- fwrite($fh, $meta . "\n");
- }
- // write strings
- foreach ($this->strings as $o => $t) {
- fwrite($fh,
- 'msgid "' . parent::prepare($o, true) . '"' . "\n" .
- 'msgstr "' . parent::prepare($t, true) . '"' . "\n\n"
- );
- }
-
- //done
- @flock($fh, LOCK_UN);
- @fclose($fh);
- chmod($file,PRADO_CHMOD);
- return true;
- }
-}
+
+ * @version $Revision: 1.2 $ $Date: 2005/01/05 03:15:14 $
+ * @package System.I18N.core
+ */
+
+// +----------------------------------------------------------------------+
+// | PEAR :: File :: Gettext :: PO |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is available at http://www.php.net/license/3_0.txt |
+// | If you did not receive a copy of the PHP license and are unable |
+// | to obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 2004 Michael Wallner |
+// +----------------------------------------------------------------------+
+//
+// $Id$
+
+/**
+ * File::Gettext::PO
+ *
+ * @author Michael Wallner
+ * @license PHP License
+ */
+
+require_once dirname(__FILE__).'/TGettext.php';
+
+/**
+ * File_Gettext_PO
+ *
+ * GNU PO file reader and writer.
+ *
+ * @author Michael Wallner
+ * @version $Revision: 1.2 $
+ * @access public
+ * @package System.I18N.core
+ */
+class TGettext_PO extends TGettext
+{
+ /**
+ * Constructor
+ *
+ * @access public
+ * @return object File_Gettext_PO
+ * @param string path to GNU PO file
+ */
+ function TGettext_PO($file = '')
+ {
+ $this->file = $file;
+ }
+
+ /**
+ * Load PO file
+ *
+ * @access public
+ * @return mixed Returns true on success or PEAR_Error on failure.
+ * @param string $file
+ */
+ function load($file = null)
+ {
+ if (!isset($file)) {
+ $file = $this->file;
+ }
+
+ // load file
+ if (!$contents = @file($file)) {
+ return false;
+ }
+ $contents = implode('', $contents);
+
+ // match all msgid/msgstr entries
+ $matched = preg_match_all(
+ '/(msgid\s+("([^"]|\\\\")*?"\s*)+)\s+' .
+ '(msgstr\s+("([^"]|\\\\")*?"\s*)+)/',
+ $contents, $matches
+ );
+ unset($contents);
+
+ if (!$matched) {
+ return false;
+ }
+
+ // get all msgids and msgtrs
+ for ($i = 0; $i < $matched; $i++) {
+ $msgid = preg_replace(
+ '/\s*msgid\s*"(.*)"\s*/s', '\\1', $matches[1][$i]);
+ $msgstr= preg_replace(
+ '/\s*msgstr\s*"(.*)"\s*/s', '\\1', $matches[4][$i]);
+ $this->strings[parent::prepare($msgid)] = parent::prepare($msgstr);
+ }
+
+ // check for meta info
+ if (isset($this->strings[''])) {
+ $this->meta = parent::meta2array($this->strings['']);
+ unset($this->strings['']);
+ }
+
+ return true;
+ }
+
+ /**
+ * Save PO file
+ *
+ * @access public
+ * @return mixed Returns true on success or PEAR_Error on failure.
+ * @param string $file
+ */
+ function save($file = null)
+ {
+ if (!isset($file)) {
+ $file = $this->file;
+ }
+
+ // open PO file
+ if (!is_resource($fh = @fopen($file, 'w'))) {
+ return false;
+ }
+
+ // lock PO file exclusively
+ if (!flock($fh, LOCK_EX)) {
+ fclose($fh);
+ return false;
+ }
+ // write meta info
+ if (count($this->meta)) {
+ $meta = 'msgid ""' . "\nmsgstr " . '""' . "\n";
+ foreach ($this->meta as $k => $v) {
+ $meta .= '"' . $k . ': ' . $v . '\n"' . "\n";
+ }
+ fwrite($fh, $meta . "\n");
+ }
+ // write strings
+ foreach ($this->strings as $o => $t) {
+ fwrite($fh,
+ 'msgid "' . parent::prepare($o, true) . '"' . "\n" .
+ 'msgstr "' . parent::prepare($t, true) . '"' . "\n\n"
+ );
+ }
+
+ //done
+ @flock($fh, LOCK_UN);
+ @fclose($fh);
+ chmod($file,PRADO_CHMOD);
+ return true;
+ }
+}
diff --git a/framework/I18N/core/Gettext/TGettext.php b/framework/I18N/core/Gettext/TGettext.php
index 39e5d07e..4ca7fadb 100644
--- a/framework/I18N/core/Gettext/TGettext.php
+++ b/framework/I18N/core/Gettext/TGettext.php
@@ -1,286 +1,286 @@
-
- * @version $Revision: 1.4 $ $Date: 2005/01/09 23:36:23 $
- * @package System.I18N.core
- */
-
-// +----------------------------------------------------------------------+
-// | PEAR :: File :: Gettext |
-// +----------------------------------------------------------------------+
-// | This source file is subject to version 3.0 of the PHP license, |
-// | that is available at http://www.php.net/license/3_0.txt |
-// | If you did not receive a copy of the PHP license and are unable |
-// | to obtain it through the world-wide-web, please send a note to |
-// | license@php.net so we can mail you a copy immediately. |
-// +----------------------------------------------------------------------+
-// | Copyright (c) 2004 Michael Wallner |
-// +----------------------------------------------------------------------+
-//
-// $Id$
-
-/**
- * File::Gettext
- *
- * @author Michael Wallner
- * @license PHP License
- */
-
-/**
- * Use PHPs builtin error messages
- */
-//ini_set('track_errors', true);
-
-/**
- * File_Gettext
- *
- * GNU gettext file reader and writer.
- *
- * #################################################################
- * # All protected members of this class are public in its childs. #
- * #################################################################
- *
- * @author Michael Wallner
- * @version $Revision: 1.4 $
- * @access public
- * @package System.I18N.core
- */
-class TGettext
-{
- /**
- * strings
- *
- * associative array with all [msgid => msgstr] entries
- *
- * @access protected
- * @var array
- */
- protected $strings = array();
-
- /**
- * meta
- *
- * associative array containing meta
- * information like project name or content type
- *
- * @access protected
- * @var array
- */
- protected $meta = array();
-
- /**
- * file path
- *
- * @access protected
- * @var string
- */
- protected $file = '';
-
- /**
- * Factory
- *
- * @static
- * @access public
- * @return object Returns File_Gettext_PO or File_Gettext_MO on success
- * or PEAR_Error on failure.
- * @param string $format MO or PO
- * @param string $file path to GNU gettext file
- */
- function factory($format, $file = '')
- {
- $format = strToUpper($format);
- $filename = dirname(__FILE__).'/'.$format.'.php';
- if(is_file($filename) == false)
- throw new Exception ("Class file $file not found");
-
- include_once $filename;
- $class = 'TGettext_' . $format;
-
- return new $class($file);
- }
-
- /**
- * poFile2moFile
- *
- * That's a simple fake of the 'msgfmt' console command. It reads the
- * contents of a GNU PO file and saves them to a GNU MO file.
- *
- * @static
- * @access public
- * @return mixed Returns true on success or PEAR_Error on failure.
- * @param string $pofile path to GNU PO file
- * @param string $mofile path to GNU MO file
- */
- function poFile2moFile($pofile, $mofile)
- {
- if (!is_file($pofile)) {
- throw new Exception("File $pofile doesn't exist.");
- }
-
- include_once dirname(__FILE__).'/PO.php';
-
- $PO = new TGettext_PO($pofile);
- if (true !== ($e = $PO->load())) {
- return $e;
- }
-
- $MO = $PO->toMO();
- if (true !== ($e = $MO->save($mofile))) {
- return $e;
- }
- unset($PO, $MO);
-
- return true;
- }
-
- /**
- * prepare
- *
- * @static
- * @access protected
- * @return string
- * @param string $string
- * @param bool $reverse
- */
- function prepare($string, $reverse = false)
- {
- if ($reverse) {
- $smap = array('"', "\n", "\t", "\r");
- $rmap = array('\"', '\\n"' . "\n" . '"', '\\t', '\\r');
- return (string) str_replace($smap, $rmap, $string);
- } else {
- $string = preg_replace('/"\s+"/', '', $string);
- $smap = array('\\n', '\\r', '\\t', '\"');
- $rmap = array("\n", "\r", "\t", '"');
- return (string) str_replace($smap, $rmap, $string);
- }
- }
-
- /**
- * meta2array
- *
- * @static
- * @access public
- * @return array
- * @param string $meta
- */
- function meta2array($meta)
- {
- $array = array();
- foreach (explode("\n", $meta) as $info) {
- if ($info = trim($info)) {
- list($key, $value) = explode(':', $info, 2);
- $array[trim($key)] = trim($value);
- }
- }
- return $array;
- }
-
- /**
- * toArray
- *
- * Returns meta info and strings as an array of a structure like that:
- *
- * array(
- * 'meta' => array(
- * 'Content-Type' => 'text/plain; charset=iso-8859-1',
- * 'Last-Translator' => 'Michael Wallner ',
- * 'PO-Revision-Date' => '2004-07-21 17:03+0200',
- * 'Language-Team' => 'German ',
- * ),
- * 'strings' => array(
- * 'All rights reserved' => 'Alle Rechte vorbehalten',
- * 'Welcome' => 'Willkommen',
- * // ...
- * )
- * )
- *
- *
- * @see fromArray()
- * @access protected
- * @return array
- */
- function toArray()
- {
- return array('meta' => $this->meta, 'strings' => $this->strings);
- }
-
- /**
- * fromArray
- *
- * Assigns meta info and strings from an array of a structure like that:
- *
- * array(
- * 'meta' => array(
- * 'Content-Type' => 'text/plain; charset=iso-8859-1',
- * 'Last-Translator' => 'Michael Wallner ',
- * 'PO-Revision-Date' => date('Y-m-d H:iO'),
- * 'Language-Team' => 'German ',
- * ),
- * 'strings' => array(
- * 'All rights reserved' => 'Alle Rechte vorbehalten',
- * 'Welcome' => 'Willkommen',
- * // ...
- * )
- * )
- *
- *
- * @see toArray()
- * @access protected
- * @return bool
- * @param array $array
- */
- function fromArray($array)
- {
- if (!array_key_exists('strings', $array)) {
- if (count($array) != 2) {
- return false;
- } else {
- list($this->meta, $this->strings) = $array;
- }
- } else {
- $this->meta = @$array['meta'];
- $this->strings = @$array['strings'];
- }
- return true;
- }
-
- /**
- * toMO
- *
- * @access protected
- * @return object File_Gettext_MO
- */
- function toMO()
- {
- include_once dirname(__FILE__).'/MO.php';
- $MO = new TGettext_MO;
- $MO->fromArray($this->toArray());
- return $MO;
- }
-
- /**
- * toPO
- *
- * @access protected
- * @return object File_Gettext_PO
- */
- function toPO()
- {
- include_once dirname(__FILE__).'/PO.php';
- $PO = new TGettext_PO;
- $PO->fromArray($this->toArray());
- return $PO;
- }
-}
+
+ * @version $Revision: 1.4 $ $Date: 2005/01/09 23:36:23 $
+ * @package System.I18N.core
+ */
+
+// +----------------------------------------------------------------------+
+// | PEAR :: File :: Gettext |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is available at http://www.php.net/license/3_0.txt |
+// | If you did not receive a copy of the PHP license and are unable |
+// | to obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 2004 Michael Wallner |
+// +----------------------------------------------------------------------+
+//
+// $Id$
+
+/**
+ * File::Gettext
+ *
+ * @author Michael Wallner
+ * @license PHP License
+ */
+
+/**
+ * Use PHPs builtin error messages
+ */
+//ini_set('track_errors', true);
+
+/**
+ * File_Gettext
+ *
+ * GNU gettext file reader and writer.
+ *
+ * #################################################################
+ * # All protected members of this class are public in its childs. #
+ * #################################################################
+ *
+ * @author Michael Wallner
+ * @version $Revision: 1.4 $
+ * @access public
+ * @package System.I18N.core
+ */
+class TGettext
+{
+ /**
+ * strings
+ *
+ * associative array with all [msgid => msgstr] entries
+ *
+ * @access protected
+ * @var array
+ */
+ protected $strings = array();
+
+ /**
+ * meta
+ *
+ * associative array containing meta
+ * information like project name or content type
+ *
+ * @access protected
+ * @var array
+ */
+ protected $meta = array();
+
+ /**
+ * file path
+ *
+ * @access protected
+ * @var string
+ */
+ protected $file = '';
+
+ /**
+ * Factory
+ *
+ * @static
+ * @access public
+ * @return object Returns File_Gettext_PO or File_Gettext_MO on success
+ * or PEAR_Error on failure.
+ * @param string $format MO or PO
+ * @param string $file path to GNU gettext file
+ */
+ function factory($format, $file = '')
+ {
+ $format = strToUpper($format);
+ $filename = dirname(__FILE__).'/'.$format.'.php';
+ if(is_file($filename) == false)
+ throw new Exception ("Class file $file not found");
+
+ include_once $filename;
+ $class = 'TGettext_' . $format;
+
+ return new $class($file);
+ }
+
+ /**
+ * poFile2moFile
+ *
+ * That's a simple fake of the 'msgfmt' console command. It reads the
+ * contents of a GNU PO file and saves them to a GNU MO file.
+ *
+ * @static
+ * @access public
+ * @return mixed Returns true on success or PEAR_Error on failure.
+ * @param string $pofile path to GNU PO file
+ * @param string $mofile path to GNU MO file
+ */
+ function poFile2moFile($pofile, $mofile)
+ {
+ if (!is_file($pofile)) {
+ throw new Exception("File $pofile doesn't exist.");
+ }
+
+ include_once dirname(__FILE__).'/PO.php';
+
+ $PO = new TGettext_PO($pofile);
+ if (true !== ($e = $PO->load())) {
+ return $e;
+ }
+
+ $MO = $PO->toMO();
+ if (true !== ($e = $MO->save($mofile))) {
+ return $e;
+ }
+ unset($PO, $MO);
+
+ return true;
+ }
+
+ /**
+ * prepare
+ *
+ * @static
+ * @access protected
+ * @return string
+ * @param string $string
+ * @param bool $reverse
+ */
+ function prepare($string, $reverse = false)
+ {
+ if ($reverse) {
+ $smap = array('"', "\n", "\t", "\r");
+ $rmap = array('\"', '\\n"' . "\n" . '"', '\\t', '\\r');
+ return (string) str_replace($smap, $rmap, $string);
+ } else {
+ $string = preg_replace('/"\s+"/', '', $string);
+ $smap = array('\\n', '\\r', '\\t', '\"');
+ $rmap = array("\n", "\r", "\t", '"');
+ return (string) str_replace($smap, $rmap, $string);
+ }
+ }
+
+ /**
+ * meta2array
+ *
+ * @static
+ * @access public
+ * @return array
+ * @param string $meta
+ */
+ function meta2array($meta)
+ {
+ $array = array();
+ foreach (explode("\n", $meta) as $info) {
+ if ($info = trim($info)) {
+ list($key, $value) = explode(':', $info, 2);
+ $array[trim($key)] = trim($value);
+ }
+ }
+ return $array;
+ }
+
+ /**
+ * toArray
+ *
+ * Returns meta info and strings as an array of a structure like that:
+ *
+ * array(
+ * 'meta' => array(
+ * 'Content-Type' => 'text/plain; charset=iso-8859-1',
+ * 'Last-Translator' => 'Michael Wallner ',
+ * 'PO-Revision-Date' => '2004-07-21 17:03+0200',
+ * 'Language-Team' => 'German ',
+ * ),
+ * 'strings' => array(
+ * 'All rights reserved' => 'Alle Rechte vorbehalten',
+ * 'Welcome' => 'Willkommen',
+ * // ...
+ * )
+ * )
+ *
+ *
+ * @see fromArray()
+ * @access protected
+ * @return array
+ */
+ function toArray()
+ {
+ return array('meta' => $this->meta, 'strings' => $this->strings);
+ }
+
+ /**
+ * fromArray
+ *
+ * Assigns meta info and strings from an array of a structure like that:
+ *
+ * array(
+ * 'meta' => array(
+ * 'Content-Type' => 'text/plain; charset=iso-8859-1',
+ * 'Last-Translator' => 'Michael Wallner ',
+ * 'PO-Revision-Date' => date('Y-m-d H:iO'),
+ * 'Language-Team' => 'German ',
+ * ),
+ * 'strings' => array(
+ * 'All rights reserved' => 'Alle Rechte vorbehalten',
+ * 'Welcome' => 'Willkommen',
+ * // ...
+ * )
+ * )
+ *
+ *
+ * @see toArray()
+ * @access protected
+ * @return bool
+ * @param array $array
+ */
+ function fromArray($array)
+ {
+ if (!array_key_exists('strings', $array)) {
+ if (count($array) != 2) {
+ return false;
+ } else {
+ list($this->meta, $this->strings) = $array;
+ }
+ } else {
+ $this->meta = @$array['meta'];
+ $this->strings = @$array['strings'];
+ }
+ return true;
+ }
+
+ /**
+ * toMO
+ *
+ * @access protected
+ * @return object File_Gettext_MO
+ */
+ function toMO()
+ {
+ include_once dirname(__FILE__).'/MO.php';
+ $MO = new TGettext_MO;
+ $MO->fromArray($this->toArray());
+ return $MO;
+ }
+
+ /**
+ * toPO
+ *
+ * @access protected
+ * @return object File_Gettext_PO
+ */
+ function toPO()
+ {
+ include_once dirname(__FILE__).'/PO.php';
+ $PO = new TGettext_PO;
+ $PO->fromArray($this->toArray());
+ return $PO;
+ }
+}
diff --git a/framework/I18N/core/HTTPNegotiator.php b/framework/I18N/core/HTTPNegotiator.php
index 9199ba15..26b532b8 100644
--- a/framework/I18N/core/HTTPNegotiator.php
+++ b/framework/I18N/core/HTTPNegotiator.php
@@ -1,129 +1,129 @@
-
- * @version $Revision: 1.2 $ $Date: 2005/01/05 03:15:14 $
- * @package System.I18N.core
- */
-
-/**
- * Include the CultureInfo class.
- */
-require_once(dirname(__FILE__).'/CultureInfo.php');
-
-/**
- * HTTPNegotiator class.
- *
- * Get the language and charset information from the client browser.
- *
- * @author Xiang Wei Zhuo
- * @version v1.0, last update on Fri Dec 24 16:01:35 EST 2004
- * @package System.I18N.core
- */
-class HTTPNegotiator
-{
- /**
- * A list of languages accepted by the browser.
- * @var array
- */
- protected $languages;
-
- /**
- * A list of charsets accepted by the browser
- * @var array
- */
- protected $charsets;
-
- /**
- * Get a list of languages acceptable by the client browser
- * @return array languages ordered in the user browser preferences.
- */
- function getLanguages()
- {
- if($this->languages !== null) {
- return $this->languages;
- }
-
- $this->languages = array();
-
- if (!isset($_SERVER['HTTP_ACCEPT_LANGUAGE']))
- return $this->languages;
-
- //$basedir = CultureInfo::dataDir();
- //$ext = CultureInfo::fileExt();
- $info = new CultureInfo();
-
- foreach(explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']) as $lang)
- {
- // Cut off any q-value that might come after a semi-colon
- if ($pos = strpos($lang, ';'))
- $lang = trim(substr($lang, 0, $pos));
-
- if (strstr($lang, '-'))
- {
- $codes = explode('-',$lang);
- if($codes[0] == 'i')
- {
- // Language not listed in ISO 639 that are not variants
- // of any listed language, which can be registerd with the
- // i-prefix, such as i-cherokee
- if(count($codes)>1)
- $lang = $codes[1];
- }
- else
- {
- for($i = 0, $k = count($codes); $i<$k; ++$i)
- {
- if($i == 0)
- $lang = strtolower($codes[0]);
- else
- $lang .= '_'.strtoupper($codes[$i]);
- }
- }
- }
-
-
-
- if($info->validCulture($lang))
- $this->languages[] = $lang;
- }
-
- return $this->languages;
- }
-
- /**
- * Get a list of charsets acceptable by the client browser.
- * @return array list of charsets in preferable order.
- */
- function getCharsets()
- {
- if($this->charsets !== null) {
- return $this->charsets;
- }
-
- $this->charsets = array();
-
- if (!isset($_SERVER['HTTP_ACCEPT_CHARSET']))
- return $this->charsets;
-
- foreach (explode(',', $_SERVER['HTTP_ACCEPT_CHARSET']) as $charset)
- {
- if (!empty($charset))
- $this->charsets[] = preg_replace('/;.*/', '', $charset);
- }
-
- return $this->charsets;
- }
-}
-
+
+ * @version $Revision: 1.2 $ $Date: 2005/01/05 03:15:14 $
+ * @package System.I18N.core
+ */
+
+/**
+ * Include the CultureInfo class.
+ */
+require_once(dirname(__FILE__).'/CultureInfo.php');
+
+/**
+ * HTTPNegotiator class.
+ *
+ * Get the language and charset information from the client browser.
+ *
+ * @author Xiang Wei Zhuo
+ * @version v1.0, last update on Fri Dec 24 16:01:35 EST 2004
+ * @package System.I18N.core
+ */
+class HTTPNegotiator
+{
+ /**
+ * A list of languages accepted by the browser.
+ * @var array
+ */
+ protected $languages;
+
+ /**
+ * A list of charsets accepted by the browser
+ * @var array
+ */
+ protected $charsets;
+
+ /**
+ * Get a list of languages acceptable by the client browser
+ * @return array languages ordered in the user browser preferences.
+ */
+ function getLanguages()
+ {
+ if($this->languages !== null) {
+ return $this->languages;
+ }
+
+ $this->languages = array();
+
+ if (!isset($_SERVER['HTTP_ACCEPT_LANGUAGE']))
+ return $this->languages;
+
+ //$basedir = CultureInfo::dataDir();
+ //$ext = CultureInfo::fileExt();
+ $info = new CultureInfo();
+
+ foreach(explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']) as $lang)
+ {
+ // Cut off any q-value that might come after a semi-colon
+ if ($pos = strpos($lang, ';'))
+ $lang = trim(substr($lang, 0, $pos));
+
+ if (strstr($lang, '-'))
+ {
+ $codes = explode('-',$lang);
+ if($codes[0] == 'i')
+ {
+ // Language not listed in ISO 639 that are not variants
+ // of any listed language, which can be registerd with the
+ // i-prefix, such as i-cherokee
+ if(count($codes)>1)
+ $lang = $codes[1];
+ }
+ else
+ {
+ for($i = 0, $k = count($codes); $i<$k; ++$i)
+ {
+ if($i == 0)
+ $lang = strtolower($codes[0]);
+ else
+ $lang .= '_'.strtoupper($codes[$i]);
+ }
+ }
+ }
+
+
+
+ if($info->validCulture($lang))
+ $this->languages[] = $lang;
+ }
+
+ return $this->languages;
+ }
+
+ /**
+ * Get a list of charsets acceptable by the client browser.
+ * @return array list of charsets in preferable order.
+ */
+ function getCharsets()
+ {
+ if($this->charsets !== null) {
+ return $this->charsets;
+ }
+
+ $this->charsets = array();
+
+ if (!isset($_SERVER['HTTP_ACCEPT_CHARSET']))
+ return $this->charsets;
+
+ foreach (explode(',', $_SERVER['HTTP_ACCEPT_CHARSET']) as $charset)
+ {
+ if (!empty($charset))
+ $this->charsets[] = preg_replace('/;.*/', '', $charset);
+ }
+
+ return $this->charsets;
+ }
+}
+
diff --git a/framework/I18N/core/IMessageSource.php b/framework/I18N/core/IMessageSource.php
index 1d40bd73..f8263b97 100644
--- a/framework/I18N/core/IMessageSource.php
+++ b/framework/I18N/core/IMessageSource.php
@@ -1,122 +1,122 @@
-
- * @version $Revision: 1.3 $ $Date: 2005/01/09 22:15:32 $
- * @package System.I18N.core
- */
-
-/**
- * IMessageSource interface.
- *
- * All messages source used by MessageFormat must be of IMessageSource.
- * It defines a set of operations to add and retrive messages from the
- * message source. In addition, message source can load a particular
- * catalogue.
- *
- * @author Xiang Wei Zhuo
- * @version v1.0, last update on Fri Dec 24 17:40:19 EST 2004
- * @package System.I18N.core
- */
-interface IMessageSource
-{
- /**
- * Load the translation table for this particular catalogue.
- * The translation should be loaded in the following order.
- * # [1] call getCatalogeList($catalogue) to get a list of
- * variants for for the specified $catalogue.
- * # [2] for each of the variants, call getSource($variant)
- * to get the resource, could be a file or catalogue ID.
- * # [3] verify that this resource is valid by calling isValidSource($source)
- * # [4] try to get the messages from the cache
- * # [5] if a cache miss, call load($source) to load the message array
- * # [6] store the messages to cache.
- * # [7] continue with the foreach loop, e.g. goto [2].
- *
- * @param string a catalogue to load
- * @return boolean true if loaded, false otherwise.
- */
- function load($catalogue = 'messages');
-
- /**
- * Get the translation table. This includes all the loaded sections.
- * It must return a 2 level array of translation strings.
- * # "catalogue+variant" the catalogue and its variants.
- * # "source string" translation keys, and its translations.
- *
- * array('catalogue+variant' =>
- * array('source string' => 'target string', ...)
- * ...),
- * ...);
- *
- *
- * @return array 2 level array translation table.
- */
- function read();
-
- /**
- * Save the list of untranslated blocks to the translation source.
- * If the translation was not found, you should add those
- * strings to the translation source via the append() method.
- * @param string the catalogue to add to
- * @return boolean true if saved successfuly, false otherwise.
- */
- function save($catalogue='messages');
-
- /**
- * Add a untranslated message to the source. Need to call save()
- * to save the messages to source.
- * @param string message to add
- * @return void
- */
- function append($message);
-
- /**
- * Delete a particular message from the specified catalogue.
- * @param string the source message to delete.
- * @param string the catalogue to delete from.
- * @return boolean true if deleted, false otherwise.
- */
- function delete($message, $catalogue='messages');
-
- /**
- * Update the translation.
- * @param string the source string.
- * @param string the new translation string.
- * @param string comments
- * @param string the catalogue of the translation.
- * @return boolean true if translation was updated, false otherwise.
- */
- function update($text, $target, $comments, $catalogue='messages');
-
- /**
- * Returns a list of catalogue as key and all it variants as value.
- * @return array list of catalogues
- */
- function catalogues();
-
- /**
- * Set the culture for this particular message source.
- * @param string the Culture name.
- */
- function setCulture($culture);
-
- /**
- * Get the culture identifier for the source.
- * @return string culture identifier.
- */
- function getCulture();
-
-}
-
+
+ * @version $Revision: 1.3 $ $Date: 2005/01/09 22:15:32 $
+ * @package System.I18N.core
+ */
+
+/**
+ * IMessageSource interface.
+ *
+ * All messages source used by MessageFormat must be of IMessageSource.
+ * It defines a set of operations to add and retrive messages from the
+ * message source. In addition, message source can load a particular
+ * catalogue.
+ *
+ * @author Xiang Wei Zhuo
+ * @version v1.0, last update on Fri Dec 24 17:40:19 EST 2004
+ * @package System.I18N.core
+ */
+interface IMessageSource
+{
+ /**
+ * Load the translation table for this particular catalogue.
+ * The translation should be loaded in the following order.
+ * # [1] call getCatalogeList($catalogue) to get a list of
+ * variants for for the specified $catalogue.
+ * # [2] for each of the variants, call getSource($variant)
+ * to get the resource, could be a file or catalogue ID.
+ * # [3] verify that this resource is valid by calling isValidSource($source)
+ * # [4] try to get the messages from the cache
+ * # [5] if a cache miss, call load($source) to load the message array
+ * # [6] store the messages to cache.
+ * # [7] continue with the foreach loop, e.g. goto [2].
+ *
+ * @param string a catalogue to load
+ * @return boolean true if loaded, false otherwise.
+ */
+ function load($catalogue = 'messages');
+
+ /**
+ * Get the translation table. This includes all the loaded sections.
+ * It must return a 2 level array of translation strings.
+ * # "catalogue+variant" the catalogue and its variants.
+ * # "source string" translation keys, and its translations.
+ *
+ * array('catalogue+variant' =>
+ * array('source string' => 'target string', ...)
+ * ...),
+ * ...);
+ *
+ *
+ * @return array 2 level array translation table.
+ */
+ function read();
+
+ /**
+ * Save the list of untranslated blocks to the translation source.
+ * If the translation was not found, you should add those
+ * strings to the translation source via the append() method.
+ * @param string the catalogue to add to
+ * @return boolean true if saved successfuly, false otherwise.
+ */
+ function save($catalogue='messages');
+
+ /**
+ * Add a untranslated message to the source. Need to call save()
+ * to save the messages to source.
+ * @param string message to add
+ * @return void
+ */
+ function append($message);
+
+ /**
+ * Delete a particular message from the specified catalogue.
+ * @param string the source message to delete.
+ * @param string the catalogue to delete from.
+ * @return boolean true if deleted, false otherwise.
+ */
+ function delete($message, $catalogue='messages');
+
+ /**
+ * Update the translation.
+ * @param string the source string.
+ * @param string the new translation string.
+ * @param string comments
+ * @param string the catalogue of the translation.
+ * @return boolean true if translation was updated, false otherwise.
+ */
+ function update($text, $target, $comments, $catalogue='messages');
+
+ /**
+ * Returns a list of catalogue as key and all it variants as value.
+ * @return array list of catalogues
+ */
+ function catalogues();
+
+ /**
+ * Set the culture for this particular message source.
+ * @param string the Culture name.
+ */
+ function setCulture($culture);
+
+ /**
+ * Get the culture identifier for the source.
+ * @return string culture identifier.
+ */
+ function getCulture();
+
+}
+
diff --git a/framework/I18N/core/MessageCache.php b/framework/I18N/core/MessageCache.php
index d4f58f1d..44392b79 100644
--- a/framework/I18N/core/MessageCache.php
+++ b/framework/I18N/core/MessageCache.php
@@ -1,171 +1,171 @@
- $cacheDir,
- 'lifeTime' => $this->getLifeTime(),
- 'automaticSerialization' => true
- );
-
- $this->cache = new TCache_Lite($options);
- }
-
- /**
- * Get the cache life time.
- * @return int Cache life time.
- */
- public function getLifeTime()
- {
- return $this->lifetime;
- }
-
- /**
- * Set the cache life time.
- * @param int $time Cache life time.
- */
- public function setLifeTime($time)
- {
- $this->lifetime = (int)$time;
- }
-
- /**
- * Get the cache file ID based section and locale.
- * @param string $catalogue The translation section.
- * @param string $culture The translation locale, e.g. "en_AU".
- */
- protected function getID($catalogue, $culture)
- {
- return $catalogue.':'.$culture;
- }
-
- /**
- * Get the cache file GROUP based section and locale.
- * @param string $catalogue The translation section.
- * @param string $culture The translation locale, e.g. "en_AU".
- */
- protected function getGroup($catalogue, $culture)
- {
- return $catalogue.':'.get_class($this);
- }
-
- /**
- * Get the data from the cache.
- * @param string $catalogue The translation section.
- * @param string $culture The translation locale, e.g. "en_AU".
- * @param string $filename If the source is a file, this file's modified
- * time is newer than the cache's modified time, no cache hit.
- * @return mixed Boolean FALSE if no cache hit. Otherwise, translation
- * table data for the specified section and locale.
- */
- public function get($catalogue, $culture, $lastmodified=0)
- {
- $ID = $this->getID($catalogue, $culture);
- $group = $this->getGroup($catalogue, $culture);
-
- $this->cache->_setFileName($ID, $group);
-
- $cache = $this->cache->getCacheFile();
-
- if(is_file($cache) == false)
- return false;
-
-
- $lastmodified = (int)$lastmodified;
-
- if($lastmodified <= 0 || $lastmodified > filemtime($cache))
- return false;
-
- //echo '@@ Cache hit: "'.$ID.'" : "'.$group.'"';
- //echo "
\n";
-
- return $this->cache->get($ID, $group);
- }
-
- /**
- * Save the data to cache for the specified section and locale.
- * @param array $data The data to save.
- * @param string $catalogue The translation section.
- * @param string $culture The translation locale, e.g. "en_AU".
- */
- public function save($data, $catalogue, $culture)
- {
- $ID = $this->getID($catalogue, $culture);
- $group = $this->getGroup($catalogue, $culture);
-
- //echo '## Cache save: "'.$ID.'" : "'.$group.'"';
- //echo "
\n";
-
- return $this->cache->save($data, $ID, $group);
- }
-
- /**
- * Clean up the cache for the specified section and locale.
- * @param string $catalogue The translation section.
- * @param string $culture The translation locale, e.g. "en_AU".
- */
- public function clean($catalogue, $culture)
- {
- $group = $this->getGroup($catalogue, $culture);
- $this->cache->clean($group);
- }
-
- /**
- * Flush the cache. Deletes all the cache files.
- */
- public function clear()
- {
- $this->cache->clean();
- }
-
-}
-
-?>
+ $cacheDir,
+ 'lifeTime' => $this->getLifeTime(),
+ 'automaticSerialization' => true
+ );
+
+ $this->cache = new TCache_Lite($options);
+ }
+
+ /**
+ * Get the cache life time.
+ * @return int Cache life time.
+ */
+ public function getLifeTime()
+ {
+ return $this->lifetime;
+ }
+
+ /**
+ * Set the cache life time.
+ * @param int $time Cache life time.
+ */
+ public function setLifeTime($time)
+ {
+ $this->lifetime = (int)$time;
+ }
+
+ /**
+ * Get the cache file ID based section and locale.
+ * @param string $catalogue The translation section.
+ * @param string $culture The translation locale, e.g. "en_AU".
+ */
+ protected function getID($catalogue, $culture)
+ {
+ return $catalogue.':'.$culture;
+ }
+
+ /**
+ * Get the cache file GROUP based section and locale.
+ * @param string $catalogue The translation section.
+ * @param string $culture The translation locale, e.g. "en_AU".
+ */
+ protected function getGroup($catalogue, $culture)
+ {
+ return $catalogue.':'.get_class($this);
+ }
+
+ /**
+ * Get the data from the cache.
+ * @param string $catalogue The translation section.
+ * @param string $culture The translation locale, e.g. "en_AU".
+ * @param string $filename If the source is a file, this file's modified
+ * time is newer than the cache's modified time, no cache hit.
+ * @return mixed Boolean FALSE if no cache hit. Otherwise, translation
+ * table data for the specified section and locale.
+ */
+ public function get($catalogue, $culture, $lastmodified=0)
+ {
+ $ID = $this->getID($catalogue, $culture);
+ $group = $this->getGroup($catalogue, $culture);
+
+ $this->cache->_setFileName($ID, $group);
+
+ $cache = $this->cache->getCacheFile();
+
+ if(is_file($cache) == false)
+ return false;
+
+
+ $lastmodified = (int)$lastmodified;
+
+ if($lastmodified <= 0 || $lastmodified > filemtime($cache))
+ return false;
+
+ //echo '@@ Cache hit: "'.$ID.'" : "'.$group.'"';
+ //echo "
\n";
+
+ return $this->cache->get($ID, $group);
+ }
+
+ /**
+ * Save the data to cache for the specified section and locale.
+ * @param array $data The data to save.
+ * @param string $catalogue The translation section.
+ * @param string $culture The translation locale, e.g. "en_AU".
+ */
+ public function save($data, $catalogue, $culture)
+ {
+ $ID = $this->getID($catalogue, $culture);
+ $group = $this->getGroup($catalogue, $culture);
+
+ //echo '## Cache save: "'.$ID.'" : "'.$group.'"';
+ //echo "
\n";
+
+ return $this->cache->save($data, $ID, $group);
+ }
+
+ /**
+ * Clean up the cache for the specified section and locale.
+ * @param string $catalogue The translation section.
+ * @param string $culture The translation locale, e.g. "en_AU".
+ */
+ public function clean($catalogue, $culture)
+ {
+ $group = $this->getGroup($catalogue, $culture);
+ $this->cache->clean($group);
+ }
+
+ /**
+ * Flush the cache. Deletes all the cache files.
+ */
+ public function clear()
+ {
+ $this->cache->clean();
+ }
+
+}
+
+?>
diff --git a/framework/I18N/core/MessageFormat.php b/framework/I18N/core/MessageFormat.php
index 7af6deb1..fd0d445d 100644
--- a/framework/I18N/core/MessageFormat.php
+++ b/framework/I18N/core/MessageFormat.php
@@ -1,255 +1,255 @@
-
- * @version $Revision: 1.5 $ $Date: 2005/08/27 03:21:12 $
- * @package System.I18N.core
- */
-
-/**
- * Get the MessageSource classes.
- */
-require_once(dirname(__FILE__).'/MessageSource.php');
-
-/**
- * Get the encoding utilities
- */
-require_once(dirname(__FILE__).'/util.php');
-
-/**
- * MessageFormat class.
- *
- * Format a message, that is, for a particular message find the
- * translated message. The following is an example using
- * a SQLite database to store the translation message.
- * Create a new message format instance and echo "Hello"
- * in simplified Chinese. This assumes that the world "Hello"
- * is translated in the database.
- *
- *
- * $source = MessageSource::factory('SQLite', 'sqlite://messages.db');
- * $source->setCulture('zh_CN');
- * $source->setCache(new MessageCache('./tmp'));
- *
- * $formatter = new MessageFormat($source);
- *
- * echo $formatter->format('Hello');
- *
- *
- * @author Xiang Wei Zhuo
- * @version v1.0, last update on Fri Dec 24 20:46:16 EST 2004
- * @package System.I18N.core
- */
-class MessageFormat
-{
- /**
- * The message source.
- * @var MessageSource
- */
- protected $source;
-
- /**
- * A list of loaded message catalogues.
- * @var array
- */
- protected $catagloues = array();
-
- /**
- * The translation messages.
- * @var array
- */
- protected $messages = array();
-
- /**
- * A list of untranslated messages.
- * @var array
- */
- protected $untranslated = array();
-
- /**
- * The prefix and suffix to append to untranslated messages.
- * @var array
- */
- protected $postscript = array('','');
-
- /**
- * Set the default catalogue.
- * @var string
- */
- public $Catalogue;
-
- /**
- * Output encoding charset
- * @var string
- */
- protected $charset = 'UTF-8';
-
- /**
- * Constructor.
- * Create a new instance of MessageFormat using the messages
- * from the supplied message source.
- * @param MessageSource the source of translation messages.
- * @param string charset for the message output.
- */
- function __construct(IMessageSource $source, $charset='UTF-8')
- {
- $this->source = $source;
- $this->setCharset($charset);
- }
-
- /**
- * Sets the charset for message output.
- * @param string charset, default is UTF-8
- */
- public function setCharset($charset)
- {
- $this->charset = $charset;
- }
-
- /**
- * Gets the charset for message output. Default is UTF-8.
- * @return string charset, default UTF-8
- */
- public function getCharset()
- {
- return $this->charset;
- }
-
- /**
- * Load the message from a particular catalogue. A listed
- * loaded catalogues is kept to prevent reload of the same
- * catalogue. The load catalogue messages are stored
- * in the $this->message array.
- * @param string message catalogue to load.
- */
- protected function loadCatalogue($catalogue)
- {
- if(in_array($catalogue,$this->catagloues))
- return;
-
- if($this->source->load($catalogue))
- {
- $this->messages[$catalogue] = $this->source->read();
- $this->catagloues[] = $catalogue;
- }
- }
-
- /**
- * Format the string. That is, for a particular string find
- * the corresponding translation. Variable subsitution is performed
- * for the $args parameter. A different catalogue can be specified
- * using the $catalogue parameter.
- * The output charset is determined by $this->getCharset();
- * @param string the string to translate.
- * @param array a list of string to substitute.
- * @param string get the translation from a particular message
- * @param string charset, the input AND output charset
- * catalogue.
- * @return string translated string.
- */
- public function format($string,$args=array(), $catalogue=null, $charset=null)
- {
- if(empty($charset)) $charset = $this->getCharset();
-
- //force args as UTF-8
- foreach($args as $k => $v)
- $args[$k] = I18N_toUTF8($v, $charset);
- $s = $this->formatString(I18N_toUTF8($string, $charset),$args,$catalogue);
- return I18N_toEncoding($s, $charset);
- }
-
- /**
- * Do string translation.
- * @param string the string to translate.
- * @param array a list of string to substitute.
- * @param string get the translation from a particular message
- * catalogue.
- * @return string translated string.
- */
- protected function formatString($string, $args=array(), $catalogue=null)
- {
- if(empty($catalogue))
- {
- if(empty($this->Catalogue))
- $catalogue = 'messages';
- else
- $catalogue = $this->Catalogue;
- }
-
- $this->loadCatalogue($catalogue);
-
- if(empty($args))
- $args = array();
-
- foreach($this->messages[$catalogue] as $variant)
- {
- // foreach of the translation units
- foreach($variant as $source => $result)
- {
- // we found it, so return the target translation
- if($source == $string)
- {
- //check if it contains only strings.
- if(is_string($result))
- $target = $result;
- else
- {
- $target = $result[0];
- }
- //found, but untranslated
- if(empty($target))
- {
- return $this->postscript[0].
- strtr($string, $args).
- $this->postscript[1];
- }
- else
- return strtr($target, $args);
- }
- }
- }
-
- // well we did not find the translation string.
- $this->source->append($string);
-
- return $this->postscript[0].
- strtr($string, $args).
- $this->postscript[1];
- }
-
- /**
- * Get the message source.
- * @return MessageSource
- */
- function getSource()
- {
- return $this->source;
- }
-
- /**
- * Set the prefix and suffix to append to untranslated messages.
- * e.g. $postscript=array('[T]','[/T]'); will output
- * "[T]Hello[/T]" if the translation for "Hello" can not be determined.
- * @param array first element is the prefix, second element the suffix.
- */
- function setUntranslatedPS($postscript)
- {
- if(is_array($postscript) && count($postscript)>=2)
- {
- $this->postscript[0] = $postscript[0];
- $this->postscript[1] = $postscript[1];
- }
- }
-}
-
+
+ * @version $Revision: 1.5 $ $Date: 2005/08/27 03:21:12 $
+ * @package System.I18N.core
+ */
+
+/**
+ * Get the MessageSource classes.
+ */
+require_once(dirname(__FILE__).'/MessageSource.php');
+
+/**
+ * Get the encoding utilities
+ */
+require_once(dirname(__FILE__).'/util.php');
+
+/**
+ * MessageFormat class.
+ *
+ * Format a message, that is, for a particular message find the
+ * translated message. The following is an example using
+ * a SQLite database to store the translation message.
+ * Create a new message format instance and echo "Hello"
+ * in simplified Chinese. This assumes that the world "Hello"
+ * is translated in the database.
+ *
+ *
+ * $source = MessageSource::factory('SQLite', 'sqlite://messages.db');
+ * $source->setCulture('zh_CN');
+ * $source->setCache(new MessageCache('./tmp'));
+ *
+ * $formatter = new MessageFormat($source);
+ *
+ * echo $formatter->format('Hello');
+ *
+ *
+ * @author Xiang Wei Zhuo
+ * @version v1.0, last update on Fri Dec 24 20:46:16 EST 2004
+ * @package System.I18N.core
+ */
+class MessageFormat
+{
+ /**
+ * The message source.
+ * @var MessageSource
+ */
+ protected $source;
+
+ /**
+ * A list of loaded message catalogues.
+ * @var array
+ */
+ protected $catagloues = array();
+
+ /**
+ * The translation messages.
+ * @var array
+ */
+ protected $messages = array();
+
+ /**
+ * A list of untranslated messages.
+ * @var array
+ */
+ protected $untranslated = array();
+
+ /**
+ * The prefix and suffix to append to untranslated messages.
+ * @var array
+ */
+ protected $postscript = array('','');
+
+ /**
+ * Set the default catalogue.
+ * @var string
+ */
+ public $Catalogue;
+
+ /**
+ * Output encoding charset
+ * @var string
+ */
+ protected $charset = 'UTF-8';
+
+ /**
+ * Constructor.
+ * Create a new instance of MessageFormat using the messages
+ * from the supplied message source.
+ * @param MessageSource the source of translation messages.
+ * @param string charset for the message output.
+ */
+ function __construct(IMessageSource $source, $charset='UTF-8')
+ {
+ $this->source = $source;
+ $this->setCharset($charset);
+ }
+
+ /**
+ * Sets the charset for message output.
+ * @param string charset, default is UTF-8
+ */
+ public function setCharset($charset)
+ {
+ $this->charset = $charset;
+ }
+
+ /**
+ * Gets the charset for message output. Default is UTF-8.
+ * @return string charset, default UTF-8
+ */
+ public function getCharset()
+ {
+ return $this->charset;
+ }
+
+ /**
+ * Load the message from a particular catalogue. A listed
+ * loaded catalogues is kept to prevent reload of the same
+ * catalogue. The load catalogue messages are stored
+ * in the $this->message array.
+ * @param string message catalogue to load.
+ */
+ protected function loadCatalogue($catalogue)
+ {
+ if(in_array($catalogue,$this->catagloues))
+ return;
+
+ if($this->source->load($catalogue))
+ {
+ $this->messages[$catalogue] = $this->source->read();
+ $this->catagloues[] = $catalogue;
+ }
+ }
+
+ /**
+ * Format the string. That is, for a particular string find
+ * the corresponding translation. Variable subsitution is performed
+ * for the $args parameter. A different catalogue can be specified
+ * using the $catalogue parameter.
+ * The output charset is determined by $this->getCharset();
+ * @param string the string to translate.
+ * @param array a list of string to substitute.
+ * @param string get the translation from a particular message
+ * @param string charset, the input AND output charset
+ * catalogue.
+ * @return string translated string.
+ */
+ public function format($string,$args=array(), $catalogue=null, $charset=null)
+ {
+ if(empty($charset)) $charset = $this->getCharset();
+
+ //force args as UTF-8
+ foreach($args as $k => $v)
+ $args[$k] = I18N_toUTF8($v, $charset);
+ $s = $this->formatString(I18N_toUTF8($string, $charset),$args,$catalogue);
+ return I18N_toEncoding($s, $charset);
+ }
+
+ /**
+ * Do string translation.
+ * @param string the string to translate.
+ * @param array a list of string to substitute.
+ * @param string get the translation from a particular message
+ * catalogue.
+ * @return string translated string.
+ */
+ protected function formatString($string, $args=array(), $catalogue=null)
+ {
+ if(empty($catalogue))
+ {
+ if(empty($this->Catalogue))
+ $catalogue = 'messages';
+ else
+ $catalogue = $this->Catalogue;
+ }
+
+ $this->loadCatalogue($catalogue);
+
+ if(empty($args))
+ $args = array();
+
+ foreach($this->messages[$catalogue] as $variant)
+ {
+ // foreach of the translation units
+ foreach($variant as $source => $result)
+ {
+ // we found it, so return the target translation
+ if($source == $string)
+ {
+ //check if it contains only strings.
+ if(is_string($result))
+ $target = $result;
+ else
+ {
+ $target = $result[0];
+ }
+ //found, but untranslated
+ if(empty($target))
+ {
+ return $this->postscript[0].
+ strtr($string, $args).
+ $this->postscript[1];
+ }
+ else
+ return strtr($target, $args);
+ }
+ }
+ }
+
+ // well we did not find the translation string.
+ $this->source->append($string);
+
+ return $this->postscript[0].
+ strtr($string, $args).
+ $this->postscript[1];
+ }
+
+ /**
+ * Get the message source.
+ * @return MessageSource
+ */
+ function getSource()
+ {
+ return $this->source;
+ }
+
+ /**
+ * Set the prefix and suffix to append to untranslated messages.
+ * e.g. $postscript=array('[T]','[/T]'); will output
+ * "[T]Hello[/T]" if the translation for "Hello" can not be determined.
+ * @param array first element is the prefix, second element the suffix.
+ */
+ function setUntranslatedPS($postscript)
+ {
+ if(is_array($postscript) && count($postscript)>=2)
+ {
+ $this->postscript[0] = $postscript[0];
+ $this->postscript[1] = $postscript[1];
+ }
+ }
+}
+
diff --git a/framework/I18N/core/MessageSource.php b/framework/I18N/core/MessageSource.php
index 76d06e9d..f0f94015 100644
--- a/framework/I18N/core/MessageSource.php
+++ b/framework/I18N/core/MessageSource.php
@@ -1,336 +1,336 @@
-
- * @version $Revision: 1.4 $ $Date: 2005/12/17 06:11:28 $
- * @package System.I18N.core
- */
-
- /**
- * Get the IMessageSource interface.
- */
-require_once(dirname(__FILE__).'/IMessageSource.php');
-
-/**
- * Get the MessageCache class file.
- */
-require_once(dirname(__FILE__).'/MessageCache.php');
-
-/**
- * Abstract MessageSource class.
- *
- * The base class for all MessageSources. Message sources must be instantiated
- * using the factory method. The default valid sources are
- *
- * # XLIFF -- using XML XLIFF format to store the translation messages.
- * # gettext -- Translated messages are stored in the gettext format.
- * # Database -- Use an existing TDbConnection to store the messages.
- * # SQLite -- (Deprecated) Store the translation messages in a SQLite database.
- * # MySQL -- (Deprecated) Using a MySQL database to store the messages.
- *
- * A custom message source can be instantiated by specifying the filename
- * parameter to point to the custom class file. E.g.
- *
- * $resource = '...'; //custom message source resource
- * $classfile = '../MessageSource_MySource.php'; //custom message source
- * $source = MessageSource::factory('MySource', $resource, $classfile);
- *
- *
- * If you are writting your own message sources, pay attention to the
- * loadCatalogue method. It details how the resources are loaded and cached.
- * See also the existing message source types as examples.
- *
- * The following example instantiates a Database message source, set the culture,
- * set the cache handler, and use the source in a message formatter.
- * The messages are stored using an existing connection. The source parameter
- * for the factory method must contain a valid ConnectionID.
- *
- * // db1 must be already configured
- * $source = MessageSource::factory('Database', 'db1');
- *
- * //set the culture and cache, store the cache in the /tmp directory.
- * $source->setCulture('en_AU')l
- * $source->setCache(new MessageCache('/tmp'));
- *
- * $formatter = new MessageFormat($source);
- *
- *
- * @author Xiang Wei Zhuo
- * @version v1.0, last update on Fri Dec 24 19:55:49 EST 2004
- * @package System.I18N.core
- */
-abstract class MessageSource implements IMessageSource
-{
- /**
- * The culture name for this message source.
- * @var string
- */
- protected $culture;
-
- /**
- * Array of translation messages.
- * @var array
- */
- protected $messages = array();
-
- /**
- * The source of message translations.
- * @var string
- */
- protected $source;
-
- /**
- * The translation cache.
- * @var MessageCache
- */
- protected $cache;
-
- protected $untranslated = array();
-
- /**
- * Private constructor. MessageSource must be initialized using
- * the factory method.
- */
- private function __construct()
- {
- //throw new Exception('Please use the factory method to instantiate.');
- }
-
- /**
- * Factory method to instantiate a new MessageSource depending on the
- * source type. The allowed source types are 'XLIFF', 'gettext' and
- * 'Database'. The source parameter depends on the source type.
- * For 'gettext' and 'XLIFF', 'source' should point to the directory
- * where the messages are stored.
- * For 'Database', 'source' must be a valid connection id.
- * If one of the deprecated types 'MySQL' or 'SQLite' is used,
- * 'source' must contain a valid DSN.
- *
- * Custom message source are possible by supplying the a filename parameter
- * in the factory method.
- *
- * @param string the message source type.
- * @param string the location of the resource or the ConnectionID.
- * @param string the filename of the custom message source.
- * @return MessageSource a new message source of the specified type.
- * @throws InvalidMessageSourceTypeException
- */
- static function &factory($type, $source='.', $filename='')
- {
- $types = array('XLIFF','gettext','Database','MySQL','SQLite');
-
- if(empty($filename) && !in_array($type, $types))
- throw new Exception('Invalid type "'.$type.'", valid types are '.
- implode(', ', $types));
-
- $class = 'MessageSource_'.$type;
-
- if(empty($filename))
- $filename = dirname(__FILE__).'/'.$class.'.php';
-
- if(is_file($filename) == false)
- throw new Exception("File $filename not found");
-
- include_once $filename;
-
- $obj = new $class($source);
-
- return $obj;
- }
-
- /**
- * Load a particular message catalogue. Use read() to
- * to get the array of messages. The catalogue loading sequence
- * is as follows
- *
- * # [1] call getCatalogeList($catalogue) to get a list of
- * variants for for the specified $catalogue.
- * # [2] for each of the variants, call getSource($variant)
- * to get the resource, could be a file or catalogue ID.
- * # [3] verify that this resource is valid by calling isValidSource($source)
- * # [4] try to get the messages from the cache
- * # [5] if a cache miss, call load($source) to load the message array
- * # [6] store the messages to cache.
- * # [7] continue with the foreach loop, e.g. goto [2].
- *
- * @param string a catalogue to load
- * @return boolean true if loaded, false otherwise.
- * @see read()
- */
- function load($catalogue='messages')
- {
- $variants = $this->getCatalogueList($catalogue);
-
- $this->messages = array();
-
- foreach($variants as $variant)
- {
- $source = $this->getSource($variant);
-
- if($this->isValidSource($source) == false) continue;
-
- $loadData = true;
-
- if($this->cache)
- {
- $data = $this->cache->get($variant,
- $this->culture, $this->getLastModified($source));
-
- if(is_array($data))
- {
- $this->messages[$variant] = $data;
- $loadData = false;
- }
- unset($data);
- }
- if($loadData)
- {
- $data = &$this->loadData($source);
- if(is_array($data))
- {
- $this->messages[$variant] = $data;
- if($this->cache)
- $this->cache->save($data, $variant, $this->culture);
- }
- unset($data);
- }
- }
-
- return true;
- }
-
- /**
- * Get the array of messages.
- * @param parameter
- * @return array translation messages.
- */
- public function read()
- {
- return $this->messages;
- }
-
- /**
- * Get the cache handler for this source.
- * @return MessageCache cache handler
- */
- public function getCache()
- {
- return $this->cache;
- }
-
- /**
- * Set the cache handler for caching the messages.
- * @param MessageCache the cache handler.
- */
- public function setCache(MessageCache $cache)
- {
- $this->cache = $cache;
- }
-
- /**
- * Add a untranslated message to the source. Need to call save()
- * to save the messages to source.
- * @param string message to add
- */
- public function append($message)
- {
- if(!in_array($message, $this->untranslated))
- $this->untranslated[] = $message;
- }
-
- /**
- * Set the culture for this message source.
- * @param string culture name
- */
- public function setCulture($culture)
- {
- $this->culture = $culture;
- }
-
- /**
- * Get the culture identifier for the source.
- * @return string culture identifier.
- */
- public function getCulture()
- {
- return $this->culture;
- }
-
- /**
- * Get the last modified unix-time for this particular catalogue+variant.
- * @param string catalogue+variant
- * @return int last modified in unix-time format.
- */
- protected function getLastModified($source)
- {
- return 0;
- }
-
- /**
- * Load the message for a particular catalogue+variant.
- * This methods needs to implemented by subclasses.
- * @param string catalogue+variant.
- * @return array of translation messages.
- */
- protected function &loadData($variant)
- {
- return array();
- }
-
- /**
- * Get the source, this could be a filename or database ID.
- * @param string catalogue+variant
- * @return string the resource key
- */
- protected function getSource($variant)
- {
- return $variant;
- }
-
- /**
- * Determine if the source is valid.
- * @param string catalogue+variant
- * @return boolean true if valid, false otherwise.
- */
- protected function isValidSource($source)
- {
- return false;
- }
-
- /**
- * Get all the variants of a particular catalogue.
- * This method must be implemented by subclasses.
- * @param string catalogue name
- * @return array list of all variants for this catalogue.
- */
- protected function getCatalogueList($catalogue)
- {
- return array();
- }
-}
-
-
-/**
- * TMessageSourceIOException thrown when unable to modify message source
- * data.
- *
- * @author Wei Zhuo
- * @version $Revision: 1.4 $ $Date: 2005/12/17 06:11:28 ${DATE} ${TIME} $
- * @package System.I18N.core
- */
-class TMessageSourceIOException extends TException
-{
-
-}
-?>
+
+ * @version $Revision: 1.4 $ $Date: 2005/12/17 06:11:28 $
+ * @package System.I18N.core
+ */
+
+ /**
+ * Get the IMessageSource interface.
+ */
+require_once(dirname(__FILE__).'/IMessageSource.php');
+
+/**
+ * Get the MessageCache class file.
+ */
+require_once(dirname(__FILE__).'/MessageCache.php');
+
+/**
+ * Abstract MessageSource class.
+ *
+ * The base class for all MessageSources. Message sources must be instantiated
+ * using the factory method. The default valid sources are
+ *
+ * # XLIFF -- using XML XLIFF format to store the translation messages.
+ * # gettext -- Translated messages are stored in the gettext format.
+ * # Database -- Use an existing TDbConnection to store the messages.
+ * # SQLite -- (Deprecated) Store the translation messages in a SQLite database.
+ * # MySQL -- (Deprecated) Using a MySQL database to store the messages.
+ *
+ * A custom message source can be instantiated by specifying the filename
+ * parameter to point to the custom class file. E.g.
+ *
+ * $resource = '...'; //custom message source resource
+ * $classfile = '../MessageSource_MySource.php'; //custom message source
+ * $source = MessageSource::factory('MySource', $resource, $classfile);
+ *
+ *
+ * If you are writting your own message sources, pay attention to the
+ * loadCatalogue method. It details how the resources are loaded and cached.
+ * See also the existing message source types as examples.
+ *
+ * The following example instantiates a Database message source, set the culture,
+ * set the cache handler, and use the source in a message formatter.
+ * The messages are stored using an existing connection. The source parameter
+ * for the factory method must contain a valid ConnectionID.
+ *
+ * // db1 must be already configured
+ * $source = MessageSource::factory('Database', 'db1');
+ *
+ * //set the culture and cache, store the cache in the /tmp directory.
+ * $source->setCulture('en_AU')l
+ * $source->setCache(new MessageCache('/tmp'));
+ *
+ * $formatter = new MessageFormat($source);
+ *
+ *
+ * @author Xiang Wei Zhuo
+ * @version v1.0, last update on Fri Dec 24 19:55:49 EST 2004
+ * @package System.I18N.core
+ */
+abstract class MessageSource implements IMessageSource
+{
+ /**
+ * The culture name for this message source.
+ * @var string
+ */
+ protected $culture;
+
+ /**
+ * Array of translation messages.
+ * @var array
+ */
+ protected $messages = array();
+
+ /**
+ * The source of message translations.
+ * @var string
+ */
+ protected $source;
+
+ /**
+ * The translation cache.
+ * @var MessageCache
+ */
+ protected $cache;
+
+ protected $untranslated = array();
+
+ /**
+ * Private constructor. MessageSource must be initialized using
+ * the factory method.
+ */
+ private function __construct()
+ {
+ //throw new Exception('Please use the factory method to instantiate.');
+ }
+
+ /**
+ * Factory method to instantiate a new MessageSource depending on the
+ * source type. The allowed source types are 'XLIFF', 'gettext' and
+ * 'Database'. The source parameter depends on the source type.
+ * For 'gettext' and 'XLIFF', 'source' should point to the directory
+ * where the messages are stored.
+ * For 'Database', 'source' must be a valid connection id.
+ * If one of the deprecated types 'MySQL' or 'SQLite' is used,
+ * 'source' must contain a valid DSN.
+ *
+ * Custom message source are possible by supplying the a filename parameter
+ * in the factory method.
+ *
+ * @param string the message source type.
+ * @param string the location of the resource or the ConnectionID.
+ * @param string the filename of the custom message source.
+ * @return MessageSource a new message source of the specified type.
+ * @throws InvalidMessageSourceTypeException
+ */
+ static function &factory($type, $source='.', $filename='')
+ {
+ $types = array('XLIFF','gettext','Database','MySQL','SQLite');
+
+ if(empty($filename) && !in_array($type, $types))
+ throw new Exception('Invalid type "'.$type.'", valid types are '.
+ implode(', ', $types));
+
+ $class = 'MessageSource_'.$type;
+
+ if(empty($filename))
+ $filename = dirname(__FILE__).'/'.$class.'.php';
+
+ if(is_file($filename) == false)
+ throw new Exception("File $filename not found");
+
+ include_once $filename;
+
+ $obj = new $class($source);
+
+ return $obj;
+ }
+
+ /**
+ * Load a particular message catalogue. Use read() to
+ * to get the array of messages. The catalogue loading sequence
+ * is as follows
+ *
+ * # [1] call getCatalogeList($catalogue) to get a list of
+ * variants for for the specified $catalogue.
+ * # [2] for each of the variants, call getSource($variant)
+ * to get the resource, could be a file or catalogue ID.
+ * # [3] verify that this resource is valid by calling isValidSource($source)
+ * # [4] try to get the messages from the cache
+ * # [5] if a cache miss, call load($source) to load the message array
+ * # [6] store the messages to cache.
+ * # [7] continue with the foreach loop, e.g. goto [2].
+ *
+ * @param string a catalogue to load
+ * @return boolean true if loaded, false otherwise.
+ * @see read()
+ */
+ function load($catalogue='messages')
+ {
+ $variants = $this->getCatalogueList($catalogue);
+
+ $this->messages = array();
+
+ foreach($variants as $variant)
+ {
+ $source = $this->getSource($variant);
+
+ if($this->isValidSource($source) == false) continue;
+
+ $loadData = true;
+
+ if($this->cache)
+ {
+ $data = $this->cache->get($variant,
+ $this->culture, $this->getLastModified($source));
+
+ if(is_array($data))
+ {
+ $this->messages[$variant] = $data;
+ $loadData = false;
+ }
+ unset($data);
+ }
+ if($loadData)
+ {
+ $data = &$this->loadData($source);
+ if(is_array($data))
+ {
+ $this->messages[$variant] = $data;
+ if($this->cache)
+ $this->cache->save($data, $variant, $this->culture);
+ }
+ unset($data);
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Get the array of messages.
+ * @param parameter
+ * @return array translation messages.
+ */
+ public function read()
+ {
+ return $this->messages;
+ }
+
+ /**
+ * Get the cache handler for this source.
+ * @return MessageCache cache handler
+ */
+ public function getCache()
+ {
+ return $this->cache;
+ }
+
+ /**
+ * Set the cache handler for caching the messages.
+ * @param MessageCache the cache handler.
+ */
+ public function setCache(MessageCache $cache)
+ {
+ $this->cache = $cache;
+ }
+
+ /**
+ * Add a untranslated message to the source. Need to call save()
+ * to save the messages to source.
+ * @param string message to add
+ */
+ public function append($message)
+ {
+ if(!in_array($message, $this->untranslated))
+ $this->untranslated[] = $message;
+ }
+
+ /**
+ * Set the culture for this message source.
+ * @param string culture name
+ */
+ public function setCulture($culture)
+ {
+ $this->culture = $culture;
+ }
+
+ /**
+ * Get the culture identifier for the source.
+ * @return string culture identifier.
+ */
+ public function getCulture()
+ {
+ return $this->culture;
+ }
+
+ /**
+ * Get the last modified unix-time for this particular catalogue+variant.
+ * @param string catalogue+variant
+ * @return int last modified in unix-time format.
+ */
+ protected function getLastModified($source)
+ {
+ return 0;
+ }
+
+ /**
+ * Load the message for a particular catalogue+variant.
+ * This methods needs to implemented by subclasses.
+ * @param string catalogue+variant.
+ * @return array of translation messages.
+ */
+ protected function &loadData($variant)
+ {
+ return array();
+ }
+
+ /**
+ * Get the source, this could be a filename or database ID.
+ * @param string catalogue+variant
+ * @return string the resource key
+ */
+ protected function getSource($variant)
+ {
+ return $variant;
+ }
+
+ /**
+ * Determine if the source is valid.
+ * @param string catalogue+variant
+ * @return boolean true if valid, false otherwise.
+ */
+ protected function isValidSource($source)
+ {
+ return false;
+ }
+
+ /**
+ * Get all the variants of a particular catalogue.
+ * This method must be implemented by subclasses.
+ * @param string catalogue name
+ * @return array list of all variants for this catalogue.
+ */
+ protected function getCatalogueList($catalogue)
+ {
+ return array();
+ }
+}
+
+
+/**
+ * TMessageSourceIOException thrown when unable to modify message source
+ * data.
+ *
+ * @author Wei Zhuo
+ * @version $Revision: 1.4 $ $Date: 2005/12/17 06:11:28 ${DATE} ${TIME} $
+ * @package System.I18N.core
+ */
+class TMessageSourceIOException extends TException
+{
+
+}
+?>
diff --git a/framework/I18N/core/MessageSource_Database.php b/framework/I18N/core/MessageSource_Database.php
index 3ccea61b..549d1c40 100644
--- a/framework/I18N/core/MessageSource_Database.php
+++ b/framework/I18N/core/MessageSource_Database.php
@@ -1,323 +1,323 @@
-_connID= (string)$source;
- }
-
- /**
- * @return TDbConnection the database connection that may be used to retrieve messages.
- */
- public function getDbConnection()
- {
- if($this->_conn===null)
- {
- $this->_conn=$this->createDbConnection($this->_connID);
- $this->_conn->setActive(true);
- }
- return $this->_conn;
- }
-
- /**
- * Creates the DB connection.
- * @param string the module ID for TDataSourceConfig
- * @return TDbConnection the created DB connection
- * @throws TConfigurationException if module ID is invalid or empty
- */
- protected function createDbConnection($connectionID)
- {
- if($connectionID!=='')
- {
- $conn=Prado::getApplication()->getModule($connectionID);
- if($conn instanceof TDataSourceConfig)
- return $conn->getDbConnection();
- else
- throw new TConfigurationException('messagesource_connectionid_invalid',$connectionID);
- }
- else
- throw new TConfigurationException('messagesource_connectionid_required');
- }
-
- /**
- * Get an array of messages for a particular catalogue and cultural
- * variant.
- * @param string the catalogue name + variant
- * @return array translation messages.
- */
- protected function &loadData($variant)
- {
- $command=$this->getDBConnection()->createCommand(
- 'SELECT t.id, t.source, t.target, t.comments
- FROM trans_unit t, catalogue c
- WHERE c.cat_id = t.cat_id
- AND c.name = :variant
- ORDER BY id ASC');
- $command->bindParameter(':variant',$variant,PDO::PARAM_STR);
- $dataReader=$command->query();
-
- $result = array();
-
- foreach ($dataReader as $row)
- $result[$row['source']] = array($row['target'],$row['id'],$row['comments']);
-
- return $result;
- }
-
- /**
- * Get the last modified unix-time for this particular catalogue+variant.
- * We need to query the database to get the date_modified.
- * @param string catalogue+variant
- * @return int last modified in unix-time format.
- */
- protected function getLastModified($source)
- {
- $command=$this->getDBConnection()->createCommand(
- 'SELECT date_modified FROM catalogue WHERE name = :source');
- $command->bindParameter(':source',$source,PDO::PARAM_STR);
- $result=$command->queryScalar();
- return $result ? $result : 0;
- }
-
-
- /**
- * Check if a particular catalogue+variant exists in the database.
- * @param string catalogue+variant
- * @return boolean true if the catalogue+variant is in the database,
- * false otherwise.
- */
- protected function isValidSource($variant)
- {
- $command=$this->getDBConnection()->createCommand(
- 'SELECT COUNT(*) FROM catalogue WHERE name = :variant');
- $command->bindParameter(':variant',$variant,PDO::PARAM_STR);
- return $command->queryScalar()==1;
- }
-
- /**
- * Get all the variants of a particular catalogue.
- * @param string catalogue name
- * @return array list of all variants for this catalogue.
- */
- protected function getCatalogueList($catalogue)
- {
- $variants = explode('_',$this->culture);
-
- $catalogues = array($catalogue);
-
- $variant = null;
-
- for($i = 0, $k = count($variants); $i < $k; ++$i)
- {
- if(isset($variants[$i]{0}))
- {
- $variant .= ($variant)?'_'.$variants[$i]:$variants[$i];
- $catalogues[] = $catalogue.'.'.$variant;
- }
- }
- return array_reverse($catalogues);
- }
-
- /**
- * Retrive catalogue details, array($cat_id, $variant, $count).
- * @param string catalogue
- * @return array catalogue details, array($cat_id, $variant, $count).
- */
- private function getCatalogueDetails($catalogue='messages')
- {
- if(empty($catalogue))
- $catalogue = 'messages';
-
- $variant = $catalogue.'.'.$this->culture;
-
- $command=$this->getDBConnection()->createCommand(
- 'SELECT cat_id FROM catalogue WHERE name = :variant');
- $command->bindParameter(':variant',$variant,PDO::PARAM_STR);
- $cat_id=$command->queryScalar();
-
- if ($cat_id===null) return false;
-
- $command=$this->getDBConnection()->createCommand(
- 'SELECT COUNT(msg_id) FROM trans_unit WHERE cat_id = :catid ');
- $command->bindParameter(':catid',$cat_id,PDO::PARAM_INT);
- $count=$command->queryScalar();
-
- return array($cat_id, $variant, $count);
- }
-
- /**
- * Update the catalogue last modified time.
- * @return boolean true if updated, false otherwise.
- */
- private function updateCatalogueTime($cat_id, $variant)
- {
- $time = time();
- $command=$this->getDBConnection()->createCommand(
- 'UPDATE catalogue SET date_modified = :moddate WHERE cat_id = :catid');
- $command->bindParameter(':moddate',$time,PDO::PARAM_INT);
- $command->bindParameter(':catid',$cat_id,PDO::PARAM_INT);
- $result=$command->execute();
-
- if(!empty($this->cache))
- $this->cache->clean($variant, $this->culture);
-
- return $result;
- }
-
- /**
- * Save the list of untranslated blocks to the translation source.
- * If the translation was not found, you should add those
- * strings to the translation source via the append() method.
- * @param string the catalogue to add to
- * @return boolean true if saved successfuly, false otherwise.
- */
- function save($catalogue='messages')
- {
- $messages = $this->untranslated;
-
- if(count($messages) <= 0) return false;
-
- $details = $this->getCatalogueDetails($catalogue);
-
- if($details)
- list($cat_id, $variant, $count) = $details;
- else
- return false;
-
- if($cat_id <= 0) return false;
- $inserted = 0;
-
- $time = time();
-
- $command=$this->getDBConnection()->createCommand(
- 'INSERT INTO trans_unit (cat_id,id,source,date_added) VALUES (:catid,:id,:source,:dateadded)');
- $command->bindParameter(':catid',$cat_id,PDO::PARAM_INT);
- $command->bindParameter(':id',$count,PDO::PARAM_INT);
- $command->bindParameter(':source',$message,PDO::PARAM_STR);
- $command->bindParameter(':dateadded',$time,PDO::PARAM_INT);
- foreach($messages as $message)
- {
- if (empty($message)) continue;
- $count++; $inserted++;
- $command->execute();
- }
- if($inserted > 0)
- $this->updateCatalogueTime($cat_id, $variant);
-
- return $inserted > 0;
- }
-
- /**
- * Delete a particular message from the specified catalogue.
- * @param string the source message to delete.
- * @param string the catalogue to delete from.
- * @return boolean true if deleted, false otherwise.
- */
- function delete($message, $catalogue='messages')
- {
- $details = $this->getCatalogueDetails($catalogue);
- if($details)
- list($cat_id, $variant, $count) = $details;
- else
- return false;
-
- $command=$this->getDBConnection()->createCommand(
- 'DELETE FROM trans_unit WHERE cat_id = :catid AND source = :message');
- $command->bindParameter(':catid',$cat_id,PDO::PARAM_INT);
- $command->bindParameter(':message',$message,PDO::PARAM_STR);
-
- return ($command->execute()==1) ? $this->updateCatalogueTime($cat_id, $variant) : false;
-
- }
-
- /**
- * Update the translation.
- * @param string the source string.
- * @param string the new translation string.
- * @param string comments
- * @param string the catalogue of the translation.
- * @return boolean true if translation was updated, false otherwise.
- */
- function update($text, $target, $comments, $catalogue='messages')
- {
- $details = $this->getCatalogueDetails($catalogue);
- if($details)
- list($cat_id, $variant, $count) = $details;
- else
- return false;
-
- $time = time();
- $command=$this->getDBConnection()->createCommand(
- 'UPDATE trans_unit SET target = :target, comments = :comments, date_modified = :datemod
- WHERE cat_id = :catid AND source = :source');
- $command->bindParameter(':target',$target,PDO::PARAM_STR);
- $command->bindParameter(':comments',$comments,PDO::PARAM_STR);
- $command->bindParameter(':datemod',$time,PDO::PARAM_INT);
- $command->bindParameter(':catid',$cat_id,PDO::PARAM_INT);
- $command->bindParameter(':source',$text,PDO::PARAM_STR);
-
- return ($command->execute()==1) ? $this->updateCatalogueTime($cat_id, $variant) : false;
- }
-
- /**
- * Returns a list of catalogue as key and all it variants as value.
- * @return array list of catalogues
- */
- function catalogues()
- {
- $command=$this->getDBConnection()->createCommand( 'SELECT name FROM catalogue ORDER BY name');
- $dataReader=$command->query();
-
- $result = array();
-
- foreach ($dataReader as $row)
- {
- $details = explode('.',$row[0]);
- if(!isset($details[1])) $details[1] = null;
-
- $result[] = $details;
- }
-
- return $result;
- }
-
-}
-?>
+_connID= (string)$source;
+ }
+
+ /**
+ * @return TDbConnection the database connection that may be used to retrieve messages.
+ */
+ public function getDbConnection()
+ {
+ if($this->_conn===null)
+ {
+ $this->_conn=$this->createDbConnection($this->_connID);
+ $this->_conn->setActive(true);
+ }
+ return $this->_conn;
+ }
+
+ /**
+ * Creates the DB connection.
+ * @param string the module ID for TDataSourceConfig
+ * @return TDbConnection the created DB connection
+ * @throws TConfigurationException if module ID is invalid or empty
+ */
+ protected function createDbConnection($connectionID)
+ {
+ if($connectionID!=='')
+ {
+ $conn=Prado::getApplication()->getModule($connectionID);
+ if($conn instanceof TDataSourceConfig)
+ return $conn->getDbConnection();
+ else
+ throw new TConfigurationException('messagesource_connectionid_invalid',$connectionID);
+ }
+ else
+ throw new TConfigurationException('messagesource_connectionid_required');
+ }
+
+ /**
+ * Get an array of messages for a particular catalogue and cultural
+ * variant.
+ * @param string the catalogue name + variant
+ * @return array translation messages.
+ */
+ protected function &loadData($variant)
+ {
+ $command=$this->getDBConnection()->createCommand(
+ 'SELECT t.id, t.source, t.target, t.comments
+ FROM trans_unit t, catalogue c
+ WHERE c.cat_id = t.cat_id
+ AND c.name = :variant
+ ORDER BY id ASC');
+ $command->bindParameter(':variant',$variant,PDO::PARAM_STR);
+ $dataReader=$command->query();
+
+ $result = array();
+
+ foreach ($dataReader as $row)
+ $result[$row['source']] = array($row['target'],$row['id'],$row['comments']);
+
+ return $result;
+ }
+
+ /**
+ * Get the last modified unix-time for this particular catalogue+variant.
+ * We need to query the database to get the date_modified.
+ * @param string catalogue+variant
+ * @return int last modified in unix-time format.
+ */
+ protected function getLastModified($source)
+ {
+ $command=$this->getDBConnection()->createCommand(
+ 'SELECT date_modified FROM catalogue WHERE name = :source');
+ $command->bindParameter(':source',$source,PDO::PARAM_STR);
+ $result=$command->queryScalar();
+ return $result ? $result : 0;
+ }
+
+
+ /**
+ * Check if a particular catalogue+variant exists in the database.
+ * @param string catalogue+variant
+ * @return boolean true if the catalogue+variant is in the database,
+ * false otherwise.
+ */
+ protected function isValidSource($variant)
+ {
+ $command=$this->getDBConnection()->createCommand(
+ 'SELECT COUNT(*) FROM catalogue WHERE name = :variant');
+ $command->bindParameter(':variant',$variant,PDO::PARAM_STR);
+ return $command->queryScalar()==1;
+ }
+
+ /**
+ * Get all the variants of a particular catalogue.
+ * @param string catalogue name
+ * @return array list of all variants for this catalogue.
+ */
+ protected function getCatalogueList($catalogue)
+ {
+ $variants = explode('_',$this->culture);
+
+ $catalogues = array($catalogue);
+
+ $variant = null;
+
+ for($i = 0, $k = count($variants); $i < $k; ++$i)
+ {
+ if(isset($variants[$i]{0}))
+ {
+ $variant .= ($variant)?'_'.$variants[$i]:$variants[$i];
+ $catalogues[] = $catalogue.'.'.$variant;
+ }
+ }
+ return array_reverse($catalogues);
+ }
+
+ /**
+ * Retrive catalogue details, array($cat_id, $variant, $count).
+ * @param string catalogue
+ * @return array catalogue details, array($cat_id, $variant, $count).
+ */
+ private function getCatalogueDetails($catalogue='messages')
+ {
+ if(empty($catalogue))
+ $catalogue = 'messages';
+
+ $variant = $catalogue.'.'.$this->culture;
+
+ $command=$this->getDBConnection()->createCommand(
+ 'SELECT cat_id FROM catalogue WHERE name = :variant');
+ $command->bindParameter(':variant',$variant,PDO::PARAM_STR);
+ $cat_id=$command->queryScalar();
+
+ if ($cat_id===null) return false;
+
+ $command=$this->getDBConnection()->createCommand(
+ 'SELECT COUNT(msg_id) FROM trans_unit WHERE cat_id = :catid ');
+ $command->bindParameter(':catid',$cat_id,PDO::PARAM_INT);
+ $count=$command->queryScalar();
+
+ return array($cat_id, $variant, $count);
+ }
+
+ /**
+ * Update the catalogue last modified time.
+ * @return boolean true if updated, false otherwise.
+ */
+ private function updateCatalogueTime($cat_id, $variant)
+ {
+ $time = time();
+ $command=$this->getDBConnection()->createCommand(
+ 'UPDATE catalogue SET date_modified = :moddate WHERE cat_id = :catid');
+ $command->bindParameter(':moddate',$time,PDO::PARAM_INT);
+ $command->bindParameter(':catid',$cat_id,PDO::PARAM_INT);
+ $result=$command->execute();
+
+ if(!empty($this->cache))
+ $this->cache->clean($variant, $this->culture);
+
+ return $result;
+ }
+
+ /**
+ * Save the list of untranslated blocks to the translation source.
+ * If the translation was not found, you should add those
+ * strings to the translation source via the append() method.
+ * @param string the catalogue to add to
+ * @return boolean true if saved successfuly, false otherwise.
+ */
+ function save($catalogue='messages')
+ {
+ $messages = $this->untranslated;
+
+ if(count($messages) <= 0) return false;
+
+ $details = $this->getCatalogueDetails($catalogue);
+
+ if($details)
+ list($cat_id, $variant, $count) = $details;
+ else
+ return false;
+
+ if($cat_id <= 0) return false;
+ $inserted = 0;
+
+ $time = time();
+
+ $command=$this->getDBConnection()->createCommand(
+ 'INSERT INTO trans_unit (cat_id,id,source,date_added) VALUES (:catid,:id,:source,:dateadded)');
+ $command->bindParameter(':catid',$cat_id,PDO::PARAM_INT);
+ $command->bindParameter(':id',$count,PDO::PARAM_INT);
+ $command->bindParameter(':source',$message,PDO::PARAM_STR);
+ $command->bindParameter(':dateadded',$time,PDO::PARAM_INT);
+ foreach($messages as $message)
+ {
+ if (empty($message)) continue;
+ $count++; $inserted++;
+ $command->execute();
+ }
+ if($inserted > 0)
+ $this->updateCatalogueTime($cat_id, $variant);
+
+ return $inserted > 0;
+ }
+
+ /**
+ * Delete a particular message from the specified catalogue.
+ * @param string the source message to delete.
+ * @param string the catalogue to delete from.
+ * @return boolean true if deleted, false otherwise.
+ */
+ function delete($message, $catalogue='messages')
+ {
+ $details = $this->getCatalogueDetails($catalogue);
+ if($details)
+ list($cat_id, $variant, $count) = $details;
+ else
+ return false;
+
+ $command=$this->getDBConnection()->createCommand(
+ 'DELETE FROM trans_unit WHERE cat_id = :catid AND source = :message');
+ $command->bindParameter(':catid',$cat_id,PDO::PARAM_INT);
+ $command->bindParameter(':message',$message,PDO::PARAM_STR);
+
+ return ($command->execute()==1) ? $this->updateCatalogueTime($cat_id, $variant) : false;
+
+ }
+
+ /**
+ * Update the translation.
+ * @param string the source string.
+ * @param string the new translation string.
+ * @param string comments
+ * @param string the catalogue of the translation.
+ * @return boolean true if translation was updated, false otherwise.
+ */
+ function update($text, $target, $comments, $catalogue='messages')
+ {
+ $details = $this->getCatalogueDetails($catalogue);
+ if($details)
+ list($cat_id, $variant, $count) = $details;
+ else
+ return false;
+
+ $time = time();
+ $command=$this->getDBConnection()->createCommand(
+ 'UPDATE trans_unit SET target = :target, comments = :comments, date_modified = :datemod
+ WHERE cat_id = :catid AND source = :source');
+ $command->bindParameter(':target',$target,PDO::PARAM_STR);
+ $command->bindParameter(':comments',$comments,PDO::PARAM_STR);
+ $command->bindParameter(':datemod',$time,PDO::PARAM_INT);
+ $command->bindParameter(':catid',$cat_id,PDO::PARAM_INT);
+ $command->bindParameter(':source',$text,PDO::PARAM_STR);
+
+ return ($command->execute()==1) ? $this->updateCatalogueTime($cat_id, $variant) : false;
+ }
+
+ /**
+ * Returns a list of catalogue as key and all it variants as value.
+ * @return array list of catalogues
+ */
+ function catalogues()
+ {
+ $command=$this->getDBConnection()->createCommand( 'SELECT name FROM catalogue ORDER BY name');
+ $dataReader=$command->query();
+
+ $result = array();
+
+ foreach ($dataReader as $row)
+ {
+ $details = explode('.',$row[0]);
+ if(!isset($details[1])) $details[1] = null;
+
+ $result[] = $details;
+ }
+
+ return $result;
+ }
+
+}
+?>
diff --git a/framework/I18N/core/MessageSource_MySQL.php b/framework/I18N/core/MessageSource_MySQL.php
index 080b89bc..0cd893d4 100644
--- a/framework/I18N/core/MessageSource_MySQL.php
+++ b/framework/I18N/core/MessageSource_MySQL.php
@@ -1,418 +1,418 @@
-
- * @version $Revision: 1.4 $ $Date: 2005/02/25 09:59:40 $
- * @package System.I18N.core
- */
-
-/**
- * Get the MessageSource class file.
- */
-require_once(dirname(__FILE__).'/MessageSource.php');
-
-/**
- * Get the I18N utility file, contains the DSN parser.
- */
-require_once(dirname(__FILE__).'/util.php');
-
-/**
- * MessageSource_MySQL class.
- *
- * Retrive the message translation from a MySQL database.
- *
- * See the MessageSource::factory() method to instantiate this class.
- *
- * @author Xiang Wei Zhuo
- * @version v1.0, last update on Fri Dec 24 16:58:58 EST 2004
- * @package System.I18N.core
- */
-class MessageSource_MySQL extends MessageSource
-{
- /**
- * The datasource string, full DSN to the database.
- * @var string
- */
- protected $source;
-
- /**
- * The DSN array property, parsed by PEAR's DB DSN parser.
- * @var array
- */
- protected $dns;
-
- /**
- * A resource link to the database
- * @var db
- */
- protected $db;
- /**
- * Constructor.
- * Create a new message source using MySQL.
- * @param string MySQL datasource, in PEAR's DB DSN format.
- * @see MessageSource::factory();
- */
- function __construct($source)
- {
- $this->source = (string)$source;
- $this->dns = parseDSN($this->source);
- $this->db = $this->connect();
- }
-
- /**
- * Destructor, close the database connection.
- */
- function __destruct()
- {
- @mysql_close($this->db);
- }
-
- /**
- * Connect to the MySQL datasource
- * @return resource MySQL connection.
- * @throws Exception, connection and database errors.
- */
- protected function connect()
- {
- /*static $conn;
-
- if($conn!==null)
- return $conn;
- */
- $dsninfo = $this->dns;
-
- if (isset($dsninfo['protocol']) && $dsninfo['protocol'] == 'unix')
- $dbhost = ':' . $dsninfo['socket'];
- else
- {
- $dbhost = $dsninfo['hostspec'] ? $dsninfo['hostspec'] : 'localhost';
- if (!empty($dsninfo['port']))
- $dbhost .= ':' . $dsninfo['port'];
- }
- $user = $dsninfo['username'];
- $pw = $dsninfo['password'];
-
- $connect_function = 'mysql_connect';
-
- if ($dbhost && $user && $pw)
- $conn = @$connect_function($dbhost, $user, $pw);
- elseif ($dbhost && $user)
- $conn = @$connect_function($dbhost, $user);
- elseif ($dbhost)
- $conn = @$connect_function($dbhost);
- else
- $conn = false;
-
- if (empty($conn))
- {
- throw new Exception('Error in connecting to '.$dsninfo);
- }
-
- if ($dsninfo['database'])
- {
- if (!@mysql_select_db($dsninfo['database'], $conn))
- throw new Exception('Error in connecting database, dns:'.
- $dsninfo);
- }
- else
- throw new Exception('Please provide a database for message'.
- ' translation.');
- return $conn;
- }
-
- /**
- * Get the database connection.
- * @return db database connection.
- */
- public function connection()
- {
- return $this->db;
- }
-
- /**
- * Get an array of messages for a particular catalogue and cultural
- * variant.
- * @param string the catalogue name + variant
- * @return array translation messages.
- */
- protected function &loadData($variant)
- {
- $variant = mysql_real_escape_string($variant);
-
- $statement =
- "SELECT t.id, t.source, t.target, t.comments
- FROM trans_unit t, catalogue c
- WHERE c.cat_id = t.cat_id
- AND c.name = '{$variant}'
- ORDER BY id ASC";
-
- $rs = mysql_query($statement,$this->db);
-
- $result = array();
-
- while($row = mysql_fetch_array($rs,MYSQL_NUM))
- {
- $source = $row[1];
- $result[$source][] = $row[2]; //target
- $result[$source][] = $row[0]; //id
- $result[$source][] = $row[3]; //comments
- }
-
- return $result;
- }
-
- /**
- * Get the last modified unix-time for this particular catalogue+variant.
- * We need to query the database to get the date_modified.
- * @param string catalogue+variant
- * @return int last modified in unix-time format.
- */
- protected function getLastModified($source)
- {
- $source = mysql_real_escape_string($source);
-
- $rs = mysql_query(
- "SELECT date_modified FROM catalogue WHERE name = '{$source}'",
- $this->db);
-
- $result = $rs ? (int)mysql_result($rs,0) : 0;
-
- return $result;
- }
-
- /**
- * Check if a particular catalogue+variant exists in the database.
- * @param string catalogue+variant
- * @return boolean true if the catalogue+variant is in the database,
- * false otherwise.
- */
- protected function isValidSource($variant)
- {
- $variant = mysql_real_escape_string ($variant);
-
- $rs = mysql_query(
- "SELECT COUNT(*) FROM catalogue WHERE name = '{$variant}'",
- $this->db);
-
- $row = mysql_fetch_array($rs,MYSQL_NUM);
-
- $result = $row && $row[0] == '1';
-
- return $result;
- }
-
- /**
- * Get all the variants of a particular catalogue.
- * @param string catalogue name
- * @return array list of all variants for this catalogue.
- */
- protected function getCatalogueList($catalogue)
- {
- $variants = explode('_',$this->culture);
-
- $catalogues = array($catalogue);
-
- $variant = null;
-
- for($i = 0, $k = count($variants); $i < $k; ++$i)
- {
- if(isset($variants[$i]{0}))
- {
- $variant .= ($variant)?'_'.$variants[$i]:$variants[$i];
- $catalogues[] = $catalogue.'.'.$variant;
- }
- }
- return array_reverse($catalogues);
- }
-
- /**
- * Retrive catalogue details, array($cat_id, $variant, $count).
- * @param string catalogue
- * @return array catalogue details, array($cat_id, $variant, $count).
- */
- private function getCatalogueDetails($catalogue='messages')
- {
- if(empty($catalogue))
- $catalogue = 'messages';
-
- $variant = $catalogue.'.'.$this->culture;
-
- $name = mysql_real_escape_string($this->getSource($variant));
-
- $rs = mysql_query("SELECT cat_id
- FROM catalogue WHERE name = '{$name}'", $this->db);
-
- if(mysql_num_rows($rs) != 1)
- return false;
-
- $cat_id = (int)mysql_result($rs,0);
-
- //first get the catalogue ID
- $rs = mysql_query(
- "SELECT count(msg_id)
- FROM trans_unit
- WHERE cat_id = {$cat_id}", $this->db);
-
- $count = (int)mysql_result($rs,0);
-
- return array($cat_id, $variant, $count);
- }
-
- /**
- * Update the catalogue last modified time.
- * @return boolean true if updated, false otherwise.
- */
- private function updateCatalogueTime($cat_id, $variant)
- {
- $time = time();
-
- $result = mysql_query("UPDATE catalogue
- SET date_modified = {$time}
- WHERE cat_id = {$cat_id}", $this->db);
-
- if(!empty($this->cache))
- $this->cache->clean($variant, $this->culture);
-
- return $result;
- }
-
- /**
- * Save the list of untranslated blocks to the translation source.
- * If the translation was not found, you should add those
- * strings to the translation source via the append() method.
- * @param string the catalogue to add to
- * @return boolean true if saved successfuly, false otherwise.
- */
- function save($catalogue='messages')
- {
- $messages = $this->untranslated;
-
- if(count($messages) <= 0) return false;
-
- $details = $this->getCatalogueDetails($catalogue);
-
- if($details)
- list($cat_id, $variant, $count) = $details;
- else
- return false;
-
- if($cat_id <= 0) return false;
- $inserted = 0;
-
- $time = time();
-
- foreach($messages as $message)
- {
- $count++; $inserted++;
- $message = mysql_real_escape_string($message);
- $statement = "INSERT INTO trans_unit
- (cat_id,id,source,date_added) VALUES
- ({$cat_id}, {$count},'{$message}',$time)";
- mysql_query($statement, $this->db);
- }
- if($inserted > 0)
- $this->updateCatalogueTime($cat_id, $variant);
-
- return $inserted > 0;
- }
-
- /**
- * Delete a particular message from the specified catalogue.
- * @param string the source message to delete.
- * @param string the catalogue to delete from.
- * @return boolean true if deleted, false otherwise.
- */
- function delete($message, $catalogue='messages')
- {
- $details = $this->getCatalogueDetails($catalogue);
- if($details)
- list($cat_id, $variant, $count) = $details;
- else
- return false;
-
- $text = mysql_real_escape_string($message);
-
- $statement = "DELETE FROM trans_unit WHERE
- cat_id = {$cat_id} AND source = '{$message}'";
- $deleted = false;
-
- mysql_query($statement, $this->db);
-
- if(mysql_affected_rows($this->db) == 1)
- $deleted = $this->updateCatalogueTime($cat_id, $variant);
-
- return $deleted;
-
- }
-
- /**
- * Update the translation.
- * @param string the source string.
- * @param string the new translation string.
- * @param string comments
- * @param string the catalogue of the translation.
- * @return boolean true if translation was updated, false otherwise.
- */
- function update($text, $target, $comments, $catalogue='messages')
- {
- $details = $this->getCatalogueDetails($catalogue);
- if($details)
- list($cat_id, $variant, $count) = $details;
- else
- return false;
-
- $comments = mysql_real_escape_string($comments);
- $target = mysql_real_escape_string($target);
- $text = mysql_real_escape_string($text);
-
- $time = time();
-
- $statement = "UPDATE trans_unit SET
- target = '{$target}',
- comments = '{$comments}',
- date_modified = '{$time}'
- WHERE cat_id = {$cat_id}
- AND source = '{$text}'";
-
- $updated = false;
-
- mysql_query($statement, $this->db);
- if(mysql_affected_rows($this->db) == 1)
- $updated = $this->updateCatalogueTime($cat_id, $variant);
-
- return $updated;
- }
-
- /**
- * Returns a list of catalogue as key and all it variants as value.
- * @return array list of catalogues
- */
- function catalogues()
- {
- $statement = 'SELECT name FROM catalogue ORDER BY name';
- $rs = mysql_query($statement, $this->db);
- $result = array();
- while($row = mysql_fetch_array($rs,MYSQL_NUM))
- {
- $details = explode('.',$row[0]);
- if(!isset($details[1])) $details[1] = null;
-
- $result[] = $details;
- }
- return $result;
- }
-
-}
-
-?>
+
+ * @version $Revision: 1.4 $ $Date: 2005/02/25 09:59:40 $
+ * @package System.I18N.core
+ */
+
+/**
+ * Get the MessageSource class file.
+ */
+require_once(dirname(__FILE__).'/MessageSource.php');
+
+/**
+ * Get the I18N utility file, contains the DSN parser.
+ */
+require_once(dirname(__FILE__).'/util.php');
+
+/**
+ * MessageSource_MySQL class.
+ *
+ * Retrive the message translation from a MySQL database.
+ *
+ * See the MessageSource::factory() method to instantiate this class.
+ *
+ * @author Xiang Wei Zhuo
+ * @version v1.0, last update on Fri Dec 24 16:58:58 EST 2004
+ * @package System.I18N.core
+ */
+class MessageSource_MySQL extends MessageSource
+{
+ /**
+ * The datasource string, full DSN to the database.
+ * @var string
+ */
+ protected $source;
+
+ /**
+ * The DSN array property, parsed by PEAR's DB DSN parser.
+ * @var array
+ */
+ protected $dns;
+
+ /**
+ * A resource link to the database
+ * @var db
+ */
+ protected $db;
+ /**
+ * Constructor.
+ * Create a new message source using MySQL.
+ * @param string MySQL datasource, in PEAR's DB DSN format.
+ * @see MessageSource::factory();
+ */
+ function __construct($source)
+ {
+ $this->source = (string)$source;
+ $this->dns = parseDSN($this->source);
+ $this->db = $this->connect();
+ }
+
+ /**
+ * Destructor, close the database connection.
+ */
+ function __destruct()
+ {
+ @mysql_close($this->db);
+ }
+
+ /**
+ * Connect to the MySQL datasource
+ * @return resource MySQL connection.
+ * @throws Exception, connection and database errors.
+ */
+ protected function connect()
+ {
+ /*static $conn;
+
+ if($conn!==null)
+ return $conn;
+ */
+ $dsninfo = $this->dns;
+
+ if (isset($dsninfo['protocol']) && $dsninfo['protocol'] == 'unix')
+ $dbhost = ':' . $dsninfo['socket'];
+ else
+ {
+ $dbhost = $dsninfo['hostspec'] ? $dsninfo['hostspec'] : 'localhost';
+ if (!empty($dsninfo['port']))
+ $dbhost .= ':' . $dsninfo['port'];
+ }
+ $user = $dsninfo['username'];
+ $pw = $dsninfo['password'];
+
+ $connect_function = 'mysql_connect';
+
+ if ($dbhost && $user && $pw)
+ $conn = @$connect_function($dbhost, $user, $pw);
+ elseif ($dbhost && $user)
+ $conn = @$connect_function($dbhost, $user);
+ elseif ($dbhost)
+ $conn = @$connect_function($dbhost);
+ else
+ $conn = false;
+
+ if (empty($conn))
+ {
+ throw new Exception('Error in connecting to '.$dsninfo);
+ }
+
+ if ($dsninfo['database'])
+ {
+ if (!@mysql_select_db($dsninfo['database'], $conn))
+ throw new Exception('Error in connecting database, dns:'.
+ $dsninfo);
+ }
+ else
+ throw new Exception('Please provide a database for message'.
+ ' translation.');
+ return $conn;
+ }
+
+ /**
+ * Get the database connection.
+ * @return db database connection.
+ */
+ public function connection()
+ {
+ return $this->db;
+ }
+
+ /**
+ * Get an array of messages for a particular catalogue and cultural
+ * variant.
+ * @param string the catalogue name + variant
+ * @return array translation messages.
+ */
+ protected function &loadData($variant)
+ {
+ $variant = mysql_real_escape_string($variant);
+
+ $statement =
+ "SELECT t.id, t.source, t.target, t.comments
+ FROM trans_unit t, catalogue c
+ WHERE c.cat_id = t.cat_id
+ AND c.name = '{$variant}'
+ ORDER BY id ASC";
+
+ $rs = mysql_query($statement,$this->db);
+
+ $result = array();
+
+ while($row = mysql_fetch_array($rs,MYSQL_NUM))
+ {
+ $source = $row[1];
+ $result[$source][] = $row[2]; //target
+ $result[$source][] = $row[0]; //id
+ $result[$source][] = $row[3]; //comments
+ }
+
+ return $result;
+ }
+
+ /**
+ * Get the last modified unix-time for this particular catalogue+variant.
+ * We need to query the database to get the date_modified.
+ * @param string catalogue+variant
+ * @return int last modified in unix-time format.
+ */
+ protected function getLastModified($source)
+ {
+ $source = mysql_real_escape_string($source);
+
+ $rs = mysql_query(
+ "SELECT date_modified FROM catalogue WHERE name = '{$source}'",
+ $this->db);
+
+ $result = $rs ? (int)mysql_result($rs,0) : 0;
+
+ return $result;
+ }
+
+ /**
+ * Check if a particular catalogue+variant exists in the database.
+ * @param string catalogue+variant
+ * @return boolean true if the catalogue+variant is in the database,
+ * false otherwise.
+ */
+ protected function isValidSource($variant)
+ {
+ $variant = mysql_real_escape_string ($variant);
+
+ $rs = mysql_query(
+ "SELECT COUNT(*) FROM catalogue WHERE name = '{$variant}'",
+ $this->db);
+
+ $row = mysql_fetch_array($rs,MYSQL_NUM);
+
+ $result = $row && $row[0] == '1';
+
+ return $result;
+ }
+
+ /**
+ * Get all the variants of a particular catalogue.
+ * @param string catalogue name
+ * @return array list of all variants for this catalogue.
+ */
+ protected function getCatalogueList($catalogue)
+ {
+ $variants = explode('_',$this->culture);
+
+ $catalogues = array($catalogue);
+
+ $variant = null;
+
+ for($i = 0, $k = count($variants); $i < $k; ++$i)
+ {
+ if(isset($variants[$i]{0}))
+ {
+ $variant .= ($variant)?'_'.$variants[$i]:$variants[$i];
+ $catalogues[] = $catalogue.'.'.$variant;
+ }
+ }
+ return array_reverse($catalogues);
+ }
+
+ /**
+ * Retrive catalogue details, array($cat_id, $variant, $count).
+ * @param string catalogue
+ * @return array catalogue details, array($cat_id, $variant, $count).
+ */
+ private function getCatalogueDetails($catalogue='messages')
+ {
+ if(empty($catalogue))
+ $catalogue = 'messages';
+
+ $variant = $catalogue.'.'.$this->culture;
+
+ $name = mysql_real_escape_string($this->getSource($variant));
+
+ $rs = mysql_query("SELECT cat_id
+ FROM catalogue WHERE name = '{$name}'", $this->db);
+
+ if(mysql_num_rows($rs) != 1)
+ return false;
+
+ $cat_id = (int)mysql_result($rs,0);
+
+ //first get the catalogue ID
+ $rs = mysql_query(
+ "SELECT count(msg_id)
+ FROM trans_unit
+ WHERE cat_id = {$cat_id}", $this->db);
+
+ $count = (int)mysql_result($rs,0);
+
+ return array($cat_id, $variant, $count);
+ }
+
+ /**
+ * Update the catalogue last modified time.
+ * @return boolean true if updated, false otherwise.
+ */
+ private function updateCatalogueTime($cat_id, $variant)
+ {
+ $time = time();
+
+ $result = mysql_query("UPDATE catalogue
+ SET date_modified = {$time}
+ WHERE cat_id = {$cat_id}", $this->db);
+
+ if(!empty($this->cache))
+ $this->cache->clean($variant, $this->culture);
+
+ return $result;
+ }
+
+ /**
+ * Save the list of untranslated blocks to the translation source.
+ * If the translation was not found, you should add those
+ * strings to the translation source via the append() method.
+ * @param string the catalogue to add to
+ * @return boolean true if saved successfuly, false otherwise.
+ */
+ function save($catalogue='messages')
+ {
+ $messages = $this->untranslated;
+
+ if(count($messages) <= 0) return false;
+
+ $details = $this->getCatalogueDetails($catalogue);
+
+ if($details)
+ list($cat_id, $variant, $count) = $details;
+ else
+ return false;
+
+ if($cat_id <= 0) return false;
+ $inserted = 0;
+
+ $time = time();
+
+ foreach($messages as $message)
+ {
+ $count++; $inserted++;
+ $message = mysql_real_escape_string($message);
+ $statement = "INSERT INTO trans_unit
+ (cat_id,id,source,date_added) VALUES
+ ({$cat_id}, {$count},'{$message}',$time)";
+ mysql_query($statement, $this->db);
+ }
+ if($inserted > 0)
+ $this->updateCatalogueTime($cat_id, $variant);
+
+ return $inserted > 0;
+ }
+
+ /**
+ * Delete a particular message from the specified catalogue.
+ * @param string the source message to delete.
+ * @param string the catalogue to delete from.
+ * @return boolean true if deleted, false otherwise.
+ */
+ function delete($message, $catalogue='messages')
+ {
+ $details = $this->getCatalogueDetails($catalogue);
+ if($details)
+ list($cat_id, $variant, $count) = $details;
+ else
+ return false;
+
+ $text = mysql_real_escape_string($message);
+
+ $statement = "DELETE FROM trans_unit WHERE
+ cat_id = {$cat_id} AND source = '{$message}'";
+ $deleted = false;
+
+ mysql_query($statement, $this->db);
+
+ if(mysql_affected_rows($this->db) == 1)
+ $deleted = $this->updateCatalogueTime($cat_id, $variant);
+
+ return $deleted;
+
+ }
+
+ /**
+ * Update the translation.
+ * @param string the source string.
+ * @param string the new translation string.
+ * @param string comments
+ * @param string the catalogue of the translation.
+ * @return boolean true if translation was updated, false otherwise.
+ */
+ function update($text, $target, $comments, $catalogue='messages')
+ {
+ $details = $this->getCatalogueDetails($catalogue);
+ if($details)
+ list($cat_id, $variant, $count) = $details;
+ else
+ return false;
+
+ $comments = mysql_real_escape_string($comments);
+ $target = mysql_real_escape_string($target);
+ $text = mysql_real_escape_string($text);
+
+ $time = time();
+
+ $statement = "UPDATE trans_unit SET
+ target = '{$target}',
+ comments = '{$comments}',
+ date_modified = '{$time}'
+ WHERE cat_id = {$cat_id}
+ AND source = '{$text}'";
+
+ $updated = false;
+
+ mysql_query($statement, $this->db);
+ if(mysql_affected_rows($this->db) == 1)
+ $updated = $this->updateCatalogueTime($cat_id, $variant);
+
+ return $updated;
+ }
+
+ /**
+ * Returns a list of catalogue as key and all it variants as value.
+ * @return array list of catalogues
+ */
+ function catalogues()
+ {
+ $statement = 'SELECT name FROM catalogue ORDER BY name';
+ $rs = mysql_query($statement, $this->db);
+ $result = array();
+ while($row = mysql_fetch_array($rs,MYSQL_NUM))
+ {
+ $details = explode('.',$row[0]);
+ if(!isset($details[1])) $details[1] = null;
+
+ $result[] = $details;
+ }
+ return $result;
+ }
+
+}
+
+?>
diff --git a/framework/I18N/core/MessageSource_SQLite.php b/framework/I18N/core/MessageSource_SQLite.php
index 22518bba..4694e018 100644
--- a/framework/I18N/core/MessageSource_SQLite.php
+++ b/framework/I18N/core/MessageSource_SQLite.php
@@ -1,354 +1,354 @@
-
- * @version $Revision: 1.4 $ $Date: 2005/02/25 09:59:40 $
- * @package System.I18N.core
- */
-
-/**
- * Get the MessageSource class file.
- */
-require_once(dirname(__FILE__).'/MessageSource.php');
-
-/**
- * Get the I18N utility file, contains the DSN parser.
- */
-require_once(dirname(__FILE__).'/util.php');
-
-/**
- * MessageSource_SQLite class.
- *
- * Retrive the message translation from a SQLite database.
- *
- * See the MessageSource::factory() method to instantiate this class.
- *
- * @author Xiang Wei Zhuo
- * @version v1.0, last update on Fri Dec 24 16:58:58 EST 2004
- * @package System.I18N.core
- */
-class MessageSource_SQLite extends MessageSource
-{
- /**
- * The SQLite datasource, the filename of the database.
- * @var string
- */
- protected $source;
-
- /**
- * Constructor.
- * Create a new message source using SQLite.
- * @see MessageSource::factory();
- * @param string SQLite datasource, in PEAR's DB DSN format.
- */
- function __construct($source)
- {
- $dsn = parseDSN((string)$source);
- $this->source = $dsn['database'];
- }
-
- /**
- * Get an array of messages for a particular catalogue and cultural
- * variant.
- * @param string the catalogue name + variant
- * @return array translation messages.
- */
- protected function &loadData($variant)
- {
- $variant = sqlite_escape_string($variant);
-
- $statement =
- "SELECT t.id, t.source, t.target, t.comments
- FROM trans_unit t, catalogue c
- WHERE c.cat_id = t.cat_id
- AND c.name = '{$variant}'
- ORDER BY id ASC";
-
- $db = sqlite_open($this->source);
- $rs = sqlite_query($statement, $db);
-
- $result = array();
-
- while($row = sqlite_fetch_array($rs,SQLITE_NUM))
- {
- $source = $row[1];
- $result[$source][] = $row[2]; //target
- $result[$source][] = $row[0]; //id
- $result[$source][] = $row[3]; //comments
- }
-
- sqlite_close($db);
-
- return $result;
- }
-
- /**
- * Get the last modified unix-time for this particular catalogue+variant.
- * We need to query the database to get the date_modified.
- * @param string catalogue+variant
- * @return int last modified in unix-time format.
- */
- protected function getLastModified($source)
- {
- $source = sqlite_escape_string($source);
-
- $db = sqlite_open($this->source);
-
- $rs = sqlite_query(
- "SELECT date_modified FROM catalogue WHERE name = '{$source}'",
- $db);
-
- $result = $rs ? (int)sqlite_fetch_single($rs) : 0;
-
- sqlite_close($db);
-
- return $result;
- }
-
- /**
- * Check if a particular catalogue+variant exists in the database.
- * @param string catalogue+variant
- * @return boolean true if the catalogue+variant is in the database,
- * false otherwise.
- */
- protected function isValidSource($variant)
- {
- $variant = sqlite_escape_string($variant);
- $db = sqlite_open($this->source);
- $rs = sqlite_query(
- "SELECT COUNT(*) FROM catalogue WHERE name = '{$variant}'",
- $db);
- $result = $rs && (int)sqlite_fetch_single($rs);
- sqlite_close($db);
-
- return $result;
- }
-
- /**
- * Get all the variants of a particular catalogue.
- * @param string catalogue name
- * @return array list of all variants for this catalogue.
- */
- protected function getCatalogueList($catalogue)
- {
- $variants = explode('_',$this->culture);
-
- $catalogues = array($catalogue);
-
- $variant = null;
-
- for($i = 0, $k = count($variants); $i < $k; ++$i)
- {
- if(isset($variants[$i]{0}))
- {
- $variant .= ($variant)?'_'.$variants[$i]:$variants[$i];
- $catalogues[] = $catalogue.'.'.$variant;
- }
- }
- return array_reverse($catalogues);
- }
-
- /**
- * Retrive catalogue details, array($cat_id, $variant, $count).
- * @param string catalogue
- * @return array catalogue details, array($cat_id, $variant, $count).
- */
- private function getCatalogueDetails($catalogue='messages')
- {
- if(empty($catalogue))
- $catalogue = 'messages';
-
- $variant = $catalogue.'.'.$this->culture;
-
- $name = sqlite_escape_string($this->getSource($variant));
-
- $db = sqlite_open($this->source);
-
- $rs = sqlite_query("SELECT cat_id
- FROM catalogue WHERE name = '{$name}'", $db);
-
- if(sqlite_num_rows($rs) != 1)
- return false;
-
- $cat_id = (int)sqlite_fetch_single($rs);
-
- //first get the catalogue ID
- $rs = sqlite_query(
- "SELECT count(msg_id)
- FROM trans_unit
- WHERE cat_id = {$cat_id}", $db);
-
- $count = (int)sqlite_fetch_single($rs);
-
- sqlite_close($db);
-
- return array($cat_id, $variant, $count);
- }
-
- /**
- * Update the catalogue last modified time.
- * @return boolean true if updated, false otherwise.
- */
- private function updateCatalogueTime($cat_id, $variant, $db)
- {
- $time = time();
-
- $result = sqlite_query("UPDATE catalogue
- SET date_modified = {$time}
- WHERE cat_id = {$cat_id}", $db);
-
- if(!empty($this->cache))
- $this->cache->clean($variant, $this->culture);
-
- return $result;
- }
-
- /**
- * Save the list of untranslated blocks to the translation source.
- * If the translation was not found, you should add those
- * strings to the translation source via the append() method.
- * @param string the catalogue to add to
- * @return boolean true if saved successfuly, false otherwise.
- */
- function save($catalogue='messages')
- {
- $messages = $this->untranslated;
-
- if(count($messages) <= 0) return false;
-
- $details = $this->getCatalogueDetails($catalogue);
-
- if($details)
- list($cat_id, $variant, $count) = $details;
- else
- return false;
-
- if($cat_id <= 0) return false;
- $inserted = 0;
-
- $db = sqlite_open($this->source);
- $time = time();
-
- foreach($messages as $message)
- {
- $message = sqlite_escape_string($message);
- $statement = "INSERT INTO trans_unit
- (cat_id,id,source,date_added) VALUES
- ({$cat_id}, {$count},'{$message}',$time)";
- if(sqlite_query($statement, $db))
- {
- $count++; $inserted++;
- }
- }
- if($inserted > 0)
- $this->updateCatalogueTime($cat_id, $variant, $db);
-
- sqlite_close($db);
-
- return $inserted > 0;
- }
-
- /**
- * Update the translation.
- * @param string the source string.
- * @param string the new translation string.
- * @param string comments
- * @param string the catalogue of the translation.
- * @return boolean true if translation was updated, false otherwise.
- */
- function update($text, $target, $comments, $catalogue='messages')
- {
- $details = $this->getCatalogueDetails($catalogue);
- if($details)
- list($cat_id, $variant, $count) = $details;
- else
- return false;
-
- $comments = sqlite_escape_string($comments);
- $target = sqlite_escape_string($target);
- $text = sqlite_escape_string($text);
-
- $time = time();
-
- $db = sqlite_open($this->source);
-
- $statement = "UPDATE trans_unit SET
- target = '{$target}',
- comments = '{$comments}',
- date_modified = '{$time}'
- WHERE cat_id = {$cat_id}
- AND source = '{$text}'";
-
- $updated = false;
-
- if(sqlite_query($statement, $db))
- $updated = $this->updateCatalogueTime($cat_id, $variant, $db);
-
- sqlite_close($db);
-
- return $updated;
- }
-
- /**
- * Delete a particular message from the specified catalogue.
- * @param string the source message to delete.
- * @param string the catalogue to delete from.
- * @return boolean true if deleted, false otherwise.
- */
- function delete($message, $catalogue='messages')
- {
- $details = $this->getCatalogueDetails($catalogue);
- if($details)
- list($cat_id, $variant, $count) = $details;
- else
- return false;
-
- $db = sqlite_open($this->source);
- $text = sqlite_escape_string($message);
-
- $statement = "DELETE FROM trans_unit WHERE
- cat_id = {$cat_id} AND source = '{$message}'";
- $deleted = false;
-
- if(sqlite_query($statement, $db))
- $deleted = $this->updateCatalogueTime($cat_id, $variant, $db);
-
- sqlite_close($db);
-
- return $deleted;
- }
-
- /**
- * Returns a list of catalogue as key and all it variants as value.
- * @return array list of catalogues
- */
- function catalogues()
- {
- $db = sqlite_open($this->source);
- $statement = 'SELECT name FROM catalogue ORDER BY name';
- $rs = sqlite_query($statement, $db);
- $result = array();
- while($row = sqlite_fetch_array($rs,SQLITE_NUM))
- {
- $details = explode('.',$row[0]);
- if(!isset($details[1])) $details[1] = null;
-
- $result[] = $details;
- }
- sqlite_close($db);
- return $result;
- }
-}
-
-?>
+
+ * @version $Revision: 1.4 $ $Date: 2005/02/25 09:59:40 $
+ * @package System.I18N.core
+ */
+
+/**
+ * Get the MessageSource class file.
+ */
+require_once(dirname(__FILE__).'/MessageSource.php');
+
+/**
+ * Get the I18N utility file, contains the DSN parser.
+ */
+require_once(dirname(__FILE__).'/util.php');
+
+/**
+ * MessageSource_SQLite class.
+ *
+ * Retrive the message translation from a SQLite database.
+ *
+ * See the MessageSource::factory() method to instantiate this class.
+ *
+ * @author Xiang Wei Zhuo
+ * @version v1.0, last update on Fri Dec 24 16:58:58 EST 2004
+ * @package System.I18N.core
+ */
+class MessageSource_SQLite extends MessageSource
+{
+ /**
+ * The SQLite datasource, the filename of the database.
+ * @var string
+ */
+ protected $source;
+
+ /**
+ * Constructor.
+ * Create a new message source using SQLite.
+ * @see MessageSource::factory();
+ * @param string SQLite datasource, in PEAR's DB DSN format.
+ */
+ function __construct($source)
+ {
+ $dsn = parseDSN((string)$source);
+ $this->source = $dsn['database'];
+ }
+
+ /**
+ * Get an array of messages for a particular catalogue and cultural
+ * variant.
+ * @param string the catalogue name + variant
+ * @return array translation messages.
+ */
+ protected function &loadData($variant)
+ {
+ $variant = sqlite_escape_string($variant);
+
+ $statement =
+ "SELECT t.id, t.source, t.target, t.comments
+ FROM trans_unit t, catalogue c
+ WHERE c.cat_id = t.cat_id
+ AND c.name = '{$variant}'
+ ORDER BY id ASC";
+
+ $db = sqlite_open($this->source);
+ $rs = sqlite_query($statement, $db);
+
+ $result = array();
+
+ while($row = sqlite_fetch_array($rs,SQLITE_NUM))
+ {
+ $source = $row[1];
+ $result[$source][] = $row[2]; //target
+ $result[$source][] = $row[0]; //id
+ $result[$source][] = $row[3]; //comments
+ }
+
+ sqlite_close($db);
+
+ return $result;
+ }
+
+ /**
+ * Get the last modified unix-time for this particular catalogue+variant.
+ * We need to query the database to get the date_modified.
+ * @param string catalogue+variant
+ * @return int last modified in unix-time format.
+ */
+ protected function getLastModified($source)
+ {
+ $source = sqlite_escape_string($source);
+
+ $db = sqlite_open($this->source);
+
+ $rs = sqlite_query(
+ "SELECT date_modified FROM catalogue WHERE name = '{$source}'",
+ $db);
+
+ $result = $rs ? (int)sqlite_fetch_single($rs) : 0;
+
+ sqlite_close($db);
+
+ return $result;
+ }
+
+ /**
+ * Check if a particular catalogue+variant exists in the database.
+ * @param string catalogue+variant
+ * @return boolean true if the catalogue+variant is in the database,
+ * false otherwise.
+ */
+ protected function isValidSource($variant)
+ {
+ $variant = sqlite_escape_string($variant);
+ $db = sqlite_open($this->source);
+ $rs = sqlite_query(
+ "SELECT COUNT(*) FROM catalogue WHERE name = '{$variant}'",
+ $db);
+ $result = $rs && (int)sqlite_fetch_single($rs);
+ sqlite_close($db);
+
+ return $result;
+ }
+
+ /**
+ * Get all the variants of a particular catalogue.
+ * @param string catalogue name
+ * @return array list of all variants for this catalogue.
+ */
+ protected function getCatalogueList($catalogue)
+ {
+ $variants = explode('_',$this->culture);
+
+ $catalogues = array($catalogue);
+
+ $variant = null;
+
+ for($i = 0, $k = count($variants); $i < $k; ++$i)
+ {
+ if(isset($variants[$i]{0}))
+ {
+ $variant .= ($variant)?'_'.$variants[$i]:$variants[$i];
+ $catalogues[] = $catalogue.'.'.$variant;
+ }
+ }
+ return array_reverse($catalogues);
+ }
+
+ /**
+ * Retrive catalogue details, array($cat_id, $variant, $count).
+ * @param string catalogue
+ * @return array catalogue details, array($cat_id, $variant, $count).
+ */
+ private function getCatalogueDetails($catalogue='messages')
+ {
+ if(empty($catalogue))
+ $catalogue = 'messages';
+
+ $variant = $catalogue.'.'.$this->culture;
+
+ $name = sqlite_escape_string($this->getSource($variant));
+
+ $db = sqlite_open($this->source);
+
+ $rs = sqlite_query("SELECT cat_id
+ FROM catalogue WHERE name = '{$name}'", $db);
+
+ if(sqlite_num_rows($rs) != 1)
+ return false;
+
+ $cat_id = (int)sqlite_fetch_single($rs);
+
+ //first get the catalogue ID
+ $rs = sqlite_query(
+ "SELECT count(msg_id)
+ FROM trans_unit
+ WHERE cat_id = {$cat_id}", $db);
+
+ $count = (int)sqlite_fetch_single($rs);
+
+ sqlite_close($db);
+
+ return array($cat_id, $variant, $count);
+ }
+
+ /**
+ * Update the catalogue last modified time.
+ * @return boolean true if updated, false otherwise.
+ */
+ private function updateCatalogueTime($cat_id, $variant, $db)
+ {
+ $time = time();
+
+ $result = sqlite_query("UPDATE catalogue
+ SET date_modified = {$time}
+ WHERE cat_id = {$cat_id}", $db);
+
+ if(!empty($this->cache))
+ $this->cache->clean($variant, $this->culture);
+
+ return $result;
+ }
+
+ /**
+ * Save the list of untranslated blocks to the translation source.
+ * If the translation was not found, you should add those
+ * strings to the translation source via the append() method.
+ * @param string the catalogue to add to
+ * @return boolean true if saved successfuly, false otherwise.
+ */
+ function save($catalogue='messages')
+ {
+ $messages = $this->untranslated;
+
+ if(count($messages) <= 0) return false;
+
+ $details = $this->getCatalogueDetails($catalogue);
+
+ if($details)
+ list($cat_id, $variant, $count) = $details;
+ else
+ return false;
+
+ if($cat_id <= 0) return false;
+ $inserted = 0;
+
+ $db = sqlite_open($this->source);
+ $time = time();
+
+ foreach($messages as $message)
+ {
+ $message = sqlite_escape_string($message);
+ $statement = "INSERT INTO trans_unit
+ (cat_id,id,source,date_added) VALUES
+ ({$cat_id}, {$count},'{$message}',$time)";
+ if(sqlite_query($statement, $db))
+ {
+ $count++; $inserted++;
+ }
+ }
+ if($inserted > 0)
+ $this->updateCatalogueTime($cat_id, $variant, $db);
+
+ sqlite_close($db);
+
+ return $inserted > 0;
+ }
+
+ /**
+ * Update the translation.
+ * @param string the source string.
+ * @param string the new translation string.
+ * @param string comments
+ * @param string the catalogue of the translation.
+ * @return boolean true if translation was updated, false otherwise.
+ */
+ function update($text, $target, $comments, $catalogue='messages')
+ {
+ $details = $this->getCatalogueDetails($catalogue);
+ if($details)
+ list($cat_id, $variant, $count) = $details;
+ else
+ return false;
+
+ $comments = sqlite_escape_string($comments);
+ $target = sqlite_escape_string($target);
+ $text = sqlite_escape_string($text);
+
+ $time = time();
+
+ $db = sqlite_open($this->source);
+
+ $statement = "UPDATE trans_unit SET
+ target = '{$target}',
+ comments = '{$comments}',
+ date_modified = '{$time}'
+ WHERE cat_id = {$cat_id}
+ AND source = '{$text}'";
+
+ $updated = false;
+
+ if(sqlite_query($statement, $db))
+ $updated = $this->updateCatalogueTime($cat_id, $variant, $db);
+
+ sqlite_close($db);
+
+ return $updated;
+ }
+
+ /**
+ * Delete a particular message from the specified catalogue.
+ * @param string the source message to delete.
+ * @param string the catalogue to delete from.
+ * @return boolean true if deleted, false otherwise.
+ */
+ function delete($message, $catalogue='messages')
+ {
+ $details = $this->getCatalogueDetails($catalogue);
+ if($details)
+ list($cat_id, $variant, $count) = $details;
+ else
+ return false;
+
+ $db = sqlite_open($this->source);
+ $text = sqlite_escape_string($message);
+
+ $statement = "DELETE FROM trans_unit WHERE
+ cat_id = {$cat_id} AND source = '{$message}'";
+ $deleted = false;
+
+ if(sqlite_query($statement, $db))
+ $deleted = $this->updateCatalogueTime($cat_id, $variant, $db);
+
+ sqlite_close($db);
+
+ return $deleted;
+ }
+
+ /**
+ * Returns a list of catalogue as key and all it variants as value.
+ * @return array list of catalogues
+ */
+ function catalogues()
+ {
+ $db = sqlite_open($this->source);
+ $statement = 'SELECT name FROM catalogue ORDER BY name';
+ $rs = sqlite_query($statement, $db);
+ $result = array();
+ while($row = sqlite_fetch_array($rs,SQLITE_NUM))
+ {
+ $details = explode('.',$row[0]);
+ if(!isset($details[1])) $details[1] = null;
+
+ $result[] = $details;
+ }
+ sqlite_close($db);
+ return $result;
+ }
+}
+
+?>
diff --git a/framework/I18N/core/MessageSource_XLIFF.php b/framework/I18N/core/MessageSource_XLIFF.php
index 207fc920..198a1290 100644
--- a/framework/I18N/core/MessageSource_XLIFF.php
+++ b/framework/I18N/core/MessageSource_XLIFF.php
@@ -1,529 +1,529 @@
-
- * @version $Revision: 1.8 $ $Date: 2005/12/17 06:11:28 $
- * @package System.I18N.core
- */
-
-/**
- * Get the MessageSource class file.
- */
-require_once(dirname(__FILE__).'/MessageSource.php');
-
-/**
- * MessageSource_XLIFF class.
- *
- * Using XML XLIFF format as the message source for translation.
- * Details and example of XLIFF can be found in the following URLs.
- *
- * # http://www.opentag.com/xliff.htm
- * # http://www-106.ibm.com/developerworks/xml/library/x-localis2/
- *
- * See the MessageSource::factory() method to instantiate this class.
- *
- * @author Xiang Wei Zhuo
- * @version v1.0, last update on Fri Dec 24 16:18:44 EST 2004
- * @package System.I18N.core
- */
-class MessageSource_XLIFF extends MessageSource
-{
- /**
- * Message data filename extension.
- * @var string
- */
- protected $dataExt = '.xml';
-
- /**
- * Separator between culture name and source.
- * @var string
- */
- protected $dataSeparator = '.';
-
- /**
- * Constructor.
- * @param string the directory where the messages are stored.
- * @see MessageSource::factory();
- */
- function __construct($source)
- {
- $this->source = (string)$source;
- }
-
- /**
- * Load the messages from a XLIFF file.
- * @param string XLIFF file.
- * @return array of messages.
- */
- protected function &loadData($filename)
- {
- //load it.
- if(false === ($XML = simplexml_load_file($filename))) {
- return false;
- }
-
- $translationUnit = $XML->xpath('//trans-unit');
-
- $translations = array();
-
- foreach($translationUnit as $unit)
- {
- $source = (string)$unit->source;
- $translations[$source][] = (string)$unit->target;
- $translations[$source][] = (string)$unit['id'];
- $translations[$source][] = (string)$unit->note;
- }
-
- return $translations;
- }
-
- /**
- * Get the last modified unix-time for this particular catalogue+variant.
- * Just use the file modified time.
- * @param string catalogue+variant
- * @return int last modified in unix-time format.
- */
- protected function getLastModified($source)
- {
- return is_file($source) ? filemtime($source) : 0;
- }
-
- /**
- * Get the XLIFF file for a specific message catalogue and cultural
- * vairant.
- * @param string message catalogue
- * @return string full path to the XLIFF file.
- */
- protected function getSource($variant)
- {
- return $this->source.'/'.$variant;
- }
-
- /**
- * Determin if the XLIFF file source is valid.
- * @param string XLIFF file
- * @return boolean true if valid, false otherwise.
- */
- protected function isValidSource($source)
- {
- return is_file($source);
- }
-
- /**
- * Get all the variants of a particular catalogue.
- * @param string catalogue name
- * @return array list of all variants for this catalogue.
- */
- protected function getCatalogueList($catalogue)
- {
- $variants = explode('_',$this->culture);
- $source = $catalogue.$this->dataExt;
- $catalogues = array($source);
- $variant = null;
-
- for($i = 0, $k = count($variants); $i < $k; ++$i)
- {
- if(isset($variants[$i]{0}))
- {
- $variant .= ($variant)?'_'.$variants[$i]:$variants[$i];
- $catalogues[] = $catalogue.$this->dataSeparator.$variant.$this->dataExt;
- }
- }
-
- $byDir = $this->getCatalogueByDir($catalogue);
- $catalogues = array_merge($byDir,array_reverse($catalogues));
- $files = array();
-
- foreach($catalogues as $file)
- {
- $files[] = $file;
- $files[] = preg_replace('/\.xml$/', '.xlf', $file);
- }
-
- return $files;
- }
-
- /**
- * Traverse through the directory structure to find the catalogues.
- * This should only be called by getCatalogueList()
- * @param string a particular catalogue.
- * @return array a list of catalogues.
- * @see getCatalogueList()
- */
- private function getCatalogueByDir($catalogue)
- {
- $variants = explode('_',$this->culture);
- $catalogues = array();
- $variant = null;
-
- for($i = 0, $k = count($variants); $i < $k; ++$i)
- {
- if(isset($variants[$i]{0}))
- {
- $variant .= ($variant)?'_'.$variants[$i]:$variants[$i];
- $catalogues[] = $variant.'/'.$catalogue.$this->dataExt;
- }
- }
-
- return array_reverse($catalogues);
- }
-
- /**
- * Returns a list of catalogue and its culture ID.
- * E.g. array('messages','en_AU')
- * @return array list of catalogues
- * @see getCatalogues()
- */
- public function catalogues()
- {
- return $this->getCatalogues();
- }
-
- /**
- * Returns a list of catalogue and its culture ID. This takes care
- * of directory structures.
- * E.g. array('messages','en_AU')
- * @return array list of catalogues
- */
- protected function getCatalogues($dir=null,$variant=null)
- {
- $dir = $dir?$dir:$this->source;
- $files = scandir($dir);
- $catalogue = array();
-
- foreach($files as $file)
- {
- if(is_dir($dir.'/'.$file) && preg_match('/^[a-z]{2}(_[A-Z]{2,3})?$/',$file)) {
- $catalogue = array_merge(
- $catalogue,
- $this->getCatalogues($dir.'/'.$file, $file)
- );
- }
-
- $pos = strpos($file,$this->dataExt);
- if($pos >0 && substr($file, -1*strlen($this->dataExt)) == $this->dataExt)
- {
- $name = substr($file,0,$pos);
- $dot = strrpos($name,$this->dataSeparator);
- $culture = $variant;
- $cat = $name;
-
- if(is_int($dot))
- {
- $culture = substr($name, $dot+1, strlen($name));
- $cat = substr($name, 0, $dot);
- }
-
- $details[0] = $cat;
- $details[1] = $culture;
- $catalogue[] = $details;
- }
- }
- sort($catalogue);
- return $catalogue;
- }
-
- /**
- * Get the variant for a catalogue depending on the current culture.
- * @param string catalogue
- * @return string the variant.
- * @see save()
- * @see update()
- * @see delete()
- */
- private function getVariants($catalogue='messages')
- {
- if($catalogue === null) {
- $catalogue = 'messages';
- }
-
- foreach($this->getCatalogueList($catalogue) as $variant)
- {
- $file = $this->getSource($variant);
- if(is_file($file)) {
- return array($variant, $file);
- }
- }
- return false;
- }
-
- /**
- * Save the list of untranslated blocks to the translation source.
- * If the translation was not found, you should add those
- * strings to the translation source via the append() method.
- * @param string the catalogue to add to
- * @return boolean true if saved successfuly, false otherwise.
- */
- public function save($catalogue='messages')
- {
- $messages = $this->untranslated;
- if(count($messages) <= 0) {
- return false;
- }
-
- $variants = $this->getVariants($catalogue);
-
- if($variants) {
- list($variant, $filename) = $variants;
- } else {
- list($variant, $filename) = $this->createMessageTemplate($catalogue);
- }
-
- if(is_writable($filename) == false) {
- throw new TIOException("Unable to save to file {$filename}, file must be writable.");
- }
-
- //create a new dom, import the existing xml
- $dom = new DOMDocument();
- $dom->load($filename);
-
- //find the body element
- $xpath = new DomXPath($dom);
- $body = $xpath->query('//body')->item(0);
-
- $lastNodes = $xpath->query('//trans-unit[last()]');
- if(($last=$lastNodes->item(0))!==null) {
- $count = (int)$last->getAttribute('id');
- } else {
- $count = 0;
- }
-
- //for each message add it to the XML file using DOM
- foreach($messages as $message)
- {
- $unit = $dom->createElement('trans-unit');
- $unit->setAttribute('id',++$count);
-
- $source = $dom->createElement('source');
- $source->appendChild($dom->createCDATASection($message));
-
- $target = $dom->createElement('target');
- $target->appendChild($dom->createCDATASection(''));
-
- $unit->appendChild($dom->createTextNode("\n"));
- $unit->appendChild($source);
- $unit->appendChild($dom->createTextNode("\n"));
- $unit->appendChild($target);
- $unit->appendChild($dom->createTextNode("\n"));
-
- $body->appendChild($dom->createTextNode("\n"));
- $body->appendChild($unit);
- $body->appendChild($dom->createTextNode("\n"));
- }
-
-
- $fileNode = $xpath->query('//file')->item(0);
- $fileNode->setAttribute('date', @date('Y-m-d\TH:i:s\Z'));
-
- //save it and clear the cache for this variant
- $dom->save($filename);
- if(!empty($this->cache)) {
- $this->cache->clean($variant, $this->culture);
- }
-
- return true;
- }
-
- /**
- * Update the translation.
- * @param string the source string.
- * @param string the new translation string.
- * @param string comments
- * @param string the catalogue to save to.
- * @return boolean true if translation was updated, false otherwise.
- */
- public function update($text, $target, $comments, $catalogue='messages')
- {
- $variants = $this->getVariants($catalogue);
-
- if($variants) {
- list($variant, $filename) = $variants;
- } else {
- return false;
- }
-
- if(is_writable($filename) == false) {
- throw new TIOException("Unable to update file {$filename}, file must be writable.");
- }
-
- //create a new dom, import the existing xml
- $dom = DOMDocument::load($filename);
-
- //find the body element
- $xpath = new DomXPath($dom);
- $units = $xpath->query('//trans-unit');
-
- //for each of the existin units
- foreach($units as $unit)
- {
- $found = false;
- $targetted = false;
- $commented = false;
-
- //in each unit, need to find the source, target and comment nodes
- //it will assume that the source is before the target.
- foreach($unit->childNodes as $node)
- {
- //source node
- if($node->nodeName == 'source' && $node->firstChild->wholeText == $text) {
- $found = true;
- }
-
- //found source, get the target and notes
- if($found)
- {
- //set the new translated string
- if($node->nodeName == 'target')
- {
- $node->nodeValue = $target;
- $targetted = true;
- }
-
- //set the notes
- if(!empty($comments) && $node->nodeName == 'note')
- {
- $node->nodeValue = $comments;
- $commented = true;
- }
- }
- }
-
- //append a target
- if($found && !$targetted) {
- $unit->appendChild($dom->createElement('target',$target));
- }
-
- //append a note
- if($found && !$commented && !empty($comments)) {
- $unit->appendChild($dom->createElement('note',$comments));
- }
-
- //finished searching
- if($found) {
- break;
- }
- }
-
- $fileNode = $xpath->query('//file')->item(0);
- $fileNode->setAttribute('date', @date('Y-m-d\TH:i:s\Z'));
-
- if($dom->save($filename) >0)
- {
- if(!empty($this->cache)) {
- $this->cache->clean($variant, $this->culture);
- }
-
- return true;
- }
-
- return false;
- }
-
- /**
- * Delete a particular message from the specified catalogue.
- * @param string the source message to delete.
- * @param string the catalogue to delete from.
- * @return boolean true if deleted, false otherwise.
- */
- public function delete($message, $catalogue='messages')
- {
- $variants = $this->getVariants($catalogue);
- if($variants) {
- list($variant, $filename) = $variants;
- } else {
- return false;
- }
-
- if(is_writable($filename) == false) {
- throw new TIOException("Unable to modify file {$filename}, file must be writable.");
- }
-
- //create a new dom, import the existing xml
- $dom = DOMDocument::load($filename);
-
- //find the body element
- $xpath = new DomXPath($dom);
- $units = $xpath->query('//trans-unit');
-
- //for each of the existin units
- foreach($units as $unit)
- {
- //in each unit, need to find the source, target and comment nodes
- //it will assume that the source is before the target.
- foreach($unit->childNodes as $node)
- {
- //source node
- if($node->nodeName == 'source' && $node->firstChild->wholeText == $message)
- {
- //we found it, remove and save the xml file.
- $unit->parentNode->removeChild($unit);
- $fileNode = $xpath->query('//file')->item(0);
- $fileNode->setAttribute('date', @date('Y-m-d\TH:i:s\Z'));
-
- if(false !== $dom->save($filename)) {
- if(!empty($this->cache)) {
- $this->cache->clean($variant, $this->culture);
- }
- return true;
- }
-
- return false;
- }
- }
- }
-
- return false;
- }
-
- protected function createMessageTemplate($catalogue)
- {
- if($catalogue === null) {
- $catalogue = 'messages';
- }
-
- $variants = $this->getCatalogueList($catalogue);
- $variant = array_shift($variants);
- $file = $this->getSource($variant);
- $dir = dirname($file);
-
- if(!is_dir($dir)) {
- @mkdir($dir);
- @chmod($dir,PRADO_CHMOD);
- }
-
- if(!is_dir($dir)) {
- throw new TException("Unable to create directory $dir");
- }
-
- file_put_contents($file, $this->getTemplate($catalogue));
- chmod($file, PRADO_CHMOD);
-
- return array($variant, $file);
- }
-
- protected function getTemplate($catalogue)
- {
- $date = @date('c');
- $xml = <<
-
-
-
-
-
-
-EOD;
- return $xml;
- }
-}
+
+ * @version $Revision: 1.8 $ $Date: 2005/12/17 06:11:28 $
+ * @package System.I18N.core
+ */
+
+/**
+ * Get the MessageSource class file.
+ */
+require_once(dirname(__FILE__).'/MessageSource.php');
+
+/**
+ * MessageSource_XLIFF class.
+ *
+ * Using XML XLIFF format as the message source for translation.
+ * Details and example of XLIFF can be found in the following URLs.
+ *
+ * # http://www.opentag.com/xliff.htm
+ * # http://www-106.ibm.com/developerworks/xml/library/x-localis2/
+ *
+ * See the MessageSource::factory() method to instantiate this class.
+ *
+ * @author Xiang Wei Zhuo
+ * @version v1.0, last update on Fri Dec 24 16:18:44 EST 2004
+ * @package System.I18N.core
+ */
+class MessageSource_XLIFF extends MessageSource
+{
+ /**
+ * Message data filename extension.
+ * @var string
+ */
+ protected $dataExt = '.xml';
+
+ /**
+ * Separator between culture name and source.
+ * @var string
+ */
+ protected $dataSeparator = '.';
+
+ /**
+ * Constructor.
+ * @param string the directory where the messages are stored.
+ * @see MessageSource::factory();
+ */
+ function __construct($source)
+ {
+ $this->source = (string)$source;
+ }
+
+ /**
+ * Load the messages from a XLIFF file.
+ * @param string XLIFF file.
+ * @return array of messages.
+ */
+ protected function &loadData($filename)
+ {
+ //load it.
+ if(false === ($XML = simplexml_load_file($filename))) {
+ return false;
+ }
+
+ $translationUnit = $XML->xpath('//trans-unit');
+
+ $translations = array();
+
+ foreach($translationUnit as $unit)
+ {
+ $source = (string)$unit->source;
+ $translations[$source][] = (string)$unit->target;
+ $translations[$source][] = (string)$unit['id'];
+ $translations[$source][] = (string)$unit->note;
+ }
+
+ return $translations;
+ }
+
+ /**
+ * Get the last modified unix-time for this particular catalogue+variant.
+ * Just use the file modified time.
+ * @param string catalogue+variant
+ * @return int last modified in unix-time format.
+ */
+ protected function getLastModified($source)
+ {
+ return is_file($source) ? filemtime($source) : 0;
+ }
+
+ /**
+ * Get the XLIFF file for a specific message catalogue and cultural
+ * vairant.
+ * @param string message catalogue
+ * @return string full path to the XLIFF file.
+ */
+ protected function getSource($variant)
+ {
+ return $this->source.'/'.$variant;
+ }
+
+ /**
+ * Determin if the XLIFF file source is valid.
+ * @param string XLIFF file
+ * @return boolean true if valid, false otherwise.
+ */
+ protected function isValidSource($source)
+ {
+ return is_file($source);
+ }
+
+ /**
+ * Get all the variants of a particular catalogue.
+ * @param string catalogue name
+ * @return array list of all variants for this catalogue.
+ */
+ protected function getCatalogueList($catalogue)
+ {
+ $variants = explode('_',$this->culture);
+ $source = $catalogue.$this->dataExt;
+ $catalogues = array($source);
+ $variant = null;
+
+ for($i = 0, $k = count($variants); $i < $k; ++$i)
+ {
+ if(isset($variants[$i]{0}))
+ {
+ $variant .= ($variant)?'_'.$variants[$i]:$variants[$i];
+ $catalogues[] = $catalogue.$this->dataSeparator.$variant.$this->dataExt;
+ }
+ }
+
+ $byDir = $this->getCatalogueByDir($catalogue);
+ $catalogues = array_merge($byDir,array_reverse($catalogues));
+ $files = array();
+
+ foreach($catalogues as $file)
+ {
+ $files[] = $file;
+ $files[] = preg_replace('/\.xml$/', '.xlf', $file);
+ }
+
+ return $files;
+ }
+
+ /**
+ * Traverse through the directory structure to find the catalogues.
+ * This should only be called by getCatalogueList()
+ * @param string a particular catalogue.
+ * @return array a list of catalogues.
+ * @see getCatalogueList()
+ */
+ private function getCatalogueByDir($catalogue)
+ {
+ $variants = explode('_',$this->culture);
+ $catalogues = array();
+ $variant = null;
+
+ for($i = 0, $k = count($variants); $i < $k; ++$i)
+ {
+ if(isset($variants[$i]{0}))
+ {
+ $variant .= ($variant)?'_'.$variants[$i]:$variants[$i];
+ $catalogues[] = $variant.'/'.$catalogue.$this->dataExt;
+ }
+ }
+
+ return array_reverse($catalogues);
+ }
+
+ /**
+ * Returns a list of catalogue and its culture ID.
+ * E.g. array('messages','en_AU')
+ * @return array list of catalogues
+ * @see getCatalogues()
+ */
+ public function catalogues()
+ {
+ return $this->getCatalogues();
+ }
+
+ /**
+ * Returns a list of catalogue and its culture ID. This takes care
+ * of directory structures.
+ * E.g. array('messages','en_AU')
+ * @return array list of catalogues
+ */
+ protected function getCatalogues($dir=null,$variant=null)
+ {
+ $dir = $dir?$dir:$this->source;
+ $files = scandir($dir);
+ $catalogue = array();
+
+ foreach($files as $file)
+ {
+ if(is_dir($dir.'/'.$file) && preg_match('/^[a-z]{2}(_[A-Z]{2,3})?$/',$file)) {
+ $catalogue = array_merge(
+ $catalogue,
+ $this->getCatalogues($dir.'/'.$file, $file)
+ );
+ }
+
+ $pos = strpos($file,$this->dataExt);
+ if($pos >0 && substr($file, -1*strlen($this->dataExt)) == $this->dataExt)
+ {
+ $name = substr($file,0,$pos);
+ $dot = strrpos($name,$this->dataSeparator);
+ $culture = $variant;
+ $cat = $name;
+
+ if(is_int($dot))
+ {
+ $culture = substr($name, $dot+1, strlen($name));
+ $cat = substr($name, 0, $dot);
+ }
+
+ $details[0] = $cat;
+ $details[1] = $culture;
+ $catalogue[] = $details;
+ }
+ }
+ sort($catalogue);
+ return $catalogue;
+ }
+
+ /**
+ * Get the variant for a catalogue depending on the current culture.
+ * @param string catalogue
+ * @return string the variant.
+ * @see save()
+ * @see update()
+ * @see delete()
+ */
+ private function getVariants($catalogue='messages')
+ {
+ if($catalogue === null) {
+ $catalogue = 'messages';
+ }
+
+ foreach($this->getCatalogueList($catalogue) as $variant)
+ {
+ $file = $this->getSource($variant);
+ if(is_file($file)) {
+ return array($variant, $file);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Save the list of untranslated blocks to the translation source.
+ * If the translation was not found, you should add those
+ * strings to the translation source via the append() method.
+ * @param string the catalogue to add to
+ * @return boolean true if saved successfuly, false otherwise.
+ */
+ public function save($catalogue='messages')
+ {
+ $messages = $this->untranslated;
+ if(count($messages) <= 0) {
+ return false;
+ }
+
+ $variants = $this->getVariants($catalogue);
+
+ if($variants) {
+ list($variant, $filename) = $variants;
+ } else {
+ list($variant, $filename) = $this->createMessageTemplate($catalogue);
+ }
+
+ if(is_writable($filename) == false) {
+ throw new TIOException("Unable to save to file {$filename}, file must be writable.");
+ }
+
+ //create a new dom, import the existing xml
+ $dom = new DOMDocument();
+ $dom->load($filename);
+
+ //find the body element
+ $xpath = new DomXPath($dom);
+ $body = $xpath->query('//body')->item(0);
+
+ $lastNodes = $xpath->query('//trans-unit[last()]');
+ if(($last=$lastNodes->item(0))!==null) {
+ $count = (int)$last->getAttribute('id');
+ } else {
+ $count = 0;
+ }
+
+ //for each message add it to the XML file using DOM
+ foreach($messages as $message)
+ {
+ $unit = $dom->createElement('trans-unit');
+ $unit->setAttribute('id',++$count);
+
+ $source = $dom->createElement('source');
+ $source->appendChild($dom->createCDATASection($message));
+
+ $target = $dom->createElement('target');
+ $target->appendChild($dom->createCDATASection(''));
+
+ $unit->appendChild($dom->createTextNode("\n"));
+ $unit->appendChild($source);
+ $unit->appendChild($dom->createTextNode("\n"));
+ $unit->appendChild($target);
+ $unit->appendChild($dom->createTextNode("\n"));
+
+ $body->appendChild($dom->createTextNode("\n"));
+ $body->appendChild($unit);
+ $body->appendChild($dom->createTextNode("\n"));
+ }
+
+
+ $fileNode = $xpath->query('//file')->item(0);
+ $fileNode->setAttribute('date', @date('Y-m-d\TH:i:s\Z'));
+
+ //save it and clear the cache for this variant
+ $dom->save($filename);
+ if(!empty($this->cache)) {
+ $this->cache->clean($variant, $this->culture);
+ }
+
+ return true;
+ }
+
+ /**
+ * Update the translation.
+ * @param string the source string.
+ * @param string the new translation string.
+ * @param string comments
+ * @param string the catalogue to save to.
+ * @return boolean true if translation was updated, false otherwise.
+ */
+ public function update($text, $target, $comments, $catalogue='messages')
+ {
+ $variants = $this->getVariants($catalogue);
+
+ if($variants) {
+ list($variant, $filename) = $variants;
+ } else {
+ return false;
+ }
+
+ if(is_writable($filename) == false) {
+ throw new TIOException("Unable to update file {$filename}, file must be writable.");
+ }
+
+ //create a new dom, import the existing xml
+ $dom = DOMDocument::load($filename);
+
+ //find the body element
+ $xpath = new DomXPath($dom);
+ $units = $xpath->query('//trans-unit');
+
+ //for each of the existin units
+ foreach($units as $unit)
+ {
+ $found = false;
+ $targetted = false;
+ $commented = false;
+
+ //in each unit, need to find the source, target and comment nodes
+ //it will assume that the source is before the target.
+ foreach($unit->childNodes as $node)
+ {
+ //source node
+ if($node->nodeName == 'source' && $node->firstChild->wholeText == $text) {
+ $found = true;
+ }
+
+ //found source, get the target and notes
+ if($found)
+ {
+ //set the new translated string
+ if($node->nodeName == 'target')
+ {
+ $node->nodeValue = $target;
+ $targetted = true;
+ }
+
+ //set the notes
+ if(!empty($comments) && $node->nodeName == 'note')
+ {
+ $node->nodeValue = $comments;
+ $commented = true;
+ }
+ }
+ }
+
+ //append a target
+ if($found && !$targetted) {
+ $unit->appendChild($dom->createElement('target',$target));
+ }
+
+ //append a note
+ if($found && !$commented && !empty($comments)) {
+ $unit->appendChild($dom->createElement('note',$comments));
+ }
+
+ //finished searching
+ if($found) {
+ break;
+ }
+ }
+
+ $fileNode = $xpath->query('//file')->item(0);
+ $fileNode->setAttribute('date', @date('Y-m-d\TH:i:s\Z'));
+
+ if($dom->save($filename) >0)
+ {
+ if(!empty($this->cache)) {
+ $this->cache->clean($variant, $this->culture);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Delete a particular message from the specified catalogue.
+ * @param string the source message to delete.
+ * @param string the catalogue to delete from.
+ * @return boolean true if deleted, false otherwise.
+ */
+ public function delete($message, $catalogue='messages')
+ {
+ $variants = $this->getVariants($catalogue);
+ if($variants) {
+ list($variant, $filename) = $variants;
+ } else {
+ return false;
+ }
+
+ if(is_writable($filename) == false) {
+ throw new TIOException("Unable to modify file {$filename}, file must be writable.");
+ }
+
+ //create a new dom, import the existing xml
+ $dom = DOMDocument::load($filename);
+
+ //find the body element
+ $xpath = new DomXPath($dom);
+ $units = $xpath->query('//trans-unit');
+
+ //for each of the existin units
+ foreach($units as $unit)
+ {
+ //in each unit, need to find the source, target and comment nodes
+ //it will assume that the source is before the target.
+ foreach($unit->childNodes as $node)
+ {
+ //source node
+ if($node->nodeName == 'source' && $node->firstChild->wholeText == $message)
+ {
+ //we found it, remove and save the xml file.
+ $unit->parentNode->removeChild($unit);
+ $fileNode = $xpath->query('//file')->item(0);
+ $fileNode->setAttribute('date', @date('Y-m-d\TH:i:s\Z'));
+
+ if(false !== $dom->save($filename)) {
+ if(!empty($this->cache)) {
+ $this->cache->clean($variant, $this->culture);
+ }
+ return true;
+ }
+
+ return false;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ protected function createMessageTemplate($catalogue)
+ {
+ if($catalogue === null) {
+ $catalogue = 'messages';
+ }
+
+ $variants = $this->getCatalogueList($catalogue);
+ $variant = array_shift($variants);
+ $file = $this->getSource($variant);
+ $dir = dirname($file);
+
+ if(!is_dir($dir)) {
+ @mkdir($dir);
+ @chmod($dir,PRADO_CHMOD);
+ }
+
+ if(!is_dir($dir)) {
+ throw new TException("Unable to create directory $dir");
+ }
+
+ file_put_contents($file, $this->getTemplate($catalogue));
+ chmod($file, PRADO_CHMOD);
+
+ return array($variant, $file);
+ }
+
+ protected function getTemplate($catalogue)
+ {
+ $date = @date('c');
+ $xml = <<
+
+
+
+
+
+
+EOD;
+ return $xml;
+ }
+}
diff --git a/framework/I18N/core/MessageSource_gettext.php b/framework/I18N/core/MessageSource_gettext.php
index 5428e32b..0e12ddba 100644
--- a/framework/I18N/core/MessageSource_gettext.php
+++ b/framework/I18N/core/MessageSource_gettext.php
@@ -1,457 +1,457 @@
-
- * @version $Revision: 1.7 $ $Date: 2005/12/17 06:11:28 $
- * @package System.I18N.core
- */
-
-/**
- * Get the MessageSource class file.
- */
-require_once(dirname(__FILE__).'/MessageSource.php');
-
-/**
- * Get the Gettext class.
- */
-require_once(dirname(__FILE__).'/Gettext/TGettext.php');
-
-/**
- * MessageSource_gettext class.
- *
- * Using Gettext MO format as the message source for translation.
- * The gettext classes are based on PEAR's gettext MO and PO classes.
- *
- * See the MessageSource::factory() method to instantiate this class.
- *
- * @author Xiang Wei Zhuo
- * @version v1.0, last update on Fri Dec 24 16:18:44 EST 2004
- * @package System.I18N.core
- */
-class MessageSource_gettext extends MessageSource
-{
- /**
- * Message data filename extension.
- * @var string
- */
- protected $dataExt = '.mo';
-
- /**
- * PO data filename extension
- * @var string
- */
- protected $poExt = '.po';
-
- /**
- * Separator between culture name and source.
- * @var string
- */
- protected $dataSeparator = '.';
-
- function __construct($source)
- {
- $this->source = (string)$source;
- }
-
-
- /**
- * Load the messages from a MO file.
- * @param string MO file.
- * @return array of messages.
- */
- protected function &loadData($filename)
- {
- $mo = TGettext::factory('MO',$filename);
- $mo->load();
- $result = $mo->toArray();
-
- $results = array();
- $count=0;
- foreach($result['strings'] as $source => $target)
- {
- $results[$source][] = $target; //target
- $results[$source][] = $count++; //id
- $results[$source][] = ''; //comments
- }
- return $results;
- }
-
- /**
- * Determin if the MO file source is valid.
- * @param string MO file
- * @return boolean true if valid, false otherwise.
- */
- protected function isValidSource($filename)
- {
- return is_file($filename);
- }
-
- /**
- * Get the MO file for a specific message catalogue and cultural
- * vairant.
- * @param string message catalogue
- * @return string full path to the MO file.
- */
- protected function getSource($variant)
- {
- return $this->source.'/'.$variant;
- }
-
- /**
- * Get the last modified unix-time for this particular catalogue+variant.
- * Just use the file modified time.
- * @param string catalogue+variant
- * @return int last modified in unix-time format.
- */
- protected function getLastModified($source)
- {
- if(is_file($source))
- return filemtime($source);
- else
- return 0;
- }
-
- /**
- * Get all the variants of a particular catalogue.
- * @param string catalogue name
- * @return array list of all variants for this catalogue.
- */
- protected function getCatalogueList($catalogue)
- {
- $variants = explode('_',$this->culture);
- $source = $catalogue.$this->dataExt;
-
- $catalogues = array($source);
-
- $variant = null;
-
- for($i = 0, $k = count($variants); $i < $k; ++$i)
- {
- if(isset($variants[$i]{0}))
- {
- $variant .= ($variant)?'_'.$variants[$i]:$variants[$i];
- $catalogues[] = $catalogue.$this->dataSeparator.
- $variant.$this->dataExt;
- }
- }
- $byDir = $this->getCatalogueByDir($catalogue);
- $catalogues = array_merge($byDir,array_reverse($catalogues));
- return $catalogues;
- }
-
-
- /**
- * Traverse through the directory structure to find the catalogues.
- * This should only be called by getCatalogueList()
- * @param string a particular catalogue.
- * @return array a list of catalogues.
- * @see getCatalogueList()
- */
- private function getCatalogueByDir($catalogue)
- {
- $variants = explode('_',$this->culture);
- $catalogues = array();
-
- $variant = null;
-
- for($i = 0, $k = count($variants); $i < $k; ++$i)
- {
- if(isset($variants[$i]{0}))
- {
- $variant .= ($variant)?'_'.$variants[$i]:$variants[$i];
- $catalogues[] = $variant.'/'.$catalogue.$this->dataExt;
- }
- }
- return array_reverse($catalogues);
- }
-
- /**
- * Get the variant for a catalogue depending on the current culture.
- * @param string catalogue
- * @return string the variant.
- * @see save()
- * @see update()
- * @see delete()
- */
- private function getVariants($catalogue='messages')
- {
- if($catalogue === null) {
- $catalogue = 'messages';
- }
-
- foreach($this->getCatalogueList($catalogue) as $variant)
- {
- $file = $this->getSource($variant);
- $po = $this->getPOFile($file);
- if(is_file($file) || is_file($po))
- return array($variant, $file, $po);
- }
- return false;
- }
-
- private function getPOFile($MOFile)
- {
- $filebase = substr($MOFile, 0, strlen($MOFile)-strlen($this->dataExt));
- return $filebase.$this->poExt;
- }
-
- /**
- * Save the list of untranslated blocks to the translation source.
- * If the translation was not found, you should add those
- * strings to the translation source via the append() method.
- * @param string the catalogue to add to
- * @return boolean true if saved successfuly, false otherwise.
- */
- function save($catalogue='messages')
- {
- $messages = $this->untranslated;
-
- if(count($messages) <= 0) return false;
-
- $variants = $this->getVariants($catalogue);
-
- if($variants)
- list($variant, $MOFile, $POFile) = $variants;
- else
- list($variant, $MOFile, $POFile) = $this->createMessageTemplate($catalogue);
-
- if(is_writable($MOFile) == false)
- throw new TIOException("Unable to save to file {$MOFile}, file must be writable.");
- if(is_writable($POFile) == false)
- throw new TIOException("Unable to save to file {$POFile}, file must be writable.");
-
- //set the strings as untranslated.
- $strings = array();
- foreach($messages as $message)
- $strings[$message] = '';
-
- //load the PO
- $po = TGettext::factory('PO',$POFile);
- $po->load();
- $result = $po->toArray();
-
- $existing = count($result['strings']);
-
- //add to strings to the existing message list
- $result['strings'] = array_merge($result['strings'],$strings);
-
- $new = count($result['strings']);
-
- if($new > $existing)
- {
- //change the date 2004-12-25 12:26
- $result['meta']['PO-Revision-Date'] = @date('Y-m-d H:i:s');
-
- $po->fromArray($result);
- $mo = $po->toMO();
- if($po->save() && $mo->save($MOFile))
- {
- if(!empty($this->cache))
- $this->cache->clean($variant, $this->culture);
- return true;
- }
- else
- return false;
- }
- return false;
- }
-
- /**
- * Delete a particular message from the specified catalogue.
- * @param string the source message to delete.
- * @param string the catalogue to delete from.
- * @return boolean true if deleted, false otherwise.
- */
- function delete($message, $catalogue='messages')
- {
- $variants = $this->getVariants($catalogue);
- if($variants)
- list($variant, $MOFile, $POFile) = $variants;
- else
- return false;
-
- if(is_writable($MOFile) == false)
- throw new TIOException("Unable to modify file {$MOFile}, file must be writable.");
- if(is_writable($POFile) == false)
- throw new TIOException("Unable to modify file {$POFile}, file must be writable.");
-
- $po = TGettext::factory('PO',$POFile);
- $po->load();
- $result = $po->toArray();
-
- foreach($result['strings'] as $string => $value)
- {
- if($string == $message)
- {
- $result['meta']['PO-Revision-Date'] = @date('Y-m-d H:i:s');
- unset($result['strings'][$string]);
-
- $po->fromArray($result);
- $mo = $po->toMO();
- if($po->save() && $mo->save($MOFile))
- {
- if(!empty($this->cache))
- $this->cache->clean($variant, $this->culture);
- return true;
- }
- else
- return false;
- }
- }
-
- return false;
- }
-
- /**
- * Update the translation.
- * @param string the source string.
- * @param string the new translation string.
- * @param string comments
- * @param string the catalogue of the translation.
- * @return boolean true if translation was updated, false otherwise.
- */
- function update($text, $target, $comments, $catalogue='messages')
- {
- $variants = $this->getVariants($catalogue);
- if($variants)
- list($variant, $MOFile, $POFile) = $variants;
- else
- return false;
-
- if(is_writable($MOFile) == false)
- throw new TIOException("Unable to update file {$MOFile}, file must be writable.");
- if(is_writable($POFile) == false)
- throw new TIOException("Unable to update file {$POFile}, file must be writable.");
-
-
- $po = TGettext::factory('PO',$POFile);
- $po->load();
- $result = $po->toArray();
-
- foreach($result['strings'] as $string => $value)
- {
- if($string == $text)
- {
- $result['strings'][$string] = $target;
- $result['meta']['PO-Revision-Date'] = @date('Y-m-d H:i:s');
-
- $po->fromArray($result);
- $mo = $po->toMO();
-
- if($po->save() && $mo->save($MOFile))
- {
- if(!empty($this->cache))
- $this->cache->clean($variant, $this->culture);
- return true;
- }
- else
- return false;
- }
- }
-
- return false;
- }
-
-
- /**
- * Returns a list of catalogue as key and all it variants as value.
- * @return array list of catalogues
- */
- function catalogues()
- {
- return $this->getCatalogues();
- }
-
- /**
- * Returns a list of catalogue and its culture ID. This takes care
- * of directory structures.
- * E.g. array('messages','en_AU')
- * @return array list of catalogues
- */
- protected function getCatalogues($dir=null,$variant=null)
- {
- $dir = $dir?$dir:$this->source;
- $files = scandir($dir);
-
- $catalogue = array();
-
- foreach($files as $file)
- {
- if(is_dir($dir.'/'.$file)
- && preg_match('/^[a-z]{2}(_[A-Z]{2,3})?$/',$file))
- {
-
- $catalogue = array_merge($catalogue,
- $this->getCatalogues($dir.'/'.$file, $file));
- }
-
- $pos = strpos($file,$this->dataExt);
-
- if($pos >0
- && substr($file,-1*strlen($this->dataExt)) == $this->dataExt)
- {
- $name = substr($file,0,$pos);
- $dot = strrpos($name,$this->dataSeparator);
- $culture = $variant;
- $cat = $name;
- if(is_int($dot))
- {
- $culture = substr($name, $dot+1,strlen($name));
- $cat = substr($name,0,$dot);
- }
- $details[0] = $cat;
- $details[1] = $culture;
-
-
- $catalogue[] = $details;
- }
- }
- sort($catalogue);
-
- return $catalogue;
- }
-
- protected function createMessageTemplate($catalogue)
- {
- if($catalogue === null) {
- $catalogue = 'messages';
- }
- $variants = $this->getCatalogueList($catalogue);
- $variant = array_shift($variants);
- $mo_file = $this->getSource($variant);
- $po_file = $this->getPOFile($mo_file);
-
- $dir = dirname($mo_file);
- if(!is_dir($dir))
- {
- @mkdir($dir);
- @chmod($dir,PRADO_CHMOD);
- }
- if(!is_dir($dir))
- throw new TException("Unable to create directory $dir");
-
- $po = TGettext::factory('PO',$po_file);
- $result['meta']['PO-Revision-Date'] = @date('Y-m-d H:i:s');
- $result['strings'] = array();
-
- $po->fromArray($result);
- $mo = $po->toMO();
- if($po->save() && $mo->save($mo_file))
- return array($variant, $mo_file, $po_file);
- else
- throw new TException("Unable to create file $po_file and $mo_file");
- }
-}
-
-?>
+
+ * @version $Revision: 1.7 $ $Date: 2005/12/17 06:11:28 $
+ * @package System.I18N.core
+ */
+
+/**
+ * Get the MessageSource class file.
+ */
+require_once(dirname(__FILE__).'/MessageSource.php');
+
+/**
+ * Get the Gettext class.
+ */
+require_once(dirname(__FILE__).'/Gettext/TGettext.php');
+
+/**
+ * MessageSource_gettext class.
+ *
+ * Using Gettext MO format as the message source for translation.
+ * The gettext classes are based on PEAR's gettext MO and PO classes.
+ *
+ * See the MessageSource::factory() method to instantiate this class.
+ *
+ * @author Xiang Wei Zhuo
+ * @version v1.0, last update on Fri Dec 24 16:18:44 EST 2004
+ * @package System.I18N.core
+ */
+class MessageSource_gettext extends MessageSource
+{
+ /**
+ * Message data filename extension.
+ * @var string
+ */
+ protected $dataExt = '.mo';
+
+ /**
+ * PO data filename extension
+ * @var string
+ */
+ protected $poExt = '.po';
+
+ /**
+ * Separator between culture name and source.
+ * @var string
+ */
+ protected $dataSeparator = '.';
+
+ function __construct($source)
+ {
+ $this->source = (string)$source;
+ }
+
+
+ /**
+ * Load the messages from a MO file.
+ * @param string MO file.
+ * @return array of messages.
+ */
+ protected function &loadData($filename)
+ {
+ $mo = TGettext::factory('MO',$filename);
+ $mo->load();
+ $result = $mo->toArray();
+
+ $results = array();
+ $count=0;
+ foreach($result['strings'] as $source => $target)
+ {
+ $results[$source][] = $target; //target
+ $results[$source][] = $count++; //id
+ $results[$source][] = ''; //comments
+ }
+ return $results;
+ }
+
+ /**
+ * Determin if the MO file source is valid.
+ * @param string MO file
+ * @return boolean true if valid, false otherwise.
+ */
+ protected function isValidSource($filename)
+ {
+ return is_file($filename);
+ }
+
+ /**
+ * Get the MO file for a specific message catalogue and cultural
+ * vairant.
+ * @param string message catalogue
+ * @return string full path to the MO file.
+ */
+ protected function getSource($variant)
+ {
+ return $this->source.'/'.$variant;
+ }
+
+ /**
+ * Get the last modified unix-time for this particular catalogue+variant.
+ * Just use the file modified time.
+ * @param string catalogue+variant
+ * @return int last modified in unix-time format.
+ */
+ protected function getLastModified($source)
+ {
+ if(is_file($source))
+ return filemtime($source);
+ else
+ return 0;
+ }
+
+ /**
+ * Get all the variants of a particular catalogue.
+ * @param string catalogue name
+ * @return array list of all variants for this catalogue.
+ */
+ protected function getCatalogueList($catalogue)
+ {
+ $variants = explode('_',$this->culture);
+ $source = $catalogue.$this->dataExt;
+
+ $catalogues = array($source);
+
+ $variant = null;
+
+ for($i = 0, $k = count($variants); $i < $k; ++$i)
+ {
+ if(isset($variants[$i]{0}))
+ {
+ $variant .= ($variant)?'_'.$variants[$i]:$variants[$i];
+ $catalogues[] = $catalogue.$this->dataSeparator.
+ $variant.$this->dataExt;
+ }
+ }
+ $byDir = $this->getCatalogueByDir($catalogue);
+ $catalogues = array_merge($byDir,array_reverse($catalogues));
+ return $catalogues;
+ }
+
+
+ /**
+ * Traverse through the directory structure to find the catalogues.
+ * This should only be called by getCatalogueList()
+ * @param string a particular catalogue.
+ * @return array a list of catalogues.
+ * @see getCatalogueList()
+ */
+ private function getCatalogueByDir($catalogue)
+ {
+ $variants = explode('_',$this->culture);
+ $catalogues = array();
+
+ $variant = null;
+
+ for($i = 0, $k = count($variants); $i < $k; ++$i)
+ {
+ if(isset($variants[$i]{0}))
+ {
+ $variant .= ($variant)?'_'.$variants[$i]:$variants[$i];
+ $catalogues[] = $variant.'/'.$catalogue.$this->dataExt;
+ }
+ }
+ return array_reverse($catalogues);
+ }
+
+ /**
+ * Get the variant for a catalogue depending on the current culture.
+ * @param string catalogue
+ * @return string the variant.
+ * @see save()
+ * @see update()
+ * @see delete()
+ */
+ private function getVariants($catalogue='messages')
+ {
+ if($catalogue === null) {
+ $catalogue = 'messages';
+ }
+
+ foreach($this->getCatalogueList($catalogue) as $variant)
+ {
+ $file = $this->getSource($variant);
+ $po = $this->getPOFile($file);
+ if(is_file($file) || is_file($po))
+ return array($variant, $file, $po);
+ }
+ return false;
+ }
+
+ private function getPOFile($MOFile)
+ {
+ $filebase = substr($MOFile, 0, strlen($MOFile)-strlen($this->dataExt));
+ return $filebase.$this->poExt;
+ }
+
+ /**
+ * Save the list of untranslated blocks to the translation source.
+ * If the translation was not found, you should add those
+ * strings to the translation source via the append() method.
+ * @param string the catalogue to add to
+ * @return boolean true if saved successfuly, false otherwise.
+ */
+ function save($catalogue='messages')
+ {
+ $messages = $this->untranslated;
+
+ if(count($messages) <= 0) return false;
+
+ $variants = $this->getVariants($catalogue);
+
+ if($variants)
+ list($variant, $MOFile, $POFile) = $variants;
+ else
+ list($variant, $MOFile, $POFile) = $this->createMessageTemplate($catalogue);
+
+ if(is_writable($MOFile) == false)
+ throw new TIOException("Unable to save to file {$MOFile}, file must be writable.");
+ if(is_writable($POFile) == false)
+ throw new TIOException("Unable to save to file {$POFile}, file must be writable.");
+
+ //set the strings as untranslated.
+ $strings = array();
+ foreach($messages as $message)
+ $strings[$message] = '';
+
+ //load the PO
+ $po = TGettext::factory('PO',$POFile);
+ $po->load();
+ $result = $po->toArray();
+
+ $existing = count($result['strings']);
+
+ //add to strings to the existing message list
+ $result['strings'] = array_merge($result['strings'],$strings);
+
+ $new = count($result['strings']);
+
+ if($new > $existing)
+ {
+ //change the date 2004-12-25 12:26
+ $result['meta']['PO-Revision-Date'] = @date('Y-m-d H:i:s');
+
+ $po->fromArray($result);
+ $mo = $po->toMO();
+ if($po->save() && $mo->save($MOFile))
+ {
+ if(!empty($this->cache))
+ $this->cache->clean($variant, $this->culture);
+ return true;
+ }
+ else
+ return false;
+ }
+ return false;
+ }
+
+ /**
+ * Delete a particular message from the specified catalogue.
+ * @param string the source message to delete.
+ * @param string the catalogue to delete from.
+ * @return boolean true if deleted, false otherwise.
+ */
+ function delete($message, $catalogue='messages')
+ {
+ $variants = $this->getVariants($catalogue);
+ if($variants)
+ list($variant, $MOFile, $POFile) = $variants;
+ else
+ return false;
+
+ if(is_writable($MOFile) == false)
+ throw new TIOException("Unable to modify file {$MOFile}, file must be writable.");
+ if(is_writable($POFile) == false)
+ throw new TIOException("Unable to modify file {$POFile}, file must be writable.");
+
+ $po = TGettext::factory('PO',$POFile);
+ $po->load();
+ $result = $po->toArray();
+
+ foreach($result['strings'] as $string => $value)
+ {
+ if($string == $message)
+ {
+ $result['meta']['PO-Revision-Date'] = @date('Y-m-d H:i:s');
+ unset($result['strings'][$string]);
+
+ $po->fromArray($result);
+ $mo = $po->toMO();
+ if($po->save() && $mo->save($MOFile))
+ {
+ if(!empty($this->cache))
+ $this->cache->clean($variant, $this->culture);
+ return true;
+ }
+ else
+ return false;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Update the translation.
+ * @param string the source string.
+ * @param string the new translation string.
+ * @param string comments
+ * @param string the catalogue of the translation.
+ * @return boolean true if translation was updated, false otherwise.
+ */
+ function update($text, $target, $comments, $catalogue='messages')
+ {
+ $variants = $this->getVariants($catalogue);
+ if($variants)
+ list($variant, $MOFile, $POFile) = $variants;
+ else
+ return false;
+
+ if(is_writable($MOFile) == false)
+ throw new TIOException("Unable to update file {$MOFile}, file must be writable.");
+ if(is_writable($POFile) == false)
+ throw new TIOException("Unable to update file {$POFile}, file must be writable.");
+
+
+ $po = TGettext::factory('PO',$POFile);
+ $po->load();
+ $result = $po->toArray();
+
+ foreach($result['strings'] as $string => $value)
+ {
+ if($string == $text)
+ {
+ $result['strings'][$string] = $target;
+ $result['meta']['PO-Revision-Date'] = @date('Y-m-d H:i:s');
+
+ $po->fromArray($result);
+ $mo = $po->toMO();
+
+ if($po->save() && $mo->save($MOFile))
+ {
+ if(!empty($this->cache))
+ $this->cache->clean($variant, $this->culture);
+ return true;
+ }
+ else
+ return false;
+ }
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Returns a list of catalogue as key and all it variants as value.
+ * @return array list of catalogues
+ */
+ function catalogues()
+ {
+ return $this->getCatalogues();
+ }
+
+ /**
+ * Returns a list of catalogue and its culture ID. This takes care
+ * of directory structures.
+ * E.g. array('messages','en_AU')
+ * @return array list of catalogues
+ */
+ protected function getCatalogues($dir=null,$variant=null)
+ {
+ $dir = $dir?$dir:$this->source;
+ $files = scandir($dir);
+
+ $catalogue = array();
+
+ foreach($files as $file)
+ {
+ if(is_dir($dir.'/'.$file)
+ && preg_match('/^[a-z]{2}(_[A-Z]{2,3})?$/',$file))
+ {
+
+ $catalogue = array_merge($catalogue,
+ $this->getCatalogues($dir.'/'.$file, $file));
+ }
+
+ $pos = strpos($file,$this->dataExt);
+
+ if($pos >0
+ && substr($file,-1*strlen($this->dataExt)) == $this->dataExt)
+ {
+ $name = substr($file,0,$pos);
+ $dot = strrpos($name,$this->dataSeparator);
+ $culture = $variant;
+ $cat = $name;
+ if(is_int($dot))
+ {
+ $culture = substr($name, $dot+1,strlen($name));
+ $cat = substr($name,0,$dot);
+ }
+ $details[0] = $cat;
+ $details[1] = $culture;
+
+
+ $catalogue[] = $details;
+ }
+ }
+ sort($catalogue);
+
+ return $catalogue;
+ }
+
+ protected function createMessageTemplate($catalogue)
+ {
+ if($catalogue === null) {
+ $catalogue = 'messages';
+ }
+ $variants = $this->getCatalogueList($catalogue);
+ $variant = array_shift($variants);
+ $mo_file = $this->getSource($variant);
+ $po_file = $this->getPOFile($mo_file);
+
+ $dir = dirname($mo_file);
+ if(!is_dir($dir))
+ {
+ @mkdir($dir);
+ @chmod($dir,PRADO_CHMOD);
+ }
+ if(!is_dir($dir))
+ throw new TException("Unable to create directory $dir");
+
+ $po = TGettext::factory('PO',$po_file);
+ $result['meta']['PO-Revision-Date'] = @date('Y-m-d H:i:s');
+ $result['strings'] = array();
+
+ $po->fromArray($result);
+ $mo = $po->toMO();
+ if($po->save() && $mo->save($mo_file))
+ return array($variant, $mo_file, $po_file);
+ else
+ throw new TException("Unable to create file $po_file and $mo_file");
+ }
+}
+
+?>
diff --git a/framework/I18N/core/NumberFormat.php b/framework/I18N/core/NumberFormat.php
index 3b683c6c..3c733713 100644
--- a/framework/I18N/core/NumberFormat.php
+++ b/framework/I18N/core/NumberFormat.php
@@ -1,306 +1,306 @@
-
- * @version $Revision: 1.6 $ $Date: 2005/12/20 09:32:42 $
- * @package System.I18N.core
- */
-
-/**
- * Get the NumberFormatInfo class file.
- */
-require_once(dirname(__FILE__).'/NumberFormatInfo.php');
-
-
-/**
- * Get the encoding utilities
- */
-require_once(dirname(__FILE__).'/util.php');
-
-
-/**
- * NumberFormat class.
- *
- * NumberFormat formats decimal numbers in any locale. The decimal
- * number is formatted according to a particular pattern. These
- * patterns can arise from the NumberFormatInfo object which is
- * culturally sensitive. The NumberFormat class can be instantiated in
- * many ways. E.g.
- *
- *
- * //create a invariant number formatter.
- * $formatter = new NumberFormat();
- *
- * //create a number format for the french language locale.
- * $fr = new NumberFormat('fr');
- *
- * //create a number format base on a NumberFormatInfo instance $numberInfo.
- * $format = new NumberFormat($numberInfo);
- *
- *
- * A normal decimal number can also be displayed as a currency
- * or as a percentage. For example
- *
- * $format->format(1234.5); //Decimal number "1234.5"
- * $format->format(1234.5,'c'); //Default currency "$1234.50"
- * $format->format(0.25, 'p') //Percent "25%"
- *
- *
- * Currency is formated using the localized currency pattern. For example
- * to format the number as Japanese Yen:
- *
- * $ja = new NumberFormat('ja_JP');
- *
- * //Japanese currency pattern, and using Japanese Yen symbol
- * $ja->format(123.14,'c','JPY'); //�?123 (Yen 123)
- *
- * For each culture, the symbol for each currency may be different.
- *
- * @author Xiang Wei Zhuo
- * @version v1.0, last update on Fri Dec 10 18:10:20 EST 2004
- * @package System.I18N.core
- */
-class NumberFormat
-{
-
- /**
- * The DateTimeFormatInfo, containing culture specific patterns and names.
- * @var DateTimeFormatInfo
- */
- protected $formatInfo;
-
- /**
- * Create a new number format instance. The constructor can be instantiated
- * with a string that represent a culture/locale. Similarly, passing
- * a CultureInfo or NumberFormatInfo instance will instantiated a instance
- * for that particular culture.
- * @param mixed either null, a CultureInfo, a NumberFormatInfo, or string
- * @return NumberFormat
- */
- function __construct($formatInfo=null)
- {
- if($formatInfo === null)
- $this->formatInfo = NumberFormatInfo::getInvariantInfo();
- else if($formatInfo instanceof CultureInfo)
- $this->formatInfo = $formatInfo->NumberFormat;
- else if($formatInfo instanceof NumberFormatInfo)
- $this->formatInfo = $formatInfo;
- else
- $this->formatInfo =
- NumberFormatInfo::getInstance($formatInfo);
- }
-
- /**
- * For the number for a certain pattern. The valid patterns are
- * 'c', 'd', 'e', 'p' or a custom pattern, such as "#.000" for
- * 3 decimal places.
- * @param mixed the number to format.
- * @param string the format pattern, either, 'c', 'd', 'e', 'p'
- * or a custom pattern. E.g. "#.000" will format the number to
- * 3 decimal places.
- * @param string 3-letter ISO 4217 code. For example, the code
- * "USD" represents the US Dollar and "EUR" represents the Euro currency.
- * @return string formatted number string
- */
- function format($number, $pattern='d', $currency='USD', $charset='UTF-8')
- {
- $this->setPattern($pattern);
-
- if(strtolower($pattern) == 'p')
- $number = $number * 100;
-
- $string = (string)$number;
-
- $decimal = $this->formatDecimal($string);
- $integer = $this->formatInteger(abs($number));
-
- if(strlen($decimal)>0)
- $result = $integer.$decimal;
- else
- $result = $integer;
-
- //get the suffix
- if($number >= 0)
- $suffix = $this->formatInfo->PositivePattern;
- else if($number < 0)
- $suffix = $this->formatInfo->NegativePattern;
- else
- $suffix = array("","");
-
- //append and prepend suffix
- $result = $suffix[0].$result.$suffix[1];
-
- //replace currency sign
- $symbol = @$this->formatInfo->getCurrencySymbol($currency);
- if($symbol === null) {
- $symbol = $currency;
- }
-
- $result = str_replace('¤',$symbol, $result);
-
- return I18N_toEncoding($result, $charset);
- }
-
- /**
- * For the integer, perform groupings and string padding.
- * @param string the decimal number in string form.
- * @return string formatted integer string with grouping
- */
- protected function formatInteger($string)
- {
- $string = (string)$string;
-
- $decimalDigits = $this->formatInfo->DecimalDigits;
- //if not decimal digits, assume 0 decimal points.
- if(is_int($decimalDigits) && $decimalDigits > 0)
- $string = (string)round(floatval($string),$decimalDigits);
- $dp = strpos($string, '.');
- if(is_int($dp))
- $string = substr($string, 0, $dp);
- $integer = '';
-
- $digitSize = $this->formatInfo->getDigitSize();
-
- $string = str_pad($string, $digitSize, '0',STR_PAD_LEFT);
-
- $len = strlen($string);
-
- $groupSeparator = $this->formatInfo->GroupSeparator;
- $groupSize = $this->formatInfo->GroupSizes;
-
-
- $firstGroup = true;
- $multiGroup = is_int($groupSize[1]);
- $count = 0;
-
- if(is_int($groupSize[0]))
- {
- //now for the integer groupings
- for($i=0; $i<$len; $i++)
- {
- $char = $string{$len-$i-1};
-
- if($multiGroup && $count == 0)
- {
- if($i != 0 && $i%$groupSize[0] == 0)
- {
- $integer = $groupSeparator . $integer;
- $count++;
- }
- }
- else if($multiGroup && $count >= 1)
- {
- if($i != 0 && ($i-$groupSize[0])%$groupSize[1] == 0)
- {
- $integer = $groupSeparator . $integer;
- $count++;
- }
- }
- else
- {
- if($i != 0 && $i%$groupSize[0] == 0)
- {
- $integer = $groupSeparator . $integer;
- $count++;
- }
- }
-
- $integer = $char . $integer;
- }
- }
- else
- $integer = $string;
-
- return $integer;
- }
-
- /**
- * Format the decimal places.
- * @param string the decimal number in string form.
- * @return string formatted decimal places.
- */
- protected function formatDecimal($string)
- {
- $dp = strpos($string, '.');
- $decimal = '';
-
- $decimalDigits = $this->formatInfo->DecimalDigits;
- $decimalSeparator = $this->formatInfo->DecimalSeparator;
-
- //do the correct rounding here
- //$string = round(floatval($string), $decimalDigits);
- if(is_int($dp))
- {
- if($decimalDigits == -1)
- {
- $decimal = substr($string, $dp+1);
- }
- else if(is_int($decimalDigits))
- {
- $float = round((float)$string, $decimalDigits);
- if(strpos((string)$float, '.') === false)
- {
- $decimal = str_pad($decimal,$decimalDigits,'0');
- }
- else
- {
- $decimal = substr($float, strpos($float,'.')+1);
- if(strlen($decimal)<$decimalDigits)
- $decimal = str_pad($decimal,$decimalDigits,'0');
- }
- }
- else
- return $decimal;
-
- return $decimalSeparator.$decimal;
- }
- else if ($decimalDigits > 0)
- return $decimalSeparator.str_pad($decimal,$decimalDigits,'0');
-
- return $decimal;
- }
-
- /**
- * Set the pattern to format against. The default patterns
- * are retrieved from the NumberFormatInfo instance.
- * @param string the requested patterns.
- * @return string a number format pattern.
- */
- protected function setPattern($pattern)
- {
- switch($pattern)
- {
- case 'c':
- case 'C':
- $this->formatInfo->setPattern(NumberFormatInfo::CURRENCY);
- break;
- case 'd':
- case 'D':
- $this->formatInfo->setPattern(NumberFormatInfo::DECIMAL);
- break;
- case 'e':
- case 'E':
- $this->formatInfo->setPattern(NumberFormatInfo::SCIENTIFIC);
- break;
- case 'p':
- case 'P':
- $this->formatInfo->setPattern(NumberFormatInfo::PERCENTAGE);
- break;
- default:
- $this->formatInfo->setPattern($pattern);
- break;
- }
- }
-}
-
+
+ * @version $Revision: 1.6 $ $Date: 2005/12/20 09:32:42 $
+ * @package System.I18N.core
+ */
+
+/**
+ * Get the NumberFormatInfo class file.
+ */
+require_once(dirname(__FILE__).'/NumberFormatInfo.php');
+
+
+/**
+ * Get the encoding utilities
+ */
+require_once(dirname(__FILE__).'/util.php');
+
+
+/**
+ * NumberFormat class.
+ *
+ * NumberFormat formats decimal numbers in any locale. The decimal
+ * number is formatted according to a particular pattern. These
+ * patterns can arise from the NumberFormatInfo object which is
+ * culturally sensitive. The NumberFormat class can be instantiated in
+ * many ways. E.g.
+ *
+ *
+ * //create a invariant number formatter.
+ * $formatter = new NumberFormat();
+ *
+ * //create a number format for the french language locale.
+ * $fr = new NumberFormat('fr');
+ *
+ * //create a number format base on a NumberFormatInfo instance $numberInfo.
+ * $format = new NumberFormat($numberInfo);
+ *
+ *
+ * A normal decimal number can also be displayed as a currency
+ * or as a percentage. For example
+ *
+ * $format->format(1234.5); //Decimal number "1234.5"
+ * $format->format(1234.5,'c'); //Default currency "$1234.50"
+ * $format->format(0.25, 'p') //Percent "25%"
+ *
+ *
+ * Currency is formated using the localized currency pattern. For example
+ * to format the number as Japanese Yen:
+ *
+ * $ja = new NumberFormat('ja_JP');
+ *
+ * //Japanese currency pattern, and using Japanese Yen symbol
+ * $ja->format(123.14,'c','JPY'); //�?123 (Yen 123)
+ *
+ * For each culture, the symbol for each currency may be different.
+ *
+ * @author Xiang Wei Zhuo
+ * @version v1.0, last update on Fri Dec 10 18:10:20 EST 2004
+ * @package System.I18N.core
+ */
+class NumberFormat
+{
+
+ /**
+ * The DateTimeFormatInfo, containing culture specific patterns and names.
+ * @var DateTimeFormatInfo
+ */
+ protected $formatInfo;
+
+ /**
+ * Create a new number format instance. The constructor can be instantiated
+ * with a string that represent a culture/locale. Similarly, passing
+ * a CultureInfo or NumberFormatInfo instance will instantiated a instance
+ * for that particular culture.
+ * @param mixed either null, a CultureInfo, a NumberFormatInfo, or string
+ * @return NumberFormat
+ */
+ function __construct($formatInfo=null)
+ {
+ if($formatInfo === null)
+ $this->formatInfo = NumberFormatInfo::getInvariantInfo();
+ else if($formatInfo instanceof CultureInfo)
+ $this->formatInfo = $formatInfo->NumberFormat;
+ else if($formatInfo instanceof NumberFormatInfo)
+ $this->formatInfo = $formatInfo;
+ else
+ $this->formatInfo =
+ NumberFormatInfo::getInstance($formatInfo);
+ }
+
+ /**
+ * For the number for a certain pattern. The valid patterns are
+ * 'c', 'd', 'e', 'p' or a custom pattern, such as "#.000" for
+ * 3 decimal places.
+ * @param mixed the number to format.
+ * @param string the format pattern, either, 'c', 'd', 'e', 'p'
+ * or a custom pattern. E.g. "#.000" will format the number to
+ * 3 decimal places.
+ * @param string 3-letter ISO 4217 code. For example, the code
+ * "USD" represents the US Dollar and "EUR" represents the Euro currency.
+ * @return string formatted number string
+ */
+ function format($number, $pattern='d', $currency='USD', $charset='UTF-8')
+ {
+ $this->setPattern($pattern);
+
+ if(strtolower($pattern) == 'p')
+ $number = $number * 100;
+
+ $string = (string)$number;
+
+ $decimal = $this->formatDecimal($string);
+ $integer = $this->formatInteger(abs($number));
+
+ if(strlen($decimal)>0)
+ $result = $integer.$decimal;
+ else
+ $result = $integer;
+
+ //get the suffix
+ if($number >= 0)
+ $suffix = $this->formatInfo->PositivePattern;
+ else if($number < 0)
+ $suffix = $this->formatInfo->NegativePattern;
+ else
+ $suffix = array("","");
+
+ //append and prepend suffix
+ $result = $suffix[0].$result.$suffix[1];
+
+ //replace currency sign
+ $symbol = @$this->formatInfo->getCurrencySymbol($currency);
+ if($symbol === null) {
+ $symbol = $currency;
+ }
+
+ $result = str_replace('¤',$symbol, $result);
+
+ return I18N_toEncoding($result, $charset);
+ }
+
+ /**
+ * For the integer, perform groupings and string padding.
+ * @param string the decimal number in string form.
+ * @return string formatted integer string with grouping
+ */
+ protected function formatInteger($string)
+ {
+ $string = (string)$string;
+
+ $decimalDigits = $this->formatInfo->DecimalDigits;
+ //if not decimal digits, assume 0 decimal points.
+ if(is_int($decimalDigits) && $decimalDigits > 0)
+ $string = (string)round(floatval($string),$decimalDigits);
+ $dp = strpos($string, '.');
+ if(is_int($dp))
+ $string = substr($string, 0, $dp);
+ $integer = '';
+
+ $digitSize = $this->formatInfo->getDigitSize();
+
+ $string = str_pad($string, $digitSize, '0',STR_PAD_LEFT);
+
+ $len = strlen($string);
+
+ $groupSeparator = $this->formatInfo->GroupSeparator;
+ $groupSize = $this->formatInfo->GroupSizes;
+
+
+ $firstGroup = true;
+ $multiGroup = is_int($groupSize[1]);
+ $count = 0;
+
+ if(is_int($groupSize[0]))
+ {
+ //now for the integer groupings
+ for($i=0; $i<$len; $i++)
+ {
+ $char = $string{$len-$i-1};
+
+ if($multiGroup && $count == 0)
+ {
+ if($i != 0 && $i%$groupSize[0] == 0)
+ {
+ $integer = $groupSeparator . $integer;
+ $count++;
+ }
+ }
+ else if($multiGroup && $count >= 1)
+ {
+ if($i != 0 && ($i-$groupSize[0])%$groupSize[1] == 0)
+ {
+ $integer = $groupSeparator . $integer;
+ $count++;
+ }
+ }
+ else
+ {
+ if($i != 0 && $i%$groupSize[0] == 0)
+ {
+ $integer = $groupSeparator . $integer;
+ $count++;
+ }
+ }
+
+ $integer = $char . $integer;
+ }
+ }
+ else
+ $integer = $string;
+
+ return $integer;
+ }
+
+ /**
+ * Format the decimal places.
+ * @param string the decimal number in string form.
+ * @return string formatted decimal places.
+ */
+ protected function formatDecimal($string)
+ {
+ $dp = strpos($string, '.');
+ $decimal = '';
+
+ $decimalDigits = $this->formatInfo->DecimalDigits;
+ $decimalSeparator = $this->formatInfo->DecimalSeparator;
+
+ //do the correct rounding here
+ //$string = round(floatval($string), $decimalDigits);
+ if(is_int($dp))
+ {
+ if($decimalDigits == -1)
+ {
+ $decimal = substr($string, $dp+1);
+ }
+ else if(is_int($decimalDigits))
+ {
+ $float = round((float)$string, $decimalDigits);
+ if(strpos((string)$float, '.') === false)
+ {
+ $decimal = str_pad($decimal,$decimalDigits,'0');
+ }
+ else
+ {
+ $decimal = substr($float, strpos($float,'.')+1);
+ if(strlen($decimal)<$decimalDigits)
+ $decimal = str_pad($decimal,$decimalDigits,'0');
+ }
+ }
+ else
+ return $decimal;
+
+ return $decimalSeparator.$decimal;
+ }
+ else if ($decimalDigits > 0)
+ return $decimalSeparator.str_pad($decimal,$decimalDigits,'0');
+
+ return $decimal;
+ }
+
+ /**
+ * Set the pattern to format against. The default patterns
+ * are retrieved from the NumberFormatInfo instance.
+ * @param string the requested patterns.
+ * @return string a number format pattern.
+ */
+ protected function setPattern($pattern)
+ {
+ switch($pattern)
+ {
+ case 'c':
+ case 'C':
+ $this->formatInfo->setPattern(NumberFormatInfo::CURRENCY);
+ break;
+ case 'd':
+ case 'D':
+ $this->formatInfo->setPattern(NumberFormatInfo::DECIMAL);
+ break;
+ case 'e':
+ case 'E':
+ $this->formatInfo->setPattern(NumberFormatInfo::SCIENTIFIC);
+ break;
+ case 'p':
+ case 'P':
+ $this->formatInfo->setPattern(NumberFormatInfo::PERCENTAGE);
+ break;
+ default:
+ $this->formatInfo->setPattern($pattern);
+ break;
+ }
+ }
+}
+
diff --git a/framework/I18N/core/NumberFormatInfo.php b/framework/I18N/core/NumberFormatInfo.php
index 17149317..2a666726 100644
--- a/framework/I18N/core/NumberFormatInfo.php
+++ b/framework/I18N/core/NumberFormatInfo.php
@@ -1,650 +1,650 @@
-
- * @version $Revision: 1.3 $ $Date: 2005/08/04 05:27:19 $
- * @package System.I18N.core
- */
-
-/**
- * Get the CultureInfo class file.
- */
-require_once(dirname(__FILE__).'/CultureInfo.php');
-
-/**
- * NumberFormatInfo class
- *
- * Defines how numeric values are formatted and displayed,
- * depending on the culture. Numeric values are formatted using
- * standard or custom patterns stored in the properties of a
- * NumberFormatInfo.
- *
- * This class contains information, such as currency, decimal
- * separators, and other numeric symbols.
- *
- * To create a NumberFormatInfo for a specific culture,
- * create a CultureInfo for that culture and retrieve the
- * CultureInfo->NumberFormat property. Or use
- * NumberFormatInfo::getInstance($culture).
- * To create a NumberFormatInfo for the invariant culture, use the
- * InvariantInfo::getInvariantInfo().
- *
- *
- * @author Xiang Wei Zhuo
- * @version v1.0, last update on Sun Dec 05 14:48:26 EST 2004
- * @package System.I18N.core
- */
-class NumberFormatInfo
-{
-
- /**
- * ICU number formatting data.
- * @var array
- */
- private $data = array();
-
- /**
- * A list of properties that are accessable/writable.
- * @var array
- */
- protected $properties = array();
-
- /**
- * The number pattern.
- * @var array
- */
- protected $pattern = array();
-
- const DECIMAL = 0;
- const CURRENCY = 1;
- const PERCENTAGE = 2;
- const SCIENTIFIC = 3;
-
- /**
- * Allow functions that begins with 'set' to be called directly
- * as an attribute/property to retrieve the value.
- * @return mixed
- */
- public function __get($name)
- {
- $getProperty = 'get'.$name;
- if(in_array($getProperty, $this->properties))
- return $this->$getProperty();
- else
- throw new Exception('Property '.$name.' does not exists.');
- }
-
- /**
- * Allow functions that begins with 'set' to be called directly
- * as an attribute/property to set the value.
- */
- public function __set($name, $value)
- {
- $setProperty = 'set'.$name;
- if(in_array($setProperty, $this->properties))
- $this->$setProperty($value);
- else
- throw new Exception('Property '.$name.' can not be set.');
- }
-
- /**
- * Initializes a new writable instance of the NumberFormatInfo class
- * that is dependent on the ICU data for number, decimal, and currency
- * formatting information. N.B.You should not initialize this
- * class directly unless you know what you are doing. Please use use
- * NumberFormatInfo::getInstance() to create an instance.
- * @param array ICU data for date time formatting.
- * @see getInstance()
- */
- public function __construct($data=array(), $type=NumberFormatInfo::DECIMAL)
- {
- $this->properties = get_class_methods($this);
-
- if(empty($data))
- throw new Exception('Please provide the ICU data to initialize.');
-
- $this->data = $data;
-
- $this->setPattern($type);
- }
-
- /**
- * Set the pattern for a specific number pattern. The validate patterns
- * NumberFormatInfo::DECIMAL, NumberFormatInfo::CURRENCY,
- * NumberFormatInfo::PERCENTAGE, or NumberFormatInfo::SCIENTIFIC
- * @param int pattern type.
- */
- public function setPattern($type=NumberFormatInfo::DECIMAL)
- {
- if(is_int($type))
- $this->pattern =
- $this->parsePattern($this->data['NumberPatterns'][$type]);
- else
- $this->pattern = $this->parsePattern($type);
-
- $this->pattern['negInfty'] =
- $this->data['NumberElements'][6].
- $this->data['NumberElements'][9];
-
- $this->pattern['posInfty'] =
- $this->data['NumberElements'][11].
- $this->data['NumberElements'][9];
- }
-
- public function getPattern()
- {
- return $this->pattern;
- }
-
- /**
- * Gets the default NumberFormatInfo that is culture-independent
- * (invariant).
- * @return NumberFormatInfo default NumberFormatInfo.
- */
- public static function getInvariantInfo($type=NumberFormatInfo::DECIMAL)
- {
- static $invariant;
- if($invariant === null)
- {
- $culture = CultureInfo::getInvariantCulture();
- $invariant = $culture->NumberFormat;
- $invariant->setPattern($type);
- }
- return $invariant;
- }
-
- /**
- * Returns the NumberFormatInfo associated with the specified culture.
- * @param CultureInfo the culture that gets the NumberFormat property.
- * @param int the number formatting type, it should be
- * NumberFormatInfo::DECIMAL, NumberFormatInfo::CURRENCY,
- * NumberFormatInfo::PERCENTAGE, or NumberFormatInfo::SCIENTIFIC
- * @return NumberFormatInfo NumberFormatInfo for the specified
- * culture.
- * @see getCurrencyInstance();
- * @see getPercentageInstance();
- * @see getScientificInstance();
- */
- public static function getInstance($culture=null,
- $type=NumberFormatInfo::DECIMAL)
- {
- if ($culture instanceof CultureInfo)
- {
- $formatInfo = $culture->NumberFormat;
- $formatInfo->setPattern($type);
- return $formatInfo;
- }
- else if(is_string($culture))
- {
- $cultureInfo = new CultureInfo($culture);
- $formatInfo = $cultureInfo->NumberFormat;
- $formatInfo->setPattern($type);
- return $formatInfo;
- }
- else
- {
- $cultureInfo = new CultureInfo();
- $formatInfo = $cultureInfo->NumberFormat;
- $formatInfo->setPattern($type);
- return $formatInfo;
- }
- }
-
- /**
- * Returns the currency format info associated with the specified culture.
- * @param CultureInfo the culture that gets the NumberFormat property.
- * @return NumberFormatInfo NumberFormatInfo for the specified
- * culture.
- */
- public static function getCurrencyInstance($culture=null)
- {
- return self::getInstance($culture, self::CURRENCY);
- }
-
- /**
- * Returns the percentage format info associated with the specified culture.
- * @param CultureInfo the culture that gets the NumberFormat property.
- * @return NumberFormatInfo NumberFormatInfo for the specified
- * culture.
- */
- public static function getPercentageInstance($culture=null)
- {
- return self::getInstance($culture, self::PERCENTAGE);
- }
-
- /**
- * Returns the scientific format info associated with the specified culture.
- * @param CultureInfo the culture that gets the NumberFormat property.
- * @return NumberFormatInfo NumberFormatInfo for the specified
- * culture.
- */
- public static function getScientificInstance($culture=null)
- {
- return self::getInstance($culture, self::SCIENTIFIC);
- }
-
- /**
- * Parse the given pattern and return a list of known properties.
- * @param string a number pattern.
- * @return array list of pattern properties.
- */
- protected function parsePattern($pattern)
- {
- $pattern = explode(';',$pattern);
-
- $negative = null;
- if(count($pattern) > 1)
- $negative = $pattern[1];
- $pattern = $pattern[0];
-
- $comma = ',';
- $dot = '.';
- $digit = '0';
- $hash = '#';
-
- //find the first group point, and decimal point
- $groupPos1 = strrpos($pattern,$comma);
- $decimalPos = strrpos($pattern,$dot);
-
- $groupPos2 = false;
- $groupSize1 = false;
- $groupSize2 = false;
- $decimalPoints = is_int($decimalPos)?-1:false;
-
- $info['negPref'] = $this->data['NumberElements'][6];
- $info['negPost'] = '';
-
- $info['negative'] = $negative;
- $info['positive'] = $pattern;
-
- //find the negative prefix and postfix
- if($negative)
- {
- $prefixPostfix = $this->getPrePostfix($negative);
- $info['negPref'] = $prefixPostfix[0];
- $info['negPost'] = $prefixPostfix[1];
- }
-
- $posfix = $this->getPrePostfix($pattern);
- $info['posPref'] = $posfix[0];
- $info['posPost'] = $posfix[1];
-
- //var_dump($pattern);
- //var_dump($decimalPos);
- if(is_int($groupPos1))
- {
- //get the second group
- $groupPos2 = strrpos(substr($pattern,0,$groupPos1),$comma);
-
- //get the number of decimal digits
- if(is_int($decimalPos))
- {
- $groupSize1 = $decimalPos - $groupPos1-1;
-
- }
- else
- {
- //no decimal point, so traverse from the back
- //to find the groupsize 1.
- for($i=strlen($pattern)-1; $i>=0; $i--)
- {
- if($pattern{$i} == $digit || $pattern{$i}==$hash)
- {
- $groupSize1 = $i - $groupPos1;
- break;
- }
- }
- }
-
- //get the second group size
- if(is_int($groupPos2))
- $groupSize2 = $groupPos1 - $groupPos2-1;
- }
-
- if(is_int($decimalPos))
- {
- for($i=strlen($pattern)-1; $i>=0; $i--)
- {
- if($pattern{$i} == $dot) break;
- if($pattern{$i} == $digit)
- {
- $decimalPoints = $i - $decimalPos;
- break;
- }
- }
- }
-
- if(is_int($decimalPos))
- $digitPattern = substr($pattern,0,$decimalPos);
- else
- $digitPattern = $pattern;
-
- $digitPattern = preg_replace('/[^0]/','',$digitPattern);
-
- $info['groupPos1'] = $groupPos1;
- $info['groupSize1'] = $groupSize1;
- $info['groupPos2'] = $groupPos2;
- $info['groupSize2'] = $groupSize2;
- $info['decimalPos'] = $decimalPos;
- $info['decimalPoints'] = $decimalPoints;
- $info['digitSize'] = strlen($digitPattern);
- return $info;
- }
-
- /**
- * Get the prefix and postfix of a pattern.
- * @param string pattern
- * @return array of prefix and postfix, array(prefix,postfix).
- */
- protected function getPrePostfix($pattern)
- {
- $regexp = '/[#,\.0]+/';
- $result = preg_split($regexp, $pattern);
- return array($result[0],$result[1]);
- }
-
-
- /**
- * Indicates the number of decimal places.
- * @return int number of decimal places.
- */
- function getDecimalDigits()
- {
- return $this->pattern['decimalPoints'];
- }
-
- /**
- * Set the number of decimal places.
- * @param int number of decimal places.
- */
- function setDecimalDigits($value)
- {
- return $this->pattern['decimalPoints'] = $value;
- }
-
- function getDigitSize()
- {
- return $this->pattern['digitSize'];
- }
-
- function setDigitSize($value)
- {
- $this->pattern['digitSize'] = $value;
- }
-
- /**
- * Gets the string to use as the decimal separator.
- * @return string decimal separator.
- */
- function getDecimalSeparator()
- {
- return $this->data['NumberElements'][0];
- }
-
- /**
- * Set the string to use as the decimal separator.
- * @param string the decimal point
- */
- function setDecimalSeparator($value)
- {
- return $this->data['NumberElements'][0] = $value;
- }
-
- /**
- * Gets the string that separates groups of digits to the left
- * of the decimal in currency values.
- * @param parameter
- * @return string currency group separator.
- */
- function getGroupSeparator()
- {
- return $this->data['NumberElements'][1];
- }
-
- /**
- * Set the string to use as the group separator.
- * @param string the group separator.
- */
- function setGroupSeparator($value)
- {
- return $this->data['NumberElements'][1] = $value;
- }
-
- /**
- * Gets the number of digits in each group to the left of the decimal
- * There can be two grouping sizes, this fucntion
- * returns array(group1, group2), if there is only 1 grouping size,
- * group2 will be false.
- * @return array grouping size(s).
- */
- function getGroupSizes()
- {
- $group1 = $this->pattern['groupSize1'];
- $group2 = $this->pattern['groupSize2'];
-
- return array($group1, $group2);
- }
-
- /**
- * Set the number of digits in each group to the left of the decimal.
- * There can be two grouping sizes, the value should
- * be an array(group1, group2), if there is only 1 grouping size,
- * group2 should be false.
- * @param array grouping size(s).
- */
- function setGroupSizes($groupSize)
- {
- $this->pattern['groupSize1'] = $groupSize[0];
- $this->pattern['groupSize2'] = $groupSize[1];
- }
-
- /**
- * Gets the format pattern for negative values.
- * The negative pattern is composed of a prefix, and postfix.
- * This function returns array(prefix, postfix).
- * @return arary negative pattern.
- */
- function getNegativePattern()
- {
- $prefix = $this->pattern['negPref'];
- $postfix = $this->pattern['negPost'];
- return array($prefix, $postfix);
- }
-
- /**
- * Set the format pattern for negative values.
- * The negative pattern is composed of a prefix, and postfix in the form
- * array(prefix, postfix).
- * @param arary negative pattern.
- */
- function setNegativePattern($pattern)
- {
- $this->pattern['negPref'] = $pattern[0];
- $this->pattern['negPost'] = $pattern[1];
- }
-
- /**
- * Gets the format pattern for positive values.
- * The positive pattern is composed of a prefix, and postfix.
- * This function returns array(prefix, postfix).
- * @return arary positive pattern.
- */
- function getPositivePattern()
- {
- $prefix = $this->pattern['posPref'];
- $postfix = $this->pattern['posPost'];
- return array($prefix, $postfix);
- }
-
- /**
- * Set the format pattern for positive values.
- * The positive pattern is composed of a prefix, and postfix in the form
- * array(prefix, postfix).
- * @param arary positive pattern.
- */
- function setPositivePattern($pattern)
- {
- $this->pattern['posPref'] = $pattern[0];
- $this->pattern['posPost'] = $pattern[1];
- }
-
- /**
- * Gets the string to use as the currency symbol.
- * @return string currency symbol.
- */
- function getCurrencySymbol($currency='USD')
- {
- if(isset($this->pattern['symbol']))
- return $this->pattern['symbol'];
- else
- return $this->data['Currencies'][$currency][0];
- }
-
-
- /**
- * Set the string to use as the currency symbol.
- * @param string currency symbol.
- */
- function setCurrencySymbol($symbol)
- {
- $this->pattern['symbol'] = $symbol;
- }
-
- /**
- * Gets the string that represents negative infinity.
- * @return string negative infinity.
- */
- function getNegativeInfinitySymbol()
- {
- return $this->pattern['negInfty'];
- }
-
- /**
- * Set the string that represents negative infinity.
- * @param string negative infinity.
- */
- function setNegativeInfinitySymbol($value)
- {
- $this->pattern['negInfty'] = $value;
- }
-
- /**
- * Gets the string that represents positive infinity.
- * @return string positive infinity.
- */
- function getPositiveInfinitySymbol()
- {
- return $this->pattern['posInfty'];
- }
-
- /**
- * Set the string that represents positive infinity.
- * @param string positive infinity.
- */
- function setPositiveInfinitySymbol($value)
- {
- $this->pattern['posInfty'] = $value;
- }
-
- /**
- * Gets the string that denotes that the associated number is negative.
- * @return string negative sign.
- */
- function getNegativeSign()
- {
- return $this->data['NumberElements'][6];
- }
-
- /**
- * Set the string that denotes that the associated number is negative.
- * @param string negative sign.
- */
- function setNegativeSign($value)
- {
- $this->data['NumberElements'][6] = $value;
- }
-
- /**
- * Gets the string that denotes that the associated number is positive.
- * @return string positive sign.
- */
- function getPositiveSign()
- {
- return $this->data['NumberElements'][11];
- }
-
- /**
- * Set the string that denotes that the associated number is positive.
- * @param string positive sign.
- */
- function setPositiveSign($value)
- {
- $this->data['NumberElements'][11] = $value;
- }
-
- /**
- * Gets the string that represents the IEEE NaN (not a number) value.
- * @return string NaN symbol.
- */
- function getNaNSymbol()
- {
- return $this->data['NumberElements'][10];
- }
-
- /**
- * Set the string that represents the IEEE NaN (not a number) value.
- * @param string NaN symbol.
- */
- function setNaNSymbol($value)
- {
- $this->data['NumberElements'][10] = $value;
- }
-
- /**
- * Gets the string to use as the percent symbol.
- * @return string percent symbol.
- */
- function getPercentSymbol()
- {
- return $this->data['NumberElements'][3];
- }
-
- /**
- * Set the string to use as the percent symbol.
- * @param string percent symbol.
- */
- function setPercentSymbol($value)
- {
- $this->data['NumberElements'][3] = $value;
- }
-
- /**
- * Gets the string to use as the per mille symbol.
- * @return string percent symbol.
- */
- function getPerMilleSymbol()
- {
- return $this->data['NumberElements'][8];
- }
-
- /**
- * Set the string to use as the per mille symbol.
- * @param string percent symbol.
- */
- function setPerMilleSymbol($value)
- {
- $this->data['NumberElements'][8] = $value;
- }
-}
-
+
+ * @version $Revision: 1.3 $ $Date: 2005/08/04 05:27:19 $
+ * @package System.I18N.core
+ */
+
+/**
+ * Get the CultureInfo class file.
+ */
+require_once(dirname(__FILE__).'/CultureInfo.php');
+
+/**
+ * NumberFormatInfo class
+ *
+ * Defines how numeric values are formatted and displayed,
+ * depending on the culture. Numeric values are formatted using
+ * standard or custom patterns stored in the properties of a
+ * NumberFormatInfo.
+ *
+ * This class contains information, such as currency, decimal
+ * separators, and other numeric symbols.
+ *
+ * To create a NumberFormatInfo for a specific culture,
+ * create a CultureInfo for that culture and retrieve the
+ * CultureInfo->NumberFormat property. Or use
+ * NumberFormatInfo::getInstance($culture).
+ * To create a NumberFormatInfo for the invariant culture, use the
+ * InvariantInfo::getInvariantInfo().
+ *
+ *
+ * @author Xiang Wei Zhuo
+ * @version v1.0, last update on Sun Dec 05 14:48:26 EST 2004
+ * @package System.I18N.core
+ */
+class NumberFormatInfo
+{
+
+ /**
+ * ICU number formatting data.
+ * @var array
+ */
+ private $data = array();
+
+ /**
+ * A list of properties that are accessable/writable.
+ * @var array
+ */
+ protected $properties = array();
+
+ /**
+ * The number pattern.
+ * @var array
+ */
+ protected $pattern = array();
+
+ const DECIMAL = 0;
+ const CURRENCY = 1;
+ const PERCENTAGE = 2;
+ const SCIENTIFIC = 3;
+
+ /**
+ * Allow functions that begins with 'set' to be called directly
+ * as an attribute/property to retrieve the value.
+ * @return mixed
+ */
+ public function __get($name)
+ {
+ $getProperty = 'get'.$name;
+ if(in_array($getProperty, $this->properties))
+ return $this->$getProperty();
+ else
+ throw new Exception('Property '.$name.' does not exists.');
+ }
+
+ /**
+ * Allow functions that begins with 'set' to be called directly
+ * as an attribute/property to set the value.
+ */
+ public function __set($name, $value)
+ {
+ $setProperty = 'set'.$name;
+ if(in_array($setProperty, $this->properties))
+ $this->$setProperty($value);
+ else
+ throw new Exception('Property '.$name.' can not be set.');
+ }
+
+ /**
+ * Initializes a new writable instance of the NumberFormatInfo class
+ * that is dependent on the ICU data for number, decimal, and currency
+ * formatting information. N.B.You should not initialize this
+ * class directly unless you know what you are doing. Please use use
+ * NumberFormatInfo::getInstance() to create an instance.
+ * @param array ICU data for date time formatting.
+ * @see getInstance()
+ */
+ public function __construct($data=array(), $type=NumberFormatInfo::DECIMAL)
+ {
+ $this->properties = get_class_methods($this);
+
+ if(empty($data))
+ throw new Exception('Please provide the ICU data to initialize.');
+
+ $this->data = $data;
+
+ $this->setPattern($type);
+ }
+
+ /**
+ * Set the pattern for a specific number pattern. The validate patterns
+ * NumberFormatInfo::DECIMAL, NumberFormatInfo::CURRENCY,
+ * NumberFormatInfo::PERCENTAGE, or NumberFormatInfo::SCIENTIFIC
+ * @param int pattern type.
+ */
+ public function setPattern($type=NumberFormatInfo::DECIMAL)
+ {
+ if(is_int($type))
+ $this->pattern =
+ $this->parsePattern($this->data['NumberPatterns'][$type]);
+ else
+ $this->pattern = $this->parsePattern($type);
+
+ $this->pattern['negInfty'] =
+ $this->data['NumberElements'][6].
+ $this->data['NumberElements'][9];
+
+ $this->pattern['posInfty'] =
+ $this->data['NumberElements'][11].
+ $this->data['NumberElements'][9];
+ }
+
+ public function getPattern()
+ {
+ return $this->pattern;
+ }
+
+ /**
+ * Gets the default NumberFormatInfo that is culture-independent
+ * (invariant).
+ * @return NumberFormatInfo default NumberFormatInfo.
+ */
+ public static function getInvariantInfo($type=NumberFormatInfo::DECIMAL)
+ {
+ static $invariant;
+ if($invariant === null)
+ {
+ $culture = CultureInfo::getInvariantCulture();
+ $invariant = $culture->NumberFormat;
+ $invariant->setPattern($type);
+ }
+ return $invariant;
+ }
+
+ /**
+ * Returns the NumberFormatInfo associated with the specified culture.
+ * @param CultureInfo the culture that gets the NumberFormat property.
+ * @param int the number formatting type, it should be
+ * NumberFormatInfo::DECIMAL, NumberFormatInfo::CURRENCY,
+ * NumberFormatInfo::PERCENTAGE, or NumberFormatInfo::SCIENTIFIC
+ * @return NumberFormatInfo NumberFormatInfo for the specified
+ * culture.
+ * @see getCurrencyInstance();
+ * @see getPercentageInstance();
+ * @see getScientificInstance();
+ */
+ public static function getInstance($culture=null,
+ $type=NumberFormatInfo::DECIMAL)
+ {
+ if ($culture instanceof CultureInfo)
+ {
+ $formatInfo = $culture->NumberFormat;
+ $formatInfo->setPattern($type);
+ return $formatInfo;
+ }
+ else if(is_string($culture))
+ {
+ $cultureInfo = new CultureInfo($culture);
+ $formatInfo = $cultureInfo->NumberFormat;
+ $formatInfo->setPattern($type);
+ return $formatInfo;
+ }
+ else
+ {
+ $cultureInfo = new CultureInfo();
+ $formatInfo = $cultureInfo->NumberFormat;
+ $formatInfo->setPattern($type);
+ return $formatInfo;
+ }
+ }
+
+ /**
+ * Returns the currency format info associated with the specified culture.
+ * @param CultureInfo the culture that gets the NumberFormat property.
+ * @return NumberFormatInfo NumberFormatInfo for the specified
+ * culture.
+ */
+ public static function getCurrencyInstance($culture=null)
+ {
+ return self::getInstance($culture, self::CURRENCY);
+ }
+
+ /**
+ * Returns the percentage format info associated with the specified culture.
+ * @param CultureInfo the culture that gets the NumberFormat property.
+ * @return NumberFormatInfo NumberFormatInfo for the specified
+ * culture.
+ */
+ public static function getPercentageInstance($culture=null)
+ {
+ return self::getInstance($culture, self::PERCENTAGE);
+ }
+
+ /**
+ * Returns the scientific format info associated with the specified culture.
+ * @param CultureInfo the culture that gets the NumberFormat property.
+ * @return NumberFormatInfo NumberFormatInfo for the specified
+ * culture.
+ */
+ public static function getScientificInstance($culture=null)
+ {
+ return self::getInstance($culture, self::SCIENTIFIC);
+ }
+
+ /**
+ * Parse the given pattern and return a list of known properties.
+ * @param string a number pattern.
+ * @return array list of pattern properties.
+ */
+ protected function parsePattern($pattern)
+ {
+ $pattern = explode(';',$pattern);
+
+ $negative = null;
+ if(count($pattern) > 1)
+ $negative = $pattern[1];
+ $pattern = $pattern[0];
+
+ $comma = ',';
+ $dot = '.';
+ $digit = '0';
+ $hash = '#';
+
+ //find the first group point, and decimal point
+ $groupPos1 = strrpos($pattern,$comma);
+ $decimalPos = strrpos($pattern,$dot);
+
+ $groupPos2 = false;
+ $groupSize1 = false;
+ $groupSize2 = false;
+ $decimalPoints = is_int($decimalPos)?-1:false;
+
+ $info['negPref'] = $this->data['NumberElements'][6];
+ $info['negPost'] = '';
+
+ $info['negative'] = $negative;
+ $info['positive'] = $pattern;
+
+ //find the negative prefix and postfix
+ if($negative)
+ {
+ $prefixPostfix = $this->getPrePostfix($negative);
+ $info['negPref'] = $prefixPostfix[0];
+ $info['negPost'] = $prefixPostfix[1];
+ }
+
+ $posfix = $this->getPrePostfix($pattern);
+ $info['posPref'] = $posfix[0];
+ $info['posPost'] = $posfix[1];
+
+ //var_dump($pattern);
+ //var_dump($decimalPos);
+ if(is_int($groupPos1))
+ {
+ //get the second group
+ $groupPos2 = strrpos(substr($pattern,0,$groupPos1),$comma);
+
+ //get the number of decimal digits
+ if(is_int($decimalPos))
+ {
+ $groupSize1 = $decimalPos - $groupPos1-1;
+
+ }
+ else
+ {
+ //no decimal point, so traverse from the back
+ //to find the groupsize 1.
+ for($i=strlen($pattern)-1; $i>=0; $i--)
+ {
+ if($pattern{$i} == $digit || $pattern{$i}==$hash)
+ {
+ $groupSize1 = $i - $groupPos1;
+ break;
+ }
+ }
+ }
+
+ //get the second group size
+ if(is_int($groupPos2))
+ $groupSize2 = $groupPos1 - $groupPos2-1;
+ }
+
+ if(is_int($decimalPos))
+ {
+ for($i=strlen($pattern)-1; $i>=0; $i--)
+ {
+ if($pattern{$i} == $dot) break;
+ if($pattern{$i} == $digit)
+ {
+ $decimalPoints = $i - $decimalPos;
+ break;
+ }
+ }
+ }
+
+ if(is_int($decimalPos))
+ $digitPattern = substr($pattern,0,$decimalPos);
+ else
+ $digitPattern = $pattern;
+
+ $digitPattern = preg_replace('/[^0]/','',$digitPattern);
+
+ $info['groupPos1'] = $groupPos1;
+ $info['groupSize1'] = $groupSize1;
+ $info['groupPos2'] = $groupPos2;
+ $info['groupSize2'] = $groupSize2;
+ $info['decimalPos'] = $decimalPos;
+ $info['decimalPoints'] = $decimalPoints;
+ $info['digitSize'] = strlen($digitPattern);
+ return $info;
+ }
+
+ /**
+ * Get the prefix and postfix of a pattern.
+ * @param string pattern
+ * @return array of prefix and postfix, array(prefix,postfix).
+ */
+ protected function getPrePostfix($pattern)
+ {
+ $regexp = '/[#,\.0]+/';
+ $result = preg_split($regexp, $pattern);
+ return array($result[0],$result[1]);
+ }
+
+
+ /**
+ * Indicates the number of decimal places.
+ * @return int number of decimal places.
+ */
+ function getDecimalDigits()
+ {
+ return $this->pattern['decimalPoints'];
+ }
+
+ /**
+ * Set the number of decimal places.
+ * @param int number of decimal places.
+ */
+ function setDecimalDigits($value)
+ {
+ return $this->pattern['decimalPoints'] = $value;
+ }
+
+ function getDigitSize()
+ {
+ return $this->pattern['digitSize'];
+ }
+
+ function setDigitSize($value)
+ {
+ $this->pattern['digitSize'] = $value;
+ }
+
+ /**
+ * Gets the string to use as the decimal separator.
+ * @return string decimal separator.
+ */
+ function getDecimalSeparator()
+ {
+ return $this->data['NumberElements'][0];
+ }
+
+ /**
+ * Set the string to use as the decimal separator.
+ * @param string the decimal point
+ */
+ function setDecimalSeparator($value)
+ {
+ return $this->data['NumberElements'][0] = $value;
+ }
+
+ /**
+ * Gets the string that separates groups of digits to the left
+ * of the decimal in currency values.
+ * @param parameter
+ * @return string currency group separator.
+ */
+ function getGroupSeparator()
+ {
+ return $this->data['NumberElements'][1];
+ }
+
+ /**
+ * Set the string to use as the group separator.
+ * @param string the group separator.
+ */
+ function setGroupSeparator($value)
+ {
+ return $this->data['NumberElements'][1] = $value;
+ }
+
+ /**
+ * Gets the number of digits in each group to the left of the decimal
+ * There can be two grouping sizes, this fucntion
+ * returns array(group1, group2), if there is only 1 grouping size,
+ * group2 will be false.
+ * @return array grouping size(s).
+ */
+ function getGroupSizes()
+ {
+ $group1 = $this->pattern['groupSize1'];
+ $group2 = $this->pattern['groupSize2'];
+
+ return array($group1, $group2);
+ }
+
+ /**
+ * Set the number of digits in each group to the left of the decimal.
+ * There can be two grouping sizes, the value should
+ * be an array(group1, group2), if there is only 1 grouping size,
+ * group2 should be false.
+ * @param array grouping size(s).
+ */
+ function setGroupSizes($groupSize)
+ {
+ $this->pattern['groupSize1'] = $groupSize[0];
+ $this->pattern['groupSize2'] = $groupSize[1];
+ }
+
+ /**
+ * Gets the format pattern for negative values.
+ * The negative pattern is composed of a prefix, and postfix.
+ * This function returns array(prefix, postfix).
+ * @return arary negative pattern.
+ */
+ function getNegativePattern()
+ {
+ $prefix = $this->pattern['negPref'];
+ $postfix = $this->pattern['negPost'];
+ return array($prefix, $postfix);
+ }
+
+ /**
+ * Set the format pattern for negative values.
+ * The negative pattern is composed of a prefix, and postfix in the form
+ * array(prefix, postfix).
+ * @param arary negative pattern.
+ */
+ function setNegativePattern($pattern)
+ {
+ $this->pattern['negPref'] = $pattern[0];
+ $this->pattern['negPost'] = $pattern[1];
+ }
+
+ /**
+ * Gets the format pattern for positive values.
+ * The positive pattern is composed of a prefix, and postfix.
+ * This function returns array(prefix, postfix).
+ * @return arary positive pattern.
+ */
+ function getPositivePattern()
+ {
+ $prefix = $this->pattern['posPref'];
+ $postfix = $this->pattern['posPost'];
+ return array($prefix, $postfix);
+ }
+
+ /**
+ * Set the format pattern for positive values.
+ * The positive pattern is composed of a prefix, and postfix in the form
+ * array(prefix, postfix).
+ * @param arary positive pattern.
+ */
+ function setPositivePattern($pattern)
+ {
+ $this->pattern['posPref'] = $pattern[0];
+ $this->pattern['posPost'] = $pattern[1];
+ }
+
+ /**
+ * Gets the string to use as the currency symbol.
+ * @return string currency symbol.
+ */
+ function getCurrencySymbol($currency='USD')
+ {
+ if(isset($this->pattern['symbol']))
+ return $this->pattern['symbol'];
+ else
+ return $this->data['Currencies'][$currency][0];
+ }
+
+
+ /**
+ * Set the string to use as the currency symbol.
+ * @param string currency symbol.
+ */
+ function setCurrencySymbol($symbol)
+ {
+ $this->pattern['symbol'] = $symbol;
+ }
+
+ /**
+ * Gets the string that represents negative infinity.
+ * @return string negative infinity.
+ */
+ function getNegativeInfinitySymbol()
+ {
+ return $this->pattern['negInfty'];
+ }
+
+ /**
+ * Set the string that represents negative infinity.
+ * @param string negative infinity.
+ */
+ function setNegativeInfinitySymbol($value)
+ {
+ $this->pattern['negInfty'] = $value;
+ }
+
+ /**
+ * Gets the string that represents positive infinity.
+ * @return string positive infinity.
+ */
+ function getPositiveInfinitySymbol()
+ {
+ return $this->pattern['posInfty'];
+ }
+
+ /**
+ * Set the string that represents positive infinity.
+ * @param string positive infinity.
+ */
+ function setPositiveInfinitySymbol($value)
+ {
+ $this->pattern['posInfty'] = $value;
+ }
+
+ /**
+ * Gets the string that denotes that the associated number is negative.
+ * @return string negative sign.
+ */
+ function getNegativeSign()
+ {
+ return $this->data['NumberElements'][6];
+ }
+
+ /**
+ * Set the string that denotes that the associated number is negative.
+ * @param string negative sign.
+ */
+ function setNegativeSign($value)
+ {
+ $this->data['NumberElements'][6] = $value;
+ }
+
+ /**
+ * Gets the string that denotes that the associated number is positive.
+ * @return string positive sign.
+ */
+ function getPositiveSign()
+ {
+ return $this->data['NumberElements'][11];
+ }
+
+ /**
+ * Set the string that denotes that the associated number is positive.
+ * @param string positive sign.
+ */
+ function setPositiveSign($value)
+ {
+ $this->data['NumberElements'][11] = $value;
+ }
+
+ /**
+ * Gets the string that represents the IEEE NaN (not a number) value.
+ * @return string NaN symbol.
+ */
+ function getNaNSymbol()
+ {
+ return $this->data['NumberElements'][10];
+ }
+
+ /**
+ * Set the string that represents the IEEE NaN (not a number) value.
+ * @param string NaN symbol.
+ */
+ function setNaNSymbol($value)
+ {
+ $this->data['NumberElements'][10] = $value;
+ }
+
+ /**
+ * Gets the string to use as the percent symbol.
+ * @return string percent symbol.
+ */
+ function getPercentSymbol()
+ {
+ return $this->data['NumberElements'][3];
+ }
+
+ /**
+ * Set the string to use as the percent symbol.
+ * @param string percent symbol.
+ */
+ function setPercentSymbol($value)
+ {
+ $this->data['NumberElements'][3] = $value;
+ }
+
+ /**
+ * Gets the string to use as the per mille symbol.
+ * @return string percent symbol.
+ */
+ function getPerMilleSymbol()
+ {
+ return $this->data['NumberElements'][8];
+ }
+
+ /**
+ * Set the string to use as the per mille symbol.
+ * @param string percent symbol.
+ */
+ function setPerMilleSymbol($value)
+ {
+ $this->data['NumberElements'][8] = $value;
+ }
+}
+
diff --git a/framework/I18N/core/TCache_Lite.php b/framework/I18N/core/TCache_Lite.php
index 6ef78852..665ca469 100644
--- a/framework/I18N/core/TCache_Lite.php
+++ b/framework/I18N/core/TCache_Lite.php
@@ -1,629 +1,629 @@
-
- * @version $Revision: 1.3 $ $Date: 2005/10/09 10:24:12 $
- * @package System.I18N.core
- */
-
-/**
-* Fast, light and safe Cache Class
-*
-* TCache_Lite is a fast, light and safe cache system. It's optimized
-* for file containers. It is fast and safe (because it uses file
-* locking and/or anti-corruption tests).
-*
-* There are some examples in the 'docs/examples' file
-* Technical choices are described in the 'docs/technical' file
-*
-* A tutorial is available in english at this url :
-* http://www.pearfr.org/index.php/en/article/cache_lite
-* (big thanks to Pierre-Alain Joye for the translation)
-*
-* The same tutorial is also available in french at this url :
-* http://www.pearfr.org/index.php/fr/article/cache_lite
-*
-* Memory Caching is from an original idea of
-* Mike BENOIT
-*
-* @package System.I18N.core
-* @version $Id$
-* @author Fabien MARTY
-* @copyright 1997-2005 The PHP Group
-* @license http://www.gnu.org/copyleft/lesser.html GNU LGPL
-* @link http://pear.php.net/package/Cache_Lite
-*/
-class TCache_Lite
-{
-
- // --- Private properties ---
-
- /**
- * Directory where to put the cache files
- * (make sure to add a trailing slash)
- *
- * @var string $_cacheDir
- */
- protected $_cacheDir = '/tmp/';
-
- /**
- * Enable / disable caching
- *
- * (can be very usefull for the debug of cached scripts)
- *
- * @var boolean $_caching
- */
- protected $_caching = true;
-
- /**
- * Cache lifetime (in seconds)
- *
- * @var int $_lifeTime
- */
- protected $_lifeTime = 3600;
-
- /**
- * Enable / disable fileLocking
- *
- * (can avoid cache corruption under bad circumstances)
- *
- * @var boolean $_fileLocking
- */
- protected $_fileLocking = true;
-
- /**
- * Timestamp of the last valid cache
- *
- * @var int $_refreshTime
- */
- protected $_refreshTime;
-
- /**
- * File name (with path)
- *
- * @var string $_file
- */
- protected $_file;
-
- /**
- * Enable / disable write control (the cache is read just after writing
- * to detect corrupt entries)
- *
- * Enable write control will lightly slow the cache writing but not the
- * cache reading. Write control can detect some corrupt cache files but
- * maybe it's not a perfect control
- *
- * @var boolean $_writeControl
- */
- protected $_writeControl = true;
-
- /**
- * Enable / disable read control
- *
- * If enabled, a control key is embeded in cache file and this key is
- * compared with the one calculated after the reading.
- *
- * @var boolean $_writeControl
- */
- protected $_readControl = true;
-
- /**
- * Type of read control (only if read control is enabled)
- *
- * Available values are :
- * 'md5' for a md5 hash control (best but slowest)
- * 'crc32' for a crc32 hash control (lightly less safe but faster,
- * better choice)
- * 'strlen' for a length only test (fastest)
- *
- * @var boolean $_readControlType
- */
- protected $_readControlType = 'crc32';
-
- /**
- * Current cache id
- *
- * @var string $_id
- */
- protected $_id;
-
- /**
- * Current cache group
- *
- * @var string $_group
- */
- protected $_group;
-
- /**
- * Enable / Disable "Memory Caching"
- *
- * NB : There is no lifetime for memory caching !
- *
- * @var boolean $_memoryCaching
- */
- protected $_memoryCaching = false;
-
- /**
- * Enable / Disable "Only Memory Caching"
- * (be carefull, memory caching is "beta quality")
- *
- * @var boolean $_onlyMemoryCaching
- */
- protected $_onlyMemoryCaching = false;
-
- /**
- * Memory caching array
- *
- * @var array $_memoryCachingArray
- */
- protected $_memoryCachingArray = array();
-
- /**
- * Memory caching counter
- *
- * @var int $memoryCachingCounter
- */
- protected $_memoryCachingCounter = 0;
-
- /**
- * Memory caching limit
- *
- * @var int $memoryCachingLimit
- */
- protected $_memoryCachingLimit = 1000;
-
- /**
- * File Name protection
- *
- * if set to true, you can use any cache id or group name
- * if set to false, it can be faster but cache ids and group names
- * will be used directly in cache file names so be carefull with
- * special characters...
- *
- * @var boolean $fileNameProtection
- */
- protected $_fileNameProtection = true;
-
- /**
- * Enable / disable automatic serialization
- *
- * it can be used to save directly datas which aren't strings
- * (but it's slower)
- *
- * @var boolean $_serialize
- */
- protected $_automaticSerialization = false;
-
- // --- Public methods ---
-
- /**
- * Constructor
- *
- * $options is an assoc. Available options are :
- * $options = array(
- * 'cacheDir' => directory where to put the cache files (string),
- * 'caching' => enable / disable caching (boolean),
- * 'lifeTime' => cache lifetime in seconds (int),
- * 'fileLocking' => enable / disable fileLocking (boolean),
- * 'writeControl' => enable / disable write control (boolean),
- * 'readControl' => enable / disable read control (boolean),
- * 'readControlType' => type of read control 'crc32', 'md5', 'strlen',
- * 'memoryCaching' => enable / disable memory caching (boolean),
- * 'onlyMemoryCaching' => enable / disable only memory caching (boolean),
- * 'memoryCachingLimit' => max nbr of records in memory caching (int),
- * 'fileNameProtection' => enable / disable file name protection (boolean),
- * 'automaticSerialization' => enable / disable serialization (boolean)
- * );
- *
- * @param array $options options
- * @access public
- */
- function TCache_Lite($options = array(null))
- {
- $availableOptions = array( 'automaticSerialization',
- 'fileNameProtection',
- 'memoryCaching',
- 'onlyMemoryCaching',
- 'memoryCachingLimit',
- 'cacheDir',
- 'caching',
- 'lifeTime',
- 'fileLocking',
- 'writeControl',
- 'readControl',
- 'readControlType');
- foreach($options as $key => $value) {
- if(in_array($key, $availableOptions)) {
- $property = '_'.$key;
- $this->$property = $value;
- }
- }
- $this->_refreshTime = time() - $this->_lifeTime;
- }
-
- /**
- * Test if a cache is available and (if yes) return it
- *
- * @param string $id cache id
- * @param string $group name of the cache group
- * @param boolean $doNotTestCacheValidity if set to true, the cache
- * validity won't be tested
- * @return string data of the cache (or false if no cache available)
- * @access public
- */
- function get($id, $group = 'default', $doNotTestCacheValidity = false)
- {
- $this->_id = $id;
- $this->_group = $group;
- $data = false;
- if ($this->_caching) {
- $this->_setFileName($id, $group);
- if ($this->_memoryCaching) {
- if (isset($this->_memoryCachingArray[$this->_file])) {
- if ($this->_automaticSerialization) {
- return unserialize(
- $this->_memoryCachingArray[$this->_file]);
- } else {
- return $this->_memoryCachingArray[$this->_file];
- }
- } else {
- if ($this->_onlyMemoryCaching) {
- return false;
- }
- }
- }
- if ($doNotTestCacheValidity) {
- if (file_exists($this->_file)) {
- $data = $this->_read();
- }
- } else {
- if (@filemtime($this->_file) > $this->_refreshTime) {
- $data = $this->_read();
- }
- }
- if (($data) and ($this->_memoryCaching)) {
- $this->_memoryCacheAdd($this->_file, $data);
- }
- if ($this->_automaticSerialization && is_string($data)) {
- $data = unserialize($data);
- }
- return $data;
- }
- return false;
- }
-
- /**
- * Save some data in a cache file
- *
- * @param string $data data to put in cache (can be another type than strings
- * if automaticSerialization is on)
- * @param string $id cache id
- * @param string $group name of the cache group
- * @return boolean true if no problem
- * @access public
- */
- function save($data, $id = null, $group = 'default')
- {
- if ($this->_caching) {
- if ($this->_automaticSerialization) {
- $data = serialize($data);
- }
- if (isset($id)) {
- $this->_setFileName($id, $group);
- }
- if ($this->_memoryCaching) {
- $this->_memoryCacheAdd($this->_file, $data);
- if ($this->_onlyMemoryCaching) {
- return true;
- }
- }
- if ($this->_writeControl) {
- if (!$this->_writeAndControl($data)) {
- @touch($this->_file, time() - 2*abs($this->_lifeTime));
- return false;
- } else {
- return true;
- }
- } else {
- return $this->_write($data);
- }
- }
- return false;
- }
-
- /**
- * Remove a cache file
- *
- * @param string $id cache id
- * @param string $group name of the cache group
- * @return boolean true if no problem
- * @access public
- */
- function remove($id, $group = 'default')
- {
- $this->_setFileName($id, $group);
- if (!@unlink($this->_file)) {
- $this->raiseError('TCache_Lite : Unable to remove cache !', -3);
- return false;
- }
- return true;
- }
-
- /**
- * Clean the cache
- *
- * if no group is specified all cache files will be destroyed
- * else only cache files of the specified group will be destroyed
- *
- * @param string $group name of the cache group
- * @return boolean true if no problem
- * @access public
- */
- function clean($group = false)
- {
- if ($this->_fileNameProtection) {
- $motif = ($group) ? 'cache_'.md5($group).'_' : 'cache_';
- } else {
- $motif = ($group) ? 'cache_'.$group.'_' : 'cache_';
- }
- if ($this->_memoryCaching) {
- while (list($key, $value) = each($this->_memoryCaching)) {
- if (strpos($key, $motif, 0)) {
- unset($this->_memoryCaching[$key]);
- $this->_memoryCachingCounter =
- $this->_memoryCachingCounter - 1;
- }
- }
- if ($this->_onlyMemoryCaching) {
- return true;
- }
- }
- if (!($dh = opendir($this->_cacheDir))) {
- $this->raiseError('TCache_Lite : Unable to open cache directory !');
- return false;
- }
- while ($file = readdir($dh)) {
- if (($file != '.') && ($file != '..')) {
- $file = $this->_cacheDir . $file;
- if (is_file($file)) {
- if (strpos($file, $motif, 0)) {
- if (!@unlink($file)) {
- $this->raiseError('Cache_Lite : Unable to remove cache !', -3);
- return false;
- }
- }
- }
- }
- }
- return true;
- }
-
- /**
- * Set a new life time
- *
- * @param int $newLifeTime new life time (in seconds)
- * @access public
- */
- function setLifeTime($newLifeTime)
- {
- $this->_lifeTime = $newLifeTime;
- $this->_refreshTime = time() - $newLifeTime;
- }
-
- /**
- *
- * @access public
- */
- function saveMemoryCachingState($id, $group = 'default')
- {
- if ($this->_caching) {
- $array = array(
- 'counter' => $this->_memoryCachingCounter,
- 'array' => $this->_memoryCachingState
- );
- $data = serialize($array);
- $this->save($data, $id, $group);
- }
- }
-
- /**
- *
- * @access public
- */
- function getMemoryCachingState($id, $group = 'default',
- $doNotTestCacheValidity = false)
- {
- if ($this->_caching) {
- if ($data = $this->get($id, $group, $doNotTestCacheValidity))
- {
- $array = unserialize($data);
- $this->_memoryCachingCounter = $array['counter'];
- $this->_memoryCachingArray = $array['array'];
- }
- }
- }
-
- /**
- * Return the cache last modification time
- *
- * BE CAREFUL : THIS METHOD IS FOR HACKING ONLY !
- *
- * @return int last modification time
- */
- function lastModified() {
- return filemtime($this->cache->_file);
- }
-
- /**
- * Trigger a PEAR error
- *
- * To improve performances, the PEAR.php file is included dynamically.
- * The file is so included only when an error is triggered. So, in most
- * cases, the file isn't included and perfs are much better.
- *
- * @param string $msg error message
- * @param int $code error code
- * @access public
- */
- function raiseError($msg, $code)
- {
- throw new Exception($msg);
- }
-
- // --- Private methods ---
-
- /**
- *
- * @access private
- */
- function _memoryCacheAdd($id, $data)
- {
- $this->_memoryCachingArray[$this->_file] = $data;
- if ($this->_memoryCachingCounter >= $this->_memoryCachingLimit) {
- list($key, $value) = each($this->_memoryCachingArray);
- unset($this->_memoryCachingArray[$key]);
- } else {
- $this->_memoryCachingCounter = $this->_memoryCachingCounter + 1;
- }
- }
-
- /**
- * Make a file name (with path)
- *
- * @param string $id cache id
- * @param string $group name of the group
- * @access private
- */
- function _setFileName($id, $group)
- {
- if ($this->_fileNameProtection) {
- $this->_file = ($this->_cacheDir.'cache_'.md5($group).'_'
- .md5($id));
- } else {
- $this->_file = $this->_cacheDir.'cache_'.$group.'_'.$id;
- }
- }
-
- function getCacheFile()
- {
- return $this->_file;
- }
-
- /**
- * Read the cache file and return the content
- *
- * @return string content of the cache file
- * @access private
- */
- function _read()
- {
- $fp = @fopen($this->_file, "rb");
- if ($this->_fileLocking) @flock($fp, LOCK_SH);
- if ($fp) {
- // because the filesize can be cached by PHP itself...
- clearstatcache();
- $length = @filesize($this->_file);
- if(version_compare(PHP_VERSION, '5.3.0', 'lt'))
- {
- $mqr = get_magic_quotes_runtime();
- set_magic_quotes_runtime(0);
- }
- if ($this->_readControl) {
- $hashControl = @fread($fp, 32);
- $length = $length - 32;
- }
- $data = @fread($fp, $length);
- if(isset($mqr))
- set_magic_quotes_runtime($mqr);
- if ($this->_fileLocking) @flock($fp, LOCK_UN);
- @fclose($fp);
- if ($this->_readControl) {
- $hashData = $this->_hash($data, $this->_readControlType);
- if ($hashData != $hashControl) {
- @touch($this->_file, time() - 2*abs($this->_lifeTime));
- return false;
- }
- }
- return $data;
- }
- $this->raiseError('Cache_Lite : Unable to read cache !', -2);
- return false;
- }
-
- /**
- * Write the given data in the cache file
- *
- * @param string $data data to put in cache
- * @return boolean true if ok
- * @access private
- */
- function _write($data)
- {
- $fp = @fopen($this->_file, "wb");
- if ($fp) {
- if ($this->_fileLocking) @flock($fp, LOCK_EX);
- if ($this->_readControl) {
- @fwrite($fp, $this->_hash($data, $this->_readControlType), 32);
- }
- $len = strlen($data);
- @fwrite($fp, $data, $len);
- if ($this->_fileLocking) @flock($fp, LOCK_UN);
- @fclose($fp);
- return true;
- }
- $this->raiseError('Cache_Lite : Unable to write cache !', -1);
- return false;
- }
-
- /**
- * Write the given data in the cache file and control it just after to avoid
- * corrupted cache entries
- *
- * @param string $data data to put in cache
- * @return boolean true if the test is ok
- * @access private
- */
- function _writeAndControl($data)
- {
- $this->_write($data);
- $dataRead = $this->_read($data);
- return ($dataRead==$data);
- }
-
- /**
- * Make a control key with the string containing datas
- *
- * @param string $data data
- * @param string $controlType type of control 'md5', 'crc32' or 'strlen'
- * @return string control key
- * @access private
- */
- function _hash($data, $controlType)
- {
- switch ($controlType) {
- case 'md5':
- return md5($data);
- case 'crc32':
- return sprintf('% 32d', crc32($data));
- case 'strlen':
- return sprintf('% 32d', strlen($data));
- default:
- $this->raiseError('Unknown controlType ! '.
- '(available values are only \'md5\', \'crc32\', \'strlen\')', -5);
- }
- }
-
-}
-
-?>
+
+ * @version $Revision: 1.3 $ $Date: 2005/10/09 10:24:12 $
+ * @package System.I18N.core
+ */
+
+/**
+* Fast, light and safe Cache Class
+*
+* TCache_Lite is a fast, light and safe cache system. It's optimized
+* for file containers. It is fast and safe (because it uses file
+* locking and/or anti-corruption tests).
+*
+* There are some examples in the 'docs/examples' file
+* Technical choices are described in the 'docs/technical' file
+*
+* A tutorial is available in english at this url :
+* http://www.pearfr.org/index.php/en/article/cache_lite
+* (big thanks to Pierre-Alain Joye for the translation)
+*
+* The same tutorial is also available in french at this url :
+* http://www.pearfr.org/index.php/fr/article/cache_lite
+*
+* Memory Caching is from an original idea of
+* Mike BENOIT
+*
+* @package System.I18N.core
+* @version $Id$
+* @author Fabien MARTY
+* @copyright 1997-2005 The PHP Group
+* @license http://www.gnu.org/copyleft/lesser.html GNU LGPL
+* @link http://pear.php.net/package/Cache_Lite
+*/
+class TCache_Lite
+{
+
+ // --- Private properties ---
+
+ /**
+ * Directory where to put the cache files
+ * (make sure to add a trailing slash)
+ *
+ * @var string $_cacheDir
+ */
+ protected $_cacheDir = '/tmp/';
+
+ /**
+ * Enable / disable caching
+ *
+ * (can be very usefull for the debug of cached scripts)
+ *
+ * @var boolean $_caching
+ */
+ protected $_caching = true;
+
+ /**
+ * Cache lifetime (in seconds)
+ *
+ * @var int $_lifeTime
+ */
+ protected $_lifeTime = 3600;
+
+ /**
+ * Enable / disable fileLocking
+ *
+ * (can avoid cache corruption under bad circumstances)
+ *
+ * @var boolean $_fileLocking
+ */
+ protected $_fileLocking = true;
+
+ /**
+ * Timestamp of the last valid cache
+ *
+ * @var int $_refreshTime
+ */
+ protected $_refreshTime;
+
+ /**
+ * File name (with path)
+ *
+ * @var string $_file
+ */
+ protected $_file;
+
+ /**
+ * Enable / disable write control (the cache is read just after writing
+ * to detect corrupt entries)
+ *
+ * Enable write control will lightly slow the cache writing but not the
+ * cache reading. Write control can detect some corrupt cache files but
+ * maybe it's not a perfect control
+ *
+ * @var boolean $_writeControl
+ */
+ protected $_writeControl = true;
+
+ /**
+ * Enable / disable read control
+ *
+ * If enabled, a control key is embeded in cache file and this key is
+ * compared with the one calculated after the reading.
+ *
+ * @var boolean $_writeControl
+ */
+ protected $_readControl = true;
+
+ /**
+ * Type of read control (only if read control is enabled)
+ *
+ * Available values are :
+ * 'md5' for a md5 hash control (best but slowest)
+ * 'crc32' for a crc32 hash control (lightly less safe but faster,
+ * better choice)
+ * 'strlen' for a length only test (fastest)
+ *
+ * @var boolean $_readControlType
+ */
+ protected $_readControlType = 'crc32';
+
+ /**
+ * Current cache id
+ *
+ * @var string $_id
+ */
+ protected $_id;
+
+ /**
+ * Current cache group
+ *
+ * @var string $_group
+ */
+ protected $_group;
+
+ /**
+ * Enable / Disable "Memory Caching"
+ *
+ * NB : There is no lifetime for memory caching !
+ *
+ * @var boolean $_memoryCaching
+ */
+ protected $_memoryCaching = false;
+
+ /**
+ * Enable / Disable "Only Memory Caching"
+ * (be carefull, memory caching is "beta quality")
+ *
+ * @var boolean $_onlyMemoryCaching
+ */
+ protected $_onlyMemoryCaching = false;
+
+ /**
+ * Memory caching array
+ *
+ * @var array $_memoryCachingArray
+ */
+ protected $_memoryCachingArray = array();
+
+ /**
+ * Memory caching counter
+ *
+ * @var int $memoryCachingCounter
+ */
+ protected $_memoryCachingCounter = 0;
+
+ /**
+ * Memory caching limit
+ *
+ * @var int $memoryCachingLimit
+ */
+ protected $_memoryCachingLimit = 1000;
+
+ /**
+ * File Name protection
+ *
+ * if set to true, you can use any cache id or group name
+ * if set to false, it can be faster but cache ids and group names
+ * will be used directly in cache file names so be carefull with
+ * special characters...
+ *
+ * @var boolean $fileNameProtection
+ */
+ protected $_fileNameProtection = true;
+
+ /**
+ * Enable / disable automatic serialization
+ *
+ * it can be used to save directly datas which aren't strings
+ * (but it's slower)
+ *
+ * @var boolean $_serialize
+ */
+ protected $_automaticSerialization = false;
+
+ // --- Public methods ---
+
+ /**
+ * Constructor
+ *
+ * $options is an assoc. Available options are :
+ * $options = array(
+ * 'cacheDir' => directory where to put the cache files (string),
+ * 'caching' => enable / disable caching (boolean),
+ * 'lifeTime' => cache lifetime in seconds (int),
+ * 'fileLocking' => enable / disable fileLocking (boolean),
+ * 'writeControl' => enable / disable write control (boolean),
+ * 'readControl' => enable / disable read control (boolean),
+ * 'readControlType' => type of read control 'crc32', 'md5', 'strlen',
+ * 'memoryCaching' => enable / disable memory caching (boolean),
+ * 'onlyMemoryCaching' => enable / disable only memory caching (boolean),
+ * 'memoryCachingLimit' => max nbr of records in memory caching (int),
+ * 'fileNameProtection' => enable / disable file name protection (boolean),
+ * 'automaticSerialization' => enable / disable serialization (boolean)
+ * );
+ *
+ * @param array $options options
+ * @access public
+ */
+ function TCache_Lite($options = array(null))
+ {
+ $availableOptions = array( 'automaticSerialization',
+ 'fileNameProtection',
+ 'memoryCaching',
+ 'onlyMemoryCaching',
+ 'memoryCachingLimit',
+ 'cacheDir',
+ 'caching',
+ 'lifeTime',
+ 'fileLocking',
+ 'writeControl',
+ 'readControl',
+ 'readControlType');
+ foreach($options as $key => $value) {
+ if(in_array($key, $availableOptions)) {
+ $property = '_'.$key;
+ $this->$property = $value;
+ }
+ }
+ $this->_refreshTime = time() - $this->_lifeTime;
+ }
+
+ /**
+ * Test if a cache is available and (if yes) return it
+ *
+ * @param string $id cache id
+ * @param string $group name of the cache group
+ * @param boolean $doNotTestCacheValidity if set to true, the cache
+ * validity won't be tested
+ * @return string data of the cache (or false if no cache available)
+ * @access public
+ */
+ function get($id, $group = 'default', $doNotTestCacheValidity = false)
+ {
+ $this->_id = $id;
+ $this->_group = $group;
+ $data = false;
+ if ($this->_caching) {
+ $this->_setFileName($id, $group);
+ if ($this->_memoryCaching) {
+ if (isset($this->_memoryCachingArray[$this->_file])) {
+ if ($this->_automaticSerialization) {
+ return unserialize(
+ $this->_memoryCachingArray[$this->_file]);
+ } else {
+ return $this->_memoryCachingArray[$this->_file];
+ }
+ } else {
+ if ($this->_onlyMemoryCaching) {
+ return false;
+ }
+ }
+ }
+ if ($doNotTestCacheValidity) {
+ if (file_exists($this->_file)) {
+ $data = $this->_read();
+ }
+ } else {
+ if (@filemtime($this->_file) > $this->_refreshTime) {
+ $data = $this->_read();
+ }
+ }
+ if (($data) and ($this->_memoryCaching)) {
+ $this->_memoryCacheAdd($this->_file, $data);
+ }
+ if ($this->_automaticSerialization && is_string($data)) {
+ $data = unserialize($data);
+ }
+ return $data;
+ }
+ return false;
+ }
+
+ /**
+ * Save some data in a cache file
+ *
+ * @param string $data data to put in cache (can be another type than strings
+ * if automaticSerialization is on)
+ * @param string $id cache id
+ * @param string $group name of the cache group
+ * @return boolean true if no problem
+ * @access public
+ */
+ function save($data, $id = null, $group = 'default')
+ {
+ if ($this->_caching) {
+ if ($this->_automaticSerialization) {
+ $data = serialize($data);
+ }
+ if (isset($id)) {
+ $this->_setFileName($id, $group);
+ }
+ if ($this->_memoryCaching) {
+ $this->_memoryCacheAdd($this->_file, $data);
+ if ($this->_onlyMemoryCaching) {
+ return true;
+ }
+ }
+ if ($this->_writeControl) {
+ if (!$this->_writeAndControl($data)) {
+ @touch($this->_file, time() - 2*abs($this->_lifeTime));
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ return $this->_write($data);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Remove a cache file
+ *
+ * @param string $id cache id
+ * @param string $group name of the cache group
+ * @return boolean true if no problem
+ * @access public
+ */
+ function remove($id, $group = 'default')
+ {
+ $this->_setFileName($id, $group);
+ if (!@unlink($this->_file)) {
+ $this->raiseError('TCache_Lite : Unable to remove cache !', -3);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Clean the cache
+ *
+ * if no group is specified all cache files will be destroyed
+ * else only cache files of the specified group will be destroyed
+ *
+ * @param string $group name of the cache group
+ * @return boolean true if no problem
+ * @access public
+ */
+ function clean($group = false)
+ {
+ if ($this->_fileNameProtection) {
+ $motif = ($group) ? 'cache_'.md5($group).'_' : 'cache_';
+ } else {
+ $motif = ($group) ? 'cache_'.$group.'_' : 'cache_';
+ }
+ if ($this->_memoryCaching) {
+ while (list($key, $value) = each($this->_memoryCaching)) {
+ if (strpos($key, $motif, 0)) {
+ unset($this->_memoryCaching[$key]);
+ $this->_memoryCachingCounter =
+ $this->_memoryCachingCounter - 1;
+ }
+ }
+ if ($this->_onlyMemoryCaching) {
+ return true;
+ }
+ }
+ if (!($dh = opendir($this->_cacheDir))) {
+ $this->raiseError('TCache_Lite : Unable to open cache directory !');
+ return false;
+ }
+ while ($file = readdir($dh)) {
+ if (($file != '.') && ($file != '..')) {
+ $file = $this->_cacheDir . $file;
+ if (is_file($file)) {
+ if (strpos($file, $motif, 0)) {
+ if (!@unlink($file)) {
+ $this->raiseError('Cache_Lite : Unable to remove cache !', -3);
+ return false;
+ }
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Set a new life time
+ *
+ * @param int $newLifeTime new life time (in seconds)
+ * @access public
+ */
+ function setLifeTime($newLifeTime)
+ {
+ $this->_lifeTime = $newLifeTime;
+ $this->_refreshTime = time() - $newLifeTime;
+ }
+
+ /**
+ *
+ * @access public
+ */
+ function saveMemoryCachingState($id, $group = 'default')
+ {
+ if ($this->_caching) {
+ $array = array(
+ 'counter' => $this->_memoryCachingCounter,
+ 'array' => $this->_memoryCachingState
+ );
+ $data = serialize($array);
+ $this->save($data, $id, $group);
+ }
+ }
+
+ /**
+ *
+ * @access public
+ */
+ function getMemoryCachingState($id, $group = 'default',
+ $doNotTestCacheValidity = false)
+ {
+ if ($this->_caching) {
+ if ($data = $this->get($id, $group, $doNotTestCacheValidity))
+ {
+ $array = unserialize($data);
+ $this->_memoryCachingCounter = $array['counter'];
+ $this->_memoryCachingArray = $array['array'];
+ }
+ }
+ }
+
+ /**
+ * Return the cache last modification time
+ *
+ * BE CAREFUL : THIS METHOD IS FOR HACKING ONLY !
+ *
+ * @return int last modification time
+ */
+ function lastModified() {
+ return filemtime($this->cache->_file);
+ }
+
+ /**
+ * Trigger a PEAR error
+ *
+ * To improve performances, the PEAR.php file is included dynamically.
+ * The file is so included only when an error is triggered. So, in most
+ * cases, the file isn't included and perfs are much better.
+ *
+ * @param string $msg error message
+ * @param int $code error code
+ * @access public
+ */
+ function raiseError($msg, $code)
+ {
+ throw new Exception($msg);
+ }
+
+ // --- Private methods ---
+
+ /**
+ *
+ * @access private
+ */
+ function _memoryCacheAdd($id, $data)
+ {
+ $this->_memoryCachingArray[$this->_file] = $data;
+ if ($this->_memoryCachingCounter >= $this->_memoryCachingLimit) {
+ list($key, $value) = each($this->_memoryCachingArray);
+ unset($this->_memoryCachingArray[$key]);
+ } else {
+ $this->_memoryCachingCounter = $this->_memoryCachingCounter + 1;
+ }
+ }
+
+ /**
+ * Make a file name (with path)
+ *
+ * @param string $id cache id
+ * @param string $group name of the group
+ * @access private
+ */
+ function _setFileName($id, $group)
+ {
+ if ($this->_fileNameProtection) {
+ $this->_file = ($this->_cacheDir.'cache_'.md5($group).'_'
+ .md5($id));
+ } else {
+ $this->_file = $this->_cacheDir.'cache_'.$group.'_'.$id;
+ }
+ }
+
+ function getCacheFile()
+ {
+ return $this->_file;
+ }
+
+ /**
+ * Read the cache file and return the content
+ *
+ * @return string content of the cache file
+ * @access private
+ */
+ function _read()
+ {
+ $fp = @fopen($this->_file, "rb");
+ if ($this->_fileLocking) @flock($fp, LOCK_SH);
+ if ($fp) {
+ // because the filesize can be cached by PHP itself...
+ clearstatcache();
+ $length = @filesize($this->_file);
+ if(version_compare(PHP_VERSION, '5.3.0', 'lt'))
+ {
+ $mqr = get_magic_quotes_runtime();
+ set_magic_quotes_runtime(0);
+ }
+ if ($this->_readControl) {
+ $hashControl = @fread($fp, 32);
+ $length = $length - 32;
+ }
+ $data = @fread($fp, $length);
+ if(isset($mqr))
+ set_magic_quotes_runtime($mqr);
+ if ($this->_fileLocking) @flock($fp, LOCK_UN);
+ @fclose($fp);
+ if ($this->_readControl) {
+ $hashData = $this->_hash($data, $this->_readControlType);
+ if ($hashData != $hashControl) {
+ @touch($this->_file, time() - 2*abs($this->_lifeTime));
+ return false;
+ }
+ }
+ return $data;
+ }
+ $this->raiseError('Cache_Lite : Unable to read cache !', -2);
+ return false;
+ }
+
+ /**
+ * Write the given data in the cache file
+ *
+ * @param string $data data to put in cache
+ * @return boolean true if ok
+ * @access private
+ */
+ function _write($data)
+ {
+ $fp = @fopen($this->_file, "wb");
+ if ($fp) {
+ if ($this->_fileLocking) @flock($fp, LOCK_EX);
+ if ($this->_readControl) {
+ @fwrite($fp, $this->_hash($data, $this->_readControlType), 32);
+ }
+ $len = strlen($data);
+ @fwrite($fp, $data, $len);
+ if ($this->_fileLocking) @flock($fp, LOCK_UN);
+ @fclose($fp);
+ return true;
+ }
+ $this->raiseError('Cache_Lite : Unable to write cache !', -1);
+ return false;
+ }
+
+ /**
+ * Write the given data in the cache file and control it just after to avoid
+ * corrupted cache entries
+ *
+ * @param string $data data to put in cache
+ * @return boolean true if the test is ok
+ * @access private
+ */
+ function _writeAndControl($data)
+ {
+ $this->_write($data);
+ $dataRead = $this->_read($data);
+ return ($dataRead==$data);
+ }
+
+ /**
+ * Make a control key with the string containing datas
+ *
+ * @param string $data data
+ * @param string $controlType type of control 'md5', 'crc32' or 'strlen'
+ * @return string control key
+ * @access private
+ */
+ function _hash($data, $controlType)
+ {
+ switch ($controlType) {
+ case 'md5':
+ return md5($data);
+ case 'crc32':
+ return sprintf('% 32d', crc32($data));
+ case 'strlen':
+ return sprintf('% 32d', strlen($data));
+ default:
+ $this->raiseError('Unknown controlType ! '.
+ '(available values are only \'md5\', \'crc32\', \'strlen\')', -5);
+ }
+ }
+
+}
+
+?>
diff --git a/framework/I18N/core/util.php b/framework/I18N/core/util.php
index c0092f19..c618b33a 100644
--- a/framework/I18N/core/util.php
+++ b/framework/I18N/core/util.php
@@ -1,188 +1,188 @@
-
- * The latest version of PRADO can be obtained from:
- * {@link http://prado.sourceforge.net/}
- *
- * @author Wei Zhuo
- * @version $Revision: 1.3 $ $Date: 2005/08/27 03:21:12 $
- * @package System.I18N.core
- */
-
-
- /**
- * For a given DSN (database connection string), return some information
- * about the DSN. This function comes from PEAR's DB package.
- *
- * LICENSE: This source file is subject to version 3.0 of the PHP license
- * that is available through the world-wide-web at the following URI:
- * http://www.php.net/license/3_0.txt. If you did not receive a copy of
- * the PHP License and are unable to obtain it through the web, please
- * send a note to license@php.net so we can mail you a copy immediately.
- *
- * @param string DSN format, similar to PEAR's DB
- * @return array DSN information.
- * @author Stig Bakken
- * @author Tomas V.V.Cox
- * @author Daniel Convissor
- * @copyright 1997-2005 The PHP Group
- * @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @link http://pear.php.net/package/DB
- */
- function parseDSN($dsn)
- {
- if (is_array($dsn)) {
- return $dsn;
- }
-
- $parsed = array(
- 'phptype' => false,
- 'dbsyntax' => false,
- 'username' => false,
- 'password' => false,
- 'protocol' => false,
- 'hostspec' => false,
- 'port' => false,
- 'socket' => false,
- 'database' => false
- );
-
- // Find phptype and dbsyntax
- if (($pos = strpos($dsn, '://')) !== false) {
- $str = substr($dsn, 0, $pos);
- $dsn = substr($dsn, $pos + 3);
- } else {
- $str = $dsn;
- $dsn = null;
- }
-
- // Get phptype and dbsyntax
- // $str => phptype(dbsyntax)
- if (preg_match('|^(.+?)\((.*?)\)$|', $str, $arr)) {
- $parsed['phptype'] = $arr[1];
- $parsed['dbsyntax'] = (empty($arr[2])) ? $arr[1] : $arr[2];
- } else {
- $parsed['phptype'] = $str;
- $parsed['dbsyntax'] = $str;
- }
-
- if (empty($dsn)) {
- return $parsed;
- }
-
- // Get (if found): username and password
- // $dsn => username:password@protocol+hostspec/database
- if (($at = strrpos($dsn,'@')) !== false) {
- $str = substr($dsn, 0, $at);
- $dsn = substr($dsn, $at + 1);
- if (($pos = strpos($str, ':')) !== false) {
- $parsed['username'] = rawurldecode(substr($str, 0, $pos));
- $parsed['password'] = rawurldecode(substr($str, $pos + 1));
- } else {
- $parsed['username'] = rawurldecode($str);
- }
- }
-
- // Find protocol and hostspec
-
- // $dsn => proto(proto_opts)/database
- if (preg_match('|^([^(]+)\((.*?)\)/?(.*?)$|', $dsn, $match)) {
- $proto = $match[1];
- $proto_opts = (!empty($match[2])) ? $match[2] : false;
- $dsn = $match[3];
-
- // $dsn => protocol+hostspec/database (old format)
- } else {
- if (strpos($dsn, '+') !== false) {
- list($proto, $dsn) = explode('+', $dsn, 2);
- }
- if (strpos($dsn, '/') !== false) {
- list($proto_opts, $dsn) = explode('/', $dsn, 2);
- } else {
- $proto_opts = $dsn;
- $dsn = null;
- }
- }
-
- // process the different protocol options
- $parsed['protocol'] = (!empty($proto)) ? $proto : 'tcp';
- $proto_opts = rawurldecode($proto_opts);
- if ($parsed['protocol'] == 'tcp') {
- if (strpos($proto_opts, ':') !== false) {
- list($parsed['hostspec'], $parsed['port']) = explode(':', $proto_opts);
- } else {
- $parsed['hostspec'] = $proto_opts;
- }
- } elseif ($parsed['protocol'] == 'unix') {
- $parsed['socket'] = $proto_opts;
- }
-
- // Get dabase if any
- // $dsn => database
- if (!empty($dsn)) {
- // /database
- if (($pos = strpos($dsn, '?')) === false) {
- $parsed['database'] = $dsn;
- // /database?param1=value1¶m2=value2
- } else {
- $parsed['database'] = substr($dsn, 0, $pos);
- $dsn = substr($dsn, $pos + 1);
- if (strpos($dsn, '&') !== false) {
- $opts = explode('&', $dsn);
- } else { // database?param1=value1
- $opts = array($dsn);
- }
- foreach ($opts as $opt) {
- list($key, $value) = explode('=', $opt);
- if (!isset($parsed[$key])) { // don't allow params overwrite
- $parsed[$key] = rawurldecode($value);
- }
- }
- }
- }
-
- return $parsed;
- }
-
-
- /**
- * Convert strings to UTF-8 via iconv. NB, the result may not by UTF-8
- * if the conversion failed.
- * @param string string to convert to UTF-8
- * @return string UTF-8 encoded string, original string if iconv failed.
- */
- function I18N_toUTF8($string, $from)
- {
- if($from != 'UTF-8')
- {
- $s = iconv($from,'UTF-8',$string); //to UTF-8
- return $s !== false ? $s : $string; //it could return false
- }
- return $string;
- }
-
- /**
- * Convert UTF-8 strings to a different encoding. NB. The result
- * may not have been encoded if iconv fails.
- * @param string the UTF-8 string for conversion
- * @return string encoded string.
- */
- function I18N_toEncoding($string, $to)
- {
- if($to != 'UTF-8')
- {
- $s = iconv('UTF-8', $to, $string);
- return $s !== false ? $s : $string;
- }
- return $string;
- }
-
+
+ * The latest version of PRADO can be obtained from:
+ * {@link http://prado.sourceforge.net/}
+ *
+ * @author Wei Zhuo
+ * @version $Revision: 1.3 $ $Date: 2005/08/27 03:21:12 $
+ * @package System.I18N.core
+ */
+
+
+ /**
+ * For a given DSN (database connection string), return some information
+ * about the DSN. This function comes from PEAR's DB package.
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @param string DSN format, similar to PEAR's DB
+ * @return array DSN information.
+ * @author Stig Bakken
+ * @author Tomas V.V.Cox
+ * @author Daniel Convissor
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @link http://pear.php.net/package/DB
+ */
+ function parseDSN($dsn)
+ {
+ if (is_array($dsn)) {
+ return $dsn;
+ }
+
+ $parsed = array(
+ 'phptype' => false,
+ 'dbsyntax' => false,
+ 'username' => false,
+ 'password' => false,
+ 'protocol' => false,
+ 'hostspec' => false,
+ 'port' => false,
+ 'socket' => false,
+ 'database' => false
+ );
+
+ // Find phptype and dbsyntax
+ if (($pos = strpos($dsn, '://')) !== false) {
+ $str = substr($dsn, 0, $pos);
+ $dsn = substr($dsn, $pos + 3);
+ } else {
+ $str = $dsn;
+ $dsn = null;
+ }
+
+ // Get phptype and dbsyntax
+ // $str => phptype(dbsyntax)
+ if (preg_match('|^(.+?)\((.*?)\)$|', $str, $arr)) {
+ $parsed['phptype'] = $arr[1];
+ $parsed['dbsyntax'] = (empty($arr[2])) ? $arr[1] : $arr[2];
+ } else {
+ $parsed['phptype'] = $str;
+ $parsed['dbsyntax'] = $str;
+ }
+
+ if (empty($dsn)) {
+ return $parsed;
+ }
+
+ // Get (if found): username and password
+ // $dsn => username:password@protocol+hostspec/database
+ if (($at = strrpos($dsn,'@')) !== false) {
+ $str = substr($dsn, 0, $at);
+ $dsn = substr($dsn, $at + 1);
+ if (($pos = strpos($str, ':')) !== false) {
+ $parsed['username'] = rawurldecode(substr($str, 0, $pos));
+ $parsed['password'] = rawurldecode(substr($str, $pos + 1));
+ } else {
+ $parsed['username'] = rawurldecode($str);
+ }
+ }
+
+ // Find protocol and hostspec
+
+ // $dsn => proto(proto_opts)/database
+ if (preg_match('|^([^(]+)\((.*?)\)/?(.*?)$|', $dsn, $match)) {
+ $proto = $match[1];
+ $proto_opts = (!empty($match[2])) ? $match[2] : false;
+ $dsn = $match[3];
+
+ // $dsn => protocol+hostspec/database (old format)
+ } else {
+ if (strpos($dsn, '+') !== false) {
+ list($proto, $dsn) = explode('+', $dsn, 2);
+ }
+ if (strpos($dsn, '/') !== false) {
+ list($proto_opts, $dsn) = explode('/', $dsn, 2);
+ } else {
+ $proto_opts = $dsn;
+ $dsn = null;
+ }
+ }
+
+ // process the different protocol options
+ $parsed['protocol'] = (!empty($proto)) ? $proto : 'tcp';
+ $proto_opts = rawurldecode($proto_opts);
+ if ($parsed['protocol'] == 'tcp') {
+ if (strpos($proto_opts, ':') !== false) {
+ list($parsed['hostspec'], $parsed['port']) = explode(':', $proto_opts);
+ } else {
+ $parsed['hostspec'] = $proto_opts;
+ }
+ } elseif ($parsed['protocol'] == 'unix') {
+ $parsed['socket'] = $proto_opts;
+ }
+
+ // Get dabase if any
+ // $dsn => database
+ if (!empty($dsn)) {
+ // /database
+ if (($pos = strpos($dsn, '?')) === false) {
+ $parsed['database'] = $dsn;
+ // /database?param1=value1¶m2=value2
+ } else {
+ $parsed['database'] = substr($dsn, 0, $pos);
+ $dsn = substr($dsn, $pos + 1);
+ if (strpos($dsn, '&') !== false) {
+ $opts = explode('&', $dsn);
+ } else { // database?param1=value1
+ $opts = array($dsn);
+ }
+ foreach ($opts as $opt) {
+ list($key, $value) = explode('=', $opt);
+ if (!isset($parsed[$key])) { // don't allow params overwrite
+ $parsed[$key] = rawurldecode($value);
+ }
+ }
+ }
+ }
+
+ return $parsed;
+ }
+
+
+ /**
+ * Convert strings to UTF-8 via iconv. NB, the result may not by UTF-8
+ * if the conversion failed.
+ * @param string string to convert to UTF-8
+ * @return string UTF-8 encoded string, original string if iconv failed.
+ */
+ function I18N_toUTF8($string, $from)
+ {
+ if($from != 'UTF-8')
+ {
+ $s = iconv($from,'UTF-8',$string); //to UTF-8
+ return $s !== false ? $s : $string; //it could return false
+ }
+ return $string;
+ }
+
+ /**
+ * Convert UTF-8 strings to a different encoding. NB. The result
+ * may not have been encoded if iconv fails.
+ * @param string the UTF-8 string for conversion
+ * @return string encoded string.
+ */
+ function I18N_toEncoding($string, $to)
+ {
+ if($to != 'UTF-8')
+ {
+ $s = iconv('UTF-8', $to, $string);
+ return $s !== false ? $s : $string;
+ }
+ return $string;
+ }
+
?>
\ No newline at end of file
--
cgit v1.2.3