Initial Commit
This commit is contained in:
163
dependencies/symfony/translation/Catalogue/AbstractOperation.php
vendored
Normal file
163
dependencies/symfony/translation/Catalogue/AbstractOperation.php
vendored
Normal file
@ -0,0 +1,163 @@
|
||||
<?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\Catalogue;
|
||||
|
||||
use Symfony\Component\Translation\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Translation\Exception\LogicException;
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
use Symfony\Component\Translation\MessageCatalogueInterface;
|
||||
/**
|
||||
* Base catalogues binary operation class.
|
||||
*
|
||||
* A catalogue binary operation performs operation on
|
||||
* source (the left argument) and target (the right argument) catalogues.
|
||||
*
|
||||
* @author Jean-François Simon <contact@jfsimon.fr>
|
||||
*/
|
||||
abstract class AbstractOperation implements \Symfony\Component\Translation\Catalogue\OperationInterface
|
||||
{
|
||||
public const OBSOLETE_BATCH = 'obsolete';
|
||||
public const NEW_BATCH = 'new';
|
||||
public const ALL_BATCH = 'all';
|
||||
protected $source;
|
||||
protected $target;
|
||||
protected $result;
|
||||
/**
|
||||
* @var array|null The domains affected by this operation
|
||||
*/
|
||||
private $domains;
|
||||
/**
|
||||
* This array stores 'all', 'new' and 'obsolete' messages for all valid domains.
|
||||
*
|
||||
* The data structure of this array is as follows:
|
||||
*
|
||||
* [
|
||||
* 'domain 1' => [
|
||||
* 'all' => [...],
|
||||
* 'new' => [...],
|
||||
* 'obsolete' => [...]
|
||||
* ],
|
||||
* 'domain 2' => [
|
||||
* 'all' => [...],
|
||||
* 'new' => [...],
|
||||
* 'obsolete' => [...]
|
||||
* ],
|
||||
* ...
|
||||
* ]
|
||||
*
|
||||
* @var array The array that stores 'all', 'new' and 'obsolete' messages
|
||||
*/
|
||||
protected $messages;
|
||||
/**
|
||||
* @throws LogicException
|
||||
*/
|
||||
public function __construct(MessageCatalogueInterface $source, MessageCatalogueInterface $target)
|
||||
{
|
||||
if ($source->getLocale() !== $target->getLocale()) {
|
||||
throw new LogicException('Operated catalogues must belong to the same locale.');
|
||||
}
|
||||
$this->source = $source;
|
||||
$this->target = $target;
|
||||
$this->result = new MessageCatalogue($source->getLocale());
|
||||
$this->messages = [];
|
||||
}
|
||||
public function getDomains() : array
|
||||
{
|
||||
if (null === $this->domains) {
|
||||
$domains = [];
|
||||
foreach ([$this->source, $this->target] as $catalogue) {
|
||||
foreach ($catalogue->getDomains() as $domain) {
|
||||
$domains[$domain] = $domain;
|
||||
if ($catalogue->all($domainIcu = $domain . MessageCatalogueInterface::INTL_DOMAIN_SUFFIX)) {
|
||||
$domains[$domainIcu] = $domainIcu;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->domains = \array_values($domains);
|
||||
}
|
||||
return $this->domains;
|
||||
}
|
||||
public function getMessages(string $domain) : array
|
||||
{
|
||||
if (!\in_array($domain, $this->getDomains())) {
|
||||
throw new InvalidArgumentException(\sprintf('Invalid domain: "%s".', $domain));
|
||||
}
|
||||
if (!isset($this->messages[$domain][self::ALL_BATCH])) {
|
||||
$this->processDomain($domain);
|
||||
}
|
||||
return $this->messages[$domain][self::ALL_BATCH];
|
||||
}
|
||||
public function getNewMessages(string $domain) : array
|
||||
{
|
||||
if (!\in_array($domain, $this->getDomains())) {
|
||||
throw new InvalidArgumentException(\sprintf('Invalid domain: "%s".', $domain));
|
||||
}
|
||||
if (!isset($this->messages[$domain][self::NEW_BATCH])) {
|
||||
$this->processDomain($domain);
|
||||
}
|
||||
return $this->messages[$domain][self::NEW_BATCH];
|
||||
}
|
||||
public function getObsoleteMessages(string $domain) : array
|
||||
{
|
||||
if (!\in_array($domain, $this->getDomains())) {
|
||||
throw new InvalidArgumentException(\sprintf('Invalid domain: "%s".', $domain));
|
||||
}
|
||||
if (!isset($this->messages[$domain][self::OBSOLETE_BATCH])) {
|
||||
$this->processDomain($domain);
|
||||
}
|
||||
return $this->messages[$domain][self::OBSOLETE_BATCH];
|
||||
}
|
||||
public function getResult() : MessageCatalogueInterface
|
||||
{
|
||||
foreach ($this->getDomains() as $domain) {
|
||||
if (!isset($this->messages[$domain])) {
|
||||
$this->processDomain($domain);
|
||||
}
|
||||
}
|
||||
return $this->result;
|
||||
}
|
||||
/**
|
||||
* @param self::*_BATCH $batch
|
||||
*/
|
||||
public function moveMessagesToIntlDomainsIfPossible(string $batch = self::ALL_BATCH) : void
|
||||
{
|
||||
// If MessageFormatter class does not exists, intl domains are not supported.
|
||||
if (!\class_exists(\MessageFormatter::class)) {
|
||||
return;
|
||||
}
|
||||
foreach ($this->getDomains() as $domain) {
|
||||
$intlDomain = $domain . MessageCatalogueInterface::INTL_DOMAIN_SUFFIX;
|
||||
$messages = match ($batch) {
|
||||
self::OBSOLETE_BATCH => $this->getObsoleteMessages($domain),
|
||||
self::NEW_BATCH => $this->getNewMessages($domain),
|
||||
self::ALL_BATCH => $this->getMessages($domain),
|
||||
default => throw new \InvalidArgumentException(\sprintf('$batch argument must be one of ["%s", "%s", "%s"].', self::ALL_BATCH, self::NEW_BATCH, self::OBSOLETE_BATCH)),
|
||||
};
|
||||
if (!$messages || !$this->source->all($intlDomain) && $this->source->all($domain)) {
|
||||
continue;
|
||||
}
|
||||
$result = $this->getResult();
|
||||
$allIntlMessages = $result->all($intlDomain);
|
||||
$currentMessages = \array_diff_key($messages, $result->all($domain));
|
||||
$result->replace($currentMessages, $domain);
|
||||
$result->replace($allIntlMessages + $messages, $intlDomain);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Performs operation on source and target catalogues for the given domain and
|
||||
* stores the results.
|
||||
*
|
||||
* @param string $domain The domain which the operation will be performed for
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected abstract function processDomain(string $domain);
|
||||
}
|
62
dependencies/symfony/translation/Catalogue/MergeOperation.php
vendored
Normal file
62
dependencies/symfony/translation/Catalogue/MergeOperation.php
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
<?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\Catalogue;
|
||||
|
||||
use Symfony\Component\Translation\MessageCatalogueInterface;
|
||||
/**
|
||||
* Merge operation between two catalogues as follows:
|
||||
* all = source ∪ target = {x: x ∈ source ∨ x ∈ target}
|
||||
* new = all ∖ source = {x: x ∈ target ∧ x ∉ source}
|
||||
* obsolete = source ∖ all = {x: x ∈ source ∧ x ∉ source ∧ x ∉ target} = ∅
|
||||
* Basically, the result contains messages from both catalogues.
|
||||
*
|
||||
* @author Jean-François Simon <contact@jfsimon.fr>
|
||||
*/
|
||||
class MergeOperation extends \Symfony\Component\Translation\Catalogue\AbstractOperation
|
||||
{
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function processDomain(string $domain)
|
||||
{
|
||||
$this->messages[$domain] = ['all' => [], 'new' => [], 'obsolete' => []];
|
||||
$intlDomain = $domain . MessageCatalogueInterface::INTL_DOMAIN_SUFFIX;
|
||||
foreach ($this->target->getCatalogueMetadata('', $domain) ?? [] as $key => $value) {
|
||||
if (null === $this->result->getCatalogueMetadata($key, $domain)) {
|
||||
$this->result->setCatalogueMetadata($key, $value, $domain);
|
||||
}
|
||||
}
|
||||
foreach ($this->target->getCatalogueMetadata('', $intlDomain) ?? [] as $key => $value) {
|
||||
if (null === $this->result->getCatalogueMetadata($key, $intlDomain)) {
|
||||
$this->result->setCatalogueMetadata($key, $value, $intlDomain);
|
||||
}
|
||||
}
|
||||
foreach ($this->source->all($domain) as $id => $message) {
|
||||
$this->messages[$domain]['all'][$id] = $message;
|
||||
$d = $this->source->defines($id, $intlDomain) ? $intlDomain : $domain;
|
||||
$this->result->add([$id => $message], $d);
|
||||
if (null !== ($keyMetadata = $this->source->getMetadata($id, $d))) {
|
||||
$this->result->setMetadata($id, $keyMetadata, $d);
|
||||
}
|
||||
}
|
||||
foreach ($this->target->all($domain) as $id => $message) {
|
||||
if (!$this->source->has($id, $domain)) {
|
||||
$this->messages[$domain]['all'][$id] = $message;
|
||||
$this->messages[$domain]['new'][$id] = $message;
|
||||
$d = $this->target->defines($id, $intlDomain) ? $intlDomain : $domain;
|
||||
$this->result->add([$id => $message], $d);
|
||||
if (null !== ($keyMetadata = $this->target->getMetadata($id, $d))) {
|
||||
$this->result->setMetadata($id, $keyMetadata, $d);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
55
dependencies/symfony/translation/Catalogue/OperationInterface.php
vendored
Normal file
55
dependencies/symfony/translation/Catalogue/OperationInterface.php
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
<?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\Catalogue;
|
||||
|
||||
use Symfony\Component\Translation\MessageCatalogueInterface;
|
||||
/**
|
||||
* Represents an operation on catalogue(s).
|
||||
*
|
||||
* An instance of this interface performs an operation on one or more catalogues and
|
||||
* stores intermediate and final results of the operation.
|
||||
*
|
||||
* The first catalogue in its argument(s) is called the 'source catalogue' or 'source' and
|
||||
* the following results are stored:
|
||||
*
|
||||
* Messages: also called 'all', are valid messages for the given domain after the operation is performed.
|
||||
*
|
||||
* New Messages: also called 'new' (new = all ∖ source = {x: x ∈ all ∧ x ∉ source}).
|
||||
*
|
||||
* Obsolete Messages: also called 'obsolete' (obsolete = source ∖ all = {x: x ∈ source ∧ x ∉ all}).
|
||||
*
|
||||
* Result: also called 'result', is the resulting catalogue for the given domain that holds the same messages as 'all'.
|
||||
*
|
||||
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
|
||||
*/
|
||||
interface OperationInterface
|
||||
{
|
||||
/**
|
||||
* Returns domains affected by operation.
|
||||
*/
|
||||
public function getDomains() : array;
|
||||
/**
|
||||
* Returns all valid messages ('all') after operation.
|
||||
*/
|
||||
public function getMessages(string $domain) : array;
|
||||
/**
|
||||
* Returns new messages ('new') after operation.
|
||||
*/
|
||||
public function getNewMessages(string $domain) : array;
|
||||
/**
|
||||
* Returns obsolete messages ('obsolete') after operation.
|
||||
*/
|
||||
public function getObsoleteMessages(string $domain) : array;
|
||||
/**
|
||||
* Returns resulting catalogue ('result').
|
||||
*/
|
||||
public function getResult() : MessageCatalogueInterface;
|
||||
}
|
75
dependencies/symfony/translation/Catalogue/TargetOperation.php
vendored
Normal file
75
dependencies/symfony/translation/Catalogue/TargetOperation.php
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
<?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\Catalogue;
|
||||
|
||||
use Symfony\Component\Translation\MessageCatalogueInterface;
|
||||
/**
|
||||
* Target operation between two catalogues:
|
||||
* intersection = source ∩ target = {x: x ∈ source ∧ x ∈ target}
|
||||
* all = intersection ∪ (target ∖ intersection) = target
|
||||
* new = all ∖ source = {x: x ∈ target ∧ x ∉ source}
|
||||
* obsolete = source ∖ all = source ∖ target = {x: x ∈ source ∧ x ∉ target}
|
||||
* Basically, the result contains messages from the target catalogue.
|
||||
*
|
||||
* @author Michael Lee <michael.lee@zerustech.com>
|
||||
*/
|
||||
class TargetOperation extends \Symfony\Component\Translation\Catalogue\AbstractOperation
|
||||
{
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function processDomain(string $domain)
|
||||
{
|
||||
$this->messages[$domain] = ['all' => [], 'new' => [], 'obsolete' => []];
|
||||
$intlDomain = $domain . MessageCatalogueInterface::INTL_DOMAIN_SUFFIX;
|
||||
foreach ($this->target->getCatalogueMetadata('', $domain) ?? [] as $key => $value) {
|
||||
if (null === $this->result->getCatalogueMetadata($key, $domain)) {
|
||||
$this->result->setCatalogueMetadata($key, $value, $domain);
|
||||
}
|
||||
}
|
||||
foreach ($this->target->getCatalogueMetadata('', $intlDomain) ?? [] as $key => $value) {
|
||||
if (null === $this->result->getCatalogueMetadata($key, $intlDomain)) {
|
||||
$this->result->setCatalogueMetadata($key, $value, $intlDomain);
|
||||
}
|
||||
}
|
||||
// For 'all' messages, the code can't be simplified as ``$this->messages[$domain]['all'] = $target->all($domain);``,
|
||||
// because doing so will drop messages like {x: x ∈ source ∧ x ∉ target.all ∧ x ∈ target.fallback}
|
||||
//
|
||||
// For 'new' messages, the code can't be simplified as ``array_diff_assoc($this->target->all($domain), $this->source->all($domain));``
|
||||
// because doing so will not exclude messages like {x: x ∈ target ∧ x ∉ source.all ∧ x ∈ source.fallback}
|
||||
//
|
||||
// For 'obsolete' messages, the code can't be simplified as ``array_diff_assoc($this->source->all($domain), $this->target->all($domain))``
|
||||
// because doing so will not exclude messages like {x: x ∈ source ∧ x ∉ target.all ∧ x ∈ target.fallback}
|
||||
foreach ($this->source->all($domain) as $id => $message) {
|
||||
if ($this->target->has($id, $domain)) {
|
||||
$this->messages[$domain]['all'][$id] = $message;
|
||||
$d = $this->source->defines($id, $intlDomain) ? $intlDomain : $domain;
|
||||
$this->result->add([$id => $message], $d);
|
||||
if (null !== ($keyMetadata = $this->source->getMetadata($id, $d))) {
|
||||
$this->result->setMetadata($id, $keyMetadata, $d);
|
||||
}
|
||||
} else {
|
||||
$this->messages[$domain]['obsolete'][$id] = $message;
|
||||
}
|
||||
}
|
||||
foreach ($this->target->all($domain) as $id => $message) {
|
||||
if (!$this->source->has($id, $domain)) {
|
||||
$this->messages[$domain]['all'][$id] = $message;
|
||||
$this->messages[$domain]['new'][$id] = $message;
|
||||
$d = $this->target->defines($id, $intlDomain) ? $intlDomain : $domain;
|
||||
$this->result->add([$id => $message], $d);
|
||||
if (null !== ($keyMetadata = $this->target->getMetadata($id, $d))) {
|
||||
$this->result->setMetadata($id, $keyMetadata, $d);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
45
dependencies/symfony/translation/CatalogueMetadataAwareInterface.php
vendored
Normal file
45
dependencies/symfony/translation/CatalogueMetadataAwareInterface.php
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* This interface is used to get, set, and delete metadata about the Catalogue.
|
||||
*
|
||||
* @author Hugo Alliaume <hugo@alliau.me>
|
||||
*/
|
||||
interface CatalogueMetadataAwareInterface
|
||||
{
|
||||
/**
|
||||
* Gets catalogue metadata for the given domain and key.
|
||||
*
|
||||
* Passing an empty domain will return an array with all catalogue metadata indexed by
|
||||
* domain and then by key. Passing an empty key will return an array with all
|
||||
* catalogue metadata for the given domain.
|
||||
*
|
||||
* @return mixed The value that was set or an array with the domains/keys or null
|
||||
*/
|
||||
public function getCatalogueMetadata(string $key = '', string $domain = 'messages') : mixed;
|
||||
/**
|
||||
* Adds catalogue metadata to a message domain.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setCatalogueMetadata(string $key, mixed $value, string $domain = 'messages');
|
||||
/**
|
||||
* Deletes catalogue metadata for the given key and domain.
|
||||
*
|
||||
* Passing an empty domain will delete all catalogue metadata. Passing an empty key will
|
||||
* delete all metadata for the given domain.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function deleteCatalogueMetadata(string $key = '', string $domain = 'messages');
|
||||
}
|
138
dependencies/symfony/translation/Command/TranslationPullCommand.php
vendored
Normal file
138
dependencies/symfony/translation/Command/TranslationPullCommand.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\Command;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Console\Attribute\AsCommand;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Console\Command\Command;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Console\Completion\CompletionInput;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Console\Completion\CompletionSuggestions;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Console\Input\InputArgument;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Console\Input\InputInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Console\Input\InputOption;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Console\Output\OutputInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\Translation\Catalogue\TargetOperation;
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
use Symfony\Component\Translation\Provider\TranslationProviderCollection;
|
||||
use Symfony\Component\Translation\Reader\TranslationReaderInterface;
|
||||
use Symfony\Component\Translation\Writer\TranslationWriterInterface;
|
||||
/**
|
||||
* @author Mathieu Santostefano <msantostefano@protonmail.com>
|
||||
*/
|
||||
#[AsCommand(name: 'translation:pull', description: 'Pull translations from a given provider.')]
|
||||
final class TranslationPullCommand extends Command
|
||||
{
|
||||
use \Symfony\Component\Translation\Command\TranslationTrait;
|
||||
private TranslationProviderCollection $providerCollection;
|
||||
private TranslationWriterInterface $writer;
|
||||
private TranslationReaderInterface $reader;
|
||||
private string $defaultLocale;
|
||||
private array $transPaths;
|
||||
private array $enabledLocales;
|
||||
public function __construct(TranslationProviderCollection $providerCollection, TranslationWriterInterface $writer, TranslationReaderInterface $reader, string $defaultLocale, array $transPaths = [], array $enabledLocales = [])
|
||||
{
|
||||
$this->providerCollection = $providerCollection;
|
||||
$this->writer = $writer;
|
||||
$this->reader = $reader;
|
||||
$this->defaultLocale = $defaultLocale;
|
||||
$this->transPaths = $transPaths;
|
||||
$this->enabledLocales = $enabledLocales;
|
||||
parent::__construct();
|
||||
}
|
||||
public function complete(CompletionInput $input, CompletionSuggestions $suggestions) : void
|
||||
{
|
||||
if ($input->mustSuggestArgumentValuesFor('provider')) {
|
||||
$suggestions->suggestValues($this->providerCollection->keys());
|
||||
return;
|
||||
}
|
||||
if ($input->mustSuggestOptionValuesFor('domains')) {
|
||||
$provider = $this->providerCollection->get($input->getArgument('provider'));
|
||||
if (\method_exists($provider, 'getDomains')) {
|
||||
$suggestions->suggestValues($provider->getDomains());
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ($input->mustSuggestOptionValuesFor('locales')) {
|
||||
$suggestions->suggestValues($this->enabledLocales);
|
||||
return;
|
||||
}
|
||||
if ($input->mustSuggestOptionValuesFor('format')) {
|
||||
$suggestions->suggestValues(['php', 'xlf', 'xlf12', 'xlf20', 'po', 'mo', 'yml', 'yaml', 'ts', 'csv', 'json', 'ini', 'res']);
|
||||
}
|
||||
}
|
||||
protected function configure() : void
|
||||
{
|
||||
$keys = $this->providerCollection->keys();
|
||||
$defaultProvider = 1 === \count($keys) ? $keys[0] : null;
|
||||
$this->setDefinition([new InputArgument('provider', null !== $defaultProvider ? InputArgument::OPTIONAL : InputArgument::REQUIRED, 'The provider to pull translations from.', $defaultProvider), new InputOption('force', null, InputOption::VALUE_NONE, 'Override existing translations with provider ones (it will delete not synchronized messages).'), new InputOption('intl-icu', null, InputOption::VALUE_NONE, 'Associated to --force option, it will write messages in "%domain%+intl-icu.%locale%.xlf" files.'), new InputOption('domains', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Specify the domains to pull.'), new InputOption('locales', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Specify the locales to pull.'), new InputOption('format', null, InputOption::VALUE_OPTIONAL, 'Override the default output format.', 'xlf12')])->setHelp(<<<'EOF'
|
||||
The <info>%command.name%</> command pulls translations from the given provider. Only
|
||||
new translations are pulled, existing ones are not overwritten.
|
||||
|
||||
You can overwrite existing translations (and remove the missing ones on local side) by using the <comment>--force</> flag:
|
||||
|
||||
<info>php %command.full_name% --force provider</>
|
||||
|
||||
Full example:
|
||||
|
||||
<info>php %command.full_name% provider --force --domains=messages --domains=validators --locales=en</>
|
||||
|
||||
This command pulls all translations associated with the <comment>messages</> and <comment>validators</> domains for the <comment>en</> locale.
|
||||
Local translations for the specified domains and locale are deleted if they're not present on the provider and overwritten if it's the case.
|
||||
Local translations for others domains and locales are ignored.
|
||||
EOF
|
||||
);
|
||||
}
|
||||
protected function execute(InputInterface $input, OutputInterface $output) : int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$provider = $this->providerCollection->get($input->getArgument('provider'));
|
||||
$force = $input->getOption('force');
|
||||
$intlIcu = $input->getOption('intl-icu');
|
||||
$locales = $input->getOption('locales') ?: $this->enabledLocales;
|
||||
$domains = $input->getOption('domains');
|
||||
$format = $input->getOption('format');
|
||||
$xliffVersion = '1.2';
|
||||
if ($intlIcu && !$force) {
|
||||
$io->note('--intl-icu option only has an effect when used with --force. Here, it will be ignored.');
|
||||
}
|
||||
switch ($format) {
|
||||
case 'xlf20':
|
||||
$xliffVersion = '2.0';
|
||||
// no break
|
||||
case 'xlf12':
|
||||
$format = 'xlf';
|
||||
}
|
||||
$writeOptions = ['path' => \end($this->transPaths), 'xliff_version' => $xliffVersion, 'default_locale' => $this->defaultLocale];
|
||||
if (!$domains) {
|
||||
$domains = $provider->getDomains();
|
||||
}
|
||||
$providerTranslations = $provider->read($domains, $locales);
|
||||
if ($force) {
|
||||
foreach ($providerTranslations->getCatalogues() as $catalogue) {
|
||||
$operation = new TargetOperation(new MessageCatalogue($catalogue->getLocale()), $catalogue);
|
||||
if ($intlIcu) {
|
||||
$operation->moveMessagesToIntlDomainsIfPossible();
|
||||
}
|
||||
$this->writer->write($operation->getResult(), $format, $writeOptions);
|
||||
}
|
||||
$io->success(\sprintf('Local translations has been updated from "%s" (for "%s" locale(s), and "%s" domain(s)).', \parse_url($provider, \PHP_URL_SCHEME), \implode(', ', $locales), \implode(', ', $domains)));
|
||||
return 0;
|
||||
}
|
||||
$localTranslations = $this->readLocalTranslations($locales, $domains, $this->transPaths);
|
||||
// Append pulled translations to local ones.
|
||||
$localTranslations->addBag($providerTranslations->diff($localTranslations));
|
||||
foreach ($localTranslations->getCatalogues() as $catalogue) {
|
||||
$this->writer->write($catalogue, $format, $writeOptions);
|
||||
}
|
||||
$io->success(\sprintf('New translations from "%s" has been written locally (for "%s" locale(s), and "%s" domain(s)).', \parse_url($provider, \PHP_URL_SCHEME), \implode(', ', $locales), \implode(', ', $domains)));
|
||||
return 0;
|
||||
}
|
||||
}
|
139
dependencies/symfony/translation/Command/TranslationPushCommand.php
vendored
Normal file
139
dependencies/symfony/translation/Command/TranslationPushCommand.php
vendored
Normal file
@ -0,0 +1,139 @@
|
||||
<?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\Command;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Console\Attribute\AsCommand;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Console\Command\Command;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Console\Completion\CompletionInput;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Console\Completion\CompletionSuggestions;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Console\Exception\InvalidArgumentException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Console\Input\InputArgument;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Console\Input\InputInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Console\Input\InputOption;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Console\Output\OutputInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\Translation\Provider\FilteringProvider;
|
||||
use Symfony\Component\Translation\Provider\TranslationProviderCollection;
|
||||
use Symfony\Component\Translation\Reader\TranslationReaderInterface;
|
||||
use Symfony\Component\Translation\TranslatorBag;
|
||||
/**
|
||||
* @author Mathieu Santostefano <msantostefano@protonmail.com>
|
||||
*/
|
||||
#[AsCommand(name: 'translation:push', description: 'Push translations to a given provider.')]
|
||||
final class TranslationPushCommand extends Command
|
||||
{
|
||||
use \Symfony\Component\Translation\Command\TranslationTrait;
|
||||
private TranslationProviderCollection $providers;
|
||||
private TranslationReaderInterface $reader;
|
||||
private array $transPaths;
|
||||
private array $enabledLocales;
|
||||
public function __construct(TranslationProviderCollection $providers, TranslationReaderInterface $reader, array $transPaths = [], array $enabledLocales = [])
|
||||
{
|
||||
$this->providers = $providers;
|
||||
$this->reader = $reader;
|
||||
$this->transPaths = $transPaths;
|
||||
$this->enabledLocales = $enabledLocales;
|
||||
parent::__construct();
|
||||
}
|
||||
public function complete(CompletionInput $input, CompletionSuggestions $suggestions) : void
|
||||
{
|
||||
if ($input->mustSuggestArgumentValuesFor('provider')) {
|
||||
$suggestions->suggestValues($this->providers->keys());
|
||||
return;
|
||||
}
|
||||
if ($input->mustSuggestOptionValuesFor('domains')) {
|
||||
$provider = $this->providers->get($input->getArgument('provider'));
|
||||
if ($provider && \method_exists($provider, 'getDomains')) {
|
||||
$domains = $provider->getDomains();
|
||||
$suggestions->suggestValues($domains);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ($input->mustSuggestOptionValuesFor('locales')) {
|
||||
$suggestions->suggestValues($this->enabledLocales);
|
||||
}
|
||||
}
|
||||
protected function configure() : void
|
||||
{
|
||||
$keys = $this->providers->keys();
|
||||
$defaultProvider = 1 === \count($keys) ? $keys[0] : null;
|
||||
$this->setDefinition([new InputArgument('provider', null !== $defaultProvider ? InputArgument::OPTIONAL : InputArgument::REQUIRED, 'The provider to push translations to.', $defaultProvider), new InputOption('force', null, InputOption::VALUE_NONE, 'Override existing translations with local ones (it will delete not synchronized messages).'), new InputOption('delete-missing', null, InputOption::VALUE_NONE, 'Delete translations available on provider but not locally.'), new InputOption('domains', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Specify the domains to push.'), new InputOption('locales', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Specify the locales to push.', $this->enabledLocales)])->setHelp(<<<'EOF'
|
||||
The <info>%command.name%</> command pushes translations to the given provider. Only new
|
||||
translations are pushed, existing ones are not overwritten.
|
||||
|
||||
You can overwrite existing translations by using the <comment>--force</> flag:
|
||||
|
||||
<info>php %command.full_name% --force provider</>
|
||||
|
||||
You can delete provider translations which are not present locally by using the <comment>--delete-missing</> flag:
|
||||
|
||||
<info>php %command.full_name% --delete-missing provider</>
|
||||
|
||||
Full example:
|
||||
|
||||
<info>php %command.full_name% provider --force --delete-missing --domains=messages --domains=validators --locales=en</>
|
||||
|
||||
This command pushes all translations associated with the <comment>messages</> and <comment>validators</> domains for the <comment>en</> locale.
|
||||
Provider translations for the specified domains and locale are deleted if they're not present locally and overwritten if it's the case.
|
||||
Provider translations for others domains and locales are ignored.
|
||||
EOF
|
||||
);
|
||||
}
|
||||
protected function execute(InputInterface $input, OutputInterface $output) : int
|
||||
{
|
||||
$provider = $this->providers->get($input->getArgument('provider'));
|
||||
if (!$this->enabledLocales) {
|
||||
throw new InvalidArgumentException(\sprintf('You must define "framework.enabled_locales" or "framework.translator.providers.%s.locales" config key in order to work with translation providers.', \parse_url($provider, \PHP_URL_SCHEME)));
|
||||
}
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$domains = $input->getOption('domains');
|
||||
$locales = $input->getOption('locales');
|
||||
$force = $input->getOption('force');
|
||||
$deleteMissing = $input->getOption('delete-missing');
|
||||
if (!$domains && $provider instanceof FilteringProvider) {
|
||||
$domains = $provider->getDomains();
|
||||
}
|
||||
// Reading local translations must be done after retrieving the domains from the provider
|
||||
// in order to manage only translations from configured domains
|
||||
$localTranslations = $this->readLocalTranslations($locales, $domains, $this->transPaths);
|
||||
if (!$domains) {
|
||||
$domains = $this->getDomainsFromTranslatorBag($localTranslations);
|
||||
}
|
||||
if (!$deleteMissing && $force) {
|
||||
$provider->write($localTranslations);
|
||||
$io->success(\sprintf('All local translations has been sent to "%s" (for "%s" locale(s), and "%s" domain(s)).', \parse_url($provider, \PHP_URL_SCHEME), \implode(', ', $locales), \implode(', ', $domains)));
|
||||
return 0;
|
||||
}
|
||||
$providerTranslations = $provider->read($domains, $locales);
|
||||
if ($deleteMissing) {
|
||||
$provider->delete($providerTranslations->diff($localTranslations));
|
||||
$io->success(\sprintf('Missing translations on "%s" has been deleted (for "%s" locale(s), and "%s" domain(s)).', \parse_url($provider, \PHP_URL_SCHEME), \implode(', ', $locales), \implode(', ', $domains)));
|
||||
// Read provider translations again, after missing translations deletion,
|
||||
// to avoid push freshly deleted translations.
|
||||
$providerTranslations = $provider->read($domains, $locales);
|
||||
}
|
||||
$translationsToWrite = $localTranslations->diff($providerTranslations);
|
||||
if ($force) {
|
||||
$translationsToWrite->addBag($localTranslations->intersect($providerTranslations));
|
||||
}
|
||||
$provider->write($translationsToWrite);
|
||||
$io->success(\sprintf('%s local translations has been sent to "%s" (for "%s" locale(s), and "%s" domain(s)).', $force ? 'All' : 'New', \parse_url($provider, \PHP_URL_SCHEME), \implode(', ', $locales), \implode(', ', $domains)));
|
||||
return 0;
|
||||
}
|
||||
private function getDomainsFromTranslatorBag(TranslatorBag $translatorBag) : array
|
||||
{
|
||||
$domains = [];
|
||||
foreach ($translatorBag->getCatalogues() as $catalogue) {
|
||||
$domains += $catalogue->getDomains();
|
||||
}
|
||||
return \array_unique($domains);
|
||||
}
|
||||
}
|
66
dependencies/symfony/translation/Command/TranslationTrait.php
vendored
Normal file
66
dependencies/symfony/translation/Command/TranslationTrait.php
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
<?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\Command;
|
||||
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
use Symfony\Component\Translation\MessageCatalogueInterface;
|
||||
use Symfony\Component\Translation\TranslatorBag;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
trait TranslationTrait
|
||||
{
|
||||
private function readLocalTranslations(array $locales, array $domains, array $transPaths) : TranslatorBag
|
||||
{
|
||||
$bag = new TranslatorBag();
|
||||
foreach ($locales as $locale) {
|
||||
$catalogue = new MessageCatalogue($locale);
|
||||
foreach ($transPaths as $path) {
|
||||
$this->reader->read($path, $catalogue);
|
||||
}
|
||||
if ($domains) {
|
||||
foreach ($domains as $domain) {
|
||||
$bag->addCatalogue($this->filterCatalogue($catalogue, $domain));
|
||||
}
|
||||
} else {
|
||||
$bag->addCatalogue($catalogue);
|
||||
}
|
||||
}
|
||||
return $bag;
|
||||
}
|
||||
private function filterCatalogue(MessageCatalogue $catalogue, string $domain) : MessageCatalogue
|
||||
{
|
||||
$filteredCatalogue = new MessageCatalogue($catalogue->getLocale());
|
||||
// extract intl-icu messages only
|
||||
$intlDomain = $domain . MessageCatalogueInterface::INTL_DOMAIN_SUFFIX;
|
||||
if ($intlMessages = $catalogue->all($intlDomain)) {
|
||||
$filteredCatalogue->add($intlMessages, $intlDomain);
|
||||
}
|
||||
// extract all messages and subtract intl-icu messages
|
||||
if ($messages = \array_diff($catalogue->all($domain), $intlMessages)) {
|
||||
$filteredCatalogue->add($messages, $domain);
|
||||
}
|
||||
foreach ($catalogue->getResources() as $resource) {
|
||||
$filteredCatalogue->addResource($resource);
|
||||
}
|
||||
if ($metadata = $catalogue->getMetadata('', $intlDomain)) {
|
||||
foreach ($metadata as $k => $v) {
|
||||
$filteredCatalogue->setMetadata($k, $v, $intlDomain);
|
||||
}
|
||||
}
|
||||
if ($metadata = $catalogue->getMetadata('', $domain)) {
|
||||
foreach ($metadata as $k => $v) {
|
||||
$filteredCatalogue->setMetadata($k, $v, $domain);
|
||||
}
|
||||
}
|
||||
return $filteredCatalogue;
|
||||
}
|
||||
}
|
225
dependencies/symfony/translation/Command/XliffLintCommand.php
vendored
Normal file
225
dependencies/symfony/translation/Command/XliffLintCommand.php
vendored
Normal file
@ -0,0 +1,225 @@
|
||||
<?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\Command;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Console\Attribute\AsCommand;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Console\CI\GithubActionReporter;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Console\Command\Command;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Console\Completion\CompletionInput;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Console\Completion\CompletionSuggestions;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Console\Exception\RuntimeException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Console\Input\InputArgument;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Console\Input\InputInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Console\Input\InputOption;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Console\Output\OutputInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\Translation\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Translation\Util\XliffUtils;
|
||||
/**
|
||||
* Validates XLIFF files syntax and outputs encountered errors.
|
||||
*
|
||||
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||
* @author Robin Chalas <robin.chalas@gmail.com>
|
||||
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
|
||||
*/
|
||||
#[AsCommand(name: 'lint:xliff', description: 'Lint an XLIFF file and outputs encountered errors')]
|
||||
class XliffLintCommand extends Command
|
||||
{
|
||||
private string $format;
|
||||
private bool $displayCorrectFiles;
|
||||
private ?\Closure $directoryIteratorProvider;
|
||||
private ?\Closure $isReadableProvider;
|
||||
private bool $requireStrictFileNames;
|
||||
public function __construct(string $name = null, callable $directoryIteratorProvider = null, callable $isReadableProvider = null, bool $requireStrictFileNames = \true)
|
||||
{
|
||||
parent::__construct($name);
|
||||
$this->directoryIteratorProvider = null === $directoryIteratorProvider ? null : $directoryIteratorProvider(...);
|
||||
$this->isReadableProvider = null === $isReadableProvider ? null : $isReadableProvider(...);
|
||||
$this->requireStrictFileNames = $requireStrictFileNames;
|
||||
}
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->addArgument('filename', InputArgument::IS_ARRAY, 'A file, a directory or "-" for reading from STDIN')->addOption('format', null, InputOption::VALUE_REQUIRED, \sprintf('The output format ("%s")', \implode('", "', $this->getAvailableFormatOptions())))->setHelp(<<<EOF
|
||||
The <info>%command.name%</info> command lints an XLIFF file and outputs to STDOUT
|
||||
the first encountered syntax error.
|
||||
|
||||
You can validates XLIFF contents passed from STDIN:
|
||||
|
||||
<info>cat filename | php %command.full_name% -</info>
|
||||
|
||||
You can also validate the syntax of a file:
|
||||
|
||||
<info>php %command.full_name% filename</info>
|
||||
|
||||
Or of a whole directory:
|
||||
|
||||
<info>php %command.full_name% dirname</info>
|
||||
<info>php %command.full_name% dirname --format=json</info>
|
||||
|
||||
EOF
|
||||
);
|
||||
}
|
||||
protected function execute(InputInterface $input, OutputInterface $output) : int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$filenames = (array) $input->getArgument('filename');
|
||||
$this->format = $input->getOption('format') ?? (GithubActionReporter::isGithubActionEnvironment() ? 'github' : 'txt');
|
||||
$this->displayCorrectFiles = $output->isVerbose();
|
||||
if (['-'] === $filenames) {
|
||||
return $this->display($io, [$this->validate(\file_get_contents('php://stdin'))]);
|
||||
}
|
||||
if (!$filenames) {
|
||||
throw new RuntimeException('Please provide a filename or pipe file content to STDIN.');
|
||||
}
|
||||
$filesInfo = [];
|
||||
foreach ($filenames as $filename) {
|
||||
if (!$this->isReadable($filename)) {
|
||||
throw new RuntimeException(\sprintf('File or directory "%s" is not readable.', $filename));
|
||||
}
|
||||
foreach ($this->getFiles($filename) as $file) {
|
||||
$filesInfo[] = $this->validate(\file_get_contents($file), $file);
|
||||
}
|
||||
}
|
||||
return $this->display($io, $filesInfo);
|
||||
}
|
||||
private function validate(string $content, string $file = null) : array
|
||||
{
|
||||
$errors = [];
|
||||
// Avoid: Warning DOMDocument::loadXML(): Empty string supplied as input
|
||||
if ('' === \trim($content)) {
|
||||
return ['file' => $file, 'valid' => \true];
|
||||
}
|
||||
$internal = \libxml_use_internal_errors(\true);
|
||||
$document = new \DOMDocument();
|
||||
$document->loadXML($content);
|
||||
if (null !== ($targetLanguage = $this->getTargetLanguageFromFile($document))) {
|
||||
$normalizedLocalePattern = \sprintf('(%s|%s)', \preg_quote($targetLanguage, '/'), \preg_quote(\str_replace('-', '_', $targetLanguage), '/'));
|
||||
// strict file names require translation files to be named '____.locale.xlf'
|
||||
// otherwise, both '____.locale.xlf' and 'locale.____.xlf' are allowed
|
||||
// also, the regexp matching must be case-insensitive, as defined for 'target-language' values
|
||||
// http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#target-language
|
||||
$expectedFilenamePattern = $this->requireStrictFileNames ? \sprintf('/^.*\\.(?i:%s)\\.(?:xlf|xliff)/', $normalizedLocalePattern) : \sprintf('/^(?:.*\\.(?i:%s)|(?i:%s)\\..*)\\.(?:xlf|xliff)/', $normalizedLocalePattern, $normalizedLocalePattern);
|
||||
if (0 === \preg_match($expectedFilenamePattern, \basename($file))) {
|
||||
$errors[] = ['line' => -1, 'column' => -1, 'message' => \sprintf('There is a mismatch between the language included in the file name ("%s") and the "%s" value used in the "target-language" attribute of the file.', \basename($file), $targetLanguage)];
|
||||
}
|
||||
}
|
||||
foreach (XliffUtils::validateSchema($document) as $xmlError) {
|
||||
$errors[] = ['line' => $xmlError['line'], 'column' => $xmlError['column'], 'message' => $xmlError['message']];
|
||||
}
|
||||
\libxml_clear_errors();
|
||||
\libxml_use_internal_errors($internal);
|
||||
return ['file' => $file, 'valid' => 0 === \count($errors), 'messages' => $errors];
|
||||
}
|
||||
private function display(SymfonyStyle $io, array $files) : int
|
||||
{
|
||||
return match ($this->format) {
|
||||
'txt' => $this->displayTxt($io, $files),
|
||||
'json' => $this->displayJson($io, $files),
|
||||
'github' => $this->displayTxt($io, $files, \true),
|
||||
default => throw new InvalidArgumentException(\sprintf('Supported formats are "%s".', \implode('", "', $this->getAvailableFormatOptions()))),
|
||||
};
|
||||
}
|
||||
private function displayTxt(SymfonyStyle $io, array $filesInfo, bool $errorAsGithubAnnotations = \false) : int
|
||||
{
|
||||
$countFiles = \count($filesInfo);
|
||||
$erroredFiles = 0;
|
||||
$githubReporter = $errorAsGithubAnnotations ? new GithubActionReporter($io) : null;
|
||||
foreach ($filesInfo as $info) {
|
||||
if ($info['valid'] && $this->displayCorrectFiles) {
|
||||
$io->comment('<info>OK</info>' . ($info['file'] ? \sprintf(' in %s', $info['file']) : ''));
|
||||
} elseif (!$info['valid']) {
|
||||
++$erroredFiles;
|
||||
$io->text('<error> ERROR </error>' . ($info['file'] ? \sprintf(' in %s', $info['file']) : ''));
|
||||
$io->listing(\array_map(function ($error) use($info, $githubReporter) {
|
||||
// general document errors have a '-1' line number
|
||||
$line = -1 === $error['line'] ? null : $error['line'];
|
||||
$githubReporter?->error($error['message'], $info['file'], $line, null !== $line ? $error['column'] : null);
|
||||
return null === $line ? $error['message'] : \sprintf('Line %d, Column %d: %s', $line, $error['column'], $error['message']);
|
||||
}, $info['messages']));
|
||||
}
|
||||
}
|
||||
if (0 === $erroredFiles) {
|
||||
$io->success(\sprintf('All %d XLIFF files contain valid syntax.', $countFiles));
|
||||
} else {
|
||||
$io->warning(\sprintf('%d XLIFF files have valid syntax and %d contain errors.', $countFiles - $erroredFiles, $erroredFiles));
|
||||
}
|
||||
return \min($erroredFiles, 1);
|
||||
}
|
||||
private function displayJson(SymfonyStyle $io, array $filesInfo) : int
|
||||
{
|
||||
$errors = 0;
|
||||
\array_walk($filesInfo, function (&$v) use(&$errors) {
|
||||
$v['file'] = (string) $v['file'];
|
||||
if (!$v['valid']) {
|
||||
++$errors;
|
||||
}
|
||||
});
|
||||
$io->writeln(\json_encode($filesInfo, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES));
|
||||
return \min($errors, 1);
|
||||
}
|
||||
/**
|
||||
* @return iterable<\SplFileInfo>
|
||||
*/
|
||||
private function getFiles(string $fileOrDirectory) : iterable
|
||||
{
|
||||
if (\is_file($fileOrDirectory)) {
|
||||
(yield new \SplFileInfo($fileOrDirectory));
|
||||
return;
|
||||
}
|
||||
foreach ($this->getDirectoryIterator($fileOrDirectory) as $file) {
|
||||
if (!\in_array($file->getExtension(), ['xlf', 'xliff'])) {
|
||||
continue;
|
||||
}
|
||||
(yield $file);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @return iterable<\SplFileInfo>
|
||||
*/
|
||||
private function getDirectoryIterator(string $directory) : iterable
|
||||
{
|
||||
$default = fn($directory) => new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS), \RecursiveIteratorIterator::LEAVES_ONLY);
|
||||
if (null !== $this->directoryIteratorProvider) {
|
||||
return ($this->directoryIteratorProvider)($directory, $default);
|
||||
}
|
||||
return $default($directory);
|
||||
}
|
||||
private function isReadable(string $fileOrDirectory) : bool
|
||||
{
|
||||
$default = fn($fileOrDirectory) => \is_readable($fileOrDirectory);
|
||||
if (null !== $this->isReadableProvider) {
|
||||
return ($this->isReadableProvider)($fileOrDirectory, $default);
|
||||
}
|
||||
return $default($fileOrDirectory);
|
||||
}
|
||||
private function getTargetLanguageFromFile(\DOMDocument $xliffContents) : ?string
|
||||
{
|
||||
foreach ($xliffContents->getElementsByTagName('file')[0]->attributes ?? [] as $attribute) {
|
||||
if ('target-language' === $attribute->nodeName) {
|
||||
return $attribute->nodeValue;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public function complete(CompletionInput $input, CompletionSuggestions $suggestions) : void
|
||||
{
|
||||
if ($input->mustSuggestOptionValuesFor('format')) {
|
||||
$suggestions->suggestValues($this->getAvailableFormatOptions());
|
||||
}
|
||||
}
|
||||
private function getAvailableFormatOptions() : array
|
||||
{
|
||||
return ['txt', 'json', 'github'];
|
||||
}
|
||||
}
|
118
dependencies/symfony/translation/DataCollector/TranslationDataCollector.php
vendored
Normal file
118
dependencies/symfony/translation/DataCollector/TranslationDataCollector.php
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
<?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\DataCollector;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\HttpFoundation\Request;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\HttpFoundation\Response;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
|
||||
use Symfony\Component\Translation\DataCollectorTranslator;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\VarDumper\Cloner\Data;
|
||||
/**
|
||||
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class TranslationDataCollector extends DataCollector implements LateDataCollectorInterface
|
||||
{
|
||||
private DataCollectorTranslator $translator;
|
||||
public function __construct(DataCollectorTranslator $translator)
|
||||
{
|
||||
$this->translator = $translator;
|
||||
}
|
||||
public function lateCollect() : void
|
||||
{
|
||||
$messages = $this->sanitizeCollectedMessages($this->translator->getCollectedMessages());
|
||||
$this->data += $this->computeCount($messages);
|
||||
$this->data['messages'] = $messages;
|
||||
$this->data = $this->cloneVar($this->data);
|
||||
}
|
||||
public function collect(Request $request, Response $response, \Throwable $exception = null) : void
|
||||
{
|
||||
$this->data['locale'] = $this->translator->getLocale();
|
||||
$this->data['fallback_locales'] = $this->translator->getFallbackLocales();
|
||||
}
|
||||
public function reset() : void
|
||||
{
|
||||
$this->data = [];
|
||||
}
|
||||
public function getMessages() : array|Data
|
||||
{
|
||||
return $this->data['messages'] ?? [];
|
||||
}
|
||||
public function getCountMissings() : int
|
||||
{
|
||||
return $this->data[DataCollectorTranslator::MESSAGE_MISSING] ?? 0;
|
||||
}
|
||||
public function getCountFallbacks() : int
|
||||
{
|
||||
return $this->data[DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK] ?? 0;
|
||||
}
|
||||
public function getCountDefines() : int
|
||||
{
|
||||
return $this->data[DataCollectorTranslator::MESSAGE_DEFINED] ?? 0;
|
||||
}
|
||||
public function getLocale()
|
||||
{
|
||||
return !empty($this->data['locale']) ? $this->data['locale'] : null;
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getFallbackLocales()
|
||||
{
|
||||
return isset($this->data['fallback_locales']) && \count($this->data['fallback_locales']) > 0 ? $this->data['fallback_locales'] : [];
|
||||
}
|
||||
public function getName() : string
|
||||
{
|
||||
return 'translation';
|
||||
}
|
||||
private function sanitizeCollectedMessages(array $messages) : array
|
||||
{
|
||||
$result = [];
|
||||
foreach ($messages as $key => $message) {
|
||||
$messageId = $message['locale'] . $message['domain'] . $message['id'];
|
||||
if (!isset($result[$messageId])) {
|
||||
$message['count'] = 1;
|
||||
$message['parameters'] = !empty($message['parameters']) ? [$message['parameters']] : [];
|
||||
$messages[$key]['translation'] = $this->sanitizeString($message['translation']);
|
||||
$result[$messageId] = $message;
|
||||
} else {
|
||||
if (!empty($message['parameters'])) {
|
||||
$result[$messageId]['parameters'][] = $message['parameters'];
|
||||
}
|
||||
++$result[$messageId]['count'];
|
||||
}
|
||||
unset($messages[$key]);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
private function computeCount(array $messages) : array
|
||||
{
|
||||
$count = [DataCollectorTranslator::MESSAGE_DEFINED => 0, DataCollectorTranslator::MESSAGE_MISSING => 0, DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK => 0];
|
||||
foreach ($messages as $message) {
|
||||
++$count[$message['state']];
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
private function sanitizeString(string $string, int $length = 80) : string
|
||||
{
|
||||
$string = \trim(\preg_replace('/\\s+/', ' ', $string));
|
||||
if (\false !== ($encoding = \mb_detect_encoding($string, null, \true))) {
|
||||
if (\mb_strlen($string, $encoding) > $length) {
|
||||
return \mb_substr($string, 0, $length - 3, $encoding) . '...';
|
||||
}
|
||||
} elseif (\strlen($string) > $length) {
|
||||
return \substr($string, 0, $length - 3) . '...';
|
||||
}
|
||||
return $string;
|
||||
}
|
||||
}
|
116
dependencies/symfony/translation/DataCollectorTranslator.php
vendored
Normal file
116
dependencies/symfony/translation/DataCollectorTranslator.php
vendored
Normal file
@ -0,0 +1,116 @@
|
||||
<?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;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface;
|
||||
use Symfony\Component\Translation\Exception\InvalidArgumentException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Translation\LocaleAwareInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Translation\TranslatorInterface;
|
||||
/**
|
||||
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
|
||||
*/
|
||||
class DataCollectorTranslator implements TranslatorInterface, \Symfony\Component\Translation\TranslatorBagInterface, LocaleAwareInterface, WarmableInterface
|
||||
{
|
||||
public const MESSAGE_DEFINED = 0;
|
||||
public const MESSAGE_MISSING = 1;
|
||||
public const MESSAGE_EQUALS_FALLBACK = 2;
|
||||
private TranslatorInterface $translator;
|
||||
private array $messages = [];
|
||||
/**
|
||||
* @param TranslatorInterface&TranslatorBagInterface&LocaleAwareInterface $translator
|
||||
*/
|
||||
public function __construct(TranslatorInterface $translator)
|
||||
{
|
||||
if (!$translator instanceof \Symfony\Component\Translation\TranslatorBagInterface || !$translator instanceof LocaleAwareInterface) {
|
||||
throw new InvalidArgumentException(\sprintf('The Translator "%s" must implement TranslatorInterface, TranslatorBagInterface and LocaleAwareInterface.', \get_debug_type($translator)));
|
||||
}
|
||||
$this->translator = $translator;
|
||||
}
|
||||
public function trans(?string $id, array $parameters = [], string $domain = null, string $locale = null) : string
|
||||
{
|
||||
$trans = $this->translator->trans($id = (string) $id, $parameters, $domain, $locale);
|
||||
$this->collectMessage($locale, $domain, $id, $trans, $parameters);
|
||||
return $trans;
|
||||
}
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function setLocale(string $locale)
|
||||
{
|
||||
$this->translator->setLocale($locale);
|
||||
}
|
||||
public function getLocale() : string
|
||||
{
|
||||
return $this->translator->getLocale();
|
||||
}
|
||||
public function getCatalogue(string $locale = null) : \Symfony\Component\Translation\MessageCatalogueInterface
|
||||
{
|
||||
return $this->translator->getCatalogue($locale);
|
||||
}
|
||||
public function getCatalogues() : array
|
||||
{
|
||||
return $this->translator->getCatalogues();
|
||||
}
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function warmUp(string $cacheDir) : array
|
||||
{
|
||||
if ($this->translator instanceof WarmableInterface) {
|
||||
return (array) $this->translator->warmUp($cacheDir);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
/**
|
||||
* Gets the fallback locales.
|
||||
*/
|
||||
public function getFallbackLocales() : array
|
||||
{
|
||||
if ($this->translator instanceof \Symfony\Component\Translation\Translator || \method_exists($this->translator, 'getFallbackLocales')) {
|
||||
return $this->translator->getFallbackLocales();
|
||||
}
|
||||
return [];
|
||||
}
|
||||
/**
|
||||
* Passes through all unknown calls onto the translator object.
|
||||
*/
|
||||
public function __call(string $method, array $args)
|
||||
{
|
||||
return $this->translator->{$method}(...$args);
|
||||
}
|
||||
public function getCollectedMessages() : array
|
||||
{
|
||||
return $this->messages;
|
||||
}
|
||||
private function collectMessage(?string $locale, ?string $domain, string $id, string $translation, ?array $parameters = []) : void
|
||||
{
|
||||
$domain ??= 'messages';
|
||||
$catalogue = $this->translator->getCatalogue($locale);
|
||||
$locale = $catalogue->getLocale();
|
||||
$fallbackLocale = null;
|
||||
if ($catalogue->defines($id, $domain)) {
|
||||
$state = self::MESSAGE_DEFINED;
|
||||
} elseif ($catalogue->has($id, $domain)) {
|
||||
$state = self::MESSAGE_EQUALS_FALLBACK;
|
||||
$fallbackCatalogue = $catalogue->getFallbackCatalogue();
|
||||
while ($fallbackCatalogue) {
|
||||
if ($fallbackCatalogue->defines($id, $domain)) {
|
||||
$fallbackLocale = $fallbackCatalogue->getLocale();
|
||||
break;
|
||||
}
|
||||
$fallbackCatalogue = $fallbackCatalogue->getFallbackCatalogue();
|
||||
}
|
||||
} else {
|
||||
$state = self::MESSAGE_MISSING;
|
||||
}
|
||||
$this->messages[] = ['locale' => $locale, 'fallbackLocale' => $fallbackLocale, 'domain' => $domain, 'id' => $id, 'translation' => $translation, 'parameters' => $parameters, 'state' => $state, 'transChoiceNumber' => isset($parameters['%count%']) && \is_numeric($parameters['%count%']) ? $parameters['%count%'] : null];
|
||||
}
|
||||
}
|
34
dependencies/symfony/translation/DependencyInjection/TranslationDumperPass.php
vendored
Normal file
34
dependencies/symfony/translation/DependencyInjection/TranslationDumperPass.php
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
<?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\DependencyInjection;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\Reference;
|
||||
/**
|
||||
* Adds tagged translation.formatter services to translation writer.
|
||||
*/
|
||||
class TranslationDumperPass implements CompilerPassInterface
|
||||
{
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if (!$container->hasDefinition('translation.writer')) {
|
||||
return;
|
||||
}
|
||||
$definition = $container->getDefinition('translation.writer');
|
||||
foreach ($container->findTaggedServiceIds('translation.dumper', \true) as $id => $attributes) {
|
||||
$definition->addMethodCall('addDumper', [$attributes[0]['alias'], new Reference($id)]);
|
||||
}
|
||||
}
|
||||
}
|
38
dependencies/symfony/translation/DependencyInjection/TranslationExtractorPass.php
vendored
Normal file
38
dependencies/symfony/translation/DependencyInjection/TranslationExtractorPass.php
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
<?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\DependencyInjection;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\Reference;
|
||||
/**
|
||||
* Adds tagged translation.extractor services to translation extractor.
|
||||
*/
|
||||
class TranslationExtractorPass implements CompilerPassInterface
|
||||
{
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if (!$container->hasDefinition('translation.extractor')) {
|
||||
return;
|
||||
}
|
||||
$definition = $container->getDefinition('translation.extractor');
|
||||
foreach ($container->findTaggedServiceIds('translation.extractor', \true) as $id => $attributes) {
|
||||
if (!isset($attributes[0]['alias'])) {
|
||||
throw new RuntimeException(\sprintf('The alias for the tag "translation.extractor" of service "%s" must be set.', $id));
|
||||
}
|
||||
$definition->addMethodCall('addExtractor', [$attributes[0]['alias'], new Reference($id)]);
|
||||
}
|
||||
}
|
||||
}
|
78
dependencies/symfony/translation/DependencyInjection/TranslatorPass.php
vendored
Normal file
78
dependencies/symfony/translation/DependencyInjection/TranslatorPass.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\DependencyInjection;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\Reference;
|
||||
class TranslatorPass implements CompilerPassInterface
|
||||
{
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if (!$container->hasDefinition('translator.default')) {
|
||||
return;
|
||||
}
|
||||
$loaders = [];
|
||||
$loaderRefs = [];
|
||||
foreach ($container->findTaggedServiceIds('translation.loader', \true) as $id => $attributes) {
|
||||
$loaderRefs[$id] = new Reference($id);
|
||||
$loaders[$id][] = $attributes[0]['alias'];
|
||||
if (isset($attributes[0]['legacy-alias'])) {
|
||||
$loaders[$id][] = $attributes[0]['legacy-alias'];
|
||||
}
|
||||
}
|
||||
if ($container->hasDefinition('translation.reader')) {
|
||||
$definition = $container->getDefinition('translation.reader');
|
||||
foreach ($loaders as $id => $formats) {
|
||||
foreach ($formats as $format) {
|
||||
$definition->addMethodCall('addLoader', [$format, $loaderRefs[$id]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$container->findDefinition('translator.default')->replaceArgument(0, ServiceLocatorTagPass::register($container, $loaderRefs))->replaceArgument(3, $loaders);
|
||||
if ($container->hasDefinition('validator') && $container->hasDefinition('translation.extractor.visitor.constraint')) {
|
||||
$constraintVisitorDefinition = $container->getDefinition('translation.extractor.visitor.constraint');
|
||||
$constraintClassNames = [];
|
||||
foreach ($container->getDefinitions() as $definition) {
|
||||
if (!$definition->hasTag('validator.constraint_validator')) {
|
||||
continue;
|
||||
}
|
||||
// Resolve constraint validator FQCN even if defined as %foo.validator.class% parameter
|
||||
$className = $container->getParameterBag()->resolveValue($definition->getClass());
|
||||
// Extraction of the constraint class name from the Constraint Validator FQCN
|
||||
$constraintClassNames[] = \str_replace('Validator', '', \substr(\strrchr($className, '\\'), 1));
|
||||
}
|
||||
$constraintVisitorDefinition->setArgument(0, $constraintClassNames);
|
||||
}
|
||||
if (!$container->hasParameter('twig.default_path')) {
|
||||
return;
|
||||
}
|
||||
$paths = \array_keys($container->getDefinition('twig.template_iterator')->getArgument(1));
|
||||
if ($container->hasDefinition('console.command.translation_debug')) {
|
||||
$definition = $container->getDefinition('console.command.translation_debug');
|
||||
$definition->replaceArgument(4, $container->getParameter('twig.default_path'));
|
||||
if (\count($definition->getArguments()) > 6) {
|
||||
$definition->replaceArgument(6, $paths);
|
||||
}
|
||||
}
|
||||
if ($container->hasDefinition('console.command.translation_extract')) {
|
||||
$definition = $container->getDefinition('console.command.translation_extract');
|
||||
$definition->replaceArgument(5, $container->getParameter('twig.default_path'));
|
||||
if (\count($definition->getArguments()) > 7) {
|
||||
$definition->replaceArgument(7, $paths);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
123
dependencies/symfony/translation/DependencyInjection/TranslatorPathsPass.php
vendored
Normal file
123
dependencies/symfony/translation/DependencyInjection/TranslatorPathsPass.php
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
<?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\DependencyInjection;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\Compiler\AbstractRecursivePass;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\Definition;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\Reference;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\HttpKernel\Controller\ArgumentResolver\TraceableValueResolver;
|
||||
/**
|
||||
* @author Yonel Ceruto <yonelceruto@gmail.com>
|
||||
*/
|
||||
class TranslatorPathsPass extends AbstractRecursivePass
|
||||
{
|
||||
private int $level = 0;
|
||||
/**
|
||||
* @var array<string, bool>
|
||||
*/
|
||||
private array $paths = [];
|
||||
/**
|
||||
* @var array<int, Definition>
|
||||
*/
|
||||
private array $definitions = [];
|
||||
/**
|
||||
* @var array<string, array<string, bool>>
|
||||
*/
|
||||
private array $controllers = [];
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if (!$container->hasDefinition('translator')) {
|
||||
return;
|
||||
}
|
||||
foreach ($this->findControllerArguments($container) as $controller => $argument) {
|
||||
$id = \substr($controller, 0, \strpos($controller, ':') ?: \strlen($controller));
|
||||
if ($container->hasDefinition($id)) {
|
||||
[$locatorRef] = $argument->getValues();
|
||||
$this->controllers[(string) $locatorRef][$container->getDefinition($id)->getClass()] = \true;
|
||||
}
|
||||
}
|
||||
try {
|
||||
parent::process($container);
|
||||
$paths = [];
|
||||
foreach ($this->paths as $class => $_) {
|
||||
if (($r = $container->getReflectionClass($class)) && !$r->isInterface()) {
|
||||
$paths[] = $r->getFileName();
|
||||
foreach ($r->getTraits() as $trait) {
|
||||
$paths[] = $trait->getFileName();
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($paths) {
|
||||
if ($container->hasDefinition('console.command.translation_debug')) {
|
||||
$definition = $container->getDefinition('console.command.translation_debug');
|
||||
$definition->replaceArgument(6, \array_merge($definition->getArgument(6), $paths));
|
||||
}
|
||||
if ($container->hasDefinition('console.command.translation_extract')) {
|
||||
$definition = $container->getDefinition('console.command.translation_extract');
|
||||
$definition->replaceArgument(7, \array_merge($definition->getArgument(7), $paths));
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
$this->level = 0;
|
||||
$this->paths = [];
|
||||
$this->definitions = [];
|
||||
}
|
||||
}
|
||||
protected function processValue(mixed $value, bool $isRoot = \false) : mixed
|
||||
{
|
||||
if ($value instanceof Reference) {
|
||||
if ('translator' === (string) $value) {
|
||||
for ($i = $this->level - 1; $i >= 0; --$i) {
|
||||
$class = $this->definitions[$i]->getClass();
|
||||
if (ServiceLocator::class === $class) {
|
||||
if (!isset($this->controllers[$this->currentId])) {
|
||||
continue;
|
||||
}
|
||||
foreach ($this->controllers[$this->currentId] as $class => $_) {
|
||||
$this->paths[$class] = \true;
|
||||
}
|
||||
} else {
|
||||
$this->paths[$class] = \true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
if ($value instanceof Definition) {
|
||||
$this->definitions[$this->level++] = $value;
|
||||
$value = parent::processValue($value, $isRoot);
|
||||
unset($this->definitions[--$this->level]);
|
||||
return $value;
|
||||
}
|
||||
return parent::processValue($value, $isRoot);
|
||||
}
|
||||
private function findControllerArguments(ContainerBuilder $container) : array
|
||||
{
|
||||
if (!$container->has('argument_resolver.service')) {
|
||||
return [];
|
||||
}
|
||||
$resolverDef = $container->findDefinition('argument_resolver.service');
|
||||
if (TraceableValueResolver::class === $resolverDef->getClass()) {
|
||||
$resolverDef = $container->getDefinition($resolverDef->getArgument(0));
|
||||
}
|
||||
$argument = $resolverDef->getArgument(0);
|
||||
if ($argument instanceof Reference) {
|
||||
$argument = $container->getDefinition($argument);
|
||||
}
|
||||
return $argument->getArgument(0);
|
||||
}
|
||||
}
|
48
dependencies/symfony/translation/Dumper/CsvFileDumper.php
vendored
Normal file
48
dependencies/symfony/translation/Dumper/CsvFileDumper.php
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
<?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\Dumper;
|
||||
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
/**
|
||||
* CsvFileDumper generates a csv formatted string representation of a message catalogue.
|
||||
*
|
||||
* @author Stealth35
|
||||
*/
|
||||
class CsvFileDumper extends \Symfony\Component\Translation\Dumper\FileDumper
|
||||
{
|
||||
private string $delimiter = ';';
|
||||
private string $enclosure = '"';
|
||||
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []) : string
|
||||
{
|
||||
$handle = \fopen('php://memory', 'r+');
|
||||
foreach ($messages->all($domain) as $source => $target) {
|
||||
\fputcsv($handle, [$source, $target], $this->delimiter, $this->enclosure);
|
||||
}
|
||||
\rewind($handle);
|
||||
$output = \stream_get_contents($handle);
|
||||
\fclose($handle);
|
||||
return $output;
|
||||
}
|
||||
/**
|
||||
* Sets the delimiter and escape character for CSV.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setCsvControl(string $delimiter = ';', string $enclosure = '"')
|
||||
{
|
||||
$this->delimiter = $delimiter;
|
||||
$this->enclosure = $enclosure;
|
||||
}
|
||||
protected function getExtension() : string
|
||||
{
|
||||
return 'csv';
|
||||
}
|
||||
}
|
30
dependencies/symfony/translation/Dumper/DumperInterface.php
vendored
Normal file
30
dependencies/symfony/translation/Dumper/DumperInterface.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?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\Dumper;
|
||||
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
/**
|
||||
* DumperInterface is the interface implemented by all translation dumpers.
|
||||
* There is no common option.
|
||||
*
|
||||
* @author Michel Salib <michelsalib@hotmail.com>
|
||||
*/
|
||||
interface DumperInterface
|
||||
{
|
||||
/**
|
||||
* Dumps the message catalogue.
|
||||
*
|
||||
* @param array $options Options that are used by the dumper
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function dump(MessageCatalogue $messages, array $options = []);
|
||||
}
|
93
dependencies/symfony/translation/Dumper/FileDumper.php
vendored
Normal file
93
dependencies/symfony/translation/Dumper/FileDumper.php
vendored
Normal file
@ -0,0 +1,93 @@
|
||||
<?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\Dumper;
|
||||
|
||||
use Symfony\Component\Translation\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Translation\Exception\RuntimeException;
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
/**
|
||||
* FileDumper is an implementation of DumperInterface that dump a message catalogue to file(s).
|
||||
*
|
||||
* Options:
|
||||
* - path (mandatory): the directory where the files should be saved
|
||||
*
|
||||
* @author Michel Salib <michelsalib@hotmail.com>
|
||||
*/
|
||||
abstract class FileDumper implements \Symfony\Component\Translation\Dumper\DumperInterface
|
||||
{
|
||||
/**
|
||||
* A template for the relative paths to files.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $relativePathTemplate = '%domain%.%locale%.%extension%';
|
||||
/**
|
||||
* Sets the template for the relative paths to files.
|
||||
*
|
||||
* @param string $relativePathTemplate A template for the relative paths to files
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setRelativePathTemplate(string $relativePathTemplate)
|
||||
{
|
||||
$this->relativePathTemplate = $relativePathTemplate;
|
||||
}
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function dump(MessageCatalogue $messages, array $options = [])
|
||||
{
|
||||
if (!\array_key_exists('path', $options)) {
|
||||
throw new InvalidArgumentException('The file dumper needs a path option.');
|
||||
}
|
||||
// save a file for each domain
|
||||
foreach ($messages->getDomains() as $domain) {
|
||||
$fullpath = $options['path'] . '/' . $this->getRelativePath($domain, $messages->getLocale());
|
||||
if (!\file_exists($fullpath)) {
|
||||
$directory = \dirname($fullpath);
|
||||
if (!\file_exists($directory) && !@\mkdir($directory, 0777, \true)) {
|
||||
throw new RuntimeException(\sprintf('Unable to create directory "%s".', $directory));
|
||||
}
|
||||
}
|
||||
$intlDomain = $domain . MessageCatalogue::INTL_DOMAIN_SUFFIX;
|
||||
$intlMessages = $messages->all($intlDomain);
|
||||
if ($intlMessages) {
|
||||
$intlPath = $options['path'] . '/' . $this->getRelativePath($intlDomain, $messages->getLocale());
|
||||
\file_put_contents($intlPath, $this->formatCatalogue($messages, $intlDomain, $options));
|
||||
$messages->replace([], $intlDomain);
|
||||
try {
|
||||
if ($messages->all($domain)) {
|
||||
\file_put_contents($fullpath, $this->formatCatalogue($messages, $domain, $options));
|
||||
}
|
||||
continue;
|
||||
} finally {
|
||||
$messages->replace($intlMessages, $intlDomain);
|
||||
}
|
||||
}
|
||||
\file_put_contents($fullpath, $this->formatCatalogue($messages, $domain, $options));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Transforms a domain of a message catalogue to its string representation.
|
||||
*/
|
||||
public abstract function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []) : string;
|
||||
/**
|
||||
* Gets the file extension of the dumper.
|
||||
*/
|
||||
protected abstract function getExtension() : string;
|
||||
/**
|
||||
* Gets the relative file path using the template.
|
||||
*/
|
||||
private function getRelativePath(string $domain, string $locale) : string
|
||||
{
|
||||
return \strtr($this->relativePathTemplate, ['%domain%' => $domain, '%locale%' => $locale, '%extension%' => $this->getExtension()]);
|
||||
}
|
||||
}
|
96
dependencies/symfony/translation/Dumper/IcuResFileDumper.php
vendored
Normal file
96
dependencies/symfony/translation/Dumper/IcuResFileDumper.php
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
<?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\Dumper;
|
||||
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
/**
|
||||
* IcuResDumper generates an ICU ResourceBundle formatted string representation of a message catalogue.
|
||||
*
|
||||
* @author Stealth35
|
||||
*/
|
||||
class IcuResFileDumper extends \Symfony\Component\Translation\Dumper\FileDumper
|
||||
{
|
||||
protected $relativePathTemplate = '%domain%/%locale%.%extension%';
|
||||
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []) : string
|
||||
{
|
||||
$data = $indexes = $resources = '';
|
||||
foreach ($messages->all($domain) as $source => $target) {
|
||||
$indexes .= \pack('v', \strlen($data) + 28);
|
||||
$data .= $source . "\x00";
|
||||
}
|
||||
$data .= $this->writePadding($data);
|
||||
$keyTop = $this->getPosition($data);
|
||||
foreach ($messages->all($domain) as $source => $target) {
|
||||
$resources .= \pack('V', $this->getPosition($data));
|
||||
$data .= \pack('V', \strlen($target)) . \mb_convert_encoding($target . "\x00", 'UTF-16LE', 'UTF-8') . $this->writePadding($data);
|
||||
}
|
||||
$resOffset = $this->getPosition($data);
|
||||
$data .= \pack('v', \count($messages->all($domain))) . $indexes . $this->writePadding($data) . $resources;
|
||||
$bundleTop = $this->getPosition($data);
|
||||
$root = \pack(
|
||||
'V7',
|
||||
$resOffset + (2 << 28),
|
||||
// Resource Offset + Resource Type
|
||||
6,
|
||||
// Index length
|
||||
$keyTop,
|
||||
// Index keys top
|
||||
$bundleTop,
|
||||
// Index resources top
|
||||
$bundleTop,
|
||||
// Index bundle top
|
||||
\count($messages->all($domain)),
|
||||
// Index max table length
|
||||
0
|
||||
);
|
||||
$header = \pack(
|
||||
'vC2v4C12@32',
|
||||
32,
|
||||
// Header size
|
||||
0xda,
|
||||
0x27,
|
||||
// Magic number 1 and 2
|
||||
20,
|
||||
0,
|
||||
0,
|
||||
2,
|
||||
// Rest of the header, ..., Size of a char
|
||||
0x52,
|
||||
0x65,
|
||||
0x73,
|
||||
0x42,
|
||||
// Data format identifier
|
||||
1,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
// Data version
|
||||
1,
|
||||
4,
|
||||
0,
|
||||
0
|
||||
);
|
||||
return $header . $root . $data;
|
||||
}
|
||||
private function writePadding(string $data) : ?string
|
||||
{
|
||||
$padding = \strlen($data) % 4;
|
||||
return $padding ? \str_repeat("\xaa", 4 - $padding) : null;
|
||||
}
|
||||
private function getPosition(string $data) : float|int
|
||||
{
|
||||
return (\strlen($data) + 28) / 4;
|
||||
}
|
||||
protected function getExtension() : string
|
||||
{
|
||||
return 'res';
|
||||
}
|
||||
}
|
34
dependencies/symfony/translation/Dumper/IniFileDumper.php
vendored
Normal file
34
dependencies/symfony/translation/Dumper/IniFileDumper.php
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
<?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\Dumper;
|
||||
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
/**
|
||||
* IniFileDumper generates an ini formatted string representation of a message catalogue.
|
||||
*
|
||||
* @author Stealth35
|
||||
*/
|
||||
class IniFileDumper extends \Symfony\Component\Translation\Dumper\FileDumper
|
||||
{
|
||||
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []) : string
|
||||
{
|
||||
$output = '';
|
||||
foreach ($messages->all($domain) as $source => $target) {
|
||||
$escapeTarget = \str_replace('"', '\\"', $target);
|
||||
$output .= $source . '="' . $escapeTarget . "\"\n";
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
protected function getExtension() : string
|
||||
{
|
||||
return 'ini';
|
||||
}
|
||||
}
|
30
dependencies/symfony/translation/Dumper/JsonFileDumper.php
vendored
Normal file
30
dependencies/symfony/translation/Dumper/JsonFileDumper.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?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\Dumper;
|
||||
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
/**
|
||||
* JsonFileDumper generates an json formatted string representation of a message catalogue.
|
||||
*
|
||||
* @author singles
|
||||
*/
|
||||
class JsonFileDumper extends \Symfony\Component\Translation\Dumper\FileDumper
|
||||
{
|
||||
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []) : string
|
||||
{
|
||||
$flags = $options['json_encoding'] ?? \JSON_PRETTY_PRINT;
|
||||
return \json_encode($messages->all($domain), $flags);
|
||||
}
|
||||
protected function getExtension() : string
|
||||
{
|
||||
return 'json';
|
||||
}
|
||||
}
|
51
dependencies/symfony/translation/Dumper/MoFileDumper.php
vendored
Normal file
51
dependencies/symfony/translation/Dumper/MoFileDumper.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\Dumper;
|
||||
|
||||
use Symfony\Component\Translation\Loader\MoFileLoader;
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
/**
|
||||
* MoFileDumper generates a gettext formatted string representation of a message catalogue.
|
||||
*
|
||||
* @author Stealth35
|
||||
*/
|
||||
class MoFileDumper extends \Symfony\Component\Translation\Dumper\FileDumper
|
||||
{
|
||||
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []) : string
|
||||
{
|
||||
$sources = $targets = $sourceOffsets = $targetOffsets = '';
|
||||
$offsets = [];
|
||||
$size = 0;
|
||||
foreach ($messages->all($domain) as $source => $target) {
|
||||
$offsets[] = \array_map('strlen', [$sources, $source, $targets, $target]);
|
||||
$sources .= "\x00" . $source;
|
||||
$targets .= "\x00" . $target;
|
||||
++$size;
|
||||
}
|
||||
$header = ['magicNumber' => MoFileLoader::MO_LITTLE_ENDIAN_MAGIC, 'formatRevision' => 0, 'count' => $size, 'offsetId' => MoFileLoader::MO_HEADER_SIZE, 'offsetTranslated' => MoFileLoader::MO_HEADER_SIZE + 8 * $size, 'sizeHashes' => 0, 'offsetHashes' => MoFileLoader::MO_HEADER_SIZE + 16 * $size];
|
||||
$sourcesSize = \strlen($sources);
|
||||
$sourcesStart = $header['offsetHashes'] + 1;
|
||||
foreach ($offsets as $offset) {
|
||||
$sourceOffsets .= $this->writeLong($offset[1]) . $this->writeLong($offset[0] + $sourcesStart);
|
||||
$targetOffsets .= $this->writeLong($offset[3]) . $this->writeLong($offset[2] + $sourcesStart + $sourcesSize);
|
||||
}
|
||||
$output = \implode('', \array_map($this->writeLong(...), $header)) . $sourceOffsets . $targetOffsets . $sources . $targets;
|
||||
return $output;
|
||||
}
|
||||
protected function getExtension() : string
|
||||
{
|
||||
return 'mo';
|
||||
}
|
||||
private function writeLong(mixed $str) : string
|
||||
{
|
||||
return \pack('V*', $str);
|
||||
}
|
||||
}
|
29
dependencies/symfony/translation/Dumper/PhpFileDumper.php
vendored
Normal file
29
dependencies/symfony/translation/Dumper/PhpFileDumper.php
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
<?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\Dumper;
|
||||
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
/**
|
||||
* PhpFileDumper generates PHP files from a message catalogue.
|
||||
*
|
||||
* @author Michel Salib <michelsalib@hotmail.com>
|
||||
*/
|
||||
class PhpFileDumper extends \Symfony\Component\Translation\Dumper\FileDumper
|
||||
{
|
||||
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []) : string
|
||||
{
|
||||
return "<?php\n\nreturn " . \var_export($messages->all($domain), \true) . ";\n";
|
||||
}
|
||||
protected function getExtension() : string
|
||||
{
|
||||
return 'php';
|
||||
}
|
||||
}
|
115
dependencies/symfony/translation/Dumper/PoFileDumper.php
vendored
Normal file
115
dependencies/symfony/translation/Dumper/PoFileDumper.php
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
<?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\Dumper;
|
||||
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
/**
|
||||
* PoFileDumper generates a gettext formatted string representation of a message catalogue.
|
||||
*
|
||||
* @author Stealth35
|
||||
*/
|
||||
class PoFileDumper extends \Symfony\Component\Translation\Dumper\FileDumper
|
||||
{
|
||||
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []) : string
|
||||
{
|
||||
$output = 'msgid ""' . "\n";
|
||||
$output .= 'msgstr ""' . "\n";
|
||||
$output .= '"Content-Type: text/plain; charset=UTF-8\\n"' . "\n";
|
||||
$output .= '"Content-Transfer-Encoding: 8bit\\n"' . "\n";
|
||||
$output .= '"Language: ' . $messages->getLocale() . '\\n"' . "\n";
|
||||
$output .= "\n";
|
||||
$newLine = \false;
|
||||
foreach ($messages->all($domain) as $source => $target) {
|
||||
if ($newLine) {
|
||||
$output .= "\n";
|
||||
} else {
|
||||
$newLine = \true;
|
||||
}
|
||||
$metadata = $messages->getMetadata($source, $domain);
|
||||
if (isset($metadata['comments'])) {
|
||||
$output .= $this->formatComments($metadata['comments']);
|
||||
}
|
||||
if (isset($metadata['flags'])) {
|
||||
$output .= $this->formatComments(\implode(',', (array) $metadata['flags']), ',');
|
||||
}
|
||||
if (isset($metadata['sources'])) {
|
||||
$output .= $this->formatComments(\implode(' ', (array) $metadata['sources']), ':');
|
||||
}
|
||||
$sourceRules = $this->getStandardRules($source);
|
||||
$targetRules = $this->getStandardRules($target);
|
||||
if (2 == \count($sourceRules) && [] !== $targetRules) {
|
||||
$output .= \sprintf('msgid "%s"' . "\n", $this->escape($sourceRules[0]));
|
||||
$output .= \sprintf('msgid_plural "%s"' . "\n", $this->escape($sourceRules[1]));
|
||||
foreach ($targetRules as $i => $targetRule) {
|
||||
$output .= \sprintf('msgstr[%d] "%s"' . "\n", $i, $this->escape($targetRule));
|
||||
}
|
||||
} else {
|
||||
$output .= \sprintf('msgid "%s"' . "\n", $this->escape($source));
|
||||
$output .= \sprintf('msgstr "%s"' . "\n", $this->escape($target));
|
||||
}
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
private function getStandardRules(string $id) : array
|
||||
{
|
||||
// Partly copied from TranslatorTrait::trans.
|
||||
$parts = [];
|
||||
if (\preg_match('/^\\|++$/', $id)) {
|
||||
$parts = \explode('|', $id);
|
||||
} elseif (\preg_match_all('/(?:\\|\\||[^\\|])++/', $id, $matches)) {
|
||||
$parts = $matches[0];
|
||||
}
|
||||
$intervalRegexp = <<<'EOF'
|
||||
/^(?P<interval>
|
||||
({\s*
|
||||
(\-?\d+(\.\d+)?[\s*,\s*\-?\d+(\.\d+)?]*)
|
||||
\s*})
|
||||
|
||||
|
|
||||
|
||||
(?P<left_delimiter>[\[\]])
|
||||
\s*
|
||||
(?P<left>-Inf|\-?\d+(\.\d+)?)
|
||||
\s*,\s*
|
||||
(?P<right>\+?Inf|\-?\d+(\.\d+)?)
|
||||
\s*
|
||||
(?P<right_delimiter>[\[\]])
|
||||
)\s*(?P<message>.*?)$/xs
|
||||
EOF;
|
||||
$standardRules = [];
|
||||
foreach ($parts as $part) {
|
||||
$part = \trim(\str_replace('||', '|', $part));
|
||||
if (\preg_match($intervalRegexp, $part)) {
|
||||
// Explicit rule is not a standard rule.
|
||||
return [];
|
||||
} else {
|
||||
$standardRules[] = $part;
|
||||
}
|
||||
}
|
||||
return $standardRules;
|
||||
}
|
||||
protected function getExtension() : string
|
||||
{
|
||||
return 'po';
|
||||
}
|
||||
private function escape(string $str) : string
|
||||
{
|
||||
return \addcslashes($str, "\x00..\x1f\"\\");
|
||||
}
|
||||
private function formatComments(string|array $comments, string $prefix = '') : ?string
|
||||
{
|
||||
$output = null;
|
||||
foreach ((array) $comments as $comment) {
|
||||
$output .= \sprintf('#%s %s' . "\n", $prefix, $comment);
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
}
|
50
dependencies/symfony/translation/Dumper/QtFileDumper.php
vendored
Normal file
50
dependencies/symfony/translation/Dumper/QtFileDumper.php
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
<?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\Dumper;
|
||||
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
/**
|
||||
* QtFileDumper generates ts files from a message catalogue.
|
||||
*
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
class QtFileDumper extends \Symfony\Component\Translation\Dumper\FileDumper
|
||||
{
|
||||
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []) : string
|
||||
{
|
||||
$dom = new \DOMDocument('1.0', 'utf-8');
|
||||
$dom->formatOutput = \true;
|
||||
$ts = $dom->appendChild($dom->createElement('TS'));
|
||||
$context = $ts->appendChild($dom->createElement('context'));
|
||||
$context->appendChild($dom->createElement('name', $domain));
|
||||
foreach ($messages->all($domain) as $source => $target) {
|
||||
$message = $context->appendChild($dom->createElement('message'));
|
||||
$metadata = $messages->getMetadata($source, $domain);
|
||||
if (isset($metadata['sources'])) {
|
||||
foreach ((array) $metadata['sources'] as $location) {
|
||||
$loc = \explode(':', $location, 2);
|
||||
$location = $message->appendChild($dom->createElement('location'));
|
||||
$location->setAttribute('filename', $loc[0]);
|
||||
if (isset($loc[1])) {
|
||||
$location->setAttribute('line', $loc[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$message->appendChild($dom->createElement('source', $source));
|
||||
$message->appendChild($dom->createElement('translation', $target));
|
||||
}
|
||||
return $dom->saveXML();
|
||||
}
|
||||
protected function getExtension() : string
|
||||
{
|
||||
return 'ts';
|
||||
}
|
||||
}
|
180
dependencies/symfony/translation/Dumper/XliffFileDumper.php
vendored
Normal file
180
dependencies/symfony/translation/Dumper/XliffFileDumper.php
vendored
Normal file
@ -0,0 +1,180 @@
|
||||
<?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\Dumper;
|
||||
|
||||
use Symfony\Component\Translation\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
/**
|
||||
* XliffFileDumper generates xliff files from a message catalogue.
|
||||
*
|
||||
* @author Michel Salib <michelsalib@hotmail.com>
|
||||
*/
|
||||
class XliffFileDumper extends \Symfony\Component\Translation\Dumper\FileDumper
|
||||
{
|
||||
public function __construct(private string $extension = 'xlf')
|
||||
{
|
||||
}
|
||||
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []) : string
|
||||
{
|
||||
$xliffVersion = '1.2';
|
||||
if (\array_key_exists('xliff_version', $options)) {
|
||||
$xliffVersion = $options['xliff_version'];
|
||||
}
|
||||
if (\array_key_exists('default_locale', $options)) {
|
||||
$defaultLocale = $options['default_locale'];
|
||||
} else {
|
||||
$defaultLocale = \Locale::getDefault();
|
||||
}
|
||||
if ('1.2' === $xliffVersion) {
|
||||
return $this->dumpXliff1($defaultLocale, $messages, $domain, $options);
|
||||
}
|
||||
if ('2.0' === $xliffVersion) {
|
||||
return $this->dumpXliff2($defaultLocale, $messages, $domain);
|
||||
}
|
||||
throw new InvalidArgumentException(\sprintf('No support implemented for dumping XLIFF version "%s".', $xliffVersion));
|
||||
}
|
||||
protected function getExtension() : string
|
||||
{
|
||||
return $this->extension;
|
||||
}
|
||||
private function dumpXliff1(string $defaultLocale, MessageCatalogue $messages, ?string $domain, array $options = []) : string
|
||||
{
|
||||
$toolInfo = ['tool-id' => 'symfony', 'tool-name' => 'Symfony'];
|
||||
if (\array_key_exists('tool_info', $options)) {
|
||||
$toolInfo = \array_merge($toolInfo, $options['tool_info']);
|
||||
}
|
||||
$dom = new \DOMDocument('1.0', 'utf-8');
|
||||
$dom->formatOutput = \true;
|
||||
$xliff = $dom->appendChild($dom->createElement('xliff'));
|
||||
$xliff->setAttribute('version', '1.2');
|
||||
$xliff->setAttribute('xmlns', 'urn:oasis:names:tc:xliff:document:1.2');
|
||||
$xliffFile = $xliff->appendChild($dom->createElement('file'));
|
||||
$xliffFile->setAttribute('source-language', \str_replace('_', '-', $defaultLocale));
|
||||
$xliffFile->setAttribute('target-language', \str_replace('_', '-', $messages->getLocale()));
|
||||
$xliffFile->setAttribute('datatype', 'plaintext');
|
||||
$xliffFile->setAttribute('original', 'file.ext');
|
||||
$xliffHead = $xliffFile->appendChild($dom->createElement('header'));
|
||||
$xliffTool = $xliffHead->appendChild($dom->createElement('tool'));
|
||||
foreach ($toolInfo as $id => $value) {
|
||||
$xliffTool->setAttribute($id, $value);
|
||||
}
|
||||
if ($catalogueMetadata = $messages->getCatalogueMetadata('', $domain) ?? []) {
|
||||
$xliffPropGroup = $xliffHead->appendChild($dom->createElement('prop-group'));
|
||||
foreach ($catalogueMetadata as $key => $value) {
|
||||
$xliffProp = $xliffPropGroup->appendChild($dom->createElement('prop'));
|
||||
$xliffProp->setAttribute('prop-type', $key);
|
||||
$xliffProp->appendChild($dom->createTextNode($value));
|
||||
}
|
||||
}
|
||||
$xliffBody = $xliffFile->appendChild($dom->createElement('body'));
|
||||
foreach ($messages->all($domain) as $source => $target) {
|
||||
$translation = $dom->createElement('trans-unit');
|
||||
$translation->setAttribute('id', \strtr(\substr(\base64_encode(\hash('sha256', $source, \true)), 0, 7), '/+', '._'));
|
||||
$translation->setAttribute('resname', $source);
|
||||
$s = $translation->appendChild($dom->createElement('source'));
|
||||
$s->appendChild($dom->createTextNode($source));
|
||||
// Does the target contain characters requiring a CDATA section?
|
||||
$text = 1 === \preg_match('/[&<>]/', $target) ? $dom->createCDATASection($target) : $dom->createTextNode($target);
|
||||
$targetElement = $dom->createElement('target');
|
||||
$metadata = $messages->getMetadata($source, $domain);
|
||||
if ($this->hasMetadataArrayInfo('target-attributes', $metadata)) {
|
||||
foreach ($metadata['target-attributes'] as $name => $value) {
|
||||
$targetElement->setAttribute($name, $value);
|
||||
}
|
||||
}
|
||||
$t = $translation->appendChild($targetElement);
|
||||
$t->appendChild($text);
|
||||
if ($this->hasMetadataArrayInfo('notes', $metadata)) {
|
||||
foreach ($metadata['notes'] as $note) {
|
||||
if (!isset($note['content'])) {
|
||||
continue;
|
||||
}
|
||||
$n = $translation->appendChild($dom->createElement('note'));
|
||||
$n->appendChild($dom->createTextNode($note['content']));
|
||||
if (isset($note['priority'])) {
|
||||
$n->setAttribute('priority', $note['priority']);
|
||||
}
|
||||
if (isset($note['from'])) {
|
||||
$n->setAttribute('from', $note['from']);
|
||||
}
|
||||
}
|
||||
}
|
||||
$xliffBody->appendChild($translation);
|
||||
}
|
||||
return $dom->saveXML();
|
||||
}
|
||||
private function dumpXliff2(string $defaultLocale, MessageCatalogue $messages, ?string $domain) : string
|
||||
{
|
||||
$dom = new \DOMDocument('1.0', 'utf-8');
|
||||
$dom->formatOutput = \true;
|
||||
$xliff = $dom->appendChild($dom->createElement('xliff'));
|
||||
$xliff->setAttribute('xmlns', 'urn:oasis:names:tc:xliff:document:2.0');
|
||||
$xliff->setAttribute('version', '2.0');
|
||||
$xliff->setAttribute('srcLang', \str_replace('_', '-', $defaultLocale));
|
||||
$xliff->setAttribute('trgLang', \str_replace('_', '-', $messages->getLocale()));
|
||||
$xliffFile = $xliff->appendChild($dom->createElement('file'));
|
||||
if (\str_ends_with($domain, MessageCatalogue::INTL_DOMAIN_SUFFIX)) {
|
||||
$xliffFile->setAttribute('id', \substr($domain, 0, -\strlen(MessageCatalogue::INTL_DOMAIN_SUFFIX)) . '.' . $messages->getLocale());
|
||||
} else {
|
||||
$xliffFile->setAttribute('id', $domain . '.' . $messages->getLocale());
|
||||
}
|
||||
if ($catalogueMetadata = $messages->getCatalogueMetadata('', $domain) ?? []) {
|
||||
$xliff->setAttribute('xmlns:m', 'urn:oasis:names:tc:xliff:metadata:2.0');
|
||||
$xliffMetadata = $xliffFile->appendChild($dom->createElement('m:metadata'));
|
||||
foreach ($catalogueMetadata as $key => $value) {
|
||||
$xliffMeta = $xliffMetadata->appendChild($dom->createElement('prop'));
|
||||
$xliffMeta->setAttribute('type', $key);
|
||||
$xliffMeta->appendChild($dom->createTextNode($value));
|
||||
}
|
||||
}
|
||||
foreach ($messages->all($domain) as $source => $target) {
|
||||
$translation = $dom->createElement('unit');
|
||||
$translation->setAttribute('id', \strtr(\substr(\base64_encode(\hash('sha256', $source, \true)), 0, 7), '/+', '._'));
|
||||
if (\strlen($source) <= 80) {
|
||||
$translation->setAttribute('name', $source);
|
||||
}
|
||||
$metadata = $messages->getMetadata($source, $domain);
|
||||
// Add notes section
|
||||
if ($this->hasMetadataArrayInfo('notes', $metadata)) {
|
||||
$notesElement = $dom->createElement('notes');
|
||||
foreach ($metadata['notes'] as $note) {
|
||||
$n = $dom->createElement('note');
|
||||
$n->appendChild($dom->createTextNode($note['content'] ?? ''));
|
||||
unset($note['content']);
|
||||
foreach ($note as $name => $value) {
|
||||
$n->setAttribute($name, $value);
|
||||
}
|
||||
$notesElement->appendChild($n);
|
||||
}
|
||||
$translation->appendChild($notesElement);
|
||||
}
|
||||
$segment = $translation->appendChild($dom->createElement('segment'));
|
||||
$s = $segment->appendChild($dom->createElement('source'));
|
||||
$s->appendChild($dom->createTextNode($source));
|
||||
// Does the target contain characters requiring a CDATA section?
|
||||
$text = 1 === \preg_match('/[&<>]/', $target) ? $dom->createCDATASection($target) : $dom->createTextNode($target);
|
||||
$targetElement = $dom->createElement('target');
|
||||
if ($this->hasMetadataArrayInfo('target-attributes', $metadata)) {
|
||||
foreach ($metadata['target-attributes'] as $name => $value) {
|
||||
$targetElement->setAttribute($name, $value);
|
||||
}
|
||||
}
|
||||
$t = $segment->appendChild($targetElement);
|
||||
$t->appendChild($text);
|
||||
$xliffFile->appendChild($translation);
|
||||
}
|
||||
return $dom->saveXML();
|
||||
}
|
||||
private function hasMetadataArrayInfo(string $key, array $metadata = null) : bool
|
||||
{
|
||||
return \is_iterable($metadata[$key] ?? null);
|
||||
}
|
||||
}
|
47
dependencies/symfony/translation/Dumper/YamlFileDumper.php
vendored
Normal file
47
dependencies/symfony/translation/Dumper/YamlFileDumper.php
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
<?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\Dumper;
|
||||
|
||||
use Symfony\Component\Translation\Exception\LogicException;
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
use Symfony\Component\Translation\Util\ArrayConverter;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Yaml\Yaml;
|
||||
/**
|
||||
* YamlFileDumper generates yaml files from a message catalogue.
|
||||
*
|
||||
* @author Michel Salib <michelsalib@hotmail.com>
|
||||
*/
|
||||
class YamlFileDumper extends \Symfony\Component\Translation\Dumper\FileDumper
|
||||
{
|
||||
private string $extension;
|
||||
public function __construct(string $extension = 'yml')
|
||||
{
|
||||
$this->extension = $extension;
|
||||
}
|
||||
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []) : string
|
||||
{
|
||||
if (!\class_exists(Yaml::class)) {
|
||||
throw new LogicException('Dumping translations in the YAML format requires the Symfony Yaml component.');
|
||||
}
|
||||
$data = $messages->all($domain);
|
||||
if (isset($options['as_tree']) && $options['as_tree']) {
|
||||
$data = ArrayConverter::expandToTree($data);
|
||||
}
|
||||
if (isset($options['inline']) && ($inline = (int) $options['inline']) > 0) {
|
||||
return Yaml::dump($data, $inline);
|
||||
}
|
||||
return Yaml::dump($data);
|
||||
}
|
||||
protected function getExtension() : string
|
||||
{
|
||||
return $this->extension;
|
||||
}
|
||||
}
|
20
dependencies/symfony/translation/Exception/ExceptionInterface.php
vendored
Normal file
20
dependencies/symfony/translation/Exception/ExceptionInterface.php
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<?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\Exception;
|
||||
|
||||
/**
|
||||
* Exception interface for all exceptions thrown by the component.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface ExceptionInterface extends \Throwable
|
||||
{
|
||||
}
|
22
dependencies/symfony/translation/Exception/IncompleteDsnException.php
vendored
Normal file
22
dependencies/symfony/translation/Exception/IncompleteDsnException.php
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
<?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\Exception;
|
||||
|
||||
class IncompleteDsnException extends \Symfony\Component\Translation\Exception\InvalidArgumentException
|
||||
{
|
||||
public function __construct(string $message, string $dsn = null, \Throwable $previous = null)
|
||||
{
|
||||
if ($dsn) {
|
||||
$message = \sprintf('Invalid "%s" provider DSN: ', $dsn) . $message;
|
||||
}
|
||||
parent::__construct($message, 0, $previous);
|
||||
}
|
||||
}
|
20
dependencies/symfony/translation/Exception/InvalidArgumentException.php
vendored
Normal file
20
dependencies/symfony/translation/Exception/InvalidArgumentException.php
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<?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\Exception;
|
||||
|
||||
/**
|
||||
* Base InvalidArgumentException for the Translation component.
|
||||
*
|
||||
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
|
||||
*/
|
||||
class InvalidArgumentException extends \InvalidArgumentException implements \Symfony\Component\Translation\Exception\ExceptionInterface
|
||||
{
|
||||
}
|
20
dependencies/symfony/translation/Exception/InvalidResourceException.php
vendored
Normal file
20
dependencies/symfony/translation/Exception/InvalidResourceException.php
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<?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\Exception;
|
||||
|
||||
/**
|
||||
* Thrown when a resource cannot be loaded.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class InvalidResourceException extends \InvalidArgumentException implements \Symfony\Component\Translation\Exception\ExceptionInterface
|
||||
{
|
||||
}
|
20
dependencies/symfony/translation/Exception/LogicException.php
vendored
Normal file
20
dependencies/symfony/translation/Exception/LogicException.php
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<?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\Exception;
|
||||
|
||||
/**
|
||||
* Base LogicException for Translation component.
|
||||
*
|
||||
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
|
||||
*/
|
||||
class LogicException extends \LogicException implements \Symfony\Component\Translation\Exception\ExceptionInterface
|
||||
{
|
||||
}
|
23
dependencies/symfony/translation/Exception/MissingRequiredOptionException.php
vendored
Normal file
23
dependencies/symfony/translation/Exception/MissingRequiredOptionException.php
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?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\Exception;
|
||||
|
||||
/**
|
||||
* @author Oskar Stark <oskarstark@googlemail.com>
|
||||
*/
|
||||
class MissingRequiredOptionException extends \Symfony\Component\Translation\Exception\IncompleteDsnException
|
||||
{
|
||||
public function __construct(string $option, string $dsn = null, \Throwable $previous = null)
|
||||
{
|
||||
$message = \sprintf('The option "%s" is required but missing.', $option);
|
||||
parent::__construct($message, $dsn, $previous);
|
||||
}
|
||||
}
|
20
dependencies/symfony/translation/Exception/NotFoundResourceException.php
vendored
Normal file
20
dependencies/symfony/translation/Exception/NotFoundResourceException.php
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<?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\Exception;
|
||||
|
||||
/**
|
||||
* Thrown when a resource does not exist.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class NotFoundResourceException extends \InvalidArgumentException implements \Symfony\Component\Translation\Exception\ExceptionInterface
|
||||
{
|
||||
}
|
35
dependencies/symfony/translation/Exception/ProviderException.php
vendored
Normal file
35
dependencies/symfony/translation/Exception/ProviderException.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\Exception;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ProviderException extends \Symfony\Component\Translation\Exception\RuntimeException implements \Symfony\Component\Translation\Exception\ProviderExceptionInterface
|
||||
{
|
||||
private ResponseInterface $response;
|
||||
private string $debug;
|
||||
public function __construct(string $message, ResponseInterface $response, int $code = 0, \Exception $previous = null)
|
||||
{
|
||||
$this->response = $response;
|
||||
$this->debug = $response->getInfo('debug') ?? '';
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
public function getResponse() : ResponseInterface
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
public function getDebug() : string
|
||||
{
|
||||
return $this->debug;
|
||||
}
|
||||
}
|
22
dependencies/symfony/translation/Exception/ProviderExceptionInterface.php
vendored
Normal file
22
dependencies/symfony/translation/Exception/ProviderExceptionInterface.php
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
<?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\Exception;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface ProviderExceptionInterface extends \Symfony\Component\Translation\Exception\ExceptionInterface
|
||||
{
|
||||
/*
|
||||
* Returns debug info coming from the Symfony\Contracts\HttpClient\ResponseInterface
|
||||
*/
|
||||
public function getDebug() : string;
|
||||
}
|
20
dependencies/symfony/translation/Exception/RuntimeException.php
vendored
Normal file
20
dependencies/symfony/translation/Exception/RuntimeException.php
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<?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\Exception;
|
||||
|
||||
/**
|
||||
* Base RuntimeException for the Translation component.
|
||||
*
|
||||
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
|
||||
*/
|
||||
class RuntimeException extends \RuntimeException implements \Symfony\Component\Translation\Exception\ExceptionInterface
|
||||
{
|
||||
}
|
35
dependencies/symfony/translation/Exception/UnsupportedSchemeException.php
vendored
Normal file
35
dependencies/symfony/translation/Exception/UnsupportedSchemeException.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\Exception;
|
||||
|
||||
use Symfony\Component\Translation\Bridge;
|
||||
use Symfony\Component\Translation\Provider\Dsn;
|
||||
class UnsupportedSchemeException extends \Symfony\Component\Translation\Exception\LogicException
|
||||
{
|
||||
private const SCHEME_TO_PACKAGE_MAP = ['crowdin' => ['class' => Bridge\Crowdin\CrowdinProviderFactory::class, 'package' => 'symfony/crowdin-translation-provider'], 'loco' => ['class' => Bridge\Loco\LocoProviderFactory::class, 'package' => 'symfony/loco-translation-provider'], 'lokalise' => ['class' => Bridge\Lokalise\LokaliseProviderFactory::class, 'package' => 'symfony/lokalise-translation-provider']];
|
||||
public function __construct(Dsn $dsn, string $name = null, array $supported = [])
|
||||
{
|
||||
$provider = $dsn->getScheme();
|
||||
if (\false !== ($pos = \strpos($provider, '+'))) {
|
||||
$provider = \substr($provider, 0, $pos);
|
||||
}
|
||||
$package = self::SCHEME_TO_PACKAGE_MAP[$provider] ?? null;
|
||||
if ($package && !\class_exists($package['class'])) {
|
||||
parent::__construct(\sprintf('Unable to synchronize translations via "%s" as the provider is not installed. Try running "composer require %s".', $provider, $package['package']));
|
||||
return;
|
||||
}
|
||||
$message = \sprintf('The "%s" scheme is not supported', $dsn->getScheme());
|
||||
if ($name && $supported) {
|
||||
$message .= \sprintf('; supported schemes for translation provider "%s" are: "%s"', $name, \implode('", "', $supported));
|
||||
}
|
||||
parent::__construct($message . '.');
|
||||
}
|
||||
}
|
59
dependencies/symfony/translation/Extractor/AbstractFileExtractor.php
vendored
Normal file
59
dependencies/symfony/translation/Extractor/AbstractFileExtractor.php
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
<?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\Extractor;
|
||||
|
||||
use Symfony\Component\Translation\Exception\InvalidArgumentException;
|
||||
/**
|
||||
* Base class used by classes that extract translation messages from files.
|
||||
*
|
||||
* @author Marcos D. Sánchez <marcosdsanchez@gmail.com>
|
||||
*/
|
||||
abstract class AbstractFileExtractor
|
||||
{
|
||||
protected function extractFiles(string|iterable $resource) : iterable
|
||||
{
|
||||
if (\is_iterable($resource)) {
|
||||
$files = [];
|
||||
foreach ($resource as $file) {
|
||||
if ($this->canBeExtracted($file)) {
|
||||
$files[] = $this->toSplFileInfo($file);
|
||||
}
|
||||
}
|
||||
} elseif (\is_file($resource)) {
|
||||
$files = $this->canBeExtracted($resource) ? [$this->toSplFileInfo($resource)] : [];
|
||||
} else {
|
||||
$files = $this->extractFromDirectory($resource);
|
||||
}
|
||||
return $files;
|
||||
}
|
||||
private function toSplFileInfo(string $file) : \SplFileInfo
|
||||
{
|
||||
return new \SplFileInfo($file);
|
||||
}
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
protected function isFile(string $file) : bool
|
||||
{
|
||||
if (!\is_file($file)) {
|
||||
throw new InvalidArgumentException(\sprintf('The "%s" file does not exist.', $file));
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected abstract function canBeExtracted(string $file);
|
||||
/**
|
||||
* @return iterable
|
||||
*/
|
||||
protected abstract function extractFromDirectory(string|array $resource);
|
||||
}
|
54
dependencies/symfony/translation/Extractor/ChainExtractor.php
vendored
Normal file
54
dependencies/symfony/translation/Extractor/ChainExtractor.php
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
<?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\Extractor;
|
||||
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
/**
|
||||
* ChainExtractor extracts translation messages from template files.
|
||||
*
|
||||
* @author Michel Salib <michelsalib@hotmail.com>
|
||||
*/
|
||||
class ChainExtractor implements \Symfony\Component\Translation\Extractor\ExtractorInterface
|
||||
{
|
||||
/**
|
||||
* The extractors.
|
||||
*
|
||||
* @var ExtractorInterface[]
|
||||
*/
|
||||
private array $extractors = [];
|
||||
/**
|
||||
* Adds a loader to the translation extractor.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addExtractor(string $format, \Symfony\Component\Translation\Extractor\ExtractorInterface $extractor)
|
||||
{
|
||||
$this->extractors[$format] = $extractor;
|
||||
}
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function setPrefix(string $prefix)
|
||||
{
|
||||
foreach ($this->extractors as $extractor) {
|
||||
$extractor->setPrefix($prefix);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function extract(string|iterable $directory, MessageCatalogue $catalogue)
|
||||
{
|
||||
foreach ($this->extractors as $extractor) {
|
||||
$extractor->extract($directory, $catalogue);
|
||||
}
|
||||
}
|
||||
}
|
36
dependencies/symfony/translation/Extractor/ExtractorInterface.php
vendored
Normal file
36
dependencies/symfony/translation/Extractor/ExtractorInterface.php
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
<?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\Extractor;
|
||||
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
/**
|
||||
* Extracts translation messages from a directory or files to the catalogue.
|
||||
* New found messages are injected to the catalogue using the prefix.
|
||||
*
|
||||
* @author Michel Salib <michelsalib@hotmail.com>
|
||||
*/
|
||||
interface ExtractorInterface
|
||||
{
|
||||
/**
|
||||
* Extracts translation messages from files, a file or a directory to the catalogue.
|
||||
*
|
||||
* @param string|iterable<string> $resource Files, a file or a directory
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function extract(string|iterable $resource, MessageCatalogue $catalogue);
|
||||
/**
|
||||
* Sets the prefix that should be used for new found messages.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setPrefix(string $prefix);
|
||||
}
|
69
dependencies/symfony/translation/Extractor/PhpAstExtractor.php
vendored
Normal file
69
dependencies/symfony/translation/Extractor/PhpAstExtractor.php
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
<?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\Extractor;
|
||||
|
||||
use WP_Ultimo\Dependencies\PhpParser\NodeTraverser;
|
||||
use WP_Ultimo\Dependencies\PhpParser\NodeVisitor;
|
||||
use WP_Ultimo\Dependencies\PhpParser\Parser;
|
||||
use WP_Ultimo\Dependencies\PhpParser\ParserFactory;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\Translation\Extractor\Visitor\AbstractVisitor;
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
/**
|
||||
* PhpAstExtractor extracts translation messages from a PHP AST.
|
||||
*
|
||||
* @author Mathieu Santostefano <msantostefano@protonmail.com>
|
||||
*/
|
||||
final class PhpAstExtractor extends \Symfony\Component\Translation\Extractor\AbstractFileExtractor implements \Symfony\Component\Translation\Extractor\ExtractorInterface
|
||||
{
|
||||
private Parser $parser;
|
||||
public function __construct(
|
||||
/**
|
||||
* @param iterable<AbstractVisitor&NodeVisitor> $visitors
|
||||
*/
|
||||
private readonly iterable $visitors,
|
||||
private string $prefix = ''
|
||||
)
|
||||
{
|
||||
if (!\class_exists(ParserFactory::class)) {
|
||||
throw new \LogicException(\sprintf('You cannot use "%s" as the "nikic/php-parser" package is not installed. Try running "composer require nikic/php-parser".', static::class));
|
||||
}
|
||||
$this->parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7);
|
||||
}
|
||||
public function extract(iterable|string $resource, MessageCatalogue $catalogue) : void
|
||||
{
|
||||
foreach ($this->extractFiles($resource) as $file) {
|
||||
$traverser = new NodeTraverser();
|
||||
/** @var AbstractVisitor&NodeVisitor $visitor */
|
||||
foreach ($this->visitors as $visitor) {
|
||||
$visitor->initialize($catalogue, $file, $this->prefix);
|
||||
$traverser->addVisitor($visitor);
|
||||
}
|
||||
$nodes = $this->parser->parse(\file_get_contents($file));
|
||||
$traverser->traverse($nodes);
|
||||
}
|
||||
}
|
||||
public function setPrefix(string $prefix) : void
|
||||
{
|
||||
$this->prefix = $prefix;
|
||||
}
|
||||
protected function canBeExtracted(string $file) : bool
|
||||
{
|
||||
return 'php' === \pathinfo($file, \PATHINFO_EXTENSION) && $this->isFile($file) && \preg_match('/\\bt\\(|->trans\\(|TranslatableMessage|Symfony\\\\Component\\\\Validator\\\\Constraints/i', \file_get_contents($file));
|
||||
}
|
||||
protected function extractFromDirectory(array|string $resource) : iterable|Finder
|
||||
{
|
||||
if (!\class_exists(Finder::class)) {
|
||||
throw new \LogicException(\sprintf('You cannot use "%s" as the "symfony/finder" package is not installed. Try running "composer require symfony/finder".', static::class));
|
||||
}
|
||||
return (new Finder())->files()->name('*.php')->in($resource);
|
||||
}
|
||||
}
|
207
dependencies/symfony/translation/Extractor/PhpExtractor.php
vendored
Normal file
207
dependencies/symfony/translation/Extractor/PhpExtractor.php
vendored
Normal file
@ -0,0 +1,207 @@
|
||||
<?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\Extractor;
|
||||
|
||||
trigger_deprecation('symfony/translation', '6.2', '"%s" is deprecated, use "%s" instead.', \Symfony\Component\Translation\Extractor\PhpExtractor::class, \Symfony\Component\Translation\Extractor\PhpAstExtractor::class);
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
/**
|
||||
* PhpExtractor extracts translation messages from a PHP template.
|
||||
*
|
||||
* @author Michel Salib <michelsalib@hotmail.com>
|
||||
*
|
||||
* @deprecated since Symfony 6.2, use the PhpAstExtractor instead
|
||||
*/
|
||||
class PhpExtractor extends \Symfony\Component\Translation\Extractor\AbstractFileExtractor implements \Symfony\Component\Translation\Extractor\ExtractorInterface
|
||||
{
|
||||
public const MESSAGE_TOKEN = 300;
|
||||
public const METHOD_ARGUMENTS_TOKEN = 1000;
|
||||
public const DOMAIN_TOKEN = 1001;
|
||||
/**
|
||||
* Prefix for new found message.
|
||||
*/
|
||||
private string $prefix = '';
|
||||
/**
|
||||
* The sequence that captures translation messages.
|
||||
*/
|
||||
protected $sequences = [['->', 'trans', '(', self::MESSAGE_TOKEN, ',', self::METHOD_ARGUMENTS_TOKEN, ',', self::DOMAIN_TOKEN], ['->', 'trans', '(', self::MESSAGE_TOKEN], ['new', 'TranslatableMessage', '(', self::MESSAGE_TOKEN, ',', self::METHOD_ARGUMENTS_TOKEN, ',', self::DOMAIN_TOKEN], ['new', 'TranslatableMessage', '(', self::MESSAGE_TOKEN], ['new', '\\', 'Symfony', '\\', 'Component', '\\', 'Translation', '\\', 'TranslatableMessage', '(', self::MESSAGE_TOKEN, ',', self::METHOD_ARGUMENTS_TOKEN, ',', self::DOMAIN_TOKEN], ['new', '\\Symfony\\Component\\Translation\\TranslatableMessage', '(', self::MESSAGE_TOKEN, ',', self::METHOD_ARGUMENTS_TOKEN, ',', self::DOMAIN_TOKEN], ['new', '\\', 'Symfony', '\\', 'Component', '\\', 'Translation', '\\', 'TranslatableMessage', '(', self::MESSAGE_TOKEN], ['new', '\\Symfony\\Component\\Translation\\TranslatableMessage', '(', self::MESSAGE_TOKEN], ['t', '(', self::MESSAGE_TOKEN, ',', self::METHOD_ARGUMENTS_TOKEN, ',', self::DOMAIN_TOKEN], ['t', '(', self::MESSAGE_TOKEN]];
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function extract(string|iterable $resource, MessageCatalogue $catalog)
|
||||
{
|
||||
$files = $this->extractFiles($resource);
|
||||
foreach ($files as $file) {
|
||||
$this->parseTokens(\token_get_all(\file_get_contents($file)), $catalog, $file);
|
||||
\gc_mem_caches();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function setPrefix(string $prefix)
|
||||
{
|
||||
$this->prefix = $prefix;
|
||||
}
|
||||
/**
|
||||
* Normalizes a token.
|
||||
*/
|
||||
protected function normalizeToken(mixed $token) : ?string
|
||||
{
|
||||
if (isset($token[1]) && 'b"' !== $token) {
|
||||
return $token[1];
|
||||
}
|
||||
return $token;
|
||||
}
|
||||
/**
|
||||
* Seeks to a non-whitespace token.
|
||||
*/
|
||||
private function seekToNextRelevantToken(\Iterator $tokenIterator) : void
|
||||
{
|
||||
for (; $tokenIterator->valid(); $tokenIterator->next()) {
|
||||
$t = $tokenIterator->current();
|
||||
if (\T_WHITESPACE !== $t[0]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
private function skipMethodArgument(\Iterator $tokenIterator) : void
|
||||
{
|
||||
$openBraces = 0;
|
||||
for (; $tokenIterator->valid(); $tokenIterator->next()) {
|
||||
$t = $tokenIterator->current();
|
||||
if ('[' === $t[0] || '(' === $t[0]) {
|
||||
++$openBraces;
|
||||
}
|
||||
if (']' === $t[0] || ')' === $t[0]) {
|
||||
--$openBraces;
|
||||
}
|
||||
if (0 === $openBraces && ',' === $t[0] || -1 === $openBraces && ')' === $t[0]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Extracts the message from the iterator while the tokens
|
||||
* match allowed message tokens.
|
||||
*/
|
||||
private function getValue(\Iterator $tokenIterator) : string
|
||||
{
|
||||
$message = '';
|
||||
$docToken = '';
|
||||
$docPart = '';
|
||||
for (; $tokenIterator->valid(); $tokenIterator->next()) {
|
||||
$t = $tokenIterator->current();
|
||||
if ('.' === $t) {
|
||||
// Concatenate with next token
|
||||
continue;
|
||||
}
|
||||
if (!isset($t[1])) {
|
||||
break;
|
||||
}
|
||||
switch ($t[0]) {
|
||||
case \T_START_HEREDOC:
|
||||
$docToken = $t[1];
|
||||
break;
|
||||
case \T_ENCAPSED_AND_WHITESPACE:
|
||||
case \T_CONSTANT_ENCAPSED_STRING:
|
||||
if ('' === $docToken) {
|
||||
$message .= \Symfony\Component\Translation\Extractor\PhpStringTokenParser::parse($t[1]);
|
||||
} else {
|
||||
$docPart = $t[1];
|
||||
}
|
||||
break;
|
||||
case \T_END_HEREDOC:
|
||||
if ($indentation = \strspn($t[1], ' ')) {
|
||||
$docPartWithLineBreaks = $docPart;
|
||||
$docPart = '';
|
||||
foreach (\preg_split('~(\\r\\n|\\n|\\r)~', $docPartWithLineBreaks, -1, \PREG_SPLIT_DELIM_CAPTURE) as $str) {
|
||||
if (\in_array($str, ["\r\n", "\n", "\r"], \true)) {
|
||||
$docPart .= $str;
|
||||
} else {
|
||||
$docPart .= \substr($str, $indentation);
|
||||
}
|
||||
}
|
||||
}
|
||||
$message .= \Symfony\Component\Translation\Extractor\PhpStringTokenParser::parseDocString($docToken, $docPart);
|
||||
$docToken = '';
|
||||
$docPart = '';
|
||||
break;
|
||||
case \T_WHITESPACE:
|
||||
break;
|
||||
default:
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
return $message;
|
||||
}
|
||||
/**
|
||||
* Extracts trans message from PHP tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function parseTokens(array $tokens, MessageCatalogue $catalog, string $filename)
|
||||
{
|
||||
$tokenIterator = new \ArrayIterator($tokens);
|
||||
for ($key = 0; $key < $tokenIterator->count(); ++$key) {
|
||||
foreach ($this->sequences as $sequence) {
|
||||
$message = '';
|
||||
$domain = 'messages';
|
||||
$tokenIterator->seek($key);
|
||||
foreach ($sequence as $sequenceKey => $item) {
|
||||
$this->seekToNextRelevantToken($tokenIterator);
|
||||
if ($this->normalizeToken($tokenIterator->current()) === $item) {
|
||||
$tokenIterator->next();
|
||||
continue;
|
||||
} elseif (self::MESSAGE_TOKEN === $item) {
|
||||
$message = $this->getValue($tokenIterator);
|
||||
if (\count($sequence) === $sequenceKey + 1) {
|
||||
break;
|
||||
}
|
||||
} elseif (self::METHOD_ARGUMENTS_TOKEN === $item) {
|
||||
$this->skipMethodArgument($tokenIterator);
|
||||
} elseif (self::DOMAIN_TOKEN === $item) {
|
||||
$domainToken = $this->getValue($tokenIterator);
|
||||
if ('' !== $domainToken) {
|
||||
$domain = $domainToken;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($message) {
|
||||
$catalog->set($message, $this->prefix . $message, $domain);
|
||||
$metadata = $catalog->getMetadata($message, $domain) ?? [];
|
||||
$normalizedFilename = \preg_replace('{[\\\\/]+}', '/', $filename);
|
||||
$metadata['sources'][] = $normalizedFilename . ':' . $tokens[$key][2];
|
||||
$catalog->setMetadata($message, $metadata, $domain);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
protected function canBeExtracted(string $file) : bool
|
||||
{
|
||||
return $this->isFile($file) && 'php' === \pathinfo($file, \PATHINFO_EXTENSION);
|
||||
}
|
||||
protected function extractFromDirectory(string|array $directory) : iterable
|
||||
{
|
||||
if (!\class_exists(Finder::class)) {
|
||||
throw new \LogicException(\sprintf('You cannot use "%s" as the "symfony/finder" package is not installed. Try running "composer require symfony/finder".', static::class));
|
||||
}
|
||||
$finder = new Finder();
|
||||
return $finder->files()->name('*.php')->in($directory);
|
||||
}
|
||||
}
|
112
dependencies/symfony/translation/Extractor/PhpStringTokenParser.php
vendored
Normal file
112
dependencies/symfony/translation/Extractor/PhpStringTokenParser.php
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
<?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\Extractor;
|
||||
|
||||
trigger_deprecation('symfony/translation', '6.2', '"%s" is deprecated.', \Symfony\Component\Translation\Extractor\PhpStringTokenParser::class);
|
||||
/*
|
||||
* The following is derived from code at http://github.com/nikic/PHP-Parser
|
||||
*
|
||||
* Copyright (c) 2011 by Nikita Popov
|
||||
*
|
||||
* Some rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
*
|
||||
* * The names of the contributors may not be used to endorse or
|
||||
* promote products derived from this software without specific
|
||||
* prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
/**
|
||||
* @deprecated since Symfony 6.2
|
||||
*/
|
||||
class PhpStringTokenParser
|
||||
{
|
||||
protected static $replacements = ['\\' => '\\', '$' => '$', 'n' => "\n", 'r' => "\r", 't' => "\t", 'f' => "\f", 'v' => "\v", 'e' => "\x1b"];
|
||||
/**
|
||||
* Parses a string token.
|
||||
*
|
||||
* @param string $str String token content
|
||||
*/
|
||||
public static function parse(string $str) : string
|
||||
{
|
||||
$bLength = 0;
|
||||
if ('b' === $str[0]) {
|
||||
$bLength = 1;
|
||||
}
|
||||
if ('\'' === $str[$bLength]) {
|
||||
return \str_replace(['\\\\', '\\\''], ['\\', '\''], \substr($str, $bLength + 1, -1));
|
||||
} else {
|
||||
return self::parseEscapeSequences(\substr($str, $bLength + 1, -1), '"');
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Parses escape sequences in strings (all string types apart from single quoted).
|
||||
*
|
||||
* @param string $str String without quotes
|
||||
* @param string|null $quote Quote type
|
||||
*/
|
||||
public static function parseEscapeSequences(string $str, string $quote = null) : string
|
||||
{
|
||||
if (null !== $quote) {
|
||||
$str = \str_replace('\\' . $quote, $quote, $str);
|
||||
}
|
||||
return \preg_replace_callback('~\\\\([\\\\$nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3})~', [__CLASS__, 'parseCallback'], $str);
|
||||
}
|
||||
private static function parseCallback(array $matches) : string
|
||||
{
|
||||
$str = $matches[1];
|
||||
if (isset(self::$replacements[$str])) {
|
||||
return self::$replacements[$str];
|
||||
} elseif ('x' === $str[0] || 'X' === $str[0]) {
|
||||
return \chr(\hexdec($str));
|
||||
} else {
|
||||
return \chr(\octdec($str));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Parses a constant doc string.
|
||||
*
|
||||
* @param string $startToken Doc string start token content (<<<SMTHG)
|
||||
* @param string $str String token content
|
||||
*/
|
||||
public static function parseDocString(string $startToken, string $str) : string
|
||||
{
|
||||
// strip last newline (thanks tokenizer for sticking it into the string!)
|
||||
$str = \preg_replace('~(\\r\\n|\\n|\\r)$~', '', $str);
|
||||
// nowdoc string
|
||||
if (\str_contains($startToken, '\'')) {
|
||||
return $str;
|
||||
}
|
||||
return self::parseEscapeSequences($str, null);
|
||||
}
|
||||
}
|
101
dependencies/symfony/translation/Extractor/Visitor/AbstractVisitor.php
vendored
Normal file
101
dependencies/symfony/translation/Extractor/Visitor/AbstractVisitor.php
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
<?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\Extractor\Visitor;
|
||||
|
||||
use WP_Ultimo\Dependencies\PhpParser\Node;
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
/**
|
||||
* @author Mathieu Santostefano <msantostefano@protonmail.com>
|
||||
*/
|
||||
abstract class AbstractVisitor
|
||||
{
|
||||
private MessageCatalogue $catalogue;
|
||||
private \SplFileInfo $file;
|
||||
private string $messagePrefix;
|
||||
public function initialize(MessageCatalogue $catalogue, \SplFileInfo $file, string $messagePrefix) : void
|
||||
{
|
||||
$this->catalogue = $catalogue;
|
||||
$this->file = $file;
|
||||
$this->messagePrefix = $messagePrefix;
|
||||
}
|
||||
protected function addMessageToCatalogue(string $message, ?string $domain, int $line) : void
|
||||
{
|
||||
$domain ??= 'messages';
|
||||
$this->catalogue->set($message, $this->messagePrefix . $message, $domain);
|
||||
$metadata = $this->catalogue->getMetadata($message, $domain) ?? [];
|
||||
$normalizedFilename = \preg_replace('{[\\\\/]+}', '/', $this->file);
|
||||
$metadata['sources'][] = $normalizedFilename . ':' . $line;
|
||||
$this->catalogue->setMetadata($message, $metadata, $domain);
|
||||
}
|
||||
protected function getStringArguments(Node\Expr\CallLike|Node\Attribute|Node\Expr\New_ $node, int|string $index, bool $indexIsRegex = \false) : array
|
||||
{
|
||||
if (\is_string($index)) {
|
||||
return $this->getStringNamedArguments($node, $index, $indexIsRegex);
|
||||
}
|
||||
$args = $node instanceof Node\Expr\CallLike ? $node->getRawArgs() : $node->args;
|
||||
if (!($arg = $args[$index] ?? null) instanceof Node\Arg) {
|
||||
return [];
|
||||
}
|
||||
return (array) $this->getStringValue($arg->value);
|
||||
}
|
||||
protected function hasNodeNamedArguments(Node\Expr\CallLike|Node\Attribute|Node\Expr\New_ $node) : bool
|
||||
{
|
||||
$args = $node instanceof Node\Expr\CallLike ? $node->getRawArgs() : $node->args;
|
||||
foreach ($args as $arg) {
|
||||
if ($arg instanceof Node\Arg && null !== $arg->name) {
|
||||
return \true;
|
||||
}
|
||||
}
|
||||
return \false;
|
||||
}
|
||||
protected function nodeFirstNamedArgumentIndex(Node\Expr\CallLike|Node\Attribute|Node\Expr\New_ $node) : int
|
||||
{
|
||||
$args = $node instanceof Node\Expr\CallLike ? $node->getRawArgs() : $node->args;
|
||||
foreach ($args as $i => $arg) {
|
||||
if ($arg instanceof Node\Arg && null !== $arg->name) {
|
||||
return $i;
|
||||
}
|
||||
}
|
||||
return \PHP_INT_MAX;
|
||||
}
|
||||
private function getStringNamedArguments(Node\Expr\CallLike|Node\Attribute $node, string $argumentName = null, bool $isArgumentNamePattern = \false) : array
|
||||
{
|
||||
$args = $node instanceof Node\Expr\CallLike ? $node->getArgs() : $node->args;
|
||||
$argumentValues = [];
|
||||
foreach ($args as $arg) {
|
||||
if (!$isArgumentNamePattern && $arg->name?->toString() === $argumentName) {
|
||||
$argumentValues[] = $this->getStringValue($arg->value);
|
||||
} elseif ($isArgumentNamePattern && \preg_match($argumentName, $arg->name?->toString() ?? '') > 0) {
|
||||
$argumentValues[] = $this->getStringValue($arg->value);
|
||||
}
|
||||
}
|
||||
return \array_filter($argumentValues);
|
||||
}
|
||||
private function getStringValue(Node $node) : ?string
|
||||
{
|
||||
if ($node instanceof Node\Scalar\String_) {
|
||||
return $node->value;
|
||||
}
|
||||
if ($node instanceof Node\Expr\BinaryOp\Concat) {
|
||||
if (null === ($left = $this->getStringValue($node->left))) {
|
||||
return null;
|
||||
}
|
||||
if (null === ($right = $this->getStringValue($node->right))) {
|
||||
return null;
|
||||
}
|
||||
return $left . $right;
|
||||
}
|
||||
if ($node instanceof Node\Expr\Assign && $node->expr instanceof Node\Scalar\String_) {
|
||||
return $node->expr->value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
90
dependencies/symfony/translation/Extractor/Visitor/ConstraintVisitor.php
vendored
Normal file
90
dependencies/symfony/translation/Extractor/Visitor/ConstraintVisitor.php
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
<?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\Extractor\Visitor;
|
||||
|
||||
use WP_Ultimo\Dependencies\PhpParser\Node;
|
||||
use WP_Ultimo\Dependencies\PhpParser\NodeVisitor;
|
||||
/**
|
||||
* @author Mathieu Santostefano <msantostefano@protonmail.com>
|
||||
*
|
||||
* Code mostly comes from https://github.com/php-translation/extractor/blob/master/src/Visitor/Php/Symfony/Constraint.php
|
||||
*/
|
||||
final class ConstraintVisitor extends \Symfony\Component\Translation\Extractor\Visitor\AbstractVisitor implements NodeVisitor
|
||||
{
|
||||
public function __construct(private readonly array $constraintClassNames = [])
|
||||
{
|
||||
}
|
||||
public function beforeTraverse(array $nodes) : ?Node
|
||||
{
|
||||
return null;
|
||||
}
|
||||
public function enterNode(Node $node) : ?Node
|
||||
{
|
||||
if (!$node instanceof Node\Expr\New_ && !$node instanceof Node\Attribute) {
|
||||
return null;
|
||||
}
|
||||
$className = $node instanceof Node\Attribute ? $node->name : $node->class;
|
||||
if (!$className instanceof Node\Name) {
|
||||
return null;
|
||||
}
|
||||
$parts = $className->parts;
|
||||
$isConstraintClass = \false;
|
||||
foreach ($parts as $part) {
|
||||
if (\in_array($part, $this->constraintClassNames, \true)) {
|
||||
$isConstraintClass = \true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$isConstraintClass) {
|
||||
return null;
|
||||
}
|
||||
$arg = $node->args[0] ?? null;
|
||||
if (!$arg instanceof Node\Arg) {
|
||||
return null;
|
||||
}
|
||||
if ($this->hasNodeNamedArguments($node)) {
|
||||
$messages = $this->getStringArguments($node, '/message/i', \true);
|
||||
} else {
|
||||
if (!$arg->value instanceof Node\Expr\Array_) {
|
||||
// There is no way to guess which argument is a message to be translated.
|
||||
return null;
|
||||
}
|
||||
$messages = [];
|
||||
$options = $arg->value;
|
||||
/** @var Node\Expr\ArrayItem $item */
|
||||
foreach ($options->items as $item) {
|
||||
if (!$item->key instanceof Node\Scalar\String_) {
|
||||
continue;
|
||||
}
|
||||
if (\false === \stripos($item->key->value ?? '', 'message')) {
|
||||
continue;
|
||||
}
|
||||
if (!$item->value instanceof Node\Scalar\String_) {
|
||||
continue;
|
||||
}
|
||||
$messages[] = $item->value->value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
foreach ($messages as $message) {
|
||||
$this->addMessageToCatalogue($message, 'validators', $node->getStartLine());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public function leaveNode(Node $node) : ?Node
|
||||
{
|
||||
return null;
|
||||
}
|
||||
public function afterTraverse(array $nodes) : ?Node
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
53
dependencies/symfony/translation/Extractor/Visitor/TransMethodVisitor.php
vendored
Normal file
53
dependencies/symfony/translation/Extractor/Visitor/TransMethodVisitor.php
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
<?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\Extractor\Visitor;
|
||||
|
||||
use WP_Ultimo\Dependencies\PhpParser\Node;
|
||||
use WP_Ultimo\Dependencies\PhpParser\NodeVisitor;
|
||||
/**
|
||||
* @author Mathieu Santostefano <msantostefano@protonmail.com>
|
||||
*/
|
||||
final class TransMethodVisitor extends \Symfony\Component\Translation\Extractor\Visitor\AbstractVisitor implements NodeVisitor
|
||||
{
|
||||
public function beforeTraverse(array $nodes) : ?Node
|
||||
{
|
||||
return null;
|
||||
}
|
||||
public function enterNode(Node $node) : ?Node
|
||||
{
|
||||
if (!$node instanceof Node\Expr\MethodCall && !$node instanceof Node\Expr\FuncCall) {
|
||||
return null;
|
||||
}
|
||||
if (!\is_string($node->name) && !$node->name instanceof Node\Identifier && !$node->name instanceof Node\Name) {
|
||||
return null;
|
||||
}
|
||||
$name = (string) $node->name;
|
||||
if ('trans' === $name || 't' === $name) {
|
||||
$firstNamedArgumentIndex = $this->nodeFirstNamedArgumentIndex($node);
|
||||
if (!($messages = $this->getStringArguments($node, 0 < $firstNamedArgumentIndex ? 0 : 'message'))) {
|
||||
return null;
|
||||
}
|
||||
$domain = $this->getStringArguments($node, 2 < $firstNamedArgumentIndex ? 2 : 'domain')[0] ?? null;
|
||||
foreach ($messages as $message) {
|
||||
$this->addMessageToCatalogue($message, $domain, $node->getStartLine());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public function leaveNode(Node $node) : ?Node
|
||||
{
|
||||
return null;
|
||||
}
|
||||
public function afterTraverse(array $nodes) : ?Node
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
53
dependencies/symfony/translation/Extractor/Visitor/TranslatableMessageVisitor.php
vendored
Normal file
53
dependencies/symfony/translation/Extractor/Visitor/TranslatableMessageVisitor.php
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
<?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\Extractor\Visitor;
|
||||
|
||||
use WP_Ultimo\Dependencies\PhpParser\Node;
|
||||
use WP_Ultimo\Dependencies\PhpParser\NodeVisitor;
|
||||
/**
|
||||
* @author Mathieu Santostefano <msantostefano@protonmail.com>
|
||||
*/
|
||||
final class TranslatableMessageVisitor extends \Symfony\Component\Translation\Extractor\Visitor\AbstractVisitor implements NodeVisitor
|
||||
{
|
||||
public function beforeTraverse(array $nodes) : ?Node
|
||||
{
|
||||
return null;
|
||||
}
|
||||
public function enterNode(Node $node) : ?Node
|
||||
{
|
||||
if (!$node instanceof Node\Expr\New_) {
|
||||
return null;
|
||||
}
|
||||
if (!($className = $node->class) instanceof Node\Name) {
|
||||
return null;
|
||||
}
|
||||
if (!\in_array('TranslatableMessage', $className->parts, \true)) {
|
||||
return null;
|
||||
}
|
||||
$firstNamedArgumentIndex = $this->nodeFirstNamedArgumentIndex($node);
|
||||
if (!($messages = $this->getStringArguments($node, 0 < $firstNamedArgumentIndex ? 0 : 'message'))) {
|
||||
return null;
|
||||
}
|
||||
$domain = $this->getStringArguments($node, 2 < $firstNamedArgumentIndex ? 2 : 'domain')[0] ?? null;
|
||||
foreach ($messages as $message) {
|
||||
$this->addMessageToCatalogue($message, $domain, $node->getStartLine());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public function leaveNode(Node $node) : ?Node
|
||||
{
|
||||
return null;
|
||||
}
|
||||
public function afterTraverse(array $nodes) : ?Node
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
50
dependencies/symfony/translation/Formatter/IntlFormatter.php
vendored
Normal file
50
dependencies/symfony/translation/Formatter/IntlFormatter.php
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
<?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\Formatter;
|
||||
|
||||
use Symfony\Component\Translation\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Translation\Exception\LogicException;
|
||||
/**
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
|
||||
*/
|
||||
class IntlFormatter implements \Symfony\Component\Translation\Formatter\IntlFormatterInterface
|
||||
{
|
||||
private $hasMessageFormatter;
|
||||
private $cache = [];
|
||||
public function formatIntl(string $message, string $locale, array $parameters = []) : string
|
||||
{
|
||||
// MessageFormatter constructor throws an exception if the message is empty
|
||||
if ('' === $message) {
|
||||
return '';
|
||||
}
|
||||
if (!($formatter = $this->cache[$locale][$message] ?? null)) {
|
||||
if (!($this->hasMessageFormatter ??= \class_exists(\MessageFormatter::class))) {
|
||||
throw new LogicException('Cannot parse message translation: please install the "intl" PHP extension or the "symfony/polyfill-intl-messageformatter" package.');
|
||||
}
|
||||
try {
|
||||
$this->cache[$locale][$message] = $formatter = new \MessageFormatter($locale, $message);
|
||||
} catch (\IntlException $e) {
|
||||
throw new InvalidArgumentException(\sprintf('Invalid message format (error #%d): ', \intl_get_error_code()) . \intl_get_error_message(), 0, $e);
|
||||
}
|
||||
}
|
||||
foreach ($parameters as $key => $value) {
|
||||
if (\in_array($key[0] ?? null, ['%', '{'], \true)) {
|
||||
unset($parameters[$key]);
|
||||
$parameters[\trim($key, '%{ }')] = $value;
|
||||
}
|
||||
}
|
||||
if (\false === ($message = $formatter->format($parameters))) {
|
||||
throw new InvalidArgumentException(\sprintf('Unable to format message (error #%s): ', $formatter->getErrorCode()) . $formatter->getErrorMessage());
|
||||
}
|
||||
return $message;
|
||||
}
|
||||
}
|
26
dependencies/symfony/translation/Formatter/IntlFormatterInterface.php
vendored
Normal file
26
dependencies/symfony/translation/Formatter/IntlFormatterInterface.php
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
<?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\Formatter;
|
||||
|
||||
/**
|
||||
* Formats ICU message patterns.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
interface IntlFormatterInterface
|
||||
{
|
||||
/**
|
||||
* Formats a localized message using rules defined by ICU MessageFormat.
|
||||
*
|
||||
* @see http://icu-project.org/apiref/icu4c/classMessageFormat.html#details
|
||||
*/
|
||||
public function formatIntl(string $message, string $locale, array $parameters = []) : string;
|
||||
}
|
43
dependencies/symfony/translation/Formatter/MessageFormatter.php
vendored
Normal file
43
dependencies/symfony/translation/Formatter/MessageFormatter.php
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
<?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\Formatter;
|
||||
|
||||
use Symfony\Component\Translation\IdentityTranslator;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Translation\TranslatorInterface;
|
||||
// Help opcache.preload discover always-needed symbols
|
||||
\class_exists(\Symfony\Component\Translation\Formatter\IntlFormatter::class);
|
||||
/**
|
||||
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
|
||||
*/
|
||||
class MessageFormatter implements \Symfony\Component\Translation\Formatter\MessageFormatterInterface, \Symfony\Component\Translation\Formatter\IntlFormatterInterface
|
||||
{
|
||||
private TranslatorInterface $translator;
|
||||
private \Symfony\Component\Translation\Formatter\IntlFormatterInterface $intlFormatter;
|
||||
/**
|
||||
* @param TranslatorInterface|null $translator An identity translator to use as selector for pluralization
|
||||
*/
|
||||
public function __construct(TranslatorInterface $translator = null, \Symfony\Component\Translation\Formatter\IntlFormatterInterface $intlFormatter = null)
|
||||
{
|
||||
$this->translator = $translator ?? new IdentityTranslator();
|
||||
$this->intlFormatter = $intlFormatter ?? new \Symfony\Component\Translation\Formatter\IntlFormatter();
|
||||
}
|
||||
public function format(string $message, string $locale, array $parameters = []) : string
|
||||
{
|
||||
if ($this->translator instanceof TranslatorInterface) {
|
||||
return $this->translator->trans($message, $parameters, null, $locale);
|
||||
}
|
||||
return \strtr($message, $parameters);
|
||||
}
|
||||
public function formatIntl(string $message, string $locale, array $parameters = []) : string
|
||||
{
|
||||
return $this->intlFormatter->formatIntl($message, $locale, $parameters);
|
||||
}
|
||||
}
|
27
dependencies/symfony/translation/Formatter/MessageFormatterInterface.php
vendored
Normal file
27
dependencies/symfony/translation/Formatter/MessageFormatterInterface.php
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
<?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\Formatter;
|
||||
|
||||
/**
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
|
||||
*/
|
||||
interface MessageFormatterInterface
|
||||
{
|
||||
/**
|
||||
* Formats a localized message pattern with given arguments.
|
||||
*
|
||||
* @param string $message The message (may also be an object that can be cast to string)
|
||||
* @param string $locale The message locale
|
||||
* @param array $parameters An array of parameters for the message
|
||||
*/
|
||||
public function format(string $message, string $locale, array $parameters = []) : string;
|
||||
}
|
24
dependencies/symfony/translation/IdentityTranslator.php
vendored
Normal file
24
dependencies/symfony/translation/IdentityTranslator.php
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
<?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;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Translation\LocaleAwareInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Translation\TranslatorTrait;
|
||||
/**
|
||||
* IdentityTranslator does not translate anything.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class IdentityTranslator implements TranslatorInterface, LocaleAwareInterface
|
||||
{
|
||||
use TranslatorTrait;
|
||||
}
|
52
dependencies/symfony/translation/Loader/ArrayLoader.php
vendored
Normal file
52
dependencies/symfony/translation/Loader/ArrayLoader.php
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
<?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 \Symfony\Component\Translation\Loader\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;
|
||||
}
|
||||
}
|
55
dependencies/symfony/translation/Loader/CsvFileLoader.php
vendored
Normal file
55
dependencies/symfony/translation/Loader/CsvFileLoader.php
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
<?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 \Symfony\Component\Translation\Loader\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;
|
||||
}
|
||||
}
|
47
dependencies/symfony/translation/Loader/FileLoader.php
vendored
Normal file
47
dependencies/symfony/translation/Loader/FileLoader.php
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
<?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 WP_Ultimo\Dependencies\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 \Symfony\Component\Translation\Loader\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
|
||||
*/
|
||||
protected abstract function loadResource(string $resource) : array;
|
||||
}
|
50
dependencies/symfony/translation/Loader/IcuDatFileLoader.php
vendored
Normal file
50
dependencies/symfony/translation/Loader/IcuDatFileLoader.php
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
<?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 WP_Ultimo\Dependencies\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 \Symfony\Component\Translation\Loader\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;
|
||||
}
|
||||
}
|
76
dependencies/symfony/translation/Loader/IcuResFileLoader.php
vendored
Normal file
76
dependencies/symfony/translation/Loader/IcuResFileLoader.php
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
<?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 WP_Ultimo\Dependencies\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 \Symfony\Component\Translation\Loader\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;
|
||||
}
|
||||
}
|
24
dependencies/symfony/translation/Loader/IniFileLoader.php
vendored
Normal file
24
dependencies/symfony/translation/Loader/IniFileLoader.php
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
<?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 \Symfony\Component\Translation\Loader\FileLoader
|
||||
{
|
||||
protected function loadResource(string $resource) : array
|
||||
{
|
||||
return \parse_ini_file($resource, \true);
|
||||
}
|
||||
}
|
46
dependencies/symfony/translation/Loader/JsonFileLoader.php
vendored
Normal file
46
dependencies/symfony/translation/Loader/JsonFileLoader.php
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
<?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 \Symfony\Component\Translation\Loader\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',
|
||||
};
|
||||
}
|
||||
}
|
30
dependencies/symfony/translation/Loader/LoaderInterface.php
vendored
Normal file
30
dependencies/symfony/translation/Loader/LoaderInterface.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?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;
|
||||
}
|
112
dependencies/symfony/translation/Loader/MoFileLoader.php
vendored
Normal file
112
dependencies/symfony/translation/Loader/MoFileLoader.php
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
<?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 \Symfony\Component\Translation\Loader\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, "\x00")) {
|
||||
[$singularId, $pluralId] = \explode("\x00", $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, "\x00")) {
|
||||
$translated = \explode("\x00", $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);
|
||||
}
|
||||
}
|
31
dependencies/symfony/translation/Loader/PhpFileLoader.php
vendored
Normal file
31
dependencies/symfony/translation/Loader/PhpFileLoader.php
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
<?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 \Symfony\Component\Translation\Loader\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'], \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);
|
||||
}
|
||||
}
|
134
dependencies/symfony/translation/Loader/PoFileLoader.php
vendored
Normal file
134
dependencies/symfony/translation/Loader/PoFileLoader.php
vendored
Normal file
@ -0,0 +1,134 @@
|
||||
<?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 \Symfony\Component\Translation\Loader\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));
|
||||
}
|
||||
}
|
||||
}
|
62
dependencies/symfony/translation/Loader/QtFileLoader.php
vendored
Normal file
62
dependencies/symfony/translation/Loader/QtFileLoader.php
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
<?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 WP_Ultimo\Dependencies\Symfony\Component\Config\Resource\FileResource;
|
||||
use WP_Ultimo\Dependencies\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 \Symfony\Component\Translation\Loader\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;
|
||||
}
|
||||
}
|
185
dependencies/symfony/translation/Loader/XliffFileLoader.php
vendored
Normal file
185
dependencies/symfony/translation/Loader/XliffFileLoader.php
vendored
Normal file
@ -0,0 +1,185 @@
|
||||
<?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 WP_Ultimo\Dependencies\Symfony\Component\Config\Resource\FileResource;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Config\Util\Exception\InvalidXmlException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Config\Util\Exception\XmlParsingException;
|
||||
use WP_Ultimo\Dependencies\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 \Symfony\Component\Translation\Loader\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;
|
||||
}
|
||||
$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');
|
||||
}
|
||||
}
|
44
dependencies/symfony/translation/Loader/YamlFileLoader.php
vendored
Normal file
44
dependencies/symfony/translation/Loader/YamlFileLoader.php
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
<?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 WP_Ultimo\Dependencies\Symfony\Component\Yaml\Exception\ParseException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Yaml\Parser as YamlParser;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Yaml\Yaml;
|
||||
/**
|
||||
* YamlFileLoader loads translations from Yaml files.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class YamlFileLoader extends \Symfony\Component\Translation\Loader\FileLoader
|
||||
{
|
||||
private $yamlParser;
|
||||
protected function loadResource(string $resource) : array
|
||||
{
|
||||
if (null === $this->yamlParser) {
|
||||
if (!\class_exists(\WP_Ultimo\Dependencies\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 ?: [];
|
||||
}
|
||||
}
|
66
dependencies/symfony/translation/LocaleSwitcher.php
vendored
Normal file
66
dependencies/symfony/translation/LocaleSwitcher.php
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
<?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;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Routing\RequestContext;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Translation\LocaleAwareInterface;
|
||||
/**
|
||||
* @author Kevin Bond <kevinbond@gmail.com>
|
||||
*/
|
||||
class LocaleSwitcher implements LocaleAwareInterface
|
||||
{
|
||||
private string $defaultLocale;
|
||||
/**
|
||||
* @param LocaleAwareInterface[] $localeAwareServices
|
||||
*/
|
||||
public function __construct(private string $locale, private iterable $localeAwareServices, private ?RequestContext $requestContext = null)
|
||||
{
|
||||
$this->defaultLocale = $locale;
|
||||
}
|
||||
public function setLocale(string $locale) : void
|
||||
{
|
||||
if (\class_exists(\Locale::class)) {
|
||||
\Locale::setDefault($locale);
|
||||
}
|
||||
$this->locale = $locale;
|
||||
$this->requestContext?->setParameter('_locale', $locale);
|
||||
foreach ($this->localeAwareServices as $service) {
|
||||
$service->setLocale($locale);
|
||||
}
|
||||
}
|
||||
public function getLocale() : string
|
||||
{
|
||||
return $this->locale;
|
||||
}
|
||||
/**
|
||||
* Switch to a new locale, execute a callback, then switch back to the original.
|
||||
*
|
||||
* @template T
|
||||
*
|
||||
* @param callable():T $callback
|
||||
*
|
||||
* @return T
|
||||
*/
|
||||
public function runWithLocale(string $locale, callable $callback) : mixed
|
||||
{
|
||||
$original = $this->getLocale();
|
||||
$this->setLocale($locale);
|
||||
try {
|
||||
return $callback();
|
||||
} finally {
|
||||
$this->setLocale($original);
|
||||
}
|
||||
}
|
||||
public function reset() : void
|
||||
{
|
||||
$this->setLocale($this->defaultLocale);
|
||||
}
|
||||
}
|
98
dependencies/symfony/translation/LoggingTranslator.php
vendored
Normal file
98
dependencies/symfony/translation/LoggingTranslator.php
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
<?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;
|
||||
|
||||
use WP_Ultimo\Dependencies\Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Translation\Exception\InvalidArgumentException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Translation\LocaleAwareInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Translation\TranslatorInterface;
|
||||
/**
|
||||
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
|
||||
*/
|
||||
class LoggingTranslator implements TranslatorInterface, \Symfony\Component\Translation\TranslatorBagInterface, LocaleAwareInterface
|
||||
{
|
||||
private TranslatorInterface $translator;
|
||||
private LoggerInterface $logger;
|
||||
/**
|
||||
* @param TranslatorInterface&TranslatorBagInterface&LocaleAwareInterface $translator The translator must implement TranslatorBagInterface
|
||||
*/
|
||||
public function __construct(TranslatorInterface $translator, LoggerInterface $logger)
|
||||
{
|
||||
if (!$translator instanceof \Symfony\Component\Translation\TranslatorBagInterface || !$translator instanceof LocaleAwareInterface) {
|
||||
throw new InvalidArgumentException(\sprintf('The Translator "%s" must implement TranslatorInterface, TranslatorBagInterface and LocaleAwareInterface.', \get_debug_type($translator)));
|
||||
}
|
||||
$this->translator = $translator;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
public function trans(?string $id, array $parameters = [], string $domain = null, string $locale = null) : string
|
||||
{
|
||||
$trans = $this->translator->trans($id = (string) $id, $parameters, $domain, $locale);
|
||||
$this->log($id, $domain, $locale);
|
||||
return $trans;
|
||||
}
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function setLocale(string $locale)
|
||||
{
|
||||
$prev = $this->translator->getLocale();
|
||||
$this->translator->setLocale($locale);
|
||||
if ($prev === $locale) {
|
||||
return;
|
||||
}
|
||||
$this->logger->debug(\sprintf('The locale of the translator has changed from "%s" to "%s".', $prev, $locale));
|
||||
}
|
||||
public function getLocale() : string
|
||||
{
|
||||
return $this->translator->getLocale();
|
||||
}
|
||||
public function getCatalogue(string $locale = null) : \Symfony\Component\Translation\MessageCatalogueInterface
|
||||
{
|
||||
return $this->translator->getCatalogue($locale);
|
||||
}
|
||||
public function getCatalogues() : array
|
||||
{
|
||||
return $this->translator->getCatalogues();
|
||||
}
|
||||
/**
|
||||
* Gets the fallback locales.
|
||||
*/
|
||||
public function getFallbackLocales() : array
|
||||
{
|
||||
if ($this->translator instanceof \Symfony\Component\Translation\Translator || \method_exists($this->translator, 'getFallbackLocales')) {
|
||||
return $this->translator->getFallbackLocales();
|
||||
}
|
||||
return [];
|
||||
}
|
||||
/**
|
||||
* Passes through all unknown calls onto the translator object.
|
||||
*/
|
||||
public function __call(string $method, array $args)
|
||||
{
|
||||
return $this->translator->{$method}(...$args);
|
||||
}
|
||||
/**
|
||||
* Logs for missing translations.
|
||||
*/
|
||||
private function log(string $id, ?string $domain, ?string $locale) : void
|
||||
{
|
||||
$domain ??= 'messages';
|
||||
$catalogue = $this->translator->getCatalogue($locale);
|
||||
if ($catalogue->defines($id, $domain)) {
|
||||
return;
|
||||
}
|
||||
if ($catalogue->has($id, $domain)) {
|
||||
$this->logger->debug('Translation use fallback catalogue.', ['id' => $id, 'domain' => $domain, 'locale' => $catalogue->getLocale()]);
|
||||
} else {
|
||||
$this->logger->warning('Translation not found.', ['id' => $id, 'domain' => $domain, 'locale' => $catalogue->getLocale()]);
|
||||
}
|
||||
}
|
||||
}
|
286
dependencies/symfony/translation/MessageCatalogue.php
vendored
Normal file
286
dependencies/symfony/translation/MessageCatalogue.php
vendored
Normal file
@ -0,0 +1,286 @@
|
||||
<?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;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Config\Resource\ResourceInterface;
|
||||
use Symfony\Component\Translation\Exception\LogicException;
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class MessageCatalogue implements \Symfony\Component\Translation\MessageCatalogueInterface, \Symfony\Component\Translation\MetadataAwareInterface, \Symfony\Component\Translation\CatalogueMetadataAwareInterface
|
||||
{
|
||||
private array $messages = [];
|
||||
private array $metadata = [];
|
||||
private array $catalogueMetadata = [];
|
||||
private array $resources = [];
|
||||
private string $locale;
|
||||
private ?\Symfony\Component\Translation\MessageCatalogueInterface $fallbackCatalogue = null;
|
||||
private ?self $parent = null;
|
||||
/**
|
||||
* @param array $messages An array of messages classified by domain
|
||||
*/
|
||||
public function __construct(string $locale, array $messages = [])
|
||||
{
|
||||
$this->locale = $locale;
|
||||
$this->messages = $messages;
|
||||
}
|
||||
public function getLocale() : string
|
||||
{
|
||||
return $this->locale;
|
||||
}
|
||||
public function getDomains() : array
|
||||
{
|
||||
$domains = [];
|
||||
foreach ($this->messages as $domain => $messages) {
|
||||
if (\str_ends_with($domain, self::INTL_DOMAIN_SUFFIX)) {
|
||||
$domain = \substr($domain, 0, -\strlen(self::INTL_DOMAIN_SUFFIX));
|
||||
}
|
||||
$domains[$domain] = $domain;
|
||||
}
|
||||
return \array_values($domains);
|
||||
}
|
||||
public function all(string $domain = null) : array
|
||||
{
|
||||
if (null !== $domain) {
|
||||
// skip messages merge if intl-icu requested explicitly
|
||||
if (\str_ends_with($domain, self::INTL_DOMAIN_SUFFIX)) {
|
||||
return $this->messages[$domain] ?? [];
|
||||
}
|
||||
return ($this->messages[$domain . self::INTL_DOMAIN_SUFFIX] ?? []) + ($this->messages[$domain] ?? []);
|
||||
}
|
||||
$allMessages = [];
|
||||
foreach ($this->messages as $domain => $messages) {
|
||||
if (\str_ends_with($domain, self::INTL_DOMAIN_SUFFIX)) {
|
||||
$domain = \substr($domain, 0, -\strlen(self::INTL_DOMAIN_SUFFIX));
|
||||
$allMessages[$domain] = $messages + ($allMessages[$domain] ?? []);
|
||||
} else {
|
||||
$allMessages[$domain] = ($allMessages[$domain] ?? []) + $messages;
|
||||
}
|
||||
}
|
||||
return $allMessages;
|
||||
}
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function set(string $id, string $translation, string $domain = 'messages')
|
||||
{
|
||||
$this->add([$id => $translation], $domain);
|
||||
}
|
||||
public function has(string $id, string $domain = 'messages') : bool
|
||||
{
|
||||
if (isset($this->messages[$domain][$id]) || isset($this->messages[$domain . self::INTL_DOMAIN_SUFFIX][$id])) {
|
||||
return \true;
|
||||
}
|
||||
if (null !== $this->fallbackCatalogue) {
|
||||
return $this->fallbackCatalogue->has($id, $domain);
|
||||
}
|
||||
return \false;
|
||||
}
|
||||
public function defines(string $id, string $domain = 'messages') : bool
|
||||
{
|
||||
return isset($this->messages[$domain][$id]) || isset($this->messages[$domain . self::INTL_DOMAIN_SUFFIX][$id]);
|
||||
}
|
||||
public function get(string $id, string $domain = 'messages') : string
|
||||
{
|
||||
if (isset($this->messages[$domain . self::INTL_DOMAIN_SUFFIX][$id])) {
|
||||
return $this->messages[$domain . self::INTL_DOMAIN_SUFFIX][$id];
|
||||
}
|
||||
if (isset($this->messages[$domain][$id])) {
|
||||
return $this->messages[$domain][$id];
|
||||
}
|
||||
if (null !== $this->fallbackCatalogue) {
|
||||
return $this->fallbackCatalogue->get($id, $domain);
|
||||
}
|
||||
return $id;
|
||||
}
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function replace(array $messages, string $domain = 'messages')
|
||||
{
|
||||
unset($this->messages[$domain], $this->messages[$domain . self::INTL_DOMAIN_SUFFIX]);
|
||||
$this->add($messages, $domain);
|
||||
}
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function add(array $messages, string $domain = 'messages')
|
||||
{
|
||||
$altDomain = \str_ends_with($domain, self::INTL_DOMAIN_SUFFIX) ? \substr($domain, 0, -\strlen(self::INTL_DOMAIN_SUFFIX)) : $domain . self::INTL_DOMAIN_SUFFIX;
|
||||
foreach ($messages as $id => $message) {
|
||||
unset($this->messages[$altDomain][$id]);
|
||||
$this->messages[$domain][$id] = $message;
|
||||
}
|
||||
if ([] === ($this->messages[$altDomain] ?? null)) {
|
||||
unset($this->messages[$altDomain]);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function addCatalogue(\Symfony\Component\Translation\MessageCatalogueInterface $catalogue)
|
||||
{
|
||||
if ($catalogue->getLocale() !== $this->locale) {
|
||||
throw new LogicException(\sprintf('Cannot add a catalogue for locale "%s" as the current locale for this catalogue is "%s".', $catalogue->getLocale(), $this->locale));
|
||||
}
|
||||
foreach ($catalogue->all() as $domain => $messages) {
|
||||
if ($intlMessages = $catalogue->all($domain . self::INTL_DOMAIN_SUFFIX)) {
|
||||
$this->add($intlMessages, $domain . self::INTL_DOMAIN_SUFFIX);
|
||||
$messages = \array_diff_key($messages, $intlMessages);
|
||||
}
|
||||
$this->add($messages, $domain);
|
||||
}
|
||||
foreach ($catalogue->getResources() as $resource) {
|
||||
$this->addResource($resource);
|
||||
}
|
||||
if ($catalogue instanceof \Symfony\Component\Translation\MetadataAwareInterface) {
|
||||
$metadata = $catalogue->getMetadata('', '');
|
||||
$this->addMetadata($metadata);
|
||||
}
|
||||
if ($catalogue instanceof \Symfony\Component\Translation\CatalogueMetadataAwareInterface) {
|
||||
$catalogueMetadata = $catalogue->getCatalogueMetadata('', '');
|
||||
$this->addCatalogueMetadata($catalogueMetadata);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function addFallbackCatalogue(\Symfony\Component\Translation\MessageCatalogueInterface $catalogue)
|
||||
{
|
||||
// detect circular references
|
||||
$c = $catalogue;
|
||||
while ($c = $c->getFallbackCatalogue()) {
|
||||
if ($c->getLocale() === $this->getLocale()) {
|
||||
throw new LogicException(\sprintf('Circular reference detected when adding a fallback catalogue for locale "%s".', $catalogue->getLocale()));
|
||||
}
|
||||
}
|
||||
$c = $this;
|
||||
do {
|
||||
if ($c->getLocale() === $catalogue->getLocale()) {
|
||||
throw new LogicException(\sprintf('Circular reference detected when adding a fallback catalogue for locale "%s".', $catalogue->getLocale()));
|
||||
}
|
||||
foreach ($catalogue->getResources() as $resource) {
|
||||
$c->addResource($resource);
|
||||
}
|
||||
} while ($c = $c->parent);
|
||||
$catalogue->parent = $this;
|
||||
$this->fallbackCatalogue = $catalogue;
|
||||
foreach ($catalogue->getResources() as $resource) {
|
||||
$this->addResource($resource);
|
||||
}
|
||||
}
|
||||
public function getFallbackCatalogue() : ?\Symfony\Component\Translation\MessageCatalogueInterface
|
||||
{
|
||||
return $this->fallbackCatalogue;
|
||||
}
|
||||
public function getResources() : array
|
||||
{
|
||||
return \array_values($this->resources);
|
||||
}
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function addResource(ResourceInterface $resource)
|
||||
{
|
||||
$this->resources[$resource->__toString()] = $resource;
|
||||
}
|
||||
public function getMetadata(string $key = '', string $domain = 'messages') : mixed
|
||||
{
|
||||
if ('' == $domain) {
|
||||
return $this->metadata;
|
||||
}
|
||||
if (isset($this->metadata[$domain])) {
|
||||
if ('' == $key) {
|
||||
return $this->metadata[$domain];
|
||||
}
|
||||
if (isset($this->metadata[$domain][$key])) {
|
||||
return $this->metadata[$domain][$key];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function setMetadata(string $key, mixed $value, string $domain = 'messages')
|
||||
{
|
||||
$this->metadata[$domain][$key] = $value;
|
||||
}
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function deleteMetadata(string $key = '', string $domain = 'messages')
|
||||
{
|
||||
if ('' == $domain) {
|
||||
$this->metadata = [];
|
||||
} elseif ('' == $key) {
|
||||
unset($this->metadata[$domain]);
|
||||
} else {
|
||||
unset($this->metadata[$domain][$key]);
|
||||
}
|
||||
}
|
||||
public function getCatalogueMetadata(string $key = '', string $domain = 'messages') : mixed
|
||||
{
|
||||
if (!$domain) {
|
||||
return $this->catalogueMetadata;
|
||||
}
|
||||
if (isset($this->catalogueMetadata[$domain])) {
|
||||
if (!$key) {
|
||||
return $this->catalogueMetadata[$domain];
|
||||
}
|
||||
if (isset($this->catalogueMetadata[$domain][$key])) {
|
||||
return $this->catalogueMetadata[$domain][$key];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function setCatalogueMetadata(string $key, mixed $value, string $domain = 'messages')
|
||||
{
|
||||
$this->catalogueMetadata[$domain][$key] = $value;
|
||||
}
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function deleteCatalogueMetadata(string $key = '', string $domain = 'messages')
|
||||
{
|
||||
if (!$domain) {
|
||||
$this->catalogueMetadata = [];
|
||||
} elseif (!$key) {
|
||||
unset($this->catalogueMetadata[$domain]);
|
||||
} else {
|
||||
unset($this->catalogueMetadata[$domain][$key]);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Adds current values with the new values.
|
||||
*
|
||||
* @param array $values Values to add
|
||||
*/
|
||||
private function addMetadata(array $values) : void
|
||||
{
|
||||
foreach ($values as $domain => $keys) {
|
||||
foreach ($keys as $key => $value) {
|
||||
$this->setMetadata($key, $value, $domain);
|
||||
}
|
||||
}
|
||||
}
|
||||
private function addCatalogueMetadata(array $values) : void
|
||||
{
|
||||
foreach ($values as $domain => $keys) {
|
||||
foreach ($keys as $key => $value) {
|
||||
$this->setCatalogueMetadata($key, $value, $domain);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
118
dependencies/symfony/translation/MessageCatalogueInterface.php
vendored
Normal file
118
dependencies/symfony/translation/MessageCatalogueInterface.php
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
<?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;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Config\Resource\ResourceInterface;
|
||||
/**
|
||||
* MessageCatalogueInterface.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface MessageCatalogueInterface
|
||||
{
|
||||
public const INTL_DOMAIN_SUFFIX = '+intl-icu';
|
||||
/**
|
||||
* Gets the catalogue locale.
|
||||
*/
|
||||
public function getLocale() : string;
|
||||
/**
|
||||
* Gets the domains.
|
||||
*/
|
||||
public function getDomains() : array;
|
||||
/**
|
||||
* Gets the messages within a given domain.
|
||||
*
|
||||
* If $domain is null, it returns all messages.
|
||||
*/
|
||||
public function all(string $domain = null) : array;
|
||||
/**
|
||||
* Sets a message translation.
|
||||
*
|
||||
* @param string $id The message id
|
||||
* @param string $translation The messages translation
|
||||
* @param string $domain The domain name
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set(string $id, string $translation, string $domain = 'messages');
|
||||
/**
|
||||
* Checks if a message has a translation.
|
||||
*
|
||||
* @param string $id The message id
|
||||
* @param string $domain The domain name
|
||||
*/
|
||||
public function has(string $id, string $domain = 'messages') : bool;
|
||||
/**
|
||||
* Checks if a message has a translation (it does not take into account the fallback mechanism).
|
||||
*
|
||||
* @param string $id The message id
|
||||
* @param string $domain The domain name
|
||||
*/
|
||||
public function defines(string $id, string $domain = 'messages') : bool;
|
||||
/**
|
||||
* Gets a message translation.
|
||||
*
|
||||
* @param string $id The message id
|
||||
* @param string $domain The domain name
|
||||
*/
|
||||
public function get(string $id, string $domain = 'messages') : string;
|
||||
/**
|
||||
* Sets translations for a given domain.
|
||||
*
|
||||
* @param array $messages An array of translations
|
||||
* @param string $domain The domain name
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function replace(array $messages, string $domain = 'messages');
|
||||
/**
|
||||
* Adds translations for a given domain.
|
||||
*
|
||||
* @param array $messages An array of translations
|
||||
* @param string $domain The domain name
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add(array $messages, string $domain = 'messages');
|
||||
/**
|
||||
* Merges translations from the given Catalogue into the current one.
|
||||
*
|
||||
* The two catalogues must have the same locale.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addCatalogue(self $catalogue);
|
||||
/**
|
||||
* Merges translations from the given Catalogue into the current one
|
||||
* only when the translation does not exist.
|
||||
*
|
||||
* This is used to provide default translations when they do not exist for the current locale.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addFallbackCatalogue(self $catalogue);
|
||||
/**
|
||||
* Gets the fallback catalogue.
|
||||
*/
|
||||
public function getFallbackCatalogue() : ?self;
|
||||
/**
|
||||
* Returns an array of resources loaded to build this collection.
|
||||
*
|
||||
* @return ResourceInterface[]
|
||||
*/
|
||||
public function getResources() : array;
|
||||
/**
|
||||
* Adds a resource for this collection.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addResource(ResourceInterface $resource);
|
||||
}
|
45
dependencies/symfony/translation/MetadataAwareInterface.php
vendored
Normal file
45
dependencies/symfony/translation/MetadataAwareInterface.php
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* This interface is used to get, set, and delete metadata about the translation messages.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface MetadataAwareInterface
|
||||
{
|
||||
/**
|
||||
* Gets metadata for the given domain and key.
|
||||
*
|
||||
* Passing an empty domain will return an array with all metadata indexed by
|
||||
* domain and then by key. Passing an empty key will return an array with all
|
||||
* metadata for the given domain.
|
||||
*
|
||||
* @return mixed The value that was set or an array with the domains/keys or null
|
||||
*/
|
||||
public function getMetadata(string $key = '', string $domain = 'messages') : mixed;
|
||||
/**
|
||||
* Adds metadata to a message domain.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setMetadata(string $key, mixed $value, string $domain = 'messages');
|
||||
/**
|
||||
* Deletes metadata for the given key and domain.
|
||||
*
|
||||
* Passing an empty domain will delete all metadata. Passing an empty key will
|
||||
* delete all metadata for the given domain.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function deleteMetadata(string $key = '', string $domain = 'messages');
|
||||
}
|
32
dependencies/symfony/translation/Provider/AbstractProviderFactory.php
vendored
Normal file
32
dependencies/symfony/translation/Provider/AbstractProviderFactory.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\Provider;
|
||||
|
||||
use Symfony\Component\Translation\Exception\IncompleteDsnException;
|
||||
abstract class AbstractProviderFactory implements \Symfony\Component\Translation\Provider\ProviderFactoryInterface
|
||||
{
|
||||
public function supports(\Symfony\Component\Translation\Provider\Dsn $dsn) : bool
|
||||
{
|
||||
return \in_array($dsn->getScheme(), $this->getSupportedSchemes(), \true);
|
||||
}
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
protected abstract function getSupportedSchemes() : array;
|
||||
protected function getUser(\Symfony\Component\Translation\Provider\Dsn $dsn) : string
|
||||
{
|
||||
return $dsn->getUser() ?? throw new IncompleteDsnException('User is not set.', $dsn->getScheme() . '://' . $dsn->getHost());
|
||||
}
|
||||
protected function getPassword(\Symfony\Component\Translation\Provider\Dsn $dsn) : string
|
||||
{
|
||||
return $dsn->getPassword() ?? throw new IncompleteDsnException('Password is not set.', $dsn->getOriginalDsn());
|
||||
}
|
||||
}
|
92
dependencies/symfony/translation/Provider/Dsn.php
vendored
Normal file
92
dependencies/symfony/translation/Provider/Dsn.php
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
<?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\Provider;
|
||||
|
||||
use Symfony\Component\Translation\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Translation\Exception\MissingRequiredOptionException;
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Oskar Stark <oskarstark@googlemail.com>
|
||||
*/
|
||||
final class Dsn
|
||||
{
|
||||
private ?string $scheme;
|
||||
private ?string $host;
|
||||
private ?string $user;
|
||||
private ?string $password;
|
||||
private ?int $port;
|
||||
private ?string $path;
|
||||
private array $options = [];
|
||||
private string $originalDsn;
|
||||
public function __construct(#[\SensitiveParameter] string $dsn)
|
||||
{
|
||||
$this->originalDsn = $dsn;
|
||||
if (\false === ($parsedDsn = \parse_url($dsn))) {
|
||||
throw new InvalidArgumentException('The translation provider DSN is invalid.');
|
||||
}
|
||||
if (!isset($parsedDsn['scheme'])) {
|
||||
throw new InvalidArgumentException('The translation provider DSN must contain a scheme.');
|
||||
}
|
||||
$this->scheme = $parsedDsn['scheme'];
|
||||
if (!isset($parsedDsn['host'])) {
|
||||
throw new InvalidArgumentException('The translation provider DSN must contain a host (use "default" by default).');
|
||||
}
|
||||
$this->host = $parsedDsn['host'];
|
||||
$this->user = '' !== ($parsedDsn['user'] ?? '') ? \urldecode($parsedDsn['user']) : null;
|
||||
$this->password = '' !== ($parsedDsn['pass'] ?? '') ? \urldecode($parsedDsn['pass']) : null;
|
||||
$this->port = $parsedDsn['port'] ?? null;
|
||||
$this->path = $parsedDsn['path'] ?? null;
|
||||
\parse_str($parsedDsn['query'] ?? '', $this->options);
|
||||
}
|
||||
public function getScheme() : string
|
||||
{
|
||||
return $this->scheme;
|
||||
}
|
||||
public function getHost() : string
|
||||
{
|
||||
return $this->host;
|
||||
}
|
||||
public function getUser() : ?string
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
public function getPassword() : ?string
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
public function getPort(int $default = null) : ?int
|
||||
{
|
||||
return $this->port ?? $default;
|
||||
}
|
||||
public function getOption(string $key, mixed $default = null) : mixed
|
||||
{
|
||||
return $this->options[$key] ?? $default;
|
||||
}
|
||||
public function getRequiredOption(string $key) : mixed
|
||||
{
|
||||
if (!\array_key_exists($key, $this->options) || '' === \trim($this->options[$key])) {
|
||||
throw new MissingRequiredOptionException($key);
|
||||
}
|
||||
return $this->options[$key];
|
||||
}
|
||||
public function getOptions() : array
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
public function getPath() : ?string
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
public function getOriginalDsn() : string
|
||||
{
|
||||
return $this->originalDsn;
|
||||
}
|
||||
}
|
53
dependencies/symfony/translation/Provider/FilteringProvider.php
vendored
Normal file
53
dependencies/symfony/translation/Provider/FilteringProvider.php
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
<?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\Provider;
|
||||
|
||||
use Symfony\Component\Translation\TranslatorBag;
|
||||
use Symfony\Component\Translation\TranslatorBagInterface;
|
||||
/**
|
||||
* Filters domains and locales between the Translator config values and those specific to each provider.
|
||||
*
|
||||
* @author Mathieu Santostefano <msantostefano@protonmail.com>
|
||||
*/
|
||||
class FilteringProvider implements \Symfony\Component\Translation\Provider\ProviderInterface
|
||||
{
|
||||
private \Symfony\Component\Translation\Provider\ProviderInterface $provider;
|
||||
private array $locales;
|
||||
private array $domains;
|
||||
public function __construct(\Symfony\Component\Translation\Provider\ProviderInterface $provider, array $locales, array $domains = [])
|
||||
{
|
||||
$this->provider = $provider;
|
||||
$this->locales = $locales;
|
||||
$this->domains = $domains;
|
||||
}
|
||||
public function __toString() : string
|
||||
{
|
||||
return (string) $this->provider;
|
||||
}
|
||||
public function write(TranslatorBagInterface $translatorBag) : void
|
||||
{
|
||||
$this->provider->write($translatorBag);
|
||||
}
|
||||
public function read(array $domains, array $locales) : TranslatorBag
|
||||
{
|
||||
$domains = !$this->domains ? $domains : \array_intersect($this->domains, $domains);
|
||||
$locales = \array_intersect($this->locales, $locales);
|
||||
return $this->provider->read($domains, $locales);
|
||||
}
|
||||
public function delete(TranslatorBagInterface $translatorBag) : void
|
||||
{
|
||||
$this->provider->delete($translatorBag);
|
||||
}
|
||||
public function getDomains() : array
|
||||
{
|
||||
return $this->domains;
|
||||
}
|
||||
}
|
34
dependencies/symfony/translation/Provider/NullProvider.php
vendored
Normal file
34
dependencies/symfony/translation/Provider/NullProvider.php
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
<?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\Provider;
|
||||
|
||||
use Symfony\Component\Translation\TranslatorBag;
|
||||
use Symfony\Component\Translation\TranslatorBagInterface;
|
||||
/**
|
||||
* @author Mathieu Santostefano <msantostefano@protonmail.com>
|
||||
*/
|
||||
class NullProvider implements \Symfony\Component\Translation\Provider\ProviderInterface
|
||||
{
|
||||
public function __toString() : string
|
||||
{
|
||||
return 'null';
|
||||
}
|
||||
public function write(TranslatorBagInterface $translatorBag, bool $override = \false) : void
|
||||
{
|
||||
}
|
||||
public function read(array $domains, array $locales) : TranslatorBag
|
||||
{
|
||||
return new TranslatorBag();
|
||||
}
|
||||
public function delete(TranslatorBagInterface $translatorBag) : void
|
||||
{
|
||||
}
|
||||
}
|
30
dependencies/symfony/translation/Provider/NullProviderFactory.php
vendored
Normal file
30
dependencies/symfony/translation/Provider/NullProviderFactory.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?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\Provider;
|
||||
|
||||
use Symfony\Component\Translation\Exception\UnsupportedSchemeException;
|
||||
/**
|
||||
* @author Mathieu Santostefano <msantostefano@protonmail.com>
|
||||
*/
|
||||
final class NullProviderFactory extends \Symfony\Component\Translation\Provider\AbstractProviderFactory
|
||||
{
|
||||
public function create(\Symfony\Component\Translation\Provider\Dsn $dsn) : \Symfony\Component\Translation\Provider\ProviderInterface
|
||||
{
|
||||
if ('null' === $dsn->getScheme()) {
|
||||
return new \Symfony\Component\Translation\Provider\NullProvider();
|
||||
}
|
||||
throw new UnsupportedSchemeException($dsn, 'null', $this->getSupportedSchemes());
|
||||
}
|
||||
protected function getSupportedSchemes() : array
|
||||
{
|
||||
return ['null'];
|
||||
}
|
||||
}
|
23
dependencies/symfony/translation/Provider/ProviderFactoryInterface.php
vendored
Normal file
23
dependencies/symfony/translation/Provider/ProviderFactoryInterface.php
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?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\Provider;
|
||||
|
||||
use Symfony\Component\Translation\Exception\IncompleteDsnException;
|
||||
use Symfony\Component\Translation\Exception\UnsupportedSchemeException;
|
||||
interface ProviderFactoryInterface
|
||||
{
|
||||
/**
|
||||
* @throws UnsupportedSchemeException
|
||||
* @throws IncompleteDsnException
|
||||
*/
|
||||
public function create(\Symfony\Component\Translation\Provider\Dsn $dsn) : \Symfony\Component\Translation\Provider\ProviderInterface;
|
||||
public function supports(\Symfony\Component\Translation\Provider\Dsn $dsn) : bool;
|
||||
}
|
27
dependencies/symfony/translation/Provider/ProviderInterface.php
vendored
Normal file
27
dependencies/symfony/translation/Provider/ProviderInterface.php
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
<?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\Provider;
|
||||
|
||||
use Symfony\Component\Translation\TranslatorBag;
|
||||
use Symfony\Component\Translation\TranslatorBagInterface;
|
||||
interface ProviderInterface
|
||||
{
|
||||
public function __toString() : string;
|
||||
/**
|
||||
* Translations available in the TranslatorBag only must be created.
|
||||
* Translations available in both the TranslatorBag and on the provider
|
||||
* must be overwritten.
|
||||
* Translations available on the provider only must be kept.
|
||||
*/
|
||||
public function write(TranslatorBagInterface $translatorBag) : void;
|
||||
public function read(array $domains, array $locales) : TranslatorBag;
|
||||
public function delete(TranslatorBagInterface $translatorBag) : void;
|
||||
}
|
49
dependencies/symfony/translation/Provider/TranslationProviderCollection.php
vendored
Normal file
49
dependencies/symfony/translation/Provider/TranslationProviderCollection.php
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
<?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\Provider;
|
||||
|
||||
use Symfony\Component\Translation\Exception\InvalidArgumentException;
|
||||
/**
|
||||
* @author Mathieu Santostefano <msantostefano@protonmail.com>
|
||||
*/
|
||||
final class TranslationProviderCollection
|
||||
{
|
||||
/**
|
||||
* @var array<string, ProviderInterface>
|
||||
*/
|
||||
private $providers;
|
||||
/**
|
||||
* @param array<string, ProviderInterface> $providers
|
||||
*/
|
||||
public function __construct(iterable $providers)
|
||||
{
|
||||
$this->providers = \is_array($providers) ? $providers : \iterator_to_array($providers);
|
||||
}
|
||||
public function __toString() : string
|
||||
{
|
||||
return '[' . \implode(',', \array_keys($this->providers)) . ']';
|
||||
}
|
||||
public function has(string $name) : bool
|
||||
{
|
||||
return isset($this->providers[$name]);
|
||||
}
|
||||
public function get(string $name) : \Symfony\Component\Translation\Provider\ProviderInterface
|
||||
{
|
||||
if (!$this->has($name)) {
|
||||
throw new InvalidArgumentException(\sprintf('Provider "%s" not found. Available: "%s".', $name, (string) $this));
|
||||
}
|
||||
return $this->providers[$name];
|
||||
}
|
||||
public function keys() : array
|
||||
{
|
||||
return \array_keys($this->providers);
|
||||
}
|
||||
}
|
46
dependencies/symfony/translation/Provider/TranslationProviderCollectionFactory.php
vendored
Normal file
46
dependencies/symfony/translation/Provider/TranslationProviderCollectionFactory.php
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
<?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\Provider;
|
||||
|
||||
use Symfony\Component\Translation\Exception\UnsupportedSchemeException;
|
||||
/**
|
||||
* @author Mathieu Santostefano <msantostefano@protonmail.com>
|
||||
*/
|
||||
class TranslationProviderCollectionFactory
|
||||
{
|
||||
private iterable $factories;
|
||||
private array $enabledLocales;
|
||||
/**
|
||||
* @param iterable<mixed, ProviderFactoryInterface> $factories
|
||||
*/
|
||||
public function __construct(iterable $factories, array $enabledLocales)
|
||||
{
|
||||
$this->factories = $factories;
|
||||
$this->enabledLocales = $enabledLocales;
|
||||
}
|
||||
public function fromConfig(array $config) : \Symfony\Component\Translation\Provider\TranslationProviderCollection
|
||||
{
|
||||
$providers = [];
|
||||
foreach ($config as $name => $currentConfig) {
|
||||
$providers[$name] = $this->fromDsnObject(new \Symfony\Component\Translation\Provider\Dsn($currentConfig['dsn']), !$currentConfig['locales'] ? $this->enabledLocales : $currentConfig['locales'], !$currentConfig['domains'] ? [] : $currentConfig['domains']);
|
||||
}
|
||||
return new \Symfony\Component\Translation\Provider\TranslationProviderCollection($providers);
|
||||
}
|
||||
public function fromDsnObject(\Symfony\Component\Translation\Provider\Dsn $dsn, array $locales, array $domains = []) : \Symfony\Component\Translation\Provider\ProviderInterface
|
||||
{
|
||||
foreach ($this->factories as $factory) {
|
||||
if ($factory->supports($dsn)) {
|
||||
return new \Symfony\Component\Translation\Provider\FilteringProvider($factory->create($dsn), $locales, $domains);
|
||||
}
|
||||
}
|
||||
throw new UnsupportedSchemeException($dsn);
|
||||
}
|
||||
}
|
209
dependencies/symfony/translation/PseudoLocalizationTranslator.php
vendored
Normal file
209
dependencies/symfony/translation/PseudoLocalizationTranslator.php
vendored
Normal file
@ -0,0 +1,209 @@
|
||||
<?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;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Translation\TranslatorInterface;
|
||||
/**
|
||||
* This translator should only be used in a development environment.
|
||||
*/
|
||||
final class PseudoLocalizationTranslator implements TranslatorInterface
|
||||
{
|
||||
private const EXPANSION_CHARACTER = '~';
|
||||
private TranslatorInterface $translator;
|
||||
private bool $accents;
|
||||
private float $expansionFactor;
|
||||
private bool $brackets;
|
||||
private bool $parseHTML;
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private array $localizableHTMLAttributes;
|
||||
/**
|
||||
* Available options:
|
||||
* * accents:
|
||||
* type: boolean
|
||||
* default: true
|
||||
* description: replace ASCII characters of the translated string with accented versions or similar characters
|
||||
* example: if true, "foo" => "ƒöö".
|
||||
*
|
||||
* * expansion_factor:
|
||||
* type: float
|
||||
* default: 1
|
||||
* validation: it must be greater than or equal to 1
|
||||
* description: expand the translated string by the given factor with spaces and tildes
|
||||
* example: if 2, "foo" => "~foo ~"
|
||||
*
|
||||
* * brackets:
|
||||
* type: boolean
|
||||
* default: true
|
||||
* description: wrap the translated string with brackets
|
||||
* example: if true, "foo" => "[foo]"
|
||||
*
|
||||
* * parse_html:
|
||||
* type: boolean
|
||||
* default: false
|
||||
* description: parse the translated string as HTML - looking for HTML tags has a performance impact but allows to preserve them from alterations - it also allows to compute the visible translated string length which is useful to correctly expand ot when it contains HTML
|
||||
* warning: unclosed tags are unsupported, they will be fixed (closed) by the parser - eg, "foo <div>bar" => "foo <div>bar</div>"
|
||||
*
|
||||
* * localizable_html_attributes:
|
||||
* type: string[]
|
||||
* default: []
|
||||
* description: the list of HTML attributes whose values can be altered - it is only useful when the "parse_html" option is set to true
|
||||
* example: if ["title"], and with the "accents" option set to true, "<a href="#" title="Go to your profile">Profile</a>" => "<a href="#" title="Ĝö ţö ýöûŕ þŕöƒîļé">Þŕöƒîļé</a>" - if "title" was not in the "localizable_html_attributes" list, the title attribute data would be left unchanged.
|
||||
*/
|
||||
public function __construct(TranslatorInterface $translator, array $options = [])
|
||||
{
|
||||
$this->translator = $translator;
|
||||
$this->accents = $options['accents'] ?? \true;
|
||||
if (1.0 > ($this->expansionFactor = $options['expansion_factor'] ?? 1.0)) {
|
||||
throw new \InvalidArgumentException('The expansion factor must be greater than or equal to 1.');
|
||||
}
|
||||
$this->brackets = $options['brackets'] ?? \true;
|
||||
$this->parseHTML = $options['parse_html'] ?? \false;
|
||||
if ($this->parseHTML && !$this->accents && 1.0 === $this->expansionFactor) {
|
||||
$this->parseHTML = \false;
|
||||
}
|
||||
$this->localizableHTMLAttributes = $options['localizable_html_attributes'] ?? [];
|
||||
}
|
||||
public function trans(string $id, array $parameters = [], string $domain = null, string $locale = null) : string
|
||||
{
|
||||
$trans = '';
|
||||
$visibleText = '';
|
||||
foreach ($this->getParts($this->translator->trans($id, $parameters, $domain, $locale)) as [$visible, $localizable, $text]) {
|
||||
if ($visible) {
|
||||
$visibleText .= $text;
|
||||
}
|
||||
if (!$localizable) {
|
||||
$trans .= $text;
|
||||
continue;
|
||||
}
|
||||
$this->addAccents($trans, $text);
|
||||
}
|
||||
$this->expand($trans, $visibleText);
|
||||
$this->addBrackets($trans);
|
||||
return $trans;
|
||||
}
|
||||
public function getLocale() : string
|
||||
{
|
||||
return $this->translator->getLocale();
|
||||
}
|
||||
private function getParts(string $originalTrans) : array
|
||||
{
|
||||
if (!$this->parseHTML) {
|
||||
return [[\true, \true, $originalTrans]];
|
||||
}
|
||||
$html = \mb_encode_numericentity($originalTrans, [0x80, 0xffff, 0, 0xffff], \mb_detect_encoding($originalTrans, null, \true) ?: 'UTF-8');
|
||||
$useInternalErrors = \libxml_use_internal_errors(\true);
|
||||
$dom = new \DOMDocument();
|
||||
$dom->loadHTML('<trans>' . $html . '</trans>');
|
||||
\libxml_clear_errors();
|
||||
\libxml_use_internal_errors($useInternalErrors);
|
||||
return $this->parseNode($dom->childNodes->item(1)->childNodes->item(0)->childNodes->item(0));
|
||||
}
|
||||
private function parseNode(\DOMNode $node) : array
|
||||
{
|
||||
$parts = [];
|
||||
foreach ($node->childNodes as $childNode) {
|
||||
if (!$childNode instanceof \DOMElement) {
|
||||
$parts[] = [\true, \true, $childNode->nodeValue];
|
||||
continue;
|
||||
}
|
||||
$parts[] = [\false, \false, '<' . $childNode->tagName];
|
||||
/** @var \DOMAttr $attribute */
|
||||
foreach ($childNode->attributes as $attribute) {
|
||||
$parts[] = [\false, \false, ' ' . $attribute->nodeName . '="'];
|
||||
$localizableAttribute = \in_array($attribute->nodeName, $this->localizableHTMLAttributes, \true);
|
||||
foreach (\preg_split('/(&(?:amp|quot|#039|lt|gt);+)/', \htmlspecialchars($attribute->nodeValue, \ENT_QUOTES, 'UTF-8'), -1, \PREG_SPLIT_DELIM_CAPTURE) as $i => $match) {
|
||||
if ('' === $match) {
|
||||
continue;
|
||||
}
|
||||
$parts[] = [\false, $localizableAttribute && 0 === $i % 2, $match];
|
||||
}
|
||||
$parts[] = [\false, \false, '"'];
|
||||
}
|
||||
$parts[] = [\false, \false, '>'];
|
||||
$parts = \array_merge($parts, $this->parseNode($childNode, $parts));
|
||||
$parts[] = [\false, \false, '</' . $childNode->tagName . '>'];
|
||||
}
|
||||
return $parts;
|
||||
}
|
||||
private function addAccents(string &$trans, string $text) : void
|
||||
{
|
||||
$trans .= $this->accents ? \strtr($text, [' ' => ' ', '!' => '¡', '"' => '″', '#' => '♯', '$' => '€', '%' => '‰', '&' => '⅋', '\'' => '´', '(' => '{', ')' => '}', '*' => '⁎', '+' => '⁺', ',' => '،', '-' => '‐', '.' => '·', '/' => '⁄', '0' => '⓪', '1' => '①', '2' => '②', '3' => '③', '4' => '④', '5' => '⑤', '6' => '⑥', '7' => '⑦', '8' => '⑧', '9' => '⑨', ':' => '∶', ';' => '⁏', '<' => '≤', '=' => '≂', '>' => '≥', '?' => '¿', '@' => '՞', 'A' => 'Å', 'B' => 'Ɓ', 'C' => 'Ç', 'D' => 'Ð', 'E' => 'É', 'F' => 'Ƒ', 'G' => 'Ĝ', 'H' => 'Ĥ', 'I' => 'Î', 'J' => 'Ĵ', 'K' => 'Ķ', 'L' => 'Ļ', 'M' => 'Ṁ', 'N' => 'Ñ', 'O' => 'Ö', 'P' => 'Þ', 'Q' => 'Ǫ', 'R' => 'Ŕ', 'S' => 'Š', 'T' => 'Ţ', 'U' => 'Û', 'V' => 'Ṽ', 'W' => 'Ŵ', 'X' => 'Ẋ', 'Y' => 'Ý', 'Z' => 'Ž', '[' => '⁅', '\\' => '∖', ']' => '⁆', '^' => '˄', '_' => '‿', '`' => '‵', 'a' => 'å', 'b' => 'ƀ', 'c' => 'ç', 'd' => 'ð', 'e' => 'é', 'f' => 'ƒ', 'g' => 'ĝ', 'h' => 'ĥ', 'i' => 'î', 'j' => 'ĵ', 'k' => 'ķ', 'l' => 'ļ', 'm' => 'ɱ', 'n' => 'ñ', 'o' => 'ö', 'p' => 'þ', 'q' => 'ǫ', 'r' => 'ŕ', 's' => 'š', 't' => 'ţ', 'u' => 'û', 'v' => 'ṽ', 'w' => 'ŵ', 'x' => 'ẋ', 'y' => 'ý', 'z' => 'ž', '{' => '(', '|' => '¦', '}' => ')', '~' => '˞']) : $text;
|
||||
}
|
||||
private function expand(string &$trans, string $visibleText) : void
|
||||
{
|
||||
if (1.0 >= $this->expansionFactor) {
|
||||
return;
|
||||
}
|
||||
$visibleLength = $this->strlen($visibleText);
|
||||
$missingLength = (int) \ceil($visibleLength * $this->expansionFactor) - $visibleLength;
|
||||
if ($this->brackets) {
|
||||
$missingLength -= 2;
|
||||
}
|
||||
if (0 >= $missingLength) {
|
||||
return;
|
||||
}
|
||||
$words = [];
|
||||
$wordsCount = 0;
|
||||
foreach (\preg_split('/ +/', $visibleText, -1, \PREG_SPLIT_NO_EMPTY) as $word) {
|
||||
$wordLength = $this->strlen($word);
|
||||
if ($wordLength >= $missingLength) {
|
||||
continue;
|
||||
}
|
||||
if (!isset($words[$wordLength])) {
|
||||
$words[$wordLength] = 0;
|
||||
}
|
||||
++$words[$wordLength];
|
||||
++$wordsCount;
|
||||
}
|
||||
if (!$words) {
|
||||
$trans .= 1 === $missingLength ? self::EXPANSION_CHARACTER : ' ' . \str_repeat(self::EXPANSION_CHARACTER, $missingLength - 1);
|
||||
return;
|
||||
}
|
||||
\arsort($words, \SORT_NUMERIC);
|
||||
$longestWordLength = \max(\array_keys($words));
|
||||
while (\true) {
|
||||
$r = \mt_rand(1, $wordsCount);
|
||||
foreach ($words as $length => $count) {
|
||||
$r -= $count;
|
||||
if ($r <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$trans .= ' ' . \str_repeat(self::EXPANSION_CHARACTER, $length);
|
||||
$missingLength -= $length + 1;
|
||||
if (0 === $missingLength) {
|
||||
return;
|
||||
}
|
||||
while ($longestWordLength >= $missingLength) {
|
||||
$wordsCount -= $words[$longestWordLength];
|
||||
unset($words[$longestWordLength]);
|
||||
if (!$words) {
|
||||
$trans .= 1 === $missingLength ? self::EXPANSION_CHARACTER : ' ' . \str_repeat(self::EXPANSION_CHARACTER, $missingLength - 1);
|
||||
return;
|
||||
}
|
||||
$longestWordLength = \max(\array_keys($words));
|
||||
}
|
||||
}
|
||||
}
|
||||
private function addBrackets(string &$trans) : void
|
||||
{
|
||||
if (!$this->brackets) {
|
||||
return;
|
||||
}
|
||||
$trans = '[' . $trans . ']';
|
||||
}
|
||||
private function strlen(string $s) : int
|
||||
{
|
||||
return \false === ($encoding = \mb_detect_encoding($s, null, \true)) ? \strlen($s) : \mb_strlen($s, $encoding);
|
||||
}
|
||||
}
|
59
dependencies/symfony/translation/Reader/TranslationReader.php
vendored
Normal file
59
dependencies/symfony/translation/Reader/TranslationReader.php
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
<?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\Reader;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\Translation\Loader\LoaderInterface;
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
/**
|
||||
* TranslationReader reads translation messages from translation files.
|
||||
*
|
||||
* @author Michel Salib <michelsalib@hotmail.com>
|
||||
*/
|
||||
class TranslationReader implements \Symfony\Component\Translation\Reader\TranslationReaderInterface
|
||||
{
|
||||
/**
|
||||
* Loaders used for import.
|
||||
*
|
||||
* @var array<string, LoaderInterface>
|
||||
*/
|
||||
private array $loaders = [];
|
||||
/**
|
||||
* Adds a loader to the translation extractor.
|
||||
*
|
||||
* @param string $format The format of the loader
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addLoader(string $format, LoaderInterface $loader)
|
||||
{
|
||||
$this->loaders[$format] = $loader;
|
||||
}
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function read(string $directory, MessageCatalogue $catalogue)
|
||||
{
|
||||
if (!\is_dir($directory)) {
|
||||
return;
|
||||
}
|
||||
foreach ($this->loaders as $format => $loader) {
|
||||
// load any existing translation files
|
||||
$finder = new Finder();
|
||||
$extension = $catalogue->getLocale() . '.' . $format;
|
||||
$files = $finder->files()->name('*.' . $extension)->in($directory);
|
||||
foreach ($files as $file) {
|
||||
$domain = \substr($file->getFilename(), 0, -1 * \strlen($extension) - 1);
|
||||
$catalogue->addCatalogue($loader->load($file->getPathname(), $catalogue->getLocale(), $domain));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
27
dependencies/symfony/translation/Reader/TranslationReaderInterface.php
vendored
Normal file
27
dependencies/symfony/translation/Reader/TranslationReaderInterface.php
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
<?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\Reader;
|
||||
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
/**
|
||||
* TranslationReader reads translation messages from translation files.
|
||||
*
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
*/
|
||||
interface TranslationReaderInterface
|
||||
{
|
||||
/**
|
||||
* Reads translation messages from a directory to the catalogue.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function read(string $directory, MessageCatalogue $catalogue);
|
||||
}
|
141
dependencies/symfony/translation/Resources/data/parents.json
vendored
Normal file
141
dependencies/symfony/translation/Resources/data/parents.json
vendored
Normal file
@ -0,0 +1,141 @@
|
||||
{
|
||||
"az_Cyrl": "root",
|
||||
"bs_Cyrl": "root",
|
||||
"en_150": "en_001",
|
||||
"en_AG": "en_001",
|
||||
"en_AI": "en_001",
|
||||
"en_AT": "en_150",
|
||||
"en_AU": "en_001",
|
||||
"en_BB": "en_001",
|
||||
"en_BE": "en_150",
|
||||
"en_BM": "en_001",
|
||||
"en_BS": "en_001",
|
||||
"en_BW": "en_001",
|
||||
"en_BZ": "en_001",
|
||||
"en_CC": "en_001",
|
||||
"en_CH": "en_150",
|
||||
"en_CK": "en_001",
|
||||
"en_CM": "en_001",
|
||||
"en_CX": "en_001",
|
||||
"en_CY": "en_001",
|
||||
"en_DE": "en_150",
|
||||
"en_DG": "en_001",
|
||||
"en_DK": "en_150",
|
||||
"en_DM": "en_001",
|
||||
"en_ER": "en_001",
|
||||
"en_FI": "en_150",
|
||||
"en_FJ": "en_001",
|
||||
"en_FK": "en_001",
|
||||
"en_FM": "en_001",
|
||||
"en_GB": "en_001",
|
||||
"en_GD": "en_001",
|
||||
"en_GG": "en_001",
|
||||
"en_GH": "en_001",
|
||||
"en_GI": "en_001",
|
||||
"en_GM": "en_001",
|
||||
"en_GY": "en_001",
|
||||
"en_HK": "en_001",
|
||||
"en_IE": "en_001",
|
||||
"en_IL": "en_001",
|
||||
"en_IM": "en_001",
|
||||
"en_IN": "en_001",
|
||||
"en_IO": "en_001",
|
||||
"en_JE": "en_001",
|
||||
"en_JM": "en_001",
|
||||
"en_KE": "en_001",
|
||||
"en_KI": "en_001",
|
||||
"en_KN": "en_001",
|
||||
"en_KY": "en_001",
|
||||
"en_LC": "en_001",
|
||||
"en_LR": "en_001",
|
||||
"en_LS": "en_001",
|
||||
"en_MG": "en_001",
|
||||
"en_MO": "en_001",
|
||||
"en_MS": "en_001",
|
||||
"en_MT": "en_001",
|
||||
"en_MU": "en_001",
|
||||
"en_MV": "en_001",
|
||||
"en_MW": "en_001",
|
||||
"en_MY": "en_001",
|
||||
"en_NA": "en_001",
|
||||
"en_NF": "en_001",
|
||||
"en_NG": "en_001",
|
||||
"en_NL": "en_150",
|
||||
"en_NR": "en_001",
|
||||
"en_NU": "en_001",
|
||||
"en_NZ": "en_001",
|
||||
"en_PG": "en_001",
|
||||
"en_PK": "en_001",
|
||||
"en_PN": "en_001",
|
||||
"en_PW": "en_001",
|
||||
"en_RW": "en_001",
|
||||
"en_SB": "en_001",
|
||||
"en_SC": "en_001",
|
||||
"en_SD": "en_001",
|
||||
"en_SE": "en_150",
|
||||
"en_SG": "en_001",
|
||||
"en_SH": "en_001",
|
||||
"en_SI": "en_150",
|
||||
"en_SL": "en_001",
|
||||
"en_SS": "en_001",
|
||||
"en_SX": "en_001",
|
||||
"en_SZ": "en_001",
|
||||
"en_TC": "en_001",
|
||||
"en_TK": "en_001",
|
||||
"en_TO": "en_001",
|
||||
"en_TT": "en_001",
|
||||
"en_TV": "en_001",
|
||||
"en_TZ": "en_001",
|
||||
"en_UG": "en_001",
|
||||
"en_VC": "en_001",
|
||||
"en_VG": "en_001",
|
||||
"en_VU": "en_001",
|
||||
"en_WS": "en_001",
|
||||
"en_ZA": "en_001",
|
||||
"en_ZM": "en_001",
|
||||
"en_ZW": "en_001",
|
||||
"es_AR": "es_419",
|
||||
"es_BO": "es_419",
|
||||
"es_BR": "es_419",
|
||||
"es_BZ": "es_419",
|
||||
"es_CL": "es_419",
|
||||
"es_CO": "es_419",
|
||||
"es_CR": "es_419",
|
||||
"es_CU": "es_419",
|
||||
"es_DO": "es_419",
|
||||
"es_EC": "es_419",
|
||||
"es_GT": "es_419",
|
||||
"es_HN": "es_419",
|
||||
"es_MX": "es_419",
|
||||
"es_NI": "es_419",
|
||||
"es_PA": "es_419",
|
||||
"es_PE": "es_419",
|
||||
"es_PR": "es_419",
|
||||
"es_PY": "es_419",
|
||||
"es_SV": "es_419",
|
||||
"es_US": "es_419",
|
||||
"es_UY": "es_419",
|
||||
"es_VE": "es_419",
|
||||
"ff_Adlm": "root",
|
||||
"hi_Latn": "en_IN",
|
||||
"ks_Deva": "root",
|
||||
"nb": "no",
|
||||
"nn": "no",
|
||||
"pa_Arab": "root",
|
||||
"pt_AO": "pt_PT",
|
||||
"pt_CH": "pt_PT",
|
||||
"pt_CV": "pt_PT",
|
||||
"pt_GQ": "pt_PT",
|
||||
"pt_GW": "pt_PT",
|
||||
"pt_LU": "pt_PT",
|
||||
"pt_MO": "pt_PT",
|
||||
"pt_MZ": "pt_PT",
|
||||
"pt_ST": "pt_PT",
|
||||
"pt_TL": "pt_PT",
|
||||
"sd_Deva": "root",
|
||||
"sr_Latn": "root",
|
||||
"uz_Arab": "root",
|
||||
"uz_Cyrl": "root",
|
||||
"zh_Hant": "root",
|
||||
"zh_Hant_MO": "zh_Hant_HK"
|
||||
}
|
21
dependencies/symfony/translation/Resources/functions.php
vendored
Normal file
21
dependencies/symfony/translation/Resources/functions.php
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
<?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;
|
||||
|
||||
if (!\function_exists(\Symfony\Component\Translation\t::class)) {
|
||||
/**
|
||||
* @author Nate Wiebe <nate@northern.co>
|
||||
*/
|
||||
function t(string $message, array $parameters = [], string $domain = null) : \Symfony\Component\Translation\TranslatableMessage
|
||||
{
|
||||
return new \Symfony\Component\Translation\TranslatableMessage($message, $parameters, $domain);
|
||||
}
|
||||
}
|
2261
dependencies/symfony/translation/Resources/schemas/xliff-core-1.2-transitional.xsd
vendored
Normal file
2261
dependencies/symfony/translation/Resources/schemas/xliff-core-1.2-transitional.xsd
vendored
Normal file
File diff suppressed because it is too large
Load Diff
411
dependencies/symfony/translation/Resources/schemas/xliff-core-2.0.xsd
vendored
Normal file
411
dependencies/symfony/translation/Resources/schemas/xliff-core-2.0.xsd
vendored
Normal file
@ -0,0 +1,411 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
XLIFF Version 2.0
|
||||
OASIS Standard
|
||||
05 August 2014
|
||||
Copyright (c) OASIS Open 2014. All rights reserved.
|
||||
Source: http://docs.oasis-open.org/xliff/xliff-core/v2.0/os/schemas/
|
||||
-->
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
elementFormDefault="qualified"
|
||||
xmlns:xlf="urn:oasis:names:tc:xliff:document:2.0"
|
||||
targetNamespace="urn:oasis:names:tc:xliff:document:2.0">
|
||||
|
||||
<!-- Import -->
|
||||
|
||||
<xs:import namespace="http://www.w3.org/XML/1998/namespace"
|
||||
schemaLocation="informativeCopiesOf3rdPartySchemas/w3c/xml.xsd"/>
|
||||
|
||||
<!-- Element Group -->
|
||||
|
||||
<xs:group name="inline">
|
||||
<xs:choice>
|
||||
<xs:element ref="xlf:cp"/>
|
||||
<xs:element ref="xlf:ph"/>
|
||||
<xs:element ref="xlf:pc"/>
|
||||
<xs:element ref="xlf:sc"/>
|
||||
<xs:element ref="xlf:ec"/>
|
||||
<xs:element ref="xlf:mrk"/>
|
||||
<xs:element ref="xlf:sm"/>
|
||||
<xs:element ref="xlf:em"/>
|
||||
</xs:choice>
|
||||
</xs:group>
|
||||
|
||||
<!-- Attribute Types -->
|
||||
|
||||
<xs:simpleType name="yesNo">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="yes"/>
|
||||
<xs:enumeration value="no"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="yesNoFirstNo">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="yes"/>
|
||||
<xs:enumeration value="firstNo"/>
|
||||
<xs:enumeration value="no"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="dirValue">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="ltr"/>
|
||||
<xs:enumeration value="rtl"/>
|
||||
<xs:enumeration value="auto"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="appliesTo">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="source"/>
|
||||
<xs:enumeration value="target"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="userDefinedValue">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="[^\s:]+:[^\s:]+"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="attrType_type">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="fmt"/>
|
||||
<xs:enumeration value="ui"/>
|
||||
<xs:enumeration value="quote"/>
|
||||
<xs:enumeration value="link"/>
|
||||
<xs:enumeration value="image"/>
|
||||
<xs:enumeration value="other"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="typeForMrkValues">
|
||||
<xs:restriction base="xs:NMTOKEN">
|
||||
<xs:enumeration value="generic"/>
|
||||
<xs:enumeration value="comment"/>
|
||||
<xs:enumeration value="term"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="attrType_typeForMrk">
|
||||
<xs:union memberTypes="xlf:typeForMrkValues xlf:userDefinedValue"/>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="priorityValue">
|
||||
<xs:restriction base="xs:positiveInteger">
|
||||
<xs:minInclusive value="1"/>
|
||||
<xs:maxInclusive value="10"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="stateType">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="initial"/>
|
||||
<xs:enumeration value="translated"/>
|
||||
<xs:enumeration value="reviewed"/>
|
||||
<xs:enumeration value="final"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<!-- Structural Elements -->
|
||||
|
||||
<xs:element name="xliff">
|
||||
<xs:complexType mixed="false">
|
||||
<xs:sequence>
|
||||
<xs:element minOccurs="1" maxOccurs="unbounded" ref="xlf:file"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="version" use="required"/>
|
||||
<xs:attribute name="srcLang" use="required"/>
|
||||
<xs:attribute name="trgLang" use="optional"/>
|
||||
<xs:attribute ref="xml:space" use="optional" default="default"/>
|
||||
<xs:anyAttribute namespace="##other" processContents="lax"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="file">
|
||||
<xs:complexType mixed="false">
|
||||
<xs:sequence>
|
||||
<xs:element minOccurs="0" maxOccurs="1" ref="xlf:skeleton"/>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"
|
||||
processContents="lax"/>
|
||||
<xs:element minOccurs="0" maxOccurs="1" ref="xlf:notes"/>
|
||||
<xs:choice minOccurs="1" maxOccurs="unbounded">
|
||||
<xs:element ref="xlf:unit"/>
|
||||
<xs:element ref="xlf:group"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
|
||||
<xs:attribute name="canResegment" use="optional" type="xlf:yesNo" default="yes"/>
|
||||
<xs:attribute name="original" use="optional"/>
|
||||
<xs:attribute name="translate" use="optional" type="xlf:yesNo" default="yes"/>
|
||||
<xs:attribute name="srcDir" use="optional" type="xlf:dirValue" default="auto"/>
|
||||
<xs:attribute name="trgDir" use="optional" type="xlf:dirValue" default="auto"/>
|
||||
<xs:attribute ref="xml:space" use="optional"/>
|
||||
<xs:anyAttribute namespace="##other" processContents="lax"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="skeleton">
|
||||
<xs:complexType mixed="true">
|
||||
<xs:sequence>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"
|
||||
processContents="lax"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="href" use="optional"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="group">
|
||||
<xs:complexType mixed="false">
|
||||
<xs:sequence>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"
|
||||
processContents="lax"/>
|
||||
<xs:element minOccurs="0" maxOccurs="1" ref="xlf:notes"/>
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element ref="xlf:unit"/>
|
||||
<xs:element ref="xlf:group"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
|
||||
<xs:attribute name="name" use="optional"/>
|
||||
<xs:attribute name="canResegment" use="optional" type="xlf:yesNo"/>
|
||||
<xs:attribute name="translate" use="optional" type="xlf:yesNo"/>
|
||||
<xs:attribute name="srcDir" use="optional" type="xlf:dirValue"/>
|
||||
<xs:attribute name="trgDir" use="optional" type="xlf:dirValue"/>
|
||||
<xs:attribute name="type" use="optional" type="xlf:userDefinedValue"/>
|
||||
<xs:attribute ref="xml:space" use="optional"/>
|
||||
<xs:anyAttribute namespace="##other" processContents="lax"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="unit">
|
||||
<xs:complexType mixed="false">
|
||||
<xs:sequence>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"
|
||||
processContents="lax"/>
|
||||
<xs:element minOccurs="0" maxOccurs="1" ref="xlf:notes"/>
|
||||
<xs:element minOccurs="0" maxOccurs="1" ref="xlf:originalData"/>
|
||||
<xs:choice minOccurs="1" maxOccurs="unbounded">
|
||||
<xs:element ref="xlf:segment"/>
|
||||
<xs:element ref="xlf:ignorable"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
|
||||
<xs:attribute name="name" use="optional"/>
|
||||
<xs:attribute name="canResegment" use="optional" type="xlf:yesNo"/>
|
||||
<xs:attribute name="translate" use="optional" type="xlf:yesNo"/>
|
||||
<xs:attribute name="srcDir" use="optional" type="xlf:dirValue"/>
|
||||
<xs:attribute name="trgDir" use="optional" type="xlf:dirValue"/>
|
||||
<xs:attribute ref="xml:space" use="optional"/>
|
||||
<xs:attribute name="type" use="optional" type="xlf:userDefinedValue"/>
|
||||
<xs:anyAttribute namespace="##other" processContents="lax"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="segment">
|
||||
<xs:complexType mixed="false">
|
||||
<xs:sequence>
|
||||
<xs:element minOccurs="1" maxOccurs="1" ref="xlf:source"/>
|
||||
<xs:element minOccurs="0" maxOccurs="1" ref="xlf:target"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="id" use="optional" type="xs:NMTOKEN"/>
|
||||
<xs:attribute name="canResegment" use="optional" type="xlf:yesNo"/>
|
||||
<xs:attribute name="state" use="optional" type="xlf:stateType" default="initial"/>
|
||||
<xs:attribute name="subState" use="optional"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="ignorable">
|
||||
<xs:complexType mixed="false">
|
||||
<xs:sequence>
|
||||
<xs:element minOccurs="1" maxOccurs="1" ref="xlf:source"/>
|
||||
<xs:element minOccurs="0" maxOccurs="1" ref="xlf:target"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="id" use="optional" type="xs:NMTOKEN"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="notes">
|
||||
<xs:complexType mixed="false">
|
||||
<xs:sequence>
|
||||
<xs:element minOccurs="1" maxOccurs="unbounded" ref="xlf:note"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="note">
|
||||
<xs:complexType mixed="true">
|
||||
<xs:attribute name="id" use="optional" type="xs:NMTOKEN"/>
|
||||
<xs:attribute name="appliesTo" use="optional" type="xlf:appliesTo"/>
|
||||
<xs:attribute name="category" use="optional"/>
|
||||
<xs:attribute name="priority" use="optional" type="xlf:priorityValue" default="1"/>
|
||||
<xs:anyAttribute namespace="##other" processContents="lax"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="originalData">
|
||||
<xs:complexType mixed="false">
|
||||
<xs:sequence>
|
||||
<xs:element minOccurs="1" maxOccurs="unbounded" ref="xlf:data"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="data">
|
||||
<xs:complexType mixed="true">
|
||||
<xs:sequence>
|
||||
<xs:element minOccurs="0" maxOccurs="unbounded" ref="xlf:cp"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
|
||||
<xs:attribute name="dir" use="optional" type="xlf:dirValue" default="auto"/>
|
||||
<xs:attribute ref="xml:space" use="optional" fixed="preserve"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="source">
|
||||
<xs:complexType mixed="true">
|
||||
<xs:group ref="xlf:inline" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:attribute ref="xml:lang" use="optional"/>
|
||||
<xs:attribute ref="xml:space" use="optional"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="target">
|
||||
<xs:complexType mixed="true">
|
||||
<xs:group ref="xlf:inline" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:attribute ref="xml:lang" use="optional"/>
|
||||
<xs:attribute ref="xml:space" use="optional"/>
|
||||
<xs:attribute name="order" use="optional" type="xs:positiveInteger"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<!-- Inline Elements -->
|
||||
|
||||
<xs:element name="cp">
|
||||
<!-- Code Point -->
|
||||
<xs:complexType mixed="false">
|
||||
<xs:attribute name="hex" use="required" type="xs:hexBinary"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="ph">
|
||||
<!-- Placeholder -->
|
||||
<xs:complexType mixed="false">
|
||||
<xs:attribute name="canCopy" use="optional" type="xlf:yesNo" default="yes"/>
|
||||
<xs:attribute name="canDelete" use="optional" type="xlf:yesNo" default="yes"/>
|
||||
<xs:attribute name="canReorder" use="optional" type="xlf:yesNoFirstNo" default="yes"/>
|
||||
<xs:attribute name="copyOf" use="optional" type="xs:NMTOKEN"/>
|
||||
<xs:attribute name="disp" use="optional"/>
|
||||
<xs:attribute name="equiv" use="optional"/>
|
||||
<xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
|
||||
<xs:attribute name="dataRef" use="optional" type="xs:NMTOKEN"/>
|
||||
<xs:attribute name="subFlows" use="optional" type="xs:NMTOKENS"/>
|
||||
<xs:attribute name="subType" use="optional" type="xlf:userDefinedValue"/>
|
||||
<xs:attribute name="type" use="optional" type="xlf:attrType_type"/>
|
||||
<xs:anyAttribute namespace="##other" processContents="lax"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="pc">
|
||||
<!-- Paired Code -->
|
||||
<xs:complexType mixed="true">
|
||||
<xs:group ref="xlf:inline" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:attribute name="canCopy" use="optional" type="xlf:yesNo" default="yes"/>
|
||||
<xs:attribute name="canDelete" use="optional" type="xlf:yesNo" default="yes"/>
|
||||
<xs:attribute name="canOverlap" use="optional" type="xlf:yesNo"/>
|
||||
<xs:attribute name="canReorder" use="optional" type="xlf:yesNoFirstNo" default="yes"/>
|
||||
<xs:attribute name="copyOf" use="optional" type="xs:NMTOKEN"/>
|
||||
<xs:attribute name="dispEnd" use="optional"/>
|
||||
<xs:attribute name="dispStart" use="optional"/>
|
||||
<xs:attribute name="equivEnd" use="optional"/>
|
||||
<xs:attribute name="equivStart" use="optional"/>
|
||||
<xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
|
||||
<xs:attribute name="dataRefEnd" use="optional" type="xs:NMTOKEN"/>
|
||||
<xs:attribute name="dataRefStart" use="optional" type="xs:NMTOKEN"/>
|
||||
<xs:attribute name="subFlowsEnd" use="optional" type="xs:NMTOKENS"/>
|
||||
<xs:attribute name="subFlowsStart" use="optional" type="xs:NMTOKENS"/>
|
||||
<xs:attribute name="subType" use="optional" type="xlf:userDefinedValue"/>
|
||||
<xs:attribute name="type" use="optional" type="xlf:attrType_type"/>
|
||||
<xs:attribute name="dir" use="optional" type="xlf:dirValue"/>
|
||||
<xs:anyAttribute namespace="##other" processContents="lax"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="sc">
|
||||
<!-- Start Code -->
|
||||
<xs:complexType mixed="false">
|
||||
<xs:attribute name="canCopy" use="optional" type="xlf:yesNo" default="yes"/>
|
||||
<xs:attribute name="canDelete" use="optional" type="xlf:yesNo" default="yes"/>
|
||||
<xs:attribute name="canOverlap" use="optional" type="xlf:yesNo" default="yes"/>
|
||||
<xs:attribute name="canReorder" use="optional" type="xlf:yesNoFirstNo" default="yes"/>
|
||||
<xs:attribute name="copyOf" use="optional" type="xs:NMTOKEN"/>
|
||||
<xs:attribute name="dataRef" use="optional" type="xs:NMTOKEN"/>
|
||||
<xs:attribute name="dir" use="optional" type="xlf:dirValue"/>
|
||||
<xs:attribute name="disp" use="optional"/>
|
||||
<xs:attribute name="equiv" use="optional"/>
|
||||
<xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
|
||||
<xs:attribute name="isolated" use="optional" type="xlf:yesNo" default="no"/>
|
||||
<xs:attribute name="subFlows" use="optional" type="xs:NMTOKENS"/>
|
||||
<xs:attribute name="subType" use="optional" type="xlf:userDefinedValue"/>
|
||||
<xs:attribute name="type" use="optional" type="xlf:attrType_type"/>
|
||||
<xs:anyAttribute namespace="##other" processContents="lax"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="ec">
|
||||
<!-- End Code -->
|
||||
<xs:complexType mixed="false">
|
||||
<xs:attribute name="canCopy" use="optional" type="xlf:yesNo" default="yes"/>
|
||||
<xs:attribute name="canDelete" use="optional" type="xlf:yesNo" default="yes"/>
|
||||
<xs:attribute name="canOverlap" use="optional" type="xlf:yesNo" default="yes"/>
|
||||
<xs:attribute name="canReorder" use="optional" type="xlf:yesNoFirstNo" default="yes"/>
|
||||
<xs:attribute name="copyOf" use="optional" type="xs:NMTOKEN"/>
|
||||
<xs:attribute name="dataRef" use="optional" type="xs:NMTOKEN"/>
|
||||
<xs:attribute name="dir" use="optional" type="xlf:dirValue"/>
|
||||
<xs:attribute name="disp" use="optional"/>
|
||||
<xs:attribute name="equiv" use="optional"/>
|
||||
<xs:attribute name="id" use="optional" type="xs:NMTOKEN"/>
|
||||
<xs:attribute name="isolated" use="optional" type="xlf:yesNo" default="no"/>
|
||||
<xs:attribute name="startRef" use="optional" type="xs:NMTOKEN"/>
|
||||
<xs:attribute name="subFlows" use="optional" type="xs:NMTOKENS"/>
|
||||
<xs:attribute name="subType" use="optional" type="xlf:userDefinedValue"/>
|
||||
<xs:attribute name="type" use="optional" type="xlf:attrType_type"/>
|
||||
<xs:anyAttribute namespace="##other" processContents="lax"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="mrk">
|
||||
<!-- Annotation Marker -->
|
||||
<xs:complexType mixed="true">
|
||||
<xs:group ref="xlf:inline" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
|
||||
<xs:attribute name="translate" use="optional" type="xlf:yesNo"/>
|
||||
<xs:attribute name="type" use="optional" type="xlf:attrType_typeForMrk"/>
|
||||
<xs:attribute name="ref" use="optional" type="xs:anyURI"/>
|
||||
<xs:attribute name="value" use="optional"/>
|
||||
<xs:anyAttribute namespace="##other" processContents="lax"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="sm">
|
||||
<!-- Start Annotation Marker -->
|
||||
<xs:complexType mixed="false">
|
||||
<xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
|
||||
<xs:attribute name="translate" use="optional" type="xlf:yesNo"/>
|
||||
<xs:attribute name="type" use="optional" type="xlf:attrType_typeForMrk"/>
|
||||
<xs:attribute name="ref" use="optional" type="xs:anyURI"/>
|
||||
<xs:attribute name="value" use="optional"/>
|
||||
<xs:anyAttribute namespace="##other" processContents="lax"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="em">
|
||||
<!-- End Annotation Marker -->
|
||||
<xs:complexType mixed="false">
|
||||
<xs:attribute name="startRef" use="required" type="xs:NMTOKEN"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
</xs:schema>
|
309
dependencies/symfony/translation/Resources/schemas/xml.xsd
vendored
Normal file
309
dependencies/symfony/translation/Resources/schemas/xml.xsd
vendored
Normal file
@ -0,0 +1,309 @@
|
||||
<?xml version='1.0'?>
|
||||
<?xml-stylesheet href="../2008/09/xsd.xsl" type="text/xsl"?>
|
||||
<xs:schema targetNamespace="http://www.w3.org/XML/1998/namespace"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns ="http://www.w3.org/1999/xhtml"
|
||||
xml:lang="en">
|
||||
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
<div>
|
||||
<h1>About the XML namespace</h1>
|
||||
|
||||
<div class="bodytext">
|
||||
<p>
|
||||
|
||||
This schema document describes the XML namespace, in a form
|
||||
suitable for import by other schema documents.
|
||||
</p>
|
||||
<p>
|
||||
See <a href="http://www.w3.org/XML/1998/namespace.html">
|
||||
http://www.w3.org/XML/1998/namespace.html</a> and
|
||||
<a href="http://www.w3.org/TR/REC-xml">
|
||||
http://www.w3.org/TR/REC-xml</a> for information
|
||||
about this namespace.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Note that local names in this namespace are intended to be
|
||||
defined only by the World Wide Web Consortium or its subgroups.
|
||||
The names currently defined in this namespace are listed below.
|
||||
They should not be used with conflicting semantics by any Working
|
||||
Group, specification, or document instance.
|
||||
</p>
|
||||
<p>
|
||||
See further below in this document for more information about <a
|
||||
href="#usage">how to refer to this schema document from your own
|
||||
XSD schema documents</a> and about <a href="#nsversioning">the
|
||||
namespace-versioning policy governing this schema document</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
|
||||
<xs:attribute name="lang">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
<div>
|
||||
|
||||
<h3>lang (as an attribute name)</h3>
|
||||
<p>
|
||||
|
||||
denotes an attribute whose value
|
||||
is a language code for the natural language of the content of
|
||||
any element; its value is inherited. This name is reserved
|
||||
by virtue of its definition in the XML specification.</p>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<h4>Notes</h4>
|
||||
<p>
|
||||
Attempting to install the relevant ISO 2- and 3-letter
|
||||
codes as the enumerated possible values is probably never
|
||||
going to be a realistic possibility.
|
||||
</p>
|
||||
<p>
|
||||
|
||||
See BCP 47 at <a href="http://www.rfc-editor.org/rfc/bcp/bcp47.txt">
|
||||
http://www.rfc-editor.org/rfc/bcp/bcp47.txt</a>
|
||||
and the IANA language subtag registry at
|
||||
<a href="http://www.iana.org/assignments/language-subtag-registry">
|
||||
http://www.iana.org/assignments/language-subtag-registry</a>
|
||||
for further information.
|
||||
</p>
|
||||
<p>
|
||||
|
||||
The union allows for the 'un-declaration' of xml:lang with
|
||||
the empty string.
|
||||
</p>
|
||||
</div>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:simpleType>
|
||||
<xs:union memberTypes="xs:language">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value=""/>
|
||||
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:union>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
|
||||
<xs:attribute name="space">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
|
||||
<div>
|
||||
|
||||
<h3>space (as an attribute name)</h3>
|
||||
<p>
|
||||
denotes an attribute whose
|
||||
value is a keyword indicating what whitespace processing
|
||||
discipline is intended for the content of the element; its
|
||||
value is inherited. This name is reserved by virtue of its
|
||||
definition in the XML specification.</p>
|
||||
|
||||
</div>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:simpleType>
|
||||
|
||||
<xs:restriction base="xs:NCName">
|
||||
<xs:enumeration value="default"/>
|
||||
<xs:enumeration value="preserve"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
|
||||
<xs:attribute name="base" type="xs:anyURI"> <xs:annotation>
|
||||
<xs:documentation>
|
||||
|
||||
<div>
|
||||
|
||||
<h3>base (as an attribute name)</h3>
|
||||
<p>
|
||||
denotes an attribute whose value
|
||||
provides a URI to be used as the base for interpreting any
|
||||
relative URIs in the scope of the element on which it
|
||||
appears; its value is inherited. This name is reserved
|
||||
by virtue of its definition in the XML Base specification.</p>
|
||||
|
||||
<p>
|
||||
See <a
|
||||
href="http://www.w3.org/TR/xmlbase/">http://www.w3.org/TR/xmlbase/</a>
|
||||
for information about this attribute.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
|
||||
<xs:attribute name="id" type="xs:ID">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
<div>
|
||||
|
||||
<h3>id (as an attribute name)</h3>
|
||||
<p>
|
||||
|
||||
denotes an attribute whose value
|
||||
should be interpreted as if declared to be of type ID.
|
||||
This name is reserved by virtue of its definition in the
|
||||
xml:id specification.</p>
|
||||
|
||||
<p>
|
||||
See <a
|
||||
href="http://www.w3.org/TR/xml-id/">http://www.w3.org/TR/xml-id/</a>
|
||||
for information about this attribute.
|
||||
</p>
|
||||
</div>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
|
||||
</xs:attribute>
|
||||
|
||||
<xs:attributeGroup name="specialAttrs">
|
||||
<xs:attribute ref="xml:base"/>
|
||||
<xs:attribute ref="xml:lang"/>
|
||||
<xs:attribute ref="xml:space"/>
|
||||
<xs:attribute ref="xml:id"/>
|
||||
</xs:attributeGroup>
|
||||
|
||||
<xs:annotation>
|
||||
|
||||
<xs:documentation>
|
||||
<div>
|
||||
|
||||
<h3>Father (in any context at all)</h3>
|
||||
|
||||
<div class="bodytext">
|
||||
<p>
|
||||
denotes Jon Bosak, the chair of
|
||||
the original XML Working Group. This name is reserved by
|
||||
the following decision of the W3C XML Plenary and
|
||||
XML Coordination groups:
|
||||
</p>
|
||||
<blockquote>
|
||||
<p>
|
||||
|
||||
In appreciation for his vision, leadership and
|
||||
dedication the W3C XML Plenary on this 10th day of
|
||||
February, 2000, reserves for Jon Bosak in perpetuity
|
||||
the XML name "xml:Father".
|
||||
</p>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
|
||||
<div xml:id="usage" id="usage">
|
||||
<h2><a name="usage">About this schema document</a></h2>
|
||||
|
||||
<div class="bodytext">
|
||||
<p>
|
||||
This schema defines attributes and an attribute group suitable
|
||||
for use by schemas wishing to allow <code>xml:base</code>,
|
||||
<code>xml:lang</code>, <code>xml:space</code> or
|
||||
<code>xml:id</code> attributes on elements they define.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To enable this, such a schema must import this schema for
|
||||
the XML namespace, e.g. as follows:
|
||||
</p>
|
||||
<pre>
|
||||
<schema.. .>
|
||||
.. .
|
||||
<import namespace="http://www.w3.org/XML/1998/namespace"
|
||||
schemaLocation="http://www.w3.org/2001/xml.xsd"/>
|
||||
</pre>
|
||||
<p>
|
||||
or
|
||||
</p>
|
||||
<pre>
|
||||
|
||||
<import namespace="http://www.w3.org/XML/1998/namespace"
|
||||
schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
|
||||
</pre>
|
||||
<p>
|
||||
Subsequently, qualified reference to any of the attributes or the
|
||||
group defined below will have the desired effect, e.g.
|
||||
</p>
|
||||
<pre>
|
||||
<type.. .>
|
||||
.. .
|
||||
<attributeGroup ref="xml:specialAttrs"/>
|
||||
</pre>
|
||||
<p>
|
||||
will define a type which will schema-validate an instance element
|
||||
with any of those attributes.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
<div id="nsversioning" xml:id="nsversioning">
|
||||
<h2><a name="nsversioning">Versioning policy for this schema document</a></h2>
|
||||
|
||||
<div class="bodytext">
|
||||
<p>
|
||||
In keeping with the XML Schema WG's standard versioning
|
||||
policy, this schema document will persist at
|
||||
<a href="http://www.w3.org/2009/01/xml.xsd">
|
||||
http://www.w3.org/2009/01/xml.xsd</a>.
|
||||
</p>
|
||||
<p>
|
||||
At the date of issue it can also be found at
|
||||
<a href="http://www.w3.org/2001/xml.xsd">
|
||||
http://www.w3.org/2001/xml.xsd</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The schema document at that URI may however change in the future,
|
||||
in order to remain compatible with the latest version of XML
|
||||
Schema itself, or with the XML namespace itself. In other words,
|
||||
if the XML Schema or XML namespaces change, the version of this
|
||||
document at <a href="http://www.w3.org/2001/xml.xsd">
|
||||
http://www.w3.org/2001/xml.xsd
|
||||
</a>
|
||||
will change accordingly; the version at
|
||||
<a href="http://www.w3.org/2009/01/xml.xsd">
|
||||
http://www.w3.org/2009/01/xml.xsd
|
||||
</a>
|
||||
will not change.
|
||||
</p>
|
||||
<p>
|
||||
|
||||
Previous dated (and unchanging) versions of this schema
|
||||
document are at:
|
||||
</p>
|
||||
<ul>
|
||||
<li><a href="http://www.w3.org/2009/01/xml.xsd">
|
||||
http://www.w3.org/2009/01/xml.xsd</a></li>
|
||||
<li><a href="http://www.w3.org/2007/08/xml.xsd">
|
||||
http://www.w3.org/2007/08/xml.xsd</a></li>
|
||||
<li><a href="http://www.w3.org/2004/10/xml.xsd">
|
||||
|
||||
http://www.w3.org/2004/10/xml.xsd</a></li>
|
||||
<li><a href="http://www.w3.org/2001/03/xml.xsd">
|
||||
http://www.w3.org/2001/03/xml.xsd</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
|
||||
</xs:schema>
|
130
dependencies/symfony/translation/Test/ProviderFactoryTestCase.php
vendored
Normal file
130
dependencies/symfony/translation/Test/ProviderFactoryTestCase.php
vendored
Normal file
@ -0,0 +1,130 @@
|
||||
<?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\Test;
|
||||
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use WP_Ultimo\Dependencies\Psr\Log\LoggerInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\HttpClient\MockHttpClient;
|
||||
use Symfony\Component\Translation\Dumper\XliffFileDumper;
|
||||
use Symfony\Component\Translation\Exception\IncompleteDsnException;
|
||||
use Symfony\Component\Translation\Exception\UnsupportedSchemeException;
|
||||
use Symfony\Component\Translation\Loader\LoaderInterface;
|
||||
use Symfony\Component\Translation\Provider\Dsn;
|
||||
use Symfony\Component\Translation\Provider\ProviderFactoryInterface;
|
||||
use Symfony\Component\Translation\TranslatorBagInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
/**
|
||||
* A test case to ease testing a translation provider factory.
|
||||
*
|
||||
* @author Mathieu Santostefano <msantostefano@protonmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
abstract class ProviderFactoryTestCase extends TestCase
|
||||
{
|
||||
protected HttpClientInterface $client;
|
||||
protected LoggerInterface|MockObject $logger;
|
||||
protected string $defaultLocale;
|
||||
protected LoaderInterface|MockObject $loader;
|
||||
protected XliffFileDumper|MockObject $xliffFileDumper;
|
||||
protected TranslatorBagInterface|MockObject $translatorBag;
|
||||
public abstract function createFactory() : ProviderFactoryInterface;
|
||||
/**
|
||||
* @return iterable<array{0: bool, 1: string}>
|
||||
*/
|
||||
public static abstract function supportsProvider() : iterable;
|
||||
/**
|
||||
* @return iterable<array{0: string, 1: string}>
|
||||
*/
|
||||
public static abstract function createProvider() : iterable;
|
||||
/**
|
||||
* @return iterable<array{0: string, 1: string|null}>
|
||||
*/
|
||||
public static function unsupportedSchemeProvider() : iterable
|
||||
{
|
||||
return [];
|
||||
}
|
||||
/**
|
||||
* @return iterable<array{0: string, 1: string|null}>
|
||||
*/
|
||||
public static function incompleteDsnProvider() : iterable
|
||||
{
|
||||
return [];
|
||||
}
|
||||
/**
|
||||
* @dataProvider supportsProvider
|
||||
*/
|
||||
public function testSupports(bool $expected, string $dsn)
|
||||
{
|
||||
$factory = $this->createFactory();
|
||||
$this->assertSame($expected, $factory->supports(new Dsn($dsn)));
|
||||
}
|
||||
/**
|
||||
* @dataProvider createProvider
|
||||
*/
|
||||
public function testCreate(string $expected, string $dsn)
|
||||
{
|
||||
$factory = $this->createFactory();
|
||||
$provider = $factory->create(new Dsn($dsn));
|
||||
$this->assertSame($expected, (string) $provider);
|
||||
}
|
||||
/**
|
||||
* @dataProvider unsupportedSchemeProvider
|
||||
*/
|
||||
public function testUnsupportedSchemeException(string $dsn, string $message = null)
|
||||
{
|
||||
$factory = $this->createFactory();
|
||||
$dsn = new Dsn($dsn);
|
||||
$this->expectException(UnsupportedSchemeException::class);
|
||||
if (null !== $message) {
|
||||
$this->expectExceptionMessage($message);
|
||||
}
|
||||
$factory->create($dsn);
|
||||
}
|
||||
/**
|
||||
* @dataProvider incompleteDsnProvider
|
||||
*/
|
||||
public function testIncompleteDsnException(string $dsn, string $message = null)
|
||||
{
|
||||
$factory = $this->createFactory();
|
||||
$dsn = new Dsn($dsn);
|
||||
$this->expectException(IncompleteDsnException::class);
|
||||
if (null !== $message) {
|
||||
$this->expectExceptionMessage($message);
|
||||
}
|
||||
$factory->create($dsn);
|
||||
}
|
||||
protected function getClient() : HttpClientInterface
|
||||
{
|
||||
return $this->client ??= new MockHttpClient();
|
||||
}
|
||||
protected function getLogger() : LoggerInterface
|
||||
{
|
||||
return $this->logger ??= $this->createMock(LoggerInterface::class);
|
||||
}
|
||||
protected function getDefaultLocale() : string
|
||||
{
|
||||
return $this->defaultLocale ??= 'en';
|
||||
}
|
||||
protected function getLoader() : LoaderInterface
|
||||
{
|
||||
return $this->loader ??= $this->createMock(LoaderInterface::class);
|
||||
}
|
||||
protected function getXliffFileDumper() : XliffFileDumper
|
||||
{
|
||||
return $this->xliffFileDumper ??= $this->createMock(XliffFileDumper::class);
|
||||
}
|
||||
protected function getTranslatorBag() : TranslatorBagInterface
|
||||
{
|
||||
return $this->translatorBag ??= $this->createMock(TranslatorBagInterface::class);
|
||||
}
|
||||
}
|
73
dependencies/symfony/translation/Test/ProviderTestCase.php
vendored
Normal file
73
dependencies/symfony/translation/Test/ProviderTestCase.php
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
<?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\Test;
|
||||
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use WP_Ultimo\Dependencies\Psr\Log\LoggerInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\HttpClient\MockHttpClient;
|
||||
use Symfony\Component\Translation\Dumper\XliffFileDumper;
|
||||
use Symfony\Component\Translation\Loader\LoaderInterface;
|
||||
use Symfony\Component\Translation\Provider\ProviderInterface;
|
||||
use Symfony\Component\Translation\TranslatorBagInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
/**
|
||||
* A test case to ease testing a translation provider.
|
||||
*
|
||||
* @author Mathieu Santostefano <msantostefano@protonmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
abstract class ProviderTestCase extends TestCase
|
||||
{
|
||||
protected HttpClientInterface $client;
|
||||
protected LoggerInterface|MockObject $logger;
|
||||
protected string $defaultLocale;
|
||||
protected LoaderInterface|MockObject $loader;
|
||||
protected XliffFileDumper|MockObject $xliffFileDumper;
|
||||
protected TranslatorBagInterface|MockObject $translatorBag;
|
||||
public static abstract function createProvider(HttpClientInterface $client, LoaderInterface $loader, LoggerInterface $logger, string $defaultLocale, string $endpoint) : ProviderInterface;
|
||||
/**
|
||||
* @return iterable<array{0: ProviderInterface, 1: string}>
|
||||
*/
|
||||
public static abstract function toStringProvider() : iterable;
|
||||
/**
|
||||
* @dataProvider toStringProvider
|
||||
*/
|
||||
public function testToString(ProviderInterface $provider, string $expected)
|
||||
{
|
||||
$this->assertSame($expected, (string) $provider);
|
||||
}
|
||||
protected function getClient() : MockHttpClient
|
||||
{
|
||||
return $this->client ??= new MockHttpClient();
|
||||
}
|
||||
protected function getLoader() : LoaderInterface
|
||||
{
|
||||
return $this->loader ??= $this->createMock(LoaderInterface::class);
|
||||
}
|
||||
protected function getLogger() : LoggerInterface
|
||||
{
|
||||
return $this->logger ??= $this->createMock(LoggerInterface::class);
|
||||
}
|
||||
protected function getDefaultLocale() : string
|
||||
{
|
||||
return $this->defaultLocale ??= 'en';
|
||||
}
|
||||
protected function getXliffFileDumper() : XliffFileDumper
|
||||
{
|
||||
return $this->xliffFileDumper ??= $this->createMock(XliffFileDumper::class);
|
||||
}
|
||||
protected function getTranslatorBag() : TranslatorBagInterface
|
||||
{
|
||||
return $this->translatorBag ??= $this->createMock(TranslatorBagInterface::class);
|
||||
}
|
||||
}
|
49
dependencies/symfony/translation/TranslatableMessage.php
vendored
Normal file
49
dependencies/symfony/translation/TranslatableMessage.php
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
<?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;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Translation\TranslatableInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Translation\TranslatorInterface;
|
||||
/**
|
||||
* @author Nate Wiebe <nate@northern.co>
|
||||
*/
|
||||
class TranslatableMessage implements TranslatableInterface
|
||||
{
|
||||
private string $message;
|
||||
private array $parameters;
|
||||
private ?string $domain;
|
||||
public function __construct(string $message, array $parameters = [], string $domain = null)
|
||||
{
|
||||
$this->message = $message;
|
||||
$this->parameters = $parameters;
|
||||
$this->domain = $domain;
|
||||
}
|
||||
public function __toString() : string
|
||||
{
|
||||
return $this->getMessage();
|
||||
}
|
||||
public function getMessage() : string
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
public function getParameters() : array
|
||||
{
|
||||
return $this->parameters;
|
||||
}
|
||||
public function getDomain() : ?string
|
||||
{
|
||||
return $this->domain;
|
||||
}
|
||||
public function trans(TranslatorInterface $translator, string $locale = null) : string
|
||||
{
|
||||
return $translator->trans($this->getMessage(), \array_map(static fn($parameter) => $parameter instanceof TranslatableInterface ? $parameter->trans($translator, $locale) : $parameter, $this->getParameters()), $this->getDomain(), $locale);
|
||||
}
|
||||
}
|
384
dependencies/symfony/translation/Translator.php
vendored
Normal file
384
dependencies/symfony/translation/Translator.php
vendored
Normal file
@ -0,0 +1,384 @@
|
||||
<?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;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Config\ConfigCacheFactory;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Config\ConfigCacheFactoryInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Config\ConfigCacheInterface;
|
||||
use Symfony\Component\Translation\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Translation\Exception\NotFoundResourceException;
|
||||
use Symfony\Component\Translation\Exception\RuntimeException;
|
||||
use Symfony\Component\Translation\Formatter\IntlFormatterInterface;
|
||||
use Symfony\Component\Translation\Formatter\MessageFormatter;
|
||||
use Symfony\Component\Translation\Formatter\MessageFormatterInterface;
|
||||
use Symfony\Component\Translation\Loader\LoaderInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Translation\LocaleAwareInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Translation\TranslatableInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Translation\TranslatorInterface;
|
||||
// Help opcache.preload discover always-needed symbols
|
||||
\class_exists(\Symfony\Component\Translation\MessageCatalogue::class);
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Translator implements TranslatorInterface, \Symfony\Component\Translation\TranslatorBagInterface, LocaleAwareInterface
|
||||
{
|
||||
/**
|
||||
* @var MessageCatalogueInterface[]
|
||||
*/
|
||||
protected $catalogues = [];
|
||||
private string $locale;
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private array $fallbackLocales = [];
|
||||
/**
|
||||
* @var LoaderInterface[]
|
||||
*/
|
||||
private array $loaders = [];
|
||||
private array $resources = [];
|
||||
private MessageFormatterInterface $formatter;
|
||||
private ?string $cacheDir;
|
||||
private bool $debug;
|
||||
private array $cacheVary;
|
||||
private ?ConfigCacheFactoryInterface $configCacheFactory;
|
||||
private array $parentLocales;
|
||||
private bool $hasIntlFormatter;
|
||||
/**
|
||||
* @throws InvalidArgumentException If a locale contains invalid characters
|
||||
*/
|
||||
public function __construct(string $locale, MessageFormatterInterface $formatter = null, string $cacheDir = null, bool $debug = \false, array $cacheVary = [])
|
||||
{
|
||||
$this->setLocale($locale);
|
||||
$this->formatter = $formatter ??= new MessageFormatter();
|
||||
$this->cacheDir = $cacheDir;
|
||||
$this->debug = $debug;
|
||||
$this->cacheVary = $cacheVary;
|
||||
$this->hasIntlFormatter = $formatter instanceof IntlFormatterInterface;
|
||||
}
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function setConfigCacheFactory(ConfigCacheFactoryInterface $configCacheFactory)
|
||||
{
|
||||
$this->configCacheFactory = $configCacheFactory;
|
||||
}
|
||||
/**
|
||||
* Adds a Loader.
|
||||
*
|
||||
* @param string $format The name of the loader (@see addResource())
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addLoader(string $format, LoaderInterface $loader)
|
||||
{
|
||||
$this->loaders[$format] = $loader;
|
||||
}
|
||||
/**
|
||||
* Adds a Resource.
|
||||
*
|
||||
* @param string $format The name of the loader (@see addLoader())
|
||||
* @param mixed $resource The resource name
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws InvalidArgumentException If the locale contains invalid characters
|
||||
*/
|
||||
public function addResource(string $format, mixed $resource, string $locale, string $domain = null)
|
||||
{
|
||||
$domain ??= 'messages';
|
||||
$this->assertValidLocale($locale);
|
||||
$locale ?: ($locale = \class_exists(\Locale::class) ? \Locale::getDefault() : 'en');
|
||||
$this->resources[$locale][] = [$format, $resource, $domain];
|
||||
if (\in_array($locale, $this->fallbackLocales)) {
|
||||
$this->catalogues = [];
|
||||
} else {
|
||||
unset($this->catalogues[$locale]);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function setLocale(string $locale)
|
||||
{
|
||||
$this->assertValidLocale($locale);
|
||||
$this->locale = $locale;
|
||||
}
|
||||
public function getLocale() : string
|
||||
{
|
||||
return $this->locale ?: (\class_exists(\Locale::class) ? \Locale::getDefault() : 'en');
|
||||
}
|
||||
/**
|
||||
* Sets the fallback locales.
|
||||
*
|
||||
* @param string[] $locales
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws InvalidArgumentException If a locale contains invalid characters
|
||||
*/
|
||||
public function setFallbackLocales(array $locales)
|
||||
{
|
||||
// needed as the fallback locales are linked to the already loaded catalogues
|
||||
$this->catalogues = [];
|
||||
foreach ($locales as $locale) {
|
||||
$this->assertValidLocale($locale);
|
||||
}
|
||||
$this->fallbackLocales = $this->cacheVary['fallback_locales'] = $locales;
|
||||
}
|
||||
/**
|
||||
* Gets the fallback locales.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getFallbackLocales() : array
|
||||
{
|
||||
return $this->fallbackLocales;
|
||||
}
|
||||
public function trans(?string $id, array $parameters = [], string $domain = null, string $locale = null) : string
|
||||
{
|
||||
if (null === $id || '' === $id) {
|
||||
return '';
|
||||
}
|
||||
$domain ??= 'messages';
|
||||
$catalogue = $this->getCatalogue($locale);
|
||||
$locale = $catalogue->getLocale();
|
||||
while (!$catalogue->defines($id, $domain)) {
|
||||
if ($cat = $catalogue->getFallbackCatalogue()) {
|
||||
$catalogue = $cat;
|
||||
$locale = $catalogue->getLocale();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$parameters = \array_map(fn($parameter) => $parameter instanceof TranslatableInterface ? $parameter->trans($this, $locale) : $parameter, $parameters);
|
||||
$len = \strlen(\Symfony\Component\Translation\MessageCatalogue::INTL_DOMAIN_SUFFIX);
|
||||
if ($this->hasIntlFormatter && ($catalogue->defines($id, $domain . \Symfony\Component\Translation\MessageCatalogue::INTL_DOMAIN_SUFFIX) || \strlen($domain) > $len && 0 === \substr_compare($domain, \Symfony\Component\Translation\MessageCatalogue::INTL_DOMAIN_SUFFIX, -$len, $len))) {
|
||||
return $this->formatter->formatIntl($catalogue->get($id, $domain), $locale, $parameters);
|
||||
}
|
||||
return $this->formatter->format($catalogue->get($id, $domain), $locale, $parameters);
|
||||
}
|
||||
public function getCatalogue(string $locale = null) : \Symfony\Component\Translation\MessageCatalogueInterface
|
||||
{
|
||||
if (!$locale) {
|
||||
$locale = $this->getLocale();
|
||||
} else {
|
||||
$this->assertValidLocale($locale);
|
||||
}
|
||||
if (!isset($this->catalogues[$locale])) {
|
||||
$this->loadCatalogue($locale);
|
||||
}
|
||||
return $this->catalogues[$locale];
|
||||
}
|
||||
public function getCatalogues() : array
|
||||
{
|
||||
return \array_values($this->catalogues);
|
||||
}
|
||||
/**
|
||||
* Gets the loaders.
|
||||
*
|
||||
* @return LoaderInterface[]
|
||||
*/
|
||||
protected function getLoaders() : array
|
||||
{
|
||||
return $this->loaders;
|
||||
}
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function loadCatalogue(string $locale)
|
||||
{
|
||||
if (null === $this->cacheDir) {
|
||||
$this->initializeCatalogue($locale);
|
||||
} else {
|
||||
$this->initializeCacheCatalogue($locale);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function initializeCatalogue(string $locale)
|
||||
{
|
||||
$this->assertValidLocale($locale);
|
||||
try {
|
||||
$this->doLoadCatalogue($locale);
|
||||
} catch (NotFoundResourceException $e) {
|
||||
if (!$this->computeFallbackLocales($locale)) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
$this->loadFallbackCatalogues($locale);
|
||||
}
|
||||
private function initializeCacheCatalogue(string $locale) : void
|
||||
{
|
||||
if (isset($this->catalogues[$locale])) {
|
||||
/* Catalogue already initialized. */
|
||||
return;
|
||||
}
|
||||
$this->assertValidLocale($locale);
|
||||
$cache = $this->getConfigCacheFactory()->cache($this->getCatalogueCachePath($locale), function (ConfigCacheInterface $cache) use($locale) {
|
||||
$this->dumpCatalogue($locale, $cache);
|
||||
});
|
||||
if (isset($this->catalogues[$locale])) {
|
||||
/* Catalogue has been initialized as it was written out to cache. */
|
||||
return;
|
||||
}
|
||||
/* Read catalogue from cache. */
|
||||
$this->catalogues[$locale] = (include $cache->getPath());
|
||||
}
|
||||
private function dumpCatalogue(string $locale, ConfigCacheInterface $cache) : void
|
||||
{
|
||||
$this->initializeCatalogue($locale);
|
||||
$fallbackContent = $this->getFallbackContent($this->catalogues[$locale]);
|
||||
$content = \sprintf(<<<EOF
|
||||
<?php
|
||||
|
||||
use Symfony\\Component\\Translation\\MessageCatalogue;
|
||||
|
||||
\$catalogue = new MessageCatalogue('%s', %s);
|
||||
|
||||
%s
|
||||
return \$catalogue;
|
||||
|
||||
EOF
|
||||
, $locale, \var_export($this->getAllMessages($this->catalogues[$locale]), \true), $fallbackContent);
|
||||
$cache->write($content, $this->catalogues[$locale]->getResources());
|
||||
}
|
||||
private function getFallbackContent(\Symfony\Component\Translation\MessageCatalogue $catalogue) : string
|
||||
{
|
||||
$fallbackContent = '';
|
||||
$current = '';
|
||||
$replacementPattern = '/[^a-z0-9_]/i';
|
||||
$fallbackCatalogue = $catalogue->getFallbackCatalogue();
|
||||
while ($fallbackCatalogue) {
|
||||
$fallback = $fallbackCatalogue->getLocale();
|
||||
$fallbackSuffix = \ucfirst(\preg_replace($replacementPattern, '_', $fallback));
|
||||
$currentSuffix = \ucfirst(\preg_replace($replacementPattern, '_', $current));
|
||||
$fallbackContent .= \sprintf(<<<'EOF'
|
||||
$catalogue%s = new MessageCatalogue('%s', %s);
|
||||
$catalogue%s->addFallbackCatalogue($catalogue%s);
|
||||
|
||||
EOF
|
||||
, $fallbackSuffix, $fallback, \var_export($this->getAllMessages($fallbackCatalogue), \true), $currentSuffix, $fallbackSuffix);
|
||||
$current = $fallbackCatalogue->getLocale();
|
||||
$fallbackCatalogue = $fallbackCatalogue->getFallbackCatalogue();
|
||||
}
|
||||
return $fallbackContent;
|
||||
}
|
||||
private function getCatalogueCachePath(string $locale) : string
|
||||
{
|
||||
return $this->cacheDir . '/catalogue.' . $locale . '.' . \strtr(\substr(\base64_encode(\hash('sha256', \serialize($this->cacheVary), \true)), 0, 7), '/', '_') . '.php';
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected function doLoadCatalogue(string $locale) : void
|
||||
{
|
||||
$this->catalogues[$locale] = new \Symfony\Component\Translation\MessageCatalogue($locale);
|
||||
if (isset($this->resources[$locale])) {
|
||||
foreach ($this->resources[$locale] as $resource) {
|
||||
if (!isset($this->loaders[$resource[0]])) {
|
||||
if (\is_string($resource[1])) {
|
||||
throw new RuntimeException(\sprintf('No loader is registered for the "%s" format when loading the "%s" resource.', $resource[0], $resource[1]));
|
||||
}
|
||||
throw new RuntimeException(\sprintf('No loader is registered for the "%s" format.', $resource[0]));
|
||||
}
|
||||
$this->catalogues[$locale]->addCatalogue($this->loaders[$resource[0]]->load($resource[1], $locale, $resource[2]));
|
||||
}
|
||||
}
|
||||
}
|
||||
private function loadFallbackCatalogues(string $locale) : void
|
||||
{
|
||||
$current = $this->catalogues[$locale];
|
||||
foreach ($this->computeFallbackLocales($locale) as $fallback) {
|
||||
if (!isset($this->catalogues[$fallback])) {
|
||||
$this->initializeCatalogue($fallback);
|
||||
}
|
||||
$fallbackCatalogue = new \Symfony\Component\Translation\MessageCatalogue($fallback, $this->getAllMessages($this->catalogues[$fallback]));
|
||||
foreach ($this->catalogues[$fallback]->getResources() as $resource) {
|
||||
$fallbackCatalogue->addResource($resource);
|
||||
}
|
||||
$current->addFallbackCatalogue($fallbackCatalogue);
|
||||
$current = $fallbackCatalogue;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function computeFallbackLocales(string $locale)
|
||||
{
|
||||
$this->parentLocales ??= \json_decode(\file_get_contents(__DIR__ . '/Resources/data/parents.json'), \true);
|
||||
$originLocale = $locale;
|
||||
$locales = [];
|
||||
while ($locale) {
|
||||
$parent = $this->parentLocales[$locale] ?? null;
|
||||
if ($parent) {
|
||||
$locale = 'root' !== $parent ? $parent : null;
|
||||
} elseif (\function_exists('locale_parse')) {
|
||||
$localeSubTags = \locale_parse($locale);
|
||||
$locale = null;
|
||||
if (1 < \count($localeSubTags)) {
|
||||
\array_pop($localeSubTags);
|
||||
$locale = \locale_compose($localeSubTags) ?: null;
|
||||
}
|
||||
} elseif ($i = \strrpos($locale, '_') ?: \strrpos($locale, '-')) {
|
||||
$locale = \substr($locale, 0, $i);
|
||||
} else {
|
||||
$locale = null;
|
||||
}
|
||||
if (null !== $locale) {
|
||||
$locales[] = $locale;
|
||||
}
|
||||
}
|
||||
foreach ($this->fallbackLocales as $fallback) {
|
||||
if ($fallback === $originLocale) {
|
||||
continue;
|
||||
}
|
||||
$locales[] = $fallback;
|
||||
}
|
||||
return \array_unique($locales);
|
||||
}
|
||||
/**
|
||||
* Asserts that the locale is valid, throws an Exception if not.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws InvalidArgumentException If the locale contains invalid characters
|
||||
*/
|
||||
protected function assertValidLocale(string $locale)
|
||||
{
|
||||
if (!\preg_match('/^[a-z0-9@_\\.\\-]*$/i', $locale)) {
|
||||
throw new InvalidArgumentException(\sprintf('Invalid "%s" locale.', $locale));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Provides the ConfigCache factory implementation, falling back to a
|
||||
* default implementation if necessary.
|
||||
*/
|
||||
private function getConfigCacheFactory() : ConfigCacheFactoryInterface
|
||||
{
|
||||
$this->configCacheFactory ??= new ConfigCacheFactory($this->debug);
|
||||
return $this->configCacheFactory;
|
||||
}
|
||||
private function getAllMessages(\Symfony\Component\Translation\MessageCatalogueInterface $catalogue) : array
|
||||
{
|
||||
$allMessages = [];
|
||||
foreach ($catalogue->all() as $domain => $messages) {
|
||||
if ($intlMessages = $catalogue->all($domain . \Symfony\Component\Translation\MessageCatalogue::INTL_DOMAIN_SUFFIX)) {
|
||||
$allMessages[$domain . \Symfony\Component\Translation\MessageCatalogue::INTL_DOMAIN_SUFFIX] = $intlMessages;
|
||||
$messages = \array_diff_key($messages, $intlMessages);
|
||||
}
|
||||
if ($messages) {
|
||||
$allMessages[$domain] = $messages;
|
||||
}
|
||||
}
|
||||
return $allMessages;
|
||||
}
|
||||
}
|
78
dependencies/symfony/translation/TranslatorBag.php
vendored
Normal file
78
dependencies/symfony/translation/TranslatorBag.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;
|
||||
|
||||
use Symfony\Component\Translation\Catalogue\AbstractOperation;
|
||||
use Symfony\Component\Translation\Catalogue\TargetOperation;
|
||||
final class TranslatorBag implements \Symfony\Component\Translation\TranslatorBagInterface
|
||||
{
|
||||
/** @var MessageCatalogue[] */
|
||||
private array $catalogues = [];
|
||||
public function addCatalogue(\Symfony\Component\Translation\MessageCatalogue $catalogue) : void
|
||||
{
|
||||
if (null !== ($existingCatalogue = $this->getCatalogue($catalogue->getLocale()))) {
|
||||
$catalogue->addCatalogue($existingCatalogue);
|
||||
}
|
||||
$this->catalogues[$catalogue->getLocale()] = $catalogue;
|
||||
}
|
||||
public function addBag(\Symfony\Component\Translation\TranslatorBagInterface $bag) : void
|
||||
{
|
||||
foreach ($bag->getCatalogues() as $catalogue) {
|
||||
$this->addCatalogue($catalogue);
|
||||
}
|
||||
}
|
||||
public function getCatalogue(string $locale = null) : \Symfony\Component\Translation\MessageCatalogueInterface
|
||||
{
|
||||
if (null === $locale || !isset($this->catalogues[$locale])) {
|
||||
$this->catalogues[$locale] = new \Symfony\Component\Translation\MessageCatalogue($locale);
|
||||
}
|
||||
return $this->catalogues[$locale];
|
||||
}
|
||||
public function getCatalogues() : array
|
||||
{
|
||||
return \array_values($this->catalogues);
|
||||
}
|
||||
public function diff(\Symfony\Component\Translation\TranslatorBagInterface $diffBag) : self
|
||||
{
|
||||
$diff = new self();
|
||||
foreach ($this->catalogues as $locale => $catalogue) {
|
||||
if (null === ($diffCatalogue = $diffBag->getCatalogue($locale))) {
|
||||
$diff->addCatalogue($catalogue);
|
||||
continue;
|
||||
}
|
||||
$operation = new TargetOperation($diffCatalogue, $catalogue);
|
||||
$operation->moveMessagesToIntlDomainsIfPossible(AbstractOperation::NEW_BATCH);
|
||||
$newCatalogue = new \Symfony\Component\Translation\MessageCatalogue($locale);
|
||||
foreach ($catalogue->getDomains() as $domain) {
|
||||
$newCatalogue->add($operation->getNewMessages($domain), $domain);
|
||||
}
|
||||
$diff->addCatalogue($newCatalogue);
|
||||
}
|
||||
return $diff;
|
||||
}
|
||||
public function intersect(\Symfony\Component\Translation\TranslatorBagInterface $intersectBag) : self
|
||||
{
|
||||
$diff = new self();
|
||||
foreach ($this->catalogues as $locale => $catalogue) {
|
||||
if (null === ($intersectCatalogue = $intersectBag->getCatalogue($locale))) {
|
||||
continue;
|
||||
}
|
||||
$operation = new TargetOperation($catalogue, $intersectCatalogue);
|
||||
$operation->moveMessagesToIntlDomainsIfPossible(AbstractOperation::OBSOLETE_BATCH);
|
||||
$obsoleteCatalogue = new \Symfony\Component\Translation\MessageCatalogue($locale);
|
||||
foreach ($operation->getDomains() as $domain) {
|
||||
$obsoleteCatalogue->add(\array_diff($operation->getMessages($domain), $operation->getNewMessages($domain)), $domain);
|
||||
}
|
||||
$diff->addCatalogue($obsoleteCatalogue);
|
||||
}
|
||||
return $diff;
|
||||
}
|
||||
}
|
33
dependencies/symfony/translation/TranslatorBagInterface.php
vendored
Normal file
33
dependencies/symfony/translation/TranslatorBagInterface.php
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
<?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;
|
||||
|
||||
use Symfony\Component\Translation\Exception\InvalidArgumentException;
|
||||
/**
|
||||
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
|
||||
*/
|
||||
interface TranslatorBagInterface
|
||||
{
|
||||
/**
|
||||
* Gets the catalogue by locale.
|
||||
*
|
||||
* @param string|null $locale The locale or null to use the default
|
||||
*
|
||||
* @throws InvalidArgumentException If the locale contains invalid characters
|
||||
*/
|
||||
public function getCatalogue(string $locale = null) : \Symfony\Component\Translation\MessageCatalogueInterface;
|
||||
/**
|
||||
* Returns all catalogues of the instance.
|
||||
*
|
||||
* @return MessageCatalogueInterface[]
|
||||
*/
|
||||
public function getCatalogues() : array;
|
||||
}
|
86
dependencies/symfony/translation/Util/ArrayConverter.php
vendored
Normal file
86
dependencies/symfony/translation/Util/ArrayConverter.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\Util;
|
||||
|
||||
/**
|
||||
* ArrayConverter generates tree like structure from a message catalogue.
|
||||
* e.g. this
|
||||
* 'foo.bar1' => 'test1',
|
||||
* 'foo.bar2' => 'test2'
|
||||
* converts to follows:
|
||||
* foo:
|
||||
* bar1: test1
|
||||
* bar2: test2.
|
||||
*
|
||||
* @author Gennady Telegin <gtelegin@gmail.com>
|
||||
*/
|
||||
class ArrayConverter
|
||||
{
|
||||
/**
|
||||
* Converts linear messages array to tree-like array.
|
||||
* For example this array('foo.bar' => 'value') will be converted to ['foo' => ['bar' => 'value']].
|
||||
*
|
||||
* @param array $messages Linear messages array
|
||||
*/
|
||||
public static function expandToTree(array $messages) : array
|
||||
{
|
||||
$tree = [];
|
||||
foreach ($messages as $id => $value) {
|
||||
$referenceToElement =& self::getElementByPath($tree, \explode('.', $id));
|
||||
$referenceToElement = $value;
|
||||
unset($referenceToElement);
|
||||
}
|
||||
return $tree;
|
||||
}
|
||||
private static function &getElementByPath(array &$tree, array $parts) : mixed
|
||||
{
|
||||
$elem =& $tree;
|
||||
$parentOfElem = null;
|
||||
foreach ($parts as $i => $part) {
|
||||
if (isset($elem[$part]) && \is_string($elem[$part])) {
|
||||
/* Process next case:
|
||||
* 'foo': 'test1',
|
||||
* 'foo.bar': 'test2'
|
||||
*
|
||||
* $tree['foo'] was string before we found array {bar: test2}.
|
||||
* Treat new element as string too, e.g. add $tree['foo.bar'] = 'test2';
|
||||
*/
|
||||
$elem =& $elem[\implode('.', \array_slice($parts, $i))];
|
||||
break;
|
||||
}
|
||||
$parentOfElem =& $elem;
|
||||
$elem =& $elem[$part];
|
||||
}
|
||||
if ($elem && \is_array($elem) && $parentOfElem) {
|
||||
/* Process next case:
|
||||
* 'foo.bar': 'test1'
|
||||
* 'foo': 'test2'
|
||||
*
|
||||
* $tree['foo'] was array = {bar: 'test1'} before we found string constant `foo`.
|
||||
* Cancel treating $tree['foo'] as array and cancel back it expansion,
|
||||
* e.g. make it $tree['foo.bar'] = 'test1' again.
|
||||
*/
|
||||
self::cancelExpand($parentOfElem, $part, $elem);
|
||||
}
|
||||
return $elem;
|
||||
}
|
||||
private static function cancelExpand(array &$tree, string $prefix, array $node) : void
|
||||
{
|
||||
$prefix .= '.';
|
||||
foreach ($node as $id => $value) {
|
||||
if (\is_string($value)) {
|
||||
$tree[$prefix . $id] = $value;
|
||||
} else {
|
||||
self::cancelExpand($tree, $prefix . $id, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
154
dependencies/symfony/translation/Util/XliffUtils.php
vendored
Normal file
154
dependencies/symfony/translation/Util/XliffUtils.php
vendored
Normal file
@ -0,0 +1,154 @@
|
||||
<?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\Util;
|
||||
|
||||
use Symfony\Component\Translation\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Translation\Exception\InvalidResourceException;
|
||||
/**
|
||||
* Provides some utility methods for XLIFF translation files, such as validating
|
||||
* their contents according to the XSD schema.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class XliffUtils
|
||||
{
|
||||
/**
|
||||
* Gets xliff file version based on the root "version" attribute.
|
||||
*
|
||||
* Defaults to 1.2 for backwards compatibility.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function getVersionNumber(\DOMDocument $dom) : string
|
||||
{
|
||||
/** @var \DOMNode $xliff */
|
||||
foreach ($dom->getElementsByTagName('xliff') as $xliff) {
|
||||
$version = $xliff->attributes->getNamedItem('version');
|
||||
if ($version) {
|
||||
return $version->nodeValue;
|
||||
}
|
||||
$namespace = $xliff->attributes->getNamedItem('xmlns');
|
||||
if ($namespace) {
|
||||
if (0 !== \substr_compare('urn:oasis:names:tc:xliff:document:', $namespace->nodeValue, 0, 34)) {
|
||||
throw new InvalidArgumentException(\sprintf('Not a valid XLIFF namespace "%s".', $namespace));
|
||||
}
|
||||
return \substr($namespace, 34);
|
||||
}
|
||||
}
|
||||
// Falls back to v1.2
|
||||
return '1.2';
|
||||
}
|
||||
/**
|
||||
* Validates and parses the given file into a DOMDocument.
|
||||
*
|
||||
* @throws InvalidResourceException
|
||||
*/
|
||||
public static function validateSchema(\DOMDocument $dom) : array
|
||||
{
|
||||
$xliffVersion = static::getVersionNumber($dom);
|
||||
$internalErrors = \libxml_use_internal_errors(\true);
|
||||
if ($shouldEnable = self::shouldEnableEntityLoader()) {
|
||||
$disableEntities = \libxml_disable_entity_loader(\false);
|
||||
}
|
||||
try {
|
||||
$isValid = @$dom->schemaValidateSource(self::getSchema($xliffVersion));
|
||||
if (!$isValid) {
|
||||
return self::getXmlErrors($internalErrors);
|
||||
}
|
||||
} finally {
|
||||
if ($shouldEnable) {
|
||||
\libxml_disable_entity_loader($disableEntities);
|
||||
}
|
||||
}
|
||||
$dom->normalizeDocument();
|
||||
\libxml_clear_errors();
|
||||
\libxml_use_internal_errors($internalErrors);
|
||||
return [];
|
||||
}
|
||||
private static function shouldEnableEntityLoader() : bool
|
||||
{
|
||||
static $dom, $schema;
|
||||
if (null === $dom) {
|
||||
$dom = new \DOMDocument();
|
||||
$dom->loadXML('<?xml version="1.0"?><test/>');
|
||||
$tmpfile = \tempnam(\sys_get_temp_dir(), 'symfony');
|
||||
\register_shutdown_function(static function () use($tmpfile) {
|
||||
@\unlink($tmpfile);
|
||||
});
|
||||
$schema = '<?xml version="1.0" encoding="utf-8"?>
|
||||
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||
<xsd:include schemaLocation="file:///' . \str_replace('\\', '/', $tmpfile) . '" />
|
||||
</xsd:schema>';
|
||||
\file_put_contents($tmpfile, '<?xml version="1.0" encoding="utf-8"?>
|
||||
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||
<xsd:element name="test" type="testType" />
|
||||
<xsd:complexType name="testType"/>
|
||||
</xsd:schema>');
|
||||
}
|
||||
return !@$dom->schemaValidateSource($schema);
|
||||
}
|
||||
public static function getErrorsAsString(array $xmlErrors) : string
|
||||
{
|
||||
$errorsAsString = '';
|
||||
foreach ($xmlErrors as $error) {
|
||||
$errorsAsString .= \sprintf("[%s %s] %s (in %s - line %d, column %d)\n", \LIBXML_ERR_WARNING === $error['level'] ? 'WARNING' : 'ERROR', $error['code'], $error['message'], $error['file'], $error['line'], $error['column']);
|
||||
}
|
||||
return $errorsAsString;
|
||||
}
|
||||
private static function getSchema(string $xliffVersion) : string
|
||||
{
|
||||
if ('1.2' === $xliffVersion) {
|
||||
$schemaSource = \file_get_contents(__DIR__ . '/../Resources/schemas/xliff-core-1.2-transitional.xsd');
|
||||
$xmlUri = 'http://www.w3.org/2001/xml.xsd';
|
||||
} elseif ('2.0' === $xliffVersion) {
|
||||
$schemaSource = \file_get_contents(__DIR__ . '/../Resources/schemas/xliff-core-2.0.xsd');
|
||||
$xmlUri = 'informativeCopiesOf3rdPartySchemas/w3c/xml.xsd';
|
||||
} else {
|
||||
throw new InvalidArgumentException(\sprintf('No support implemented for loading XLIFF version "%s".', $xliffVersion));
|
||||
}
|
||||
return self::fixXmlLocation($schemaSource, $xmlUri);
|
||||
}
|
||||
/**
|
||||
* Internally changes the URI of a dependent xsd to be loaded locally.
|
||||
*/
|
||||
private static function fixXmlLocation(string $schemaSource, string $xmlUri) : string
|
||||
{
|
||||
$newPath = \str_replace('\\', '/', __DIR__) . '/../Resources/schemas/xml.xsd';
|
||||
$parts = \explode('/', $newPath);
|
||||
$locationstart = 'file:///';
|
||||
if (0 === \stripos($newPath, 'phar://')) {
|
||||
$tmpfile = \tempnam(\sys_get_temp_dir(), 'symfony');
|
||||
if ($tmpfile) {
|
||||
\copy($newPath, $tmpfile);
|
||||
$parts = \explode('/', \str_replace('\\', '/', $tmpfile));
|
||||
} else {
|
||||
\array_shift($parts);
|
||||
$locationstart = 'phar:///';
|
||||
}
|
||||
}
|
||||
$drive = '\\' === \DIRECTORY_SEPARATOR ? \array_shift($parts) . '/' : '';
|
||||
$newPath = $locationstart . $drive . \implode('/', \array_map('rawurlencode', $parts));
|
||||
return \str_replace($xmlUri, $newPath, $schemaSource);
|
||||
}
|
||||
/**
|
||||
* Returns the XML errors of the internal XML parser.
|
||||
*/
|
||||
private static function getXmlErrors(bool $internalErrors) : array
|
||||
{
|
||||
$errors = [];
|
||||
foreach (\libxml_get_errors() as $error) {
|
||||
$errors[] = ['level' => \LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR', 'code' => $error->code, 'message' => \trim($error->message), 'file' => $error->file ?: 'n/a', 'line' => $error->line, 'column' => $error->column];
|
||||
}
|
||||
\libxml_clear_errors();
|
||||
\libxml_use_internal_errors($internalErrors);
|
||||
return $errors;
|
||||
}
|
||||
}
|
67
dependencies/symfony/translation/Writer/TranslationWriter.php
vendored
Normal file
67
dependencies/symfony/translation/Writer/TranslationWriter.php
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
<?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\Writer;
|
||||
|
||||
use Symfony\Component\Translation\Dumper\DumperInterface;
|
||||
use Symfony\Component\Translation\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Translation\Exception\RuntimeException;
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
/**
|
||||
* TranslationWriter writes translation messages.
|
||||
*
|
||||
* @author Michel Salib <michelsalib@hotmail.com>
|
||||
*/
|
||||
class TranslationWriter implements \Symfony\Component\Translation\Writer\TranslationWriterInterface
|
||||
{
|
||||
/**
|
||||
* @var array<string, DumperInterface>
|
||||
*/
|
||||
private array $dumpers = [];
|
||||
/**
|
||||
* Adds a dumper to the writer.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addDumper(string $format, DumperInterface $dumper)
|
||||
{
|
||||
$this->dumpers[$format] = $dumper;
|
||||
}
|
||||
/**
|
||||
* Obtains the list of supported formats.
|
||||
*/
|
||||
public function getFormats() : array
|
||||
{
|
||||
return \array_keys($this->dumpers);
|
||||
}
|
||||
/**
|
||||
* Writes translation from the catalogue according to the selected format.
|
||||
*
|
||||
* @param string $format The format to use to dump the messages
|
||||
* @param array $options Options that are passed to the dumper
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function write(MessageCatalogue $catalogue, string $format, array $options = [])
|
||||
{
|
||||
if (!isset($this->dumpers[$format])) {
|
||||
throw new InvalidArgumentException(\sprintf('There is no dumper associated with format "%s".', $format));
|
||||
}
|
||||
// get the right dumper
|
||||
$dumper = $this->dumpers[$format];
|
||||
if (isset($options['path']) && !\is_dir($options['path']) && !@\mkdir($options['path'], 0777, \true) && !\is_dir($options['path'])) {
|
||||
throw new RuntimeException(\sprintf('Translation Writer was not able to create directory "%s".', $options['path']));
|
||||
}
|
||||
// save
|
||||
$dumper->dump($catalogue, $options);
|
||||
}
|
||||
}
|
33
dependencies/symfony/translation/Writer/TranslationWriterInterface.php
vendored
Normal file
33
dependencies/symfony/translation/Writer/TranslationWriterInterface.php
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
<?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\Writer;
|
||||
|
||||
use Symfony\Component\Translation\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
/**
|
||||
* TranslationWriter writes translation messages.
|
||||
*
|
||||
* @author Michel Salib <michelsalib@hotmail.com>
|
||||
*/
|
||||
interface TranslationWriterInterface
|
||||
{
|
||||
/**
|
||||
* Writes translation from the catalogue according to the selected format.
|
||||
*
|
||||
* @param string $format The format to use to dump the messages
|
||||
* @param array $options Options that are passed to the dumper
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function write(MessageCatalogue $catalogue, string $format, array $options = []);
|
||||
}
|
Reference in New Issue
Block a user