init
This commit is contained in:
+91
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Exception\InvalidArgumentException;
|
||||
use MailPoetVendor\Symfony\Contracts\Translation\LocaleAwareInterface;
|
||||
use MailPoetVendor\Symfony\Contracts\Translation\TranslatorInterface;
|
||||
class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInterface, LocaleAwareInterface, WarmableInterface
|
||||
{
|
||||
public const MESSAGE_DEFINED = 0;
|
||||
public const MESSAGE_MISSING = 1;
|
||||
public const MESSAGE_EQUALS_FALLBACK = 2;
|
||||
private $translator;
|
||||
private $messages = [];
|
||||
public function __construct(TranslatorInterface $translator)
|
||||
{
|
||||
if (!$translator instanceof 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)
|
||||
{
|
||||
$trans = $this->translator->trans($id = (string) $id, $parameters, $domain, $locale);
|
||||
$this->collectMessage($locale, $domain, $id, $trans, $parameters);
|
||||
return $trans;
|
||||
}
|
||||
public function setLocale(string $locale)
|
||||
{
|
||||
$this->translator->setLocale($locale);
|
||||
}
|
||||
public function getLocale()
|
||||
{
|
||||
return $this->translator->getLocale();
|
||||
}
|
||||
public function getCatalogue(?string $locale = null)
|
||||
{
|
||||
return $this->translator->getCatalogue($locale);
|
||||
}
|
||||
public function getCatalogues() : array
|
||||
{
|
||||
return $this->translator->getCatalogues();
|
||||
}
|
||||
public function warmUp(string $cacheDir)
|
||||
{
|
||||
if ($this->translator instanceof WarmableInterface) {
|
||||
return (array) $this->translator->warmUp($cacheDir);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
public function getFallbackLocales()
|
||||
{
|
||||
if ($this->translator instanceof Translator || \method_exists($this->translator, 'getFallbackLocales')) {
|
||||
return $this->translator->getFallbackLocales();
|
||||
}
|
||||
return [];
|
||||
}
|
||||
public function __call(string $method, array $args)
|
||||
{
|
||||
return $this->translator->{$method}(...$args);
|
||||
}
|
||||
public function getCollectedMessages()
|
||||
{
|
||||
return $this->messages;
|
||||
}
|
||||
private function collectMessage(?string $locale, ?string $domain, string $id, string $translation, ?array $parameters = [])
|
||||
{
|
||||
if (null === $domain) {
|
||||
$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];
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation\Exception;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
interface ExceptionInterface extends \Throwable
|
||||
{
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation\Exception;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
class IncompleteDsnException extends 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);
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation\Exception;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation\Exception;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
class InvalidResourceException extends \InvalidArgumentException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation\Exception;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
class LogicException extends \LogicException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation\Exception;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
class MissingRequiredOptionException extends 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);
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation\Exception;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
class NotFoundResourceException extends \InvalidArgumentException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation\Exception;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
class ProviderException extends RuntimeException implements ProviderExceptionInterface
|
||||
{
|
||||
private $response;
|
||||
private $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;
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation\Exception;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
interface ProviderExceptionInterface extends ExceptionInterface
|
||||
{
|
||||
public function getDebug() : string;
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation\Exception;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
class RuntimeException extends \RuntimeException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation\Exception;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Bridge;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Provider\Dsn;
|
||||
class UnsupportedSchemeException extends 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 . '.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<?php
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Symfony\Contracts\Translation\LocaleAwareInterface;
|
||||
use MailPoetVendor\Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use MailPoetVendor\Symfony\Contracts\Translation\TranslatorTrait;
|
||||
class IdentityTranslator implements TranslatorInterface, LocaleAwareInterface
|
||||
{
|
||||
use TranslatorTrait;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Psr\Log\LoggerInterface;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Exception\InvalidArgumentException;
|
||||
use MailPoetVendor\Symfony\Contracts\Translation\LocaleAwareInterface;
|
||||
use MailPoetVendor\Symfony\Contracts\Translation\TranslatorInterface;
|
||||
class LoggingTranslator implements TranslatorInterface, TranslatorBagInterface, LocaleAwareInterface
|
||||
{
|
||||
private $translator;
|
||||
private $logger;
|
||||
public function __construct(TranslatorInterface $translator, LoggerInterface $logger)
|
||||
{
|
||||
if (!$translator instanceof 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)
|
||||
{
|
||||
$trans = $this->translator->trans($id = (string) $id, $parameters, $domain, $locale);
|
||||
$this->log($id, $domain, $locale);
|
||||
return $trans;
|
||||
}
|
||||
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()
|
||||
{
|
||||
return $this->translator->getLocale();
|
||||
}
|
||||
public function getCatalogue(?string $locale = null)
|
||||
{
|
||||
return $this->translator->getCatalogue($locale);
|
||||
}
|
||||
public function getCatalogues() : array
|
||||
{
|
||||
return $this->translator->getCatalogues();
|
||||
}
|
||||
public function getFallbackLocales()
|
||||
{
|
||||
if ($this->translator instanceof Translator || \method_exists($this->translator, 'getFallbackLocales')) {
|
||||
return $this->translator->getFallbackLocales();
|
||||
}
|
||||
return [];
|
||||
}
|
||||
public function __call(string $method, array $args)
|
||||
{
|
||||
return $this->translator->{$method}(...$args);
|
||||
}
|
||||
private function log(string $id, ?string $domain, ?string $locale)
|
||||
{
|
||||
if (null === $domain) {
|
||||
$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()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Symfony\Component\Config\Resource\ResourceInterface;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Exception\LogicException;
|
||||
class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterface
|
||||
{
|
||||
private $messages = [];
|
||||
private $metadata = [];
|
||||
private $resources = [];
|
||||
private $locale;
|
||||
private $fallbackCatalogue;
|
||||
private $parent;
|
||||
public function __construct(string $locale, array $messages = [])
|
||||
{
|
||||
$this->locale = $locale;
|
||||
$this->messages = $messages;
|
||||
}
|
||||
public function getLocale()
|
||||
{
|
||||
return $this->locale;
|
||||
}
|
||||
public function getDomains()
|
||||
{
|
||||
$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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
public function set(string $id, string $translation, string $domain = 'messages')
|
||||
{
|
||||
$this->add([$id => $translation], $domain);
|
||||
}
|
||||
public function has(string $id, string $domain = 'messages')
|
||||
{
|
||||
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')
|
||||
{
|
||||
return isset($this->messages[$domain][$id]) || isset($this->messages[$domain . self::INTL_DOMAIN_SUFFIX][$id]);
|
||||
}
|
||||
public function get(string $id, string $domain = 'messages')
|
||||
{
|
||||
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;
|
||||
}
|
||||
public function replace(array $messages, string $domain = 'messages')
|
||||
{
|
||||
unset($this->messages[$domain], $this->messages[$domain . self::INTL_DOMAIN_SUFFIX]);
|
||||
$this->add($messages, $domain);
|
||||
}
|
||||
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]);
|
||||
}
|
||||
}
|
||||
public function addCatalogue(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 MetadataAwareInterface) {
|
||||
$metadata = $catalogue->getMetadata('', '');
|
||||
$this->addMetadata($metadata);
|
||||
}
|
||||
}
|
||||
public function addFallbackCatalogue(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()
|
||||
{
|
||||
return $this->fallbackCatalogue;
|
||||
}
|
||||
public function getResources()
|
||||
{
|
||||
return \array_values($this->resources);
|
||||
}
|
||||
public function addResource(ResourceInterface $resource)
|
||||
{
|
||||
$this->resources[$resource->__toString()] = $resource;
|
||||
}
|
||||
public function getMetadata(string $key = '', string $domain = 'messages')
|
||||
{
|
||||
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;
|
||||
}
|
||||
public function setMetadata(string $key, $value, string $domain = 'messages')
|
||||
{
|
||||
$this->metadata[$domain][$key] = $value;
|
||||
}
|
||||
public function deleteMetadata(string $key = '', string $domain = 'messages')
|
||||
{
|
||||
if ('' == $domain) {
|
||||
$this->metadata = [];
|
||||
} elseif ('' == $key) {
|
||||
unset($this->metadata[$domain]);
|
||||
} else {
|
||||
unset($this->metadata[$domain][$key]);
|
||||
}
|
||||
}
|
||||
private function addMetadata(array $values)
|
||||
{
|
||||
foreach ($values as $domain => $keys) {
|
||||
foreach ($keys as $key => $value) {
|
||||
$this->setMetadata($key, $value, $domain);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Symfony\Component\Config\Resource\ResourceInterface;
|
||||
interface MessageCatalogueInterface
|
||||
{
|
||||
public const INTL_DOMAIN_SUFFIX = '+intl-icu';
|
||||
public function getLocale();
|
||||
public function getDomains();
|
||||
public function all(?string $domain = null);
|
||||
public function set(string $id, string $translation, string $domain = 'messages');
|
||||
public function has(string $id, string $domain = 'messages');
|
||||
public function defines(string $id, string $domain = 'messages');
|
||||
public function get(string $id, string $domain = 'messages');
|
||||
public function replace(array $messages, string $domain = 'messages');
|
||||
public function add(array $messages, string $domain = 'messages');
|
||||
public function addCatalogue(self $catalogue);
|
||||
public function addFallbackCatalogue(self $catalogue);
|
||||
public function getFallbackCatalogue();
|
||||
public function getResources();
|
||||
public function addResource(ResourceInterface $resource);
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
interface MetadataAwareInterface
|
||||
{
|
||||
public function getMetadata(string $key = '', string $domain = 'messages');
|
||||
public function setMetadata(string $key, $value, string $domain = 'messages');
|
||||
public function deleteMetadata(string $key = '', string $domain = 'messages');
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation\Provider;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Exception\IncompleteDsnException;
|
||||
abstract class AbstractProviderFactory implements ProviderFactoryInterface
|
||||
{
|
||||
public function supports(Dsn $dsn) : bool
|
||||
{
|
||||
return \in_array($dsn->getScheme(), $this->getSupportedSchemes(), \true);
|
||||
}
|
||||
protected abstract function getSupportedSchemes() : array;
|
||||
protected function getUser(Dsn $dsn) : string
|
||||
{
|
||||
if (null === ($user = $dsn->getUser())) {
|
||||
throw new IncompleteDsnException('User is not set.', $dsn->getScheme() . '://' . $dsn->getHost());
|
||||
}
|
||||
return $user;
|
||||
}
|
||||
protected function getPassword(Dsn $dsn) : string
|
||||
{
|
||||
if (null === ($password = $dsn->getPassword())) {
|
||||
throw new IncompleteDsnException('Password is not set.', $dsn->getOriginalDsn());
|
||||
}
|
||||
return $password;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation\Provider;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Exception\InvalidArgumentException;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Exception\MissingRequiredOptionException;
|
||||
final class Dsn
|
||||
{
|
||||
private $scheme;
|
||||
private $host;
|
||||
private $user;
|
||||
private $password;
|
||||
private $port;
|
||||
private $path;
|
||||
private $options;
|
||||
private $originalDsn;
|
||||
public function __construct(string $dsn)
|
||||
{
|
||||
$this->originalDsn = $dsn;
|
||||
if (\false === ($params = \parse_url($dsn))) {
|
||||
throw new InvalidArgumentException('The translation provider DSN is invalid.');
|
||||
}
|
||||
if (!isset($params['scheme'])) {
|
||||
throw new InvalidArgumentException('The translation provider DSN must contain a scheme.');
|
||||
}
|
||||
$this->scheme = $params['scheme'];
|
||||
if (!isset($params['host'])) {
|
||||
throw new InvalidArgumentException('The translation provider DSN must contain a host (use "default" by default).');
|
||||
}
|
||||
$this->host = $params['host'];
|
||||
$this->user = '' !== ($params['user'] ?? '') ? \rawurldecode($params['user']) : null;
|
||||
$this->password = '' !== ($params['pass'] ?? '') ? \rawurldecode($params['pass']) : null;
|
||||
$this->port = $params['port'] ?? null;
|
||||
$this->path = $params['path'] ?? null;
|
||||
\parse_str($params['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, $default = null)
|
||||
{
|
||||
return $this->options[$key] ?? $default;
|
||||
}
|
||||
public function getRequiredOption(string $key)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation\Provider;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Symfony\Component\Translation\TranslatorBag;
|
||||
use MailPoetVendor\Symfony\Component\Translation\TranslatorBagInterface;
|
||||
class FilteringProvider implements ProviderInterface
|
||||
{
|
||||
private $provider;
|
||||
private $locales;
|
||||
private $domains;
|
||||
public function __construct(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;
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation\Provider;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Symfony\Component\Translation\TranslatorBag;
|
||||
use MailPoetVendor\Symfony\Component\Translation\TranslatorBagInterface;
|
||||
class NullProvider implements 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
|
||||
{
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation\Provider;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Exception\UnsupportedSchemeException;
|
||||
final class NullProviderFactory extends AbstractProviderFactory
|
||||
{
|
||||
public function create(Dsn $dsn) : ProviderInterface
|
||||
{
|
||||
if ('null' === $dsn->getScheme()) {
|
||||
return new NullProvider();
|
||||
}
|
||||
throw new UnsupportedSchemeException($dsn, 'null', $this->getSupportedSchemes());
|
||||
}
|
||||
protected function getSupportedSchemes() : array
|
||||
{
|
||||
return ['null'];
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation\Provider;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Exception\IncompleteDsnException;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Exception\UnsupportedSchemeException;
|
||||
interface ProviderFactoryInterface
|
||||
{
|
||||
public function create(Dsn $dsn) : ProviderInterface;
|
||||
public function supports(Dsn $dsn) : bool;
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation\Provider;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Symfony\Component\Translation\TranslatorBag;
|
||||
use MailPoetVendor\Symfony\Component\Translation\TranslatorBagInterface;
|
||||
interface ProviderInterface
|
||||
{
|
||||
public function __toString() : string;
|
||||
public function write(TranslatorBagInterface $translatorBag) : void;
|
||||
public function read(array $domains, array $locales) : TranslatorBag;
|
||||
public function delete(TranslatorBagInterface $translatorBag) : void;
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation\Provider;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Exception\InvalidArgumentException;
|
||||
final class TranslationProviderCollection
|
||||
{
|
||||
private $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) : 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);
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation\Provider;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Exception\UnsupportedSchemeException;
|
||||
class TranslationProviderCollectionFactory
|
||||
{
|
||||
private $factories;
|
||||
private $enabledLocales;
|
||||
public function __construct(iterable $factories, array $enabledLocales)
|
||||
{
|
||||
$this->factories = $factories;
|
||||
$this->enabledLocales = $enabledLocales;
|
||||
}
|
||||
public function fromConfig(array $config) : TranslationProviderCollection
|
||||
{
|
||||
$providers = [];
|
||||
foreach ($config as $name => $currentConfig) {
|
||||
$providers[$name] = $this->fromDsnObject(new Dsn($currentConfig['dsn']), !$currentConfig['locales'] ? $this->enabledLocales : $currentConfig['locales'], !$currentConfig['domains'] ? [] : $currentConfig['domains']);
|
||||
}
|
||||
return new TranslationProviderCollection($providers);
|
||||
}
|
||||
public function fromDsnObject(Dsn $dsn, array $locales, array $domains = []) : ProviderInterface
|
||||
{
|
||||
foreach ($this->factories as $factory) {
|
||||
if ($factory->supports($dsn)) {
|
||||
return new FilteringProvider($factory->create($dsn), $locales, $domains);
|
||||
}
|
||||
}
|
||||
throw new UnsupportedSchemeException($dsn);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<?php
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Symfony\Contracts\Translation\TranslatorInterface;
|
||||
final class PseudoLocalizationTranslator implements TranslatorInterface
|
||||
{
|
||||
private const EXPANSION_CHARACTER = '~';
|
||||
private $translator;
|
||||
private $accents;
|
||||
private $expansionFactor;
|
||||
private $brackets;
|
||||
private $parseHTML;
|
||||
private $localizableHTMLAttributes;
|
||||
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, 0x10ffff, 0, 0x1fffff], \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];
|
||||
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);
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation\Test;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\PHPUnit\Framework\TestCase;
|
||||
use MailPoetVendor\Psr\Log\LoggerInterface;
|
||||
use MailPoetVendor\Symfony\Component\HttpClient\MockHttpClient;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Dumper\XliffFileDumper;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Exception\IncompleteDsnException;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Exception\UnsupportedSchemeException;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Loader\LoaderInterface;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Provider\Dsn;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Provider\ProviderFactoryInterface;
|
||||
use MailPoetVendor\Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
abstract class ProviderFactoryTestCase extends TestCase
|
||||
{
|
||||
protected $client;
|
||||
protected $logger;
|
||||
protected $defaultLocale;
|
||||
protected $loader;
|
||||
protected $xliffFileDumper;
|
||||
public abstract function createFactory() : ProviderFactoryInterface;
|
||||
public static abstract function supportsProvider() : iterable;
|
||||
public static abstract function createProvider() : iterable;
|
||||
public static function unsupportedSchemeProvider() : iterable
|
||||
{
|
||||
return [];
|
||||
}
|
||||
public static function incompleteDsnProvider() : iterable
|
||||
{
|
||||
return [];
|
||||
}
|
||||
public function testSupports(bool $expected, string $dsn)
|
||||
{
|
||||
$factory = $this->createFactory();
|
||||
$this->assertSame($expected, $factory->supports(new Dsn($dsn)));
|
||||
}
|
||||
public function testCreate(string $expected, string $dsn)
|
||||
{
|
||||
$factory = $this->createFactory();
|
||||
$provider = $factory->create(new Dsn($dsn));
|
||||
$this->assertSame($expected, (string) $provider);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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 ?? ($this->client = new MockHttpClient());
|
||||
}
|
||||
protected function getLogger() : LoggerInterface
|
||||
{
|
||||
return $this->logger ?? ($this->logger = $this->createMock(LoggerInterface::class));
|
||||
}
|
||||
protected function getDefaultLocale() : string
|
||||
{
|
||||
return $this->defaultLocale ?? ($this->defaultLocale = 'en');
|
||||
}
|
||||
protected function getLoader() : LoaderInterface
|
||||
{
|
||||
return $this->loader ?? ($this->loader = $this->createMock(LoaderInterface::class));
|
||||
}
|
||||
protected function getXliffFileDumper() : XliffFileDumper
|
||||
{
|
||||
return $this->xliffFileDumper ?? ($this->xliffFileDumper = $this->createMock(XliffFileDumper::class));
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation\Test;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\PHPUnit\Framework\TestCase;
|
||||
use MailPoetVendor\Psr\Log\LoggerInterface;
|
||||
use MailPoetVendor\Symfony\Component\HttpClient\MockHttpClient;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Dumper\XliffFileDumper;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Loader\LoaderInterface;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Provider\ProviderInterface;
|
||||
use MailPoetVendor\Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
abstract class ProviderTestCase extends TestCase
|
||||
{
|
||||
protected $client;
|
||||
protected $logger;
|
||||
protected $defaultLocale;
|
||||
protected $loader;
|
||||
protected $xliffFileDumper;
|
||||
public static abstract function createProvider(HttpClientInterface $client, LoaderInterface $loader, LoggerInterface $logger, string $defaultLocale, string $endpoint) : ProviderInterface;
|
||||
public static abstract function toStringProvider() : iterable;
|
||||
public function testToString(ProviderInterface $provider, string $expected)
|
||||
{
|
||||
$this->assertSame($expected, (string) $provider);
|
||||
}
|
||||
protected function getClient() : MockHttpClient
|
||||
{
|
||||
return $this->client ?? ($this->client = new MockHttpClient());
|
||||
}
|
||||
protected function getLoader() : LoaderInterface
|
||||
{
|
||||
return $this->loader ?? ($this->loader = $this->createMock(LoaderInterface::class));
|
||||
}
|
||||
protected function getLogger() : LoggerInterface
|
||||
{
|
||||
return $this->logger ?? ($this->logger = $this->createMock(LoggerInterface::class));
|
||||
}
|
||||
protected function getDefaultLocale() : string
|
||||
{
|
||||
return $this->defaultLocale ?? ($this->defaultLocale = 'en');
|
||||
}
|
||||
protected function getXliffFileDumper() : XliffFileDumper
|
||||
{
|
||||
return $this->xliffFileDumper ?? ($this->xliffFileDumper = $this->createMock(XliffFileDumper::class));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<?php
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Symfony\Contracts\Translation\TranslatableInterface;
|
||||
use MailPoetVendor\Symfony\Contracts\Translation\TranslatorInterface;
|
||||
class TranslatableMessage implements TranslatableInterface
|
||||
{
|
||||
private $message;
|
||||
private $parameters;
|
||||
private $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 function ($parameter) use($translator, $locale) {
|
||||
return $parameter instanceof TranslatableInterface ? $parameter->trans($translator, $locale) : $parameter;
|
||||
}, $this->getParameters()), $this->getDomain(), $locale);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Symfony\Component\Config\ConfigCacheFactory;
|
||||
use MailPoetVendor\Symfony\Component\Config\ConfigCacheFactoryInterface;
|
||||
use MailPoetVendor\Symfony\Component\Config\ConfigCacheInterface;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Exception\InvalidArgumentException;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Exception\NotFoundResourceException;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Exception\RuntimeException;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Formatter\IntlFormatterInterface;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Formatter\MessageFormatter;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Formatter\MessageFormatterInterface;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Loader\LoaderInterface;
|
||||
use MailPoetVendor\Symfony\Contracts\Translation\LocaleAwareInterface;
|
||||
use MailPoetVendor\Symfony\Contracts\Translation\TranslatorInterface;
|
||||
// Help opcache.preload discover always-needed symbols
|
||||
\class_exists(MessageCatalogue::class);
|
||||
class Translator implements TranslatorInterface, TranslatorBagInterface, LocaleAwareInterface
|
||||
{
|
||||
protected $catalogues = [];
|
||||
private $locale;
|
||||
private $fallbackLocales = [];
|
||||
private $loaders = [];
|
||||
private $resources = [];
|
||||
private $formatter;
|
||||
private $cacheDir;
|
||||
private $debug;
|
||||
private $cacheVary;
|
||||
private $configCacheFactory;
|
||||
private $parentLocales;
|
||||
private $hasIntlFormatter;
|
||||
public function __construct(string $locale, ?MessageFormatterInterface $formatter = null, ?string $cacheDir = null, bool $debug = \false, array $cacheVary = [])
|
||||
{
|
||||
$this->setLocale($locale);
|
||||
if (null === $formatter) {
|
||||
$formatter = new MessageFormatter();
|
||||
}
|
||||
$this->formatter = $formatter;
|
||||
$this->cacheDir = $cacheDir;
|
||||
$this->debug = $debug;
|
||||
$this->cacheVary = $cacheVary;
|
||||
$this->hasIntlFormatter = $formatter instanceof IntlFormatterInterface;
|
||||
}
|
||||
public function setConfigCacheFactory(ConfigCacheFactoryInterface $configCacheFactory)
|
||||
{
|
||||
$this->configCacheFactory = $configCacheFactory;
|
||||
}
|
||||
public function addLoader(string $format, LoaderInterface $loader)
|
||||
{
|
||||
$this->loaders[$format] = $loader;
|
||||
}
|
||||
public function addResource(string $format, $resource, string $locale, ?string $domain = null)
|
||||
{
|
||||
if (null === $domain) {
|
||||
$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]);
|
||||
}
|
||||
}
|
||||
public function setLocale(string $locale)
|
||||
{
|
||||
$this->assertValidLocale($locale);
|
||||
$this->locale = $locale;
|
||||
}
|
||||
public function getLocale()
|
||||
{
|
||||
return $this->locale ?: (\class_exists(\Locale::class) ? \Locale::getDefault() : 'en');
|
||||
}
|
||||
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;
|
||||
}
|
||||
public function getFallbackLocales() : array
|
||||
{
|
||||
return $this->fallbackLocales;
|
||||
}
|
||||
public function trans(?string $id, array $parameters = [], ?string $domain = null, ?string $locale = null)
|
||||
{
|
||||
if (null === $id || '' === $id) {
|
||||
return '';
|
||||
}
|
||||
if (null === $domain) {
|
||||
$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;
|
||||
}
|
||||
}
|
||||
$len = \strlen(MessageCatalogue::INTL_DOMAIN_SUFFIX);
|
||||
if ($this->hasIntlFormatter && ($catalogue->defines($id, $domain . MessageCatalogue::INTL_DOMAIN_SUFFIX) || \strlen($domain) > $len && 0 === \substr_compare($domain, 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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
protected function getLoaders()
|
||||
{
|
||||
return $this->loaders;
|
||||
}
|
||||
protected function loadCatalogue(string $locale)
|
||||
{
|
||||
if (null === $this->cacheDir) {
|
||||
$this->initializeCatalogue($locale);
|
||||
} else {
|
||||
$this->initializeCacheCatalogue($locale);
|
||||
}
|
||||
}
|
||||
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])) {
|
||||
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])) {
|
||||
return;
|
||||
}
|
||||
$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(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';
|
||||
}
|
||||
protected function doLoadCatalogue(string $locale) : void
|
||||
{
|
||||
$this->catalogues[$locale] = new 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 MessageCatalogue($fallback, $this->getAllMessages($this->catalogues[$fallback]));
|
||||
foreach ($this->catalogues[$fallback]->getResources() as $resource) {
|
||||
$fallbackCatalogue->addResource($resource);
|
||||
}
|
||||
$current->addFallbackCatalogue($fallbackCatalogue);
|
||||
$current = $fallbackCatalogue;
|
||||
}
|
||||
}
|
||||
protected function computeFallbackLocales(string $locale)
|
||||
{
|
||||
if (null === $this->parentLocales) {
|
||||
$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);
|
||||
}
|
||||
protected function assertValidLocale(string $locale)
|
||||
{
|
||||
if (!\preg_match('/^[a-z0-9@_\\.\\-]*$/i', $locale)) {
|
||||
throw new InvalidArgumentException(\sprintf('Invalid "%s" locale.', $locale));
|
||||
}
|
||||
}
|
||||
private function getConfigCacheFactory() : ConfigCacheFactoryInterface
|
||||
{
|
||||
if (!$this->configCacheFactory) {
|
||||
$this->configCacheFactory = new ConfigCacheFactory($this->debug);
|
||||
}
|
||||
return $this->configCacheFactory;
|
||||
}
|
||||
private function getAllMessages(MessageCatalogueInterface $catalogue) : array
|
||||
{
|
||||
$allMessages = [];
|
||||
foreach ($catalogue->all() as $domain => $messages) {
|
||||
if ($intlMessages = $catalogue->all($domain . MessageCatalogue::INTL_DOMAIN_SUFFIX)) {
|
||||
$allMessages[$domain . MessageCatalogue::INTL_DOMAIN_SUFFIX] = $intlMessages;
|
||||
$messages = \array_diff_key($messages, $intlMessages);
|
||||
}
|
||||
if ($messages) {
|
||||
$allMessages[$domain] = $messages;
|
||||
}
|
||||
}
|
||||
return $allMessages;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Catalogue\AbstractOperation;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Catalogue\TargetOperation;
|
||||
final class TranslatorBag implements TranslatorBagInterface
|
||||
{
|
||||
private $catalogues = [];
|
||||
public function addCatalogue(MessageCatalogue $catalogue) : void
|
||||
{
|
||||
if (null !== ($existingCatalogue = $this->getCatalogue($catalogue->getLocale()))) {
|
||||
$catalogue->addCatalogue($existingCatalogue);
|
||||
}
|
||||
$this->catalogues[$catalogue->getLocale()] = $catalogue;
|
||||
}
|
||||
public function addBag(TranslatorBagInterface $bag) : void
|
||||
{
|
||||
foreach ($bag->getCatalogues() as $catalogue) {
|
||||
$this->addCatalogue($catalogue);
|
||||
}
|
||||
}
|
||||
public function getCatalogue(?string $locale = null) : MessageCatalogueInterface
|
||||
{
|
||||
if (null === $locale || !isset($this->catalogues[$locale])) {
|
||||
$this->catalogues[$locale] = new MessageCatalogue($locale);
|
||||
}
|
||||
return $this->catalogues[$locale];
|
||||
}
|
||||
public function getCatalogues() : array
|
||||
{
|
||||
return \array_values($this->catalogues);
|
||||
}
|
||||
public function diff(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 MessageCatalogue($locale);
|
||||
foreach ($catalogue->getDomains() as $domain) {
|
||||
$newCatalogue->add($operation->getNewMessages($domain), $domain);
|
||||
}
|
||||
$diff->addCatalogue($newCatalogue);
|
||||
}
|
||||
return $diff;
|
||||
}
|
||||
public function intersect(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 MessageCatalogue($locale);
|
||||
foreach ($operation->getDomains() as $domain) {
|
||||
$obsoleteCatalogue->add(\array_diff($operation->getMessages($domain), $operation->getNewMessages($domain)), $domain);
|
||||
}
|
||||
$diff->addCatalogue($obsoleteCatalogue);
|
||||
}
|
||||
return $diff;
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Exception\InvalidArgumentException;
|
||||
interface TranslatorBagInterface
|
||||
{
|
||||
public function getCatalogue(?string $locale = null);
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation\Util;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
class ArrayConverter
|
||||
{
|
||||
public static function expandToTree(array $messages)
|
||||
{
|
||||
$tree = [];
|
||||
foreach ($messages as $id => $value) {
|
||||
$referenceToElement =& self::getElementByPath($tree, self::getKeyParts($id));
|
||||
$referenceToElement = $value;
|
||||
unset($referenceToElement);
|
||||
}
|
||||
return $tree;
|
||||
}
|
||||
private static function &getElementByPath(array &$tree, array $parts)
|
||||
{
|
||||
$elem =& $tree;
|
||||
$parentOfElem = null;
|
||||
foreach ($parts as $i => $part) {
|
||||
if (isset($elem[$part]) && \is_string($elem[$part])) {
|
||||
$elem =& $elem[\implode('.', \array_slice($parts, $i))];
|
||||
break;
|
||||
}
|
||||
$parentOfElem =& $elem;
|
||||
$elem =& $elem[$part];
|
||||
}
|
||||
if ($elem && \is_array($elem) && $parentOfElem) {
|
||||
self::cancelExpand($parentOfElem, $part, $elem);
|
||||
}
|
||||
return $elem;
|
||||
}
|
||||
private static function cancelExpand(array &$tree, string $prefix, array $node)
|
||||
{
|
||||
$prefix .= '.';
|
||||
foreach ($node as $id => $value) {
|
||||
if (\is_string($value)) {
|
||||
$tree[$prefix . $id] = $value;
|
||||
} else {
|
||||
self::cancelExpand($tree, $prefix . $id, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
private static function getKeyParts(string $key) : array
|
||||
{
|
||||
$parts = \explode('.', $key);
|
||||
$partsCount = \count($parts);
|
||||
$result = [];
|
||||
$buffer = '';
|
||||
foreach ($parts as $index => $part) {
|
||||
if (0 === $index && '' === $part) {
|
||||
$buffer = '.';
|
||||
continue;
|
||||
}
|
||||
if ($index === $partsCount - 1 && '' === $part) {
|
||||
$buffer .= '.';
|
||||
$result[] = $buffer;
|
||||
continue;
|
||||
}
|
||||
if (isset($parts[$index + 1]) && '' === $parts[$index + 1]) {
|
||||
$buffer .= $part;
|
||||
continue;
|
||||
}
|
||||
if ($buffer) {
|
||||
$result[] = $buffer . $part;
|
||||
$buffer = '';
|
||||
continue;
|
||||
}
|
||||
$result[] = $part;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Symfony\Component\Translation\Util;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Exception\InvalidArgumentException;
|
||||
use MailPoetVendor\Symfony\Component\Translation\Exception\InvalidResourceException;
|
||||
class XliffUtils
|
||||
{
|
||||
public static function getVersionNumber(\DOMDocument $dom) : string
|
||||
{
|
||||
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';
|
||||
}
|
||||
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
|
||||
{
|
||||
// Version prior to 8.0 can be enabled without deprecation
|
||||
if (\PHP_VERSION_ID < 80000) {
|
||||
return \true;
|
||||
}
|
||||
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-strict.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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<?php
|
||||
@@ -0,0 +1 @@
|
||||
<?php
|
||||
Reference in New Issue
Block a user