diff options
Diffstat (limited to 'framework/i18n/gettext/CGettextMoFile.php')
| -rw-r--r-- | framework/i18n/gettext/CGettextMoFile.php | 269 |
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 © 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"); + } +} |
