first commit
This commit is contained in:
57
vendor/symfony/translation/Loader/ArrayLoader.php
vendored
Normal file
57
vendor/symfony/translation/Loader/ArrayLoader.php
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Translation\Loader;
|
||||
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
|
||||
/**
|
||||
* ArrayLoader loads translations from a PHP array.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ArrayLoader implements LoaderInterface
|
||||
{
|
||||
public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue
|
||||
{
|
||||
$resource = $this->flatten($resource);
|
||||
$catalogue = new MessageCatalogue($locale);
|
||||
$catalogue->add($resource, $domain);
|
||||
|
||||
return $catalogue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flattens an nested array of translations.
|
||||
*
|
||||
* The scheme used is:
|
||||
* 'key' => ['key2' => ['key3' => 'value']]
|
||||
* Becomes:
|
||||
* 'key.key2.key3' => 'value'
|
||||
*/
|
||||
private function flatten(array $messages): array
|
||||
{
|
||||
$result = [];
|
||||
foreach ($messages as $key => $value) {
|
||||
if (\is_array($value)) {
|
||||
foreach ($this->flatten($value) as $k => $v) {
|
||||
if (null !== $v) {
|
||||
$result[$key.'.'.$k] = $v;
|
||||
}
|
||||
}
|
||||
} elseif (null !== $value) {
|
||||
$result[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
64
vendor/symfony/translation/Loader/CsvFileLoader.php
vendored
Normal file
64
vendor/symfony/translation/Loader/CsvFileLoader.php
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Translation\Loader;
|
||||
|
||||
use Symfony\Component\Translation\Exception\NotFoundResourceException;
|
||||
|
||||
/**
|
||||
* CsvFileLoader loads translations from CSV files.
|
||||
*
|
||||
* @author Saša Stamenković <umpirsky@gmail.com>
|
||||
*/
|
||||
class CsvFileLoader extends FileLoader
|
||||
{
|
||||
private string $delimiter = ';';
|
||||
private string $enclosure = '"';
|
||||
private string $escape = '\\';
|
||||
|
||||
protected function loadResource(string $resource): array
|
||||
{
|
||||
$messages = [];
|
||||
|
||||
try {
|
||||
$file = new \SplFileObject($resource, 'rb');
|
||||
} catch (\RuntimeException $e) {
|
||||
throw new NotFoundResourceException(sprintf('Error opening file "%s".', $resource), 0, $e);
|
||||
}
|
||||
|
||||
$file->setFlags(\SplFileObject::READ_CSV | \SplFileObject::SKIP_EMPTY);
|
||||
$file->setCsvControl($this->delimiter, $this->enclosure, $this->escape);
|
||||
|
||||
foreach ($file as $data) {
|
||||
if (false === $data) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!str_starts_with($data[0], '#') && isset($data[1]) && 2 === \count($data)) {
|
||||
$messages[$data[0]] = $data[1];
|
||||
}
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the delimiter, enclosure, and escape character for CSV.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setCsvControl(string $delimiter = ';', string $enclosure = '"', string $escape = '\\')
|
||||
{
|
||||
$this->delimiter = $delimiter;
|
||||
$this->enclosure = $enclosure;
|
||||
$this->escape = $escape;
|
||||
}
|
||||
}
|
57
vendor/symfony/translation/Loader/FileLoader.php
vendored
Normal file
57
vendor/symfony/translation/Loader/FileLoader.php
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Translation\Loader;
|
||||
|
||||
use Symfony\Component\Config\Resource\FileResource;
|
||||
use Symfony\Component\Translation\Exception\InvalidResourceException;
|
||||
use Symfony\Component\Translation\Exception\NotFoundResourceException;
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
|
||||
/**
|
||||
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
|
||||
*/
|
||||
abstract class FileLoader extends ArrayLoader
|
||||
{
|
||||
public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue
|
||||
{
|
||||
if (!stream_is_local($resource)) {
|
||||
throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
|
||||
}
|
||||
|
||||
if (!file_exists($resource)) {
|
||||
throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
|
||||
}
|
||||
|
||||
$messages = $this->loadResource($resource);
|
||||
|
||||
// empty resource
|
||||
$messages ??= [];
|
||||
|
||||
// not an array
|
||||
if (!\is_array($messages)) {
|
||||
throw new InvalidResourceException(sprintf('Unable to load file "%s".', $resource));
|
||||
}
|
||||
|
||||
$catalogue = parent::load($messages, $locale, $domain);
|
||||
|
||||
if (class_exists(FileResource::class)) {
|
||||
$catalogue->addResource(new FileResource($resource));
|
||||
}
|
||||
|
||||
return $catalogue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidResourceException if stream content has an invalid format
|
||||
*/
|
||||
abstract protected function loadResource(string $resource): array;
|
||||
}
|
58
vendor/symfony/translation/Loader/IcuDatFileLoader.php
vendored
Normal file
58
vendor/symfony/translation/Loader/IcuDatFileLoader.php
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Translation\Loader;
|
||||
|
||||
use Symfony\Component\Config\Resource\FileResource;
|
||||
use Symfony\Component\Translation\Exception\InvalidResourceException;
|
||||
use Symfony\Component\Translation\Exception\NotFoundResourceException;
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
|
||||
/**
|
||||
* IcuResFileLoader loads translations from a resource bundle.
|
||||
*
|
||||
* @author stealth35
|
||||
*/
|
||||
class IcuDatFileLoader extends IcuResFileLoader
|
||||
{
|
||||
public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue
|
||||
{
|
||||
if (!stream_is_local($resource.'.dat')) {
|
||||
throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
|
||||
}
|
||||
|
||||
if (!file_exists($resource.'.dat')) {
|
||||
throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
|
||||
}
|
||||
|
||||
try {
|
||||
$rb = new \ResourceBundle($locale, $resource);
|
||||
} catch (\Exception) {
|
||||
$rb = null;
|
||||
}
|
||||
|
||||
if (!$rb) {
|
||||
throw new InvalidResourceException(sprintf('Cannot load resource "%s".', $resource));
|
||||
} elseif (intl_is_failure($rb->getErrorCode())) {
|
||||
throw new InvalidResourceException($rb->getErrorMessage(), $rb->getErrorCode());
|
||||
}
|
||||
|
||||
$messages = $this->flatten($rb);
|
||||
$catalogue = new MessageCatalogue($locale);
|
||||
$catalogue->add($messages, $domain);
|
||||
|
||||
if (class_exists(FileResource::class)) {
|
||||
$catalogue->addResource(new FileResource($resource.'.dat'));
|
||||
}
|
||||
|
||||
return $catalogue;
|
||||
}
|
||||
}
|
86
vendor/symfony/translation/Loader/IcuResFileLoader.php
vendored
Normal file
86
vendor/symfony/translation/Loader/IcuResFileLoader.php
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Translation\Loader;
|
||||
|
||||
use Symfony\Component\Config\Resource\DirectoryResource;
|
||||
use Symfony\Component\Translation\Exception\InvalidResourceException;
|
||||
use Symfony\Component\Translation\Exception\NotFoundResourceException;
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
|
||||
/**
|
||||
* IcuResFileLoader loads translations from a resource bundle.
|
||||
*
|
||||
* @author stealth35
|
||||
*/
|
||||
class IcuResFileLoader implements LoaderInterface
|
||||
{
|
||||
public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue
|
||||
{
|
||||
if (!stream_is_local($resource)) {
|
||||
throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
|
||||
}
|
||||
|
||||
if (!is_dir($resource)) {
|
||||
throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
|
||||
}
|
||||
|
||||
try {
|
||||
$rb = new \ResourceBundle($locale, $resource);
|
||||
} catch (\Exception) {
|
||||
$rb = null;
|
||||
}
|
||||
|
||||
if (!$rb) {
|
||||
throw new InvalidResourceException(sprintf('Cannot load resource "%s".', $resource));
|
||||
} elseif (intl_is_failure($rb->getErrorCode())) {
|
||||
throw new InvalidResourceException($rb->getErrorMessage(), $rb->getErrorCode());
|
||||
}
|
||||
|
||||
$messages = $this->flatten($rb);
|
||||
$catalogue = new MessageCatalogue($locale);
|
||||
$catalogue->add($messages, $domain);
|
||||
|
||||
if (class_exists(DirectoryResource::class)) {
|
||||
$catalogue->addResource(new DirectoryResource($resource));
|
||||
}
|
||||
|
||||
return $catalogue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flattens an ResourceBundle.
|
||||
*
|
||||
* The scheme used is:
|
||||
* key { key2 { key3 { "value" } } }
|
||||
* Becomes:
|
||||
* 'key.key2.key3' => 'value'
|
||||
*
|
||||
* This function takes an array by reference and will modify it
|
||||
*
|
||||
* @param \ResourceBundle $rb The ResourceBundle that will be flattened
|
||||
* @param array $messages Used internally for recursive calls
|
||||
* @param string|null $path Current path being parsed, used internally for recursive calls
|
||||
*/
|
||||
protected function flatten(\ResourceBundle $rb, array &$messages = [], ?string $path = null): array
|
||||
{
|
||||
foreach ($rb as $key => $value) {
|
||||
$nodePath = $path ? $path.'.'.$key : $key;
|
||||
if ($value instanceof \ResourceBundle) {
|
||||
$this->flatten($value, $messages, $nodePath);
|
||||
} else {
|
||||
$messages[$nodePath] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
}
|
25
vendor/symfony/translation/Loader/IniFileLoader.php
vendored
Normal file
25
vendor/symfony/translation/Loader/IniFileLoader.php
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Translation\Loader;
|
||||
|
||||
/**
|
||||
* IniFileLoader loads translations from an ini file.
|
||||
*
|
||||
* @author stealth35
|
||||
*/
|
||||
class IniFileLoader extends FileLoader
|
||||
{
|
||||
protected function loadResource(string $resource): array
|
||||
{
|
||||
return parse_ini_file($resource, true);
|
||||
}
|
||||
}
|
51
vendor/symfony/translation/Loader/JsonFileLoader.php
vendored
Normal file
51
vendor/symfony/translation/Loader/JsonFileLoader.php
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Translation\Loader;
|
||||
|
||||
use Symfony\Component\Translation\Exception\InvalidResourceException;
|
||||
|
||||
/**
|
||||
* JsonFileLoader loads translations from an json file.
|
||||
*
|
||||
* @author singles
|
||||
*/
|
||||
class JsonFileLoader extends FileLoader
|
||||
{
|
||||
protected function loadResource(string $resource): array
|
||||
{
|
||||
$messages = [];
|
||||
if ($data = file_get_contents($resource)) {
|
||||
$messages = json_decode($data, true);
|
||||
|
||||
if (0 < $errorCode = json_last_error()) {
|
||||
throw new InvalidResourceException('Error parsing JSON: '.$this->getJSONErrorMessage($errorCode));
|
||||
}
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates JSON_ERROR_* constant into meaningful message.
|
||||
*/
|
||||
private function getJSONErrorMessage(int $errorCode): string
|
||||
{
|
||||
return match ($errorCode) {
|
||||
\JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
|
||||
\JSON_ERROR_STATE_MISMATCH => 'Underflow or the modes mismatch',
|
||||
\JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
|
||||
\JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON',
|
||||
\JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded',
|
||||
default => 'Unknown error',
|
||||
};
|
||||
}
|
||||
}
|
32
vendor/symfony/translation/Loader/LoaderInterface.php
vendored
Normal file
32
vendor/symfony/translation/Loader/LoaderInterface.php
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Translation\Loader;
|
||||
|
||||
use Symfony\Component\Translation\Exception\InvalidResourceException;
|
||||
use Symfony\Component\Translation\Exception\NotFoundResourceException;
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
|
||||
/**
|
||||
* LoaderInterface is the interface implemented by all translation loaders.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface LoaderInterface
|
||||
{
|
||||
/**
|
||||
* Loads a locale.
|
||||
*
|
||||
* @throws NotFoundResourceException when the resource cannot be found
|
||||
* @throws InvalidResourceException when the resource cannot be loaded
|
||||
*/
|
||||
public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue;
|
||||
}
|
138
vendor/symfony/translation/Loader/MoFileLoader.php
vendored
Normal file
138
vendor/symfony/translation/Loader/MoFileLoader.php
vendored
Normal file
@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Translation\Loader;
|
||||
|
||||
use Symfony\Component\Translation\Exception\InvalidResourceException;
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2010, Union of RAD http://union-of-rad.org (http://lithify.me/)
|
||||
*/
|
||||
class MoFileLoader extends FileLoader
|
||||
{
|
||||
/**
|
||||
* Magic used for validating the format of an MO file as well as
|
||||
* detecting if the machine used to create that file was little endian.
|
||||
*/
|
||||
public const MO_LITTLE_ENDIAN_MAGIC = 0x950412DE;
|
||||
|
||||
/**
|
||||
* Magic used for validating the format of an MO file as well as
|
||||
* detecting if the machine used to create that file was big endian.
|
||||
*/
|
||||
public const MO_BIG_ENDIAN_MAGIC = 0xDE120495;
|
||||
|
||||
/**
|
||||
* The size of the header of an MO file in bytes.
|
||||
*/
|
||||
public const MO_HEADER_SIZE = 28;
|
||||
|
||||
/**
|
||||
* Parses machine object (MO) format, independent of the machine's endian it
|
||||
* was created on. Both 32bit and 64bit systems are supported.
|
||||
*/
|
||||
protected function loadResource(string $resource): array
|
||||
{
|
||||
$stream = fopen($resource, 'r');
|
||||
|
||||
$stat = fstat($stream);
|
||||
|
||||
if ($stat['size'] < self::MO_HEADER_SIZE) {
|
||||
throw new InvalidResourceException('MO stream content has an invalid format.');
|
||||
}
|
||||
$magic = unpack('V1', fread($stream, 4));
|
||||
$magic = hexdec(substr(dechex(current($magic)), -8));
|
||||
|
||||
if (self::MO_LITTLE_ENDIAN_MAGIC == $magic) {
|
||||
$isBigEndian = false;
|
||||
} elseif (self::MO_BIG_ENDIAN_MAGIC == $magic) {
|
||||
$isBigEndian = true;
|
||||
} else {
|
||||
throw new InvalidResourceException('MO stream content has an invalid format.');
|
||||
}
|
||||
|
||||
// formatRevision
|
||||
$this->readLong($stream, $isBigEndian);
|
||||
$count = $this->readLong($stream, $isBigEndian);
|
||||
$offsetId = $this->readLong($stream, $isBigEndian);
|
||||
$offsetTranslated = $this->readLong($stream, $isBigEndian);
|
||||
// sizeHashes
|
||||
$this->readLong($stream, $isBigEndian);
|
||||
// offsetHashes
|
||||
$this->readLong($stream, $isBigEndian);
|
||||
|
||||
$messages = [];
|
||||
|
||||
for ($i = 0; $i < $count; ++$i) {
|
||||
$pluralId = null;
|
||||
$translated = null;
|
||||
|
||||
fseek($stream, $offsetId + $i * 8);
|
||||
|
||||
$length = $this->readLong($stream, $isBigEndian);
|
||||
$offset = $this->readLong($stream, $isBigEndian);
|
||||
|
||||
if ($length < 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fseek($stream, $offset);
|
||||
$singularId = fread($stream, $length);
|
||||
|
||||
if (str_contains($singularId, "\000")) {
|
||||
[$singularId, $pluralId] = explode("\000", $singularId);
|
||||
}
|
||||
|
||||
fseek($stream, $offsetTranslated + $i * 8);
|
||||
$length = $this->readLong($stream, $isBigEndian);
|
||||
$offset = $this->readLong($stream, $isBigEndian);
|
||||
|
||||
if ($length < 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fseek($stream, $offset);
|
||||
$translated = fread($stream, $length);
|
||||
|
||||
if (str_contains($translated, "\000")) {
|
||||
$translated = explode("\000", $translated);
|
||||
}
|
||||
|
||||
$ids = ['singular' => $singularId, 'plural' => $pluralId];
|
||||
$item = compact('ids', 'translated');
|
||||
|
||||
if (!empty($item['ids']['singular'])) {
|
||||
$id = $item['ids']['singular'];
|
||||
if (isset($item['ids']['plural'])) {
|
||||
$id .= '|'.$item['ids']['plural'];
|
||||
}
|
||||
$messages[$id] = stripcslashes(implode('|', (array) $item['translated']));
|
||||
}
|
||||
}
|
||||
|
||||
fclose($stream);
|
||||
|
||||
return array_filter($messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an unsigned long from stream respecting endianness.
|
||||
*
|
||||
* @param resource $stream
|
||||
*/
|
||||
private function readLong($stream, bool $isBigEndian): int
|
||||
{
|
||||
$result = unpack($isBigEndian ? 'N1' : 'V1', fread($stream, 4));
|
||||
$result = current($result);
|
||||
|
||||
return (int) substr($result, -8);
|
||||
}
|
||||
}
|
35
vendor/symfony/translation/Loader/PhpFileLoader.php
vendored
Normal file
35
vendor/symfony/translation/Loader/PhpFileLoader.php
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Translation\Loader;
|
||||
|
||||
/**
|
||||
* PhpFileLoader loads translations from PHP files returning an array of translations.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class PhpFileLoader extends FileLoader
|
||||
{
|
||||
private static ?array $cache = [];
|
||||
|
||||
protected function loadResource(string $resource): array
|
||||
{
|
||||
if ([] === self::$cache && \function_exists('opcache_invalidate') && filter_var(\ini_get('opcache.enable'), \FILTER_VALIDATE_BOOL) && (!\in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true) || filter_var(\ini_get('opcache.enable_cli'), \FILTER_VALIDATE_BOOL))) {
|
||||
self::$cache = null;
|
||||
}
|
||||
|
||||
if (null === self::$cache) {
|
||||
return require $resource;
|
||||
}
|
||||
|
||||
return self::$cache[$resource] ??= require $resource;
|
||||
}
|
||||
}
|
147
vendor/symfony/translation/Loader/PoFileLoader.php
vendored
Normal file
147
vendor/symfony/translation/Loader/PoFileLoader.php
vendored
Normal file
@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Translation\Loader;
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2010, Union of RAD https://github.com/UnionOfRAD/lithium
|
||||
* @copyright Copyright (c) 2012, Clemens Tolboom
|
||||
*/
|
||||
class PoFileLoader extends FileLoader
|
||||
{
|
||||
/**
|
||||
* Parses portable object (PO) format.
|
||||
*
|
||||
* From https://www.gnu.org/software/gettext/manual/gettext.html#PO-Files
|
||||
* we should be able to parse files having:
|
||||
*
|
||||
* white-space
|
||||
* # translator-comments
|
||||
* #. extracted-comments
|
||||
* #: reference...
|
||||
* #, flag...
|
||||
* #| msgid previous-untranslated-string
|
||||
* msgid untranslated-string
|
||||
* msgstr translated-string
|
||||
*
|
||||
* extra or different lines are:
|
||||
*
|
||||
* #| msgctxt previous-context
|
||||
* #| msgid previous-untranslated-string
|
||||
* msgctxt context
|
||||
*
|
||||
* #| msgid previous-untranslated-string-singular
|
||||
* #| msgid_plural previous-untranslated-string-plural
|
||||
* msgid untranslated-string-singular
|
||||
* msgid_plural untranslated-string-plural
|
||||
* msgstr[0] translated-string-case-0
|
||||
* ...
|
||||
* msgstr[N] translated-string-case-n
|
||||
*
|
||||
* The definition states:
|
||||
* - white-space and comments are optional.
|
||||
* - msgid "" that an empty singleline defines a header.
|
||||
*
|
||||
* This parser sacrifices some features of the reference implementation the
|
||||
* differences to that implementation are as follows.
|
||||
* - No support for comments spanning multiple lines.
|
||||
* - Translator and extracted comments are treated as being the same type.
|
||||
* - Message IDs are allowed to have other encodings as just US-ASCII.
|
||||
*
|
||||
* Items with an empty id are ignored.
|
||||
*/
|
||||
protected function loadResource(string $resource): array
|
||||
{
|
||||
$stream = fopen($resource, 'r');
|
||||
|
||||
$defaults = [
|
||||
'ids' => [],
|
||||
'translated' => null,
|
||||
];
|
||||
|
||||
$messages = [];
|
||||
$item = $defaults;
|
||||
$flags = [];
|
||||
|
||||
while ($line = fgets($stream)) {
|
||||
$line = trim($line);
|
||||
|
||||
if ('' === $line) {
|
||||
// Whitespace indicated current item is done
|
||||
if (!\in_array('fuzzy', $flags)) {
|
||||
$this->addMessage($messages, $item);
|
||||
}
|
||||
$item = $defaults;
|
||||
$flags = [];
|
||||
} elseif (str_starts_with($line, '#,')) {
|
||||
$flags = array_map('trim', explode(',', substr($line, 2)));
|
||||
} elseif (str_starts_with($line, 'msgid "')) {
|
||||
// We start a new msg so save previous
|
||||
// TODO: this fails when comments or contexts are added
|
||||
$this->addMessage($messages, $item);
|
||||
$item = $defaults;
|
||||
$item['ids']['singular'] = substr($line, 7, -1);
|
||||
} elseif (str_starts_with($line, 'msgstr "')) {
|
||||
$item['translated'] = substr($line, 8, -1);
|
||||
} elseif ('"' === $line[0]) {
|
||||
$continues = isset($item['translated']) ? 'translated' : 'ids';
|
||||
|
||||
if (\is_array($item[$continues])) {
|
||||
end($item[$continues]);
|
||||
$item[$continues][key($item[$continues])] .= substr($line, 1, -1);
|
||||
} else {
|
||||
$item[$continues] .= substr($line, 1, -1);
|
||||
}
|
||||
} elseif (str_starts_with($line, 'msgid_plural "')) {
|
||||
$item['ids']['plural'] = substr($line, 14, -1);
|
||||
} elseif (str_starts_with($line, 'msgstr[')) {
|
||||
$size = strpos($line, ']');
|
||||
$item['translated'][(int) substr($line, 7, 1)] = substr($line, $size + 3, -1);
|
||||
}
|
||||
}
|
||||
// save last item
|
||||
if (!\in_array('fuzzy', $flags)) {
|
||||
$this->addMessage($messages, $item);
|
||||
}
|
||||
fclose($stream);
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a translation item to the messages.
|
||||
*
|
||||
* A .po file could contain by error missing plural indexes. We need to
|
||||
* fix these before saving them.
|
||||
*/
|
||||
private function addMessage(array &$messages, array $item): void
|
||||
{
|
||||
if (!empty($item['ids']['singular'])) {
|
||||
$id = stripcslashes($item['ids']['singular']);
|
||||
if (isset($item['ids']['plural'])) {
|
||||
$id .= '|'.stripcslashes($item['ids']['plural']);
|
||||
}
|
||||
|
||||
$translated = (array) $item['translated'];
|
||||
// PO are by definition indexed so sort by index.
|
||||
ksort($translated);
|
||||
// Make sure every index is filled.
|
||||
end($translated);
|
||||
$count = key($translated);
|
||||
// Fill missing spots with '-'.
|
||||
$empties = array_fill(0, $count + 1, '-');
|
||||
$translated += $empties;
|
||||
ksort($translated);
|
||||
|
||||
$messages[$id] = stripcslashes(implode('|', $translated));
|
||||
}
|
||||
}
|
||||
}
|
78
vendor/symfony/translation/Loader/QtFileLoader.php
vendored
Normal file
78
vendor/symfony/translation/Loader/QtFileLoader.php
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Translation\Loader;
|
||||
|
||||
use Symfony\Component\Config\Resource\FileResource;
|
||||
use Symfony\Component\Config\Util\XmlUtils;
|
||||
use Symfony\Component\Translation\Exception\InvalidResourceException;
|
||||
use Symfony\Component\Translation\Exception\NotFoundResourceException;
|
||||
use Symfony\Component\Translation\Exception\RuntimeException;
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
|
||||
/**
|
||||
* QtFileLoader loads translations from QT Translations XML files.
|
||||
*
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
class QtFileLoader implements LoaderInterface
|
||||
{
|
||||
public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue
|
||||
{
|
||||
if (!class_exists(XmlUtils::class)) {
|
||||
throw new RuntimeException('Loading translations from the QT format requires the Symfony Config component.');
|
||||
}
|
||||
|
||||
if (!stream_is_local($resource)) {
|
||||
throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
|
||||
}
|
||||
|
||||
if (!file_exists($resource)) {
|
||||
throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
|
||||
}
|
||||
|
||||
try {
|
||||
$dom = XmlUtils::loadFile($resource);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
throw new InvalidResourceException(sprintf('Unable to load "%s".', $resource), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
$internalErrors = libxml_use_internal_errors(true);
|
||||
libxml_clear_errors();
|
||||
|
||||
$xpath = new \DOMXPath($dom);
|
||||
$nodes = $xpath->evaluate('//TS/context/name[text()="'.$domain.'"]');
|
||||
|
||||
$catalogue = new MessageCatalogue($locale);
|
||||
if (1 == $nodes->length) {
|
||||
$translations = $nodes->item(0)->nextSibling->parentNode->parentNode->getElementsByTagName('message');
|
||||
foreach ($translations as $translation) {
|
||||
$translationValue = (string) $translation->getElementsByTagName('translation')->item(0)->nodeValue;
|
||||
|
||||
if (!empty($translationValue)) {
|
||||
$catalogue->set(
|
||||
(string) $translation->getElementsByTagName('source')->item(0)->nodeValue,
|
||||
$translationValue,
|
||||
$domain
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (class_exists(FileResource::class)) {
|
||||
$catalogue->addResource(new FileResource($resource));
|
||||
}
|
||||
}
|
||||
|
||||
libxml_use_internal_errors($internalErrors);
|
||||
|
||||
return $catalogue;
|
||||
}
|
||||
}
|
237
vendor/symfony/translation/Loader/XliffFileLoader.php
vendored
Normal file
237
vendor/symfony/translation/Loader/XliffFileLoader.php
vendored
Normal file
@ -0,0 +1,237 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Translation\Loader;
|
||||
|
||||
use Symfony\Component\Config\Resource\FileResource;
|
||||
use Symfony\Component\Config\Util\Exception\InvalidXmlException;
|
||||
use Symfony\Component\Config\Util\Exception\XmlParsingException;
|
||||
use Symfony\Component\Config\Util\XmlUtils;
|
||||
use Symfony\Component\Translation\Exception\InvalidResourceException;
|
||||
use Symfony\Component\Translation\Exception\NotFoundResourceException;
|
||||
use Symfony\Component\Translation\Exception\RuntimeException;
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
use Symfony\Component\Translation\Util\XliffUtils;
|
||||
|
||||
/**
|
||||
* XliffFileLoader loads translations from XLIFF files.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class XliffFileLoader implements LoaderInterface
|
||||
{
|
||||
public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue
|
||||
{
|
||||
if (!class_exists(XmlUtils::class)) {
|
||||
throw new RuntimeException('Loading translations from the Xliff format requires the Symfony Config component.');
|
||||
}
|
||||
|
||||
if (!$this->isXmlString($resource)) {
|
||||
if (!stream_is_local($resource)) {
|
||||
throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
|
||||
}
|
||||
|
||||
if (!file_exists($resource)) {
|
||||
throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
|
||||
}
|
||||
|
||||
if (!is_file($resource)) {
|
||||
throw new InvalidResourceException(sprintf('This is neither a file nor an XLIFF string "%s".', $resource));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if ($this->isXmlString($resource)) {
|
||||
$dom = XmlUtils::parse($resource);
|
||||
} else {
|
||||
$dom = XmlUtils::loadFile($resource);
|
||||
}
|
||||
} catch (\InvalidArgumentException|XmlParsingException|InvalidXmlException $e) {
|
||||
throw new InvalidResourceException(sprintf('Unable to load "%s": ', $resource).$e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
if ($errors = XliffUtils::validateSchema($dom)) {
|
||||
throw new InvalidResourceException(sprintf('Invalid resource provided: "%s"; Errors: ', $resource).XliffUtils::getErrorsAsString($errors));
|
||||
}
|
||||
|
||||
$catalogue = new MessageCatalogue($locale);
|
||||
$this->extract($dom, $catalogue, $domain);
|
||||
|
||||
if (is_file($resource) && class_exists(FileResource::class)) {
|
||||
$catalogue->addResource(new FileResource($resource));
|
||||
}
|
||||
|
||||
return $catalogue;
|
||||
}
|
||||
|
||||
private function extract(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain): void
|
||||
{
|
||||
$xliffVersion = XliffUtils::getVersionNumber($dom);
|
||||
|
||||
if ('1.2' === $xliffVersion) {
|
||||
$this->extractXliff1($dom, $catalogue, $domain);
|
||||
}
|
||||
|
||||
if ('2.0' === $xliffVersion) {
|
||||
$this->extractXliff2($dom, $catalogue, $domain);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract messages and metadata from DOMDocument into a MessageCatalogue.
|
||||
*/
|
||||
private function extractXliff1(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain): void
|
||||
{
|
||||
$xml = simplexml_import_dom($dom);
|
||||
$encoding = $dom->encoding ? strtoupper($dom->encoding) : null;
|
||||
|
||||
$namespace = 'urn:oasis:names:tc:xliff:document:1.2';
|
||||
$xml->registerXPathNamespace('xliff', $namespace);
|
||||
|
||||
foreach ($xml->xpath('//xliff:file') as $file) {
|
||||
$fileAttributes = $file->attributes();
|
||||
|
||||
$file->registerXPathNamespace('xliff', $namespace);
|
||||
|
||||
foreach ($file->xpath('.//xliff:prop') as $prop) {
|
||||
$catalogue->setCatalogueMetadata($prop->attributes()['prop-type'], (string) $prop, $domain);
|
||||
}
|
||||
|
||||
foreach ($file->xpath('.//xliff:trans-unit') as $translation) {
|
||||
$attributes = $translation->attributes();
|
||||
|
||||
if (!(isset($attributes['resname']) || isset($translation->source))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($translation->target) && 'needs-translation' === (string) $translation->target->attributes()['state']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$source = isset($attributes['resname']) && $attributes['resname'] ? $attributes['resname'] : $translation->source;
|
||||
// If the xlf file has another encoding specified, try to convert it because
|
||||
// simple_xml will always return utf-8 encoded values
|
||||
$target = $this->utf8ToCharset((string) ($translation->target ?? $translation->source), $encoding);
|
||||
|
||||
$catalogue->set((string) $source, $target, $domain);
|
||||
|
||||
$metadata = [
|
||||
'source' => (string) $translation->source,
|
||||
'file' => [
|
||||
'original' => (string) $fileAttributes['original'],
|
||||
],
|
||||
];
|
||||
if ($notes = $this->parseNotesMetadata($translation->note, $encoding)) {
|
||||
$metadata['notes'] = $notes;
|
||||
}
|
||||
|
||||
if (isset($translation->target) && $translation->target->attributes()) {
|
||||
$metadata['target-attributes'] = [];
|
||||
foreach ($translation->target->attributes() as $key => $value) {
|
||||
$metadata['target-attributes'][$key] = (string) $value;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($attributes['id'])) {
|
||||
$metadata['id'] = (string) $attributes['id'];
|
||||
}
|
||||
|
||||
$catalogue->setMetadata((string) $source, $metadata, $domain);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function extractXliff2(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain): void
|
||||
{
|
||||
$xml = simplexml_import_dom($dom);
|
||||
$encoding = $dom->encoding ? strtoupper($dom->encoding) : null;
|
||||
|
||||
$xml->registerXPathNamespace('xliff', 'urn:oasis:names:tc:xliff:document:2.0');
|
||||
|
||||
foreach ($xml->xpath('//xliff:unit') as $unit) {
|
||||
foreach ($unit->segment as $segment) {
|
||||
$attributes = $unit->attributes();
|
||||
$source = $attributes['name'] ?? $segment->source;
|
||||
|
||||
// If the xlf file has another encoding specified, try to convert it because
|
||||
// simple_xml will always return utf-8 encoded values
|
||||
$target = $this->utf8ToCharset((string) ($segment->target ?? $segment->source), $encoding);
|
||||
|
||||
$catalogue->set((string) $source, $target, $domain);
|
||||
|
||||
$metadata = [];
|
||||
if (isset($segment->target) && $segment->target->attributes()) {
|
||||
$metadata['target-attributes'] = [];
|
||||
foreach ($segment->target->attributes() as $key => $value) {
|
||||
$metadata['target-attributes'][$key] = (string) $value;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($unit->notes)) {
|
||||
$metadata['notes'] = [];
|
||||
foreach ($unit->notes->note as $noteNode) {
|
||||
$note = [];
|
||||
foreach ($noteNode->attributes() as $key => $value) {
|
||||
$note[$key] = (string) $value;
|
||||
}
|
||||
$note['content'] = (string) $noteNode;
|
||||
$metadata['notes'][] = $note;
|
||||
}
|
||||
}
|
||||
|
||||
$catalogue->setMetadata((string) $source, $metadata, $domain);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a UTF8 string to the specified encoding.
|
||||
*/
|
||||
private function utf8ToCharset(string $content, ?string $encoding = null): string
|
||||
{
|
||||
if ('UTF-8' !== $encoding && !empty($encoding)) {
|
||||
return mb_convert_encoding($content, $encoding, 'UTF-8');
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
private function parseNotesMetadata(?\SimpleXMLElement $noteElement = null, ?string $encoding = null): array
|
||||
{
|
||||
$notes = [];
|
||||
|
||||
if (null === $noteElement) {
|
||||
return $notes;
|
||||
}
|
||||
|
||||
/** @var \SimpleXMLElement $xmlNote */
|
||||
foreach ($noteElement as $xmlNote) {
|
||||
$noteAttributes = $xmlNote->attributes();
|
||||
$note = ['content' => $this->utf8ToCharset((string) $xmlNote, $encoding)];
|
||||
if (isset($noteAttributes['priority'])) {
|
||||
$note['priority'] = (int) $noteAttributes['priority'];
|
||||
}
|
||||
|
||||
if (isset($noteAttributes['from'])) {
|
||||
$note['from'] = (string) $noteAttributes['from'];
|
||||
}
|
||||
|
||||
$notes[] = $note;
|
||||
}
|
||||
|
||||
return $notes;
|
||||
}
|
||||
|
||||
private function isXmlString(string $resource): bool
|
||||
{
|
||||
return str_starts_with($resource, '<?xml');
|
||||
}
|
||||
}
|
51
vendor/symfony/translation/Loader/YamlFileLoader.php
vendored
Normal file
51
vendor/symfony/translation/Loader/YamlFileLoader.php
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Translation\Loader;
|
||||
|
||||
use Symfony\Component\Translation\Exception\InvalidResourceException;
|
||||
use Symfony\Component\Translation\Exception\LogicException;
|
||||
use Symfony\Component\Yaml\Exception\ParseException;
|
||||
use Symfony\Component\Yaml\Parser as YamlParser;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
/**
|
||||
* YamlFileLoader loads translations from Yaml files.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class YamlFileLoader extends FileLoader
|
||||
{
|
||||
private YamlParser $yamlParser;
|
||||
|
||||
protected function loadResource(string $resource): array
|
||||
{
|
||||
if (!isset($this->yamlParser)) {
|
||||
if (!class_exists(\Symfony\Component\Yaml\Parser::class)) {
|
||||
throw new LogicException('Loading translations from the YAML format requires the Symfony Yaml component.');
|
||||
}
|
||||
|
||||
$this->yamlParser = new YamlParser();
|
||||
}
|
||||
|
||||
try {
|
||||
$messages = $this->yamlParser->parseFile($resource, Yaml::PARSE_CONSTANT);
|
||||
} catch (ParseException $e) {
|
||||
throw new InvalidResourceException(sprintf('The file "%s" does not contain valid YAML: ', $resource).$e->getMessage(), 0, $e);
|
||||
}
|
||||
|
||||
if (null !== $messages && !\is_array($messages)) {
|
||||
throw new InvalidResourceException(sprintf('Unable to load file "%s".', $resource));
|
||||
}
|
||||
|
||||
return $messages ?: [];
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user