1212namespace Symfony \Bundle \FrameworkBundle \Command ;
1313
1414use Symfony \Component \Console \Command \Command ;
15+ use Symfony \Component \Console \Completion \CompletionInput ;
16+ use Symfony \Component \Console \Completion \CompletionSuggestions ;
1517use Symfony \Component \Console \Exception \InvalidArgumentException ;
1618use Symfony \Component \Console \Input \InputArgument ;
1719use Symfony \Component \Console \Input \InputInterface ;
@@ -41,6 +43,10 @@ class TranslationUpdateCommand extends Command
4143private const ASC ='asc ' ;
4244private const DESC ='desc ' ;
4345private const SORT_ORDERS = [self ::ASC ,self ::DESC ];
46+ private const FORMATS = [
47+ 'xlf12 ' => ['xlf ' ,'1.2 ' ],
48+ 'xlf20 ' => ['xlf ' ,'2.0 ' ],
49+ ];
4450
4551protected static $ defaultName ='translation:extract|translation:update ' ;
4652protected static $ defaultDescription ='Extract missing translations keys from code to translation files. ' ;
@@ -53,8 +59,9 @@ class TranslationUpdateCommand extends Command
5359private $ defaultViewsPath ;
5460private $ transPaths ;
5561private $ codePaths ;
62+ private $ enabledLocales ;
5663
57- public function __construct (TranslationWriterInterface $ writer ,TranslationReaderInterface $ reader ,ExtractorInterface $ extractor ,string $ defaultLocale ,string $ defaultTransPath =null ,string $ defaultViewsPath =null ,array $ transPaths = [],array $ codePaths = [])
64+ public function __construct (TranslationWriterInterface $ writer ,TranslationReaderInterface $ reader ,ExtractorInterface $ extractor ,string $ defaultLocale ,string $ defaultTransPath =null ,string $ defaultViewsPath =null ,array $ transPaths = [],array $ codePaths = [], array $ enabledLocales = [] )
5865 {
5966parent ::__construct ();
6067
@@ -66,6 +73,7 @@ public function __construct(TranslationWriterInterface $writer, TranslationReade
6673$ this ->defaultViewsPath =$ defaultViewsPath ;
6774$ this ->transPaths =$ transPaths ;
6875$ this ->codePaths =$ codePaths ;
76+ $ this ->enabledLocales =$ enabledLocales ;
6977 }
7078
7179/**
@@ -155,10 +163,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
155163trigger_deprecation ('symfony/framework-bundle ' ,'5.3 ' ,'The "--output-format" option is deprecated, use "--format=xlf%d" instead. ' ,10 *$ xliffVersion );
156164 }
157165
158- switch ($ format ) {
159- case 'xlf20 ' :$ xliffVersion ='2.0 ' ;
160- // no break
161- case 'xlf12 ' :$ format ='xlf ' ;
166+ if (\in_array ($ format ,array_keys (self ::FORMATS ),true )) {
167+ [$ format ,$ xliffVersion ] =self ::FORMATS [$ format ];
162168 }
163169
164170// check format
@@ -173,15 +179,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int
173179$ kernel =$ this ->getApplication ()->getKernel ();
174180
175181// Define Root Paths
176- $ transPaths =$ this ->transPaths ;
177- if ($ this ->defaultTransPath ) {
178- $ transPaths [] =$ this ->defaultTransPath ;
179- }
180- $ codePaths =$ this ->codePaths ;
181- $ codePaths [] =$ kernel ->getProjectDir ().'/src ' ;
182- if ($ this ->defaultViewsPath ) {
183- $ codePaths [] =$ this ->defaultViewsPath ;
184- }
182+ $ transPaths =$ this ->getRootTransPaths ();
183+ $ codePaths =$ this ->getRootCodePaths ($ kernel );
184+
185185$ currentName ='default directory ' ;
186186
187187// Override with provided Bundle info
@@ -214,24 +214,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int
214214$ io ->title ('Translation Messages Extractor and Dumper ' );
215215$ io ->comment (sprintf ('Generating "<info>%s</info>" translation files for "<info>%s</info>" ' ,$ input ->getArgument ('locale ' ),$ currentName ));
216216
217- // load any messages from templates
218- $ extractedCatalogue =new MessageCatalogue ($ input ->getArgument ('locale ' ));
219217$ io ->comment ('Parsing templates... ' );
220- $ this ->extractor ->setPrefix ($ input ->getOption ('prefix ' ));
221- foreach ($ codePathsas $ path ) {
222- if (is_dir ($ path ) ||is_file ($ path )) {
223- $ this ->extractor ->extract ($ path ,$ extractedCatalogue );
224- }
225- }
218+ $ extractedCatalogue =$ this ->extractMessages ($ input ->getArgument ('locale ' ),$ codePaths ,$ input ->getOption ('prefix ' ));
226219
227- // load any existing messages from the translation files
228- $ currentCatalogue =new MessageCatalogue ($ input ->getArgument ('locale ' ));
229220$ io ->comment ('Loading translation files... ' );
230- foreach ($ transPathsas $ path ) {
231- if (is_dir ($ path )) {
232- $ this ->reader ->read ($ path ,$ currentCatalogue );
233- }
234- }
221+ $ currentCatalogue =$ this ->loadCurrentMessages ($ input ->getArgument ('locale ' ),$ transPaths );
235222
236223if (null !==$ domain =$ input ->getOption ('domain ' )) {
237224$ currentCatalogue =$ this ->filterCatalogue ($ currentCatalogue ,$ domain );
@@ -329,6 +316,60 @@ protected function execute(InputInterface $input, OutputInterface $output): int
329316return 0 ;
330317 }
331318
319+ public function complete (CompletionInput $ input ,CompletionSuggestions $ suggestions ):void
320+ {
321+ if ($ input ->mustSuggestArgumentValuesFor ('locale ' )) {
322+ $ suggestions ->suggestValues ($ this ->enabledLocales );
323+
324+ return ;
325+ }
326+
327+ /** @var KernelInterface $kernel */
328+ $ kernel =$ this ->getApplication ()->getKernel ();
329+ if ($ input ->mustSuggestArgumentValuesFor ('bundle ' )) {
330+ $ bundles = [];
331+
332+ foreach ($ kernel ->getBundles ()as $ bundle ) {
333+ $ bundles [] =$ bundle ->getName ();
334+ if ($ bundle ->getContainerExtension ()) {
335+ $ bundles [] =$ bundle ->getContainerExtension ()->getAlias ();
336+ }
337+ }
338+
339+ $ suggestions ->suggestValues ($ bundles );
340+
341+ return ;
342+ }
343+
344+ if ($ input ->mustSuggestOptionValuesFor ('format ' )) {
345+ $ suggestions ->suggestValues (array_merge (
346+ $ this ->writer ->getFormats (),
347+ array_keys (self ::FORMATS )
348+ ));
349+
350+ return ;
351+ }
352+
353+ if ($ input ->mustSuggestOptionValuesFor ('domain ' ) &&$ locale =$ input ->getArgument ('locale ' )) {
354+ $ extractedCatalogue =$ this ->extractMessages ($ locale ,$ this ->getRootCodePaths ($ kernel ),$ input ->getOption ('prefix ' ));
355+
356+ $ currentCatalogue =$ this ->loadCurrentMessages ($ locale ,$ this ->getRootTransPaths ());
357+
358+ // process catalogues
359+ $ operation =$ input ->getOption ('clean ' )
360+ ?new TargetOperation ($ currentCatalogue ,$ extractedCatalogue )
361+ :new MergeOperation ($ currentCatalogue ,$ extractedCatalogue );
362+
363+ $ suggestions ->suggestValues ($ operation ->getDomains ());
364+
365+ return ;
366+ }
367+
368+ if ($ input ->mustSuggestOptionValuesFor ('sort ' )) {
369+ $ suggestions ->suggestValues (self ::SORT_ORDERS );
370+ }
371+ }
372+
332373private function filterCatalogue (MessageCatalogue $ catalogue ,string $ domain ):MessageCatalogue
333374 {
334375$ filteredCatalogue =new MessageCatalogue ($ catalogue ->getLocale ());
@@ -361,4 +402,50 @@ private function filterCatalogue(MessageCatalogue $catalogue, string $domain): M
361402
362403return $ filteredCatalogue ;
363404 }
405+
406+ private function extractMessages (string $ locale ,array $ transPaths ,string $ prefix ):MessageCatalogue
407+ {
408+ $ extractedCatalogue =new MessageCatalogue ($ locale );
409+ $ this ->extractor ->setPrefix ($ prefix );
410+ foreach ($ transPathsas $ path ) {
411+ if (is_dir ($ path ) ||is_file ($ path )) {
412+ $ this ->extractor ->extract ($ path ,$ extractedCatalogue );
413+ }
414+ }
415+
416+ return $ extractedCatalogue ;
417+ }
418+
419+ private function loadCurrentMessages (string $ locale ,array $ transPaths ):MessageCatalogue
420+ {
421+ $ currentCatalogue =new MessageCatalogue ($ locale );
422+ foreach ($ transPathsas $ path ) {
423+ if (is_dir ($ path )) {
424+ $ this ->reader ->read ($ path ,$ currentCatalogue );
425+ }
426+ }
427+
428+ return $ currentCatalogue ;
429+ }
430+
431+ private function getRootTransPaths ():array
432+ {
433+ $ transPaths =$ this ->transPaths ;
434+ if ($ this ->defaultTransPath ) {
435+ $ transPaths [] =$ this ->defaultTransPath ;
436+ }
437+
438+ return $ transPaths ;
439+ }
440+
441+ private function getRootCodePaths (KernelInterface $ kernel ):array
442+ {
443+ $ codePaths =$ this ->codePaths ;
444+ $ codePaths [] =$ kernel ->getProjectDir ().'/src ' ;
445+ if ($ this ->defaultViewsPath ) {
446+ $ codePaths [] =$ this ->defaultViewsPath ;
447+ }
448+
449+ return $ codePaths ;
450+ }
364451}