[Translation][Extractor] Allow extracting an array of files besides extracting a directory

This commit is contained in:
Marcos Sánchez 2015-03-20 16:00:27 -03:00 committed by Fabien Potencier
parent 1a4d7d7e48
commit 9d6596c69b
11 changed files with 219 additions and 18 deletions

View File

@ -7,6 +7,7 @@ CHANGELOG
* added LogoutUrlExtension (provides `logout_url` and `logout_path`) * added LogoutUrlExtension (provides `logout_url` and `logout_path`)
* added an HttpFoundation extension (provides the `absolute_url` and the `relative_path` functions) * added an HttpFoundation extension (provides the `absolute_url` and the `relative_path` functions)
* added AssetExtension (provides the `asset` and `asset_version` 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 2.5.0
----- -----

View File

@ -0,0 +1 @@
<h1>{{ 'Hi!'|trans }}</h1>

View File

@ -83,4 +83,42 @@ class TwigExtractorTest extends \PHPUnit_Framework_TestCase
$extractor = new TwigExtractor($twig); $extractor = new TwigExtractor($twig);
$extractor->extract(__DIR__.'/../Fixtures', new MessageCatalogue('en')); $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')))),
);
}
} }

View File

@ -12,6 +12,7 @@
namespace Symfony\Bridge\Twig\Translation; namespace Symfony\Bridge\Twig\Translation;
use Symfony\Component\Finder\Finder; use Symfony\Component\Finder\Finder;
use Symfony\Component\Translation\Extractor\AbstractFileExtractor;
use Symfony\Component\Translation\Extractor\ExtractorInterface; use Symfony\Component\Translation\Extractor\ExtractorInterface;
use Symfony\Component\Translation\MessageCatalogue; use Symfony\Component\Translation\MessageCatalogue;
@ -21,7 +22,7 @@ use Symfony\Component\Translation\MessageCatalogue;
* @author Michel Salib <michelsalib@hotmail.com> * @author Michel Salib <michelsalib@hotmail.com>
* @author Fabien Potencier <fabien@symfony.com> * @author Fabien Potencier <fabien@symfony.com>
*/ */
class TwigExtractor implements ExtractorInterface class TwigExtractor extends AbstractFileExtractor implements ExtractorInterface
{ {
/** /**
* Default domain for found messages. * Default domain for found messages.
@ -52,11 +53,9 @@ class TwigExtractor implements ExtractorInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function extract($directory, MessageCatalogue $catalogue) public function extract($resource, MessageCatalogue $catalogue)
{ {
// load any existing translation files $files = $this->extractFiles($resource);
$finder = new Finder();
$files = $finder->files()->name('*.twig')->sortByName()->in($directory);
foreach ($files as $file) { foreach ($files as $file) {
try { try {
$this->extractTemplate(file_get_contents($file->getPathname()), $catalogue); $this->extractTemplate(file_get_contents($file->getPathname()), $catalogue);
@ -89,4 +88,26 @@ class TwigExtractor implements ExtractorInterface
$visitor->disable(); $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);
}
} }

View File

@ -28,7 +28,7 @@
"symfony/intl": "~2.3|~3.0.0", "symfony/intl": "~2.3|~3.0.0",
"symfony/routing": "~2.2|~3.0.0", "symfony/routing": "~2.2|~3.0.0",
"symfony/templating": "~2.1|~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/yaml": "~2.0,>=2.0.5|~3.0.0",
"symfony/security": "~2.6|~3.0.0", "symfony/security": "~2.6|~3.0.0",
"symfony/stopwatch": "~2.2|~3.0.0", "symfony/stopwatch": "~2.2|~3.0.0",

View File

@ -1,6 +1,11 @@
CHANGELOG CHANGELOG
========= =========
2.7.0
-----
* Added possibility to extract translation messages from a file or files besides extracting from a directory
2.6.0 2.6.0
----- -----

View File

@ -17,7 +17,12 @@ use Symfony\Component\Translation\MessageCatalogue;
class PhpExtractorTest extends TestCase class PhpExtractorTest extends TestCase
{ {
public function testExtraction() /**
* @dataProvider resourcesProvider
*
* @param array|string $resource
*/
public function testExtraction($resource)
{ {
// Arrange // Arrange
$extractor = new PhpExtractor(); $extractor = new PhpExtractor();
@ -25,7 +30,7 @@ class PhpExtractorTest extends TestCase
$catalogue = new MessageCatalogue('en'); $catalogue = new MessageCatalogue('en');
// Act // Act
$extractor->extract(__DIR__.'/../Fixtures/Resources/views/', $catalogue); $extractor->extract($resource, $catalogue);
$expectedHeredoc = <<<EOF $expectedHeredoc = <<<EOF
heredoc key with whitespace and escaped \$\n sequences heredoc key with whitespace and escaped \$\n sequences
@ -50,4 +55,28 @@ EOF;
$this->assertEquals($expectedCatalogue, $actualCatalogue); $this->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)),
);
}
} }

View File

@ -12,6 +12,7 @@
namespace Symfony\Bundle\FrameworkBundle\Translation; namespace Symfony\Bundle\FrameworkBundle\Translation;
use Symfony\Component\Finder\Finder; use Symfony\Component\Finder\Finder;
use Symfony\Component\Translation\Extractor\AbstractFileExtractor;
use Symfony\Component\Translation\MessageCatalogue; use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\Extractor\ExtractorInterface; use Symfony\Component\Translation\Extractor\ExtractorInterface;
@ -20,7 +21,7 @@ use Symfony\Component\Translation\Extractor\ExtractorInterface;
* *
* @author Michel Salib <michelsalib@hotmail.com> * @author Michel Salib <michelsalib@hotmail.com>
*/ */
class PhpExtractor implements ExtractorInterface class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface
{ {
const MESSAGE_TOKEN = 300; const MESSAGE_TOKEN = 300;
@ -54,11 +55,9 @@ class PhpExtractor implements ExtractorInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function extract($directory, MessageCatalogue $catalog) public function extract($resource, MessageCatalogue $catalog)
{ {
// load any existing translation files $files = $this->extractFiles($resource);
$finder = new Finder();
$files = $finder->files()->name('*.php')->in($directory);
foreach ($files as $file) { foreach ($files as $file) {
$this->parseTokens(token_get_all(file_get_contents($file)), $catalog); $this->parseTokens(token_get_all(file_get_contents($file)), $catalog);
} }
@ -174,4 +173,28 @@ class PhpExtractor implements ExtractorInterface
} }
} }
} }
/**
* @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);
}
} }

View File

@ -29,7 +29,7 @@
"symfony/security-csrf": "~2.6|~3.0.0", "symfony/security-csrf": "~2.6|~3.0.0",
"symfony/stopwatch": "~2.3|~3.0.0", "symfony/stopwatch": "~2.3|~3.0.0",
"symfony/templating": "~2.1|~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" "doctrine/annotations": "~1.0"
}, },
"require-dev": { "require-dev": {

View File

@ -0,0 +1,83 @@
<?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\Component\Translation\Extractor;
/**
* Base class used by classes that extract translation messages from files.
*
* @author Marcos D. Sánchez <marcosdsanchez@gmail.com>
*/
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);
}

View File

@ -14,7 +14,7 @@ namespace Symfony\Component\Translation\Extractor;
use Symfony\Component\Translation\MessageCatalogue; 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. * New found messages are injected to the catalogue using the prefix.
* *
* @author Michel Salib <michelsalib@hotmail.com> * @author Michel Salib <michelsalib@hotmail.com>
@ -22,12 +22,12 @@ use Symfony\Component\Translation\MessageCatalogue;
interface ExtractorInterface 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 * @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. * Sets the prefix that should be used for new found messages.