This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
symfony/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php

216 lines
8.7 KiB
PHP
Raw Normal View History

<?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;
use Symfony\Component\Console\Style\SymfonyStyle;
[translation][framework-bundle] Deprecated DiffOperation The ``DiffOperation`` class has been deprecated and ``TargetOperation`` should be used instead, because ``DiffOperation`` has nothing to do with 'diff', thus its class name is misleading. Also added detailed documents for all operation interface and classes. The following names should have consistent meanings for all operations: The name of ``intersection`` is temporarily introduced here to explain this issue. * [x] ``intersection`` = source &#x2229; target = {x: x &#x2208; source &#x2227; x &#x2208; target} * [x] ``all`` = **result of the operation, depends on the operation.** * [x] ``new`` = all &#x2216; source = {x: x &#x2208; all &#x2227; x &#x2209; source} * [x] ``obsolete`` = source &#x2216; all = {x: x &#x2208; source &#x2227; x &#x2209; all} The following analysis explains why ``DiffOperation`` should be deprecated. * [x] ``all`` = source &#x222a; target = {x: x &#x2208; source &#x2228; x &#x2208; target} * [x] ``new`` = all &#x2216; source = {x: x &#x2208; target &#x2227; &#x2209; source} * [x] ``obsolete`` = source &#x2216; all = {x: x &#x2208; source &#x2227; x &#x2209; source &#x2227; x &#x2209; target} = &#x2205; This absolutely makes sense. * [ ] ``all`` = intersection &#x222a; (target &#x2216; intersection) = target * [x] ``new`` = all &#x2216; source = {x: x &#x2208; target &#x2227; x &#x2209; source} * [x] ``obsolete`` = source &#x2216; all = source &#x2216; target = {x: x &#x2208; source &#x2227; x &#x2209; target} The ``all`` part is confusing because 'diff' should either mean 'relative complement' or 'symmetric difference' operation: * ``all`` = source &#x2216; target = {x: x &#x2208; source &#x2227; x &#x2209; target} * ``all`` = (source &#x2216; target) &#x222a; (target &#x2216; source) = {x: x &#x2208; source &#x2227; x &#x2209; target &#x2228; x &#x2208; target &#x2227; x &#x2209; source} * ``all`` = intersection &#x222a; (target &#x2216; intersection) = target So the name of ``DiffOperation`` is misleading and inappropriate. Unfortunately, there is no corresponding set operation for this class, so it's hard to give it an apppriate name. From my point of view, I believe the most accurate name for this class should be ``TargetOperation`` because its result is same as the target set. | Q | A | ------------- | --- | Bug fix? | no | New feature? | no | BC breaks? | no | Deprecations? | yes | Tests pass? | yes | Fixed tickets | n/a | License | MIT | Doc PR | n/a
2015-08-16 03:22:07 +01:00
use Symfony\Component\Translation\Catalogue\TargetOperation;
use Symfony\Component\Translation\Catalogue\MergeOperation;
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;
/**
* A command that parses templates to extract translation messages and adds them
* into the translation files.
2011-09-19 13:47:33 +01:00
*
* @author Michel Salib <michelsalib@hotmail.com>
*/
class TranslationUpdateCommand extends ContainerAwareCommand
{
/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->setName('translation:update')
->setDefinition(array(
new InputArgument('locale', InputArgument::REQUIRED, 'The locale'),
new InputArgument('bundle', InputArgument::OPTIONAL, 'The bundle name or directory where to load the messages, defaults to app/Resources folder'),
new InputOption('prefix', null, InputOption::VALUE_OPTIONAL, 'Override the default prefix', '__'),
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'),
new InputOption('no-backup', null, InputOption::VALUE_NONE, 'Should backup be disabled'),
new InputOption('clean', null, InputOption::VALUE_NONE, 'Should clean not found messages'),
))
->setDescription('Updates the translation file')
2015-12-21 11:01:57 +00:00
->setHelp(<<<'EOF'
The <info>%command.name%</info> command extract translation strings from templates
of a given bundle or the app folder. It can display them or merge the new ones into the translation files.
When new translation strings are found it can automatically add a prefix to the translation
message.
Example running against a Bundle (AcmeBundle)
<info>php %command.full_name% --dump-messages en AcmeBundle</info>
<info>php %command.full_name% --force --prefix="new_" fr AcmeBundle</info>
Example running against app messages (app/Resources folder)
<info>php %command.full_name% --dump-messages en</info>
<info>php %command.full_name% --force --prefix="new_" fr</info>
EOF
)
;
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$io = new SymfonyStyle($input, $output);
// check presence of force or dump-message
if ($input->getOption('force') !== true && $input->getOption('dump-messages') !== true) {
$io->error('You must choose one of --force or --dump-messages');
2011-10-29 11:05:45 +01:00
return 1;
}
2011-09-19 13:47:33 +01:00
// check format
$writer = $this->getContainer()->get('translation.writer');
$supportedFormats = $writer->getFormats();
if (!in_array($input->getOption('output-format'), $supportedFormats)) {
$io->error(array('Wrong output format', 'Supported formats are: '.implode(', ', $supportedFormats).'.'));
2011-10-29 11:05:45 +01:00
return 1;
}
$kernel = $this->getContainer()->get('kernel');
// Define Root Path to App folder
$transPaths = array($kernel->getRootDir().'/Resources/');
$currentName = 'app folder';
// Override with provided Bundle info
if (null !== $input->getArgument('bundle')) {
try {
$foundBundle = $kernel->getBundle($input->getArgument('bundle'));
$transPaths = array(
$foundBundle->getPath().'/Resources/',
sprintf('%s/Resources/%s/', $kernel->getRootDir(), $foundBundle->getName()),
);
$currentName = $foundBundle->getName();
} catch (\InvalidArgumentException $e) {
// such a bundle does not exist, so treat the argument as path
$transPaths = array($input->getArgument('bundle').'/Resources/');
$currentName = $transPaths[0];
if (!is_dir($transPaths[0])) {
throw new \InvalidArgumentException(sprintf('<error>"%s" is neither an enabled bundle nor a directory.</error>', $transPaths[0]));
}
}
}
$io->title('Translation Messages Extractor and Dumper');
$io->comment(sprintf('Generating "<info>%s</info>" translation files for "<info>%s</info>"', $input->getArgument('locale'), $currentName));
// load any messages from templates
$extractedCatalogue = new MessageCatalogue($input->getArgument('locale'));
$io->comment('Parsing templates...');
$extractor = $this->getContainer()->get('translation.extractor');
$extractor->setPrefix($input->getOption('prefix'));
foreach ($transPaths as $path) {
$path .= 'views';
if (is_dir($path)) {
$extractor->extract($path, $extractedCatalogue);
}
}
2011-09-19 13:47:33 +01:00
// load any existing messages from the translation files
$currentCatalogue = new MessageCatalogue($input->getArgument('locale'));
$io->comment('Loading translation files...');
$loader = $this->getContainer()->get('translation.loader');
foreach ($transPaths as $path) {
$path .= 'translations';
if (is_dir($path)) {
$loader->loadMessages($path, $currentCatalogue);
}
}
// process catalogues
$operation = $input->getOption('clean')
[translation][framework-bundle] Deprecated DiffOperation The ``DiffOperation`` class has been deprecated and ``TargetOperation`` should be used instead, because ``DiffOperation`` has nothing to do with 'diff', thus its class name is misleading. Also added detailed documents for all operation interface and classes. The following names should have consistent meanings for all operations: The name of ``intersection`` is temporarily introduced here to explain this issue. * [x] ``intersection`` = source &#x2229; target = {x: x &#x2208; source &#x2227; x &#x2208; target} * [x] ``all`` = **result of the operation, depends on the operation.** * [x] ``new`` = all &#x2216; source = {x: x &#x2208; all &#x2227; x &#x2209; source} * [x] ``obsolete`` = source &#x2216; all = {x: x &#x2208; source &#x2227; x &#x2209; all} The following analysis explains why ``DiffOperation`` should be deprecated. * [x] ``all`` = source &#x222a; target = {x: x &#x2208; source &#x2228; x &#x2208; target} * [x] ``new`` = all &#x2216; source = {x: x &#x2208; target &#x2227; &#x2209; source} * [x] ``obsolete`` = source &#x2216; all = {x: x &#x2208; source &#x2227; x &#x2209; source &#x2227; x &#x2209; target} = &#x2205; This absolutely makes sense. * [ ] ``all`` = intersection &#x222a; (target &#x2216; intersection) = target * [x] ``new`` = all &#x2216; source = {x: x &#x2208; target &#x2227; x &#x2209; source} * [x] ``obsolete`` = source &#x2216; all = source &#x2216; target = {x: x &#x2208; source &#x2227; x &#x2209; target} The ``all`` part is confusing because 'diff' should either mean 'relative complement' or 'symmetric difference' operation: * ``all`` = source &#x2216; target = {x: x &#x2208; source &#x2227; x &#x2209; target} * ``all`` = (source &#x2216; target) &#x222a; (target &#x2216; source) = {x: x &#x2208; source &#x2227; x &#x2209; target &#x2228; x &#x2208; target &#x2227; x &#x2209; source} * ``all`` = intersection &#x222a; (target &#x2216; intersection) = target So the name of ``DiffOperation`` is misleading and inappropriate. Unfortunately, there is no corresponding set operation for this class, so it's hard to give it an apppriate name. From my point of view, I believe the most accurate name for this class should be ``TargetOperation`` because its result is same as the target set. | Q | A | ------------- | --- | Bug fix? | no | New feature? | no | BC breaks? | no | Deprecations? | yes | Tests pass? | yes | Fixed tickets | n/a | License | MIT | Doc PR | n/a
2015-08-16 03:22:07 +01:00
? new TargetOperation($currentCatalogue, $extractedCatalogue)
: new MergeOperation($currentCatalogue, $extractedCatalogue);
2011-09-19 13:47:33 +01:00
// Exit if no messages found.
if (!count($operation->getDomains())) {
$io->warning('No translation messages were found.');
return;
}
$resultMessage = 'Translation files were successfully updated.';
// show compiled list of messages
if (true === $input->getOption('dump-messages')) {
$extractedMessagesCount = 0;
$io->newLine();
foreach ($operation->getDomains() as $domain) {
$newKeys = array_keys($operation->getNewMessages($domain));
$allKeys = array_keys($operation->getMessages($domain));
$domainMessagesCount = count($newKeys) + count($allKeys);
$io->section(sprintf('Messages extracted for domain "<info>%s</info>" (%d messages)', $domain, $domainMessagesCount));
$io->listing(array_merge(
array_diff($allKeys, $newKeys),
array_map(function ($id) {
return sprintf('<fg=green>%s</>', $id);
}, $newKeys),
array_map(function ($id) {
return sprintf('<fg=red>%s</>', $id);
}, array_keys($operation->getObsoleteMessages($domain)))
));
$extractedMessagesCount += $domainMessagesCount;
}
if ($input->getOption('output-format') == 'xlf') {
$io->comment('Xliff output version is <info>1.2</info>');
2012-05-03 17:14:01 +01:00
}
$resultMessage = sprintf('%d messages were successfully extracted', $extractedMessagesCount);
}
2014-04-30 18:04:41 +01:00
if ($input->getOption('no-backup') === true) {
$writer->disableBackup();
}
// save the files
2012-05-03 17:14:01 +01:00
if ($input->getOption('force') === true) {
$io->comment('Writing files...');
$bundleTransPath = false;
foreach ($transPaths as $path) {
$path .= 'translations';
if (is_dir($path)) {
$bundleTransPath = $path;
}
}
if (!$bundleTransPath) {
$bundleTransPath = end($transPaths).'translations';
}
$writer->writeTranslations($operation->getResult(), $input->getOption('output-format'), array('path' => $bundleTransPath, 'default_locale' => $this->getContainer()->getParameter('kernel.default_locale')));
if (true === $input->getOption('dump-messages')) {
$resultMessage .= ' and translation files were updated.';
}
}
$io->success($resultMessage);
}
}