Skip to content

[Translation] Adding Translation Providers #38475

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions UPGRADE-5.3.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ FrameworkBundle
* Deprecate the `KernelTestCase::$container` property, use `KernelTestCase::getContainer()` instead
* Rename the container parameter `profiler_listener.only_master_requests` to `profiler_listener.only_main_requests`
* Deprecate registering workflow services as public
* Deprecate option `--xliff-version` of the `translation:update` command, use e.g. `--format=xlf20` instead
* Deprecate option `--output-format` of the `translation:update` command, use e.g. `--format=xlf20` instead

HttpFoundation
--------------
Expand Down
2 changes: 2 additions & 0 deletions UPGRADE-6.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ FrameworkBundle
* Removed the `lock.RESOURCE_NAME` and `lock.RESOURCE_NAME.store` services and the `lock`, `LockInterface`, `lock.store` and `PersistingStoreInterface` aliases, use `lock.RESOURCE_NAME.factory`, `lock.factory` or `LockFactory` instead.
* Remove the `KernelTestCase::$container` property, use `KernelTestCase::getContainer()` instead
* Registered workflow services are now private
* Remove option `--xliff-version` of the `translation:update` command, use e.g. `--output-format=xlf20` instead
* Remove option `--output-format` of the `translation:update` command, use e.g. `--output-format=xlf20` instead

HttpFoundation
--------------
Expand Down
2 changes: 1 addition & 1 deletion link
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ if (!is_dir("$pathToProject/vendor/symfony")) {
$sfPackages = array('symfony/symfony' => __DIR__);

$filesystem = new Filesystem();
$braces = array('Bundle', 'Bridge', 'Component', 'Component/Security', 'Component/Mailer/Bridge', 'Component/Messenger/Bridge', 'Component/Notifier/Bridge', 'Contracts');
$braces = array('Bundle', 'Bridge', 'Component', 'Component/Security', 'Component/Mailer/Bridge', 'Component/Messenger/Bridge', 'Component/Notifier/Bridge', 'Contracts', 'Component/Translation/Bridge');
$directories = array_merge(...array_values(array_map(function ($part) {
return glob(__DIR__.'/src/Symfony/'.$part.'/*', GLOB_ONLYDIR | GLOB_NOSORT);
}, $braces)));
Expand Down
2 changes: 2 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ CHANGELOG
* Rename the container parameter `profiler_listener.only_master_requests` to `profiler_listener.only_main_requests`
* Add service `fragment.uri_generator` to generate the URI of a fragment
* Deprecate registering workflow services as public
* Deprecate option `--xliff-version` of the `translation:update` command, use e.g. `--format=xlf20` instead
* Deprecate option `--output-format` of the `translation:update` command, use e.g. `--format=xlf20` instead

5.2.0
-----
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,13 @@ protected function configure()
new InputArgument('locale', InputArgument::REQUIRED, 'The locale'),
new InputArgument('bundle', InputArgument::OPTIONAL, 'The bundle name or directory where to load the messages'),
new InputOption('prefix', null, InputOption::VALUE_OPTIONAL, 'Override the default prefix', '__'),
new InputOption('output-format', null, InputOption::VALUE_OPTIONAL, 'Override the default output format', 'xlf'),
new InputOption('output-format', null, InputOption::VALUE_OPTIONAL, 'Override the default output format (deprecated)'),
new InputOption('format', null, InputOption::VALUE_OPTIONAL, 'Override the default output format', 'xlf12'),
new InputOption('dump-messages', null, InputOption::VALUE_NONE, 'Should the messages be dumped in the console'),
new InputOption('force', null, InputOption::VALUE_NONE, 'Should the update be done'),
new InputOption('clean', null, InputOption::VALUE_NONE, 'Should clean not found messages'),
new InputOption('domain', null, InputOption::VALUE_OPTIONAL, 'Specify the domain to update'),
new InputOption('xliff-version', null, InputOption::VALUE_OPTIONAL, 'Override the default xliff version', '1.2'),
new InputOption('xliff-version', null, InputOption::VALUE_OPTIONAL, 'Override the default xliff version (deprecated)'),
new InputOption('sort', null, InputOption::VALUE_OPTIONAL, 'Return list of messages sorted alphabetically', 'asc'),
new InputOption('as-tree', null, InputOption::VALUE_OPTIONAL, 'Dump the messages as a tree-like structure: The given value defines the level where to switch to inline YAML'),
])
Expand Down Expand Up @@ -112,8 +113,8 @@ protected function configure()

You can dump a tree-like structure using the yaml format with <comment>--as-tree</> flag:

<info>php %command.full_name% --force --output-format=yaml --as-tree=3 en AcmeBundle</info>
<info>php %command.full_name% --force --output-format=yaml --sort=asc --as-tree=3 fr</info>
<info>php %command.full_name% --force --format=yaml --as-tree=3 en AcmeBundle</info>
<info>php %command.full_name% --force --format=yaml --sort=asc --as-tree=3 fr</info>

EOF
)
Expand All @@ -135,13 +136,31 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return 1;
}

$format = $input->getOption('output-format') ?: $input->getOption('format');
$xliffVersion = $input->getOption('xliff-version') ?? '1.2';

if ($input->getOption('xliff-version')) {
trigger_deprecation('symfony/framework-bundle', '5.3', 'The "--xliff-version" option is deprecated, use "--format=xlf%d" instead.', 10 * $xliffVersion);
}

if ($input->getOption('output-format')) {
trigger_deprecation('symfony/framework-bundle', '5.3', 'The "--output-format" option is deprecated, use "--format=xlf%d" instead.', 10 * $xliffVersion);
}

switch ($format) {
case 'xlf20': $xliffVersion = '2.0';
// no break
case 'xlf12': $format = 'xlf';
}

// check format
$supportedFormats = $this->writer->getFormats();
if (!\in_array($input->getOption('output-format'), $supportedFormats, true)) {
$errorIo->error(['Wrong output format', 'Supported formats are: '.implode(', ', $supportedFormats).'.']);
if (!\in_array($format, $supportedFormats, true)) {
$errorIo->error(['Wrong output format', 'Supported formats are: '.implode(', ', $supportedFormats).', xlf12 and xlf20.']);

return 1;
}

/** @var KernelInterface $kernel */
$kernel = $this->getApplication()->getKernel();

Expand Down Expand Up @@ -225,23 +244,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int

$resultMessage = 'Translation files were successfully updated';

// move new messages to intl domain when possible
if (class_exists(\MessageFormatter::class)) {
foreach ($operation->getDomains() as $domain) {
$intlDomain = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX;
$newMessages = $operation->getNewMessages($domain);

if ([] === $newMessages || ([] === $currentCatalogue->all($intlDomain) && [] !== $currentCatalogue->all($domain))) {
continue;
}

$result = $operation->getResult();
$allIntlMessages = $result->all($intlDomain);
$currentMessages = array_diff_key($newMessages, $result->all($domain));
$result->replace($currentMessages, $domain);
$result->replace($allIntlMessages + $newMessages, $intlDomain);
}
}
$operation->moveMessagesToIntlDomainsIfPossible('new');

// show compiled list of messages
if (true === $input->getOption('dump-messages')) {
Expand Down Expand Up @@ -284,8 +287,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$extractedMessagesCount += $domainMessagesCount;
}

if ('xlf' === $input->getOption('output-format')) {
$io->comment(sprintf('Xliff output version is <info>%s</info>', $input->getOption('xliff-version')));
if ('xlf' === $format) {
$io->comment(sprintf('Xliff output version is <info>%s</info>', $xliffVersion));
}

$resultMessage = sprintf('%d message%s successfully extracted', $extractedMessagesCount, $extractedMessagesCount > 1 ? 's were' : ' was');
Expand All @@ -306,7 +309,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$bundleTransPath = end($transPaths);
}

$this->writer->write($operation->getResult(), $input->getOption('output-format'), ['path' => $bundleTransPath, 'default_locale' => $this->defaultLocale, 'xliff_version' => $input->getOption('xliff-version'), 'as_tree' => $input->getOption('as-tree'), 'inline' => $input->getOption('as-tree') ?? 0]);
$this->writer->write($operation->getResult(), $format, ['path' => $bundleTransPath, 'default_locale' => $this->defaultLocale, 'xliff_version' => $xliffVersion, 'as_tree' => $input->getOption('as-tree'), 'inline' => $input->getOption('as-tree') ?? 0]);

if (true === $input->getOption('dump-messages')) {
$resultMessage .= ' and translation files were updated';
Expand Down Expand Up @@ -335,11 +338,13 @@ private function filterCatalogue(MessageCatalogue $catalogue, string $domain): M
foreach ($catalogue->getResources() as $resource) {
$filteredCatalogue->addResource($resource);
}

if ($metadata = $catalogue->getMetadata('', $intlDomain)) {
foreach ($metadata as $k => $v) {
$filteredCatalogue->setMetadata($k, $v, $intlDomain);
}
}

if ($metadata = $catalogue->getMetadata('', $domain)) {
foreach ($metadata as $k => $v) {
$filteredCatalogue->setMetadata($k, $v, $domain);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class UnusedTagsPass implements CompilerPassInterface
'translation.dumper',
'translation.extractor',
'translation.loader',
'translation.provider_factory',
'twig.extension',
'twig.loader',
'twig.runtime',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,7 @@ private function addTranslatorSection(ArrayNodeDefinition $rootNode, callable $e
->fixXmlConfig('fallback')
->fixXmlConfig('path')
->fixXmlConfig('enabled_locale')
->fixXmlConfig('provider')
->children()
->arrayNode('fallbacks')
->info('Defaults to the value of "default_locale".')
Expand Down Expand Up @@ -822,6 +823,27 @@ private function addTranslatorSection(ArrayNodeDefinition $rootNode, callable $e
->end()
->end()
->end()
->arrayNode('providers')
->info('Translation providers you can read/write your translations from')
->useAttributeAsKey('name')
->prototype('array')
->fixXmlConfig('domain')
->fixXmlConfig('locale')
->children()
->scalarNode('dsn')->end()
->arrayNode('domains')
->prototype('scalar')->end()
->defaultValue([])
->end()
->arrayNode('locales')
->prototype('scalar')->end()
->defaultValue([])
->info('If not set, all locales listed under framework.translator.enabled_locales are used.')
->end()
->end()
->end()
->defaultValue([])
->end()
->end()
->end()
->end()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@
use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Component\String\LazyString;
use Symfony\Component\String\Slugger\SluggerInterface;
use Symfony\Component\Translation\Bridge\Loco\Provider\LocoProviderFactory;
use Symfony\Component\Translation\Command\XliffLintCommand as BaseXliffLintCommand;
use Symfony\Component\Translation\PseudoLocalizationTranslator;
use Symfony\Component\Translation\Translator;
Expand Down Expand Up @@ -1222,11 +1223,14 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder
if (!$this->isConfigEnabled($container, $config)) {
$container->removeDefinition('console.command.translation_debug');
$container->removeDefinition('console.command.translation_update');
$container->removeDefinition('console.command.translation_pull');
$container->removeDefinition('console.command.translation_push');

return;
}

$loader->load('translation.php');
$loader->load('translation_providers.php');

// Use the "real" translator instead of the identity default
$container->setAlias('translator', 'translator.default')->setPublic(true);
Expand Down Expand Up @@ -1348,6 +1352,46 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder
$options,
]);
}

$classToServices = [
LocoProviderFactory::class => 'translation.provider_factory.loco',
];

$parentPackages = ['symfony/framework-bundle', 'symfony/translation', 'symfony/http-client'];

foreach ($classToServices as $class => $service) {
$package = sprintf('symfony/%s-translation', substr($service, \strlen('translation.provider_factory.')));

if (!$container->hasDefinition('http_client') || !ContainerBuilder::willBeAvailable($package, $class, $parentPackages)) {
$container->removeDefinition($service);
}
}

if (!$config['providers']) {
return;
}

foreach ($config['providers'] as $name => $provider) {
if (!$config['enabled_locales'] && !$provider['locales']) {
throw new LogicException(sprintf('You must specify one of "framework.translator.enabled_locales" or "framework.translator.providers.%s.locales" in order to use translation providers.', $name));
}
}

$container->getDefinition('console.command.translation_pull')
->replaceArgument(4, array_merge($transPaths, [$config['default_path']]))
->replaceArgument(5, $config['enabled_locales'])
;

$container->getDefinition('console.command.translation_push')
->replaceArgument(2, array_merge($transPaths, [$config['default_path']]))
->replaceArgument(3, $config['enabled_locales'])
;

$container->getDefinition('translation.provider_collection_factory')
->replaceArgument(1, $config['enabled_locales'])
;

$container->getDefinition('translation.provider_collection')->setArgument(0, $config['providers']);
}

private function registerValidationConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader, bool $propertyInfoEnabled)
Expand Down
22 changes: 22 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/Resources/config/console.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
use Symfony\Component\Messenger\Command\FailedMessagesShowCommand;
use Symfony\Component\Messenger\Command\SetupTransportsCommand;
use Symfony\Component\Messenger\Command\StopWorkersCommand;
use Symfony\Component\Translation\Command\TranslationPullCommand;
use Symfony\Component\Translation\Command\TranslationPushCommand;
use Symfony\Component\Translation\Command\XliffLintCommand;
use Symfony\Component\Validator\Command\DebugCommand as ValidatorDebugCommand;

Expand Down Expand Up @@ -232,6 +234,26 @@
])
->tag('console.command')

->set('console.command.translation_pull', TranslationPullCommand::class)
->args([
service('translation.provider_collection'),
service('translation.writer'),
service('translation.reader'),
param('kernel.default_locale'),
[], // Translator paths
[], // Enabled locales
])
->tag('console.command', ['command' => 'translation:pull'])

->set('console.command.translation_push', TranslationPushCommand::class)
->args([
service('translation.provider_collection'),
service('translation.reader'),
[], // Translator paths
[], // Enabled locales
])
->tag('console.command', ['command' => 'translation:push'])

->set('console.command.workflow_dump', WorkflowDumpCommand::class)
->tag('console.command')

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@
<xsd:element name="path" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="enabled-locale" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="pseudo-localization" type="pseudo_localization" minOccurs="0" maxOccurs="1" />
<xsd:element name="provider" type="translation_provider" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attribute name="enabled" type="xsd:boolean" />
<xsd:attribute name="fallback" type="xsd:string" />
Expand All @@ -195,6 +196,15 @@
<xsd:attribute name="parse_html" type="xsd:boolean" />
</xsd:complexType>

<xsd:complexType name="translation_provider">
<xsd:sequence>
<xsd:element name="domain" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="locale" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="dsn" type="xsd:string" />
</xsd:complexType>

<xsd:complexType name="validation">
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="static-method" type="xsd:string" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Symfony\Component\Translation\Bridge\Loco\Provider\LocoProviderFactory;
use Symfony\Component\Translation\Provider\NullProviderFactory;
use Symfony\Component\Translation\Provider\TranslationProviderCollection;
use Symfony\Component\Translation\Provider\TranslationProviderCollectionFactory;

return static function (ContainerConfigurator $container) {
$container->services()
->set('translation.provider_collection', TranslationProviderCollection::class)
->factory([service('translation.provider_collection_factory'), 'fromConfig'])
->args([
[], // Providers
])

->set('translation.provider_collection_factory', TranslationProviderCollectionFactory::class)
->args([
tagged_iterator('translation.provider_factory'),
[], // Enabled locales
])

->set('translation.provider_factory.null', NullProviderFactory::class)
->tag('translation.provider_factory')

->set('translation.provider_factory.loco', LocoProviderFactory::class)
->args([
service('http_client'),
service('logger'),
param('kernel.default_locale'),
service('translation.loader.xliff'),
])
->tag('translation.provider_factory')
;
};
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ protected static function getBundleDefaultConfig()
'parse_html' => false,
'localizable_html_attributes' => [],
],
'providers' => [],
],
'validation' => [
'enabled' => !class_exists(FullStack::class),
Expand Down
4 changes: 4 additions & 0 deletions src/Symfony/Component/Translation/Bridge/Loco/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/Tests export-ignore
/phpunit.xml.dist export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
Loading