Add cached translation loader

This commit is contained in:
Romain Neutron
2013-12-03 15:58:02 +01:00
parent a057f0cc06
commit 199134c25a
8 changed files with 267 additions and 14 deletions

View File

@@ -110,8 +110,8 @@ use Alchemy\Phrasea\Exception\InvalidArgumentException;
use Alchemy\Phrasea\Twig\JSUniqueID;
use Alchemy\Phrasea\Twig\Camelize;
use Alchemy\Phrasea\Twig\BytesConverter;
use Alchemy\Phrasea\Utilities\CachedTranslator;
use FFMpeg\FFMpegServiceProvider;
use JMS\TranslationBundle\Translation\Loader\Symfony\XliffLoader;
use Neutron\Silex\Provider\ImagineServiceProvider;
use MediaVorus\MediaVorusServiceProvider;
use MediaVorus\Utils\RawImageMimeTypeGuesser;
@@ -130,7 +130,7 @@ use Silex\Application\TranslationTrait;
use Silex\Provider\FormServiceProvider;
use Silex\Provider\MonologServiceProvider;
use Silex\Provider\SessionServiceProvider;
use Silex\Provider\TranslationServiceProvider;
use Alchemy\Phrasea\Core\Provider\TranslationServiceProvider;
use Silex\Provider\TwigServiceProvider;
use Silex\Provider\SwiftmailerServiceProvider;
use Silex\Provider\UrlGeneratorServiceProvider;
@@ -138,8 +138,6 @@ use Silex\Provider\ValidatorServiceProvider;
use Silex\Provider\ServiceControllerServiceProvider;
use Symfony\Bridge\Twig\Extension\TranslationExtension;
use Symfony\Component\Translation\Loader\MoFileLoader;
use Symfony\Component\Translation\Loader\PoFileLoader;
use Symfony\Component\Translation\Translator;
use Unoconv\UnoconvServiceProvider;
use XPDF\PdfToText;
use XPDF\XPDFServiceProvider;
@@ -311,10 +309,13 @@ class Application extends SilexApplication
$this->register(new TranslationServiceProvider(), [
'locale_fallbacks' => ['fr'],
'translator.cache-options' => [
'debug' => $this['debug'],
'cache_dir' => $this['root.path'].'/tmp/translations'
],
]);
$this['translator'] = $this->share($this->extend('translator', function(Translator $translator, $app) {
$translator->addLoader('xliff', new XliffLoader());
$this['translator'] = $this->share($this->extend('translator', function(CachedTranslator $translator, $app) {
$translator->addResource('xliff', __DIR__.'/../../../resources/locales/messages.fr.xliff', 'fr', 'messages');
$translator->addResource('xliff', __DIR__.'/../../../resources/locales/validators.fr.xliff', 'fr', 'validators');
$translator->addResource('xliff', __DIR__.'/../../../resources/locales/messages.en.xliff', 'en', 'messages');

View File

@@ -53,6 +53,7 @@ class Uninstaller extends Command
foreach ([
$root.'/tmp/serializer',
$root.'/tmp/cache_twig',
$root.'/tmp/translations',
$root.'/tmp/cache_minify',
$root.'/tmp/download',
$root.'/tmp/locks',

View File

@@ -0,0 +1,57 @@
<?php
namespace Alchemy\Phrasea\Core\Provider;
use Alchemy\Phrasea\Utilities\CachedTranslator;
use Silex\Application;
use Silex\ServiceProviderInterface;
use Symfony\Component\Translation\MessageSelector;
use Symfony\Component\Translation\Loader\ArrayLoader;
use JMS\TranslationBundle\Translation\Loader\Symfony\XliffLoader;
class TranslationServiceProvider implements ServiceProviderInterface
{
public function register(Application $app)
{
$app['translator.cache-options'] = array();
$app['translator'] = $app->share(function ($app) {
$app['translator.cache-options'] = array_replace(
array(
'debug' => $app['debug'],
), $app['translator.cache-options']
);
$translator = new CachedTranslator($app, $app['translator.message_selector'], $app['translator.cache-options']);
// Handle deprecated 'locale_fallback'
if (isset($app['locale_fallback'])) {
$app['locale_fallbacks'] = (array) $app['locale_fallback'];
}
$translator->setFallbackLocales($app['locale_fallbacks']);
$translator->addLoader('array', new ArrayLoader());
$translator->addLoader('xliff', new XliffLoader());
foreach ($app['translator.domains'] as $domain => $data) {
foreach ($data as $locale => $messages) {
$translator->addResource('array', $messages, $locale, $domain);
}
}
return $translator;
});
$app['translator.message_selector'] = $app->share(function () {
return new MessageSelector();
});
$app['translator.domains'] = array();
$app['locale_fallbacks'] = array('en');
}
public function boot(Application $app)
{
}
}

View File

@@ -0,0 +1,106 @@
<?php
/*
* This file is part of the Silex framework.
*
* (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 Alchemy\Phrasea\Utilities;
use Alchemy\Phrasea\Application;
use Silex\Translator;
use Symfony\Component\Config\ConfigCache;
use Symfony\Component\Translation\MessageSelector;
/**
* Translator that gets the current locale from the Silex application
* and cache the translations on filesystem.
*/
class CachedTranslator extends Translator
{
protected $app;
protected $options = array(
'cache_dir' => null,
'debug' => false,
);
public function __construct(Application $app, MessageSelector $selector, array $options = array())
{
$this->app = $app;
if ($diff = array_diff(array_keys($options), array_keys($this->options))) {
throw new \InvalidArgumentException(sprintf('The Translator does not support the following options: \'%s\'.', implode('\', \'', $diff)));
}
$this->options = array_merge($this->options, $options);
parent::__construct($app, $selector);
}
/**
* {@inheritdoc}
*/
protected function loadCatalogue($locale)
{
if (isset($this->catalogues[$locale])) {
return;
}
if (null === $this->options['cache_dir']) {
return parent::loadCatalogue($locale);
}
$cache = new ConfigCache($this->options['cache_dir'].'/catalogue.'.$locale.'.php', $this->options['debug']);
if (!$cache->isFresh()) {
parent::loadCatalogue($locale);
$fallbackContent = '';
$current = '';
foreach ($this->computeFallbackLocales($locale) as $fallback) {
$fallbackSuffix = ucfirst(str_replace('-', '_', $fallback));
$fallbackContent .= sprintf(<<<EOF
\$catalogue%s = new MessageCatalogue('%s', %s);
\$catalogue%s->addFallbackCatalogue(\$catalogue%s);
EOF
,
$fallbackSuffix,
$fallback,
var_export($this->catalogues[$fallback]->all(), true),
ucfirst(str_replace('-', '_', $current)),
$fallbackSuffix
);
$current = $fallback;
}
$content = sprintf(<<<EOF
<?php
use Symfony\Component\Translation\MessageCatalogue;
\$catalogue = new MessageCatalogue('%s', %s);
%s
return \$catalogue;
EOF
,
$locale,
var_export($this->catalogues[$locale]->all(), true),
$fallbackContent
);
$cache->write($content, $this->catalogues[$locale]->getResources());
return;
}
$this->catalogues[$locale] = include $cache;
}
}

View File

@@ -312,6 +312,7 @@ class appbox extends base
$finder->in([
$this->app['root.path'] . '/tmp/cache_minify/',
$this->app['root.path'] . '/tmp/cache_twig/',
$this->app['root.path'] . '/tmp/translations/',
$this->app['root.path'] . '/tmp/cache/profiler/',
$this->app['root.path'] . '/tmp/doctrine/',
$this->app['root.path'] . '/tmp/serializer/',

View File

@@ -37,6 +37,7 @@ class module_console_systemClearCache extends Command
->in([
$this->container['root.path'] . '/tmp/cache_minify/',
$this->container['root.path'] . '/tmp/cache_twig/',
$this->container['root.path'] . '/tmp/translations/',
$this->container['root.path'] . '/tmp/cache/profiler/',
$this->container['root.path'] . '/tmp/doctrine/',
$this->container['root.path'] . '/tmp/serializer/',

View File

@@ -337,6 +337,76 @@ class ApplicationTest extends \PhraseanetPHPUnitAbstract
$this->assertInstanceOf('MediaAlchemyst\Alchemyst', $app['media-alchemyst']);
}
/**
* @dataProvider transProvider
*/
public function testCachedTranslator($key, $locale, $expected)
{
$tempDir = __DIR__ . '/temp-trans';
$this->cleanupTempDir($tempDir);
$app = $this->getPreparedApp($tempDir);
$this->assertInstanceOf('Alchemy\Phrasea\Utilities\CachedTranslator', $app['translator']);
$result = $app['translator']->trans($key, array(), null, $locale);
$this->assertEquals($expected, $result);
$this->assertFileExists($tempDir.'/catalogue.'.($locale ?: 'en').'.php');
}
public function transProvider()
{
return array(
array('key1', 'de', 'The german translation'),
array('test.key', 'de', 'It works in german'),
);
}
protected function getPreparedApp($tempDir)
{
$app = new Application('test');
$app['translator.cache-options'] = [
'debug' => false,
'cache_dir' => $tempDir,
];
$app['translator.domains'] = array(
'messages' => array(
'en' => array (
'key1' => 'The translation',
'key_only_english' => 'Foo',
'key2' => 'One apple|%count% apples',
'test' => array(
'key' => 'It works'
)
),
'de' => array (
'key1' => 'The german translation',
'key2' => 'One german apple|%count% german apples',
'test' => array(
'key' => 'It works in german'
)
)
)
);
return $app;
}
private function cleanupTempDir($dir)
{
if (!is_dir($dir)) {
return;
}
foreach (new \DirectoryIterator($dir) as $fileinfo) {
if ($fileinfo->isFile()) {
unlink($fileinfo->getPathname());
}
}
}
private function getAppThatReturnLocale()
{
$app = new Application('test');

View File

@@ -0,0 +1,16 @@
<?php
namespace Alchemy\Tests\Phrasea\Core\Provider;
/**
* @covers Alchemy\Phrasea\Core\Provider\TranslatorServiceProvider
*/
class TranslatorServiceProvidertest extends ServiceProviderTestCase
{
public function provideServiceDescription()
{
return [
['Alchemy\Phrasea\Core\Provider\TranslationServiceProvider', 'translator', 'Alchemy\Phrasea\Utilities\CachedTranslator'],
];
}
}