[FrameworkBundle][Translation] moved cache to Translation component

[Translation][Cache] removed accessors for options.
This commit is contained in:
Abdellatif Ait boudad 2014-06-21 22:28:07 +01:00 committed by Tristan Maindron
parent eb1e3c344c
commit 8b2d9a8d4d
4 changed files with 279 additions and 79 deletions

View File

@ -14,7 +14,6 @@ namespace Symfony\Bundle\FrameworkBundle\Translation;
use Symfony\Component\Translation\Translator as BaseTranslator;
use Symfony\Component\Translation\MessageSelector;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Config\ConfigCache;
/**
* Translator.
@ -24,10 +23,6 @@ use Symfony\Component\Config\ConfigCache;
class Translator extends BaseTranslator
{
protected $container;
protected $options = array(
'cache_dir' => null,
'debug' => false,
);
protected $loaderIds;
/**
@ -50,14 +45,7 @@ class Translator extends BaseTranslator
$this->container = $container;
$this->loaderIds = $loaderIds;
// check option names
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(null, $selector);
parent::__construct(null, $selector, $options);
}
/**
@ -80,72 +68,10 @@ class Translator extends BaseTranslator
/**
* {@inheritdoc}
*/
protected function loadCatalogue($locale)
protected function initializeCatalogue($locale)
{
if (isset($this->catalogues[$locale])) {
return;
}
if (null === $this->options['cache_dir']) {
$this->initialize();
return parent::loadCatalogue($locale);
}
$this->assertValidLocale($locale);
$cache = new ConfigCache($this->options['cache_dir'].'/catalogue.'.$locale.'.php', $this->options['debug']);
if (!$cache->isFresh()) {
$this->initialize();
parent::loadCatalogue($locale);
$fallbackContent = '';
$current = '';
$replacementPattern = '/[^a-z0-9_]/i';
foreach ($this->computeFallbackLocales($locale) as $fallback) {
$fallbackSuffix = ucfirst(preg_replace($replacementPattern, '_', $fallback));
$currentSuffix = ucfirst(preg_replace($replacementPattern, '_', $current));
$fallbackContent .= sprintf(<<<EOF
\$catalogue%s = new MessageCatalogue('%s', %s);
\$catalogue%s->addFallbackCatalogue(\$catalogue%s);
EOF
,
$fallbackSuffix,
$fallback,
var_export($this->catalogues[$fallback]->all(), true),
$currentSuffix,
$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;
$this->initialize();
parent::initializeCatalogue($locale);
}
protected function initialize()

View File

@ -1,6 +1,10 @@
CHANGELOG
=========
2.6.0
-----
* added possibility to cache catalogues
2.5.0
-----

View File

@ -0,0 +1,169 @@
<?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\Tests;
use Symfony\Component\Translation\Translator;
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\MessageSelector;
class TranslatorCacheTest extends \PHPUnit_Framework_TestCase
{
protected $tmpDir;
protected function setUp()
{
$this->tmpDir = sys_get_temp_dir().'/sf2_translation';
$this->deleteTmpDir();
}
public function tearDown()
{
$this->deleteTmpDir();
}
protected function deleteTmpDir()
{
if (!file_exists($dir = $this->tmpDir)) {
return;
}
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->tmpDir), \RecursiveIteratorIterator::CHILD_FIRST);
foreach ($iterator as $path) {
if (preg_match('#[/\\\\]\.\.?$#', $path->__toString())) {
continue;
}
if ($path->isDir()) {
rmdir($path->__toString());
} else {
unlink($path->__toString());
}
}
rmdir($this->tmpDir);
}
public function testTransWithoutCaching()
{
$translator = $this->getTranslator($this->getLoader());
$translator->setLocale('fr');
$translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR'));
$this->assertEquals('foo (FR)', $translator->trans('foo'));
$this->assertEquals('bar (EN)', $translator->trans('bar'));
$this->assertEquals('foobar (ES)', $translator->trans('foobar'));
$this->assertEquals('choice 0 (EN)', $translator->transChoice('choice', 0));
$this->assertEquals('no translation', $translator->trans('no translation'));
$this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo'));
$this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1));
}
public function testTransWithCaching()
{
// prime the cache
$translator = $this->getTranslator($this->getLoader(), array('cache_dir' => $this->tmpDir));
$translator->setLocale('fr');
$translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR'));
$this->assertEquals('foo (FR)', $translator->trans('foo'));
$this->assertEquals('bar (EN)', $translator->trans('bar'));
$this->assertEquals('foobar (ES)', $translator->trans('foobar'));
$this->assertEquals('choice 0 (EN)', $translator->transChoice('choice', 0));
$this->assertEquals('no translation', $translator->trans('no translation'));
$this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo'));
$this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1));
// do it another time as the cache is primed now
$loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface');
$translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir));
$translator->setLocale('fr');
$translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR'));
$this->assertEquals('foo (FR)', $translator->trans('foo'));
$this->assertEquals('bar (EN)', $translator->trans('bar'));
$this->assertEquals('foobar (ES)', $translator->trans('foobar'));
$this->assertEquals('choice 0 (EN)', $translator->transChoice('choice', 0));
$this->assertEquals('no translation', $translator->trans('no translation'));
$this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo'));
$this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1));
}
protected function getCatalogue($locale, $messages)
{
$catalogue = new MessageCatalogue($locale);
foreach ($messages as $key => $translation) {
$catalogue->set($key, $translation);
}
return $catalogue;
}
protected function getLoader()
{
$loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface');
$loader
->expects($this->at(0))
->method('load')
->will($this->returnValue($this->getCatalogue('fr', array(
'foo' => 'foo (FR)',
))))
;
$loader
->expects($this->at(1))
->method('load')
->will($this->returnValue($this->getCatalogue('en', array(
'foo' => 'foo (EN)',
'bar' => 'bar (EN)',
'choice' => '{0} choice 0 (EN)|{1} choice 1 (EN)|]1,Inf] choice inf (EN)',
))))
;
$loader
->expects($this->at(2))
->method('load')
->will($this->returnValue($this->getCatalogue('es', array(
'foobar' => 'foobar (ES)',
))))
;
$loader
->expects($this->at(3))
->method('load')
->will($this->returnValue($this->getCatalogue('pt-PT', array(
'foobarfoo' => 'foobarfoo (PT-PT)',
))))
;
$loader
->expects($this->at(4))
->method('load')
->will($this->returnValue($this->getCatalogue('pt_BR', array(
'other choice' => '{0} other choice 0 (PT-BR)|{1} other choice 1 (PT-BR)|]1,Inf] other choice inf (PT-BR)',
))))
;
return $loader;
}
public function getTranslator($loader, $options = array())
{
$translator = new Translator(
$loader,
new MessageSelector(),
$options
);
$translator->addLoader('loader', $loader);
$translator->addResource('loader', 'foo', 'fr');
$translator->addResource('loader', 'foo', 'en');
$translator->addResource('loader', 'foo', 'es');
$translator->addResource('loader', 'foo', 'pt-PT'); // European Portuguese
$translator->addResource('loader', 'foo', 'pt_BR'); // Brazilian Portuguese
return $translator;
}
}

View File

@ -13,6 +13,7 @@ namespace Symfony\Component\Translation;
use Symfony\Component\Translation\Loader\LoaderInterface;
use Symfony\Component\Translation\Exception\NotFoundResourceException;
use Symfony\Component\Config\ConfigCache;
/**
* Translator.
@ -33,6 +34,14 @@ class Translator implements TranslatorInterface
*/
protected $locale;
/**
* @var array
*/
protected $options = array(
'cache_dir' => null,
'debug' => false,
);
/**
* @var array
*/
@ -58,15 +67,23 @@ class Translator implements TranslatorInterface
*
* @param string $locale The locale
* @param MessageSelector|null $selector The message selector for pluralization
* @param array $options An array of options
*
* @throws \InvalidArgumentException If a locale contains invalid characters
*
* @api
*/
public function __construct($locale, MessageSelector $selector = null)
public function __construct($locale, MessageSelector $selector = null, array $options = array())
{
$this->setLocale($locale);
$this->selector = $selector ?: new MessageSelector();
// check option names
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);
}
/**
@ -282,7 +299,22 @@ class Translator implements TranslatorInterface
return $messages;
}
/*
* @param string $locale
*/
protected function loadCatalogue($locale)
{
if (null === $this->options['cache_dir']) {
$this->initializeCatalogue($locale);
} else {
$this->initializeCacheCatalogue($locale);
}
}
/**
* @param string $locale
*/
protected function initializeCatalogue($locale)
{
try {
$this->doLoadCatalogue($locale);
@ -294,6 +326,75 @@ class Translator implements TranslatorInterface
$this->loadFallbackCatalogues($locale);
}
/**
* @param string $locale
*/
private function initializeCacheCatalogue($locale)
{
if (isset($this->catalogues[$locale])) {
return;
}
if (null === $this->options['cache_dir']) {
$this->initialize();
return parent::loadCatalogue($locale);
}
$cache = new ConfigCache($this->options['cache_dir'].'/catalogue.'.$locale.'.php', $this->options['debug']);
if (!$cache->isFresh()) {
$this->initialize();
parent::loadCatalogue($locale);
$fallbackContent = '';
$current = '';
$replacementPattern = '/[^a-z0-9_]/i';
foreach ($this->computeFallbackLocales($locale) as $fallback) {
$fallbackSuffix = ucfirst(preg_replace($replacementPattern, '_', $fallback));
$currentSuffix = ucfirst(preg_replace($replacementPattern, '_', $current));
$fallbackContent .= sprintf(<<<EOF
\$catalogue%s = new MessageCatalogue('%s', %s);
\$catalogue%s->addFallbackCatalogue(\$catalogue%s);
EOF
,
$fallbackSuffix,
$fallback,
var_export($this->catalogues[$fallback]->all(), true),
$currentSuffix,
$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;
}
private function doLoadCatalogue($locale)
{
$this->catalogues[$locale] = new MessageCatalogue($locale);