Skip to content

Commit 6e55fa8

Browse files
welcoMatticOlivier Dolbeau
and
Olivier Dolbeau
committed
Added Translation Providers
Co-authored-by: Olivier Dolbeau <[email protected]>
1 parent be384cf commit 6e55fa8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+3616
-51
lines changed

UPGRADE-5.3.md

+2
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ FrameworkBundle
4444
* Deprecate the `KernelTestCase::$container` property, use `KernelTestCase::getContainer()` instead
4545
* Rename the container parameter `profiler_listener.only_master_requests` to `profiler_listener.only_main_requests`
4646
* Deprecate registering workflow services as public
47+
* Deprecate option `--xliff-version` of the `translation:update` command, use e.g. `--format=xlf20` instead
48+
* Deprecate option `--output-format` of the `translation:update` command, use e.g. `--format=xlf20` instead
4749

4850
HttpFoundation
4951
--------------

UPGRADE-6.0.md

+2
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ FrameworkBundle
8686
* 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.
8787
* Remove the `KernelTestCase::$container` property, use `KernelTestCase::getContainer()` instead
8888
* Registered workflow services are now private
89+
* Remove option `--xliff-version` of the `translation:update` command, use e.g. `--output-format=xlf20` instead
90+
* Remove option `--output-format` of the `translation:update` command, use e.g. `--output-format=xlf20` instead
8991

9092
HttpFoundation
9193
--------------

link

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ if (!is_dir("$pathToProject/vendor/symfony")) {
4141
$sfPackages = array('symfony/symfony' => __DIR__);
4242

4343
$filesystem = new Filesystem();
44-
$braces = array('Bundle', 'Bridge', 'Component', 'Component/Security', 'Component/Mailer/Bridge', 'Component/Messenger/Bridge', 'Component/Notifier/Bridge', 'Contracts');
44+
$braces = array('Bundle', 'Bridge', 'Component', 'Component/Security', 'Component/Mailer/Bridge', 'Component/Messenger/Bridge', 'Component/Notifier/Bridge', 'Contracts', 'Component/Translation/Bridge');
4545
$directories = array_merge(...array_values(array_map(function ($part) {
4646
return glob(__DIR__.'/src/Symfony/'.$part.'/*', GLOB_ONLYDIR | GLOB_NOSORT);
4747
}, $braces)));

src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ CHANGELOG
2020
* Rename the container parameter `profiler_listener.only_master_requests` to `profiler_listener.only_main_requests`
2121
* Add service `fragment.uri_generator` to generate the URI of a fragment
2222
* Deprecate registering workflow services as public
23+
* Deprecate option `--xliff-version` of the `translation:update` command, use e.g. `--format=xlf20` instead
24+
* Deprecate option `--output-format` of the `translation:update` command, use e.g. `--format=xlf20` instead
2325

2426
5.2.0
2527
-----

src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php

+31-26
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,13 @@ protected function configure()
7777
new InputArgument('locale', InputArgument::REQUIRED, 'The locale'),
7878
new InputArgument('bundle', InputArgument::OPTIONAL, 'The bundle name or directory where to load the messages'),
7979
new InputOption('prefix', null, InputOption::VALUE_OPTIONAL, 'Override the default prefix', '__'),
80-
new InputOption('output-format', null, InputOption::VALUE_OPTIONAL, 'Override the default output format', 'xlf'),
80+
new InputOption('output-format', null, InputOption::VALUE_OPTIONAL, 'Override the default output format (deprecated)'),
81+
new InputOption('format', null, InputOption::VALUE_OPTIONAL, 'Override the default output format', 'xlf12'),
8182
new InputOption('dump-messages', null, InputOption::VALUE_NONE, 'Should the messages be dumped in the console'),
8283
new InputOption('force', null, InputOption::VALUE_NONE, 'Should the update be done'),
8384
new InputOption('clean', null, InputOption::VALUE_NONE, 'Should clean not found messages'),
8485
new InputOption('domain', null, InputOption::VALUE_OPTIONAL, 'Specify the domain to update'),
85-
new InputOption('xliff-version', null, InputOption::VALUE_OPTIONAL, 'Override the default xliff version', '1.2'),
86+
new InputOption('xliff-version', null, InputOption::VALUE_OPTIONAL, 'Override the default xliff version (deprecated)'),
8687
new InputOption('sort', null, InputOption::VALUE_OPTIONAL, 'Return list of messages sorted alphabetically', 'asc'),
8788
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'),
8889
])
@@ -112,8 +113,8 @@ protected function configure()
112113
113114
You can dump a tree-like structure using the yaml format with <comment>--as-tree</> flag:
114115
115-
<info>php %command.full_name% --force --output-format=yaml --as-tree=3 en AcmeBundle</info>
116-
<info>php %command.full_name% --force --output-format=yaml --sort=asc --as-tree=3 fr</info>
116+
<info>php %command.full_name% --force --format=yaml --as-tree=3 en AcmeBundle</info>
117+
<info>php %command.full_name% --force --format=yaml --sort=asc --as-tree=3 fr</info>
117118

118119
EOF
119120
)
@@ -135,13 +136,31 @@ protected function execute(InputInterface $input, OutputInterface $output): int
135136
return 1;
136137
}
137138

139+
$format = $input->getOption('output-format') ?: $input->getOption('format');
140+
$xliffVersion = $input->getOption('xliff-version') ?? '1.2';
141+
142+
if ($input->getOption('xliff-version')) {
143+
trigger_deprecation('symfony/framework-bundle', '5.3', 'The "--xliff-version" option is deprecated, use "--format=xlf%d" instead.', 10 * $xliffVersion);
144+
}
145+
146+
if ($input->getOption('output-format')) {
147+
trigger_deprecation('symfony/framework-bundle', '5.3', 'The "--output-format" option is deprecated, use "--format=xlf%d" instead.', 10 * $xliffVersion);
148+
}
149+
150+
switch ($format) {
151+
case 'xlf20': $xliffVersion = '2.0';
152+
// no break
153+
case 'xlf12': $format = 'xlf';
154+
}
155+
138156
// check format
139157
$supportedFormats = $this->writer->getFormats();
140-
if (!\in_array($input->getOption('output-format'), $supportedFormats, true)) {
141-
$errorIo->error(['Wrong output format', 'Supported formats are: '.implode(', ', $supportedFormats).'.']);
158+
if (!\in_array($format, $supportedFormats, true)) {
159+
$errorIo->error(['Wrong output format', 'Supported formats are: '.implode(', ', $supportedFormats).', xlf12 and xlf20.']);
142160

143161
return 1;
144162
}
163+
145164
/** @var KernelInterface $kernel */
146165
$kernel = $this->getApplication()->getKernel();
147166

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

226245
$resultMessage = 'Translation files were successfully updated';
227246

228-
// move new messages to intl domain when possible
229-
if (class_exists(\MessageFormatter::class)) {
230-
foreach ($operation->getDomains() as $domain) {
231-
$intlDomain = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX;
232-
$newMessages = $operation->getNewMessages($domain);
233-
234-
if ([] === $newMessages || ([] === $currentCatalogue->all($intlDomain) && [] !== $currentCatalogue->all($domain))) {
235-
continue;
236-
}
237-
238-
$result = $operation->getResult();
239-
$allIntlMessages = $result->all($intlDomain);
240-
$currentMessages = array_diff_key($newMessages, $result->all($domain));
241-
$result->replace($currentMessages, $domain);
242-
$result->replace($allIntlMessages + $newMessages, $intlDomain);
243-
}
244-
}
247+
$operation->moveMessagesToIntlDomainsIfPossible('new');
245248

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

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

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

309-
$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]);
312+
$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]);
310313

311314
if (true === $input->getOption('dump-messages')) {
312315
$resultMessage .= ' and translation files were updated';
@@ -335,11 +338,13 @@ private function filterCatalogue(MessageCatalogue $catalogue, string $domain): M
335338
foreach ($catalogue->getResources() as $resource) {
336339
$filteredCatalogue->addResource($resource);
337340
}
341+
338342
if ($metadata = $catalogue->getMetadata('', $intlDomain)) {
339343
foreach ($metadata as $k => $v) {
340344
$filteredCatalogue->setMetadata($k, $v, $intlDomain);
341345
}
342346
}
347+
343348
if ($metadata = $catalogue->getMetadata('', $domain)) {
344349
foreach ($metadata as $k => $v) {
345350
$filteredCatalogue->setMetadata($k, $v, $domain);

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php

+1
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ class UnusedTagsPass implements CompilerPassInterface
8585
'translation.dumper',
8686
'translation.extractor',
8787
'translation.loader',
88+
'translation.provider_factory',
8889
'twig.extension',
8990
'twig.loader',
9091
'twig.runtime',

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php

+22
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,7 @@ private function addTranslatorSection(ArrayNodeDefinition $rootNode, callable $e
785785
->fixXmlConfig('fallback')
786786
->fixXmlConfig('path')
787787
->fixXmlConfig('enabled_locale')
788+
->fixXmlConfig('provider')
788789
->children()
789790
->arrayNode('fallbacks')
790791
->info('Defaults to the value of "default_locale".')
@@ -822,6 +823,27 @@ private function addTranslatorSection(ArrayNodeDefinition $rootNode, callable $e
822823
->end()
823824
->end()
824825
->end()
826+
->arrayNode('providers')
827+
->info('Translation providers you can read/write your translations from')
828+
->useAttributeAsKey('name')
829+
->prototype('array')
830+
->fixXmlConfig('domain')
831+
->fixXmlConfig('locale')
832+
->children()
833+
->scalarNode('dsn')->end()
834+
->arrayNode('domains')
835+
->prototype('scalar')->end()
836+
->defaultValue([])
837+
->end()
838+
->arrayNode('locales')
839+
->prototype('scalar')->end()
840+
->defaultValue([])
841+
->info('If not set, all locales listed under framework.translator.enabled_locales are used.')
842+
->end()
843+
->end()
844+
->end()
845+
->defaultValue([])
846+
->end()
825847
->end()
826848
->end()
827849
->end()

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

+44
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@
169169
use Symfony\Component\Stopwatch\Stopwatch;
170170
use Symfony\Component\String\LazyString;
171171
use Symfony\Component\String\Slugger\SluggerInterface;
172+
use Symfony\Component\Translation\Bridge\Loco\Provider\LocoProviderFactory;
172173
use Symfony\Component\Translation\Command\XliffLintCommand as BaseXliffLintCommand;
173174
use Symfony\Component\Translation\PseudoLocalizationTranslator;
174175
use Symfony\Component\Translation\Translator;
@@ -1222,11 +1223,14 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder
12221223
if (!$this->isConfigEnabled($container, $config)) {
12231224
$container->removeDefinition('console.command.translation_debug');
12241225
$container->removeDefinition('console.command.translation_update');
1226+
$container->removeDefinition('console.command.translation_pull');
1227+
$container->removeDefinition('console.command.translation_push');
12251228

12261229
return;
12271230
}
12281231

12291232
$loader->load('translation.php');
1233+
$loader->load('translation_providers.php');
12301234

12311235
// Use the "real" translator instead of the identity default
12321236
$container->setAlias('translator', 'translator.default')->setPublic(true);
@@ -1348,6 +1352,46 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder
13481352
$options,
13491353
]);
13501354
}
1355+
1356+
$classToServices = [
1357+
LocoProviderFactory::class => 'translation.provider_factory.loco',
1358+
];
1359+
1360+
$parentPackages = ['symfony/framework-bundle', 'symfony/translation', 'symfony/http-client'];
1361+
1362+
foreach ($classToServices as $class => $service) {
1363+
$package = sprintf('symfony/%s-translation', substr($service, \strlen('translation.provider_factory.')));
1364+
1365+
if (!$container->hasDefinition('http_client') || !ContainerBuilder::willBeAvailable($package, $class, $parentPackages)) {
1366+
$container->removeDefinition($service);
1367+
}
1368+
}
1369+
1370+
if (!$config['providers']) {
1371+
return;
1372+
}
1373+
1374+
foreach ($config['providers'] as $name => $provider) {
1375+
if (!$config['enabled_locales'] && !$provider['locales']) {
1376+
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));
1377+
}
1378+
}
1379+
1380+
$container->getDefinition('console.command.translation_pull')
1381+
->replaceArgument(4, array_merge($transPaths, [$config['default_path']]))
1382+
->replaceArgument(5, $config['enabled_locales'])
1383+
;
1384+
1385+
$container->getDefinition('console.command.translation_push')
1386+
->replaceArgument(2, array_merge($transPaths, [$config['default_path']]))
1387+
->replaceArgument(3, $config['enabled_locales'])
1388+
;
1389+
1390+
$container->getDefinition('translation.provider_collection_factory')
1391+
->replaceArgument(1, $config['enabled_locales'])
1392+
;
1393+
1394+
$container->getDefinition('translation.provider_collection')->setArgument(0, $config['providers']);
13511395
}
13521396

13531397
private function registerValidationConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader, bool $propertyInfoEnabled)

src/Symfony/Bundle/FrameworkBundle/Resources/config/console.php

+22
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@
4646
use Symfony\Component\Messenger\Command\FailedMessagesShowCommand;
4747
use Symfony\Component\Messenger\Command\SetupTransportsCommand;
4848
use Symfony\Component\Messenger\Command\StopWorkersCommand;
49+
use Symfony\Component\Translation\Command\TranslationPullCommand;
50+
use Symfony\Component\Translation\Command\TranslationPushCommand;
4951
use Symfony\Component\Translation\Command\XliffLintCommand;
5052
use Symfony\Component\Validator\Command\DebugCommand as ValidatorDebugCommand;
5153

@@ -232,6 +234,26 @@
232234
])
233235
->tag('console.command')
234236

237+
->set('console.command.translation_pull', TranslationPullCommand::class)
238+
->args([
239+
service('translation.provider_collection'),
240+
service('translation.writer'),
241+
service('translation.reader'),
242+
param('kernel.default_locale'),
243+
[], // Translator paths
244+
[], // Enabled locales
245+
])
246+
->tag('console.command', ['command' => 'translation:pull'])
247+
248+
->set('console.command.translation_push', TranslationPushCommand::class)
249+
->args([
250+
service('translation.provider_collection'),
251+
service('translation.reader'),
252+
[], // Translator paths
253+
[], // Enabled locales
254+
])
255+
->tag('console.command', ['command' => 'translation:push'])
256+
235257
->set('console.command.workflow_dump', WorkflowDumpCommand::class)
236258
->tag('console.command')
237259

src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd

+10
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@
176176
<xsd:element name="path" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
177177
<xsd:element name="enabled-locale" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
178178
<xsd:element name="pseudo-localization" type="pseudo_localization" minOccurs="0" maxOccurs="1" />
179+
<xsd:element name="provider" type="translation_provider" minOccurs="0" maxOccurs="unbounded" />
179180
</xsd:sequence>
180181
<xsd:attribute name="enabled" type="xsd:boolean" />
181182
<xsd:attribute name="fallback" type="xsd:string" />
@@ -195,6 +196,15 @@
195196
<xsd:attribute name="parse_html" type="xsd:boolean" />
196197
</xsd:complexType>
197198

199+
<xsd:complexType name="translation_provider">
200+
<xsd:sequence>
201+
<xsd:element name="domain" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
202+
<xsd:element name="locale" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
203+
</xsd:sequence>
204+
<xsd:attribute name="name" type="xsd:string" />
205+
<xsd:attribute name="dsn" type="xsd:string" />
206+
</xsd:complexType>
207+
198208
<xsd:complexType name="validation">
199209
<xsd:choice minOccurs="0" maxOccurs="unbounded">
200210
<xsd:element name="static-method" type="xsd:string" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
13+
14+
use Symfony\Component\Translation\Bridge\Loco\Provider\LocoProviderFactory;
15+
use Symfony\Component\Translation\Provider\NullProviderFactory;
16+
use Symfony\Component\Translation\Provider\TranslationProviderCollection;
17+
use Symfony\Component\Translation\Provider\TranslationProviderCollectionFactory;
18+
19+
return static function (ContainerConfigurator $container) {
20+
$container->services()
21+
->set('translation.provider_collection', TranslationProviderCollection::class)
22+
->factory([service('translation.provider_collection_factory'), 'fromConfig'])
23+
->args([
24+
[], // Providers
25+
])
26+
27+
->set('translation.provider_collection_factory', TranslationProviderCollectionFactory::class)
28+
->args([
29+
tagged_iterator('translation.provider_factory'),
30+
[], // Enabled locales
31+
])
32+
33+
->set('translation.provider_factory.null', NullProviderFactory::class)
34+
->tag('translation.provider_factory')
35+
36+
->set('translation.provider_factory.loco', LocoProviderFactory::class)
37+
->args([
38+
service('http_client'),
39+
service('logger'),
40+
param('kernel.default_locale'),
41+
service('translation.loader.xliff'),
42+
])
43+
->tag('translation.provider_factory')
44+
;
45+
};

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php

+1
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,7 @@ protected static function getBundleDefaultConfig()
418418
'parse_html' => false,
419419
'localizable_html_attributes' => [],
420420
],
421+
'providers' => [],
421422
],
422423
'validation' => [
423424
'enabled' => !class_exists(FullStack::class),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/Tests export-ignore
2+
/phpunit.xml.dist export-ignore
3+
/.gitattributes export-ignore
4+
/.gitignore export-ignore

0 commit comments

Comments
 (0)