mirror of
https://github.com/Icinga/icingaweb2.git
synced 2025-09-26 11:19:14 +02:00
parent
8694a13fea
commit
ab8793c69a
@ -0,0 +1,417 @@
|
||||
<?php
|
||||
/* Icinga Web 2 | (c) 2016 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Module\Translation\Catalog;
|
||||
|
||||
use Exception;
|
||||
use SplFileObject;
|
||||
use Icinga\Application\Benchmark;
|
||||
use Icinga\Module\Translation\Exception\CatalogParserException;
|
||||
use Icinga\Util\File;
|
||||
|
||||
/**
|
||||
* Class CatalogParser
|
||||
*
|
||||
* Reads gettext PO files and outputs the contained entries.
|
||||
*
|
||||
* @package Icinga\Module\Translation\Catalog
|
||||
*/
|
||||
class CatalogParser
|
||||
{
|
||||
/**
|
||||
* Escaped chars to resolve
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $escapedChars = array(
|
||||
'\n' => "\n",
|
||||
'\"' => '"',
|
||||
'\t' => "\t",
|
||||
'\r' => "\r",
|
||||
'\\' => "\\"
|
||||
);
|
||||
|
||||
/**
|
||||
* The path of the file being parsed
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $catalogPath;
|
||||
|
||||
/**
|
||||
* The File being parsed
|
||||
*
|
||||
* @var File
|
||||
*/
|
||||
protected $catalogFile = null;
|
||||
|
||||
/**
|
||||
* The line that is being parsed
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $stack = '';
|
||||
|
||||
/**
|
||||
* Number of current line
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $lineNumber = 0;
|
||||
|
||||
/**
|
||||
* Position in current stack
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $position = 0;
|
||||
|
||||
/**
|
||||
* Create a new CatalogParser
|
||||
*
|
||||
* @param string $catalogPath The path to the catalog file to parse
|
||||
*/
|
||||
public function __construct($catalogPath)
|
||||
{
|
||||
$this->catalogPath = $catalogPath;
|
||||
$this->catalogFile = new File($catalogPath);
|
||||
$this->catalogFile->setFlags(SplFileObject::DROP_NEW_LINE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the given catalog file and return its entries
|
||||
*
|
||||
* @param string $catalogPath The path to the catalog file to parse
|
||||
*/
|
||||
public static function parsePath($catalogPath)
|
||||
{
|
||||
Benchmark::measure('CatalogParser::parsePath()');
|
||||
$parser = new static($catalogPath);
|
||||
return $parser->parse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the catalog file and return its entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function parse()
|
||||
{
|
||||
$parsedData = array();
|
||||
$currentEntry = array();
|
||||
$lastType = null;
|
||||
$lastNumber = 0;
|
||||
while ($this->checkStack()) {
|
||||
$returnedValue = $this->handleStack();
|
||||
if (isset($returnedValue['type']) && $returnedValue['type'] === 'newline') {
|
||||
if (! empty($currentEntry)) {
|
||||
$parsedData[] = $currentEntry;
|
||||
$currentEntry = array();
|
||||
$lastType = null;
|
||||
$lastNumber = 0;
|
||||
}
|
||||
} else {
|
||||
if (isset($returnedValue['type'])) {
|
||||
$lastType = $returnedValue['type'];
|
||||
}
|
||||
if (isset($returnedValue['number'])) {
|
||||
$lastNumber = $returnedValue['number'];
|
||||
}
|
||||
if (! isset($currentEntry['obsolete']) || isset($returnedValue['obsolete'])) {
|
||||
$currentEntry['obsolete'] = isset($returnedValue['obsolete']);
|
||||
}
|
||||
if (isset($returnedValue['value'])) {
|
||||
$currentEntry = $this->processParsedValues(
|
||||
$currentEntry,
|
||||
$returnedValue['value'],
|
||||
$lastType,
|
||||
$lastNumber
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! empty($currentEntry)) {
|
||||
$parsedData[] = $currentEntry;
|
||||
}
|
||||
|
||||
return $parsedData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process values parsed by method parse
|
||||
*
|
||||
* @param array $currentEntry The current entry of method parse
|
||||
* @param array|string $returnedValue The value returned by handleStack
|
||||
* @param string $lastType The type the current value belongs to
|
||||
* @param int $lastNumber The key the value belongs to if msgstr_plural
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws CatalogParserException
|
||||
*/
|
||||
protected function processParsedValues($currentEntry, $returnedValue, $lastType, $lastNumber)
|
||||
{
|
||||
if ($lastType === null) {
|
||||
throw new CatalogParserException(
|
||||
$this->catalogPath,
|
||||
$this->lineNumber,
|
||||
$this->position - strlen($returnedValue) - 1,
|
||||
"Missing type before \"$returnedValue\""
|
||||
);
|
||||
}
|
||||
|
||||
$escapedChars = static::$escapedChars;
|
||||
if (is_array($returnedValue)) {
|
||||
$returnedValue = array_map(
|
||||
function ($value) use ($escapedChars) { return strtr($value, $escapedChars); },
|
||||
$returnedValue
|
||||
);
|
||||
} else {
|
||||
$returnedValue = strtr($returnedValue, $escapedChars);
|
||||
}
|
||||
|
||||
if ($lastType === 'msgstr') {
|
||||
if (isset($currentEntry['msgstr'][$lastNumber])) {
|
||||
$currentEntry['msgstr'][$lastNumber] .= $returnedValue;
|
||||
} else {
|
||||
$currentEntry['msgstr'][$lastNumber] = $returnedValue;
|
||||
}
|
||||
} else {
|
||||
if (isset($currentEntry[$lastType])) {
|
||||
if (is_array($currentEntry[$lastType])) {
|
||||
$currentEntry[$lastType] = array_merge($currentEntry[$lastType], $returnedValue);
|
||||
} else {
|
||||
$currentEntry[$lastType] .= $returnedValue;
|
||||
}
|
||||
} else {
|
||||
$currentEntry[$lastType] = $returnedValue;
|
||||
}
|
||||
}
|
||||
|
||||
return $currentEntry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether there is still data available on the stack
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkStack()
|
||||
{
|
||||
if (! $this->stack) {
|
||||
if (! $this->catalogFile->eof()) {
|
||||
$line = $this->catalogFile->fgets();
|
||||
$this->lineNumber++;
|
||||
$this->stack = $line;
|
||||
$this->position = 0;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the current main expression and return the result
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function handleStack()
|
||||
{
|
||||
$this->trimStackLeft();
|
||||
if (! $this->stack) {
|
||||
return array('type' => 'newline');
|
||||
}
|
||||
|
||||
switch ($char = $this->pullCharFromStack())
|
||||
{
|
||||
case '#':
|
||||
return $this->handleHash();
|
||||
case '"':
|
||||
return array('value' => $this->readUntil('"'));
|
||||
default:
|
||||
$this->putCharInStack($char);
|
||||
return $this->handleKeyword($this->readUntil(' '));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return first character from stack and remove it
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function pullCharFromStack()
|
||||
{
|
||||
if (! $this->stack) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$char = $this->stack[0];
|
||||
$this->stack = substr($this->stack, 1) ?: '';
|
||||
$this->position++;
|
||||
|
||||
return $char;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the current hash expression and return the result
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws CatalogParserException
|
||||
*/
|
||||
protected function handleHash()
|
||||
{
|
||||
switch ($char = $this->pullCharFromStack()) {
|
||||
case ' ':
|
||||
return array(
|
||||
'type' => 'translator_comments',
|
||||
'value' => array($this->getStackAndClear())
|
||||
);
|
||||
case '.':
|
||||
return array(
|
||||
'type' => 'extracted_comments',
|
||||
'value' => array(ltrim($this->getStackAndClear()))
|
||||
);
|
||||
case ':':
|
||||
return array(
|
||||
'type' => 'paths',
|
||||
'value' => preg_split('/:\d+\K\s+(?=\S+)/', trim($this->getStackAndClear()))
|
||||
);
|
||||
case ',':
|
||||
return array(
|
||||
'type' => 'flags',
|
||||
'value' => array_map('trim', explode(',', $this->getStackAndClear()))
|
||||
);
|
||||
case '|':
|
||||
return $this->handlePrevious();
|
||||
case '~':
|
||||
return array(
|
||||
'obsolete' => true
|
||||
);
|
||||
case null:
|
||||
return array(
|
||||
'type' => 'translator_comments',
|
||||
'value' => array('')
|
||||
);
|
||||
default:
|
||||
throw new CatalogParserException(
|
||||
$this->catalogPath,
|
||||
$this->lineNumber,
|
||||
$this->position,
|
||||
"Unexpected char \"$char\" after #"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return stack content and clear it afterwards
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStackAndClear()
|
||||
{
|
||||
$stack = $this->stack;
|
||||
$this->stack = '';
|
||||
|
||||
return $stack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle stack if first two chars were #|
|
||||
*
|
||||
* @return array Contains the key value if successful
|
||||
*/
|
||||
protected function handlePrevious()
|
||||
{
|
||||
$this->trimStackLeft();
|
||||
$result = $this->handleKeyword($this->readUntil(' '));
|
||||
|
||||
return array('type' => 'previous_' . $result['type']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim whitespaces on the left of the stack
|
||||
*/
|
||||
protected function trimStackLeft()
|
||||
{
|
||||
$oldStack = $this->stack;
|
||||
$this->stack = ltrim($this->stack);
|
||||
|
||||
if ($this->stack !== $oldStack) {
|
||||
$this->position += strlen($oldStack) - strlen($this->stack);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read until given char comes up
|
||||
*
|
||||
* @param string $endPoint Char to search for
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws CatalogParserException In case the given char cannot be found
|
||||
*/
|
||||
protected function readUntil($endPoint)
|
||||
{
|
||||
$pattern = '/(?<!\\\\)' . preg_quote($endPoint, '/') . '/';
|
||||
|
||||
try {
|
||||
list($string, $this->stack) = preg_split($pattern, $this->stack, 2);
|
||||
} catch (Exception $_) {
|
||||
throw new CatalogParserException(
|
||||
$this->catalogPath,
|
||||
$this->lineNumber,
|
||||
$this->position + strlen($this->stack) + 1,
|
||||
"Missing \"$endPoint\""
|
||||
);
|
||||
}
|
||||
|
||||
$this->position += strlen($string) + 1;
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if keyword is correct
|
||||
*
|
||||
* @param string $keyword The keyword to check
|
||||
*
|
||||
* @return array Returns array with key type if correct
|
||||
*
|
||||
* @throws CatalogParserException In case the given keyword is incorrect
|
||||
*/
|
||||
protected function handleKeyword($keyword)
|
||||
{
|
||||
switch ($keyword)
|
||||
{
|
||||
case 'msgctxt':
|
||||
case 'msgid':
|
||||
case 'msgid_plural':
|
||||
case 'msgstr':
|
||||
return array('type' => $keyword);
|
||||
case (preg_match('/^(?:msgstr\[([0-9])\])$/', $keyword, $matches) ? true : false):
|
||||
return array('type' => 'msgstr', 'number' => $matches[1]);
|
||||
default:
|
||||
throw new CatalogParserException(
|
||||
$this->catalogPath,
|
||||
$this->lineNumber,
|
||||
$this->position - strlen($keyword),
|
||||
"\"$keyword\" is not a valid keyword"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Put char in front of the current stack
|
||||
*
|
||||
* @param string $char Char to be put in front
|
||||
*/
|
||||
protected function putCharInStack($char)
|
||||
{
|
||||
$this->position--;
|
||||
$this->stack = $char . $this->stack;
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
/* Icinga Web 2 | (c) 2016 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Module\Translation\Exception;
|
||||
|
||||
/**
|
||||
* Class CatalogParserException
|
||||
*
|
||||
* Will be thrown if CatalogParser finds a syntax error.
|
||||
*
|
||||
* @package Icinga\Module\Translation\Exception
|
||||
*/
|
||||
class CatalogParserException extends CatalogException
|
||||
{
|
||||
/**
|
||||
* Path of catalog file
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* Line where the exception appears
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $lineNumber;
|
||||
|
||||
/**
|
||||
* Position of character that causes the exception
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $position;
|
||||
|
||||
/**
|
||||
* CatalogParserException constructor
|
||||
*
|
||||
* @param string $path Path in which the exception appears
|
||||
* @param int $lineNumber Line in which the exception appears
|
||||
* @param int $position Position in which the exception appears
|
||||
* @param string $message The reason for the syntax error
|
||||
*/
|
||||
public function __construct($path, $lineNumber, $position, $message)
|
||||
{
|
||||
$this->path = $path;
|
||||
$this->lineNumber = $lineNumber;
|
||||
$this->position = $position;
|
||||
|
||||
parent::__construct(
|
||||
'Syntax error in file %s on line %s and position %s: %s',
|
||||
$path,
|
||||
$lineNumber,
|
||||
$position,
|
||||
$message
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return path in which the exception appears
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPath()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return line in which the exception appears
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getLineNumber()
|
||||
{
|
||||
return $this->lineNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return position in which the exception appears
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getPosition()
|
||||
{
|
||||
return $this->position;
|
||||
}
|
||||
}
|
@ -0,0 +1,579 @@
|
||||
<?php
|
||||
/* Icinga Web 2 | (c) 2016 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Tests\Icinga\Module\Translation\Catalog;
|
||||
|
||||
|
||||
use Icinga\Module\Translation\Exception\CatalogParserException;
|
||||
use Icinga\Module\Translation\Catalog\CatalogParser;
|
||||
use Icinga\Test\BaseTestCase;
|
||||
|
||||
class CatalogParserTest extends BaseTestCase
|
||||
{
|
||||
protected $tmpFilePath;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->tmpFilePath = tempnam(sys_get_temp_dir(), 'CatalogParserTest_TestFile');
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
unlink($this->tmpFilePath);
|
||||
}
|
||||
|
||||
protected function parseString($string)
|
||||
{
|
||||
file_put_contents($this->tmpFilePath, $string);
|
||||
return CatalogParser::parsePath($this->tmpFilePath);
|
||||
}
|
||||
|
||||
public function testWhetherAMessageContextIsParsedCorrectly()
|
||||
{
|
||||
$parserResult = $this->parseString('msgctxt "context of the message"');
|
||||
|
||||
$this->assertEquals(
|
||||
'context of the message',
|
||||
$parserResult[0]['msgctxt'],
|
||||
'CatalogParser does not parse a msgctxt correctly'
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhetherAnObsoleteMessageContextIsParsedCorrectly()
|
||||
{
|
||||
$parserResult = $this->parseString('#~ msgctxt "context of the message"');
|
||||
|
||||
$this->assertEquals(
|
||||
'context of the message',
|
||||
$parserResult[0]['msgctxt'],
|
||||
'CatalogParser does not parse a obsolete msgctxt correctly'
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhetherAPreviousMessageContextIsParsedCorrectly()
|
||||
{
|
||||
$parserResult = $this->parseString('#| msgctxt "previous context of the message"');
|
||||
|
||||
$this->assertEquals(
|
||||
'previous context of the message',
|
||||
$parserResult[0]['previous_msgctxt'],
|
||||
'CatalogParser does not parse a previous msgctxt correctly'
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhetherAMessageIdIsParsedCorrectly()
|
||||
{
|
||||
$parserResult = $this->parseString('msgid "this is a msgid"');
|
||||
|
||||
$this->assertEquals(
|
||||
'this is a msgid',
|
||||
$parserResult[0]['msgid'],
|
||||
'CatalogParser does not parse a msgid correctly'
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhetherAnObsoleteMessageIdIsParsedCorrectly()
|
||||
{
|
||||
$parserResult = $this->parseString('#~ msgid "this is a msgid"');
|
||||
|
||||
$this->assertEquals(
|
||||
'this is a msgid',
|
||||
$parserResult[0]['msgid'],
|
||||
'CatalogParser does not parse a obsolete msgid correctly'
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhetherAPreviousMessageIdIsParsedCorrectly()
|
||||
{
|
||||
$parserResult = $this->parseString('#| msgid "fuzzy id of the message"');
|
||||
|
||||
$this->assertEquals(
|
||||
'fuzzy id of the message',
|
||||
$parserResult[0]['previous_msgid'],
|
||||
'CatalogParser does not parse a previous msgid correctly'
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhetherAMessageStringIsParsedCorrectly()
|
||||
{
|
||||
$parserResult = $this->parseString('msgstr "translation"');
|
||||
|
||||
$this->assertEquals(
|
||||
'translation',
|
||||
$parserResult[0]['msgstr'][0],
|
||||
'CatalogParser does not parse a msgstr correctly'
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhetherAnObsoleteMessageStringIsParsedCorrectly()
|
||||
{
|
||||
$parserResult = $this->parseString('#~ msgstr "translation"');
|
||||
|
||||
$this->assertEquals(
|
||||
'translation',
|
||||
$parserResult[0]['msgstr'][0],
|
||||
'CatalogParser does not parse a obsolete msgstr correctly'
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhetherAPluralMessageIdIsParsedCorrectly()
|
||||
{
|
||||
$parserResult = $this->parseString('msgid_plural "id_plural"');
|
||||
|
||||
$this->assertEquals(
|
||||
'id_plural',
|
||||
$parserResult[0]['msgid_plural'],
|
||||
'CatalogParser does not parse a msgid_plural correctly'
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhetherAnObsoletePluralMessageIdIsParsedCorrectly()
|
||||
{
|
||||
$parserResult = $this->parseString('#~ msgid_plural "id_plural"');
|
||||
|
||||
$this->assertEquals(
|
||||
'id_plural',
|
||||
$parserResult[0]['msgid_plural'],
|
||||
'CatalogParser does not parse a obsolete msgid_plural correctly'
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhetherAPreviousPluralMessageIdIsParsedCorrectly()
|
||||
{
|
||||
$parserResult = $this->parseString('#| msgid_plural "id_plural"');
|
||||
|
||||
$this->assertEquals(
|
||||
'id_plural',
|
||||
$parserResult[0]['previous_msgid_plural'],
|
||||
'CatalogParser does not parse a previous msgid_plural correctly'
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhetherAPluralMessageStringIsParsedCorrectly()
|
||||
{
|
||||
$parserResult = $this->parseString(<<<EOF
|
||||
msgstr[0] "translation0"
|
||||
msgstr[1] "translation1"
|
||||
EOF
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
'translation0',
|
||||
$parserResult[0]['msgstr'][0],
|
||||
'CatalogParser does not parse a msgstr[0] correctly'
|
||||
);
|
||||
$this->assertEquals(
|
||||
'translation1',
|
||||
$parserResult[0]['msgstr'][1],
|
||||
'CatalogParser does not parse a msgstr[1] correctly'
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhetherAnObsoletePluralMessageStringIsParsedCorrectly()
|
||||
{
|
||||
$parserResult = $this->parseString(<<<EOF
|
||||
#~ msgstr[0] "translation0"
|
||||
#~ msgstr[1] "translation1"
|
||||
EOF
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
'translation0',
|
||||
$parserResult[0]['msgstr'][0],
|
||||
'CatalogParser does not parse a obsolete msgstr[0] correctly'
|
||||
);
|
||||
$this->assertEquals(
|
||||
'translation1',
|
||||
$parserResult[0]['msgstr'][1],
|
||||
'CatalogParser does not parse a obsolete msgstr[1] correctly'
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhetherAnObsoleteEntryIsCorrectlyIdentified()
|
||||
{
|
||||
$parserResult = $this->parseString('#~ msgid "translation"');
|
||||
|
||||
$this->assertTrue(
|
||||
$parserResult[0]['obsolete'],
|
||||
'CatalogParser does not identify obsolete entries correctly'
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhetherATranslatorCommentIsParsedCorrectly()
|
||||
{
|
||||
$parserResult = $this->parseString('# this is a translator comment');
|
||||
|
||||
$this->assertEquals(
|
||||
'this is a translator comment',
|
||||
$parserResult[0]['translator_comments'][0],
|
||||
'CatalogParser does not parse a translator comment correctly'
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhetherAExtractedCommentIsParsedCorrectly()
|
||||
{
|
||||
$parserResult = $this->parseString('#. this is a extracted comment');
|
||||
|
||||
$this->assertEquals(
|
||||
'this is a extracted comment',
|
||||
$parserResult[0]['extracted_comments'][0],
|
||||
'CatalogParser does not parse a extracted comment correctly'
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhetherASinglePathIsParsedCorrectly()
|
||||
{
|
||||
$parserResult = $this->parseString('#: /this/is/a/test/path:999');
|
||||
|
||||
$this->assertEquals(
|
||||
'/this/is/a/test/path:999',
|
||||
$parserResult[0]['paths'][0],
|
||||
'CatalogParser does not parse paths correctly'
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhetherMultiplePathsAreParsedCorrectly()
|
||||
{
|
||||
$parserResult = $this->parseString(<<<EOF
|
||||
#: /this/is/a/test/path:999 /this/is/another/test/path:99
|
||||
#: /this/is/still/another/test/path:9
|
||||
EOF
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
'/this/is/a/test/path:999',
|
||||
$parserResult[0]['paths'][0],
|
||||
'CatalogParser does not parse paths correctly'
|
||||
);
|
||||
$this->assertEquals(
|
||||
'/this/is/another/test/path:99',
|
||||
$parserResult[0]['paths'][1],
|
||||
'CatalogParser does not parse paths correctly'
|
||||
);
|
||||
$this->assertEquals(
|
||||
'/this/is/still/another/test/path:9',
|
||||
$parserResult[0]['paths'][2],
|
||||
'CatalogParser does not parse paths correctly'
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhetherASingleFlagIsParsedCorrectly()
|
||||
{
|
||||
$parserResult = $this->parseString('#, this-is-a-flag');
|
||||
|
||||
$this->assertEquals(
|
||||
'this-is-a-flag',
|
||||
$parserResult[0]['flags'][0],
|
||||
'CatalogParser does not parse flags correctly'
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhetherMultipleFlagsAreParsedCorrectly()
|
||||
{
|
||||
$parserResult = $this->parseString(<<<EOF
|
||||
#, this-is-a-flag, this-is-another-flag
|
||||
#, this-is-still-another-flag
|
||||
EOF
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
'this-is-a-flag',
|
||||
$parserResult[0]['flags'][0],
|
||||
'CatalogParser does not parse flags correctly'
|
||||
);
|
||||
$this->assertEquals(
|
||||
'this-is-another-flag',
|
||||
$parserResult[0]['flags'][1],
|
||||
'CatalogParser does not parse flags correctly'
|
||||
);
|
||||
$this->assertEquals(
|
||||
'this-is-still-another-flag',
|
||||
$parserResult[0]['flags'][2],
|
||||
'CatalogParser does not parse flags correctly'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testWhetherAExtractedCommentIsParsedCorrectly
|
||||
* @depends testWhetherAMessageIdIsParsedCorrectly
|
||||
* @depends testWhetherAMessageContextIsParsedCorrectly
|
||||
* @depends testWhetherAMessageStringIsParsedCorrectly
|
||||
* @depends testWhetherAPluralMessageStringIsParsedCorrectly
|
||||
*/
|
||||
public function testWhetherEscapedCharactersAreProperlyResolved()
|
||||
{
|
||||
$parserResult = $this->parseString(<<<EOT
|
||||
#. one line\\nanother line
|
||||
msgid "a\\nb"
|
||||
msgctxt "a\\r\\nb"
|
||||
msgstr "a\\\\\\tb"
|
||||
msgstr[1] "a\\""
|
||||
EOT
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
"one line\nanother line",
|
||||
$parserResult[0]['extracted_comments'][0],
|
||||
'CatalogParser does not properly resolve escaped characters in extracted comments'
|
||||
);
|
||||
$this->assertEquals(
|
||||
"a\nb",
|
||||
$parserResult[0]['msgid'],
|
||||
'CatalogParser does not properly resolve escaped characters in message ids'
|
||||
);
|
||||
$this->assertEquals(
|
||||
"a\r\nb",
|
||||
$parserResult[0]['msgctxt'],
|
||||
'CatalogParser does not properly resolve escaped characters in message contexts'
|
||||
);
|
||||
$this->assertEquals(
|
||||
"a\\\\\tb",
|
||||
$parserResult[0]['msgstr'][0],
|
||||
'CatalogParser does not properly resolve escaped characters in message strings'
|
||||
);
|
||||
$this->assertEquals(
|
||||
'a"',
|
||||
$parserResult[0]['msgstr'][1],
|
||||
'CatalogParser does not properly resolve escaped characters in plural message strings'
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhetherMissingKeywordsCauseAnError()
|
||||
{
|
||||
try {
|
||||
$this->parseString(' "string with type missing in front"');
|
||||
} catch (CatalogParserException $e) {
|
||||
$this->assertEquals(5, $e->getPosition(), 'CatalogParser reports incorrect error positions');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->fail('CatalogParser does not throw an exception if keyword is missing');
|
||||
}
|
||||
|
||||
public function testWhetherInvalidKeywordsCauseAnError()
|
||||
{
|
||||
try {
|
||||
$this->parseString('wrongkeyword "string with invalid type in front"');
|
||||
} catch (CatalogParserException $e) {
|
||||
$this->assertEquals(1, $e->getPosition(), 'CatalogParser reports incorrect error positions');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->fail('CatalogParser does not throw an exception if given keyword is wrong');
|
||||
}
|
||||
|
||||
public function testWhetherInvalidEmbeddedKeywordsCauseAnError()
|
||||
{
|
||||
try {
|
||||
$this->parseString('#| wrongkeyword "string with type missing in front"');
|
||||
} catch (CatalogParserException $e) {
|
||||
$this->assertEquals(4, $e->getPosition(), 'CatalogParser reports incorrect error positions');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->fail('CatalogParser does not throw an exception if given previous keyword is wrong');
|
||||
}
|
||||
|
||||
public function testWhetherSuperfluousQuotesCauseAnError()
|
||||
{
|
||||
try {
|
||||
$this->parseString('#| msgid "string with a superfluous " in it"');
|
||||
} catch (CatalogParserException $e) {
|
||||
$this->assertEquals(42, $e->getPosition(), 'CatalogParser reports incorrect error positions');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->fail('CatalogParser does not throw an exception if superfluous quotes exist');
|
||||
}
|
||||
|
||||
public function testWhetherMissingClosingQuotesCauseAnError()
|
||||
{
|
||||
try {
|
||||
$this->parseString('msgstr "string with missing closing quote');
|
||||
} catch (CatalogParserException $e) {
|
||||
$this->assertEquals(47, $e->getPosition(), 'CatalogParser reports incorrect error positions');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->fail('CatalogParser does not throw an exception if closing quote is missing');
|
||||
}
|
||||
|
||||
public function testWhetherMissingSpacesAfterAValidKeywordCauseAnError()
|
||||
{
|
||||
try {
|
||||
$this->parseString('msgstr');
|
||||
} catch (CatalogParserException $e) {
|
||||
$this->assertEquals(7, $e->getPosition(), 'CatalogParser reports incorrect error positions');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->fail('CatalogParser does not throw an exception if space is missing after keyword');
|
||||
}
|
||||
|
||||
public function testWhetherInvalidHashIdentifiersCauseAnError()
|
||||
{
|
||||
try {
|
||||
$this->parseString('#a');
|
||||
} catch (CatalogParserException $e) {
|
||||
$this->assertEquals(2, $e->getPosition(), 'CatalogParser reports incorrect error positions');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->fail('CatalogParser does not throw an exception if char after hash is wrong');
|
||||
}
|
||||
|
||||
public function testWhetherParserParsesAWholeFile()
|
||||
{
|
||||
$parserResult = $this->parseString(<<<EOT
|
||||
# TranslatorComment is here
|
||||
# and some more translator comments
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Header: Info\\n"
|
||||
"More header info: info\\n"
|
||||
"More header info: info\\n"
|
||||
"More header info: info\\n"
|
||||
|
||||
#. This is an extracted comment
|
||||
#: /this/is/a/path:123 /this/is/another/path:456
|
||||
#: /this/is/yet/another/path:789
|
||||
#, php-format, fuzzy
|
||||
msgctxt "Message context"
|
||||
#| msgid "To be translated"
|
||||
msgid "To "
|
||||
"translate"
|
||||
msgstr "Zu "
|
||||
"übersetzen"
|
||||
|
||||
# This is a comment for a normal entry
|
||||
msgid_plural "Translation for plural"
|
||||
msgstr[0] "Übersetzung für plural 1"
|
||||
msgstr[1] "Übersetzung für plural 2"
|
||||
|
||||
#~ msgid "Obsolete message id"
|
||||
#~ msgstr "Obsolete message string"
|
||||
EOT
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
"TranslatorComment is here",
|
||||
$parserResult[0]['translator_comments'][0],
|
||||
'CatalogParser does not properly parse the first line in translator comments in complete file test'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
"and some more translator comments",
|
||||
$parserResult[0]['translator_comments'][1],
|
||||
'CatalogParser does not properly parse the second line in translator comments in complete file test'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
"Header: Info\nMore header info: info\nMore header info: info\nMore header info: info\n",
|
||||
$parserResult[0]['msgstr'][0],
|
||||
'CatalogParser does not properly parse the header correctly in complete file test'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
"This is an extracted comment",
|
||||
$parserResult[1]['extracted_comments'][0],
|
||||
'CatalogParser does not properly parse extracted comments correctly in complete file test'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
"/this/is/a/path:123",
|
||||
$parserResult[1]['paths'][0],
|
||||
'CatalogParser does not properly parse the first path correctly in complete file test'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
"/this/is/another/path:456",
|
||||
$parserResult[1]['paths'][1],
|
||||
'CatalogParser does not properly parse the second path correctly in complete file test'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
"/this/is/yet/another/path:789",
|
||||
$parserResult[1]['paths'][2],
|
||||
'CatalogParser does not properly parse the third path correctly in complete file test'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
"php-format",
|
||||
$parserResult[1]['flags'][0],
|
||||
'CatalogParser does not properly parse the first flag correctly in complete file test'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
"fuzzy",
|
||||
$parserResult[1]['flags'][1],
|
||||
'CatalogParser does not properly parse the second flag correctly in complete file test'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
"Message context",
|
||||
$parserResult[1]['msgctxt'],
|
||||
'CatalogParser does not properly parse the message context correctly in complete file test'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
"To be translated",
|
||||
$parserResult[1]['previous_msgid'],
|
||||
'CatalogParser does not properly parse the previous message id correctly in complete file test'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
"To translate",
|
||||
$parserResult[1]['msgid'],
|
||||
'CatalogParser does not properly parse the message id correctly in complete file test'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
"Zu übersetzen",
|
||||
$parserResult[1]['msgstr'][0],
|
||||
'CatalogParser does not properly parse the message correctly in complete file test'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
"This is a comment for a normal entry",
|
||||
$parserResult[2]['translator_comments'][0],
|
||||
'CatalogParser does not properly parse the translator comments correctly in complete file test'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
"Translation for plural",
|
||||
$parserResult[2]['msgid_plural'],
|
||||
'CatalogParser does not properly parse message id plural correctly in complete file test'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
"Übersetzung für plural 1",
|
||||
$parserResult[2]['msgstr'][0],
|
||||
'CatalogParser does not properly parse the first plural message correctly in complete file test'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
"Übersetzung für plural 2",
|
||||
$parserResult[2]['msgstr'][1],
|
||||
'CatalogParser does not properly parse the second plural message correctly in complete file test'
|
||||
);
|
||||
|
||||
$this->assertTrue(
|
||||
$parserResult[3]['obsolete'],
|
||||
'CatalogParser does not set obsolete correctly in complete file test'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
"Obsolete message id",
|
||||
$parserResult[3]['msgid'],
|
||||
'CatalogParser does not properly parse obsolete message id correctly in complete file test'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
"Obsolete message string",
|
||||
$parserResult[3]['msgstr'][0],
|
||||
'CatalogParser does not properly parse obsolete message correctly in complete file test'
|
||||
);
|
||||
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user