summaryrefslogtreecommitdiff
path: root/framework/i18n/gettext
diff options
context:
space:
mode:
authorPatrick Seeger <pseeger@ccwn.org>2012-04-13 23:11:05 +0200
committerPatrick Seeger <pseeger@ccwn.org>2012-04-13 23:11:05 +0200
commit341cc4dd9c53ffbfb863e026dd58549c1082c7a7 (patch)
tree1bbbed20313bafb9b063b6b4d894fe580d8b000f /framework/i18n/gettext
yii-framework 1.1.10 hinzugefügtHEADmaster
Diffstat (limited to 'framework/i18n/gettext')
-rw-r--r--framework/i18n/gettext/CGettextFile.php36
-rw-r--r--framework/i18n/gettext/CGettextMoFile.php269
-rw-r--r--framework/i18n/gettext/CGettextPoFile.php89
3 files changed, 394 insertions, 0 deletions
diff --git a/framework/i18n/gettext/CGettextFile.php b/framework/i18n/gettext/CGettextFile.php
new file mode 100644
index 0000000..6ce8494
--- /dev/null
+++ b/framework/i18n/gettext/CGettextFile.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * CGettextFile class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+/**
+ * CGettextFile is the base class for representing a Gettext message file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CGettextFile.php 2798 2011-01-01 19:29:03Z qiang.xue $
+ * @package system.i18n.gettext
+ * @since 1.0
+ */
+abstract class CGettextFile extends CComponent
+{
+ /**
+ * Loads messages from a file.
+ * @param string $file file path
+ * @param string $context message context
+ * @return array message translations (source message => translated message)
+ */
+ abstract public function load($file,$context);
+ /**
+ * Saves messages to a file.
+ * @param string $file file path
+ * @param array $messages message translations (message id => translated message).
+ * Note if the message has a context, the message id must be prefixed with
+ * the context with chr(4) as the separator.
+ */
+ abstract public function save($file,$messages);
+}
diff --git a/framework/i18n/gettext/CGettextMoFile.php b/framework/i18n/gettext/CGettextMoFile.php
new file mode 100644
index 0000000..324bb15
--- /dev/null
+++ b/framework/i18n/gettext/CGettextMoFile.php
@@ -0,0 +1,269 @@
+<?php
+/**
+ * CGettextMoFile class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+/**
+ * CGettextMoFile represents an MO Gettext message file.
+ *
+ * This class is written by adapting Michael's Gettext_MO class in PEAR.
+ * Please refer to the following license terms.
+ *
+ * Copyright (c) 2004-2005, Michael Wallner <mike@iworks.at>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CGettextMoFile.php 2798 2011-01-01 19:29:03Z qiang.xue $
+ * @package system.i18n.gettext
+ * @since 1.0
+ */
+class CGettextMoFile extends CGettextFile
+{
+ /**
+ * @var boolean whether to use Big Endian when reading and writing an integer.
+ */
+ public $useBigEndian=false;
+
+ /**
+ * Constructor.
+ * @param boolean $useBigEndian whether to use Big Endian when reading and writing an integer.
+ */
+ public function __construct($useBigEndian=false)
+ {
+ $this->useBigEndian=$useBigEndian;
+ }
+
+ /**
+ * Loads messages from an MO file.
+ * @param string $file file path
+ * @param string $context message context
+ * @return array message translations (source message => translated message)
+ */
+ public function load($file,$context)
+ {
+ if(!($fr=@fopen($file,'rb')))
+ throw new CException(Yii::t('yii','Unable to read file "{file}".',
+ array('{file}'=>$file)));
+
+ if(!@flock($fr,LOCK_SH))
+ throw new CException(Yii::t('yii','Unable to lock file "{file}" for reading.',
+ array('{file}'=>$file)));
+
+ $magic=current($array=unpack('c',$this->readByte($fr,4)));
+ if($magic==-34)
+ $this->useBigEndian=false;
+ else if($magic==-107)
+ $this->useBigEndian=true;
+ else
+ throw new CException(Yii::t('yii','Invalid MO file: {file} (magic: {magic}).',
+ array('{file}'=>$file,'{magic}'=>$magic)));
+
+ if(($revision=$this->readInteger($fr))!=0)
+ throw new CException(Yii::t('yii','Invalid MO file revision: {revision}.',
+ array('{revision}'=>$revision)));
+
+ $count=$this->readInteger($fr);
+ $sourceOffset=$this->readInteger($fr);
+ $targetOffset=$this->readInteger($fr);
+
+ $sourceLengths=array();
+ $sourceOffsets=array();
+ fseek($fr,$sourceOffset);
+ for($i=0;$i<$count;++$i)
+ {
+ $sourceLengths[]=$this->readInteger($fr);
+ $sourceOffsets[]=$this->readInteger($fr);
+ }
+
+ $targetLengths=array();
+ $targetOffsets=array();
+ fseek($fr,$targetOffset);
+ for($i=0;$i<$count;++$i)
+ {
+ $targetLengths[]=$this->readInteger($fr);
+ $targetOffsets[]=$this->readInteger($fr);
+ }
+
+ $messages=array();
+ for($i=0;$i<$count;++$i)
+ {
+ $id=$this->readString($fr,$sourceLengths[$i],$sourceOffsets[$i]);
+ if(($pos=strpos($id,chr(4)))!==false && substr($id,0,$pos)===$context)
+ {
+ $id=substr($id,$pos+1);
+ $message=$this->readString($fr,$targetLengths[$i],$targetOffsets[$i]);
+ $messages[$id]=$message;
+ }
+ }
+
+ @flock($fr,LOCK_UN);
+ @fclose($fr);
+
+ return $messages;
+ }
+
+ /**
+ * Saves messages to an MO file.
+ * @param string $file file path
+ * @param array $messages message translations (message id => translated message).
+ * Note if the message has a context, the message id must be prefixed with
+ * the context with chr(4) as the separator.
+ */
+ public function save($file,$messages)
+ {
+ if(!($fw=@fopen($file,'wb')))
+ throw new CException(Yii::t('yii','Unable to write file "{file}".',
+ array('{file}'=>$file)));
+
+ if(!@flock($fw,LOCK_EX))
+ throw new CException(Yii::t('yii','Unable to lock file "{file}" for writing.',
+ array('{file}'=>$file)));
+
+ // magic
+ if($this->useBigEndian)
+ $this->writeByte($fw,pack('c*', 0x95, 0x04, 0x12, 0xde));
+ else
+ $this->writeByte($fw,pack('c*', 0xde, 0x12, 0x04, 0x95));
+
+ // revision
+ $this->writeInteger($fw,0);
+
+ // message count
+ $n=count($messages);
+ $this->writeInteger($fw,$n);
+
+ // offset of source message table
+ $offset=28;
+ $this->writeInteger($fw,$offset);
+ $offset+=($n*8);
+ $this->writeInteger($fw,$offset);
+ // hashtable size, omitted
+ $this->writeInteger($fw,0);
+ $offset+=($n*8);
+ $this->writeInteger($fw,$offset);
+
+ // length and offsets for source messagess
+ foreach(array_keys($messages) as $id)
+ {
+ $len=strlen($id);
+ $this->writeInteger($fw,$len);
+ $this->writeInteger($fw,$offset);
+ $offset+=$len+1;
+ }
+
+ // length and offsets for target messagess
+ foreach($messages as $message)
+ {
+ $len=strlen($message);
+ $this->writeInteger($fw,$len);
+ $this->writeInteger($fw,$offset);
+ $offset+=$len+1;
+ }
+
+ // source messages
+ foreach(array_keys($messages) as $id)
+ $this->writeString($fw,$id);
+
+ // target messages
+ foreach($messages as $message)
+ $this->writeString($fw,$message);
+
+ @flock($fw,LOCK_UN);
+ @fclose($fw);
+ }
+
+ /**
+ * Reads one or several bytes.
+ * @param resource $fr file handle
+ * @param integer $n number of bytes to read
+ * @return string bytes
+ */
+ protected function readByte($fr,$n=1)
+ {
+ if($n>0)
+ return fread($fr,$n);
+ }
+
+ /**
+ * Writes bytes.
+ * @param resource $fw file handle
+ * @param string $data the data
+ * @return integer how many bytes are written
+ */
+ protected function writeByte($fw,$data)
+ {
+ return fwrite($fw,$data);
+ }
+
+ /**
+ * Reads a 4-byte integer.
+ * @param resource $fr file handle
+ * @return integer the result
+ * @see useBigEndian
+ */
+ protected function readInteger($fr)
+ {
+ return current($array=unpack($this->useBigEndian ? 'N' : 'V', $this->readByte($fr,4)));
+ }
+
+ /**
+ * Writes a 4-byte integer.
+ * @param resource $fw file handle
+ * @param integer $data the data
+ * @return integer how many bytes are written
+ */
+ protected function writeInteger($fw,$data)
+ {
+ return $this->writeByte($fw,pack($this->useBigEndian ? 'N' : 'V', (int)$data));
+ }
+
+ /**
+ * Reads a string.
+ * @param resource $fr file handle
+ * @param integer $length string length
+ * @param integer $offset offset of the string in the file. If null, it reads from the current position.
+ * @return string the result
+ */
+ protected function readString($fr,$length,$offset=null)
+ {
+ if($offset!==null)
+ fseek($fr,$offset);
+ return $this->readByte($fr,$length);
+ }
+
+ /**
+ * Writes a string.
+ * @param resource $fw file handle
+ * @param string $data the string
+ * @return integer how many bytes are written
+ */
+ protected function writeString($fw,$data)
+ {
+ return $this->writeByte($fw,$data."\0");
+ }
+}
diff --git a/framework/i18n/gettext/CGettextPoFile.php b/framework/i18n/gettext/CGettextPoFile.php
new file mode 100644
index 0000000..d34a16a
--- /dev/null
+++ b/framework/i18n/gettext/CGettextPoFile.php
@@ -0,0 +1,89 @@
+<?php
+/**
+ * CGettextPoFile class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+/**
+ * CGettextPoFile represents a PO Gettext message file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CGettextPoFile.php 2798 2011-01-01 19:29:03Z qiang.xue $
+ * @package system.i18n.gettext
+ * @since 1.0
+ */
+class CGettextPoFile extends CGettextFile
+{
+ /**
+ * Loads messages from a PO file.
+ * @param string $file file path
+ * @param string $context message context
+ * @return array message translations (source message => translated message)
+ */
+ public function load($file,$context)
+ {
+ $pattern='/(msgctxt\s+"(.*?(?<!\\\\))")?'
+ . '\s+msgid\s+"(.*?(?<!\\\\))"'
+ . '\s+msgstr\s+"(.*?(?<!\\\\))"/';
+ $content=file_get_contents($file);
+ $n=preg_match_all($pattern,$content,$matches);
+ $messages=array();
+ for($i=0;$i<$n;++$i)
+ {
+ if($matches[2][$i]===$context)
+ {
+ $id=$this->decode($matches[3][$i]);
+ $message=$this->decode($matches[4][$i]);
+ $messages[$id]=$message;
+ }
+ }
+ return $messages;
+ }
+
+ /**
+ * Saves messages to a PO file.
+ * @param string $file file path
+ * @param array $messages message translations (message id => translated message).
+ * Note if the message has a context, the message id must be prefixed with
+ * the context with chr(4) as the separator.
+ */
+ public function save($file,$messages)
+ {
+ $content='';
+ foreach($messages as $id=>$message)
+ {
+ if(($pos=strpos($id,chr(4)))!==false)
+ {
+ $content.='msgctxt "'.substr($id,0,$pos)."\"\n";
+ $id=substr($id,$pos+1);
+ }
+ $content.='msgid "'.$this->encode($id)."\"\n";
+ $content.='msgstr "'.$this->encode($message)."\"\n\n";
+ }
+ file_put_contents($file,$content);
+ }
+
+ /**
+ * Encodes special characters in a message.
+ * @param string $string message to be encoded
+ * @return string the encoded message
+ */
+ protected function encode($string)
+ {
+ return str_replace(array('"', "\n", "\t", "\r"),array('\\"', "\\n", '\\t', '\\r'),$string);
+ }
+
+ /**
+ * Decodes special characters in a message.
+ * @param string $string message to be decoded
+ * @return string the decoded message
+ */
+ protected function decode($string)
+ {
+ return str_replace(array('\\"', "\\n", '\\t', '\\r'),array('"', "\n", "\t", "\r"),$string);
+ }
+} \ No newline at end of file