[Web] Update composer libraries

This commit is contained in:
FreddleSpl0it committed 2026-10-28 11:56:29 +01:00
1 parent 9cc8ac2908
commit bd8c784118
1297 files changed
+71585 -24460

No files matched your search

@@ -0,0 +1,129 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Translation\Command;
use Symfony\Component\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\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Translation\Exception\ExceptionInterface;
use Symfony\Component\Translation\TranslatorBagInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* Lint translations files syntax and outputs encountered errors.
*
* @author Hugo Alliaume <hugo@alliau.me>
*/
#[AsCommand(name: 'lint:translations', description: 'Lint translations files syntax and outputs encountered errors')]
class TranslationLintCommand extends Command
{
private SymfonyStyle $io;
public function __construct(
private TranslatorInterface&TranslatorBagInterface $translator,
private array $enabledLocales = [],
) {
parent::__construct();
}
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
if ($input->mustSuggestOptionValuesFor('locale')) {
$suggestions->suggestValues($this->enabledLocales);
}
}
protected function configure(): void
{
$this
->setDefinition([
new InputOption('locale', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Specify the locales to lint.', $this->enabledLocales),
])
->setHelp(<<<'EOF'
The <info>%command.name%</> command lint translations.
<info>php %command.full_name%</>
EOF
);
}
protected function initialize(InputInterface $input, OutputInterface $output): void
{
$this->io = new SymfonyStyle($input, $output);
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$locales = $input->getOption('locale');
/** @var array<string, array<string, array<string, \Throwable>>> $errors */
$errors = [];
$domainsByLocales = [];
foreach ($locales as $locale) {
$messageCatalogue = $this->translator->getCatalogue($locale);
foreach ($domainsByLocales[$locale] = $messageCatalogue->getDomains() as $domain) {
foreach ($messageCatalogue->all($domain) as $id => $translation) {
try {
$this->translator->trans($id, [], $domain, $messageCatalogue->getLocale());
} catch (ExceptionInterface $e) {
$errors[$locale][$domain][$id] = $e;
}
}
}
}
if (!$domainsByLocales) {
$this->io->error('No translation files were found.');
return Command::SUCCESS;
}
$this->io->table(
['Locale', 'Domains', 'Valid?'],
array_map(
static fn (string $locale, array $domains) => [
$locale,
implode(', ', $domains),
!\array_key_exists($locale, $errors) ? '<info>Yes</>' : '<error>No</>',
],
array_keys($domainsByLocales),
$domainsByLocales
),
);
if ($errors) {
foreach ($errors as $locale => $domains) {
foreach ($domains as $domain => $domainsErrors) {
$this->io->section(\sprintf('Errors for locale "%s" and domain "%s"', $locale, $domain));
foreach ($domainsErrors as $id => $error) {
$this->io->text(\sprintf('Translation key "%s" is invalid:', $id));
$this->io->error($error->getMessage());
}
}
}
return Command::FAILURE;
}
$this->io->success('All translations are valid.');
return Command::SUCCESS;
}
}
@@ -34,22 +34,14 @@ final class TranslationPullCommand extends Command
{
use TranslationTrait;
private TranslationProviderCollection $providerCollection;
private TranslationWriterInterface $writer;
private TranslationReaderInterface $reader;
private string $defaultLocale;
private array $transPaths;
private array $enabledLocales;
public function __construct(TranslationProviderCollection $providerCollection, TranslationWriterInterface $writer, TranslationReaderInterface $reader, string $defaultLocale, array $transPaths = [], array $enabledLocales = [])
{
$this->providerCollection = $providerCollection;
$this->writer = $writer;
$this->reader = $reader;
$this->defaultLocale = $defaultLocale;
$this->transPaths = $transPaths;
$this->enabledLocales = $enabledLocales;
public function __construct(
private TranslationProviderCollection $providerCollection,
private TranslationWriterInterface $writer,
private TranslationReaderInterface $reader,
private string $defaultLocale,
private array $transPaths = [],
private array $enabledLocales = [],
) {
parent::__construct();
}
@@ -92,27 +84,28 @@ final class TranslationPullCommand extends Command
new InputArgument('provider', null !== $defaultProvider ? InputArgument::OPTIONAL : InputArgument::REQUIRED, 'The provider to pull translations from.', $defaultProvider),
new InputOption('force', null, InputOption::VALUE_NONE, 'Override existing translations with provider ones (it will delete not synchronized messages).'),
new InputOption('intl-icu', null, InputOption::VALUE_NONE, 'Associated to --force option, it will write messages in "%domain%+intl-icu.%locale%.xlf" files.'),
new InputOption('domains', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Specify the domains to pull.'),
new InputOption('locales', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Specify the locales to pull.'),
new InputOption('format', null, InputOption::VALUE_OPTIONAL, 'Override the default output format.', 'xlf12'),
new InputOption('as-tree', null, InputOption::VALUE_OPTIONAL, 'Write messages as a tree-like structure. Needs --format=yaml. The given value defines the level where to switch to inline YAML'),
new InputOption('domains', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Specify the domains to pull.'),
new InputOption('locales', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Specify the locales to pull.'),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'Override the default output format.', 'xlf12'),
new InputOption('as-tree', null, InputOption::VALUE_REQUIRED, 'Write messages as a tree-like structure. Needs --format=yaml. The given value defines the level where to switch to inline YAML'),
])
->setHelp(<<<'EOF'
The <info>%command.name%</> command pulls translations from the given provider. Only
new translations are pulled, existing ones are not overwritten.
The <info>%command.name%</> command pulls translations from the given provider. Only
new translations are pulled, existing ones are not overwritten.
You can overwrite existing translations (and remove the missing ones on local side) by using the <comment>--force</> flag:
You can overwrite existing translations (and remove the missing ones on local side) by using the <info>--force</> flag:
<info>php %command.full_name% --force provider</>
<info>php %command.full_name% --force provider</>
Full example:
Full example:
<info>php %command.full_name% provider --force --domains=messages --domains=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.
Local translations for others domains and locales are ignored.
EOF
This command pulls all translations associated with the <info>messages</> and <info>validators</> domains for the <info>en</> locale.
Local translations for the specified domains and locale are deleted if they're not present on the provider and overwritten if it's the case.
Local translations for others domains and locales are ignored.
EOF
)
;
}
@@ -163,7 +156,7 @@ EOF
$this->writer->write($operation->getResult(), $format, $writeOptions);
}
$io->success(sprintf('Local translations has been updated from "%s" (for "%s" locale(s), and "%s" domain(s)).', parse_url($provider, \PHP_URL_SCHEME), implode(', ', $locales), implode(', ', $domains)));
$io->success(\sprintf('Local translations has been updated from "%s" (for "%s" locale(s), and "%s" domain(s)).', parse_url($provider, \PHP_URL_SCHEME), implode(', ', $locales), implode(', ', $domains)));
return 0;
}
@@ -177,7 +170,7 @@ EOF
$this->writer->write($catalogue, $format, $writeOptions);
}
$io->success(sprintf('New translations from "%s" has been written locally (for "%s" locale(s), and "%s" domain(s)).', parse_url($provider, \PHP_URL_SCHEME), implode(', ', $locales), implode(', ', $domains)));
$io->success(\sprintf('New translations from "%s" has been written locally (for "%s" locale(s), and "%s" domain(s)).', parse_url($provider, \PHP_URL_SCHEME), implode(', ', $locales), implode(', ', $domains)));
return 0;
}
@@ -34,18 +34,12 @@ final class TranslationPushCommand extends Command
{
use TranslationTrait;
private TranslationProviderCollection $providers;
private TranslationReaderInterface $reader;
private array $transPaths;
private array $enabledLocales;
public function __construct(TranslationProviderCollection $providers, TranslationReaderInterface $reader, array $transPaths = [], array $enabledLocales = [])
{
$this->providers = $providers;
$this->reader = $reader;
$this->transPaths = $transPaths;
$this->enabledLocales = $enabledLocales;
public function __construct(
private TranslationProviderCollection $providers,
private TranslationReaderInterface $reader,
private array $transPaths = [],
private array $enabledLocales = [],
) {
parent::__construct();
}
@@ -60,7 +54,7 @@ final class TranslationPushCommand extends Command
if ($input->mustSuggestOptionValuesFor('domains')) {
$provider = $this->providers->get($input->getArgument('provider'));
if ($provider && method_exists($provider, 'getDomains')) {
if (method_exists($provider, 'getDomains')) {
$domains = $provider->getDomains();
$suggestions->suggestValues($domains);
}
@@ -83,29 +77,30 @@ final class TranslationPushCommand extends Command
new InputArgument('provider', null !== $defaultProvider ? InputArgument::OPTIONAL : InputArgument::REQUIRED, 'The provider to push translations to.', $defaultProvider),
new InputOption('force', null, InputOption::VALUE_NONE, 'Override existing translations with local ones (it will delete not synchronized messages).'),
new InputOption('delete-missing', null, InputOption::VALUE_NONE, 'Delete translations available on provider but not locally.'),
new InputOption('domains', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Specify the domains to push.'),
new InputOption('locales', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Specify the locales to push.', $this->enabledLocales),
new InputOption('domains', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Specify the domains to push.'),
new InputOption('locales', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Specify the locales to push.', $this->enabledLocales),
])
->setHelp(<<<'EOF'
The <info>%command.name%</> command pushes translations to the given provider. Only new
translations are pushed, existing ones are not overwritten.
The <info>%command.name%</> command pushes translations to the given provider. Only new
translations are pushed, existing ones are not overwritten.
You can overwrite existing translations by using the <comment>--force</> flag:
You can overwrite existing translations by using the <info>--force</> flag:
<info>php %command.full_name% --force provider</>
<info>php %command.full_name% --force provider</>
You can delete provider translations which are not present locally by using the <comment>--delete-missing</> flag:
You can delete provider translations which are not present locally by using the <info>--delete-missing</> flag:
<info>php %command.full_name% --delete-missing provider</>
<info>php %command.full_name% --delete-missing provider</>
Full example:
Full example:
<info>php %command.full_name% provider --force --delete-missing --domains=messages --domains=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.
Provider translations for others domains and locales are ignored.
EOF
This command pushes all translations associated with the <info>messages</> and <info>validators</> domains for the <info>en</> locale.
Provider translations for the specified domains and locale are deleted if they're not present locally and overwritten if it's the case.
Provider translations for others domains and locales are ignored.
EOF
)
;
}
@@ -115,7 +110,7 @@ EOF
$provider = $this->providers->get($input->getArgument('provider'));
if (!$this->enabledLocales) {
throw new InvalidArgumentException(sprintf('You must define "framework.enabled_locales" or "framework.translator.providers.%s.locales" config key in order to work with translation providers.', parse_url($provider, \PHP_URL_SCHEME)));
throw new InvalidArgumentException(\sprintf('You must define "framework.enabled_locales" or "framework.translator.providers.%s.locales" config key in order to work with translation providers.', parse_url($provider, \PHP_URL_SCHEME)));
}
$io = new SymfonyStyle($input, $output);
@@ -139,7 +134,7 @@ EOF
if (!$deleteMissing && $force) {
$provider->write($localTranslations);
$io->success(sprintf('All local translations has been sent to "%s" (for "%s" locale(s), and "%s" domain(s)).', parse_url($provider, \PHP_URL_SCHEME), implode(', ', $locales), implode(', ', $domains)));
$io->success(\sprintf('All local translations has been sent to "%s" (for "%s" locale(s), and "%s" domain(s)).', parse_url($provider, \PHP_URL_SCHEME), implode(', ', $locales), implode(', ', $domains)));
return 0;
}
@@ -149,7 +144,7 @@ EOF
if ($deleteMissing) {
$provider->delete($providerTranslations->diff($localTranslations));
$io->success(sprintf('Missing translations on "%s" has been deleted (for "%s" locale(s), and "%s" domain(s)).', parse_url($provider, \PHP_URL_SCHEME), implode(', ', $locales), implode(', ', $domains)));
$io->success(\sprintf('Missing translations on "%s" has been deleted (for "%s" locale(s), and "%s" domain(s)).', parse_url($provider, \PHP_URL_SCHEME), implode(', ', $locales), implode(', ', $domains)));
// Read provider translations again, after missing translations deletion,
// to avoid push freshly deleted translations.
@@ -164,7 +159,7 @@ EOF
$provider->write($translationsToWrite);
$io->success(sprintf('%s local translations has been sent to "%s" (for "%s" locale(s), and "%s" domain(s)).', $force ? 'All' : 'New', parse_url($provider, \PHP_URL_SCHEME), implode(', ', $locales), implode(', ', $domains)));
$io->success(\sprintf('%s local translations has been sent to "%s" (for "%s" locale(s), and "%s" domain(s)).', $force ? 'All' : 'New', parse_url($provider, \PHP_URL_SCHEME), implode(', ', $locales), implode(', ', $domains)));
return 0;
}
@@ -174,9 +169,9 @@ EOF
$domains = [];
foreach ($translatorBag->getCatalogues() as $catalogue) {
$domains += $catalogue->getDomains();
$domains = array_merge($domains, $catalogue->getDomains());
}
return array_unique($domains);
return array_values(array_unique($domains));
}
}
@@ -39,43 +39,45 @@ class XliffLintCommand extends Command
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)
{
public function __construct(
?string $name = null,
?callable $directoryIteratorProvider = null,
?callable $isReadableProvider = null,
private bool $requireStrictFileNames = true,
) {
parent::__construct($name);
$this->directoryIteratorProvider = null === $directoryIteratorProvider ? null : $directoryIteratorProvider(...);
$this->isReadableProvider = null === $isReadableProvider ? null : $isReadableProvider(...);
$this->requireStrictFileNames = $requireStrictFileNames;
}
/**
* @return void
*/
protected function configure()
protected function configure(): void
{
$this
->addArgument('filename', InputArgument::IS_ARRAY, 'A file, a directory or "-" for reading from STDIN')
->addOption('format', null, InputOption::VALUE_REQUIRED, sprintf('The output format ("%s")', implode('", "', $this->getAvailableFormatOptions())))
->addOption('format', null, InputOption::VALUE_REQUIRED, \sprintf('The output format ("%s")', implode('", "', $this->getAvailableFormatOptions())))
->setHelp(<<<EOF
The <info>%command.name%</info> command lints an XLIFF file and outputs to STDOUT
the first encountered syntax error.
The <info>%command.name%</info> command lints an XLIFF file and outputs to STDOUT
the first encountered syntax error.
You can validates XLIFF contents passed from STDIN:
You can validates XLIFF contents passed from STDIN:
<info>cat filename | php %command.full_name% -</info>
<info>cat filename | php %command.full_name% -</info>
You can also validate the syntax of a file:
You can also validate the syntax of a file:
<info>php %command.full_name% filename</info>
<info>php %command.full_name% filename</info>
Or of a whole directory:
Or of a whole directory:
<info>php %command.full_name% dirname</info>
<info>php %command.full_name% dirname --format=json</info>
<info>php %command.full_name% dirname</info>
EOF
The <info>--format</info> option specifies the format of the command output:
<info>php %command.full_name% dirname --format=json</info>
EOF
)
;
}
@@ -98,7 +100,7 @@ EOF
$filesInfo = [];
foreach ($filenames as $filename) {
if (!$this->isReadable($filename)) {
throw new RuntimeException(sprintf('File or directory "%s" is not readable.', $filename));
throw new RuntimeException(\sprintf('File or directory "%s" is not readable.', $filename));
}
foreach ($this->getFiles($filename) as $file) {
@@ -124,18 +126,18 @@ EOF
$document->loadXML($content);
if (null !== $targetLanguage = $this->getTargetLanguageFromFile($document)) {
$normalizedLocalePattern = sprintf('(%s|%s)', preg_quote($targetLanguage, '/'), preg_quote(str_replace('-', '_', $targetLanguage), '/'));
$normalizedLocalePattern = \sprintf('(%s|%s)', preg_quote($targetLanguage, '/'), preg_quote(str_replace('-', '_', $targetLanguage), '/'));
// strict file names require translation files to be named '____.locale.xlf'
// otherwise, both '____.locale.xlf' and 'locale.____.xlf' are allowed
// also, the regexp matching must be case-insensitive, as defined for 'target-language' values
// http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#target-language
$expectedFilenamePattern = $this->requireStrictFileNames ? sprintf('/^.*\.(?i:%s)\.(?:xlf|xliff)/', $normalizedLocalePattern) : sprintf('/^(?:.*\.(?i:%s)|(?i:%s)\..*)\.(?:xlf|xliff)/', $normalizedLocalePattern, $normalizedLocalePattern);
$expectedFilenamePattern = $this->requireStrictFileNames ? \sprintf('/^.*\.(?i:%s)\.(?:xlf|xliff)/', $normalizedLocalePattern) : \sprintf('/^(?:.*\.(?i:%s)|(?i:%s)\..*)\.(?:xlf|xliff)/', $normalizedLocalePattern, $normalizedLocalePattern);
if (0 === preg_match($expectedFilenamePattern, basename($file))) {
$errors[] = [
'line' => -1,
'column' => -1,
'message' => sprintf('There is a mismatch between the language included in the file name ("%s") and the "%s" value used in the "target-language" attribute of the file.', basename($file), $targetLanguage),
'message' => \sprintf('There is a mismatch between the language included in the file name ("%s") and the "%s" value used in the "target-language" attribute of the file.', basename($file), $targetLanguage),
];
}
}
@@ -151,7 +153,7 @@ EOF
libxml_clear_errors();
libxml_use_internal_errors($internal);
return ['file' => $file, 'valid' => 0 === \count($errors), 'messages' => $errors];
return ['file' => $file, 'valid' => !$errors, 'messages' => $errors];
}
private function display(SymfonyStyle $io, array $files): int
@@ -160,7 +162,7 @@ EOF
'txt' => $this->displayTxt($io, $files),
'json' => $this->displayJson($io, $files),
'github' => $this->displayTxt($io, $files, true),
default => throw new InvalidArgumentException(sprintf('Supported formats are "%s".', implode('", "', $this->getAvailableFormatOptions()))),
default => throw new InvalidArgumentException(\sprintf('Supported formats are "%s".', implode('", "', $this->getAvailableFormatOptions()))),
};
}
@@ -172,25 +174,25 @@ EOF
foreach ($filesInfo as $info) {
if ($info['valid'] && $this->displayCorrectFiles) {
$io->comment('<info>OK</info>'.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
$io->comment('<info>OK</info>'.($info['file'] ? \sprintf(' in %s', $info['file']) : ''));
} elseif (!$info['valid']) {
++$erroredFiles;
$io->text('<error> ERROR </error>'.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
$io->listing(array_map(function ($error) use ($info, $githubReporter) {
$io->text('<error> ERROR </error>'.($info['file'] ? \sprintf(' in %s', $info['file']) : ''));
$io->listing(array_map(static function ($error) use ($info, $githubReporter) {
// general document errors have a '-1' line number
$line = -1 === $error['line'] ? null : $error['line'];
$githubReporter?->error($error['message'], $info['file'], $line, null !== $line ? $error['column'] : null);
return null === $line ? $error['message'] : sprintf('Line %d, Column %d: %s', $line, $error['column'], $error['message']);
return null === $line ? $error['message'] : \sprintf('Line %d, Column %d: %s', $line, $error['column'], $error['message']);
}, $info['messages']));
}
}
if (0 === $erroredFiles) {
$io->success(sprintf('All %d XLIFF files contain valid syntax.', $countFiles));
$io->success(\sprintf('All %d XLIFF files contain valid syntax.', $countFiles));
} else {
$io->warning(sprintf('%d XLIFF files have valid syntax and %d contain errors.', $countFiles - $erroredFiles, $erroredFiles));
$io->warning(\sprintf('%d XLIFF files have valid syntax and %d contain errors.', $countFiles - $erroredFiles, $erroredFiles));
}
return min($erroredFiles, 1);
@@ -200,7 +202,7 @@ EOF
{
$errors = 0;
array_walk($filesInfo, function (&$v) use (&$errors) {
array_walk($filesInfo, static function (&$v) use (&$errors) {
$v['file'] = (string) $v['file'];
if (!$v['valid']) {
++$errors;
@@ -224,7 +226,7 @@ EOF
}
foreach ($this->getDirectoryIterator($fileOrDirectory) as $file) {
if (!\in_array($file->getExtension(), ['xlf', 'xliff'])) {
if (!\in_array($file->getExtension(), ['xlf', 'xliff'], true)) {
continue;
}
@@ -237,7 +239,7 @@ EOF
*/
private function getDirectoryIterator(string $directory): iterable
{
$default = fn ($directory) => new \RecursiveIteratorIterator(
$default = static fn ($directory) => new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS),
\RecursiveIteratorIterator::LEAVES_ONLY
);
@@ -251,7 +253,7 @@ EOF
private function isReadable(string $fileOrDirectory): bool
{
$default = fn ($fileOrDirectory) => is_readable($fileOrDirectory);
$default = static fn ($fileOrDirectory) => is_readable($fileOrDirectory);
if (null !== $this->isReadableProvider) {
return ($this->isReadableProvider)($fileOrDirectory, $default);
@@ -278,6 +280,7 @@ EOF
}
}
/** @return string[] */
private function getAvailableFormatOptions(): array
{
return ['txt', 'json', 'github'];