mirror of
https://github.com/Icinga/icingaweb2.git
synced 2025-09-26 03:09:10 +02:00
parent
ab8793c69a
commit
af73da2437
269
modules/translation/library/Translation/Catalog/Catalog.php
Normal file
269
modules/translation/library/Translation/Catalog/Catalog.php
Normal file
@ -0,0 +1,269 @@
|
||||
<?php
|
||||
/* Icinga Web 2 | (c) 2016 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Module\Translation\Catalog;
|
||||
|
||||
use ArrayIterator;
|
||||
use DateTime;
|
||||
use Exception;
|
||||
use IteratorAggregate;
|
||||
use Icinga\Application\Benchmark;
|
||||
use Icinga\Exception\IcingaException;
|
||||
use Icinga\Web\FileCache;
|
||||
use Icinga\Module\Translation\Exception\CatalogEntryException;
|
||||
use Icinga\Module\Translation\Exception\CatalogException;
|
||||
use Icinga\Module\Translation\Exception\CatalogHeaderException;
|
||||
|
||||
/**
|
||||
* Class Catalog
|
||||
*
|
||||
* Provides a convenient interface to handle gettext PO files.
|
||||
*
|
||||
* @package Icinga\Module\Translation\Catalog
|
||||
*/
|
||||
class Catalog implements IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* Header for this Catalog
|
||||
*
|
||||
* @var CatalogHeader
|
||||
*/
|
||||
protected $header;
|
||||
|
||||
/**
|
||||
* Entries for this Catalog
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $entries;
|
||||
|
||||
/**
|
||||
* Create a new Catalog
|
||||
*
|
||||
* @param CatalogHeader $header
|
||||
* @param array $entries
|
||||
*/
|
||||
public function __construct(CatalogHeader $header, array $entries)
|
||||
{
|
||||
$this->header = $header;
|
||||
$this->entries = $entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a new Catalog from the given array of entries
|
||||
*
|
||||
* @param array $rawEntries
|
||||
*
|
||||
* @return Catalog
|
||||
*
|
||||
* @throws CatalogException
|
||||
*/
|
||||
public static function fromArray(array $rawEntries)
|
||||
{
|
||||
Benchmark::measure('Catalog::fromArray()');
|
||||
|
||||
$header = null;
|
||||
$entries = array();
|
||||
foreach ($rawEntries as $key => $rawEntry) {
|
||||
if (isset($rawEntry['msgid']) && empty($rawEntry['msgid'])) {
|
||||
$header = CatalogHeader::fromString($rawEntry['msgstr'][0]);
|
||||
if (isset($rawEntry['translator_comments'])) {
|
||||
$header->setCopyrightInformation($rawEntry['translator_comments']);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
$entries[] = CatalogEntry::fromArray($rawEntry);
|
||||
} catch (CatalogEntryException $e) {
|
||||
throw $e->setEntryNumber($header ? $key : $key + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($header === null) {
|
||||
throw new CatalogHeaderException('Header not found');
|
||||
}
|
||||
|
||||
return new Catalog($header, $entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a new Catalog from the given path
|
||||
*
|
||||
* @param string $catalogPath
|
||||
* @param bool $cache Whether to utilize the cache or not
|
||||
*
|
||||
* @return Catalog
|
||||
*
|
||||
* @throws CatalogException
|
||||
*/
|
||||
public static function fromPath($catalogPath, $cache = false)
|
||||
{
|
||||
Benchmark::measure('Catalog::fromPath()');
|
||||
|
||||
if ($cache) {
|
||||
$catalog = static::fromCache($catalogPath);
|
||||
if ($catalog !== null) {
|
||||
return $catalog;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$entries = CatalogParser::parsePath($catalogPath);
|
||||
$catalog = Catalog::fromArray($entries);
|
||||
} catch (CatalogHeaderException $e) {
|
||||
throw new CatalogException(
|
||||
'An exception occurred while reading "' . $catalogPath . '": ' . $e->getMessage()
|
||||
);
|
||||
} catch (CatalogEntryException $e) {
|
||||
throw new CatalogException(
|
||||
'Invalid entry #' . $e->getEntryNumber() . ' in "' . $catalogPath . '": ' . $e->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
if ($cache) {
|
||||
static::cacheEntries($catalogPath, $entries);
|
||||
}
|
||||
|
||||
return $catalog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to create and return a new Catalog from the given cached path
|
||||
*
|
||||
* @param string $catalogPath
|
||||
*
|
||||
* @return Catalog|null Null in case the path has not been cached yet
|
||||
*/
|
||||
protected static function fromCache($catalogPath)
|
||||
{
|
||||
Benchmark::measure('Catalog::fromCache()');
|
||||
|
||||
$eTag = FileCache::etagForFiles($catalogPath);
|
||||
$cacheFile = 'translation-' . $eTag . '.po.json';
|
||||
$cache = FileCache::instance();
|
||||
if ($cache->has($cacheFile)) {
|
||||
return static::fromArray(json_decode($cache->get($cacheFile), true));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache the given catalog entries
|
||||
*
|
||||
* @param string $catalogPath The path to the po file which has initially been parsed
|
||||
* @param array $entries The entries as produced by CatalogParser::parsePath()
|
||||
*/
|
||||
protected static function cacheEntries($catalogPath, array $entries)
|
||||
{
|
||||
$eTag = FileCache::etagForFiles($catalogPath);
|
||||
$cacheFile = 'translation-' . $eTag . '.po.json';
|
||||
FileCache::instance()->store($cacheFile, json_encode($entries));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a iterator for this catalogs entries
|
||||
*
|
||||
* @return ArrayIterator
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
return new ArrayIterator($this->entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the given header exists
|
||||
*
|
||||
* @param string $name The name of the header
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasHeader($name)
|
||||
{
|
||||
return isset($this->header[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of the given header
|
||||
*
|
||||
* @param string $name The name of the header
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getHeader($name)
|
||||
{
|
||||
return $this->header[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the given header to the given value
|
||||
*
|
||||
* @param string $name The name of the header
|
||||
* @param string $value The value of the header
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setHeader($name, $value)
|
||||
{
|
||||
$this->header[$name] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the given header
|
||||
*
|
||||
* @param string $name The name of the header
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function removeHeader($name)
|
||||
{
|
||||
unset ($this->header[$name]);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the creation date of this Catalog
|
||||
*
|
||||
* @return DateTime
|
||||
*/
|
||||
public function creationDate()
|
||||
{
|
||||
return date_create_from_format(CatalogHeader::DATETIME_FORMAT, $this->getHeader('POT-Creation-Date'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the revision date of this Catalog
|
||||
*
|
||||
* @return DateTime
|
||||
*/
|
||||
public function revisionDate()
|
||||
{
|
||||
return date_create_from_format(CatalogHeader::DATETIME_FORMAT, $this->getHeader('PO-Revision-Date'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Render and return this catalog as a string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
$renderedCatalog = $this->header->render();
|
||||
foreach ($this->entries as $entry) {
|
||||
$renderedCatalog .= "\n\n" . $entry->render();
|
||||
}
|
||||
|
||||
return $renderedCatalog;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Catalog::render()
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
try {
|
||||
return $this->render();
|
||||
} catch (Exception $e) {
|
||||
return 'Failed to render Catalog: ' . IcingaException::describe($e);
|
||||
}
|
||||
}
|
||||
}
|
654
modules/translation/library/Translation/Catalog/CatalogEntry.php
Normal file
654
modules/translation/library/Translation/Catalog/CatalogEntry.php
Normal file
@ -0,0 +1,654 @@
|
||||
<?php
|
||||
/* Icinga Web 2 | (c) 2016 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Module\Translation\Catalog;
|
||||
|
||||
use Exception;
|
||||
use Icinga\Exception\IcingaException;
|
||||
use Icinga\Module\Translation\Exception\CatalogEntryException;
|
||||
|
||||
/**
|
||||
* Class CatalogEntry
|
||||
*
|
||||
* Provides a convenient interface to handle entries of gettext PO files.
|
||||
*
|
||||
* @package Icinga\Module\Translation\Catalog
|
||||
*/
|
||||
class CatalogEntry
|
||||
{
|
||||
/**
|
||||
* Maximal amount of chars per line
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const MAX_LINE_LENGTH = 80;
|
||||
|
||||
/**
|
||||
* Regex that matches php-format placeholders
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const PHP_FORMAT_REGEX = '/(?<!%)%(?:\d+\$)?[+-]?(?:[ 0]|\'.)?-?\d*(?:\.\d+)?[bcdeEufFgGosxX]/';
|
||||
|
||||
/**
|
||||
* Obsolete tag for this CatalogEntry
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $obsolete;
|
||||
|
||||
/**
|
||||
* Context for this CatalogEntry
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $messageContext;
|
||||
|
||||
/**
|
||||
* Untranslated message for this CatalogEntry
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $messageId;
|
||||
|
||||
/**
|
||||
* Untranslated plural messages for this CatalogEntry
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $messageIdPlural;
|
||||
|
||||
/**
|
||||
* Translated message for this CatalogEntry
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $message;
|
||||
|
||||
/**
|
||||
* Translated plural messages for this CatalogEntry
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $messagePlurals;
|
||||
|
||||
/**
|
||||
* Context of the message before the last change for this CatalogEntry
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $previousMessageContext;
|
||||
|
||||
/**
|
||||
* Untranslated message before the last change for this CatalogEntry
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $previousMessageId;
|
||||
|
||||
/**
|
||||
* Untranslated plural messages before the last change for this CatalogEntry
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $previousMessageIdPlural;
|
||||
|
||||
/**
|
||||
* Translator comments for this CatalogEntry
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $translatorComments;
|
||||
|
||||
/**
|
||||
* Extracted comments for this CatalogEntry
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $extractedComments;
|
||||
|
||||
/**
|
||||
* Paths in which this CatalogEntry is used
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $paths;
|
||||
|
||||
/**
|
||||
* Information about the translation status and format of this CatalogEntry
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $flags;
|
||||
|
||||
/**
|
||||
* Create a new CatalogEntry
|
||||
*
|
||||
* @param string $messageId The untranslated message
|
||||
* @param string $message The translated message
|
||||
* @param string $messageContext The message's context
|
||||
*/
|
||||
public function __construct($messageId, $message, $messageContext = null)
|
||||
{
|
||||
$this->messageId = $messageId;
|
||||
$this->message = $message;
|
||||
$this->messageContext = $messageContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a new CatalogEntry from the given array
|
||||
*
|
||||
* @param array $entry
|
||||
*
|
||||
* @return CatalogEntry
|
||||
*
|
||||
* @throws CatalogEntryException
|
||||
*/
|
||||
public static function fromArray(array $entry)
|
||||
{
|
||||
if (! isset($entry['msgid']) || ! isset($entry['msgstr'][0])) {
|
||||
throw new CatalogEntryException('Missing msgid or msgstr');
|
||||
}
|
||||
|
||||
$catalogEntry = new static(
|
||||
$entry['msgid'],
|
||||
$entry['msgstr'][0],
|
||||
isset($entry['msgctxt']) ? $entry['msgctxt'] : null
|
||||
);
|
||||
|
||||
foreach ($entry as $key => $value)
|
||||
{
|
||||
switch ($key)
|
||||
{
|
||||
case 'obsolete':
|
||||
$catalogEntry->setObsolete($value);
|
||||
break;
|
||||
case 'msgid_plural':
|
||||
$catalogEntry->setMessageIdPlural($value);
|
||||
break;
|
||||
case 'msgstr':
|
||||
unset($value[0]);
|
||||
if (! empty($value)) {
|
||||
$catalogEntry->setMessagePlurals($value);
|
||||
}
|
||||
break;
|
||||
case 'previous_msgctxt':
|
||||
$catalogEntry->setPreviousMessageContext($value);
|
||||
break;
|
||||
case 'previous_msgid':
|
||||
$catalogEntry->setPreviousMessageId($value);
|
||||
break;
|
||||
case 'previous_msgid_plural':
|
||||
$catalogEntry->setPreviousMessageIdPlural($value);
|
||||
break;
|
||||
case 'translator_comments':
|
||||
$catalogEntry->setTranslatorComments($value);
|
||||
break;
|
||||
case 'extracted_comments':
|
||||
$catalogEntry->setExtractedComments($value);
|
||||
break;
|
||||
case 'paths':
|
||||
$catalogEntry->setPaths($value);
|
||||
break;
|
||||
case 'flags':
|
||||
$catalogEntry->setFlags($value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $catalogEntry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the message for this CatalogEntry
|
||||
*
|
||||
* @param string $message
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setMessage($message)
|
||||
{
|
||||
$this->message = $message;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the message for this CatalogEntry
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMessage()
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the message id for this CatalogEntry
|
||||
*
|
||||
* @param string $messageId
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setMessageId($messageId)
|
||||
{
|
||||
$this->messageId = $messageId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the message id for this CatalogEntry
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMessageId()
|
||||
{
|
||||
return $this->messageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the message context for this CatalogEntry
|
||||
*
|
||||
* @param string $messageContext
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setMessageContext($messageContext)
|
||||
{
|
||||
$this->messageContext = $messageContext;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the message context for this CatalogEntry
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMessageContext()
|
||||
{
|
||||
return $this->messageContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether this CatalogEntry is obsolete
|
||||
*
|
||||
* @param bool $state
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setObsolete($state = true)
|
||||
{
|
||||
$this->obsolete = $state;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this CatalogEntry is obsolete
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getObsolete()
|
||||
{
|
||||
return $this->obsolete;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the plural message id for this CatalogEntry
|
||||
*
|
||||
* @param string $messageIdPlural
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setMessageIdPlural($messageIdPlural)
|
||||
{
|
||||
$this->messageIdPlural = $messageIdPlural;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the plural message id for this CatalogEntry
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMessageIdPlural()
|
||||
{
|
||||
return $this->messageIdPlural;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the plural messages for this CatalogEntry
|
||||
*
|
||||
* @param array $messagePlurals
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setMessagePlurals(array $messagePlurals)
|
||||
{
|
||||
$this->messagePlurals = $messagePlurals;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the plural messages for this CatalogEntry
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getMessagePlurals()
|
||||
{
|
||||
return $this->messagePlurals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the previous message context for this CatalogEntry
|
||||
*
|
||||
* @param string $previousMessageContext
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setPreviousMessageContext($previousMessageContext)
|
||||
{
|
||||
$this->previousMessageContext = $previousMessageContext;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the previous message context for this CatalogEntry
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPreviousMessageContext()
|
||||
{
|
||||
return $this->previousMessageContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the previous message id for this CatalogEntry
|
||||
*
|
||||
* @param string $previousMessageId
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setPreviousMessageId($previousMessageId)
|
||||
{
|
||||
$this->previousMessageId = $previousMessageId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the previous message id for this CatalogEntry
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPreviousMessageId()
|
||||
{
|
||||
return $this->previousMessageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the previous plural message id for this CatalogEntry
|
||||
*
|
||||
* @param string $previousMessageIdPlural
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setPreviousMessageIdPlural($previousMessageIdPlural)
|
||||
{
|
||||
$this->previousMessageIdPlural = $previousMessageIdPlural;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the previous plural message id for this CatalogEntry
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPreviousMessageIdPlural()
|
||||
{
|
||||
return $this->previousMessageIdPlural;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set translator comments for this CatalogEntry
|
||||
*
|
||||
* @param array $translatorComments
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setTranslatorComments(array $translatorComments)
|
||||
{
|
||||
$this->translatorComments = $translatorComments;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return translator comments for this CatalogEntry
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTranslatorComments()
|
||||
{
|
||||
return $this->translatorComments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set extracted comments for this CatalogEntry
|
||||
*
|
||||
* @param array $extractedComments
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setExtractedComments(array $extractedComments)
|
||||
{
|
||||
$this->extractedComments = $extractedComments;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return extracted comments for this CatalogEntry
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getExtractedComments()
|
||||
{
|
||||
return $this->extractedComments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set paths for this CatalogEntry
|
||||
*
|
||||
* @param array $paths
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setPaths(array $paths)
|
||||
{
|
||||
$this->paths = $paths;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return paths for this CatalogEntry
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPaths()
|
||||
{
|
||||
return $this->paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set flags for this CatalogEntry
|
||||
*
|
||||
* @param array $flags
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setFlags(array $flags)
|
||||
{
|
||||
$this->flags = $flags;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return flags for this CatalogEntry
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFlags()
|
||||
{
|
||||
return $this->flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this CatalogEntry is translated
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isTranslated()
|
||||
{
|
||||
return ! empty($this->message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this CatalogEntry is fuzzy
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isFuzzy()
|
||||
{
|
||||
return ! empty($this->flags) && in_array('fuzzy', $this->flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this CatalogEntry is faulty
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isFaulty()
|
||||
{
|
||||
if ($this->getMessage()) {
|
||||
$numberOfPlaceholdersInId = preg_match_all(static::PHP_FORMAT_REGEX, $this->messageId, $_);
|
||||
$numberOfPlaceholdersInMessage = preg_match_all(static::PHP_FORMAT_REGEX, $this->message, $_);
|
||||
if ($numberOfPlaceholdersInId !== $numberOfPlaceholdersInMessage) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->getMessageIdPlural()) {
|
||||
$numberOfPlaceholdersInIdPlural = preg_match_all(static::PHP_FORMAT_REGEX, $this->messageIdPlural, $_);
|
||||
foreach ($this->messagePlurals as $value) {
|
||||
$numberOfPlaceholdersInMessagePlural = preg_match_all(static::PHP_FORMAT_REGEX, $value, $_);
|
||||
if ($numberOfPlaceholdersInIdPlural !== $numberOfPlaceholdersInMessagePlural) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this CatalogEntry is obsolete
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isObsolete()
|
||||
{
|
||||
return $this->obsolete;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render and return this CatalogEntry as string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
$entries = array_merge(
|
||||
array_map(function ($value) { return '# ' . $value; }, $this->translatorComments ?: array()),
|
||||
array_map(function ($value) { return '#. ' . $value; }, $this->extractedComments ?: array()),
|
||||
array_map(function ($value) { return '#: ' . $value; }, $this->paths ?: array()),
|
||||
array_map(function ($value) { return '#, ' . $value; }, $this->flags ?: array()),
|
||||
$this->renderAttribute('#| msgctxt ', $this->previousMessageContext, '#| '),
|
||||
$this->renderAttribute('#| msgid ', $this->previousMessageId, '#| '),
|
||||
$this->renderAttribute('#| msgid_plural ', $this->previousMessageIdPlural, '#| '),
|
||||
$this->renderAttribute('msgctxt ', $this->messageContext),
|
||||
$this->renderAttribute('msgid ', $this->messageId),
|
||||
$this->renderAttribute('msgid_plural ', $this->messageIdPlural)
|
||||
);
|
||||
|
||||
if (! empty($this->messagePlurals)) {
|
||||
$entries = array_merge($entries, $this->renderAttribute('msgstr[0] ', $this->message));
|
||||
foreach ($this->messagePlurals as $key => $value)
|
||||
{
|
||||
$entries = array_merge($entries, $this->renderAttribute('msgstr[' . $key . '] ', $value));
|
||||
}
|
||||
} else {
|
||||
$entries = array_merge($entries, $this->renderAttribute('msgstr ', $this->message));
|
||||
}
|
||||
|
||||
return implode("\n", $entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reformat the given string to fit line length limitation and .po format and add quotes
|
||||
*
|
||||
* @param string $attributePrefix The attribute prefix that will be placed in front of the string
|
||||
* @param string $string The string to split
|
||||
* @param string $lineContinuationPrefix The prefix that will be placed in front of the multi lines of the string
|
||||
*
|
||||
* @return array Returns an array of split lines
|
||||
*/
|
||||
public function renderAttribute($attributePrefix, $string, $lineContinuationPrefix = '')
|
||||
{
|
||||
if (empty($string)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$string = strtr($string, array_flip(CatalogParser::$escapedChars));
|
||||
|
||||
$attributePrefix = ($this->isObsolete() ? ('#~ ' . $attributePrefix) : $attributePrefix);
|
||||
$oneLine = $attributePrefix . '"' . $string . '"';
|
||||
if (strlen($oneLine) <= static::MAX_LINE_LENGTH) {
|
||||
return array($oneLine);
|
||||
}
|
||||
|
||||
$splitLines = array($attributePrefix . '""');
|
||||
$lastSpace = 0;
|
||||
$lastCut = 0;
|
||||
$additionalChars = strlen($lineContinuationPrefix) + 2;
|
||||
for ($i = 0; $i < strlen($string); $i++)
|
||||
{
|
||||
if ($i - $lastCut + $additionalChars === static::MAX_LINE_LENGTH) {
|
||||
$splitLines[] = ($this->isObsolete() ? '#~ ' : '')
|
||||
. $lineContinuationPrefix
|
||||
. '"'
|
||||
. substr($string, $lastCut, $lastSpace - $lastCut + 1)
|
||||
. '"';
|
||||
$lastCut = $lastSpace + 1;
|
||||
}
|
||||
|
||||
if ($string[$i] === ' ') {
|
||||
$lastSpace = $i;
|
||||
}
|
||||
}
|
||||
|
||||
$splitLines[] = ($this->isObsolete() ? '#~ ' : '')
|
||||
. $lineContinuationPrefix
|
||||
. '"'
|
||||
. substr($string, $lastCut)
|
||||
. '"';
|
||||
return $splitLines;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see CatalogEntry::render()
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
try {
|
||||
return $this->render();
|
||||
} catch (Exception $e) {
|
||||
return 'Failed to render CatalogEntry: ' . IcingaException::describe($e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,192 @@
|
||||
<?php
|
||||
/* Icinga Web 2 | (c) 2016 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Module\Translation\Catalog;
|
||||
|
||||
use ArrayAccess;
|
||||
use Exception;
|
||||
use Icinga\Exception\IcingaException;
|
||||
use Icinga\Module\Translation\Exception\CatalogHeaderException;
|
||||
|
||||
/**
|
||||
* Class CatalogHeader
|
||||
*
|
||||
* Provides a convenient interface to handle headers of gettext PO files.
|
||||
*
|
||||
* @package Icinga\Module\Translation\Catalog
|
||||
*/
|
||||
class CatalogHeader implements ArrayAccess
|
||||
{
|
||||
/**
|
||||
* The format used in header entries to represent date and time
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const DATETIME_FORMAT = 'Y-m-d H:iO';
|
||||
|
||||
/**
|
||||
* The entries of this CatalogHeader
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $headers;
|
||||
|
||||
/**
|
||||
* The copyright information for this CatalogHeader
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $copyrightInformation;
|
||||
|
||||
/**
|
||||
* Create a new CatalogHeader
|
||||
*
|
||||
* @param array $headers
|
||||
*/
|
||||
public function __construct(array $headers)
|
||||
{
|
||||
$this->headers = $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a new CatalogHeader from the given string
|
||||
*
|
||||
* @param string $header
|
||||
*
|
||||
* @return CatalogHeader
|
||||
*
|
||||
* @throws CatalogHeaderException
|
||||
*/
|
||||
public static function fromString($header)
|
||||
{
|
||||
$lines = preg_split(
|
||||
'/\n(?=\S+: )/',
|
||||
substr($header, -1) === "\n"
|
||||
? substr($header, 0, -1)
|
||||
: $header
|
||||
);
|
||||
|
||||
$headers = array();
|
||||
foreach ($lines as $line)
|
||||
{
|
||||
try {
|
||||
list($key, $value) = explode(': ', $line, 2);
|
||||
} catch (Exception $_) {
|
||||
throw new CatalogHeaderException('Missing ": " in "' . $line . '"');
|
||||
}
|
||||
|
||||
$headers[$key] = $value;
|
||||
}
|
||||
|
||||
return new CatalogHeader($headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set copyright information for this CatalogHeader
|
||||
*
|
||||
* @param array $copyrightInformation
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setCopyrightInformation($copyrightInformation)
|
||||
{
|
||||
$this->copyrightInformation = $copyrightInformation;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return copyright information for this CatalogHeader
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCopyrightInformation()
|
||||
{
|
||||
return $this->copyrightInformation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the given header exists
|
||||
*
|
||||
* @param string $name The name of the header
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($name)
|
||||
{
|
||||
return isset($this->headers[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of the given header
|
||||
*
|
||||
* @param string $name The name of the header
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function offsetGet($name)
|
||||
{
|
||||
return $this->headers[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the given header to the given value
|
||||
*
|
||||
* @param string $name The name of the header
|
||||
* @param string $value The value of the header
|
||||
*/
|
||||
public function offsetSet($name, $value)
|
||||
{
|
||||
$this->headers[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unset the given header
|
||||
*
|
||||
* @param string $name The name of the header
|
||||
*/
|
||||
public function offsetUnset($name)
|
||||
{
|
||||
unset($this->headers[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render and return this CatalogHeader as string
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws CatalogHeaderException
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
if (empty($this->headers)) {
|
||||
throw new CatalogHeaderException('No headers to render');
|
||||
}
|
||||
|
||||
$entries = array();
|
||||
if (! empty($this->copyrightInformation)) {
|
||||
foreach ($this->copyrightInformation as $value) {
|
||||
$entries[] = '# ' . $value;
|
||||
}
|
||||
}
|
||||
|
||||
$entries[] = "msgid \"\"\nmsgstr \"\"";
|
||||
foreach ($this->headers as $key => $value)
|
||||
{
|
||||
$entries[] = '"' . strtr(sprintf('%s: %s', $key, $value), array_flip(CatalogParser::$escapedChars)) . '\n"';
|
||||
}
|
||||
|
||||
return implode("\n", $entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see CatalogHeader::render()
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
try {
|
||||
return $this->render();
|
||||
} catch (Exception $e) {
|
||||
return 'Failed to render CatalogHeader: ' . IcingaException::describe($e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/* Icinga Web 2 | (c) 2016 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Module\Translation\Exception;
|
||||
|
||||
/**
|
||||
* Class CatalogEntryException
|
||||
*
|
||||
* Will be thrown if catalog entry related errors are encountered.
|
||||
*
|
||||
* @package Icinga\Module\Translation\Exception
|
||||
*/
|
||||
class CatalogEntryException extends CatalogException
|
||||
{
|
||||
/**
|
||||
* Number of the faulty entry
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $entryNumber;
|
||||
|
||||
/**
|
||||
* Set the number of the faulty entry
|
||||
*
|
||||
* @param int $entryNumber
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setEntryNumber($entryNumber)
|
||||
{
|
||||
$this->entryNumber = $entryNumber;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of the faulty entry
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getEntryNumber()
|
||||
{
|
||||
return $this->entryNumber;
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
/* Icinga Web 2 | (c) 2016 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Module\Translation\Exception;
|
||||
|
||||
use Icinga\Exception\IcingaException;
|
||||
|
||||
/**
|
||||
* Class CatalogException
|
||||
*
|
||||
* Will be thrown if Catalog encounters an error.
|
||||
*
|
||||
* @package Icinga\Module\Translation\Exception
|
||||
*/
|
||||
class CatalogException extends IcingaException
|
||||
{
|
||||
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
/* Icinga Web 2 | (c) 2016 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Module\Translation\Exception;
|
||||
|
||||
/**
|
||||
* Class CatalogHeaderException
|
||||
*
|
||||
* Will be thrown if catalog header related errors are encountered.
|
||||
*
|
||||
* @package Icinga\Module\Translation\Exception
|
||||
*/
|
||||
class CatalogHeaderException extends CatalogException
|
||||
{
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user