summaryrefslogtreecommitdiff
path: root/framework/i18n/gettext/CGettextMoFile.php
diff options
context:
space:
mode:
Diffstat (limited to 'framework/i18n/gettext/CGettextMoFile.php')
-rw-r--r--framework/i18n/gettext/CGettextMoFile.php269
1 files changed, 269 insertions, 0 deletions
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");
+ }
+}