Add JMS translation tools

This commit is contained in:
Romain Neutron
2013-11-25 15:58:55 +01:00
parent 2ba164701d
commit 458aac8dd8
9 changed files with 351 additions and 3 deletions

View File

@@ -75,6 +75,8 @@ if ($cli['configuration.store']->isSetup()) {
} }
} }
$cli->command(new \Alchemy\Phrasea\Command\Developer\TranslationDumper());
$cli->command(new InstallAll()); $cli->command(new InstallAll());
$cli->command(new BowerInstall()); $cli->command(new BowerInstall());
$cli->command(new ComposerInstall()); $cli->command(new ComposerInstall());

View File

@@ -53,7 +53,8 @@
"behat/mink-extension" : "~1.0", "behat/mink-extension" : "~1.0",
"behat/mink-goutte-driver" : "~1.0", "behat/mink-goutte-driver" : "~1.0",
"behat/mink-selenium2-driver" : "~1.0", "behat/mink-selenium2-driver" : "~1.0",
"fabpot/goutte" : "~1.0" "fabpot/goutte" : "~1.0",
"jms/translation-bundle" : "~1.1"
}, },
"repositories": [ "repositories": [
{ {

115
composer.lock generated
View File

@@ -3,7 +3,7 @@
"This file locks the dependencies of your project to a known state", "This file locks the dependencies of your project to a known state",
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file"
], ],
"hash": "bdd3d36f118f024ac51b2466a8e11d09", "hash": "daac732f5ff830bcaf81da6ecf16f9e2",
"packages": [ "packages": [
{ {
"name": "alchemy-fr/tcpdf-clone", "name": "alchemy-fr/tcpdf-clone",
@@ -3745,6 +3745,119 @@
], ],
"time": "2013-10-04 15:03:51" "time": "2013-10-04 15:03:51"
}, },
{
"name": "jms/translation-bundle",
"version": "1.1.0",
"target-dir": "JMS/TranslationBundle",
"source": {
"type": "git",
"url": "https://github.com/schmittjoh/JMSTranslationBundle.git",
"reference": "6f03035a38badaf8c48767c7664c3196df1eebdf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/schmittjoh/JMSTranslationBundle/zipball/6f03035a38badaf8c48767c7664c3196df1eebdf",
"reference": "6f03035a38badaf8c48767c7664c3196df1eebdf",
"shasum": ""
},
"require": {
"nikic/php-parser": "0.9.1",
"symfony/console": "*",
"symfony/framework-bundle": "~2.1"
},
"conflict": {
"twig/twig": "1.10.2"
},
"require-dev": {
"jms/di-extra-bundle": ">=1.1",
"sensio/framework-extra-bundle": "*",
"symfony/browser-kit": "*",
"symfony/class-loader": "*",
"symfony/css-selector": "*",
"symfony/finder": "*",
"symfony/form": "*",
"symfony/process": "*",
"symfony/security": "*",
"symfony/twig-bundle": "*",
"symfony/validator": "*",
"symfony/yaml": "*"
},
"type": "symfony-bundle",
"extra": {
"branch-alias": {
"dev-master": "1.1-dev"
}
},
"autoload": {
"psr-0": {
"JMS\\TranslationBundle": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache2"
],
"authors": [
{
"name": "Johannes M. Schmitt",
"email": "schmittjoh@gmail.com",
"homepage": "http://jmsyst.com",
"role": "Developer of wrapped JMSSerializerBundle"
}
],
"description": "Puts the Symfony2 Translation Component on steroids",
"homepage": "http://jmsyst.com/bundles/JMSTranslationBundle",
"keywords": [
"extract",
"extraction",
"i18n",
"interface",
"multilanguage",
"translation",
"ui",
"webinterface"
],
"time": "2013-06-08 14:08:19"
},
{
"name": "nikic/php-parser",
"version": "v0.9.1",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "b1cc9ce676b4350b23d0fafc8244d08eee2fe287"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/b1cc9ce676b4350b23d0fafc8244d08eee2fe287",
"reference": "b1cc9ce676b4350b23d0fafc8244d08eee2fe287",
"shasum": ""
},
"require": {
"php": ">=5.2"
},
"type": "library",
"autoload": {
"psr-0": {
"PHPParser": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD"
],
"authors": [
{
"name": "Nikita Popov"
}
],
"description": "A PHP parser written in PHP",
"keywords": [
"parser",
"php"
],
"time": "2012-04-23 22:52:11"
},
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
"version": "1.2.13", "version": "1.2.13",

View File

@@ -12,6 +12,7 @@
namespace Alchemy\Phrasea; namespace Alchemy\Phrasea;
use Alchemy\Phrasea\Command\CommandInterface; use Alchemy\Phrasea\Command\CommandInterface;
use Alchemy\Phrasea\Core\CLIProvider\TranslationExtractorServiceProvider;
use Symfony\Component\Console; use Symfony\Component\Console;
use Alchemy\Phrasea\Core\CLIProvider\CLIDriversServiceProvider; use Alchemy\Phrasea\Core\CLIProvider\CLIDriversServiceProvider;
use Alchemy\Phrasea\Core\CLIProvider\ComposerSetupServiceProvider; use Alchemy\Phrasea\Core\CLIProvider\ComposerSetupServiceProvider;
@@ -58,6 +59,7 @@ class CLI extends Application
$this->register(new LessBuilderServiceProvider()); $this->register(new LessBuilderServiceProvider());
$this->register(new SignalHandlerServiceProvider()); $this->register(new SignalHandlerServiceProvider());
$this->register(new TaskManagerServiceProvider()); $this->register(new TaskManagerServiceProvider());
$this->register(new TranslationExtractorServiceProvider());
$this->bindRoutes(); $this->bindRoutes();
} }

View File

@@ -0,0 +1,51 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2013 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Command\Developer;
use Alchemy\Phrasea\Command\Command;
use Doctrine\Common\Annotations\DocParser;
use JMS\TranslationBundle\Translation\ConfigBuilder;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class TranslationDumper extends Command
{
public function __construct()
{
parent::__construct('translation:dump');
$this->setDescription('Dump translation files');
}
/**
* {@inheritdoc}
*/
protected function doExecute(InputInterface $input, OutputInterface $output)
{
$builder = new ConfigBuilder();
$config = $builder->setLocale('fr')
->setOutputFormat('po')
->setTranslationsDir(__DIR__ . '/../../../../../translations')
->setScanDirs(array(
$this->container['root.path'].'/lib',
$this->container['root.path'].'/templates',
$this->container['root.path'].'/bin',
$this->container['root.path'].'/www',
))
->getConfig();
$this->container['translation-extractor.updater']->process($config);
// var_dump($this->container['translation-extractor.updater']->getChangeSet($config));
return 0;
}
}

View File

@@ -0,0 +1,83 @@
<?php
namespace Alchemy\Phrasea\Command\Developer\Utils;
use Alchemy\Phrasea\Application;
use JMS\TranslationBundle\Model\Message;
use JMS\TranslationBundle\Model\MessageCatalogue;
use JMS\TranslationBundle\Translation\Extractor\FileVisitorInterface;
use Symfony\Component\Validator\Constraint;
/**
* Extracts translations validation constraints for Phraseanet definition.
*/
class ConstraintExtractor implements FileVisitorInterface, \PHPParser_NodeVisitor
{
private $messageProperties = array('message', 'minMessage', 'maxMessage', 'multipleMessage',
'extractFieldsMessage', 'missingFieldsMessage', 'notFoundMessage',
'notReadableMessage', 'maxSizeMessage', 'mimeTypesMessage',
'uplaodIniSizeErrorMessage', 'uploadFormSizeErrorMessage',
'uploadErrorMessage', 'mimeTypesMessage', 'sizeNotDetectedMessage',
'maxWidthMessage', 'maxWidthMessage', 'minWidthMessage', 'maxHeightMessage',
'minHeightMessage', 'invalidMessage',);
private $app;
private $traverser;
private $file;
private $catalogue;
private $namespace = '';
public function __construct(Application $app)
{
$this->app = $app;
$this->traverser = new \PHPParser_NodeTraverser();
$this->traverser->addVisitor($this);
}
public function enterNode(\PHPParser_Node $node)
{
if ($node instanceof \PHPParser_Node_Stmt_Namespace) {
$this->namespace = implode('\\', $node->name->parts);
return;
}
if (!$node instanceof \PHPParser_Node_Stmt_Class) {
return;
}
$name = '' === $this->namespace ? $node->name : $this->namespace.'\\'.$node->name;
if (!class_exists($name)) {
return;
}
if (!is_a($name, 'Symfony\Component\Validator\Constraint', true)) {
return;
}
$constraint = $name::create($this->app);
$ref = new \ReflectionClass($name);
foreach ($this->messageProperties as $prop) {
if ($ref->hasProperty($prop)) {
$message = new Message($constraint->$prop, 'validators');
$this->catalogue->add($message);
}
}
}
public function visitPhpFile(\SplFileInfo $file, MessageCatalogue $catalogue, array $ast)
{
$this->file = $file;
$this->namespace = '';
$this->catalogue = $catalogue;
$this->traverser->traverse($ast);
}
public function beforeTraverse(array $nodes) { }
public function leaveNode(\PHPParser_Node $node) { }
public function afterTraverse(array $nodes) { }
public function visitFile(\SplFileInfo $file, MessageCatalogue $catalogue) { }
public function visitTwigFile(\SplFileInfo $file, MessageCatalogue $catalogue, \Twig_Node $ast) { }
}

View File

@@ -0,0 +1,96 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2013 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Core\CLIProvider;
use Alchemy\Phrasea\Command\Developer\Utils\ConstraintExtractor;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\DocParser;
use Doctrine\Common\Annotations\AnnotationRegistry;
use Doctrine\Common\Persistence\Mapping\Driver\AnnotationDriver;
use Gedmo\SoftDeleteable\Mapping\Driver\Annotation;
use JMS\TranslationBundle\Translation\ConfigBuilder;
use JMS\TranslationBundle\Translation\Dumper\SymfonyDumperAdapter;
use JMS\TranslationBundle\Translation\Extractor\File\DefaultPhpFileExtractor;
use JMS\TranslationBundle\Translation\Extractor\File\FormExtractor;
use JMS\TranslationBundle\Translation\Extractor\File\TwigFileExtractor;
use JMS\TranslationBundle\Translation\Extractor\File\ValidationExtractor;
use JMS\TranslationBundle\Translation\Extractor\FileExtractor;
use JMS\TranslationBundle\Translation\ExtractorManager;
use JMS\TranslationBundle\Translation\FileWriter;
use JMS\TranslationBundle\Translation\Loader\SymfonyLoaderAdapter;
use JMS\TranslationBundle\Translation\LoaderManager;
use JMS\TranslationBundle\Translation\Updater;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Translation\Dumper\PoFileDumper;
use Symfony\Component\Translation\Loader\PoFileLoader;
use Silex\Application;
use Silex\ServiceProviderInterface;
use Symfony\Component\Process\PhpExecutableFinder;
class TranslationExtractorServiceProvider implements ServiceProviderInterface
{
public function register(Application $app)
{
$app['translation-extractor.logger'] = $app->share(function (Application $app) {
return $app['monolog'];
});
$app['translation-extractor.doc-parser'] = $app->share(function () {
$parser = new DocParser();
$parser->addNamespace("JMS\TranslationBundle\Annotation");
return $parser;
});
$app['translation-extractor.node-visitors'] = $app->share(function (Application $app) {
return array(
new ConstraintExtractor($app),
new ValidationExtractor($app['validator']->getMetadataFactory()),
new DefaultPhpFileExtractor($app['translation-extractor.doc-parser']),
new TwigFileExtractor($app['twig']),
new FormExtractor($app['translation-extractor.doc-parser']),
);
});
$app['translation-extractor.file-extractor'] = $app->share(function (Application $app) {
return new FileExtractor($app['twig'], $app['translation-extractor.logger'], $app['translation-extractor.node-visitors']);
});
$app['translation-extractor.extractor-manager'] = $app->share(function (Application $app) {
return new ExtractorManager($app['translation-extractor.file-extractor'], $app['translation-extractor.logger']);
});
$app['translation-extractor.writer'] = $app->share(function (Application $app) {
return new FileWriter($app['translation-extractor.writers']);
});
$app['translation-extractor.writers'] = $app->share(function () {
return array('po' => new SymfonyDumperAdapter(new PoFileDumper(), 'po'));
});
$app['translation-extractor.loader-manager'] = $app->share(function (Application $app) {
return new LoaderManager($app['translation-extractor.loaders']);
});
$app['translation-extractor.loaders'] = $app->share(function () {
return array(
'po' => new SymfonyLoaderAdapter(new PoFileLoader())
);
});
$app['translation-extractor.updater'] = $app->share(function (Application $app) {
AnnotationRegistry::registerAutoloadNamespace('JMS\TranslationBundle\Annotation', $app['root.path'].'/vendor/jms/translation-bundle');
return new Updater($app['translation-extractor.loader-manager'], $app['translation-extractor.extractor-manager'], $app['translation-extractor.logger'], $app['translation-extractor.writer']);
});
}
public function boot(Application $app)
{
}
}

View File

@@ -1008,7 +1008,7 @@
<div id="dialog_dwnl" title="{{ 'action : exporter' | trans }}" style="display:none;"></div> <div id="dialog_dwnl" title="{{ 'action : exporter' | trans }}" style="display:none;"></div>
<script type="text/javascript"> <script type="text/javascript">
{% include "prod/thesaurus.js" %} {% include "prod/thesaurus.js.twig" %}
</script> </script>
<script type="text/javascript" src="{{ path('minifier', { 'g' : 'prod' }) }}"></script> <script type="text/javascript" src="{{ path('minifier', { 'g' : 'prod' }) }}"></script>
<script type="text/javascript"> <script type="text/javascript">