diff --git a/src/Symfony/Bridge/Twig/CHANGELOG.md b/src/Symfony/Bridge/Twig/CHANGELOG.md index 978139032141..b4df596fa2c0 100644 --- a/src/Symfony/Bridge/Twig/CHANGELOG.md +++ b/src/Symfony/Bridge/Twig/CHANGELOG.md @@ -7,6 +7,7 @@ CHANGELOG * added LogoutUrlExtension (provides `logout_url` and `logout_path`) * added an HttpFoundation extension (provides the `absolute_url` and the `relative_path` functions) * added AssetExtension (provides the `asset` and `asset_version` functions) + * Added possibility to extract translation messages from a file or files besides extracting from a directory 2.5.0 ----- diff --git a/src/Symfony/Bridge/Twig/Tests/Fixtures/extractor/with_translations.html.twig b/src/Symfony/Bridge/Twig/Tests/Fixtures/extractor/with_translations.html.twig new file mode 100644 index 000000000000..8cdb42d293c2 --- /dev/null +++ b/src/Symfony/Bridge/Twig/Tests/Fixtures/extractor/with_translations.html.twig @@ -0,0 +1 @@ +

{{ 'Hi!'|trans }}

diff --git a/src/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php b/src/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php index a483ab857aa5..570e5c26fbc1 100644 --- a/src/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php @@ -83,4 +83,42 @@ public function testExtractSyntaxError() $extractor = new TwigExtractor($twig); $extractor->extract(__DIR__.'/../Fixtures', new MessageCatalogue('en')); } + + /** + * @dataProvider resourceProvider + */ + public function testExtractWithFiles($resource) + { + $loader = new \Twig_Loader_Array(array()); + $twig = new \Twig_Environment($loader, array( + 'strict_variables' => true, + 'debug' => true, + 'cache' => false, + 'autoescape' => false, + )); + $twig->addExtension(new TranslationExtension($this->getMock('Symfony\Component\Translation\TranslatorInterface'))); + + $extractor = new TwigExtractor($twig); + $catalogue = new MessageCatalogue('en'); + $extractor->extract($resource, $catalogue); + + $this->assertTrue($catalogue->has('Hi!', 'messages')); + $this->assertEquals('Hi!', $catalogue->get('Hi!', 'messages')); + } + + /** + * @return array + */ + public function resourceProvider() + { + $directory = __DIR__.'/../Fixtures/extractor/'; + + return array( + array($directory.'with_translations.html.twig'), + array(array($directory.'with_translations.html.twig')), + array(array(new \SplFileInfo($directory.'with_translations.html.twig'))), + array(new \ArrayObject(array($directory.'with_translations.html.twig'))), + array(new \ArrayObject(array(new \SplFileInfo($directory.'with_translations.html.twig')))), + ); + } } diff --git a/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php b/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php index 2045bf2755bc..ad3fd9fe3330 100644 --- a/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php +++ b/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php @@ -12,6 +12,7 @@ namespace Symfony\Bridge\Twig\Translation; use Symfony\Component\Finder\Finder; +use Symfony\Component\Translation\Extractor\AbstractFileExtractor; use Symfony\Component\Translation\Extractor\ExtractorInterface; use Symfony\Component\Translation\MessageCatalogue; @@ -21,7 +22,7 @@ * @author Michel Salib * @author Fabien Potencier */ -class TwigExtractor implements ExtractorInterface +class TwigExtractor extends AbstractFileExtractor implements ExtractorInterface { /** * Default domain for found messages. @@ -52,11 +53,9 @@ public function __construct(\Twig_Environment $twig) /** * {@inheritdoc} */ - public function extract($directory, MessageCatalogue $catalogue) + public function extract($resource, MessageCatalogue $catalogue) { - // load any existing translation files - $finder = new Finder(); - $files = $finder->files()->name('*.twig')->sortByName()->in($directory); + $files = $this->extractFiles($resource); foreach ($files as $file) { try { $this->extractTemplate(file_get_contents($file->getPathname()), $catalogue); @@ -89,4 +88,26 @@ protected function extractTemplate($template, MessageCatalogue $catalogue) $visitor->disable(); } + + /** + * @param string $file + * + * @return bool + */ + protected function canBeExtracted($file) + { + return $this->isFile($file) && 'twig' === pathinfo($file, PATHINFO_EXTENSION); + } + + /** + * @param string|array $directory + * + * @return array + */ + protected function extractFromDirectory($directory) + { + $finder = new Finder(); + + return $finder->files()->name('*.twig')->in($directory); + } } diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index e64b63d5d591..d64f9eaeec8a 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -28,7 +28,7 @@ "symfony/intl": "~2.3|~3.0.0", "symfony/routing": "~2.2|~3.0.0", "symfony/templating": "~2.1|~3.0.0", - "symfony/translation": "~2.2|~3.0.0", + "symfony/translation": "~2.7|~3.0.0", "symfony/yaml": "~2.0,>=2.0.5|~3.0.0", "symfony/security": "~2.6|~3.0.0", "symfony/stopwatch": "~2.2|~3.0.0", diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index 21d13c6040f1..f4d69e73db08 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +2.7.0 +----- + + * Added possibility to extract translation messages from a file or files besides extracting from a directory + 2.6.0 ----- diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/PhpExtractorTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/PhpExtractorTest.php index 135c79635411..420d2f2d71dd 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/PhpExtractorTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/PhpExtractorTest.php @@ -17,7 +17,12 @@ class PhpExtractorTest extends TestCase { - public function testExtraction() + /** + * @dataProvider resourcesProvider + * + * @param array|string $resource + */ + public function testExtraction($resource) { // Arrange $extractor = new PhpExtractor(); @@ -25,7 +30,7 @@ public function testExtraction() $catalogue = new MessageCatalogue('en'); // Act - $extractor->extract(__DIR__.'/../Fixtures/Resources/views/', $catalogue); + $extractor->extract($resource, $catalogue); $expectedHeredoc = <<assertEquals($expectedCatalogue, $actualCatalogue); } + + public function resourcesProvider() + { + $directory = __DIR__.'/../Fixtures/Resources/views/'; + $splFiles = array(); + foreach (new \DirectoryIterator($directory) as $fileInfo) { + if ($fileInfo->isDot()) { + continue; + } + if ('translation.html.php' === $fileInfo->getBasename()) { + $phpFile = $fileInfo->getPathname(); + } + $splFiles[] = $fileInfo->getFileInfo(); + } + + return array( + array($directory), + array($phpFile), + array(glob($directory.'*')), + array($splFiles), + array(new \ArrayObject(glob($directory.'*'))), + array(new \ArrayObject($splFiles)), + ); + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Translation/PhpExtractor.php b/src/Symfony/Bundle/FrameworkBundle/Translation/PhpExtractor.php index 5007a86bcaa0..22c8759236e3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Translation/PhpExtractor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Translation/PhpExtractor.php @@ -12,6 +12,7 @@ namespace Symfony\Bundle\FrameworkBundle\Translation; use Symfony\Component\Finder\Finder; +use Symfony\Component\Translation\Extractor\AbstractFileExtractor; use Symfony\Component\Translation\MessageCatalogue; use Symfony\Component\Translation\Extractor\ExtractorInterface; @@ -20,7 +21,7 @@ * * @author Michel Salib */ -class PhpExtractor implements ExtractorInterface +class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface { const MESSAGE_TOKEN = 300; @@ -54,11 +55,9 @@ class PhpExtractor implements ExtractorInterface /** * {@inheritdoc} */ - public function extract($directory, MessageCatalogue $catalog) + public function extract($resource, MessageCatalogue $catalog) { - // load any existing translation files - $finder = new Finder(); - $files = $finder->files()->name('*.php')->in($directory); + $files = $this->extractFiles($resource); foreach ($files as $file) { $this->parseTokens(token_get_all(file_get_contents($file)), $catalog); } @@ -174,4 +173,28 @@ protected function parseTokens($tokens, MessageCatalogue $catalog) } } } + + /** + * @param string $file + * + * @throws \InvalidArgumentException + * + * @return bool + */ + protected function canBeExtracted($file) + { + return $this->isFile($file) && 'php' === pathinfo($file, PATHINFO_EXTENSION); + } + + /** + * @param string|array $directory + * + * @return array + */ + protected function extractFromDirectory($directory) + { + $finder = new Finder(); + + return $finder->files()->name('*.php')->in($directory); + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index e00601544a03..98012bdaccb9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -29,7 +29,7 @@ "symfony/security-csrf": "~2.6|~3.0.0", "symfony/stopwatch": "~2.3|~3.0.0", "symfony/templating": "~2.1|~3.0.0", - "symfony/translation": "~2.6|~3.0.0", + "symfony/translation": "~2.7|~3.0.0", "doctrine/annotations": "~1.0" }, "require-dev": { diff --git a/src/Symfony/Component/Translation/Extractor/AbstractFileExtractor.php b/src/Symfony/Component/Translation/Extractor/AbstractFileExtractor.php new file mode 100644 index 000000000000..196bc3341060 --- /dev/null +++ b/src/Symfony/Component/Translation/Extractor/AbstractFileExtractor.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Extractor; + +/** + * Base class used by classes that extract translation messages from files. + * + * @author Marcos D. Sánchez + */ +abstract class AbstractFileExtractor +{ + /** + * @param string|array $resource files, a file or a directory + * + * @return array + */ + protected function extractFiles($resource) + { + if (is_array($resource) || $resource instanceof \Traversable) { + $files = array(); + foreach ($resource as $file) { + if ($this->canBeExtracted($file)) { + $files[] = $this->toSplFileInfo($file); + } + } + } elseif (is_file($resource)) { + $files = $this->canBeExtracted($resource) ? array($this->toSplFileInfo($resource)) : array(); + } else { + $files = $this->extractFromDirectory($resource); + } + + return $files; + } + + /** + * @param string $file + * + * @return \SplFileInfo + */ + private function toSplFileInfo($file) + { + return ($file instanceof \SplFileInfo) ? $file : new \SplFileInfo($file); + } + + /** + * @param string $file + * + * @throws \InvalidArgumentException + * + * @return bool + */ + protected function isFile($file) + { + if (!is_file($file)) { + throw new \InvalidArgumentException(sprintf('The "%s" file does not exist.', $file)); + } + + return true; + } + + /** + * @param string $file + * + * @return bool + */ + abstract protected function canBeExtracted($file); + + /** + * @param string|array $resource files, a file or a directory + * + * @return array files to be extracted + */ + abstract protected function extractFromDirectory($resource); +} diff --git a/src/Symfony/Component/Translation/Extractor/ExtractorInterface.php b/src/Symfony/Component/Translation/Extractor/ExtractorInterface.php index 6f877c31e724..438f80b32f05 100644 --- a/src/Symfony/Component/Translation/Extractor/ExtractorInterface.php +++ b/src/Symfony/Component/Translation/Extractor/ExtractorInterface.php @@ -14,7 +14,7 @@ use Symfony\Component\Translation\MessageCatalogue; /** - * Extracts translation messages from a template directory to the catalogue. + * Extracts translation messages from a directory or files to the catalogue. * New found messages are injected to the catalogue using the prefix. * * @author Michel Salib @@ -22,12 +22,12 @@ interface ExtractorInterface { /** - * Extracts translation messages from a template directory to the catalogue. + * Extracts translation messages from files, a file or a directory to the catalogue. * - * @param string $directory The path to look into + * @param string|array $resource files, a file or a directory * @param MessageCatalogue $catalogue The catalogue */ - public function extract($directory, MessageCatalogue $catalogue); + public function extract($resource, MessageCatalogue $catalogue); /** * Sets the prefix that should be used for new found messages.