[Web] Update composer libs

- Removing symfony/deprecation-contracts (v2.4.0)
  - Upgrading ddeboer/imap (1.12.1 => 1.13.1)
  - Upgrading directorytree/ldaprecord (v2.6.3 => v2.10.1)
  - Upgrading illuminate/contracts (v8.53.1 => v9.3.0)
  - Upgrading nesbot/carbon (2.51.1 => 2.57.0)
  - Upgrading phpmailer/phpmailer (v6.5.0 => v6.6.0)
  - Upgrading psr/container (1.1.1 => 2.0.2)
  - Upgrading psr/log (1.1.4 => 3.0.0)
  - Upgrading psr/simple-cache (1.0.1 => 2.0.0)
  - Upgrading robthree/twofactorauth (1.8.0 => 1.8.1)
  - Upgrading symfony/polyfill-ctype (v1.23.0 => v1.24.0)
  - Upgrading symfony/polyfill-mbstring (v1.23.1 => v1.24.0)
  - Upgrading symfony/polyfill-php80 (v1.23.1 => v1.24.0)
  - Upgrading symfony/translation (v5.3.4 => v6.0.5)
  - Upgrading symfony/translation-contracts (v2.4.0 => v3.0.0)
  - Upgrading symfony/var-dumper (v5.3.6 => v6.0.5)
  - Upgrading tightenco/collect (v8.34.0 => v8.83.2)
  - Upgrading twig/twig (v3.3.2 => v3.3.8)
This commit is contained in:
andryyy
2022-03-02 20:08:24 +01:00
parent 24275ffdbf
commit 98bc947d00
940 changed files with 7649 additions and 14226 deletions

View File

@@ -1,6 +1,13 @@
CHANGELOG
=========
5.4
---
* Add `github` format & autodetection to render errors as annotations when
running the XLIFF linter command in a Github Actions environment.
* Translation providers are not experimental anymore
5.3
---

View File

@@ -80,10 +80,21 @@ abstract class AbstractOperation implements OperationInterface
/**
* {@inheritdoc}
*/
public function getDomains()
public function getDomains(): array
{
if (null === $this->domains) {
$this->domains = array_values(array_unique(array_merge($this->source->getDomains(), $this->target->getDomains())));
$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;
@@ -92,7 +103,7 @@ abstract class AbstractOperation implements OperationInterface
/**
* {@inheritdoc}
*/
public function getMessages(string $domain)
public function getMessages(string $domain): array
{
if (!\in_array($domain, $this->getDomains())) {
throw new InvalidArgumentException(sprintf('Invalid domain: "%s".', $domain));
@@ -108,7 +119,7 @@ abstract class AbstractOperation implements OperationInterface
/**
* {@inheritdoc}
*/
public function getNewMessages(string $domain)
public function getNewMessages(string $domain): array
{
if (!\in_array($domain, $this->getDomains())) {
throw new InvalidArgumentException(sprintf('Invalid domain: "%s".', $domain));
@@ -124,7 +135,7 @@ abstract class AbstractOperation implements OperationInterface
/**
* {@inheritdoc}
*/
public function getObsoleteMessages(string $domain)
public function getObsoleteMessages(string $domain): array
{
if (!\in_array($domain, $this->getDomains())) {
throw new InvalidArgumentException(sprintf('Invalid domain: "%s".', $domain));
@@ -140,7 +151,7 @@ abstract class AbstractOperation implements OperationInterface
/**
* {@inheritdoc}
*/
public function getResult()
public function getResult(): MessageCatalogueInterface
{
foreach ($this->getDomains() as $domain) {
if (!isset($this->messages[$domain])) {

View File

@@ -36,36 +36,26 @@ interface OperationInterface
{
/**
* Returns domains affected by operation.
*
* @return array
*/
public function getDomains();
public function getDomains(): array;
/**
* Returns all valid messages ('all') after operation.
*
* @return array
*/
public function getMessages(string $domain);
public function getMessages(string $domain): array;
/**
* Returns new messages ('new') after operation.
*
* @return array
*/
public function getNewMessages(string $domain);
public function getNewMessages(string $domain): array;
/**
* Returns obsolete messages ('obsolete') after operation.
*
* @return array
*/
public function getObsoleteMessages(string $domain);
public function getObsoleteMessages(string $domain): array;
/**
* Returns resulting catalogue ('result').
*
* @return MessageCatalogueInterface
*/
public function getResult();
public function getResult(): MessageCatalogueInterface;
}

View File

@@ -49,7 +49,7 @@ class TargetOperation extends AbstractOperation
foreach ($this->source->all($domain) as $id => $message) {
if ($this->target->has($id, $domain)) {
$this->messages[$domain]['all'][$id] = $message;
$d = $this->target->defines($id, $intlDomain) ? $intlDomain : $domain;
$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);

View File

@@ -11,7 +11,10 @@
namespace Symfony\Component\Translation\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
@@ -25,22 +28,18 @@ use Symfony\Component\Translation\Writer\TranslationWriterInterface;
/**
* @author Mathieu Santostefano <msantostefano@protonmail.com>
*
* @experimental in 5.3
*/
#[AsCommand(name: 'translation:pull', description: 'Pull translations from a given provider.')]
final class TranslationPullCommand extends Command
{
use TranslationTrait;
protected static $defaultName = 'translation:pull';
protected static $defaultDescription = 'Pull translations from a given provider.';
private $providerCollection;
private $writer;
private $reader;
private $defaultLocale;
private $transPaths;
private $enabledLocales;
private string $defaultLocale;
private array $transPaths;
private array $enabledLocales;
public function __construct(TranslationProviderCollection $providerCollection, TranslationWriterInterface $writer, TranslationReaderInterface $reader, string $defaultLocale, array $transPaths = [], array $enabledLocales = [])
{
@@ -54,6 +53,36 @@ final class TranslationPullCommand extends Command
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 ($provider && method_exists($provider, 'getDomains')) {
$domains = $provider->getDomains();
$suggestions->suggestValues($domains);
}
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']);
}
}
/**
* {@inheritdoc}
*/
@@ -81,7 +110,7 @@ You can overwrite existing translations (and remove the missing ones on local si
Full example:
<info>php %command.full_name% provider --force --domains=messages,validators --locales=en</>
<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.
@@ -119,6 +148,7 @@ EOF
$writeOptions = [
'path' => end($this->transPaths),
'xliff_version' => $xliffVersion,
'default_locale' => $this->defaultLocale,
];
if (!$domains) {

View File

@@ -11,7 +11,10 @@
namespace Symfony\Component\Translation\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
@@ -24,20 +27,16 @@ use Symfony\Component\Translation\TranslatorBag;
/**
* @author Mathieu Santostefano <msantostefano@protonmail.com>
*
* @experimental in 5.3
*/
#[AsCommand(name: 'translation:push', description: 'Push translations to a given provider.')]
final class TranslationPushCommand extends Command
{
use TranslationTrait;
protected static $defaultName = 'translation:push';
protected static $defaultDescription = 'Push translations to a given provider.';
private $providers;
private $reader;
private $transPaths;
private $enabledLocales;
private array $transPaths;
private array $enabledLocales;
public function __construct(TranslationProviderCollection $providers, TranslationReaderInterface $reader, array $transPaths = [], array $enabledLocales = [])
{
@@ -49,6 +48,30 @@ final class TranslationPushCommand extends Command
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);
}
}
/**
* {@inheritdoc}
*/
@@ -79,7 +102,7 @@ You can delete provider translations which are not present locally by using the
Full example:
<info>php %command.full_name% provider --force --delete-missing --domains=messages,validators --locales=en</>
<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.

View File

@@ -32,8 +32,7 @@ trait TranslationTrait
if ($domains) {
foreach ($domains as $domain) {
$catalogue = $this->filterCatalogue($catalogue, $domain);
$bag->addCatalogue($catalogue);
$bag->addCatalogue($this->filterCatalogue($catalogue, $domain));
}
} else {
$bag->addCatalogue($catalogue);

View File

@@ -11,7 +11,11 @@
namespace Symfony\Component\Translation\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\CI\GithubActionReporter;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
@@ -28,23 +32,21 @@ use Symfony\Component\Translation\Util\XliffUtils;
* @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
{
protected static $defaultName = 'lint:xliff';
protected static $defaultDescription = 'Lint an XLIFF file and outputs encountered errors';
private $format;
private $displayCorrectFiles;
private $directoryIteratorProvider;
private $isReadableProvider;
private $requireStrictFileNames;
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 = $directoryIteratorProvider;
$this->isReadableProvider = $isReadableProvider;
$this->directoryIteratorProvider = null === $directoryIteratorProvider || $directoryIteratorProvider instanceof \Closure ? $directoryIteratorProvider : \Closure::fromCallable($directoryIteratorProvider);
$this->isReadableProvider = null === $isReadableProvider || $isReadableProvider instanceof \Closure ? $isReadableProvider : \Closure::fromCallable($isReadableProvider);
$this->requireStrictFileNames = $requireStrictFileNames;
}
@@ -54,9 +56,8 @@ class XliffLintCommand extends Command
protected function configure()
{
$this
->setDescription(self::$defaultDescription)
->addArgument('filename', InputArgument::IS_ARRAY, 'A file, a directory or "-" for reading from STDIN')
->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format', 'txt')
->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format')
->setHelp(<<<EOF
The <info>%command.name%</info> command lints an XLIFF file and outputs to STDOUT
the first encountered syntax error.
@@ -79,11 +80,11 @@ EOF
;
}
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$filenames = (array) $input->getArgument('filename');
$this->format = $input->getOption('format');
$this->format = $input->getOption('format') ?? (GithubActionReporter::isGithubActionEnvironment() ? 'github' : 'txt');
$this->displayCorrectFiles = $output->isVerbose();
if (['-'] === $filenames) {
@@ -160,15 +161,18 @@ EOF
return $this->displayTxt($io, $files);
case 'json':
return $this->displayJson($io, $files);
case 'github':
return $this->displayTxt($io, $files, true);
default:
throw new InvalidArgumentException(sprintf('The format "%s" is not supported.', $this->format));
}
}
private function displayTxt(SymfonyStyle $io, array $filesInfo)
private function displayTxt(SymfonyStyle $io, array $filesInfo, bool $errorAsGithubAnnotations = false)
{
$countFiles = \count($filesInfo);
$erroredFiles = 0;
$githubReporter = $errorAsGithubAnnotations ? new GithubActionReporter($io) : null;
foreach ($filesInfo as $info) {
if ($info['valid'] && $this->displayCorrectFiles) {
@@ -176,9 +180,15 @@ EOF
} elseif (!$info['valid']) {
++$erroredFiles;
$io->text('<error> ERROR </error>'.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
$io->listing(array_map(function ($error) {
$io->listing(array_map(function ($error) use ($info, $githubReporter) {
// general document errors have a '-1' line number
return -1 === $error['line'] ? $error['message'] : sprintf('Line %d, Column %d: %s', $error['line'], $error['column'], $error['message']);
$line = -1 === $error['line'] ? null : $error['line'];
if ($githubReporter) {
$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']));
}
}
@@ -264,4 +274,11 @@ EOF
return null;
}
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
if ($input->mustSuggestOptionValuesFor('format')) {
$suggestions->suggestValues(['txt', 'json', 'github']);
}
}
}

View File

@@ -62,34 +62,22 @@ class TranslationDataCollector extends DataCollector implements LateDataCollecto
$this->data = [];
}
/**
* @return array|Data
*/
public function getMessages()
public function getMessages(): array|Data
{
return $this->data['messages'] ?? [];
}
/**
* @return int
*/
public function getCountMissings()
public function getCountMissings(): int
{
return $this->data[DataCollectorTranslator::MESSAGE_MISSING] ?? 0;
}
/**
* @return int
*/
public function getCountFallbacks()
public function getCountFallbacks(): int
{
return $this->data[DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK] ?? 0;
}
/**
* @return int
*/
public function getCountDefines()
public function getCountDefines(): int
{
return $this->data[DataCollectorTranslator::MESSAGE_DEFINED] ?? 0;
}
@@ -110,7 +98,7 @@ class TranslationDataCollector extends DataCollector implements LateDataCollecto
/**
* {@inheritdoc}
*/
public function getName()
public function getName(): string
{
return 'translation';
}

View File

@@ -25,15 +25,11 @@ class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInter
public const MESSAGE_MISSING = 1;
public const MESSAGE_EQUALS_FALLBACK = 2;
/**
* @var TranslatorInterface|TranslatorBagInterface
*/
private $translator;
private $messages = [];
private array $messages = [];
/**
* @param TranslatorInterface $translator The translator must implement TranslatorBagInterface
* @param TranslatorInterface&TranslatorBagInterface&LocaleAwareInterface $translator
*/
public function __construct(TranslatorInterface $translator)
{
@@ -47,7 +43,7 @@ class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInter
/**
* {@inheritdoc}
*/
public function trans(?string $id, array $parameters = [], string $domain = null, string $locale = null)
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);
@@ -66,7 +62,7 @@ class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInter
/**
* {@inheritdoc}
*/
public function getLocale()
public function getLocale(): string
{
return $this->translator->getLocale();
}
@@ -74,7 +70,7 @@ class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInter
/**
* {@inheritdoc}
*/
public function getCatalogue(string $locale = null)
public function getCatalogue(string $locale = null): MessageCatalogueInterface
{
return $this->translator->getCatalogue($locale);
}
@@ -92,7 +88,7 @@ class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInter
*
* @return string[]
*/
public function warmUp(string $cacheDir)
public function warmUp(string $cacheDir): array
{
if ($this->translator instanceof WarmableInterface) {
return (array) $this->translator->warmUp($cacheDir);
@@ -103,10 +99,8 @@ class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInter
/**
* Gets the fallback locales.
*
* @return array The fallback locales
*/
public function getFallbackLocales()
public function getFallbackLocales(): array
{
if ($this->translator instanceof Translator || method_exists($this->translator, 'getFallbackLocales')) {
return $this->translator->getFallbackLocales();
@@ -123,10 +117,7 @@ class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInter
return $this->translator->{$method}(...$args);
}
/**
* @return array
*/
public function getCollectedMessages()
public function getCollectedMessages(): array
{
return $this->messages;
}

View File

@@ -20,28 +20,15 @@ use Symfony\Component\DependencyInjection\Reference;
*/
class TranslationDumperPass implements CompilerPassInterface
{
private $writerServiceId;
private $dumperTag;
public function __construct(string $writerServiceId = 'translation.writer', string $dumperTag = 'translation.dumper')
{
if (1 < \func_num_args()) {
trigger_deprecation('symfony/translation', '5.3', 'Configuring "%s" is deprecated.', __CLASS__);
}
$this->writerServiceId = $writerServiceId;
$this->dumperTag = $dumperTag;
}
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition($this->writerServiceId)) {
if (!$container->hasDefinition('translation.writer')) {
return;
}
$definition = $container->getDefinition($this->writerServiceId);
$definition = $container->getDefinition('translation.writer');
foreach ($container->findTaggedServiceIds($this->dumperTag, true) as $id => $attributes) {
foreach ($container->findTaggedServiceIds('translation.dumper', true) as $id => $attributes) {
$definition->addMethodCall('addDumper', [$attributes[0]['alias'], new Reference($id)]);
}
}

View File

@@ -21,28 +21,15 @@ use Symfony\Component\DependencyInjection\Reference;
*/
class TranslationExtractorPass implements CompilerPassInterface
{
private $extractorServiceId;
private $extractorTag;
public function __construct(string $extractorServiceId = 'translation.extractor', string $extractorTag = 'translation.extractor')
{
if (0 < \func_num_args()) {
trigger_deprecation('symfony/translation', '5.3', 'Configuring "%s" is deprecated.', __CLASS__);
}
$this->extractorServiceId = $extractorServiceId;
$this->extractorTag = $extractorTag;
}
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition($this->extractorServiceId)) {
if (!$container->hasDefinition('translation.extractor')) {
return;
}
$definition = $container->getDefinition($this->extractorServiceId);
$definition = $container->getDefinition('translation.extractor');
foreach ($container->findTaggedServiceIds($this->extractorTag, true) as $id => $attributes) {
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));
}

View File

@@ -18,34 +18,15 @@ use Symfony\Component\DependencyInjection\Reference;
class TranslatorPass implements CompilerPassInterface
{
private $translatorServiceId;
private $readerServiceId;
private $loaderTag;
private $debugCommandServiceId;
private $updateCommandServiceId;
public function __construct(string $translatorServiceId = 'translator.default', string $readerServiceId = 'translation.reader', string $loaderTag = 'translation.loader', string $debugCommandServiceId = 'console.command.translation_debug', string $updateCommandServiceId = 'console.command.translation_update')
{
if (0 < \func_num_args()) {
trigger_deprecation('symfony/translation', '5.3', 'Configuring "%s" is deprecated.', __CLASS__);
}
$this->translatorServiceId = $translatorServiceId;
$this->readerServiceId = $readerServiceId;
$this->loaderTag = $loaderTag;
$this->debugCommandServiceId = $debugCommandServiceId;
$this->updateCommandServiceId = $updateCommandServiceId;
}
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition($this->translatorServiceId)) {
if (!$container->hasDefinition('translator.default')) {
return;
}
$loaders = [];
$loaderRefs = [];
foreach ($container->findTaggedServiceIds($this->loaderTag, true) as $id => $attributes) {
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'])) {
@@ -53,8 +34,8 @@ class TranslatorPass implements CompilerPassInterface
}
}
if ($container->hasDefinition($this->readerServiceId)) {
$definition = $container->getDefinition($this->readerServiceId);
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]]);
@@ -63,7 +44,7 @@ class TranslatorPass implements CompilerPassInterface
}
$container
->findDefinition($this->translatorServiceId)
->findDefinition('translator.default')
->replaceArgument(0, ServiceLocatorTagPass::register($container, $loaderRefs))
->replaceArgument(3, $loaders)
;
@@ -73,16 +54,16 @@ class TranslatorPass implements CompilerPassInterface
}
$paths = array_keys($container->getDefinition('twig.template_iterator')->getArgument(1));
if ($container->hasDefinition($this->debugCommandServiceId)) {
$definition = $container->getDefinition($this->debugCommandServiceId);
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($this->updateCommandServiceId)) {
$definition = $container->getDefinition($this->updateCommandServiceId);
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) {

View File

@@ -22,30 +22,26 @@ use Symfony\Component\DependencyInjection\ServiceLocator;
*/
class TranslatorPathsPass extends AbstractRecursivePass
{
private $translatorServiceId;
private $debugCommandServiceId;
private $updateCommandServiceId;
private $resolverServiceId;
private $level = 0;
private $paths = [];
private $definitions = [];
private $controllers = [];
private int $level = 0;
public function __construct(string $translatorServiceId = 'translator', string $debugCommandServiceId = 'console.command.translation_debug', string $updateCommandServiceId = 'console.command.translation_update', string $resolverServiceId = 'argument_resolver.service')
{
if (0 < \func_num_args()) {
trigger_deprecation('symfony/translation', '5.3', 'Configuring "%s" is deprecated.', __CLASS__);
}
/**
* @var array<string, bool>
*/
private array $paths = [];
$this->translatorServiceId = $translatorServiceId;
$this->debugCommandServiceId = $debugCommandServiceId;
$this->updateCommandServiceId = $updateCommandServiceId;
$this->resolverServiceId = $resolverServiceId;
}
/**
* @var array<int, Definition>
*/
private array $definitions = [];
/**
* @var array<string, array<string, bool>>
*/
private array $controllers = [];
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition($this->translatorServiceId)) {
if (!$container->hasDefinition('translator')) {
return;
}
@@ -70,12 +66,12 @@ class TranslatorPathsPass extends AbstractRecursivePass
}
}
if ($paths) {
if ($container->hasDefinition($this->debugCommandServiceId)) {
$definition = $container->getDefinition($this->debugCommandServiceId);
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($this->updateCommandServiceId)) {
$definition = $container->getDefinition($this->updateCommandServiceId);
if ($container->hasDefinition('console.command.translation_extract')) {
$definition = $container->getDefinition('console.command.translation_extract');
$definition->replaceArgument(7, array_merge($definition->getArgument(7), $paths));
}
}
@@ -86,10 +82,10 @@ class TranslatorPathsPass extends AbstractRecursivePass
}
}
protected function processValue($value, bool $isRoot = false)
protected function processValue(mixed $value, bool $isRoot = false): mixed
{
if ($value instanceof Reference) {
if ((string) $value === $this->translatorServiceId) {
if ('translator' === (string) $value) {
for ($i = $this->level - 1; $i >= 0; --$i) {
$class = $this->definitions[$i]->getClass();
@@ -124,8 +120,8 @@ class TranslatorPathsPass extends AbstractRecursivePass
private function findControllerArguments(ContainerBuilder $container): array
{
if ($container->hasDefinition($this->resolverServiceId)) {
$argument = $container->getDefinition($this->resolverServiceId)->getArgument(0);
if ($container->hasDefinition('argument_resolver.service')) {
$argument = $container->getDefinition('argument_resolver.service')->getArgument(0);
if ($argument instanceof Reference) {
$argument = $container->getDefinition($argument);
}
@@ -133,8 +129,8 @@ class TranslatorPathsPass extends AbstractRecursivePass
return $argument->getArgument(0);
}
if ($container->hasDefinition('debug.'.$this->resolverServiceId)) {
$argument = $container->getDefinition('debug.'.$this->resolverServiceId)->getArgument(0);
if ($container->hasDefinition('debug.'.'argument_resolver.service')) {
$argument = $container->getDefinition('debug.'.'argument_resolver.service')->getArgument(0);
if ($argument instanceof Reference) {
$argument = $container->getDefinition($argument);
}

View File

@@ -20,13 +20,13 @@ use Symfony\Component\Translation\MessageCatalogue;
*/
class CsvFileDumper extends FileDumper
{
private $delimiter = ';';
private $enclosure = '"';
private string $delimiter = ';';
private string $enclosure = '"';
/**
* {@inheritdoc}
*/
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = [])
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string
{
$handle = fopen('php://memory', 'r+');
@@ -53,7 +53,7 @@ class CsvFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
protected function getExtension()
protected function getExtension(): string
{
return 'csv';
}

View File

@@ -86,17 +86,13 @@ abstract class FileDumper implements DumperInterface
/**
* Transforms a domain of a message catalogue to its string representation.
*
* @return string representation
*/
abstract public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []);
abstract public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string;
/**
* Gets the file extension of the dumper.
*
* @return string file extension
*/
abstract protected function getExtension();
abstract protected function getExtension(): string;
/**
* Gets the relative file path using the template.

View File

@@ -28,7 +28,7 @@ class IcuResFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = [])
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string
{
$data = $indexes = $resources = '';
@@ -97,7 +97,7 @@ class IcuResFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
protected function getExtension()
protected function getExtension(): string
{
return 'res';
}

View File

@@ -23,7 +23,7 @@ class IniFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = [])
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string
{
$output = '';
@@ -38,7 +38,7 @@ class IniFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
protected function getExtension()
protected function getExtension(): string
{
return 'ini';
}

View File

@@ -23,7 +23,7 @@ class JsonFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = [])
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string
{
$flags = $options['json_encoding'] ?? \JSON_PRETTY_PRINT;
@@ -33,7 +33,7 @@ class JsonFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
protected function getExtension()
protected function getExtension(): string
{
return 'json';
}

View File

@@ -24,7 +24,7 @@ class MoFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = [])
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string
{
$sources = $targets = $sourceOffsets = $targetOffsets = '';
$offsets = [];
@@ -70,12 +70,12 @@ class MoFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
protected function getExtension()
protected function getExtension(): string
{
return 'mo';
}
private function writeLong($str): string
private function writeLong(mixed $str): string
{
return pack('V*', $str);
}

View File

@@ -23,7 +23,7 @@ class PhpFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = [])
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string
{
return "<?php\n\nreturn ".var_export($messages->all($domain), true).";\n";
}
@@ -31,7 +31,7 @@ class PhpFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
protected function getExtension()
protected function getExtension(): string
{
return 'php';
}

View File

@@ -23,7 +23,7 @@ class PoFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = [])
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string
{
$output = 'msgid ""'."\n";
$output .= 'msgstr ""'."\n";
@@ -114,7 +114,7 @@ EOF;
/**
* {@inheritdoc}
*/
protected function getExtension()
protected function getExtension(): string
{
return 'po';
}
@@ -124,7 +124,7 @@ EOF;
return addcslashes($str, "\0..\37\42\134");
}
private function formatComments($comments, string $prefix = ''): ?string
private function formatComments(string|array $comments, string $prefix = ''): ?string
{
$output = null;

View File

@@ -23,7 +23,7 @@ class QtFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = [])
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string
{
$dom = new \DOMDocument('1.0', 'utf-8');
$dom->formatOutput = true;
@@ -54,7 +54,7 @@ class QtFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
protected function getExtension()
protected function getExtension(): string
{
return 'ts';
}

View File

@@ -24,7 +24,7 @@ class XliffFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = [])
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string
{
$xliffVersion = '1.2';
if (\array_key_exists('xliff_version', $options)) {
@@ -50,7 +50,7 @@ class XliffFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
protected function getExtension()
protected function getExtension(): string
{
return 'xlf';
}

View File

@@ -23,7 +23,7 @@ use Symfony\Component\Yaml\Yaml;
*/
class YamlFileDumper extends FileDumper
{
private $extension;
private string $extension;
public function __construct(string $extension = 'yml')
{
@@ -33,7 +33,7 @@ class YamlFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = [])
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.');
@@ -55,7 +55,7 @@ class YamlFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
protected function getExtension()
protected function getExtension(): string
{
return $this->extension;
}

View File

@@ -15,18 +15,16 @@ use Symfony\Contracts\HttpClient\ResponseInterface;
/**
* @author Fabien Potencier <fabien@symfony.com>
*
* @experimental in 5.3
*/
class ProviderException extends RuntimeException implements ProviderExceptionInterface
{
private $response;
private $debug;
private string $debug;
public function __construct(string $message, ResponseInterface $response, int $code = 0, \Exception $previous = null)
{
$this->response = $response;
$this->debug .= $response->getInfo('debug') ?? '';
$this->debug = $response->getInfo('debug') ?? '';
parent::__construct($message, $code, $previous);
}

View File

@@ -13,8 +13,6 @@ namespace Symfony\Component\Translation\Exception;
/**
* @author Fabien Potencier <fabien@symfony.com>
*
* @experimental in 5.3
*/
interface ProviderExceptionInterface extends ExceptionInterface
{

View File

@@ -20,12 +20,7 @@ use Symfony\Component\Translation\Exception\InvalidArgumentException;
*/
abstract class AbstractFileExtractor
{
/**
* @param string|iterable $resource Files, a file or a directory
*
* @return iterable
*/
protected function extractFiles($resource)
protected function extractFiles(string|iterable $resource): iterable
{
if (is_iterable($resource)) {
$files = [];
@@ -49,11 +44,9 @@ abstract class AbstractFileExtractor
}
/**
* @return bool
*
* @throws InvalidArgumentException
*/
protected function isFile(string $file)
protected function isFile(string $file): bool
{
if (!is_file($file)) {
throw new InvalidArgumentException(sprintf('The "%s" file does not exist.', $file));
@@ -68,9 +61,7 @@ abstract class AbstractFileExtractor
abstract protected function canBeExtracted(string $file);
/**
* @param string|array $resource Files, a file or a directory
*
* @return iterable files to be extracted
* @return iterable
*/
abstract protected function extractFromDirectory($resource);
abstract protected function extractFromDirectory(string|array $resource);
}

View File

@@ -25,7 +25,7 @@ class ChainExtractor implements ExtractorInterface
*
* @var ExtractorInterface[]
*/
private $extractors = [];
private array $extractors = [];
/**
* Adds a loader to the translation extractor.
@@ -48,7 +48,7 @@ class ChainExtractor implements ExtractorInterface
/**
* {@inheritdoc}
*/
public function extract($directory, MessageCatalogue $catalogue)
public function extract(string|iterable $directory, MessageCatalogue $catalogue)
{
foreach ($this->extractors as $extractor) {
$extractor->extract($directory, $catalogue);

View File

@@ -26,7 +26,7 @@ interface ExtractorInterface
*
* @param string|iterable<string> $resource Files, a file or a directory
*/
public function extract($resource, MessageCatalogue $catalogue);
public function extract(string|iterable $resource, MessageCatalogue $catalogue);
/**
* Sets the prefix that should be used for new found messages.

View File

@@ -27,15 +27,11 @@ class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface
/**
* Prefix for new found message.
*
* @var string
*/
private $prefix = '';
private string $prefix = '';
/**
* The sequence that captures translation messages.
*
* @var array
*/
protected $sequences = [
[
@@ -135,7 +131,7 @@ class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface
/**
* {@inheritdoc}
*/
public function extract($resource, MessageCatalogue $catalog)
public function extract(string|iterable $resource, MessageCatalogue $catalog)
{
$files = $this->extractFiles($resource);
foreach ($files as $file) {
@@ -155,12 +151,8 @@ class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface
/**
* Normalizes a token.
*
* @param mixed $token
*
* @return string|null
*/
protected function normalizeToken($token)
protected function normalizeToken(mixed $token): ?string
{
if (isset($token[1]) && 'b"' !== $token) {
return $token[1];
@@ -315,11 +307,9 @@ class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface
}
/**
* @return bool
*
* @throws \InvalidArgumentException
*/
protected function canBeExtracted(string $file)
protected function canBeExtracted(string $file): bool
{
return $this->isFile($file) && 'php' === pathinfo($file, \PATHINFO_EXTENSION);
}
@@ -327,8 +317,12 @@ class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface
/**
* {@inheritdoc}
*/
protected function extractFromDirectory($directory)
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);

View File

@@ -64,10 +64,8 @@ class PhpStringTokenParser
* Parses a string token.
*
* @param string $str String token content
*
* @return string The parsed string
*/
public static function parse(string $str)
public static function parse(string $str): string
{
$bLength = 0;
if ('b' === $str[0]) {
@@ -90,10 +88,8 @@ class PhpStringTokenParser
*
* @param string $str String without quotes
* @param string|null $quote Quote type
*
* @return string String with escape sequences parsed
*/
public static function parseEscapeSequences(string $str, string $quote = null)
public static function parseEscapeSequences(string $str, string $quote = null): string
{
if (null !== $quote) {
$str = str_replace('\\'.$quote, $quote, $str);
@@ -124,10 +120,8 @@ class PhpStringTokenParser
*
* @param string $startToken Doc string start token content (<<<SMTHG)
* @param string $str String token content
*
* @return string Parsed string
*/
public static function parseDocString(string $startToken, string $str)
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);

View File

@@ -37,7 +37,7 @@ class MessageFormatter implements MessageFormatterInterface, IntlFormatterInterf
/**
* {@inheritdoc}
*/
public function format(string $message, string $locale, array $parameters = [])
public function format(string $message, string $locale, array $parameters = []): string
{
if ($this->translator instanceof TranslatorInterface) {
return $this->translator->trans($message, $parameters, null, $locale);

View File

@@ -23,8 +23,6 @@ interface MessageFormatterInterface
* @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
*
* @return string
*/
public function format(string $message, string $locale, array $parameters = []);
public function format(string $message, string $locale, array $parameters = []): string;
}

View File

@@ -1,4 +1,4 @@
Copyright (c) 2004-2021 Fabien Potencier
Copyright (c) 2004-2022 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -23,7 +23,7 @@ class ArrayLoader implements LoaderInterface
/**
* {@inheritdoc}
*/
public function load($resource, string $locale, string $domain = 'messages')
public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue
{
$resource = $this->flatten($resource);
$catalogue = new MessageCatalogue($locale);

View File

@@ -20,14 +20,14 @@ use Symfony\Component\Translation\Exception\NotFoundResourceException;
*/
class CsvFileLoader extends FileLoader
{
private $delimiter = ';';
private $enclosure = '"';
private $escape = '\\';
private string $delimiter = ';';
private string $enclosure = '"';
private string $escape = '\\';
/**
* {@inheritdoc}
*/
protected function loadResource(string $resource)
protected function loadResource(string $resource): array
{
$messages = [];

View File

@@ -14,6 +14,7 @@ namespace Symfony\Component\Translation\Loader;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Translation\Exception\InvalidResourceException;
use Symfony\Component\Translation\Exception\NotFoundResourceException;
use Symfony\Component\Translation\MessageCatalogue;
/**
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
@@ -23,7 +24,7 @@ abstract class FileLoader extends ArrayLoader
/**
* {@inheritdoc}
*/
public function load($resource, string $locale, string $domain = 'messages')
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));
@@ -55,9 +56,7 @@ abstract class FileLoader extends ArrayLoader
}
/**
* @return array
*
* @throws InvalidResourceException if stream content has an invalid format
*/
abstract protected function loadResource(string $resource);
abstract protected function loadResource(string $resource): array;
}

View File

@@ -26,7 +26,7 @@ class IcuDatFileLoader extends IcuResFileLoader
/**
* {@inheritdoc}
*/
public function load($resource, string $locale, string $domain = 'messages')
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));

View File

@@ -26,7 +26,7 @@ class IcuResFileLoader implements LoaderInterface
/**
* {@inheritdoc}
*/
public function load($resource, string $locale, string $domain = 'messages')
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));
@@ -72,10 +72,8 @@ class IcuResFileLoader implements LoaderInterface
* @param \ResourceBundle $rb The ResourceBundle that will be flattened
* @param array $messages Used internally for recursive calls
* @param string $path Current path being parsed, used internally for recursive calls
*
* @return array the flattened ResourceBundle
*/
protected function flatten(\ResourceBundle $rb, array &$messages = [], string $path = null)
protected function flatten(\ResourceBundle $rb, array &$messages = [], string $path = null): array
{
foreach ($rb as $key => $value) {
$nodePath = $path ? $path.'.'.$key : $key;

View File

@@ -21,7 +21,7 @@ class IniFileLoader extends FileLoader
/**
* {@inheritdoc}
*/
protected function loadResource(string $resource)
protected function loadResource(string $resource): array
{
return parse_ini_file($resource, true);
}

View File

@@ -23,7 +23,7 @@ class JsonFileLoader extends FileLoader
/**
* {@inheritdoc}
*/
protected function loadResource(string $resource)
protected function loadResource(string $resource): array
{
$messages = [];
if ($data = file_get_contents($resource)) {

View File

@@ -25,14 +25,8 @@ interface LoaderInterface
/**
* Loads a locale.
*
* @param mixed $resource A resource
* @param string $locale A locale
* @param string $domain The domain
*
* @return MessageCatalogue A MessageCatalogue instance
*
* @throws NotFoundResourceException when the resource cannot be found
* @throws InvalidResourceException when the resource cannot be loaded
*/
public function load($resource, string $locale, string $domain = 'messages');
public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue;
}

View File

@@ -22,13 +22,13 @@ class MoFileLoader extends FileLoader
* Magic used for validating the format of an MO file as well as
* detecting if the machine used to create that file was little endian.
*/
public const MO_LITTLE_ENDIAN_MAGIC = 0x950412de;
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;
public const MO_BIG_ENDIAN_MAGIC = 0xDE120495;
/**
* The size of the header of an MO file in bytes.
@@ -41,7 +41,7 @@ class MoFileLoader extends FileLoader
*
* {@inheritdoc}
*/
protected function loadResource(string $resource)
protected function loadResource(string $resource): array
{
$stream = fopen($resource, 'r');

View File

@@ -18,12 +18,12 @@ namespace Symfony\Component\Translation\Loader;
*/
class PhpFileLoader extends FileLoader
{
private static $cache = [];
private static ?array $cache = [];
/**
* {@inheritdoc}
*/
protected function loadResource(string $resource)
protected function loadResource(string $resource): array
{
if ([] === self::$cache && \function_exists('opcache_invalidate') && filter_var(ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN) && (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) || filter_var(ini_get('opcache.enable_cli'), \FILTER_VALIDATE_BOOLEAN))) {
self::$cache = null;

View File

@@ -60,7 +60,7 @@ class PoFileLoader extends FileLoader
*
* {@inheritdoc}
*/
protected function loadResource(string $resource)
protected function loadResource(string $resource): array
{
$stream = fopen($resource, 'r');

View File

@@ -28,7 +28,7 @@ class QtFileLoader implements LoaderInterface
/**
* {@inheritdoc}
*/
public function load($resource, string $locale, string $domain = 'messages')
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.');

View File

@@ -31,7 +31,7 @@ class XliffFileLoader implements LoaderInterface
/**
* {@inheritdoc}
*/
public function load($resource, string $locale, string $domain = 'messages')
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.');
@@ -57,7 +57,7 @@ class XliffFileLoader implements LoaderInterface
} else {
$dom = XmlUtils::loadFile($resource);
}
} catch (\InvalidArgumentException | XmlParsingException | InvalidXmlException $e) {
} catch (\InvalidArgumentException|XmlParsingException|InvalidXmlException $e) {
throw new InvalidResourceException(sprintf('Unable to load "%s": ', $resource).$e->getMessage(), $e->getCode(), $e);
}

View File

@@ -29,7 +29,7 @@ class YamlFileLoader extends FileLoader
/**
* {@inheritdoc}
*/
protected function loadResource(string $resource)
protected function loadResource(string $resource): array
{
if (null === $this->yamlParser) {
if (!class_exists(\Symfony\Component\Yaml\Parser::class)) {

View File

@@ -21,15 +21,11 @@ use Symfony\Contracts\Translation\TranslatorInterface;
*/
class LoggingTranslator implements TranslatorInterface, TranslatorBagInterface, LocaleAwareInterface
{
/**
* @var TranslatorInterface|TranslatorBagInterface
*/
private $translator;
private $logger;
/**
* @param TranslatorInterface $translator The translator must implement TranslatorBagInterface
* @param TranslatorInterface&TranslatorBagInterface&LocaleAwareInterface $translator The translator must implement TranslatorBagInterface
*/
public function __construct(TranslatorInterface $translator, LoggerInterface $logger)
{
@@ -44,7 +40,7 @@ class LoggingTranslator implements TranslatorInterface, TranslatorBagInterface,
/**
* {@inheritdoc}
*/
public function trans(?string $id, array $parameters = [], string $domain = null, string $locale = null)
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);
@@ -69,7 +65,7 @@ class LoggingTranslator implements TranslatorInterface, TranslatorBagInterface,
/**
* {@inheritdoc}
*/
public function getLocale()
public function getLocale(): string
{
return $this->translator->getLocale();
}
@@ -77,7 +73,7 @@ class LoggingTranslator implements TranslatorInterface, TranslatorBagInterface,
/**
* {@inheritdoc}
*/
public function getCatalogue(string $locale = null)
public function getCatalogue(string $locale = null): MessageCatalogueInterface
{
return $this->translator->getCatalogue($locale);
}
@@ -92,10 +88,8 @@ class LoggingTranslator implements TranslatorInterface, TranslatorBagInterface,
/**
* Gets the fallback locales.
*
* @return array The fallback locales
*/
public function getFallbackLocales()
public function getFallbackLocales(): array
{
if ($this->translator instanceof Translator || method_exists($this->translator, 'getFallbackLocales')) {
return $this->translator->getFallbackLocales();

View File

@@ -19,12 +19,12 @@ use Symfony\Component\Translation\Exception\LogicException;
*/
class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterface
{
private $messages = [];
private $metadata = [];
private $resources = [];
private $locale;
private $fallbackCatalogue;
private $parent;
private array $messages = [];
private array $metadata = [];
private array $resources = [];
private string $locale;
private $fallbackCatalogue = null;
private ?self $parent = null;
/**
* @param array $messages An array of messages classified by domain
@@ -38,7 +38,7 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*/
public function getLocale()
public function getLocale(): string
{
return $this->locale;
}
@@ -46,7 +46,7 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*/
public function getDomains()
public function getDomains(): array
{
$domains = [];
@@ -63,7 +63,7 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*/
public function all(string $domain = null)
public function all(string $domain = null): array
{
if (null !== $domain) {
// skip messages merge if intl-icu requested explicitly
@@ -99,7 +99,7 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*/
public function has(string $id, string $domain = 'messages')
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;
@@ -115,7 +115,7 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*/
public function defines(string $id, string $domain = 'messages')
public function defines(string $id, string $domain = 'messages'): bool
{
return isset($this->messages[$domain][$id]) || isset($this->messages[$domain.self::INTL_DOMAIN_SUFFIX][$id]);
}
@@ -123,7 +123,7 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*/
public function get(string $id, string $domain = 'messages')
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];
@@ -233,7 +233,7 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*/
public function getFallbackCatalogue()
public function getFallbackCatalogue(): ?MessageCatalogueInterface
{
return $this->fallbackCatalogue;
}
@@ -241,7 +241,7 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*/
public function getResources()
public function getResources(): array
{
return array_values($this->resources);
}
@@ -257,7 +257,7 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*/
public function getMetadata(string $key = '', string $domain = 'messages')
public function getMetadata(string $key = '', string $domain = 'messages'): mixed
{
if ('' == $domain) {
return $this->metadata;
@@ -279,7 +279,7 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*/
public function setMetadata(string $key, $value, string $domain = 'messages')
public function setMetadata(string $key, mixed $value, string $domain = 'messages')
{
$this->metadata[$domain][$key] = $value;
}

View File

@@ -24,17 +24,13 @@ interface MessageCatalogueInterface
/**
* Gets the catalogue locale.
*
* @return string The locale
*/
public function getLocale();
public function getLocale(): string;
/**
* Gets the domains.
*
* @return array An array of domains
*/
public function getDomains();
public function getDomains(): array;
/**
* Gets the messages within a given domain.
@@ -42,10 +38,8 @@ interface MessageCatalogueInterface
* If $domain is null, it returns all messages.
*
* @param string $domain The domain name
*
* @return array An array of messages
*/
public function all(string $domain = null);
public function all(string $domain = null): array;
/**
* Sets a message translation.
@@ -61,30 +55,24 @@ interface MessageCatalogueInterface
*
* @param string $id The message id
* @param string $domain The domain name
*
* @return bool true if the message has a translation, false otherwise
*/
public function has(string $id, string $domain = 'messages');
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
*
* @return bool true if the message has a translation, false otherwise
*/
public function defines(string $id, string $domain = 'messages');
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
*
* @return string The message translation
*/
public function get(string $id, string $domain = 'messages');
public function get(string $id, string $domain = 'messages'): string;
/**
* Sets translations for a given domain.
@@ -119,17 +107,15 @@ interface MessageCatalogueInterface
/**
* Gets the fallback catalogue.
*
* @return self|null A MessageCatalogueInterface instance or null when no fallback has been set
*/
public function getFallbackCatalogue();
public function getFallbackCatalogue(): ?self;
/**
* Returns an array of resources loaded to build this collection.
*
* @return ResourceInterface[] An array of resources
* @return ResourceInterface[]
*/
public function getResources();
public function getResources(): array;
/**
* Adds a resource for this collection.

View File

@@ -27,14 +27,12 @@ interface MetadataAwareInterface
*
* @return mixed The value that was set or an array with the domains/keys or null
*/
public function getMetadata(string $key = '', string $domain = 'messages');
public function getMetadata(string $key = '', string $domain = 'messages'): mixed;
/**
* Adds metadata to a message domain.
*
* @param mixed $value
*/
public function setMetadata(string $key, $value, string $domain = 'messages');
public function setMetadata(string $key, mixed $value, string $domain = 'messages');
/**
* Deletes metadata for the given key and domain.

View File

@@ -20,14 +20,14 @@ use Symfony\Component\Translation\Exception\MissingRequiredOptionException;
*/
final class Dsn
{
private $scheme;
private $host;
private $user;
private $password;
private $port;
private $path;
private $options;
private $originalDsn;
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(string $dsn)
{
@@ -79,7 +79,7 @@ final class Dsn
return $this->port ?? $default;
}
public function getOption(string $key, $default = null)
public function getOption(string $key, mixed $default = null)
{
return $this->options[$key] ?? $default;
}

View File

@@ -18,14 +18,12 @@ 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>
*
* @experimental in 5.3
*/
class FilteringProvider implements ProviderInterface
{
private $provider;
private $locales;
private $domains;
private array $locales;
private array $domains;
public function __construct(ProviderInterface $provider, array $locales, array $domains = [])
{

View File

@@ -16,8 +16,6 @@ use Symfony\Component\Translation\TranslatorBagInterface;
/**
* @author Mathieu Santostefano <msantostefano@protonmail.com>
*
* @experimental in 5.3
*/
class NullProvider implements ProviderInterface
{

View File

@@ -15,8 +15,6 @@ use Symfony\Component\Translation\Exception\UnsupportedSchemeException;
/**
* @author Mathieu Santostefano <msantostefano@protonmail.com>
*
* @experimental in 5.3
*/
final class NullProviderFactory extends AbstractProviderFactory
{

View File

@@ -15,11 +15,12 @@ use Symfony\Component\Translation\Exception\InvalidArgumentException;
/**
* @author Mathieu Santostefano <msantostefano@protonmail.com>
*
* @experimental in 5.3
*/
final class TranslationProviderCollection
{
/**
* @var array<string, ProviderInterface>
*/
private $providers;
/**
@@ -27,10 +28,7 @@ final class TranslationProviderCollection
*/
public function __construct(iterable $providers)
{
$this->providers = [];
foreach ($providers as $name => $provider) {
$this->providers[$name] = $provider;
}
$this->providers = \is_array($providers) ? $providers : iterator_to_array($providers);
}
public function __toString(): string

View File

@@ -15,16 +15,14 @@ use Symfony\Component\Translation\Exception\UnsupportedSchemeException;
/**
* @author Mathieu Santostefano <msantostefano@protonmail.com>
*
* @experimental in 5.3
*/
class TranslationProviderCollectionFactory
{
private $factories;
private $enabledLocales;
private iterable $factories;
private array $enabledLocales;
/**
* @param ProviderFactoryInterface[] $factories
* @param iterable<mixed, ProviderFactoryInterface> $factories
*/
public function __construct(iterable $factories, array $enabledLocales)
{

View File

@@ -21,11 +21,15 @@ final class PseudoLocalizationTranslator implements TranslatorInterface
private const EXPANSION_CHARACTER = '~';
private $translator;
private $accents;
private $expansionFactor;
private $brackets;
private $parseHTML;
private $localizableHTMLAttributes;
private bool $accents;
private float $expansionFactor;
private bool $brackets;
private bool $parseHTML;
/**
* @var string[]
*/
private array $localizableHTMLAttributes;
/**
* Available options:
@@ -82,7 +86,7 @@ final class PseudoLocalizationTranslator implements TranslatorInterface
/**
* {@inheritdoc}
*/
public function trans(string $id, array $parameters = [], string $domain = null, string $locale = null)
public function trans(string $id, array $parameters = [], string $domain = null, string $locale = null): string
{
$trans = '';
$visibleText = '';

View File

@@ -23,6 +23,16 @@ $translator->addResource('array', [
echo $translator->trans('Hello World!'); // outputs « Bonjour ! »
```
Sponsor
-------
The Translation component for Symfony 5.4/6.0 is [backed][1] by:
* [Crowdin][2], a cloud-based localization management software helping teams to go global and stay agile.
* [Lokalise][3], a continuous localization and translation management platform that integrates into your development workflow so you can ship localized products, faster.
Help Symfony by [sponsoring][4] its development!
Resources
---------
@@ -31,3 +41,8 @@ Resources
* [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls)
in the [main Symfony repository](https://github.com/symfony/symfony)
[1]: https://symfony.com/backers
[2]: https://crowdin.com
[3]: https://lokalise.com
[4]: https://symfony.com/sponsor

View File

@@ -25,9 +25,9 @@ class TranslationReader implements TranslationReaderInterface
/**
* Loaders used for import.
*
* @var array
* @var array<string, LoaderInterface>
*/
private $loaders = [];
private array $loaders = [];
/**
* Adds a loader to the translation extractor.

View File

@@ -19,13 +19,16 @@ $usageInstructions = <<<END
# show the translation status of all locales
$ php translation-status.php
# show the translation status of all locales and all their missing translations
# only show the translation status of incomplete or erroneous locales
$ php translation-status.php --incomplete
# show the translation status of all locales, all their missing translations and mismatches between trans-unit id and source
$ php translation-status.php -v
# show the status of a single locale
$ php translation-status.php fr
# show the status of a single locale and all its missing translations
# show the status of a single locale, missing translations and mismatches between trans-unit id and source
$ php translation-status.php fr -v
END;
@@ -35,6 +38,8 @@ $config = [
'verbose_output' => false,
// NULL = analyze all locales
'locale_to_analyze' => null,
// append --incomplete to only show incomplete languages
'include_completed_languages' => true,
// the reference files all the other translations are compared to
'original_files' => [
'src/Symfony/Component/Form/Resources/translations/validators.en.xlf',
@@ -46,12 +51,17 @@ $config = [
$argc = $_SERVER['argc'];
$argv = $_SERVER['argv'];
if ($argc > 3) {
if ($argc > 4) {
echo str_replace('translation-status.php', $argv[0], $usageInstructions);
exit(1);
}
foreach (array_slice($argv, 1) as $argumentOrOption) {
if ('--incomplete' === $argumentOrOption) {
$config['include_completed_languages'] = false;
continue;
}
if (str_starts_with($argumentOrOption, '-')) {
$config['verbose_output'] = true;
} else {
@@ -67,6 +77,7 @@ foreach ($config['original_files'] as $originalFilePath) {
}
$totalMissingTranslations = 0;
$totalTranslationMismatches = 0;
foreach ($config['original_files'] as $originalFilePath) {
$translationFilePaths = findTranslationFiles($originalFilePath, $config['locale_to_analyze']);
@@ -75,11 +86,14 @@ foreach ($config['original_files'] as $originalFilePath) {
$totalMissingTranslations += array_sum(array_map(function ($translation) {
return count($translation['missingKeys']);
}, array_values($translationStatus)));
$totalTranslationMismatches += array_sum(array_map(function ($translation) {
return count($translation['mismatches']);
}, array_values($translationStatus)));
printTranslationStatus($originalFilePath, $translationStatus, $config['verbose_output']);
printTranslationStatus($originalFilePath, $translationStatus, $config['verbose_output'], $config['include_completed_languages']);
}
exit($totalMissingTranslations > 0 ? 1 : 0);
exit($totalTranslationMismatches > 0 ? 1 : 0);
function findTranslationFiles($originalFilePath, $localeToAnalyze)
{
@@ -112,21 +126,29 @@ function calculateTranslationStatus($originalFilePath, $translationFilePaths)
foreach ($translationFilePaths as $locale => $translationPath) {
$translatedKeys = extractTranslationKeys($translationPath);
$missingKeys = array_diff_key($allTranslationKeys, $translatedKeys);
$mismatches = findTransUnitMismatches($allTranslationKeys, $translatedKeys);
$translationStatus[$locale] = [
'total' => count($allTranslationKeys),
'translated' => count($translatedKeys),
'missingKeys' => $missingKeys,
'mismatches' => $mismatches,
];
$translationStatus[$locale]['is_completed'] = isTranslationCompleted($translationStatus[$locale]);
}
return $translationStatus;
}
function printTranslationStatus($originalFilePath, $translationStatus, $verboseOutput)
function isTranslationCompleted(array $translationStatus): bool
{
return $translationStatus['total'] === $translationStatus['translated'] && 0 === count($translationStatus['mismatches']);
}
function printTranslationStatus($originalFilePath, $translationStatus, $verboseOutput, $includeCompletedLanguages)
{
printTitle($originalFilePath);
printTable($translationStatus, $verboseOutput);
printTable($translationStatus, $verboseOutput, $includeCompletedLanguages);
echo \PHP_EOL.\PHP_EOL;
}
@@ -152,13 +174,35 @@ function extractTranslationKeys($filePath)
return $translationKeys;
}
/**
* Check whether the trans-unit id and source match with the base translation.
*/
function findTransUnitMismatches(array $baseTranslationKeys, array $translatedKeys): array
{
$mismatches = [];
foreach ($baseTranslationKeys as $translationId => $translationKey) {
if (!isset($translatedKeys[$translationId])) {
continue;
}
if ($translatedKeys[$translationId] !== $translationKey) {
$mismatches[$translationId] = [
'found' => $translatedKeys[$translationId],
'expected' => $translationKey,
];
}
}
return $mismatches;
}
function printTitle($title)
{
echo $title.\PHP_EOL;
echo str_repeat('=', strlen($title)).\PHP_EOL.\PHP_EOL;
}
function printTable($translations, $verboseOutput)
function printTable($translations, $verboseOutput, bool $includeCompletedLanguages)
{
if (0 === count($translations)) {
echo 'No translations found';
@@ -168,24 +212,47 @@ function printTable($translations, $verboseOutput)
$longestLocaleNameLength = max(array_map('strlen', array_keys($translations)));
foreach ($translations as $locale => $translation) {
if (!$includeCompletedLanguages && $translation['is_completed']) {
continue;
}
if ($translation['translated'] > $translation['total']) {
textColorRed();
} elseif ($translation['translated'] === $translation['total']) {
} elseif (count($translation['mismatches']) > 0) {
textColorRed();
} elseif ($translation['is_completed']) {
textColorGreen();
}
echo sprintf('| Locale: %-'.$longestLocaleNameLength.'s | Translated: %d/%d', $locale, $translation['translated'], $translation['total']).\PHP_EOL;
echo sprintf(
'| Locale: %-'.$longestLocaleNameLength.'s | Translated: %2d/%2d | Mismatches: %d |',
$locale,
$translation['translated'],
$translation['total'],
count($translation['mismatches'])
).\PHP_EOL;
textColorNormal();
$shouldBeClosed = false;
if (true === $verboseOutput && count($translation['missingKeys']) > 0) {
echo str_repeat('-', 80).\PHP_EOL;
echo '| Missing Translations:'.\PHP_EOL;
echo '| Missing Translations:'.\PHP_EOL;
foreach ($translation['missingKeys'] as $id => $content) {
echo sprintf('| (id=%s) %s', $id, $content).\PHP_EOL;
echo sprintf('| (id=%s) %s', $id, $content).\PHP_EOL;
}
$shouldBeClosed = true;
}
if (true === $verboseOutput && count($translation['mismatches']) > 0) {
echo '| Mismatches between trans-unit id and source:'.\PHP_EOL;
foreach ($translation['mismatches'] as $id => $content) {
echo sprintf('| (id=%s) Expected: %s', $id, $content['expected']).\PHP_EOL;
echo sprintf('| Found: %s', $content['found']).\PHP_EOL;
}
$shouldBeClosed = true;
}
if ($shouldBeClosed) {
echo str_repeat('-', 80).\PHP_EOL;
}
}

View File

@@ -12,7 +12,6 @@
"en_BS": "en_001",
"en_BW": "en_001",
"en_BZ": "en_001",
"en_CA": "en_001",
"en_CC": "en_001",
"en_CH": "en_150",
"en_CK": "en_001",
@@ -65,7 +64,6 @@
"en_NU": "en_001",
"en_NZ": "en_001",
"en_PG": "en_001",
"en_PH": "en_001",
"en_PK": "en_001",
"en_PN": "en_001",
"en_PW": "en_001",

View File

@@ -33,7 +33,7 @@ abstract class ProviderFactoryTestCase extends TestCase
{
protected $client;
protected $logger;
protected $defaultLocale;
protected string $defaultLocale;
protected $loader;
protected $xliffFileDumper;
@@ -122,26 +122,26 @@ abstract class ProviderFactoryTestCase extends TestCase
protected function getClient(): HttpClientInterface
{
return $this->client ?? $this->client = new MockHttpClient();
return $this->client ??= new MockHttpClient();
}
protected function getLogger(): LoggerInterface
{
return $this->logger ?? $this->logger = $this->createMock(LoggerInterface::class);
return $this->logger ??= $this->createMock(LoggerInterface::class);
}
protected function getDefaultLocale(): string
{
return $this->defaultLocale ?? $this->defaultLocale = 'en';
return $this->defaultLocale ??= 'en';
}
protected function getLoader(): LoaderInterface
{
return $this->loader ?? $this->loader = $this->createMock(LoaderInterface::class);
return $this->loader ??= $this->createMock(LoaderInterface::class);
}
protected function getXliffFileDumper(): XliffFileDumper
{
return $this->xliffFileDumper ?? $this->xliffFileDumper = $this->createMock(XliffFileDumper::class);
return $this->xliffFileDumper ??= $this->createMock(XliffFileDumper::class);
}
}

View File

@@ -11,7 +11,6 @@
namespace Symfony\Component\Translation\Test;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpClient\MockHttpClient;
@@ -31,7 +30,7 @@ abstract class ProviderTestCase extends TestCase
{
protected $client;
protected $logger;
protected $defaultLocale;
protected string $defaultLocale;
protected $loader;
protected $xliffFileDumper;
@@ -52,35 +51,26 @@ abstract class ProviderTestCase extends TestCase
protected function getClient(): MockHttpClient
{
return $this->client ?? $this->client = new MockHttpClient();
return $this->client ??= new MockHttpClient();
}
/**
* @return LoaderInterface&MockObject
*/
protected function getLoader(): LoaderInterface
{
return $this->loader ?? $this->loader = $this->createMock(LoaderInterface::class);
return $this->loader ??= $this->createMock(LoaderInterface::class);
}
/**
* @return LoaderInterface&MockObject
*/
protected function getLogger(): LoggerInterface
{
return $this->logger ?? $this->logger = $this->createMock(LoggerInterface::class);
return $this->logger ??= $this->createMock(LoggerInterface::class);
}
protected function getDefaultLocale(): string
{
return $this->defaultLocale ?? $this->defaultLocale = 'en';
return $this->defaultLocale ??= 'en';
}
/**
* @return LoaderInterface&MockObject
*/
protected function getXliffFileDumper(): XliffFileDumper
{
return $this->xliffFileDumper ?? $this->xliffFileDumper = $this->createMock(XliffFileDumper::class);
return $this->xliffFileDumper ??= $this->createMock(XliffFileDumper::class);
}
}

View File

@@ -19,9 +19,9 @@ use Symfony\Contracts\Translation\TranslatorInterface;
*/
class TranslatableMessage implements TranslatableInterface
{
private $message;
private $parameters;
private $domain;
private string $message;
private array $parameters;
private ?string $domain;
public function __construct(string $message, array $parameters = [], string $domain = null)
{
@@ -52,6 +52,11 @@ class TranslatableMessage implements TranslatableInterface
public function trans(TranslatorInterface $translator, string $locale = null): string
{
return $translator->trans($this->getMessage(), $this->getParameters(), $this->getDomain(), $locale);
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);
}
}

View File

@@ -37,54 +37,33 @@ class Translator implements TranslatorInterface, TranslatorBagInterface, LocaleA
*/
protected $catalogues = [];
/**
* @var string
*/
private $locale;
private string $locale;
/**
* @var array
* @var string[]
*/
private $fallbackLocales = [];
private array $fallbackLocales = [];
/**
* @var LoaderInterface[]
*/
private $loaders = [];
private array $loaders = [];
/**
* @var array
*/
private $resources = [];
private array $resources = [];
/**
* @var MessageFormatterInterface
*/
private $formatter;
/**
* @var string
*/
private $cacheDir;
private ?string $cacheDir;
/**
* @var bool
*/
private $debug;
private bool $debug;
private $cacheVary;
private array $cacheVary;
/**
* @var ConfigCacheFactoryInterface|null
*/
private $configCacheFactory;
/**
* @var array|null
*/
private $parentLocales;
private array $parentLocales;
private $hasIntlFormatter;
private bool $hasIntlFormatter;
/**
* @throws InvalidArgumentException If a locale contains invalid characters
@@ -127,7 +106,7 @@ class Translator implements TranslatorInterface, TranslatorBagInterface, LocaleA
*
* @throws InvalidArgumentException If the locale contains invalid characters
*/
public function addResource(string $format, $resource, string $locale, string $domain = null)
public function addResource(string $format, mixed $resource, string $locale, string $domain = null)
{
if (null === $domain) {
$domain = 'messages';
@@ -157,7 +136,7 @@ class Translator implements TranslatorInterface, TranslatorBagInterface, LocaleA
/**
* {@inheritdoc}
*/
public function getLocale()
public function getLocale(): string
{
return $this->locale ?: (class_exists(\Locale::class) ? \Locale::getDefault() : 'en');
}
@@ -165,6 +144,8 @@ class Translator implements TranslatorInterface, TranslatorBagInterface, LocaleA
/**
* Sets the fallback locales.
*
* @param string[] $locales
*
* @throws InvalidArgumentException If a locale contains invalid characters
*/
public function setFallbackLocales(array $locales)
@@ -192,7 +173,7 @@ class Translator implements TranslatorInterface, TranslatorBagInterface, LocaleA
/**
* {@inheritdoc}
*/
public function trans(?string $id, array $parameters = [], string $domain = null, string $locale = null)
public function trans(?string $id, array $parameters = [], string $domain = null, string $locale = null): string
{
if (null === $id || '' === $id) {
return '';
@@ -227,7 +208,7 @@ class Translator implements TranslatorInterface, TranslatorBagInterface, LocaleA
/**
* {@inheritdoc}
*/
public function getCatalogue(string $locale = null)
public function getCatalogue(string $locale = null): MessageCatalogueInterface
{
if (!$locale) {
$locale = $this->getLocale();
@@ -253,9 +234,9 @@ class Translator implements TranslatorInterface, TranslatorBagInterface, LocaleA
/**
* Gets the loaders.
*
* @return array LoaderInterface[]
* @return LoaderInterface[]
*/
protected function getLoaders()
protected function getLoaders(): array
{
return $this->loaders;
}
@@ -407,18 +388,10 @@ EOF
protected function computeFallbackLocales(string $locale)
{
if (null === $this->parentLocales) {
$this->parentLocales = json_decode(file_get_contents(__DIR__.'/Resources/data/parents.json'), true);
}
$this->parentLocales ??= json_decode(file_get_contents(__DIR__.'/Resources/data/parents.json'), true);
$originLocale = $locale;
$locales = [];
foreach ($this->fallbackLocales as $fallback) {
if ($fallback === $locale) {
continue;
}
$locales[] = $fallback;
}
while ($locale) {
$parent = $this->parentLocales[$locale] ?? null;
@@ -439,10 +412,18 @@ EOF
}
if (null !== $locale) {
array_unshift($locales, $locale);
$locales[] = $locale;
}
}
foreach ($this->fallbackLocales as $fallback) {
if ($fallback === $originLocale) {
continue;
}
$locales[] = $fallback;
}
return array_unique($locales);
}
@@ -453,7 +434,7 @@ EOF
*/
protected function assertValidLocale(string $locale)
{
if (!preg_match('/^[a-z0-9@_\\.\\-]*$/i', (string) $locale)) {
if (!preg_match('/^[a-z0-9@_\\.\\-]*$/i', $locale)) {
throw new InvalidArgumentException(sprintf('Invalid "%s" locale.', $locale));
}
}
@@ -464,9 +445,7 @@ EOF
*/
private function getConfigCacheFactory(): ConfigCacheFactoryInterface
{
if (!$this->configCacheFactory) {
$this->configCacheFactory = new ConfigCacheFactory($this->debug);
}
$this->configCacheFactory ??= new ConfigCacheFactory($this->debug);
return $this->configCacheFactory;
}

View File

@@ -17,7 +17,7 @@ use Symfony\Component\Translation\Catalogue\TargetOperation;
final class TranslatorBag implements TranslatorBagInterface
{
/** @var MessageCatalogue[] */
private $catalogues = [];
private array $catalogues = [];
public function addCatalogue(MessageCatalogue $catalogue): void
{
@@ -38,7 +38,7 @@ final class TranslatorBag implements TranslatorBagInterface
/**
* {@inheritdoc}
*/
public function getCatalogue(string $locale = null)
public function getCatalogue(string $locale = null): MessageCatalogueInterface
{
if (null === $locale || !isset($this->catalogues[$locale])) {
$this->catalogues[$locale] = new MessageCatalogue($locale);

View File

@@ -14,10 +14,6 @@ namespace Symfony\Component\Translation;
use Symfony\Component\Translation\Exception\InvalidArgumentException;
/**
* TranslatorBagInterface.
*
* @method MessageCatalogueInterface[] getCatalogues() Returns all catalogues of the instance
*
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
*/
interface TranslatorBagInterface
@@ -27,9 +23,14 @@ interface TranslatorBagInterface
*
* @param string|null $locale The locale or null to use the default
*
* @return MessageCatalogueInterface
*
* @throws InvalidArgumentException If the locale contains invalid characters
*/
public function getCatalogue(string $locale = null);
public function getCatalogue(string $locale = null): MessageCatalogueInterface;
/**
* Returns all catalogues of the instance.
*
* @return MessageCatalogueInterface[]
*/
public function getCatalogues(): array;
}

View File

@@ -30,10 +30,8 @@ class ArrayConverter
* For example this array('foo.bar' => 'value') will be converted to ['foo' => ['bar' => 'value']].
*
* @param array $messages Linear messages array
*
* @return array Tree-like messages array
*/
public static function expandToTree(array $messages)
public static function expandToTree(array $messages): array
{
$tree = [];

View File

@@ -85,11 +85,6 @@ class XliffUtils
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();

View File

@@ -23,7 +23,10 @@ use Symfony\Component\Translation\MessageCatalogue;
*/
class TranslationWriter implements TranslationWriterInterface
{
private $dumpers = [];
/**
* @var array<string, DumperInterface>
*/
private array $dumpers = [];
/**
* Adds a dumper to the writer.
@@ -35,10 +38,8 @@ class TranslationWriter implements TranslationWriterInterface
/**
* Obtains the list of supported formats.
*
* @return array
*/
public function getFormats()
public function getFormats(): array
{
return array_keys($this->dumpers);
}

View File

@@ -16,33 +16,33 @@
}
],
"require": {
"php": ">=7.2.5",
"symfony/deprecation-contracts": "^2.1",
"php": ">=8.0.2",
"symfony/polyfill-mbstring": "~1.0",
"symfony/polyfill-php80": "^1.16",
"symfony/translation-contracts": "^2.3"
"symfony/translation-contracts": "^2.3|^3.0"
},
"require-dev": {
"symfony/config": "^4.4|^5.0",
"symfony/console": "^4.4|^5.0",
"symfony/dependency-injection": "^5.0",
"symfony/http-kernel": "^5.0",
"symfony/intl": "^4.4|^5.0",
"symfony/config": "^5.4|^6.0",
"symfony/console": "^5.4|^6.0",
"symfony/dependency-injection": "^5.4|^6.0",
"symfony/http-client-contracts": "^1.1|^2.0|^3.0",
"symfony/http-kernel": "^5.4|^6.0",
"symfony/intl": "^5.4|^6.0",
"symfony/polyfill-intl-icu": "^1.21",
"symfony/service-contracts": "^1.1.2|^2",
"symfony/yaml": "^4.4|^5.0",
"symfony/finder": "^4.4|^5.0",
"symfony/service-contracts": "^1.1.2|^2|^3",
"symfony/yaml": "^5.4|^6.0",
"symfony/finder": "^5.4|^6.0",
"psr/log": "^1|^2|^3"
},
"conflict": {
"symfony/config": "<4.4",
"symfony/dependency-injection": "<5.0",
"symfony/http-kernel": "<5.0",
"symfony/twig-bundle": "<5.0",
"symfony/yaml": "<4.4"
"symfony/config": "<5.4",
"symfony/dependency-injection": "<5.4",
"symfony/http-kernel": "<5.4",
"symfony/twig-bundle": "<5.4",
"symfony/yaml": "<5.4",
"symfony/console": "<5.4"
},
"provide": {
"symfony/translation-implementation": "2.3"
"symfony/translation-implementation": "2.3|3.0"
},
"suggest": {
"symfony/config": "",