2011-08-29 18:22:52 +01:00
< ? 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\Bundle\FrameworkBundle\Command ;
2015-05-09 15:43:35 +01:00
use Symfony\Component\Console\Style\SymfonyStyle ;
2015-08-16 03:22:07 +01:00
use Symfony\Component\Translation\Catalogue\TargetOperation ;
2013-03-11 12:57:32 +00:00
use Symfony\Component\Translation\Catalogue\MergeOperation ;
2011-08-29 18:22:52 +01:00
use Symfony\Component\Console\Input\InputInterface ;
use Symfony\Component\Console\Output\OutputInterface ;
use Symfony\Component\Console\Input\InputArgument ;
use Symfony\Component\Console\Input\InputOption ;
use Symfony\Component\Translation\MessageCatalogue ;
/**
2015-09-28 14:27:37 +01:00
* A command that parses templates to extract translation messages and adds them
* into the translation files .
2011-09-19 13:47:33 +01:00
*
2011-08-29 18:22:52 +01:00
* @ author Michel Salib < michelsalib @ hotmail . com >
*/
class TranslationUpdateCommand extends ContainerAwareCommand
{
/**
2013-03-07 13:36:36 +00:00
* { @ inheritdoc }
2011-08-29 18:22:52 +01:00
*/
protected function configure ()
{
$this
-> setName ( 'translation:update' )
-> setDefinition ( array (
new InputArgument ( 'locale' , InputArgument :: REQUIRED , 'The locale' ),
2015-01-18 20:33:17 +00:00
new InputArgument ( 'bundle' , InputArgument :: OPTIONAL , 'The bundle name or directory where to load the messages, defaults to app/Resources folder' ),
2015-01-04 09:47:47 +00:00
new InputOption ( 'prefix' , null , InputOption :: VALUE_OPTIONAL , 'Override the default prefix' , '__' ),
2017-06-13 10:38:29 +01:00
new InputOption ( 'no-prefix' , null , InputOption :: VALUE_NONE , '[DEPRECATED] If set, no prefix is added to the translations' ),
2015-01-04 09:47:47 +00:00
new InputOption ( 'output-format' , null , InputOption :: VALUE_OPTIONAL , 'Override the default output format' , 'yml' ),
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' ),
2015-01-06 17:50:02 +00:00
new InputOption ( 'no-backup' , null , InputOption :: VALUE_NONE , 'Should backup be disabled' ),
2015-01-04 09:47:47 +00:00
new InputOption ( 'clean' , null , InputOption :: VALUE_NONE , 'Should clean not found messages' ),
2016-07-09 17:21:29 +01:00
new InputOption ( 'domain' , null , InputOption :: VALUE_OPTIONAL , 'Specify the domain to update' ),
2011-08-29 18:22:52 +01:00
))
2012-04-11 07:57:57 +01:00
-> setDescription ( 'Updates the translation file' )
2015-12-21 11:01:57 +00:00
-> setHelp ( <<< 'EOF'
2016-02-10 10:18:08 +00:00
The < info >% command . name %</ info > command extracts translation strings from templates
2014-06-30 11:29:03 +01:00
of a given bundle or the app folder . It can display them or merge the new ones into the translation files .
2016-02-12 06:23:50 +00:00
2011-08-29 18:22:52 +01:00
When new translation strings are found it can automatically add a prefix to the translation
message .
2014-06-30 11:29:03 +01:00
Example running against a Bundle ( AcmeBundle )
2015-01-04 09:47:47 +00:00
< info > php % command . full_name % -- dump - messages en AcmeBundle </ info >
< info > php % command . full_name % -- force -- prefix = " new_ " fr AcmeBundle </ info >
2014-03-13 04:05:55 +00:00
2014-06-30 11:29:03 +01:00
Example running against app messages ( app / Resources folder )
2015-01-06 17:50:02 +00:00
< info > php % command . full_name % -- dump - messages en </ info >
< info > php % command . full_name % -- force -- prefix = " new_ " fr </ info >
2011-08-29 18:22:52 +01:00
EOF
2012-02-12 15:37:55 +00:00
)
;
2011-08-29 18:22:52 +01:00
}
2016-09-28 05:49:39 +01:00
/**
* { @ inheritdoc }
*/
public function isEnabled ()
{
if ( ! class_exists ( 'Symfony\Component\Translation\Translator' )) {
return false ;
}
return parent :: isEnabled ();
}
2011-08-29 18:22:52 +01:00
/**
2013-03-07 13:36:36 +00:00
* { @ inheritdoc }
2011-08-29 18:22:52 +01:00
*/
protected function execute ( InputInterface $input , OutputInterface $output )
{
2015-12-07 12:11:35 +00:00
$io = new SymfonyStyle ( $input , $output );
2017-01-12 17:30:35 +00:00
$errorIo = $io -> getErrorStyle ();
2015-06-19 16:48:24 +01:00
2011-08-29 18:22:52 +01:00
// check presence of force or dump-message
if ( $input -> getOption ( 'force' ) !== true && $input -> getOption ( 'dump-messages' ) !== true ) {
2016-11-28 17:54:55 +00:00
$errorIo -> error ( 'You must choose one of --force or --dump-messages' );
2011-10-29 11:05:45 +01:00
2012-10-14 10:21:53 +01:00
return 1 ;
2011-08-29 18:22:52 +01:00
}
2011-09-19 13:47:33 +01:00
2011-08-29 18:22:52 +01:00
// check format
$writer = $this -> getContainer () -> get ( 'translation.writer' );
$supportedFormats = $writer -> getFormats ();
if ( ! in_array ( $input -> getOption ( 'output-format' ), $supportedFormats )) {
2016-11-28 17:54:55 +00:00
$errorIo -> error ( array ( 'Wrong output format' , 'Supported formats are: ' . implode ( ', ' , $supportedFormats ) . '.' ));
2011-10-29 11:05:45 +01:00
2012-10-14 10:21:53 +01:00
return 1 ;
2011-08-29 18:22:52 +01:00
}
2015-01-18 20:33:17 +00:00
$kernel = $this -> getContainer () -> get ( 'kernel' );
2011-08-29 18:22:52 +01:00
2014-06-30 11:29:03 +01:00
// Define Root Path to App folder
2015-06-19 16:48:24 +01:00
$transPaths = array ( $kernel -> getRootDir () . '/Resources/' );
2015-05-09 15:43:35 +01:00
$currentName = 'app folder' ;
2014-06-30 11:29:03 +01:00
// Override with provided Bundle info
if ( null !== $input -> getArgument ( 'bundle' )) {
2015-01-18 20:33:17 +00:00
try {
$foundBundle = $kernel -> getBundle ( $input -> getArgument ( 'bundle' ));
2015-06-25 13:52:11 +01:00
$transPaths = array (
$foundBundle -> getPath () . '/Resources/' ,
sprintf ( '%s/Resources/%s/' , $kernel -> getRootDir (), $foundBundle -> getName ()),
);
2015-01-18 20:33:17 +00:00
$currentName = $foundBundle -> getName ();
} catch ( \InvalidArgumentException $e ) {
// such a bundle does not exist, so treat the argument as path
2015-06-25 13:52:11 +01:00
$transPaths = array ( $input -> getArgument ( 'bundle' ) . '/Resources/' );
$currentName = $transPaths [ 0 ];
2015-01-18 20:33:17 +00:00
2015-06-25 13:52:11 +01:00
if ( ! is_dir ( $transPaths [ 0 ])) {
throw new \InvalidArgumentException ( sprintf ( '<error>"%s" is neither an enabled bundle nor a directory.</error>' , $transPaths [ 0 ]));
2015-01-18 20:33:17 +00:00
}
}
2014-06-30 11:29:03 +01:00
}
2016-11-28 17:54:55 +00:00
$errorIo -> title ( 'Translation Messages Extractor and Dumper' );
$errorIo -> comment ( sprintf ( 'Generating "<info>%s</info>" translation files for "<info>%s</info>"' , $input -> getArgument ( 'locale' ), $currentName ));
2011-08-29 18:22:52 +01:00
// load any messages from templates
2013-03-11 12:57:32 +00:00
$extractedCatalogue = new MessageCatalogue ( $input -> getArgument ( 'locale' ));
2016-11-28 17:54:55 +00:00
$errorIo -> comment ( 'Parsing templates...' );
2011-08-29 18:22:52 +01:00
$extractor = $this -> getContainer () -> get ( 'translation.extractor' );
2017-06-13 10:38:29 +01:00
$prefix = $input -> getOption ( 'prefix' );
// @deprecated since version 3.4, to be removed in 4.0 along with the --no-prefix option
if ( $input -> getOption ( 'no-prefix' )) {
@ trigger_error ( 'The "--no-prefix" option is deprecated since version 3.4 and will be removed in 4.0. Use the "--prefix" option with an empty string as value instead.' , E_USER_DEPRECATED );
$prefix = '' ;
}
$extractor -> setPrefix ( $prefix );
2015-06-19 16:48:24 +01:00
foreach ( $transPaths as $path ) {
2015-07-07 20:01:23 +01:00
$path .= 'views' ;
2015-06-19 16:48:24 +01:00
if ( is_dir ( $path )) {
$extractor -> extract ( $path , $extractedCatalogue );
}
}
2011-09-19 13:47:33 +01:00
2011-08-29 18:22:52 +01:00
// load any existing messages from the translation files
2013-03-11 12:57:32 +00:00
$currentCatalogue = new MessageCatalogue ( $input -> getArgument ( 'locale' ));
2016-11-28 17:54:55 +00:00
$errorIo -> comment ( 'Loading translation files...' );
2011-08-29 18:22:52 +01:00
$loader = $this -> getContainer () -> get ( 'translation.loader' );
2015-06-19 16:48:24 +01:00
foreach ( $transPaths as $path ) {
2015-07-07 20:01:23 +01:00
$path .= 'translations' ;
2015-06-19 16:48:24 +01:00
if ( is_dir ( $path )) {
$loader -> loadMessages ( $path , $currentCatalogue );
}
}
2013-03-11 12:57:32 +00:00
2016-07-09 17:21:29 +01:00
if ( null !== $domain = $input -> getOption ( 'domain' )) {
$currentCatalogue = $this -> filterCatalogue ( $currentCatalogue , $domain );
$extractedCatalogue = $this -> filterCatalogue ( $extractedCatalogue , $domain );
}
2013-03-11 12:57:32 +00:00
// process catalogues
$operation = $input -> getOption ( 'clean' )
2015-08-16 03:22:07 +01:00
? new TargetOperation ( $currentCatalogue , $extractedCatalogue )
2013-03-11 12:57:32 +00:00
: new MergeOperation ( $currentCatalogue , $extractedCatalogue );
2011-09-19 13:47:33 +01:00
2014-12-20 13:54:47 +00:00
// Exit if no messages found.
if ( ! count ( $operation -> getDomains ())) {
2016-11-28 17:54:55 +00:00
$errorIo -> warning ( 'No translation messages were found.' );
2014-12-20 13:54:47 +00:00
return ;
}
2016-01-25 11:59:24 +00:00
$resultMessage = 'Translation files were successfully updated' ;
2015-12-10 19:14:16 +00:00
2011-08-29 18:22:52 +01:00
// show compiled list of messages
2015-09-28 14:27:37 +01:00
if ( true === $input -> getOption ( 'dump-messages' )) {
$extractedMessagesCount = 0 ;
2015-12-07 12:11:35 +00:00
$io -> newLine ();
2013-03-11 12:57:32 +00:00
foreach ( $operation -> getDomains () as $domain ) {
$newKeys = array_keys ( $operation -> getNewMessages ( $domain ));
$allKeys = array_keys ( $operation -> getMessages ( $domain ));
2015-09-28 14:27:37 +01:00
2016-09-07 09:32:40 +01:00
$list = array_merge (
2015-05-09 15:43:35 +01:00
array_diff ( $allKeys , $newKeys ),
array_map ( function ( $id ) {
return sprintf ( '<fg=green>%s</>' , $id );
}, $newKeys ),
2015-06-25 13:52:11 +01:00
array_map ( function ( $id ) {
2015-05-09 15:43:35 +01:00
return sprintf ( '<fg=red>%s</>' , $id );
}, array_keys ( $operation -> getObsoleteMessages ( $domain )))
2016-09-07 09:32:40 +01:00
);
$domainMessagesCount = count ( $list );
$io -> section ( sprintf ( 'Messages extracted for domain "<info>%s</info>" (%d message%s)' , $domain , $domainMessagesCount , $domainMessagesCount > 1 ? 's' : '' ));
$io -> listing ( $list );
2015-09-28 14:27:37 +01:00
$extractedMessagesCount += $domainMessagesCount ;
2011-08-29 18:22:52 +01:00
}
2013-03-11 12:57:32 +00:00
2015-04-18 22:27:35 +01:00
if ( $input -> getOption ( 'output-format' ) == 'xlf' ) {
2016-11-28 17:54:55 +00:00
$errorIo -> comment ( 'Xliff output version is <info>1.2</info>' );
2012-05-03 17:14:01 +01:00
}
2015-09-28 14:27:37 +01:00
2016-09-07 09:32:40 +01:00
$resultMessage = sprintf ( '%d message%s successfully extracted' , $extractedMessagesCount , $extractedMessagesCount > 1 ? 's were' : ' was' );
2011-08-29 18:22:52 +01:00
}
2014-04-30 18:04:41 +01:00
if ( $input -> getOption ( 'no-backup' ) === true ) {
$writer -> disableBackup ();
}
2011-08-29 18:22:52 +01:00
// save the files
2012-05-03 17:14:01 +01:00
if ( $input -> getOption ( 'force' ) === true ) {
2016-11-28 17:54:55 +00:00
$errorIo -> comment ( 'Writing files...' );
2015-06-25 13:52:11 +01:00
2015-06-19 16:48:24 +01:00
$bundleTransPath = false ;
foreach ( $transPaths as $path ) {
2015-07-07 20:01:23 +01:00
$path .= 'translations' ;
2015-06-19 16:48:24 +01:00
if ( is_dir ( $path )) {
$bundleTransPath = $path ;
}
}
2015-11-30 13:03:45 +00:00
if ( ! $bundleTransPath ) {
$bundleTransPath = end ( $transPaths ) . 'translations' ;
2015-06-19 16:48:24 +01:00
}
2015-09-28 14:27:37 +01:00
2015-11-30 13:03:45 +00:00
$writer -> writeTranslations ( $operation -> getResult (), $input -> getOption ( 'output-format' ), array ( 'path' => $bundleTransPath , 'default_locale' => $this -> getContainer () -> getParameter ( 'kernel.default_locale' )));
2015-12-10 17:11:25 +00:00
2015-09-28 14:27:37 +01:00
if ( true === $input -> getOption ( 'dump-messages' )) {
2016-01-25 11:59:24 +00:00
$resultMessage .= ' and translation files were updated' ;
2015-09-28 14:27:37 +01:00
}
2011-08-29 18:22:52 +01:00
}
2015-05-09 15:43:35 +01:00
2016-11-28 17:54:55 +00:00
$errorIo -> success ( $resultMessage . '.' );
2011-08-29 18:22:52 +01:00
}
2016-07-09 17:21:29 +01:00
private function filterCatalogue ( MessageCatalogue $catalogue , $domain )
{
$filteredCatalogue = new MessageCatalogue ( $catalogue -> getLocale ());
if ( $messages = $catalogue -> all ( $domain )) {
$filteredCatalogue -> add ( $messages , $domain );
}
foreach ( $catalogue -> getResources () as $resource ) {
$filteredCatalogue -> addResource ( $resource );
}
if ( $metadata = $catalogue -> getMetadata ( '' , $domain )) {
foreach ( $metadata as $k => $v ) {
$filteredCatalogue -> setMetadata ( $k , $v , $domain );
}
}
return $filteredCatalogue ;
}
2011-08-29 18:22:52 +01:00
}