diff options
author | ctrlaltca <> | 2012-07-12 11:21:01 +0000 |
---|---|---|
committer | ctrlaltca <> | 2012-07-12 11:21:01 +0000 |
commit | 903ae8a581fac1e6917fc3e31d2ad8fb91df80c3 (patch) | |
tree | e08bf04f0823650a231227ac3499121270172a23 /framework/I18N | |
parent | 3e4e6e66aeb3f8fea4e1eb4237498ef9d2358f63 (diff) |
standardize the use of unix eol; use svn properties to enforce native eol
Diffstat (limited to 'framework/I18N')
29 files changed, 9117 insertions, 9117 deletions
diff --git a/framework/I18N/TChoiceFormat.php b/framework/I18N/TChoiceFormat.php index a1d7ad74..401b25d9 100644 --- a/framework/I18N/TChoiceFormat.php +++ b/framework/I18N/TChoiceFormat.php @@ -1,111 +1,111 @@ -<?php
-/**
- * TChoiceFormat, I18N choice format component.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TChoiceFormat, I18N choice format component. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.I18N
- */
-
- /**
- * Get the ChoiceFormat class.
- */
-Prado::using('System.I18N.core.ChoiceFormat');
-Prado::using('System.I18N.TTranslate');
-
-/**
- * TChoiceFormat class.
- *
- * This component performs message/string choice translation. The translation
- * source is set in the TGlobalization module. The following example
- * demonstrates a simple 2 choice message translation.
- * <code>
- * <com:TChoiceFormat Value="1">[1] One Apple. |[2] Two Apples</com:TChoiceFormat>
- * </code>
- *
- * The Choice has <b>Value</b> "1" (one), thus the translated string
- * is "One Apple". If the <b>Value</b> is "2", then it will show
- * "Two Apples".
- *
- * The message/string choices are separated by the pipe "|" followed
- * by a set notation of the form
- * # <tt>[1,2]</tt> -- accepts values between 1 and 2, inclusive.
- * # <tt>(1,2)</tt> -- accepts values between 1 and 2, excluding 1 and 2.
- * # <tt>{1,2,3,4}</tt> -- only values defined in the set are accepted.
- * # <tt>[-Inf,0)</tt> -- 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.
- *
- * The string choosen for display depends on the <b>Value</b> property.
- * The <b>Value</b> is evaluated for each set until the Value is found
- * to belong to a particular set.
- *
- * Properties
- * - <b>Value</b>, float,
- * <br>Gets or sets the Value that determines which string choice to display.
- * Since version 3.1.2 the following set notation is also possible.
- *
- * # <tt>{n: n % 10 > 1 && n % 10 < 5}</tt> -- matches numbers like 2, 3, 4, 22, 23, 24
- *
- * Where set is defined by the expression after <tt>n:</tt>. In particular, the expression
- * accepts the following mathematical/logical operators to form a set of logical conditions
- * on the value given by <tt>n</tt>:
- * # <tt><</tt> -- less than.
- * # <tt><=</tt> -- less than equals.
- * # <tt>></tt> -- greater than.
- * # <tt>>=</tt> -- greater than equals.
- * # <tt>==</tt> -- of equal value.
- * # <tt>%</tt> -- modulo, e.g., 1 % 10 equals 1, 11 % 10 equals 1.
- * # <tt>-</tt> -- minus, negative.
- * # <tt>+</tt> -- addition.
- * # <tt>&</tt> -- conditional AND.
- * # <tt>&&</tt> -- condition AND with short circuit.
- * # <tt>|</tt> -- conditional OR.
- * # <tt>||</tt> -- conditional OR with short circuit.
- * # <tt>!</tt> -- negation.
- *
- * Additional round brackets can also be used to perform grouping.
- *
- * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @version v1.0, last update on Fri Dec 24 21:38:49 EST 2004
- * @package System.I18N
- */
-class TChoiceFormat extends TTranslate
-{
- /**
- * @return float the numerical value.
- */
- public function getValue()
- {
- return $this->getViewState('Value','');
- }
-
- /**
- * Sets the numerical choice value
- * @param float the choice value
- */
- public function setValue($value)
- {
- $this->setViewState('Value',$value,'');
- }
-
- /**
- * Display the choosen translated string.
- * Overrides the parent method, also calls parent's renderBody to
- * translate.
- */
- protected function translateText($text, $subs)
- {
- $text = parent::translateText($text, $subs);
- $choice = new ChoiceFormat();
- $value = $this->getValue();
- $string = $choice->format($text, $value);
- if($string)
- return strtr($string, array('{Value}'=> $value));
- }
-}
-?>
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.I18N + */ + + /** + * Get the ChoiceFormat class. + */ +Prado::using('System.I18N.core.ChoiceFormat'); +Prado::using('System.I18N.TTranslate'); + +/** + * TChoiceFormat class. + * + * This component performs message/string choice translation. The translation + * source is set in the TGlobalization module. The following example + * demonstrates a simple 2 choice message translation. + * <code> + * <com:TChoiceFormat Value="1">[1] One Apple. |[2] Two Apples</com:TChoiceFormat> + * </code> + * + * The Choice has <b>Value</b> "1" (one), thus the translated string + * is "One Apple". If the <b>Value</b> is "2", then it will show + * "Two Apples". + * + * The message/string choices are separated by the pipe "|" followed + * by a set notation of the form + * # <tt>[1,2]</tt> -- accepts values between 1 and 2, inclusive. + * # <tt>(1,2)</tt> -- accepts values between 1 and 2, excluding 1 and 2. + * # <tt>{1,2,3,4}</tt> -- only values defined in the set are accepted. + * # <tt>[-Inf,0)</tt> -- 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. + * + * The string choosen for display depends on the <b>Value</b> property. + * The <b>Value</b> is evaluated for each set until the Value is found + * to belong to a particular set. + * + * Properties + * - <b>Value</b>, float, + * <br>Gets or sets the Value that determines which string choice to display. + * Since version 3.1.2 the following set notation is also possible. + * + * # <tt>{n: n % 10 > 1 && n % 10 < 5}</tt> -- matches numbers like 2, 3, 4, 22, 23, 24 + * + * Where set is defined by the expression after <tt>n:</tt>. In particular, the expression + * accepts the following mathematical/logical operators to form a set of logical conditions + * on the value given by <tt>n</tt>: + * # <tt><</tt> -- less than. + * # <tt><=</tt> -- less than equals. + * # <tt>></tt> -- greater than. + * # <tt>>=</tt> -- greater than equals. + * # <tt>==</tt> -- of equal value. + * # <tt>%</tt> -- modulo, e.g., 1 % 10 equals 1, 11 % 10 equals 1. + * # <tt>-</tt> -- minus, negative. + * # <tt>+</tt> -- addition. + * # <tt>&</tt> -- conditional AND. + * # <tt>&&</tt> -- condition AND with short circuit. + * # <tt>|</tt> -- conditional OR. + * # <tt>||</tt> -- conditional OR with short circuit. + * # <tt>!</tt> -- negation. + * + * Additional round brackets can also be used to perform grouping. + * + * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com> + * @version v1.0, last update on Fri Dec 24 21:38:49 EST 2004 + * @package System.I18N + */ +class TChoiceFormat extends TTranslate +{ + /** + * @return float the numerical value. + */ + public function getValue() + { + return $this->getViewState('Value',''); + } + + /** + * Sets the numerical choice value + * @param float the choice value + */ + public function setValue($value) + { + $this->setViewState('Value',$value,''); + } + + /** + * Display the choosen translated string. + * Overrides the parent method, also calls parent's renderBody to + * translate. + */ + protected function translateText($text, $subs) + { + $text = parent::translateText($text, $subs); + $choice = new ChoiceFormat(); + $value = $this->getValue(); + $string = $choice->format($text, $value); + if($string) + return strtr($string, array('{Value}'=> $value)); + } +} +?> diff --git a/framework/I18N/TDateFormat.php b/framework/I18N/TDateFormat.php index 831fe71d..7604dd44 100644 --- a/framework/I18N/TDateFormat.php +++ b/framework/I18N/TDateFormat.php @@ -1,254 +1,254 @@ -<?php
-/**
- * TDateFromat formatting component.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.I18N
- */
-
-/**
- * Get the DateFormat class.
- */
-Prado::using('System.I18N.core.DateFormat');
-
-/**
- * Get the parent control class.
- */
-Prado::using('System.I18N.TI18NControl');
-
-/**
- * To format dates and/or time according to the current locale use
- * <code>
- * <com:TDateFormat Pattern="dd:MMM:yyyy" Value="01/01/2001" />
- *</code>
- * The date will be formatted according to the current locale (or culture)
- * using the format specified by 'Pattern' attribute.
- *
- * To format date and/or time for a locale (e.g. de_DE) include a Culture
- * attribute, for example:
- * <code>
- * <com:TDateFormat Culture="de_DE" Value="01/01/2001 12:00" />
- * </code>
- * The date will be formatted according to this format.
- *
- * If no Pattern was specified then the date will be formatted with the
- * default format (both date and time). If no value for the date is specified
- * then the current date will be used. E.g.: <code><com:TDateFormat /></code>
- * will result in the current date, formatted with default localized pattern.
- *
- * Namespace: System.I18N
- *
- * Properties
- * - <b>Value</b>, date,
- * <br>Gets or sets the date to format. The tag content is used as Value
- * if the Value property is not specified.
- * - <b>Pattern</b>, string,
- * <br>Gets or sets the formatting pattern. The predefined patterns are
- * 'fulldate', 'longdate', 'mediumdate', 'shortdate', 'fulltime',
- * 'longtime', 'mediumtime', and 'shorttime'. Custom patterns can specified
- * when the Pattern property does not match the predefined patterns.
- * - <b>DefaultText</b>, string,
- * <br>Gets or sets the default text. If Value is not set, DefaultText will be
- * shown instead of todays date and time.
- *
- * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @version v1.0, last update on Sat Dec 11 15:25:11 EST 2004
- * @package System.I18N
- */
-class TDateFormat extends TI18NControl implements IDataRenderer
-{
- /**
- * Default DateFormat, set to the application culture.
- * @var DateFormat
- */
- protected static $formatter;
-
- /**
- * A set of pattern presets and their respective formatting shorthand.
- * @var array
- */
- static private $_patternPresets = array(
- 'fulldate'=>'P','full'=>'P',
- 'longdate'=>'D','long'=>'d',
- 'mediumdate'=>'p','medium'=>'p',
- 'shortdate'=>'d','short'=>'d',
- 'fulltime'=>'Q', 'longtime'=>'T',
- 'mediumtime'=>'q', 'shorttime'=>'t');
-
- /**
- * Sets the date time formatting pattern.
- * @param string format pattern.
- */
- public function setPattern($value)
- {
- $this->setViewState('Pattern',$value,'');
- }
-
- /**
- * Gets the date time format pattern.
- * @return string format pattern.
- */
- public function getPattern()
- {
- $string = $this->getViewState('Pattern','');
-
- $pattern = null;
-
- //try the subpattern of "date time" presets
- $subpatterns = explode(' ',$string,2);
- $datetime = array();
- if(count($subpatterns)==2)
- {
- $datetime[] = $this->getPreset($subpatterns[0]);
- $datetime[] = $this->getPreset($subpatterns[1]);
- }
-
- //we have a good subpattern
- if(count($datetime) == 2
- && strlen($datetime[0]) == 1
- && strlen($datetime[1]) == 1)
- {
- $pattern = $datetime;
- }
- else //no subpattern, try the presets
- $pattern = $this->getPreset($string);
-
- //no presets found, use the string as the pattern
- //and let the DateFormat handle it.
- if($pattern===null)
- $pattern = $string;
- if (!is_array($pattern) && strlen($pattern) == 0)
- $pattern = null;
- return $pattern;
- }
-
- /**
- * For a given string, try and find a preset pattern.
- * @param string the preset pattern name
- * @return string a preset pattern if found, null otherwise.
- */
- protected function getPreset($string)
- {
- $string = strtolower($string);
- foreach(self::$_patternPresets as $pattern => $preset)
- {
- if($string == $pattern)
- return $preset;
- }
- }
-
- /**
- * Get the date-time value for this control.
- * @return string date time value.
- */
- public function getValue()
- {
- $value = $this->getViewState('Value','');
- if(empty($value))
- {
- $defaultText = $this->getDefaultText();
- if(empty($defaultText))
- return time();
- }
- return $value;
- }
-
- /**
- * Set the date-time value for this control.
- * @param string the date-time 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 date-time value for this control.
- * This method is required by {@link IDataRenderer}.
- * It is the same as {@link getValue()}.
- * @return string date time value.
- * @see getValue
- * @since 3.1.2
- */
- public function getData()
- {
- return $this->getValue();
- }
-
- /**
- * Set the date-time value for this control.
- * This method is required by {@link IDataRenderer}.
- * It is the same as {@link setValue()}.
- * @param string the date-time value.
- * @see setValue
- * @since 3.1.2
- */
- public function setData($value)
- {
- $this->setValue($value);
- }
-
- /**
- * Renders the localized version of the date-time value.
- * If the culture is not specified, the default application
- * culture will be used.
- * This method overrides parent's implementation.
- */
- protected function getFormattedDate()
- {
- $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 DateFormat($app->getCulture());
-
- $culture = $this->getCulture();
-
- //return the specific cultural formatted date time
- if(strlen($culture) && $app->getCulture() !== $culture)
- {
- $formatter = new DateFormat($culture);
- return $formatter->format($value,
- $this->getPattern(),
- $this->getCharset());
- }
- //return the application wide culture formatted date time.
- $result = self::$formatter->format($value,
- $this->getPattern(),
- $this->getCharset());
- return $result;
- }
-
- public function render($writer)
- {
- $writer->write($this->getFormattedDate());
- }
-
+<?php +/** + * TDateFromat formatting component. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.I18N + */ + +/** + * Get the DateFormat class. + */ +Prado::using('System.I18N.core.DateFormat'); + +/** + * Get the parent control class. + */ +Prado::using('System.I18N.TI18NControl'); + +/** + * To format dates and/or time according to the current locale use + * <code> + * <com:TDateFormat Pattern="dd:MMM:yyyy" Value="01/01/2001" /> + *</code> + * The date will be formatted according to the current locale (or culture) + * using the format specified by 'Pattern' attribute. + * + * To format date and/or time for a locale (e.g. de_DE) include a Culture + * attribute, for example: + * <code> + * <com:TDateFormat Culture="de_DE" Value="01/01/2001 12:00" /> + * </code> + * The date will be formatted according to this format. + * + * If no Pattern was specified then the date will be formatted with the + * default format (both date and time). If no value for the date is specified + * then the current date will be used. E.g.: <code><com:TDateFormat /></code> + * will result in the current date, formatted with default localized pattern. + * + * Namespace: System.I18N + * + * Properties + * - <b>Value</b>, date, + * <br>Gets or sets the date to format. The tag content is used as Value + * if the Value property is not specified. + * - <b>Pattern</b>, string, + * <br>Gets or sets the formatting pattern. The predefined patterns are + * 'fulldate', 'longdate', 'mediumdate', 'shortdate', 'fulltime', + * 'longtime', 'mediumtime', and 'shorttime'. Custom patterns can specified + * when the Pattern property does not match the predefined patterns. + * - <b>DefaultText</b>, string, + * <br>Gets or sets the default text. If Value is not set, DefaultText will be + * shown instead of todays date and time. + * + * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com> + * @version v1.0, last update on Sat Dec 11 15:25:11 EST 2004 + * @package System.I18N + */ +class TDateFormat extends TI18NControl implements IDataRenderer +{ + /** + * Default DateFormat, set to the application culture. + * @var DateFormat + */ + protected static $formatter; + + /** + * A set of pattern presets and their respective formatting shorthand. + * @var array + */ + static private $_patternPresets = array( + 'fulldate'=>'P','full'=>'P', + 'longdate'=>'D','long'=>'d', + 'mediumdate'=>'p','medium'=>'p', + 'shortdate'=>'d','short'=>'d', + 'fulltime'=>'Q', 'longtime'=>'T', + 'mediumtime'=>'q', 'shorttime'=>'t'); + + /** + * Sets the date time formatting pattern. + * @param string format pattern. + */ + public function setPattern($value) + { + $this->setViewState('Pattern',$value,''); + } + + /** + * Gets the date time format pattern. + * @return string format pattern. + */ + public function getPattern() + { + $string = $this->getViewState('Pattern',''); + + $pattern = null; + + //try the subpattern of "date time" presets + $subpatterns = explode(' ',$string,2); + $datetime = array(); + if(count($subpatterns)==2) + { + $datetime[] = $this->getPreset($subpatterns[0]); + $datetime[] = $this->getPreset($subpatterns[1]); + } + + //we have a good subpattern + if(count($datetime) == 2 + && strlen($datetime[0]) == 1 + && strlen($datetime[1]) == 1) + { + $pattern = $datetime; + } + else //no subpattern, try the presets + $pattern = $this->getPreset($string); + + //no presets found, use the string as the pattern + //and let the DateFormat handle it. + if($pattern===null) + $pattern = $string; + if (!is_array($pattern) && strlen($pattern) == 0) + $pattern = null; + return $pattern; + } + + /** + * For a given string, try and find a preset pattern. + * @param string the preset pattern name + * @return string a preset pattern if found, null otherwise. + */ + protected function getPreset($string) + { + $string = strtolower($string); + foreach(self::$_patternPresets as $pattern => $preset) + { + if($string == $pattern) + return $preset; + } + } + + /** + * Get the date-time value for this control. + * @return string date time value. + */ + public function getValue() + { + $value = $this->getViewState('Value',''); + if(empty($value)) + { + $defaultText = $this->getDefaultText(); + if(empty($defaultText)) + return time(); + } + return $value; + } + + /** + * Set the date-time value for this control. + * @param string the date-time 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 date-time value for this control. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link getValue()}. + * @return string date time value. + * @see getValue + * @since 3.1.2 + */ + public function getData() + { + return $this->getValue(); + } + + /** + * Set the date-time value for this control. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link setValue()}. + * @param string the date-time value. + * @see setValue + * @since 3.1.2 + */ + public function setData($value) + { + $this->setValue($value); + } + + /** + * Renders the localized version of the date-time value. + * If the culture is not specified, the default application + * culture will be used. + * This method overrides parent's implementation. + */ + protected function getFormattedDate() + { + $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 DateFormat($app->getCulture()); + + $culture = $this->getCulture(); + + //return the specific cultural formatted date time + if(strlen($culture) && $app->getCulture() !== $culture) + { + $formatter = new DateFormat($culture); + return $formatter->format($value, + $this->getPattern(), + $this->getCharset()); + } + //return the application wide culture formatted date time. + $result = self::$formatter->format($value, + $this->getPattern(), + $this->getCharset()); + return $result; + } + + public function render($writer) + { + $writer->write($this->getFormattedDate()); + } + }
\ No newline at end of file diff --git a/framework/I18N/TGlobalization.php b/framework/I18N/TGlobalization.php index 54f5d66f..d5496353 100644 --- a/framework/I18N/TGlobalization.php +++ b/framework/I18N/TGlobalization.php @@ -1,299 +1,299 @@ -<?php
-/**
- * TGlobalization class file.
- *
- * @author Wei Zhuo<weizhuo[at]gmail[dot]com>
- * @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<weizhuo[at]gmail[dot]com>
- * @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. <tt>en_US</tt> 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. <tt>en_US</tt> 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. <tt>UTF-8</tt>
- */
- public function setDefaultCharset($charset)
- {
- $this->_defaultCharset = $charset;
- }
-
- /**
- * @return string current application culture
- */
- public function getCulture()
- {
- return $this->_culture;
- }
-
- /**
- * @param string culture, e.g. <tt>en_US</tt> 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. <tt>UTF-8</tt>
- */
- 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:
- * <code>
- * $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 '@@'
- * </code>
- * 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
- * <code>
- * $files = $app->getLocalizedResource("path/to/Home.page","en_US");
- * </code>
- * will return
- * <pre>
- * 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'
- * </pre>
- * 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;
- }
-
-}
-
-?>
+<?php +/** + * TGlobalization class file. + * + * @author Wei Zhuo<weizhuo[at]gmail[dot]com> + * @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<weizhuo[at]gmail[dot]com> + * @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. <tt>en_US</tt> 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. <tt>en_US</tt> 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. <tt>UTF-8</tt> + */ + public function setDefaultCharset($charset) + { + $this->_defaultCharset = $charset; + } + + /** + * @return string current application culture + */ + public function getCulture() + { + return $this->_culture; + } + + /** + * @param string culture, e.g. <tt>en_US</tt> 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. <tt>UTF-8</tt> + */ + 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: + * <code> + * $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 '@@' + * </code> + * 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 + * <code> + * $files = $app->getLocalizedResource("path/to/Home.page","en_US"); + * </code> + * will return + * <pre> + * 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' + * </pre> + * 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 @@ -<?php
-/**
- * TMultiView and TView class file.
- *
- * @author Wei Zhuo<weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TMultiView and TView class file. + * + * @author Wei Zhuo<weizhuo[at]gmail[dot]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<weizhuo[at]gmail[dot]com>
- * @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<weizhuo[at]gmail[dot]com> + * @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 @@ -<?php
-/**
- * Base I18N component.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @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
- * - <b>Culture</b>, string,
- * <br>Gets or sets the culture for formatting. If the Culture property
- * is not specified. The culture from the Application/Page is used.
- * - <b>Charset</b>, string,
- * <br>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 <weizhuo[at]gmail[dot]com>
- * @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,'');
- }
-}
-
+<?php +/** + * Base I18N component. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @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 + * - <b>Culture</b>, string, + * <br>Gets or sets the culture for formatting. If the Culture property + * is not specified. The culture from the Application/Page is used. + * - <b>Charset</b>, string, + * <br>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 <weizhuo[at]gmail[dot]com> + * @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 @@ -<?php
-/**
- * TNumberFromat component.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @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
- * <code>
- * <com:TNumberFormat Pattern="0.##" value="2.0" />
- * </code>
- *
- * 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.
- * <code>
- * <com:TNumberFormat Type="currency" Culture="en_US" Currency="EUR" Value="100" />
- * </code>
- *
- * Namespace: System.I18N
- *
- * Properties
- * - <b>Value</b>, number,
- * <br>Gets or sets the number to format. The tag content is used as Value
- * if the Value property is not specified.
- * - <b>Type</b>, string,
- * <br>Gets or sets the formatting type. The valid types are
- * 'decimal', 'currency', 'percentage' and 'scientific'.
- * - <b>Currency</b>, string,
- * <br>Gets or sets the currency symbol for the currency format.
- * The default is 'USD' if the Currency property is not specified.
- * - <b>Pattern</b>, string,
- * <br>Gets or sets the custom number formatting pattern.
- * - <b>DefaultText</b>, string,
- * <br>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 <weizhuo[at]gmail[dot]com>
- * @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());
- }
-}
-
-?>
+<?php +/** + * TNumberFromat component. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @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 + * <code> + * <com:TNumberFormat Pattern="0.##" value="2.0" /> + * </code> + * + * 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. + * <code> + * <com:TNumberFormat Type="currency" Culture="en_US" Currency="EUR" Value="100" /> + * </code> + * + * Namespace: System.I18N + * + * Properties + * - <b>Value</b>, number, + * <br>Gets or sets the number to format. The tag content is used as Value + * if the Value property is not specified. + * - <b>Type</b>, string, + * <br>Gets or sets the formatting type. The valid types are + * 'decimal', 'currency', 'percentage' and 'scientific'. + * - <b>Currency</b>, string, + * <br>Gets or sets the currency symbol for the currency format. + * The default is 'USD' if the Currency property is not specified. + * - <b>Pattern</b>, string, + * <br>Gets or sets the custom number formatting pattern. + * - <b>DefaultText</b>, string, + * <br>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 <weizhuo[at]gmail[dot]com> + * @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 @@ -<?php
-/**
- * TTranslate, I18N translation component.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @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.
- * <code>
- * <com:TTranslate Text="Goodbye" />
- * </code>
- *
- * 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.
- * <code>
- * <com:TTranslate Parameters.time=<%= time() %> >
- * The unix-time is "{time}".
- * </com:TTranslate>
- * </code>
- *
- * More complex string substitution can be applied using the
- * TTranslateParameter component.
- *
- * Namespace: System.I18N
- *
- * Properties
- * - <b>Text</b>, string,
- * <br>Gets or sets the string to translate.
- * - <b>Catalogue</b>, string,
- * <br>Gets or sets the catalogue for message translation. The
- * default catalogue can be set by the @Page directive.
- * - <b>Key</b>, string,
- * <br>Gets or sets the key used to message look up.
- * - <b>Trim</b>, boolean,
- * <br>Gets or sets an option to trim the contents.
- * Default is to trim the contents.
- *
- * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @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());
- }
-}
-
+<?php +/** + * TTranslate, I18N translation component. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @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. + * <code> + * <com:TTranslate Text="Goodbye" /> + * </code> + * + * 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. + * <code> + * <com:TTranslate Parameters.time=<%= time() %> > + * The unix-time is "{time}". + * </com:TTranslate> + * </code> + * + * More complex string substitution can be applied using the + * TTranslateParameter component. + * + * Namespace: System.I18N + * + * Properties + * - <b>Text</b>, string, + * <br>Gets or sets the string to translate. + * - <b>Catalogue</b>, string, + * <br>Gets or sets the catalogue for message translation. The + * default catalogue can be set by the @Page directive. + * - <b>Key</b>, string, + * <br>Gets or sets the key used to message look up. + * - <b>Trim</b>, boolean, + * <br>Gets or sets an option to trim the contents. + * Default is to trim the contents. + * + * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com> + * @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 @@ -<?php
-/**
- * TTranslateParameter component.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TTranslateParameter component. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]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.
- * <code>
- * <com:TTranslate>
- * {greeting} {name}!
- * <com:TTranslateParameter Key="name">World</com:TTranslateParameter>
- * <com:TTranslateParameter Key="greeting">Hello</com:TTranslateParameter>
- * </com:TTranslate>
- * </code>
- *
- * Namespace: System.I18N
- *
- * Properties
- * - <b>Key</b>, string, <b>required</b>.
- * <br>Gets or sets the string in TTranslate to substitute.
- * - <b>Trim</b>, boolean,
- * <br>Gets or sets an option to trim the contents of the TParam.
- * Default is to trim the contents.
- *
- * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @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. + * <code> + * <com:TTranslate> + * {greeting} {name}! + * <com:TTranslateParameter Key="name">World</com:TTranslateParameter> + * <com:TTranslateParameter Key="greeting">Hello</com:TTranslateParameter> + * </com:TTranslate> + * </code> + * + * Namespace: System.I18N + * + * Properties + * - <b>Key</b>, string, <b>required</b>. + * <br>Gets or sets the string in TTranslate to substitute. + * - <b>Trim</b>, boolean, + * <br>Gets or sets an option to trim the contents of the TParam. + * Default is to trim the contents. + * + * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com> + * @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 @@ -<?php
-
-/**
- * ChoiceFormat class file.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the BSD License.
- *
- * Copyright(c) 2004 by Qiang Xue. All rights reserved.
- *
- * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue}
- * The latest version of PRADO can be obtained from:
- * {@link http://prado.sourceforge.net/}
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @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."
- *
- * <code>
- * $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"
- * </code>
- *
- * The message/string choices are separated by the pipe "|" followed
- * by a set notation of the form
- * # <tt>[1,2]</tt> -- accepts values between 1 and 2, inclusive.
- * # <tt>(1,2)</tt> -- accepts values between 1 and 2, excluding 1 and 2.
- * # <tt>{1,2,3,4}</tt> -- only values defined in the set are accepted.
- * # <tt>[-Inf,0)</tt> -- 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.
- *
- * # <tt>{n: n % 10 > 1 && n % 10 < 5}</tt> -- matches numbers like 2, 3, 4, 22, 23, 24
- *
- * Where set is defined by the expression after <tt>n:</tt>. In particular, the expression
- * accepts the following mathematical/logical operators to form a set of logical conditions
- * on the value given by <tt>n</tt>:
- * # <tt><</tt> -- less than.
- * # <tt><=</tt> -- less than equals.
- * # <tt>></tt> -- greater than.
- * # <tt>>=</tt> -- greater than equals.
- * # <tt>==</tt> -- of equal value.
- * # <tt>%</tt> -- modulo, e.g., 1 % 10 equals 1, 11 % 10 equals 1.
- * # <tt>-</tt> -- minus, negative.
- * # <tt>+</tt> -- addition.
- * # <tt>&</tt> -- conditional AND.
- * # <tt>&&</tt> -- condition AND with short circuit.
- * # <tt>|</tt> -- conditional OR.
- * # <tt>||</tt> -- conditional OR with short circuit.
- * # <tt>!</tt> -- negation.
- *
- * Additional round brackets can also be used to perform grouping.
- *
- * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @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;
- }
-}
-
-?>
+<?php + +/** + * ChoiceFormat class file. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the BSD License. + * + * Copyright(c) 2004 by Qiang Xue. All rights reserved. + * + * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue} + * The latest version of PRADO can be obtained from: + * {@link http://prado.sourceforge.net/} + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @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." + * + * <code> + * $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" + * </code> + * + * The message/string choices are separated by the pipe "|" followed + * by a set notation of the form + * # <tt>[1,2]</tt> -- accepts values between 1 and 2, inclusive. + * # <tt>(1,2)</tt> -- accepts values between 1 and 2, excluding 1 and 2. + * # <tt>{1,2,3,4}</tt> -- only values defined in the set are accepted. + * # <tt>[-Inf,0)</tt> -- 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. + * + * # <tt>{n: n % 10 > 1 && n % 10 < 5}</tt> -- matches numbers like 2, 3, 4, 22, 23, 24 + * + * Where set is defined by the expression after <tt>n:</tt>. In particular, the expression + * accepts the following mathematical/logical operators to form a set of logical conditions + * on the value given by <tt>n</tt>: + * # <tt><</tt> -- less than. + * # <tt><=</tt> -- less than equals. + * # <tt>></tt> -- greater than. + * # <tt>>=</tt> -- greater than equals. + * # <tt>==</tt> -- of equal value. + * # <tt>%</tt> -- modulo, e.g., 1 % 10 equals 1, 11 % 10 equals 1. + * # <tt>-</tt> -- minus, negative. + * # <tt>+</tt> -- addition. + * # <tt>&</tt> -- conditional AND. + * # <tt>&&</tt> -- condition AND with short circuit. + * # <tt>|</tt> -- conditional OR. + * # <tt>||</tt> -- conditional OR with short circuit. + * # <tt>!</tt> -- negation. + * + * Additional round brackets can also be used to perform grouping. + * + * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com> + * @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 @@ -<?php
-
-/**
- * CultureInfo class file.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the BSD License.
- *
- * Copyright(c) 2004 by Qiang Xue. All rights reserved.
- *
- * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue}
- * The latest version of PRADO can be obtained from:
- * {@link http://prado.sourceforge.net/}
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @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 "<languagecode>_<country/regioncode>",
- * where <languagecode> 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 <country/regioncode2> 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 <weizhuo[at]gmail[dot]com>
- * @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. <code>new CultureInfo('en_AU');</cdoe>
- * 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 "<language>_(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
- * "<languagecode2>_(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 <code>array('Language','Country');</code>
- * '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 <code>array('Language','Country');</code>
- * '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. <code>array(0 => array('hello'), 1 => 'world');</code>
- * becomes <code>array(0 => 'hello', 1 => 'world');</code>
- * @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));
- }
-}
-
+<?php + +/** + * CultureInfo class file. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the BSD License. + * + * Copyright(c) 2004 by Qiang Xue. All rights reserved. + * + * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue} + * The latest version of PRADO can be obtained from: + * {@link http://prado.sourceforge.net/} + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @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 "<languagecode>_<country/regioncode>", + * where <languagecode> 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 <country/regioncode2> 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 <weizhuo[at]gmail[dot]com> + * @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. <code>new CultureInfo('en_AU');</cdoe> + * 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 "<language>_(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 + * "<languagecode2>_(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 <code>array('Language','Country');</code> + * '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 <code>array('Language','Country');</code> + * '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. <code>array(0 => array('hello'), 1 => 'world');</code> + * becomes <code>array(0 => 'hello', 1 => 'world');</code> + * @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 @@ -<?php
-/**
- * DateFormat class file.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the BSD License.
- *
- * Copyright(c) 2004 by Qiang Xue. All rights reserved.
- *
- * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue}
- * The latest version of PRADO can be obtained from:
- * {@link http://prado.sourceforge.net/}
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @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 <weizhuo[at]gmail[dot]com>
- * @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;
- }
-
-}
-
-?>
+<?php +/** + * DateFormat class file. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the BSD License. + * + * Copyright(c) 2004 by Qiang Xue. All rights reserved. + * + * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue} + * The latest version of PRADO can be obtained from: + * {@link http://prado.sourceforge.net/} + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @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 <weizhuo[at]gmail[dot]com> + * @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 @@ -<?php
-
-/**
- * DateTimeFormatInfo class file.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the BSD License.
- *
- * Copyright(c) 2004 by Qiang Xue. All rights reserved.
- *
- * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue}
- * The latest version of PRADO can be obtained from:
- * {@link http://prado.sourceforge.net/}
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @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:
- * <code>
- * $culture = new CultureInfo('en_AU');
- * $dtfi = $culture->DateTimeFormat;
- * </code>
- *
- * To create a DateTimeFormatInfo for the invariant culture, use
- * <code>
- * DateTimeFormatInfo::getInstance($culture=null);
- * </code>
- * 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.
- *
- * <code>
- * 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
- * --------------------------------------------------------------------------
- * </code>
- *
- * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @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. <b>N.B.</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,
- * <code>array("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat");</code>
- * @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,
- * <code>array("S", "M", "T", "W", "T", "F", "S");</code>
- * @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,
- * <code>array("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
- * "Friday", "Saturday".);</code>
- * @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,
- * <code>array("J","F","M","A","M","J","J","A","S","O","N","D");</code>
- * @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,
- * <code>array("Jan", "Feb", "Mar", "Apr", "May", "Jun",
- * "Jul", "Aug", "Sep","Oct","Nov","Dec");</code>
- * @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,
- * <code>array("January", "February", "March", "April", "May", "June",
- * "July", "August", "September","October","November","December");</code>
- * @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 <code>array('AM','PM');</code>
- * @return array AM and PM markers
- */
- function getAMPMMarkers()
- {
- return $this->data['AmPmMarkers'];
- }
-
- /**
- * Set the AM and PM markers array.
- * For example <code>array('AM','PM');</code>
- * @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);
- }
-
-}
+<?php + +/** + * DateTimeFormatInfo class file. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the BSD License. + * + * Copyright(c) 2004 by Qiang Xue. All rights reserved. + * + * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue} + * The latest version of PRADO can be obtained from: + * {@link http://prado.sourceforge.net/} + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @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: + * <code> + * $culture = new CultureInfo('en_AU'); + * $dtfi = $culture->DateTimeFormat; + * </code> + * + * To create a DateTimeFormatInfo for the invariant culture, use + * <code> + * DateTimeFormatInfo::getInstance($culture=null); + * </code> + * 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. + * + * <code> + * 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 + * -------------------------------------------------------------------------- + * </code> + * + * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com> + * @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. <b>N.B.</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, + * <code>array("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat");</code> + * @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, + * <code>array("S", "M", "T", "W", "T", "F", "S");</code> + * @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, + * <code>array("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", + * "Friday", "Saturday".);</code> + * @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, + * <code>array("J","F","M","A","M","J","J","A","S","O","N","D");</code> + * @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, + * <code>array("Jan", "Feb", "Mar", "Apr", "May", "Jun", + * "Jul", "Aug", "Sep","Oct","Nov","Dec");</code> + * @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, + * <code>array("January", "February", "March", "April", "May", "June", + * "July", "August", "September","October","November","December");</code> + * @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 <code>array('AM','PM');</code> + * @return array AM and PM markers + */ + function getAMPMMarkers() + { + return $this->data['AmPmMarkers']; + } + + /** + * Set the AM and PM markers array. + * For example <code>array('AM','PM');</code> + * @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 @@ -<?php
-/**
- * TGettext_MO class file.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the BSD License.
- *
- * Copyright(c) 2004 by Qiang Xue. All rights reserved.
- *
- * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue}
- * The latest version of PRADO can be obtained from:
- * {@link http://prado.sourceforge.net/}
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @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 <mike@iworks.at> |
-// +----------------------------------------------------------------------+
-//
-// $Id$
-
-/**
- * File::Gettext::MO
- *
- * @author Michael Wallner <mike@php.net>
- * @license PHP License
- */
-
-require_once dirname(__FILE__).'/TGettext.php';
-
-/**
- * File_Gettext_MO
- *
- * GNU MO file reader and writer.
- *
- * @author Michael Wallner <mike@php.net>
- * @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;
- }
-}
+<?php +/** + * TGettext_MO class file. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the BSD License. + * + * Copyright(c) 2004 by Qiang Xue. All rights reserved. + * + * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue} + * The latest version of PRADO can be obtained from: + * {@link http://prado.sourceforge.net/} + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @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 <mike@iworks.at> | +// +----------------------------------------------------------------------+ +// +// $Id$ + +/** + * File::Gettext::MO + * + * @author Michael Wallner <mike@php.net> + * @license PHP License + */ + +require_once dirname(__FILE__).'/TGettext.php'; + +/** + * File_Gettext_MO + * + * GNU MO file reader and writer. + * + * @author Michael Wallner <mike@php.net> + * @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 @@ -<?php
-/**
- * TGettext_PO class file.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the BSD License.
- *
- * Copyright(c) 2004 by Qiang Xue. All rights reserved.
- *
- * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue}
- * The latest version of PRADO can be obtained from:
- * {@link http://prado.sourceforge.net/}
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @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 <mike@iworks.at> |
-// +----------------------------------------------------------------------+
-//
-// $Id$
-
-/**
- * File::Gettext::PO
- *
- * @author Michael Wallner <mike@php.net>
- * @license PHP License
- */
-
-require_once dirname(__FILE__).'/TGettext.php';
-
-/**
- * File_Gettext_PO
- *
- * GNU PO file reader and writer.
- *
- * @author Michael Wallner <mike@php.net>
- * @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;
- }
-}
+<?php +/** + * TGettext_PO class file. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the BSD License. + * + * Copyright(c) 2004 by Qiang Xue. All rights reserved. + * + * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue} + * The latest version of PRADO can be obtained from: + * {@link http://prado.sourceforge.net/} + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @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 <mike@iworks.at> | +// +----------------------------------------------------------------------+ +// +// $Id$ + +/** + * File::Gettext::PO + * + * @author Michael Wallner <mike@php.net> + * @license PHP License + */ + +require_once dirname(__FILE__).'/TGettext.php'; + +/** + * File_Gettext_PO + * + * GNU PO file reader and writer. + * + * @author Michael Wallner <mike@php.net> + * @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 @@ -<?php
-/**
- * TGettext class file.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the BSD License.
- *
- * Copyright(c) 2004 by Qiang Xue. All rights reserved.
- *
- * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue}
- * The latest version of PRADO can be obtained from:
- * {@link http://prado.sourceforge.net/}
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @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 <mike@iworks.at> |
-// +----------------------------------------------------------------------+
-//
-// $Id$
-
-/**
- * File::Gettext
- *
- * @author Michael Wallner <mike@php.net>
- * @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 <mike@php.net>
- * @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:
- * <code>
- * array(
- * 'meta' => array(
- * 'Content-Type' => 'text/plain; charset=iso-8859-1',
- * 'Last-Translator' => 'Michael Wallner <mike@iworks.at>',
- * 'PO-Revision-Date' => '2004-07-21 17:03+0200',
- * 'Language-Team' => 'German <mail@example.com>',
- * ),
- * 'strings' => array(
- * 'All rights reserved' => 'Alle Rechte vorbehalten',
- * 'Welcome' => 'Willkommen',
- * // ...
- * )
- * )
- * </code>
- *
- * @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:
- * <code>
- * array(
- * 'meta' => array(
- * 'Content-Type' => 'text/plain; charset=iso-8859-1',
- * 'Last-Translator' => 'Michael Wallner <mike@iworks.at>',
- * 'PO-Revision-Date' => date('Y-m-d H:iO'),
- * 'Language-Team' => 'German <mail@example.com>',
- * ),
- * 'strings' => array(
- * 'All rights reserved' => 'Alle Rechte vorbehalten',
- * 'Welcome' => 'Willkommen',
- * // ...
- * )
- * )
- * </code>
- *
- * @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;
- }
-}
+<?php +/** + * TGettext class file. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the BSD License. + * + * Copyright(c) 2004 by Qiang Xue. All rights reserved. + * + * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue} + * The latest version of PRADO can be obtained from: + * {@link http://prado.sourceforge.net/} + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @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 <mike@iworks.at> | +// +----------------------------------------------------------------------+ +// +// $Id$ + +/** + * File::Gettext + * + * @author Michael Wallner <mike@php.net> + * @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 <mike@php.net> + * @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: + * <code> + * array( + * 'meta' => array( + * 'Content-Type' => 'text/plain; charset=iso-8859-1', + * 'Last-Translator' => 'Michael Wallner <mike@iworks.at>', + * 'PO-Revision-Date' => '2004-07-21 17:03+0200', + * 'Language-Team' => 'German <mail@example.com>', + * ), + * 'strings' => array( + * 'All rights reserved' => 'Alle Rechte vorbehalten', + * 'Welcome' => 'Willkommen', + * // ... + * ) + * ) + * </code> + * + * @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: + * <code> + * array( + * 'meta' => array( + * 'Content-Type' => 'text/plain; charset=iso-8859-1', + * 'Last-Translator' => 'Michael Wallner <mike@iworks.at>', + * 'PO-Revision-Date' => date('Y-m-d H:iO'), + * 'Language-Team' => 'German <mail@example.com>', + * ), + * 'strings' => array( + * 'All rights reserved' => 'Alle Rechte vorbehalten', + * 'Welcome' => 'Willkommen', + * // ... + * ) + * ) + * </code> + * + * @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 @@ -<?php
-
-/**
- * HTTPNegotiator class file.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the BSD License.
- *
- * Copyright(c) 2004 by Qiang Xue. All rights reserved.
- *
- * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue}
- * The latest version of PRADO can be obtained from:
- * {@link http://prado.sourceforge.net/}
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @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 <weizhuo[at]gmail[dot]com>
- * @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;
- }
-}
-
+<?php + +/** + * HTTPNegotiator class file. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the BSD License. + * + * Copyright(c) 2004 by Qiang Xue. All rights reserved. + * + * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue} + * The latest version of PRADO can be obtained from: + * {@link http://prado.sourceforge.net/} + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @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 <weizhuo[at]gmail[dot]com> + * @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 @@ -<?php
-
-/**
- * IMessageSource interface file.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the BSD License.
- *
- * Copyright(c) 2004 by Qiang Xue. All rights reserved.
- *
- * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue}
- * The latest version of PRADO can be obtained from:
- * {@link http://prado.sourceforge.net/}
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @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 <weizhuo[at]gmail[dot]com>
- * @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.
- * <code>
- * array('catalogue+variant' =>
- * array('source string' => 'target string', ...)
- * ...),
- * ...);
- * </code>
- *
- * @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 <b>append()</b> 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();
-
-}
-
+<?php + +/** + * IMessageSource interface file. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the BSD License. + * + * Copyright(c) 2004 by Qiang Xue. All rights reserved. + * + * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue} + * The latest version of PRADO can be obtained from: + * {@link http://prado.sourceforge.net/} + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @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 <weizhuo[at]gmail[dot]com> + * @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. + * <code> + * array('catalogue+variant' => + * array('source string' => 'target string', ...) + * ...), + * ...); + * </code> + * + * @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 <b>append()</b> 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 @@ -<?php
-/**
- * Translation table cache.
- * @author $Author: weizhuo $
- * @version $Id$
- * @package System.I18N.core
- */
-
-/**
- * Load the cache lite library.
- */
-require_once(dirname(__FILE__).'/TCache_Lite.php');
-
-/**
- * Cache the translation table into the file system.
- * It can cache each cataloug+variant or just the whole section.
- * @package System.I18N.core
- * @author $Author: weizhuo $
- * @version $Id$
- */
-class MessageCache
-{
-
- /**
- * Cache Lite instance.
- * @var TCache_Lite
- */
- protected $cache;
-
- /**
- * Caceh life time, default is 1 year.
- */
- protected $lifetime = 3153600;
-
-
- /**
- * Create a new Translation cache.
- * @param string $cacheDir Directory to store the cache files.
- */
- public function __construct($cacheDir)
- {
- $cacheDir = $cacheDir.'/';
-
- if(!is_dir($cacheDir))
- throw new Exception(
- 'The cache directory '.$cacheDir.' does not exists.'.
- 'The cache directory must be writable by the server.');
- if(!is_writable($cacheDir))
- throw new Exception(
- 'The cache directory '.$cacheDir.' must be writable '.
- 'by the server.');
-
- $options = array(
- 'cacheDir' => $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 "<br>\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 "<br>\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();
- }
-
-}
-
-?>
+<?php +/** + * Translation table cache. + * @author $Author: weizhuo $ + * @version $Id$ + * @package System.I18N.core + */ + +/** + * Load the cache lite library. + */ +require_once(dirname(__FILE__).'/TCache_Lite.php'); + +/** + * Cache the translation table into the file system. + * It can cache each cataloug+variant or just the whole section. + * @package System.I18N.core + * @author $Author: weizhuo $ + * @version $Id$ + */ +class MessageCache +{ + + /** + * Cache Lite instance. + * @var TCache_Lite + */ + protected $cache; + + /** + * Caceh life time, default is 1 year. + */ + protected $lifetime = 3153600; + + + /** + * Create a new Translation cache. + * @param string $cacheDir Directory to store the cache files. + */ + public function __construct($cacheDir) + { + $cacheDir = $cacheDir.'/'; + + if(!is_dir($cacheDir)) + throw new Exception( + 'The cache directory '.$cacheDir.' does not exists.'. + 'The cache directory must be writable by the server.'); + if(!is_writable($cacheDir)) + throw new Exception( + 'The cache directory '.$cacheDir.' must be writable '. + 'by the server.'); + + $options = array( + 'cacheDir' => $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 "<br>\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 "<br>\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 @@ -<?php
-
-/**
- * MessageFormat class file.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the BSD License.
- *
- * Copyright(c) 2004 by Qiang Xue. All rights reserved.
- *
- * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue}
- * The latest version of PRADO can be obtained from:
- * {@link http://prado.sourceforge.net/}
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @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.
- *
- * <code>
- * $source = MessageSource::factory('SQLite', 'sqlite://messages.db');
- * $source->setCulture('zh_CN');
- * $source->setCache(new MessageCache('./tmp'));
- *
- * $formatter = new MessageFormat($source);
- *
- * echo $formatter->format('Hello');
- * </code>
- *
- * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @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];
- }
- }
-}
-
+<?php + +/** + * MessageFormat class file. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the BSD License. + * + * Copyright(c) 2004 by Qiang Xue. All rights reserved. + * + * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue} + * The latest version of PRADO can be obtained from: + * {@link http://prado.sourceforge.net/} + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @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. + * + * <code> + * $source = MessageSource::factory('SQLite', 'sqlite://messages.db'); + * $source->setCulture('zh_CN'); + * $source->setCache(new MessageCache('./tmp')); + * + * $formatter = new MessageFormat($source); + * + * echo $formatter->format('Hello'); + * </code> + * + * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com> + * @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 @@ -<?php
-
-/**
- * MessageSource class file.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the BSD License.
- *
- * Copyright(c) 2004 by Qiang Xue. All rights reserved.
- *
- * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue}
- * The latest version of PRADO can be obtained from:
- * {@link http://prado.sourceforge.net/}
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @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.
- * <code>
- * $resource = '...'; //custom message source resource
- * $classfile = '../MessageSource_MySource.php'; //custom message source
- * $source = MessageSource::factory('MySource', $resource, $classfile);
- * </code>
- *
- * 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.
- * <code>
- * // 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);
- * </code>
- *
- * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @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<weizhuo[at]gmail[dot]com>
- * @version $Revision: 1.4 $ $Date: 2005/12/17 06:11:28 ${DATE} ${TIME} $
- * @package System.I18N.core
- */
-class TMessageSourceIOException extends TException
-{
-
-}
-?>
+<?php + +/** + * MessageSource class file. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the BSD License. + * + * Copyright(c) 2004 by Qiang Xue. All rights reserved. + * + * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue} + * The latest version of PRADO can be obtained from: + * {@link http://prado.sourceforge.net/} + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @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. + * <code> + * $resource = '...'; //custom message source resource + * $classfile = '../MessageSource_MySource.php'; //custom message source + * $source = MessageSource::factory('MySource', $resource, $classfile); + * </code> + * + * 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. + * <code> + * // 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); + * </code> + * + * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com> + * @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<weizhuo[at]gmail[dot]com> + * @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 @@ -<?php
-/**
- * MessageSource_Database class file.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the BSD License.
- *
- * Copyright(c) 2004 by Qiang Xue. All rights reserved.
- *
- * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue}
- * The latest version of PRADO can be obtained from:
- * {@link http://prado.sourceforge.net/}
- *
- * @package System.I18N.core
- */
-
-/**
- * Get the MessageSource class file.
- */
-require_once(dirname(__FILE__).'/MessageSource.php');
-
-/**
- * MessageSource_Database class.
- *
- * Retrive the message translation from a database.
- *
- * See the MessageSource::factory() method to instantiate this class.
- *
- * @package System.I18N.core
- */
-class MessageSource_Database extends MessageSource
-{
- private $_connID='';
- private $_conn;
-
- /**
- * Constructor.
- * Create a new message source using a Database
- * @param string MySQL datasource, in PEAR's DB DSN format.
- * @see MessageSource::factory();
- */
- function __construct($source)
- {
- $this->_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 <b>append()</b> 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;
- }
-
-}
-?>
+<?php +/** + * MessageSource_Database class file. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the BSD License. + * + * Copyright(c) 2004 by Qiang Xue. All rights reserved. + * + * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue} + * The latest version of PRADO can be obtained from: + * {@link http://prado.sourceforge.net/} + * + * @package System.I18N.core + */ + +/** + * Get the MessageSource class file. + */ +require_once(dirname(__FILE__).'/MessageSource.php'); + +/** + * MessageSource_Database class. + * + * Retrive the message translation from a database. + * + * See the MessageSource::factory() method to instantiate this class. + * + * @package System.I18N.core + */ +class MessageSource_Database extends MessageSource +{ + private $_connID=''; + private $_conn; + + /** + * Constructor. + * Create a new message source using a Database + * @param string MySQL datasource, in PEAR's DB DSN format. + * @see MessageSource::factory(); + */ + function __construct($source) + { + $this->_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 <b>append()</b> 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 @@ -<?php
-
-/**
- * MessageSource_MySQL class file.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the BSD License.
- *
- * Copyright(c) 2004 by Qiang Xue. All rights reserved.
- *
- * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue}
- * The latest version of PRADO can be obtained from:
- * {@link http://prado.sourceforge.net/}
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @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 <weizhuo[at]gmail[dot]com>
- * @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 <b>append()</b> 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;
- }
-
-}
-
-?>
+<?php + +/** + * MessageSource_MySQL class file. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the BSD License. + * + * Copyright(c) 2004 by Qiang Xue. All rights reserved. + * + * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue} + * The latest version of PRADO can be obtained from: + * {@link http://prado.sourceforge.net/} + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @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 <weizhuo[at]gmail[dot]com> + * @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 <b>append()</b> 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 @@ -<?php
-
-/**
- * MessageSource_SQLite class file.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the BSD License.
- *
- * Copyright(c) 2004 by Qiang Xue. All rights reserved.
- *
- * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue}
- * The latest version of PRADO can be obtained from:
- * {@link http://prado.sourceforge.net/}
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @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 <weizhuo[at]gmail[dot]com>
- * @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 <b>append()</b> 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;
- }
-}
-
-?>
+<?php + +/** + * MessageSource_SQLite class file. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the BSD License. + * + * Copyright(c) 2004 by Qiang Xue. All rights reserved. + * + * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue} + * The latest version of PRADO can be obtained from: + * {@link http://prado.sourceforge.net/} + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @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 <weizhuo[at]gmail[dot]com> + * @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 <b>append()</b> 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 @@ -<?php
-
-/**
- * MessageSource_XLIFF class file.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the BSD License.
- *
- * Copyright(c) 2004 by Qiang Xue. All rights reserved.
- *
- * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue}
- * The latest version of PRADO can be obtained from:
- * {@link http://prado.sourceforge.net/}
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @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 <weizhuo[at]gmail[dot]com>
- * @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 <b>append()</b> 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
-<?xml version="1.0" encoding="UTF-8"?>
-<xliff version="1.0">
- <file source-language="EN" target-language="{$this->culture}" datatype="plaintext" original="$catalogue" date="$date" product-name="$catalogue">
- <body>
- </body>
- </file>
-</xliff>
-EOD;
- return $xml;
- }
-}
+<?php + +/** + * MessageSource_XLIFF class file. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the BSD License. + * + * Copyright(c) 2004 by Qiang Xue. All rights reserved. + * + * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue} + * The latest version of PRADO can be obtained from: + * {@link http://prado.sourceforge.net/} + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @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 <weizhuo[at]gmail[dot]com> + * @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 <b>append()</b> 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 +<?xml version="1.0" encoding="UTF-8"?> +<xliff version="1.0"> + <file source-language="EN" target-language="{$this->culture}" datatype="plaintext" original="$catalogue" date="$date" product-name="$catalogue"> + <body> + </body> + </file> +</xliff> +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 @@ -<?php
-
-/**
- * MessageSource_gettext class file.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the BSD License.
- *
- * Copyright(c) 2004 by Qiang Xue. All rights reserved.
- *
- * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue}
- * The latest version of PRADO can be obtained from:
- * {@link http://prado.sourceforge.net/}
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @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 <weizhuo[at]gmail[dot]com>
- * @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 <b>append()</b> 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");
- }
-}
-
-?>
+<?php + +/** + * MessageSource_gettext class file. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the BSD License. + * + * Copyright(c) 2004 by Qiang Xue. All rights reserved. + * + * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue} + * The latest version of PRADO can be obtained from: + * {@link http://prado.sourceforge.net/} + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @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 <weizhuo[at]gmail[dot]com> + * @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 <b>append()</b> 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 @@ -<?php
-
-/**
- * NumberFormat class file.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the BSD License.
- *
- * Copyright(c) 2004 by Qiang Xue. All rights reserved.
- *
- * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue}
- * The latest version of PRADO can be obtained from:
- * {@link http://prado.sourceforge.net/}
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @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.
- *
- * <code>
- * //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);
- * </code>
- *
- * A normal decimal number can also be displayed as a currency
- * or as a percentage. For example
- * <code>
- * $format->format(1234.5); //Decimal number "1234.5"
- * $format->format(1234.5,'c'); //Default currency "$1234.50"
- * $format->format(0.25, 'p') //Percent "25%"
- * </code>
- *
- * Currency is formated using the localized currency pattern. For example
- * to format the number as Japanese Yen:
- * <code>
- * $ja = new NumberFormat('ja_JP');
- *
- * //Japanese currency pattern, and using Japanese Yen symbol
- * $ja->format(123.14,'c','JPY'); //�?123 (Yen 123)
- * </code>
- * For each culture, the symbol for each currency may be different.
- *
- * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @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;
- }
- }
-}
-
+<?php + +/** + * NumberFormat class file. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the BSD License. + * + * Copyright(c) 2004 by Qiang Xue. All rights reserved. + * + * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue} + * The latest version of PRADO can be obtained from: + * {@link http://prado.sourceforge.net/} + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @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. + * + * <code> + * //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); + * </code> + * + * A normal decimal number can also be displayed as a currency + * or as a percentage. For example + * <code> + * $format->format(1234.5); //Decimal number "1234.5" + * $format->format(1234.5,'c'); //Default currency "$1234.50" + * $format->format(0.25, 'p') //Percent "25%" + * </code> + * + * Currency is formated using the localized currency pattern. For example + * to format the number as Japanese Yen: + * <code> + * $ja = new NumberFormat('ja_JP'); + * + * //Japanese currency pattern, and using Japanese Yen symbol + * $ja->format(123.14,'c','JPY'); //�?123 (Yen 123) + * </code> + * For each culture, the symbol for each currency may be different. + * + * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com> + * @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 @@ -<?php
-
-/**
- * NumberFormatInfo class file.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the BSD License.
- *
- * Copyright(c) 2004 by Qiang Xue. All rights reserved.
- *
- * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue}
- * The latest version of PRADO can be obtained from:
- * {@link http://prado.sourceforge.net/}
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @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 <weizhuo[at]gmail[dot]com>
- * @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. <b>N.B.</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 <b>array(group1, group2)</b>, 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 <b>array(group1, group2)</b>, 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 <b>array(prefix, postfix)</b>.
- * @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
- * <b>array(prefix, postfix)</b>.
- * @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 <b>array(prefix, postfix)</b>.
- * @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
- * <b>array(prefix, postfix)</b>.
- * @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;
- }
-}
-
+<?php + +/** + * NumberFormatInfo class file. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the BSD License. + * + * Copyright(c) 2004 by Qiang Xue. All rights reserved. + * + * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue} + * The latest version of PRADO can be obtained from: + * {@link http://prado.sourceforge.net/} + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @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 <weizhuo[at]gmail[dot]com> + * @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. <b>N.B.</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 <b>array(group1, group2)</b>, 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 <b>array(group1, group2)</b>, 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 <b>array(prefix, postfix)</b>. + * @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 + * <b>array(prefix, postfix)</b>. + * @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 <b>array(prefix, postfix)</b>. + * @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 + * <b>array(prefix, postfix)</b>. + * @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 @@ -<?php
-
-/**
- * TCache_Lite class file.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the BSD License.
- *
- * Copyright(c) 2004 by Qiang Xue. All rights reserved.
- *
- * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue}
- * The latest version of PRADO can be obtained from:
- * {@link http://prado.sourceforge.net/}
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @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 <ipso@snappymail.ca>
-*
-* @package System.I18N.core
-* @version $Id$
-* @author Fabien MARTY <fab@php.net>
-* @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);
- }
- }
-
-}
-
-?>
+<?php + +/** + * TCache_Lite class file. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the BSD License. + * + * Copyright(c) 2004 by Qiang Xue. All rights reserved. + * + * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue} + * The latest version of PRADO can be obtained from: + * {@link http://prado.sourceforge.net/} + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @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 <ipso@snappymail.ca> +* +* @package System.I18N.core +* @version $Id$ +* @author Fabien MARTY <fab@php.net> +* @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 @@ -<?php
-
-/**
- * I18N Utility file.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the BSD License.
- *
- * Copyright(c) 2004 by Wei Zhuo. All rights reserved.
- *
- * To contact the author write to <weizhuo[at]gmail[dot]com>
- * The latest version of PRADO can be obtained from:
- * {@link http://prado.sourceforge.net/}
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @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 <ssb@php.net>
- * @author Tomas V.V.Cox <cox@idecnet.com>
- * @author Daniel Convissor <danielc@php.net>
- * @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;
- }
-
+<?php + +/** + * I18N Utility file. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the BSD License. + * + * Copyright(c) 2004 by Wei Zhuo. All rights reserved. + * + * To contact the author write to <weizhuo[at]gmail[dot]com> + * The latest version of PRADO can be obtained from: + * {@link http://prado.sourceforge.net/} + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @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 <ssb@php.net> + * @author Tomas V.V.Cox <cox@idecnet.com> + * @author Daniel Convissor <danielc@php.net> + * @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 |