From 597a3107bcd6ce184ea7a35108616a44c22e5232 Mon Sep 17 00:00:00 2001 From: florianv Date: Mon, 20 Jan 2014 00:09:51 +0100 Subject: [PATCH 1/2] Added a translation:debug command --- .../Bundle/FrameworkBundle/CHANGELOG.md | 1 + .../Command/TranslationDebugCommand.php | 225 ++++++++++++++++++ 2 files changed, 226 insertions(+) create mode 100644 src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index ffb90d2ed671b..9658d5729c648 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 2.5.0 ----- + * Added `translation:debug` command * Added `config:debug` command * Added `yaml:lint` command * Deprecated the `RouterApacheDumperCommand` which will be removed in Symfony 3.0. diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php new file mode 100644 index 0000000000000..ccdeb59e36571 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php @@ -0,0 +1,225 @@ + + * + * 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\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; + +/** + * Helps finding unused or missing translation messages in a given locale + * and comparing them with the fallback ones. + * + * @author Florian Voutzinos + */ +class TranslationDebugCommand extends ContainerAwareCommand +{ + const MESSAGE_MISSING = 0; + const MESSAGE_UNUSED = 1; + const MESSAGE_EQUALS_FALLBACK = 2; + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('translation:debug') + ->setDefinition(array( + new InputArgument('locale', InputArgument::REQUIRED, 'The locale'), + new InputArgument('bundle', InputArgument::REQUIRED, 'The bundle name'), + new InputOption('domain', null, InputOption::VALUE_OPTIONAL, 'The messages domain'), + new InputOption('only-missing', null, InputOption::VALUE_NONE, 'Displays only missing messages'), + new InputOption('only-unused', null, InputOption::VALUE_NONE, 'Displays only unused messages'), + )) + ->setDescription('Displays translation messages informations') + ->setHelp(<<%command.name% command helps finding unused or missing translation messages and +comparing them with the fallback ones by inspecting the templates and translation files of a given bundle. + +You can display informations about a bundle translations in a specific locale: + +php %command.full_name% en AcmeDemoBundle + +You can also specify a translation domain for the search: + +php %command.full_name% --domain=messages en AcmeDemoBundle + +You can only display missing messages: + +php %command.full_name% --only-missing en AcmeDemoBundle + +You can only display unused messages: + +php %command.full_name% --only-unused en AcmeDemoBundle +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $locale = $input->getArgument('locale'); + $domain = $input->getOption('domain'); + $bundle = $this->getContainer()->get('kernel')->getBundle($input->getArgument('bundle')); + $loader = $this->getContainer()->get('translation.loader'); + + // Extract used messages + $extractedCatalogue = new MessageCatalogue($locale); + $this->getContainer()->get('translation.extractor') + ->extract($bundle->getPath().'/Resources/views/', $extractedCatalogue); + + // Load defined messages + $currentCatalogue = new MessageCatalogue($locale); + $loader->loadMessages($bundle->getPath().'/Resources/translations', $currentCatalogue); + + // Merge defined and extracted messages to get all message ids + $mergeOperation = new MergeOperation($extractedCatalogue, $currentCatalogue); + $allMessages = $mergeOperation->getResult()->all($domain); + if (null !== $domain) { + $allMessages = array($domain => $allMessages); + } + + // No defined or extracted messages + if (empty($allMessages) || null !== $domain && empty($allMessages[$domain])) { + $outputMessage = sprintf('No defined or extracted messages for locale "%s"', $locale); + + if (null !== $domain) { + $outputMessage .= sprintf(' and domain "%s"', $domain); + } + + $output->writeln($outputMessage); + + return; + } + + // Load the fallback catalogues + $fallbackCatalogues = array(); + foreach ($this->getContainer()->get('translator')->getFallbackLocales() as $fallbackLocale) { + if ($fallbackLocale === $locale) { + continue; + } + + $fallbackCatalogue = new MessageCatalogue($fallbackLocale); + $loader->loadMessages($bundle->getPath().'/Resources/translations', $fallbackCatalogue); + $fallbackCatalogues[] = $fallbackCatalogue; + } + + // Display legend + $output->writeln(sprintf('Legend: %s Missing message %s Unused message %s Equals fallback message', + $this->formatState(self::MESSAGE_MISSING), + $this->formatState(self::MESSAGE_UNUSED), + $this->formatState(self::MESSAGE_EQUALS_FALLBACK) + )); + + /** @var \Symfony\Component\Console\Helper\TableHelper $tableHelper */ + $tableHelper = $this->getHelperSet()->get('table'); + + // Display header line + $headers = array('State(s)', 'Id', sprintf('Message Preview (%s)', $locale)); + foreach ($fallbackCatalogues as $fallbackCatalogue) { + $headers[] = sprintf('Fallback Message Preview (%s)', $fallbackCatalogue->getLocale()); + } + $tableHelper->setHeaders($headers); + + // Iterate all message ids and determine their state + foreach ($allMessages as $domain => $messages) { + foreach (array_keys($messages) as $messageId) { + $value = $currentCatalogue->get($messageId, $domain); + $states = array(); + + if ($extractedCatalogue->defines($messageId, $domain)) { + if (!$currentCatalogue->defines($messageId, $domain)) { + $states[] = self::MESSAGE_MISSING; + } + } elseif ($currentCatalogue->defines($messageId, $domain)) { + $states[] = self::MESSAGE_UNUSED; + } + + if (!in_array(self::MESSAGE_UNUSED, $states) && true === $input->getOption('only-unused') + || !in_array(self::MESSAGE_MISSING, $states) && true === $input->getOption('only-missing')) { + continue; + } + + foreach ($fallbackCatalogues as $fallbackCatalogue) { + if ($fallbackCatalogue->defines($messageId, $domain) + && $value === $fallbackCatalogue->get($messageId, $domain)) { + $states[] = self::MESSAGE_EQUALS_FALLBACK; + break; + } + } + + $row = array($this->formatStates($states), $this->formatId($messageId), $this->sanitizeString($value)); + foreach ($fallbackCatalogues as $fallbackCatalogue) { + $row[] = $this->sanitizeString($fallbackCatalogue->get($messageId, $domain)); + } + + $tableHelper->addRow($row); + } + } + + $tableHelper->render($output); + } + + private function formatState($state) + { + if (self::MESSAGE_MISSING === $state) { + return 'x'; + } + + if (self::MESSAGE_UNUSED === $state) { + return 'o'; + } + + if (self::MESSAGE_EQUALS_FALLBACK === $state) { + return '='; + } + + return $state; + } + + private function formatStates(array $states) + { + $result = array(); + foreach ($states as $state) { + $result[] = $this->formatState($state); + } + + return implode(' ', $result); + } + + private function formatId($id) + { + return sprintf('%s', $id); + } + + private function sanitizeString($string, $lenght = 40) + { + $string = trim(preg_replace('/\s+/', ' ', $string)); + + if (function_exists('mb_strlen') && false !== $encoding = mb_detect_encoding($string)) { + if (mb_strlen($string, $encoding) > $lenght) { + return mb_substr($string, 0, $lenght - 3, $encoding).'...'; + } + } elseif (strlen($string) > $lenght) { + return substr($string, 0, $lenght - 3).'...'; + } + + return $string; + } +} From f039bde3d8a6c9b88b7b050a73b308fd0a3379f3 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 3 Mar 2014 17:28:48 +0100 Subject: [PATCH 2/2] [FrameworkBundle] fixed edge cases for translation:debug and tweaked the output --- .../Command/TranslationDebugCommand.php | 76 ++++++++++--------- 1 file changed, 41 insertions(+), 35 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php index ccdeb59e36571..a1f136005336e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php @@ -12,11 +12,13 @@ namespace Symfony\Bundle\FrameworkBundle\Command; use Symfony\Component\Translation\Catalogue\MergeOperation; +use Symfony\Component\Console\Helper\Table; 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; +use Symfony\Component\Translation\Translator; /** * Helps finding unused or missing translation messages in a given locale @@ -46,10 +48,11 @@ protected function configure() )) ->setDescription('Displays translation messages informations') ->setHelp(<<%command.name% command helps finding unused or missing translation messages and -comparing them with the fallback ones by inspecting the templates and translation files of a given bundle. +The %command.name% command helps finding unused or missing translation +messages and comparing them with the fallback ones by inspecting the +templates and translation files of a given bundle. -You can display informations about a bundle translations in a specific locale: +You can display information about bundle translations in a specific locale: php %command.full_name% en AcmeDemoBundle @@ -81,12 +84,13 @@ protected function execute(InputInterface $input, OutputInterface $output) // Extract used messages $extractedCatalogue = new MessageCatalogue($locale); - $this->getContainer()->get('translation.extractor') - ->extract($bundle->getPath().'/Resources/views/', $extractedCatalogue); + $this->getContainer()->get('translation.extractor')->extract($bundle->getPath().'/Resources/views', $extractedCatalogue); // Load defined messages $currentCatalogue = new MessageCatalogue($locale); - $loader->loadMessages($bundle->getPath().'/Resources/translations', $currentCatalogue); + if (is_dir($bundle->getPath().'/Resources/translations')) { + $loader->loadMessages($bundle->getPath().'/Resources/translations', $currentCatalogue); + } // Merge defined and extracted messages to get all message ids $mergeOperation = new MergeOperation($extractedCatalogue, $currentCatalogue); @@ -110,32 +114,28 @@ protected function execute(InputInterface $input, OutputInterface $output) // Load the fallback catalogues $fallbackCatalogues = array(); - foreach ($this->getContainer()->get('translator')->getFallbackLocales() as $fallbackLocale) { - if ($fallbackLocale === $locale) { - continue; - } + $translator = $this->getContainer()->get('translator'); + if ($translator instanceof Translator) { + foreach ($translator->getFallbackLocales() as $fallbackLocale) { + if ($fallbackLocale === $locale) { + continue; + } - $fallbackCatalogue = new MessageCatalogue($fallbackLocale); - $loader->loadMessages($bundle->getPath().'/Resources/translations', $fallbackCatalogue); - $fallbackCatalogues[] = $fallbackCatalogue; + $fallbackCatalogue = new MessageCatalogue($fallbackLocale); + $loader->loadMessages($bundle->getPath().'/Resources/translations', $fallbackCatalogue); + $fallbackCatalogues[] = $fallbackCatalogue; + } } - // Display legend - $output->writeln(sprintf('Legend: %s Missing message %s Unused message %s Equals fallback message', - $this->formatState(self::MESSAGE_MISSING), - $this->formatState(self::MESSAGE_UNUSED), - $this->formatState(self::MESSAGE_EQUALS_FALLBACK) - )); - - /** @var \Symfony\Component\Console\Helper\TableHelper $tableHelper */ - $tableHelper = $this->getHelperSet()->get('table'); + /** @var \Symfony\Component\Console\Helper\Table $table */ + $table = new Table($output); // Display header line $headers = array('State(s)', 'Id', sprintf('Message Preview (%s)', $locale)); foreach ($fallbackCatalogues as $fallbackCatalogue) { $headers[] = sprintf('Fallback Message Preview (%s)', $fallbackCatalogue->getLocale()); } - $tableHelper->setHeaders($headers); + $table->setHeaders($headers); // Iterate all message ids and determine their state foreach ($allMessages as $domain => $messages) { @@ -157,9 +157,9 @@ protected function execute(InputInterface $input, OutputInterface $output) } foreach ($fallbackCatalogues as $fallbackCatalogue) { - if ($fallbackCatalogue->defines($messageId, $domain) - && $value === $fallbackCatalogue->get($messageId, $domain)) { + if ($fallbackCatalogue->defines($messageId, $domain) && $value === $fallbackCatalogue->get($messageId, $domain)) { $states[] = self::MESSAGE_EQUALS_FALLBACK; + break; } } @@ -169,25 +169,31 @@ protected function execute(InputInterface $input, OutputInterface $output) $row[] = $this->sanitizeString($fallbackCatalogue->get($messageId, $domain)); } - $tableHelper->addRow($row); + $table->addRow($row); } } - $tableHelper->render($output); + $table->render(); + + $output->writeln(''); + $output->writeln('Legend:'); + $output->writeln(sprintf(' %s Missing message', $this->formatState(self::MESSAGE_MISSING))); + $output->writeln(sprintf(' %s Unused message', $this->formatState(self::MESSAGE_UNUSED))); + $output->writeln(sprintf(' %s Same as the fallback message', $this->formatState(self::MESSAGE_EQUALS_FALLBACK))); } private function formatState($state) { if (self::MESSAGE_MISSING === $state) { - return 'x'; + return 'x'; } if (self::MESSAGE_UNUSED === $state) { - return 'o'; + return 'o'; } if (self::MESSAGE_EQUALS_FALLBACK === $state) { - return '='; + return '='; } return $state; @@ -208,16 +214,16 @@ private function formatId($id) return sprintf('%s', $id); } - private function sanitizeString($string, $lenght = 40) + private function sanitizeString($string, $length = 40) { $string = trim(preg_replace('/\s+/', ' ', $string)); if (function_exists('mb_strlen') && false !== $encoding = mb_detect_encoding($string)) { - if (mb_strlen($string, $encoding) > $lenght) { - return mb_substr($string, 0, $lenght - 3, $encoding).'...'; + if (mb_strlen($string, $encoding) > $length) { + return mb_substr($string, 0, $length - 3, $encoding).'...'; } - } elseif (strlen($string) > $lenght) { - return substr($string, 0, $lenght - 3).'...'; + } elseif (strlen($string) > $length) { + return substr($string, 0, $length - 3).'...'; } return $string; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy