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.