2023-03-11 12:04:29 +03:00
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* Zend Framework (http://framework.zend.com/)
|
|
|
|
*
|
|
|
|
* @link http://github.com/zendframework/zf2 for the canonical source repository
|
2023-04-01 09:03:34 +03:00
|
|
|
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
|
2023-03-11 12:04:29 +03:00
|
|
|
* @license http://framework.zend.com/license/new-bsd New BSD License
|
|
|
|
*/
|
|
|
|
|
|
|
|
namespace Zend\I18n\Translator\Loader;
|
|
|
|
|
|
|
|
use Zend\I18n\Exception;
|
|
|
|
use Zend\I18n\Translator\Plural\Rule as PluralRule;
|
|
|
|
use Zend\I18n\Translator\TextDomain;
|
|
|
|
use Zend\Stdlib\ErrorHandler;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gettext loader.
|
|
|
|
*/
|
|
|
|
class Gettext extends AbstractFileLoader
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Current file pointer.
|
|
|
|
*
|
|
|
|
* @var resource
|
|
|
|
*/
|
|
|
|
protected $file;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether the current file is little endian.
|
|
|
|
*
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
protected $littleEndian;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* load(): defined by FileLoaderInterface.
|
|
|
|
*
|
|
|
|
* @see FileLoaderInterface::load()
|
|
|
|
* @param string $locale
|
|
|
|
* @param string $filename
|
|
|
|
* @return TextDomain
|
|
|
|
* @throws Exception\InvalidArgumentException
|
|
|
|
*/
|
|
|
|
public function load($locale, $filename)
|
|
|
|
{
|
|
|
|
$resolvedFile = $this->resolveFile($filename);
|
|
|
|
if (!$resolvedFile) {
|
|
|
|
throw new Exception\InvalidArgumentException(sprintf(
|
|
|
|
'Could not find or open file %s for reading',
|
|
|
|
$filename
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
$textDomain = new TextDomain();
|
|
|
|
|
|
|
|
ErrorHandler::start();
|
|
|
|
$this->file = fopen($resolvedFile, 'rb');
|
|
|
|
$error = ErrorHandler::stop();
|
|
|
|
if (false === $this->file) {
|
|
|
|
throw new Exception\InvalidArgumentException(sprintf(
|
|
|
|
'Could not open file %s for reading',
|
|
|
|
$filename
|
|
|
|
), 0, $error);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify magic number
|
|
|
|
$magic = fread($this->file, 4);
|
|
|
|
|
|
|
|
if ($magic == "\x95\x04\x12\xde") {
|
|
|
|
$this->littleEndian = false;
|
|
|
|
} elseif ($magic == "\xde\x12\x04\x95") {
|
|
|
|
$this->littleEndian = true;
|
|
|
|
} else {
|
|
|
|
fclose($this->file);
|
|
|
|
throw new Exception\InvalidArgumentException(sprintf(
|
|
|
|
'%s is not a valid gettext file',
|
|
|
|
$filename
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify major revision (only 0 and 1 supported)
|
|
|
|
$majorRevision = ($this->readInteger() >> 16);
|
|
|
|
|
|
|
|
if ($majorRevision !== 0 && $majorRevision !== 1) {
|
|
|
|
fclose($this->file);
|
|
|
|
throw new Exception\InvalidArgumentException(sprintf(
|
|
|
|
'%s has an unknown major revision',
|
|
|
|
$filename
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Gather main information
|
|
|
|
$numStrings = $this->readInteger();
|
|
|
|
$originalStringTableOffset = $this->readInteger();
|
|
|
|
$translationStringTableOffset = $this->readInteger();
|
|
|
|
|
|
|
|
// Usually there follow size and offset of the hash table, but we have
|
|
|
|
// no need for it, so we skip them.
|
|
|
|
fseek($this->file, $originalStringTableOffset);
|
|
|
|
$originalStringTable = $this->readIntegerList(2 * $numStrings);
|
|
|
|
|
|
|
|
fseek($this->file, $translationStringTableOffset);
|
|
|
|
$translationStringTable = $this->readIntegerList(2 * $numStrings);
|
|
|
|
|
|
|
|
// Read in all translations
|
|
|
|
for ($current = 0; $current < $numStrings; $current++) {
|
|
|
|
$sizeKey = $current * 2 + 1;
|
|
|
|
$offsetKey = $current * 2 + 2;
|
|
|
|
$originalStringSize = $originalStringTable[$sizeKey];
|
|
|
|
$originalStringOffset = $originalStringTable[$offsetKey];
|
|
|
|
$translationStringSize = $translationStringTable[$sizeKey];
|
|
|
|
$translationStringOffset = $translationStringTable[$offsetKey];
|
|
|
|
|
|
|
|
$originalString = array('');
|
|
|
|
if ($originalStringSize > 0) {
|
|
|
|
fseek($this->file, $originalStringOffset);
|
|
|
|
$originalString = explode("\0", fread($this->file, $originalStringSize));
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($translationStringSize > 0) {
|
|
|
|
fseek($this->file, $translationStringOffset);
|
|
|
|
$translationString = explode("\0", fread($this->file, $translationStringSize));
|
|
|
|
|
|
|
|
if (count($originalString) > 1 && count($translationString) > 1) {
|
|
|
|
$textDomain[$originalString[0]] = $translationString;
|
|
|
|
|
|
|
|
array_shift($originalString);
|
|
|
|
|
|
|
|
foreach ($originalString as $string) {
|
2023-04-01 09:03:34 +03:00
|
|
|
if (! isset($textDomain[$string])) {
|
|
|
|
$textDomain[$string] = '';
|
|
|
|
}
|
2023-03-11 12:04:29 +03:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$textDomain[$originalString[0]] = $translationString[0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read header entries
|
|
|
|
if (array_key_exists('', $textDomain)) {
|
|
|
|
$rawHeaders = explode("\n", trim($textDomain['']));
|
|
|
|
|
|
|
|
foreach ($rawHeaders as $rawHeader) {
|
|
|
|
list($header, $content) = explode(':', $rawHeader, 2);
|
|
|
|
|
|
|
|
if (trim(strtolower($header)) === 'plural-forms') {
|
|
|
|
$textDomain->setPluralRule(PluralRule::fromString($content));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unset($textDomain['']);
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose($this->file);
|
|
|
|
|
|
|
|
return $textDomain;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Read a single integer from the current file.
|
|
|
|
*
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
protected function readInteger()
|
|
|
|
{
|
|
|
|
if ($this->littleEndian) {
|
|
|
|
$result = unpack('Vint', fread($this->file, 4));
|
|
|
|
} else {
|
|
|
|
$result = unpack('Nint', fread($this->file, 4));
|
|
|
|
}
|
|
|
|
|
|
|
|
return $result['int'];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Read an integer from the current file.
|
|
|
|
*
|
|
|
|
* @param int $num
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
protected function readIntegerList($num)
|
|
|
|
{
|
|
|
|
if ($this->littleEndian) {
|
|
|
|
return unpack('V' . $num, fread($this->file, 4 * $num));
|
|
|
|
}
|
|
|
|
|
|
|
|
return unpack('N' . $num, fread($this->file, 4 * $num));
|
|
|
|
}
|
|
|
|
}
|