diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php
index dc06c73dba03e..906182c3fc317 100644
--- a/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php
+++ b/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php
@@ -13,7 +13,7 @@
use Symfony\Bridge\Twig\Extension\TranslationExtension;
use Symfony\Component\Translation\Translator;
-use Symfony\Component\Translation\MessageSelector;
+use Symfony\Component\Translation\MessageCatalogueProvider\ResourceMessageCatalogueProvider;
use Symfony\Component\Translation\Loader\ArrayLoader;
class TranslationExtensionTest extends \PHPUnit_Framework_TestCase
@@ -34,7 +34,7 @@ public function testTrans($template, $expected, array $variables = array())
print $template."\n";
$loader = new \Twig_Loader_Array(array('index' => $template));
$twig = new \Twig_Environment($loader, array('debug' => true, 'cache' => false));
- $twig->addExtension(new TranslationExtension(new Translator('en', new MessageSelector())));
+ $twig->addExtension(new TranslationExtension($this->getTranslator('en')));
echo $twig->compile($twig->parse($twig->tokenize($twig->getLoader()->getSource('index'), 'index')))."\n\n";
$this->assertEquals($expected, $this->getTemplate($template)->render($variables));
@@ -136,12 +136,13 @@ public function testDefaultTranslationDomain()
',
);
- $translator = new Translator('en', new MessageSelector());
- $translator->addLoader('array', new ArrayLoader());
- $translator->addResource('array', array('foo' => 'foo (messages)'), 'en');
- $translator->addResource('array', array('foo' => 'foo (custom)'), 'en', 'custom');
- $translator->addResource('array', array('foo' => 'foo (foo)'), 'en', 'foo');
-
+ $loaders = array('array' => new ArrayLoader());
+ $resources = array(
+ array('array', array('foo' => 'foo (messages)'), 'en'),
+ array('array', array('foo' => 'foo (custom)'), 'en', 'custom'),
+ array('array', array('foo' => 'foo (foo)'), 'en', 'foo'),
+ );
+ $translator = $this->getTranslator('en', $loaders, $resources);
$template = $this->getTemplate($templates, $translator);
$this->assertEquals('foo (foo)foo (custom)foo (foo)foo (custom)foo (foo)foo (custom)', trim($template->render(array())));
@@ -169,13 +170,15 @@ public function testDefaultTranslationDomainWithNamedArguments()
',
);
- $translator = new Translator('en', new MessageSelector());
- $translator->addLoader('array', new ArrayLoader());
- $translator->addResource('array', array('foo' => 'foo (messages)'), 'en');
- $translator->addResource('array', array('foo' => 'foo (custom)'), 'en', 'custom');
- $translator->addResource('array', array('foo' => 'foo (foo)'), 'en', 'foo');
- $translator->addResource('array', array('foo' => 'foo (fr)'), 'fr', 'custom');
+ $loaders = array('array' => new ArrayLoader());
+ $resources = array(
+ array('array', array('foo' => 'foo (messages)'), 'en'),
+ array('array', array('foo' => 'foo (custom)'), 'en', 'custom'),
+ array('array', array('foo' => 'foo (foo)'), 'en', 'foo'),
+ array('array', array('foo' => 'foo (fr)'), 'fr', 'custom'),
+ );
+ $translator = $this->getTranslator('en', $loaders, $resources);
$template = $this->getTemplate($templates, $translator);
$this->assertEquals('foo (custom)foo (foo)foo (custom)foo (custom)foo (fr)foo (custom)foo (fr)', trim($template->render(array())));
@@ -184,7 +187,7 @@ public function testDefaultTranslationDomainWithNamedArguments()
protected function getTemplate($template, $translator = null)
{
if (null === $translator) {
- $translator = new Translator('en', new MessageSelector());
+ $translator = $this->getTranslator('en');
}
if (is_array($template)) {
@@ -197,4 +200,9 @@ protected function getTemplate($template, $translator = null)
return $twig->loadTemplate('index');
}
+
+ private function getTranslator($locale, $loaders = array(), $resources = array())
+ {
+ return new Translator($locale, new ResourceMessageCatalogueProvider($loaders, $resources));
+ }
}
diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json
index e33589122f77d..253610f42c887 100644
--- a/src/Symfony/Bridge/Twig/composer.json
+++ b/src/Symfony/Bridge/Twig/composer.json
@@ -27,7 +27,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.7|~3.0.0",
+ "symfony/translation": "~2.8|~3.0.0",
"symfony/yaml": "~2.0,>=2.0.5|~3.0.0",
"symfony/security": "~2.6|~3.0.0",
"symfony/security-acl": "~2.6|~3.0.0",
diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TranslationsCacheWarmer.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TranslationsCacheWarmer.php
index 223f0216ba9ad..4f9cdc15d0061 100644
--- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TranslationsCacheWarmer.php
+++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TranslationsCacheWarmer.php
@@ -14,6 +14,7 @@
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;
use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface;
use Symfony\Component\Translation\TranslatorInterface;
+use Symfony\Component\Translation\MessageCatalogueProvider\MessageCatalogueProviderInterface;
/**
* Generates the catalogues for translations.
@@ -23,10 +24,12 @@
class TranslationsCacheWarmer implements CacheWarmerInterface
{
private $translator;
+ private $messageCatalogueProvider;
- public function __construct(TranslatorInterface $translator)
+ public function __construct(TranslatorInterface $translator, MessageCatalogueProviderInterface $messageCatalogueProvider = null)
{
$this->translator = $translator;
+ $this->messageCatalogueProvider = $messageCatalogueProvider;
}
/**
@@ -36,6 +39,8 @@ public function warmUp($cacheDir)
{
if ($this->translator instanceof WarmableInterface) {
$this->translator->warmUp($cacheDir);
+ } elseif ($this->messageCatalogueProvider instanceof WarmableInterface) {
+ $this->messageCatalogueProvider->warmUp($cacheDir);
}
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslatorPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslatorPass.php
index 4e450166afa44..713a393461c35 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslatorPass.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslatorPass.php
@@ -41,5 +41,6 @@ public function process(ContainerBuilder $container)
}
$container->findDefinition('translator.default')->replaceArgument(2, $loaders);
+ $container->findDefinition('translation.message_catalogue_provider.resource')->replaceArgument(1, $loaders);
}
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
index ce16580f2a456..43b03088b635e 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
@@ -662,9 +662,13 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder
$this->translationConfigEnabled = true;
// Use the "real" translator instead of the identity default
- $container->setAlias('translator', 'translator.default');
+ $container->setAlias('translator', 'translation.translator');
$translator = $container->findDefinition('translator.default');
- $translator->addMethodCall('setFallbackLocales', array($config['fallbacks']));
+ $resourceMessageCatalogueProvider = $container->findDefinition('translation.message_catalogue_provider.resource');
+ if ($config['fallbacks']) {
+ $translator->addMethodCall('setFallbackLocales', array($config['fallbacks']));
+ $resourceMessageCatalogueProvider->replaceArgument(3, $config['fallbacks']);
+ }
$container->setParameter('translator.logging', $config['logging']);
@@ -713,6 +717,7 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder
}
$files = array();
+ $resources = array();
$finder = Finder::create()
->files()
->filter(function (\SplFileInfo $file) {
@@ -728,6 +733,7 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder
$files[$locale] = array();
}
+ $resources[] = array($format, (string) $file, $locale, $domain);
$files[$locale][] = (string) $file;
}
@@ -737,6 +743,7 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder
);
$translator->replaceArgument(3, $options);
+ $resourceMessageCatalogueProvider->replaceArgument(2, $resources);
}
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml
index c3e2558ae4237..20144695305e9 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml
@@ -15,7 +15,7 @@
%form.type_extension.csrf.enabled%
%form.type_extension.csrf.field_name%
-
+
%validator.translation_domain%
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml
index ff4c18f212f2d..3e476438576f6 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml
@@ -158,7 +158,33 @@
+
+
+
+ %kernel.debug%
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ %kernel.cache_dir%/translations
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/LoggingTranslatorPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/LoggingTranslatorPassTest.php
index ad0d65390d2f9..c682ee9ad0ac3 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/LoggingTranslatorPassTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/LoggingTranslatorPassTest.php
@@ -48,7 +48,7 @@ public function testProcess()
$parameterBag->expects($this->once())
->method('resolveValue')
- ->will($this->returnValue("Symfony\Bundle\FrameworkBundle\Translation\Translator"));
+ ->will($this->returnValue("Symfony\Component\Translation\Translator"));
$container->expects($this->once())
->method('getParameterBag')
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/TranslatorPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/TranslatorPassTest.php
index 83f70514d5456..349166be7fca3 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/TranslatorPassTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/TranslatorPassTest.php
@@ -39,7 +39,7 @@ public function testValidCollector()
$container->expects($this->once())
->method('findTaggedServiceIds')
->will($this->returnValue(array('xliff' => array(array('alias' => 'xliff', 'legacy-alias' => 'xlf')))));
- $container->expects($this->once())
+ $container->expects($this->any())
->method('findDefinition')
->will($this->returnValue($this->getMock('Symfony\Component\DependencyInjection\Definition')));
$pass = new TranslatorPass();
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
index c14acf4d57a4a..bb64f6e9954da 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
@@ -221,8 +221,53 @@ public function testAssets()
public function testTranslator()
{
$container = $this->createContainerFromFile('full');
- $this->assertTrue($container->hasDefinition('translator.default'), '->registerTranslatorConfiguration() loads translation.xml');
- $this->assertEquals('translator.default', (string) $container->getAlias('translator'), '->registerTranslatorConfiguration() redefines translator service from identity to real translator');
+ $this->assertTrue($container->hasDefinition('translation.translator'), '->registerTranslatorConfiguration() loads translation.xml');
+ $this->assertEquals('translation.translator', (string) $container->getAlias('translator'), '->registerTranslatorConfiguration() redefines translator service from identity to real translator');
+ $resources = $container->getDefinition('translation.message_catalogue_provider.resource')->getArgument(2);
+
+ $files = array_map(function ($resource) { return realpath($resource[1]); }, $resources);
+ $ref = new \ReflectionClass('Symfony\Component\Validator\Validation');
+ $this->assertContains(
+ strtr(dirname($ref->getFileName()).'/Resources/translations/validators.en.xlf', '/', DIRECTORY_SEPARATOR),
+ $files,
+ '->registerTranslatorConfiguration() finds Validator translation resources'
+ );
+ $ref = new \ReflectionClass('Symfony\Component\Form\Form');
+ $this->assertContains(
+ strtr(dirname($ref->getFileName()).'/Resources/translations/validators.en.xlf', '/', DIRECTORY_SEPARATOR),
+ $files,
+ '->registerTranslatorConfiguration() finds Form translation resources'
+ );
+ $ref = new \ReflectionClass('Symfony\Component\Security\Core\Security');
+ $this->assertContains(
+ strtr(dirname($ref->getFileName()).'/Resources/translations/security.en.xlf', '/', DIRECTORY_SEPARATOR),
+ $files,
+ '->registerTranslatorConfiguration() finds Security translation resources'
+ );
+ $this->assertContains(
+ strtr(__DIR__.'/Fixtures/translations/test_paths.en.yml', '/', DIRECTORY_SEPARATOR),
+ $files,
+ '->registerTranslatorConfiguration() finds translation resources in custom paths'
+ );
+
+ $this->assertEquals(array('fr'), $container->getDefinition('translation.message_catalogue_provider.resource')->getArgument(3));
+ }
+
+ /**
+ * @group legacy
+ */
+ public function testLegacyTranslator()
+ {
+ $container = $this->createContainerFromClosure(function ($container) {
+ $container->loadFromExtension('framework', array(
+ 'translator' => array(
+ 'fallback' => 'fr',
+ 'paths' => array('%kernel.root_dir%/Fixtures/translations'),
+ 'paths' => array('%kernel.root_dir%/Fixtures/translations'),
+ ),
+ ));
+ });
+
$options = $container->getDefinition('translator.default')->getArgument(3);
$files = array_map(function ($resource) { return realpath($resource); }, $options['resource_files']['en']);
@@ -254,14 +299,30 @@ public function testTranslator()
$this->assertEquals(array('fr'), $calls[1][1][0]);
}
- public function testTranslatorMultipleFallbacks()
+ /**
+ * @group legacy
+ */
+ public function testLegacyTranslatorMultipleFallbacks()
{
- $container = $this->createContainerFromFile('translator_fallbacks');
+ $container = $this->createContainerFromClosure(function ($container) {
+ $container->loadFromExtension('framework', array(
+ 'translator' => array(
+ 'fallbacks' => array('en', 'fr'),
+ ),
+ ));
+ });
$calls = $container->getDefinition('translator.default')->getMethodCalls();
$this->assertEquals(array('en', 'fr'), $calls[1][1][0]);
}
+ public function testTranslatorMultipleFallbacks()
+ {
+ $container = $this->createContainerFromFile('translator_fallbacks');
+
+ $this->assertEquals(array('en', 'fr'), $container->getDefinition('translation.message_catalogue_provider.resource')->getArgument(3));
+ }
+
/**
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
*/
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php
index e09b0d3f33a85..fce54e4f13304 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php
@@ -16,6 +16,9 @@
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Translation\MessageSelector;
+/**
+ * @group legacy
+ */
class TranslatorTest extends \PHPUnit_Framework_TestCase
{
protected $tmpDir;
@@ -94,16 +97,6 @@ public function testTransWithCaching()
$this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax'));
}
- public function testTransWithCachingWithInvalidLocale()
- {
- $loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface');
- $translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir), 'loader', '\Symfony\Bundle\FrameworkBundle\Tests\Translation\TranslatorWithInvalidLocale');
- $translator->setLocale('invalid locale');
-
- $this->setExpectedException('\InvalidArgumentException');
- $translator->trans('foo');
- }
-
public function testLoadResourcesWithoutCaching()
{
$loader = new \Symfony\Component\Translation\Loader\YamlFileLoader();
@@ -269,14 +262,3 @@ private function createTranslator($loader, $options, $translatorClass = '\Symfon
);
}
}
-
-class TranslatorWithInvalidLocale extends Translator
-{
- /**
- * {@inheritdoc}
- */
- public function setLocale($locale)
- {
- $this->locale = $locale;
- }
-}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php b/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php
index fba6d70d6978b..b9a8c94cde38b 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php
@@ -11,6 +11,8 @@
namespace Symfony\Bundle\FrameworkBundle\Translation;
+@trigger_error('The '.__NAMESPACE__.'\Translator class is deprecated since version 2.8 and will be removed in 3.0. Use directly the Symfony\Component\Translation\Translator class instead.', E_USER_DEPRECATED);
+
use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface;
use Symfony\Component\Translation\Translator as BaseTranslator;
use Symfony\Component\Translation\MessageSelector;
@@ -20,6 +22,8 @@
* Translator.
*
* @author Fabien Potencier
+ *
+ * @deprecated since 2.8, to be removed in 3.0. Use the Symfony\Component\Translation\Translator instead.
*/
class Translator extends BaseTranslator implements WarmableInterface
{
@@ -65,11 +69,11 @@ public function __construct(ContainerInterface $container, MessageSelector $sele
$this->options = array_merge($this->options, $options);
$this->resourceLocales = array_keys($this->options['resource_files']);
+
+ parent::__construct($container->getParameter('kernel.default_locale'), $selector, $this->options['cache_dir'], $this->options['debug']);
if (null !== $this->options['cache_dir'] && $this->options['debug']) {
$this->loadResources();
}
-
- parent::__construct($container->getParameter('kernel.default_locale'), $selector, $this->options['cache_dir'], $this->options['debug']);
}
/**
diff --git a/src/Symfony/Bundle/FrameworkBundle/Translation/WarmableMessageCatalogueProvider.php b/src/Symfony/Bundle/FrameworkBundle/Translation/WarmableMessageCatalogueProvider.php
new file mode 100644
index 0000000000000..c822ea54c6b9c
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Translation/WarmableMessageCatalogueProvider.php
@@ -0,0 +1,51 @@
+messageCatalogueProvider = $messageCatalogueProvider;
+ $this->resourceMessageCatalogueProvider = $resourceMessageCatalogueProvider;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function warmUp($cacheDir)
+ {
+ // skip warmUp when translator doesn't use cache
+ if (!$this->messageCatalogueProvider instanceof CachedMessageCatalogueProvider) {
+ return;
+ }
+
+ $locales = array_merge($this->resourceMessageCatalogueProvider->getFallbackLocales(), array_keys($this->resourceMessageCatalogueProvider->getResources()));
+ foreach (array_unique($locales) as $locale) {
+ $this->getCatalogue($locale);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCatalogue($locale)
+ {
+ return $this->messageCatalogueProvider->getCatalogue($locale);
+ }
+}
diff --git a/src/Symfony/Component/Translation/Catalogue/TargetOperation.php b/src/Symfony/Component/Translation/Catalogue/TargetOperation.php
index eea1fefe2fb22..e081e139a33fa 100644
--- a/src/Symfony/Component/Translation/Catalogue/TargetOperation.php
+++ b/src/Symfony/Component/Translation/Catalogue/TargetOperation.php
@@ -42,7 +42,7 @@ protected function processDomain($domain)
//
// For 'obsolete' messages, the code can't be simplifed as ``array_diff_assoc($this->source->all($domain), $this->target->all($domain))``
// because doing so will not exclude messages like {x: x ∈ source ∧ x ∉ target.all ∧ x ∈ target.fallback}
-
+
foreach ($this->source->all($domain) as $id => $message) {
if ($this->target->has($id, $domain)) {
$this->messages[$domain]['all'][$id] = $message;
diff --git a/src/Symfony/Component/Translation/MessageCatalogueProvider/CachedMessageCatalogueProvider.php b/src/Symfony/Component/Translation/MessageCatalogueProvider/CachedMessageCatalogueProvider.php
new file mode 100644
index 0000000000000..6a13f4f6d859c
--- /dev/null
+++ b/src/Symfony/Component/Translation/MessageCatalogueProvider/CachedMessageCatalogueProvider.php
@@ -0,0 +1,196 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\MessageCatalogueProvider;
+
+use Symfony\Component\Config\ConfigCacheInterface;
+use Symfony\Component\Config\ConfigCacheFactoryInterface;
+use Symfony\Component\Config\ConfigCacheFactory;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\MessageCatalogueInterface;
+
+/**
+ * Manages cache catalogues.
+ *
+ * @author Abdellatif Ait boudad
+ */
+class CachedMessageCatalogueProvider implements MessageCatalogueProviderInterface
+{
+ /**
+ * @var MessageCatalogueProviderInterface
+ */
+ private $messageCatalogueProvider;
+
+ /**
+ * @var ConfigCacheFactoryInterface
+ */
+ private $configCacheFactory;
+
+ /**
+ * @var string
+ */
+ private $cacheDir;
+
+ /**
+ * @var MessageCatalogueInterface[]
+ */
+ private $catalogues;
+
+ /**
+ * @param MessageCatalogueProviderInterface $messageCatalogueProvider The message catalogue provider to use for loading the catalogue.
+ * @param ConfigCacheFactoryInterface $configCacheFactory The ConfigCache factory to use.
+ * @param string $cacheDir The directory to use for the cache.
+ */
+ public function __construct(MessageCatalogueProviderInterface $messageCatalogueProvider, ConfigCacheFactoryInterface $configCacheFactory, $cacheDir)
+ {
+ $this->messageCatalogueProvider = $messageCatalogueProvider;
+ $this->configCacheFactory = $configCacheFactory;
+ $this->cacheDir = $cacheDir;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCatalogue($locale)
+ {
+ if (isset($this->catalogues[$locale]) && file_exists($this->getCatalogueCachePath($locale))) {
+ return $this->catalogues[$locale];
+ }
+
+ $messageCatalogueProvider = $this->messageCatalogueProvider;
+
+ return $this->catalogues[$locale] = $this->cache($locale, function () use ($messageCatalogueProvider, $locale) {
+ return $messageCatalogueProvider->getCatalogue($locale);
+ });
+ }
+
+ /**
+ * This method is added because it is needed in the Translator for BC. It should be removed in 3.0.
+ *
+ * @internal
+ */
+ public function cache($locale, $callback)
+ {
+ if (!is_callable($callback)) {
+ throw new \InvalidArgumentException(sprintf('Invalid type for callback argument. Expected callable, but got "%s".', gettype($callback)));
+ }
+
+ $self = $this; // required for PHP 5.3 where "$this" cannot be used in anonymous functions. Change in Symfony 3.0.
+ $cache = $this->configCacheFactory->cache($this->getCatalogueCachePath($locale),
+ function (ConfigCacheInterface $cache) use ($self, $callback) {
+ $self->dumpCatalogue($callback(), $cache);
+ }
+ );
+
+ return include $cache->getPath();
+ }
+
+ /**
+ * Sets the ConfigCache factory to use.
+ *
+ * This method is added because it is needed in the Translator for BC. It should be removed in 3.0.
+ *
+ * @param ConfigCacheFactoryInterface $configCacheFactory
+ *
+ * @internal
+ */
+ public function setConfigCacheFactory(ConfigCacheFactoryInterface $configCacheFactory)
+ {
+ $this->configCacheFactory = $configCacheFactory;
+ }
+
+ /**
+ * Provides the ConfigCache factory implementation.
+ *
+ * This method is added because it is needed in the Translator for BC. It should be removed in 3.0.
+ *
+ * @return ConfigCacheFactoryInterface $configCacheFactory
+ *
+ * @internal
+ */
+ public function getConfigCacheFactory()
+ {
+ return $this->configCacheFactory;
+ }
+
+ /**
+ * This method is public because it needs to be callable from a closure in PHP 5.3. It should be made protected (or even private, if possible) in 3.0.
+ *
+ * @internal
+ */
+ public function dumpCatalogue($catalogue, ConfigCacheInterface $cache)
+ {
+ $fallbackContent = $this->getFallbackContent($catalogue);
+
+ $content = sprintf(<<getLocale(),
+ var_export($catalogue->all(), true),
+ $fallbackContent
+ );
+
+ $cache->write($content, $catalogue->getResources());
+ }
+
+ private function getFallbackContent(MessageCatalogue $catalogue)
+ {
+ $fallbackContent = '';
+ $current = '';
+ $replacementPattern = '/[^a-z0-9_]/i';
+ $fallbackCatalogue = $catalogue->getFallbackCatalogue();
+ while ($fallbackCatalogue) {
+ $fallback = $fallbackCatalogue->getLocale();
+ $fallbackSuffix = ucfirst(preg_replace($replacementPattern, '_', $fallback));
+ $currentSuffix = ucfirst(preg_replace($replacementPattern, '_', $current));
+
+ $fallbackContent .= sprintf(<<addFallbackCatalogue(\$catalogue%s);
+
+EOF
+ ,
+ $fallbackSuffix,
+ $fallback,
+ var_export($fallbackCatalogue->all(), true),
+ $currentSuffix,
+ $fallbackSuffix
+ );
+ $current = $fallbackCatalogue->getLocale();
+ $fallbackCatalogue = $fallbackCatalogue->getFallbackCatalogue();
+ }
+
+ return $fallbackContent;
+ }
+
+ /**
+ * This method is public because it is needed in the Translator for BC. It should be made private in 3.0.
+ *
+ * @internal
+ */
+ public function getCatalogueCachePath($locale)
+ {
+ if ($this->messageCatalogueProvider instanceof ResourceMessageCatalogueProvider) {
+ return $this->cacheDir.'/catalogue.'.$locale.'.'.sha1(serialize($this->messageCatalogueProvider->getFallbackLocales())).'.php';
+ }
+
+ return $this->cacheDir.'/'.'catalogue.'.$locale.'.php';
+ }
+}
diff --git a/src/Symfony/Component/Translation/MessageCatalogueProvider/ContainerAwareResourceMessageCatalogueProvider.php b/src/Symfony/Component/Translation/MessageCatalogueProvider/ContainerAwareResourceMessageCatalogueProvider.php
new file mode 100644
index 0000000000000..0c85493b17cba
--- /dev/null
+++ b/src/Symfony/Component/Translation/MessageCatalogueProvider/ContainerAwareResourceMessageCatalogueProvider.php
@@ -0,0 +1,79 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\MessageCatalogueProvider;
+
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * MessageCatalogueProvider loads catalogue from resources with
+ * lazily loads loaders from the dependency injection container.
+ *
+ * @author Abdellatif Ait boudad
+ */
+class ContainerAwareResourceMessageCatalogueProvider extends ResourceMessageCatalogueProvider
+{
+ /**
+ * @var ContainerInterface
+ */
+ private $container;
+
+ /**
+ * @var array
+ */
+ private $loaderIds;
+
+ /**
+ * @var array
+ */
+ private $fileResources;
+
+ /**
+ * @param ContainerInterface $container A ContainerInterface instance
+ * @param array $loaderIds
+ * @param array $fileResources
+ * @param array $fallbackLocales The fallback locales.
+ */
+ public function __construct(ContainerInterface $container, $loaderIds, $fileResources, $fallbackLocales = array())
+ {
+ $this->container = $container;
+ $this->loaderIds = $loaderIds;
+ $this->fileResources = $fileResources;
+ $this->setFallbackLocales($fallbackLocales);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getLoaders()
+ {
+ foreach ($this->loaderIds as $id => $aliases) {
+ foreach ($aliases as $alias) {
+ $this->addLoader($alias, $this->container->get($id));
+ }
+ }
+
+ return parent::getLoaders();
+ }
+
+ /**
+ * @return array
+ */
+ public function getResources()
+ {
+ foreach ($this->fileResources as $key => $resource) {
+ $this->addResource($resource[0], $resource[1], $resource[2], isset($resource[3]) ? $resource[3] : null);
+ unset($this->fileResources[$key]);
+ }
+
+ return parent::getResources();
+ }
+}
diff --git a/src/Symfony/Component/Translation/MessageCatalogueProvider/MessageCatalogueProviderInterface.php b/src/Symfony/Component/Translation/MessageCatalogueProvider/MessageCatalogueProviderInterface.php
new file mode 100644
index 0000000000000..0d2098d4a29b8
--- /dev/null
+++ b/src/Symfony/Component/Translation/MessageCatalogueProvider/MessageCatalogueProviderInterface.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\MessageCatalogueProvider;
+
+use Symfony\Component\Translation\MessageCatalogueInterface;
+
+/**
+ * The MessageCatalogueProviderInterface provide a MessageCatalogue chain loaded.
+ *
+ * @author Abdellatif Ait boudad
+ */
+interface MessageCatalogueProviderInterface
+{
+ /**
+ * Gets the message catalogue by locale.
+ *
+ * @param string $locale The locale
+ *
+ * @return MessageCatalogueInterface
+ */
+ public function getCatalogue($locale);
+}
diff --git a/src/Symfony/Component/Translation/MessageCatalogueProvider/ResourceMessageCatalogueProvider.php b/src/Symfony/Component/Translation/MessageCatalogueProvider/ResourceMessageCatalogueProvider.php
new file mode 100644
index 0000000000000..9156f689a02d0
--- /dev/null
+++ b/src/Symfony/Component/Translation/MessageCatalogueProvider/ResourceMessageCatalogueProvider.php
@@ -0,0 +1,215 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\MessageCatalogueProvider;
+
+use Symfony\Component\Translation\Loader\LoaderInterface;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Translator;
+
+/**
+ * MessageCatalogueProvider loads catalogue from resources.
+ *
+ * @author Abdellatif Ait boudad
+ */
+class ResourceMessageCatalogueProvider implements MessageCatalogueProviderInterface
+{
+ /**
+ * @var array
+ */
+ private $resources = array();
+
+ /**
+ * @var LoaderInterface[] An array of LoaderInterface objects
+ */
+ private $loaders = array();
+
+ /**
+ * @var array
+ */
+ private $fallbackLocales;
+
+ /**
+ * @var MessageCatalogueInterface[]
+ */
+ private $catalogues;
+
+ /**
+ * @param LoaderInterface[] $loaders An array of loaders
+ * @param array $resources An array of resources
+ * @param array $fallbackLocales The fallback locales.
+ */
+ public function __construct(array $loaders = array(), $resources = array(), $fallbackLocales = array())
+ {
+ $this->setFallbackLocales($fallbackLocales);
+ foreach ($loaders as $format => $loader) {
+ $this->addLoader($format, $loader);
+ }
+
+ foreach ($resources as $resource) {
+ $this->addResource($resource[0], $resource[1], $resource[2], isset($resource[3]) ? $resource[3] : null);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCatalogue($locale)
+ {
+ if (isset($this->catalogues[$locale])) {
+ return $this->catalogues[$locale];
+ }
+
+ $catalogue = $this->loadCatalogue($locale);
+ $this->loadFallbackCatalogues($catalogue);
+
+ return $this->catalogues[$locale] = $catalogue;
+ }
+
+ /**
+ * Adds a Resource.
+ *
+ * @param string $format The name of the loader (@see addLoader())
+ * @param mixed $resource The resource name
+ * @param string $locale The locale
+ * @param string $domain The domain
+ */
+ public function addResource($format, $resource, $locale, $domain = null)
+ {
+ Translator::assertLocale($locale);
+
+ if (null === $domain) {
+ $domain = 'messages';
+ }
+
+ $this->resources[$locale][] = array($format, $resource, $domain);
+ if (in_array($locale, $this->fallbackLocales)) {
+ $this->catalogues = array();
+ } else {
+ unset($this->catalogues[$locale]);
+ }
+ }
+
+ /**
+ * Adds a Loader.
+ *
+ * @param string $format The name of the loader (@see addResource())
+ * @param LoaderInterface $loader A LoaderInterface instance
+ */
+ public function addLoader($format, LoaderInterface $loader)
+ {
+ $this->loaders[$format] = $loader;
+ }
+
+ /**
+ * Returns the registered loaders.
+ *
+ * @return LoaderInterface[] An array of LoaderInterface instances
+ */
+ public function getLoaders()
+ {
+ return $this->loaders;
+ }
+
+ /**
+ * Gets the registered resources.
+ *
+ * @return array
+ */
+ public function getResources()
+ {
+ return $this->resources;
+ }
+
+ /**
+ * Sets the fallback locales.
+ *
+ * @param array $locales The fallback locales
+ */
+ public function setFallbackLocales(array $locales)
+ {
+ // needed as the fallback locales are linked to the already loaded catalogues
+ $this->catalogues = array();
+
+ foreach ($locales as $locale) {
+ Translator::assertLocale($locale);
+ }
+
+ $this->fallbackLocales = $locales;
+ }
+
+ /**
+ * Gets the fallback locales.
+ *
+ * @return array $locales The fallback locales
+ */
+ public function getFallbackLocales()
+ {
+ return $this->fallbackLocales;
+ }
+
+ /**
+ * This method is public because it is needed in the Translator for BC. It should be made private in 3.0.
+ *
+ * @internal
+ */
+ public function loadCatalogue($locale)
+ {
+ $catalogue = new MessageCatalogue($locale);
+
+ $loaders = $this->getLoaders();
+ $resources = $this->getResources();
+ foreach ((isset($resources[$locale]) ? $resources[$locale] : array()) as $resource) {
+ if (!isset($loaders[$resource[0]])) {
+ throw new \RuntimeException(sprintf('The "%s" translation loader is not registered.', $resource[0]));
+ }
+
+ $catalogue->addCatalogue($this->loaders[$resource[0]]->load($resource[1], $locale, $resource[2]));
+ }
+
+ return $catalogue;
+ }
+
+ /**
+ * This method is public because it is needed in the Translator for BC. It should be made private in 3.0.
+ *
+ * @internal
+ */
+ public function computeFallbackLocales($locale)
+ {
+ $locales = array();
+ foreach ($this->fallbackLocales as $fallback) {
+ if ($fallback === $locale) {
+ continue;
+ }
+
+ $locales[] = $fallback;
+ }
+
+ if (strrchr($locale, '_') !== false) {
+ array_unshift($locales, substr($locale, 0, -strlen(strrchr($locale, '_'))));
+ }
+
+ return array_unique($locales);
+ }
+
+ private function loadFallbackCatalogues($catalogue)
+ {
+ $current = $catalogue;
+ foreach ($this->computeFallbackLocales($catalogue->getLocale()) as $fallback) {
+ $catalogue = isset($this->catalogues[$fallback]) ? $this->catalogues[$fallback] : $this->loadCatalogue($fallback);
+
+ $fallbackCatalogue = new MessageCatalogue($fallback, $catalogue->all());
+ $current->addFallbackCatalogue($fallbackCatalogue);
+ $current = $fallbackCatalogue;
+ }
+ }
+}
diff --git a/src/Symfony/Component/Translation/README.md b/src/Symfony/Component/Translation/README.md
index 984f40d7f2243..00422f70c7fa0 100644
--- a/src/Symfony/Component/Translation/README.md
+++ b/src/Symfony/Component/Translation/README.md
@@ -9,13 +9,13 @@ use Symfony\Component\Translation\Translator;
use Symfony\Component\Translation\MessageSelector;
use Symfony\Component\Translation\Loader\ArrayLoader;
-$translator = new Translator('fr_FR', new MessageSelector());
-$translator->setFallbackLocales(array('fr'));
-$translator->addLoader('array', new ArrayLoader());
-$translator->addResource('array', array(
+$resourceCatalogue = new ResourceMessageCatalogueProvider();
+$resourceCatalogue->addLoader('array', new ArrayLoader());
+$resourceCatalogue->addResource('array', array(
'Hello World!' => 'Bonjour',
), 'fr');
+$translator = new Translator('fr', $resourceCatalogue);
echo $translator->trans('Hello World!')."\n";
```
diff --git a/src/Symfony/Component/Translation/Tests/Catalogue/TargetOperationTest.php b/src/Symfony/Component/Translation/Tests/Catalogue/TargetOperationTest.php
index 0217162aea6fe..271d17fb8f311 100644
--- a/src/Symfony/Component/Translation/Tests/Catalogue/TargetOperationTest.php
+++ b/src/Symfony/Component/Translation/Tests/Catalogue/TargetOperationTest.php
@@ -79,5 +79,4 @@ protected function createOperation(MessageCatalogueInterface $source, MessageCat
{
return new TargetOperation($source, $target);
}
-
}
diff --git a/src/Symfony/Component/Translation/Tests/DataCollectorTranslatorTest.php b/src/Symfony/Component/Translation/Tests/DataCollectorTranslatorTest.php
index 5ef81712f413c..b34e696a555d8 100644
--- a/src/Symfony/Component/Translation/Tests/DataCollectorTranslatorTest.php
+++ b/src/Symfony/Component/Translation/Tests/DataCollectorTranslatorTest.php
@@ -14,14 +14,13 @@
use Symfony\Component\Translation\Translator;
use Symfony\Component\Translation\DataCollectorTranslator;
use Symfony\Component\Translation\Loader\ArrayLoader;
+use Symfony\Component\Translation\MessageCatalogueProvider\ResourceMessageCatalogueProvider;
class DataCollectorTranslatorTest extends \PHPUnit_Framework_TestCase
{
public function testCollectMessages()
{
$collector = $this->createCollector();
- $collector->setFallbackLocales(array('fr', 'ru'));
-
$collector->trans('foo');
$collector->trans('bar');
$collector->transChoice('choice', 0);
@@ -80,14 +79,23 @@ public function testCollectMessages()
private function createCollector()
{
- $translator = new Translator('en');
- $translator->addLoader('array', new ArrayLoader());
- $translator->addResource('array', array('foo' => 'foo (en)'), 'en');
- $translator->addResource('array', array('bar' => 'bar (fr)'), 'fr');
- $translator->addResource('array', array('bar_ru' => 'bar (ru)'), 'ru');
+ $loaders = array('array' => new ArrayLoader());
+ $resources = array(
+ array('array', array('foo' => 'foo (en)'), 'en'),
+ array('array', array('bar' => 'bar (fr)'), 'fr'),
+ array('array', array('bar_ru' => 'bar (ru)'), 'ru'),
+ );
+ $translator = $this->getTranslator('en', $loaders, $resources, array('fr', 'ru'));
$collector = new DataCollectorTranslator($translator);
return $collector;
}
+
+ private function getTranslator($locale, $loaders = array(), $resources = array(), $fallbackLocales = array())
+ {
+ $resourceCatalogue = new ResourceMessageCatalogueProvider($loaders, $resources, $fallbackLocales);
+
+ return new Translator($locale, $resourceCatalogue);
+ }
}
diff --git a/src/Symfony/Component/Translation/Tests/LegacyTranslatorTest.php b/src/Symfony/Component/Translation/Tests/LegacyTranslatorTest.php
new file mode 100644
index 0000000000000..7d9029f71ea9f
--- /dev/null
+++ b/src/Symfony/Component/Translation/Tests/LegacyTranslatorTest.php
@@ -0,0 +1,333 @@
+
+ *
+ * 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\MessageSelector;
+use Symfony\Component\Translation\Loader\ArrayLoader;
+
+/**
+ * @group legacy
+ */
+class LegacyTranslatorTest extends TranslatorTest
+{
+ public function testSetFallbackLocales()
+ {
+ $resources = array(
+ array('array', array('foo' => 'foofoo'), 'en'),
+ array('array', array('bar' => 'foobar'), 'fr'),
+ );
+ $translator = $this->getTranslator('en', array('array' => new ArrayLoader()), $resources);
+
+ // force catalogue loading
+ $translator->trans('bar');
+
+ $translator->setFallbackLocales(array('fr'));
+ $this->assertEquals('foobar', $translator->trans('bar'));
+ }
+
+ public function testSetFallbackLocalesMultiple()
+ {
+ $resources = array(
+ array('array', array('foo' => 'foo (en)'), 'en'),
+ array('array', array('bar' => 'bar (fr)'), 'fr'),
+ );
+ $translator = $this->getTranslator('en', array('array' => new ArrayLoader()), $resources);
+
+ // force catalogue loading
+ $translator->trans('bar');
+
+ $translator->setFallbackLocales(array('fr_FR', 'fr'));
+ $this->assertEquals('bar (fr)', $translator->trans('bar'));
+ }
+
+ /**
+ * @dataProvider getInvalidLocalesTests
+ * @expectedException \InvalidArgumentException
+ */
+ public function testSetFallbackInvalidLocales($locale)
+ {
+ $this->getTranslator('fr', array(), array(), array('fr', $locale));
+ }
+
+ /**
+ * @dataProvider getValidLocalesTests
+ */
+ public function testSetFallbackValidLocales($locale)
+ {
+ $this->getTranslator('fr_FR', array(), array(), array('fr', $locale));
+ // no assertion. this method just asserts that no exception is thrown
+ }
+
+ public function testTransWithFallbackLocale()
+ {
+ $loaders = array('array' => new ArrayLoader());
+ $resources = array(
+ array('array', array('bar' => 'foobar'), 'en'),
+ );
+ $translator = $this->getTranslator('fr_FR', $loaders, $resources, array('en'));
+
+ $this->assertEquals('foobar', $translator->trans('bar'));
+ }
+
+ /**
+ * @dataProvider getInvalidLocalesTests
+ * @expectedException \InvalidArgumentException
+ */
+ public function testAddResourceInvalidLocales($locale)
+ {
+ $this->getTranslator('fr', array(), array(array('array', array('foo' => 'foofoo'), $locale)));
+ }
+
+ /**
+ * @dataProvider getValidLocalesTests
+ */
+ public function testAddResourceValidLocales($locale)
+ {
+ $this->getTranslator('fr', array(), array(array('array', array('foo' => 'foofoo'), $locale)));
+ // no assertion. this method just asserts that no exception is thrown
+ }
+
+ public function testAddResourceAfterTrans()
+ {
+ $translator = $this->getTranslator('en', array('array' => new ArrayLoader()));
+
+ $translator->addResource('array', array('foo' => 'foofoo'), 'en');
+ $this->assertEquals('foofoo', $translator->trans('foo'));
+
+ $translator->addResource('array', array('bar' => 'foobar'), 'en');
+ $this->assertEquals('foobar', $translator->trans('bar'));
+ }
+
+ /**
+ * @dataProvider getTransFileTests
+ * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+ */
+ public function testTransWithoutFallbackLocaleFile($format, $loader)
+ {
+ $loaderClass = 'Symfony\\Component\\Translation\\Loader\\'.$loader;
+
+ $loaders = array($format => new $loaderClass());
+ $resources = array(
+ array($format, __DIR__.'/fixtures/non-existing', 'en'),
+ array($format, __DIR__.'/fixtures/resources.'.$format, 'en'),
+ );
+ $translator = $this->getTranslator('en', $loaders, $resources);
+
+ // force catalogue loading
+ $translator->trans('foo');
+ }
+
+ /**
+ * @dataProvider getTransFileTests
+ */
+ public function testTransWithFallbackLocaleFile($format, $loader)
+ {
+ $loaderClass = 'Symfony\\Component\\Translation\\Loader\\'.$loader;
+ $loaders = array($format => new $loaderClass());
+ $resources = array(
+ array($format, __DIR__.'/fixtures/resources.'.$format, 'en', 'resources'),
+ );
+ $translator = $this->getTranslator('en_GB', $loaders, $resources);
+
+ $this->assertEquals('bar', $translator->trans('foo', array(), 'resources'));
+ }
+
+ public function testTransWithFallbackLocaleBis()
+ {
+ $loaders = array('array' => new ArrayLoader());
+ $resources = array(
+ array('array', array('foo' => 'foofoo'), 'en_US'),
+ array('array', array('bar' => 'foobar'), 'en'),
+ );
+ $translator = $this->getTranslator('en_US', $loaders, $resources);
+
+ $this->assertEquals('foobar', $translator->trans('bar'));
+ }
+
+ public function testTransWithFallbackLocaleTer()
+ {
+ $loaders = array('array' => new ArrayLoader());
+ $resources = array(
+ array('array', array('foo' => 'foo (en_US)'), 'en_US'),
+ array('array', array('bar' => 'bar (en)'), 'en'),
+ );
+ $translator = $this->getTranslator('fr_FR', $loaders, $resources, array('en_US', 'en'));
+
+ $this->assertEquals('foo (en_US)', $translator->trans('foo'));
+ $this->assertEquals('bar (en)', $translator->trans('bar'));
+ }
+
+ public function testTransNonExistentWithFallback()
+ {
+ $loaders = array('array' => new ArrayLoader());
+ $translator = $this->getTranslator('fr', $loaders, array(), array('en'));
+
+ $this->assertEquals('non-existent', $translator->trans('non-existent'));
+ }
+
+ /**
+ * @expectedException \RuntimeException
+ */
+ public function testWhenAResourceHasNoRegisteredLoader()
+ {
+ $resources = array(array('array', array('foo' => 'foofoo'), 'en'));
+ $translator = $this->getTranslator('en', array(), $resources);
+
+ $translator->trans('foo');
+ }
+
+ protected function getTranslator($locale, $loaders = array(), $resources = array(), $fallbackLocales = array())
+ {
+ $translator = new Translator($locale);
+ $translator->setFallbackLocales($fallbackLocales);
+ foreach ($loaders as $format => $loader) {
+ $translator->addLoader($format, $loader);
+ }
+
+ foreach ($resources as $resource) {
+ $translator->addResource($resource[0], $resource[1], $resource[2], isset($resource[3]) ? $resource[3] : null);
+ }
+
+ return $translator;
+ }
+
+ /**
+ * @group legacy
+ * @dataProvider dataProviderGetMessages
+ */
+ public function testLegacyGetMessages($resources, $locale, $expected)
+ {
+ $locales = array_keys($resources);
+ $_locale = !is_null($locale) ? $locale : reset($locales);
+ $locales = array_slice($locales, 0, array_search($_locale, $locales));
+
+ $translator = new Translator($_locale, new MessageSelector());
+ $translator->setFallbackLocales(array_reverse($locales));
+ $translator->addLoader('array', new ArrayLoader());
+ foreach ($resources as $_locale => $domainMessages) {
+ foreach ($domainMessages as $domain => $messages) {
+ $translator->addResource('array', $messages, $_locale, $domain);
+ }
+ }
+ $result = $translator->getMessages($locale);
+
+ $this->assertEquals($expected, $result);
+ }
+
+ public function getTransFileTests()
+ {
+ return array(
+ array('csv', 'CsvFileLoader'),
+ array('ini', 'IniFileLoader'),
+ array('mo', 'MoFileLoader'),
+ array('po', 'PoFileLoader'),
+ array('php', 'PhpFileLoader'),
+ array('ts', 'QtFileLoader'),
+ array('xlf', 'XliffFileLoader'),
+ array('yml', 'YamlFileLoader'),
+ array('json', 'JsonFileLoader'),
+ );
+ }
+
+ public function dataProviderGetMessages()
+ {
+ $resources = array(
+ 'en' => array(
+ 'jsmessages' => array(
+ 'foo' => 'foo (EN)',
+ 'bar' => 'bar (EN)',
+ ),
+ 'messages' => array(
+ 'foo' => 'foo messages (EN)',
+ ),
+ 'validators' => array(
+ 'int' => 'integer (EN)',
+ ),
+ ),
+ 'pt-PT' => array(
+ 'messages' => array(
+ 'foo' => 'foo messages (PT)',
+ ),
+ 'validators' => array(
+ 'str' => 'integer (PT)',
+ ),
+ ),
+ 'pt_BR' => array(
+ 'validators' => array(
+ 'int' => 'integer (BR)',
+ ),
+ ),
+ );
+
+ return array(
+ array($resources, null,
+ array(
+ 'jsmessages' => array(
+ 'foo' => 'foo (EN)',
+ 'bar' => 'bar (EN)',
+ ),
+ 'messages' => array(
+ 'foo' => 'foo messages (EN)',
+ ),
+ 'validators' => array(
+ 'int' => 'integer (EN)',
+ ),
+ ),
+ ),
+ array($resources, 'en',
+ array(
+ 'jsmessages' => array(
+ 'foo' => 'foo (EN)',
+ 'bar' => 'bar (EN)',
+ ),
+ 'messages' => array(
+ 'foo' => 'foo messages (EN)',
+ ),
+ 'validators' => array(
+ 'int' => 'integer (EN)',
+ ),
+ ),
+ ),
+ array($resources, 'pt-PT',
+ array(
+ 'jsmessages' => array(
+ 'foo' => 'foo (EN)',
+ 'bar' => 'bar (EN)',
+ ),
+ 'messages' => array(
+ 'foo' => 'foo messages (PT)',
+ ),
+ 'validators' => array(
+ 'int' => 'integer (EN)',
+ 'str' => 'integer (PT)',
+ ),
+ ),
+ ),
+ array($resources, 'pt_BR',
+ array(
+ 'jsmessages' => array(
+ 'foo' => 'foo (EN)',
+ 'bar' => 'bar (EN)',
+ ),
+ 'messages' => array(
+ 'foo' => 'foo messages (PT)',
+ ),
+ 'validators' => array(
+ 'int' => 'integer (BR)',
+ 'str' => 'integer (PT)',
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/src/Symfony/Component/Translation/Tests/LoggingTranslatorTest.php b/src/Symfony/Component/Translation/Tests/LoggingTranslatorTest.php
index 9f3e849bf4fda..cfc5e0c8224c0 100644
--- a/src/Symfony/Component/Translation/Tests/LoggingTranslatorTest.php
+++ b/src/Symfony/Component/Translation/Tests/LoggingTranslatorTest.php
@@ -14,6 +14,7 @@
use Symfony\Component\Translation\Translator;
use Symfony\Component\Translation\LoggingTranslator;
use Symfony\Component\Translation\Loader\ArrayLoader;
+use Symfony\Component\Translation\MessageCatalogueProvider\ResourceMessageCatalogueProvider;
class LoggingTranslatorTest extends \PHPUnit_Framework_TestCase
{
@@ -25,7 +26,7 @@ public function testTransWithNoTranslationIsLogged()
->with('Translation not found.')
;
- $translator = new Translator('ar');
+ $translator = $this->getTranslator('ar');
$loggableTranslator = new LoggingTranslator($translator, $logger);
$loggableTranslator->transChoice('some_message2', 10, array('%count%' => 10));
$loggableTranslator->trans('bar');
@@ -39,11 +40,19 @@ public function testTransChoiceFallbackIsLogged()
->with('Translation use fallback catalogue.')
;
- $translator = new Translator('ar');
- $translator->setFallbackLocales(array('en'));
- $translator->addLoader('array', new ArrayLoader());
- $translator->addResource('array', array('some_message2' => 'one thing|%count% things'), 'en');
+ $loaders = array('array' => new ArrayLoader());
+ $resources = array(
+ array('array', array('some_message2' => 'one thing|%count% things'), 'en'),
+ );
+ $translator = $this->getTranslator('ar', $loaders, $resources, array('en'));
$loggableTranslator = new LoggingTranslator($translator, $logger);
$loggableTranslator->transChoice('some_message2', 10, array('%count%' => 10));
}
+
+ private function getTranslator($locale, $loaders = array(), $resources = array(), $fallbackLocales = array())
+ {
+ $resourceCatalogue = new ResourceMessageCatalogueProvider($loaders, $resources, $fallbackLocales);
+
+ return new Translator($locale, $resourceCatalogue);
+ }
}
diff --git a/src/Symfony/Component/Translation/Tests/MessageCatalogueProvider/CachedMessageCatalogueProviderTest.php b/src/Symfony/Component/Translation/Tests/MessageCatalogueProvider/CachedMessageCatalogueProviderTest.php
new file mode 100644
index 0000000000000..a0df77884cb83
--- /dev/null
+++ b/src/Symfony/Component/Translation/Tests/MessageCatalogueProvider/CachedMessageCatalogueProviderTest.php
@@ -0,0 +1,223 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\MessageCatalogueProvider\Tests;
+
+use Symfony\Component\Translation\Loader\ArrayLoader;
+use Symfony\Component\Config\Resource\SelfCheckingResourceInterface;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\MessageCatalogueProvider\CachedMessageCatalogueProvider;
+use Symfony\Component\Translation\MessageCatalogueProvider\ResourceMessageCatalogueProvider;
+use Symfony\Component\Config\ConfigCacheFactory;
+
+class CachedMessageCatalogueProviderTest extends \PHPUnit_Framework_TestCase
+{
+ private $tmpDir;
+
+ protected function setUp()
+ {
+ $this->tmpDir = sys_get_temp_dir().'/sf2_translation';
+ $this->deleteTmpDir();
+ }
+
+ protected function tearDown()
+ {
+ $this->deleteTmpDir();
+ }
+
+ /**
+ * @dataProvider runForDebugAndProduction
+ */
+ public function testDifferentTranslatorsForSameLocaleDoNotOverwriteEachOthersCache($debug)
+ {
+ /*
+ * Similar to the previous test. After we used the second translator, make
+ * sure there's still a useable cache for the first one.
+ */
+
+ $locale = 'any_locale';
+ $format = 'some_format';
+ $msgid = 'test';
+
+ // Create a Translator and prime its cache
+ $messageCatalogueProvider = $this->getMessageCatalogueProvider($debug, array($format => new ArrayLoader()), array(array($format, array($msgid => 'OK'), $locale)));
+ $messageCatalogueProvider->getCatalogue($locale);
+
+ // Create another Translator with a different catalogue for the same locale
+ $messageCatalogueProvider = $this->getMessageCatalogueProvider($debug, array($format => new ArrayLoader()), array(array($format, array($msgid => 'FAIL'), $locale)));
+ $messageCatalogueProvider->getCatalogue($locale);
+
+ // Now the first translator must still have a useable cache.
+ $messageCatalogueProvider = $this->getMessageCatalogueProvider($debug, array($format => $this->createFailingLoader()), array(array($format, array($msgid => 'OK'), $locale)));
+ $catalogue = $messageCatalogueProvider->getCatalogue($locale);
+ $this->assertEquals('OK', $catalogue->get($msgid), '-> the cache was overwritten by another translator instance in '.($debug ? 'debug' : 'production'));
+ }
+
+ public function testPrimaryAndFallbackCataloguesContainTheSameMessagesRegardlessOfCaching()
+ {
+ $loaders = array('array' => new ArrayLoader());
+ $resources = array(
+ array('array', array('foo' => 'foo (a)'), 'a'),
+ array('array', array('foo' => 'foo (b)'), 'b'),
+ array('array', array('bar' => 'bar (b)'), 'b'),
+ );
+
+ /*
+ * As a safeguard against potential BC breaks, make sure that primary and fallback
+ * catalogues (reachable via getFallbackCatalogue()) always contain the full set of
+ * messages provided by the loader. This must also be the case when these catalogues
+ * are (internally) read from a cache.
+ *
+ * Optimizations inside the translator must not change this behaviour.
+ */
+
+ /*
+ * Create a translator that loads two catalogues for two different locales.
+ * The catalogues contain distinct sets of messages.
+ */
+ $messageCatalogueProvider = $this->getMessageCatalogueProvider(false, $loaders, $resources, array('b'));
+
+ $catalogue = $messageCatalogueProvider->getCatalogue('a');
+ $this->assertFalse($catalogue->defines('bar')); // Sure, the "a" catalogue does not contain that message.
+
+ $fallback = $catalogue->getFallbackCatalogue();
+ $this->assertTrue($fallback->defines('foo')); // "foo" is present in "a" and "b"
+
+ /*
+ * Now, repeat the same test.
+ * Behind the scenes, the cache is used. But that should not matter, right?
+ */
+ $messageCatalogueProvider = $this->getMessageCatalogueProvider(false, $loaders, $resources, array('b'));
+
+ $catalogue = $messageCatalogueProvider->getCatalogue('a');
+ $this->assertFalse($catalogue->defines('bar'));
+
+ $fallback = $catalogue->getFallbackCatalogue();
+ $this->assertTrue($fallback->defines('foo'));
+ }
+
+ public function testDifferentCacheFilesAreUsedForDifferentSetsOfFallbackLocales()
+ {
+ $loaders = array('array' => new ArrayLoader());
+ $resources = array(
+ array('array', array('foo' => 'foo (a)'), 'a'),
+ array('array', array('bar' => 'bar (b)'), 'b'),
+ );
+
+ /*
+ * Because the cache file contains a catalogue including all of its fallback
+ * catalogues, we must take the set of fallback locales into consideration when
+ * loading a catalogue from the cache.
+ */
+ $messageCatalogueProvider = $this->getMessageCatalogueProvider(false, $loaders, $resources, array('b'));
+ $catalogue = $messageCatalogueProvider->getCatalogue('a');
+ $this->assertEquals('bar (b)', $catalogue->get('bar'));
+
+ // Use a fresh translator with no fallback locales, result should be the same
+ $messageCatalogueProvider = $this->getMessageCatalogueProvider(false, $loaders, $resources);
+ $catalogue = $messageCatalogueProvider->getCatalogue('a');
+ $this->assertEquals('bar', $catalogue->get('bar'));
+ }
+
+ public function testRefreshCacheWhenResourcesAreNoLongerFresh()
+ {
+ $resource = $this->getMock('Symfony\Component\Config\Resource\SelfCheckingResourceInterface');
+ $loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface');
+ $resource->method('isFresh')->will($this->returnValue(false));
+ $loader
+ ->expects($this->exactly(2))
+ ->method('load')
+ ->will($this->returnValue($this->getCatalogue('fr', array(), array($resource))));
+
+ // prime the cache
+ $messageCatalogueProvider = $this->getMessageCatalogueProvider(true, array('loader' => $loader), array(array('loader', 'foo', 'fr')));
+ $messageCatalogueProvider->getCatalogue('fr');
+
+ // prime the cache second time
+ $messageCatalogueProvider = $this->getMessageCatalogueProvider(true, array('loader' => $loader), array(array('loader', 'foo', 'fr')));
+ $messageCatalogueProvider->getCatalogue('fr');
+ }
+
+ public function runForDebugAndProduction()
+ {
+ return array(array(true), array(false));
+ }
+
+ /**
+ * @return LoaderInterface
+ */
+ private function createFailingLoader()
+ {
+ $loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface');
+ $loader
+ ->expects($this->never())
+ ->method('load');
+
+ return $loader;
+ }
+
+ protected function getMessageCatalogueProvider($debug, $loaders = array(), $resources = array(), $fallbackLocales = array())
+ {
+ $resourceCatalogue = new ResourceMessageCatalogueProvider($loaders, $resources, $fallbackLocales);
+
+ return new CachedMessageCatalogueProvider($resourceCatalogue, new ConfigCacheFactory($debug), $this->tmpDir);
+ }
+
+ private function getCatalogue($locale, $messages, $resources = array())
+ {
+ $catalogue = new MessageCatalogue($locale);
+ foreach ($messages as $key => $translation) {
+ $catalogue->set($key, $translation);
+ }
+ foreach ($resources as $resource) {
+ $catalogue->addResource($resource);
+ }
+
+ return $catalogue;
+ }
+
+ private 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);
+ }
+}
+
+class StaleResource implements SelfCheckingResourceInterface
+{
+ public function isFresh($timestamp)
+ {
+ return false;
+ }
+
+ public function getResource()
+ {
+ }
+
+ public function __toString()
+ {
+ return '';
+ }
+}
diff --git a/src/Symfony/Component/Translation/Tests/MessageCatalogueProvider/ResourceMessageCatalogueProviderTest.php b/src/Symfony/Component/Translation/Tests/MessageCatalogueProvider/ResourceMessageCatalogueProviderTest.php
new file mode 100644
index 0000000000000..19b5725023d7a
--- /dev/null
+++ b/src/Symfony/Component/Translation/Tests/MessageCatalogueProvider/ResourceMessageCatalogueProviderTest.php
@@ -0,0 +1,177 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\MessageCatalogueProvider\Tests;
+
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\MessageCatalogueProvider\ResourceMessageCatalogueProvider;
+use Symfony\Component\Translation\Loader\ArrayLoader;
+
+class ResourceMessageCatalogueProviderTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @dataProvider getInvalidLocalesTests
+ * @expectedException \InvalidArgumentException
+ */
+ public function testAddResourceInvalidLocales($locale)
+ {
+ $translatorBag = $this->getMessageCatalogueProvider();
+ $translatorBag->addResource('array', array('foo' => 'foofoo'), $locale);
+ }
+
+ /**
+ * @dataProvider getValidLocalesTests
+ */
+ public function testAddResourceValidLocales($locale)
+ {
+ $translatorBag = $this->getMessageCatalogueProvider();
+ $translatorBag->addResource('array', array('foo' => 'foofoo'), $locale);
+ // no assertion. this method just asserts that no exception is thrown
+ }
+
+ public function testGetCatalogue()
+ {
+ $translatorBag = $this->getMessageCatalogueProvider();
+ $this->assertEquals(new MessageCatalogue('en'), $translatorBag->getCatalogue('en'));
+ }
+
+ /**
+ * @expectedException \RuntimeException
+ */
+ public function testWhenAResourceHasNoRegisteredLoader()
+ {
+ $translatorBag = $this->getMessageCatalogueProvider();
+ $translatorBag->addResource('array', array('foo' => 'foofoo'), 'en');
+
+ $translatorBag->getCatalogue('en');
+ }
+
+ /**
+ * @dataProvider getTransFileTests
+ * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+ */
+ public function testLoadLocaleFile($format, $loader)
+ {
+ $loaderClass = 'Symfony\\Component\\Translation\\Loader\\'.$loader;
+ $loaders = array($format => new $loaderClass());
+ $resources = array(
+ array($format, __DIR__.'/fixtures/non-existing', 'en'),
+ array($format, __DIR__.'/fixtures/resources.'.$format, 'en'),
+ );
+
+ $translatorBag = $this->getMessageCatalogueProvider($loaders, $resources);
+
+ // force catalogue loading
+ $translatorBag->getCatalogue('en');
+ }
+
+ public function testSetFallbackLocales()
+ {
+ $loaders = array('array' => new ArrayLoader());
+ $resources = array(
+ array('array', array('foo' => 'foofoo'), 'en'),
+ array('array', array('bar' => 'foobar'), 'fr'),
+ );
+
+ // load catalogue
+ $translatorBag = $this->getMessageCatalogueProvider($loaders, $resources, array());
+ $translatorBag->setFallbackLocales(array('fr'));
+
+ $catalogue = $translatorBag->getCatalogue('en');
+ $this->assertEquals('foobar', $catalogue->get('bar'));
+ }
+
+ /**
+ * @dataProvider getInvalidLocalesTests
+ * @expectedException \InvalidArgumentException
+ */
+ public function testSetFallbackInvalidLocales($locale)
+ {
+ $this->getMessageCatalogueProvider(array(), array(), array($locale));
+ }
+
+ /**
+ * @dataProvider getValidLocalesTests
+ */
+ public function testSetFallbackValidLocales($locale)
+ {
+ $this->getMessageCatalogueProvider(array(), array(), array($locale));
+ // no assertion. this method just asserts that no exception is thrown
+ }
+
+ public function testLoadCatalogueWithFallbackLocale()
+ {
+ $loaders = array('array' => new ArrayLoader());
+ $resources = array(
+ array('array', array('bar' => 'foobar'), 'en'),
+ );
+ $translatorBag = $this->getMessageCatalogueProvider($loaders, $resources, array('en'));
+
+ // load catalogue
+ $catalogue = $translatorBag->getCatalogue('fr_FR');
+
+ $this->assertEquals('foobar', $catalogue->get('bar'));
+ }
+
+ private function getMessageCatalogueProvider($loaders = array(), $resources = array(), $fallbacklocales = array())
+ {
+ return new ResourceMessageCatalogueProvider($loaders, $resources, $fallbacklocales);
+ }
+
+ public function getInvalidLocalesTests()
+ {
+ return array(
+ array('fr FR'),
+ array('français'),
+ array('fr+en'),
+ array('utf#8'),
+ array('fr&en'),
+ array('fr~FR'),
+ array(' fr'),
+ array('fr '),
+ array('fr*'),
+ array('fr/FR'),
+ array('fr\\FR'),
+ );
+ }
+
+ public function getValidLocalesTests()
+ {
+ return array(
+ array(''),
+ array(null),
+ array('fr'),
+ array('francais'),
+ array('FR'),
+ array('frFR'),
+ array('fr-FR'),
+ array('fr_FR'),
+ array('fr.FR'),
+ array('fr-FR.UTF8'),
+ array('sr@latin'),
+ );
+ }
+
+ public function getTransFileTests()
+ {
+ return array(
+ array('csv', 'CsvFileLoader'),
+ array('ini', 'IniFileLoader'),
+ array('mo', 'MoFileLoader'),
+ array('po', 'PoFileLoader'),
+ array('php', 'PhpFileLoader'),
+ array('ts', 'QtFileLoader'),
+ array('xlf', 'XliffFileLoader'),
+ array('yml', 'YamlFileLoader'),
+ array('json', 'JsonFileLoader'),
+ );
+ }
+}
diff --git a/src/Symfony/Component/Translation/Tests/TranslatorCacheTest.php b/src/Symfony/Component/Translation/Tests/TranslatorCacheTest.php
index 75093580b472b..5acd2546c0ac7 100644
--- a/src/Symfony/Component/Translation/Tests/TranslatorCacheTest.php
+++ b/src/Symfony/Component/Translation/Tests/TranslatorCacheTest.php
@@ -17,6 +17,9 @@
use Symfony\Component\Translation\Translator;
use Symfony\Component\Translation\MessageCatalogue;
+/**
+ * @group legacy
+ */
class TranslatorCacheTest extends \PHPUnit_Framework_TestCase
{
protected $tmpDir;
@@ -148,36 +151,6 @@ public function testDifferentTranslatorsForSameLocaleDoNotOverwriteEachOthersCac
$this->assertEquals('OK', $translator->trans($msgid), '-> the cache was overwritten by another translator instance in '.($debug ? 'debug' : 'production'));
}
- public function testDifferentCacheFilesAreUsedForDifferentSetsOfFallbackLocales()
- {
- /*
- * Because the cache file contains a catalogue including all of its fallback
- * catalogues, we must take the set of fallback locales into consideration when
- * loading a catalogue from the cache.
- */
- $translator = new Translator('a', null, $this->tmpDir);
- $translator->setFallbackLocales(array('b'));
-
- $translator->addLoader('array', new ArrayLoader());
- $translator->addResource('array', array('foo' => 'foo (a)'), 'a');
- $translator->addResource('array', array('bar' => 'bar (b)'), 'b');
-
- $this->assertEquals('bar (b)', $translator->trans('bar'));
-
- // Remove fallback locale
- $translator->setFallbackLocales(array());
- $this->assertEquals('bar', $translator->trans('bar'));
-
- // Use a fresh translator with no fallback locales, result should be the same
- $translator = new Translator('a', null, $this->tmpDir);
-
- $translator->addLoader('array', new ArrayLoader());
- $translator->addResource('array', array('foo' => 'foo (a)'), 'a');
- $translator->addResource('array', array('bar' => 'bar (b)'), 'b');
-
- $this->assertEquals('bar', $translator->trans('bar'));
- }
-
public function testPrimaryAndFallbackCataloguesContainTheSameMessagesRegardlessOfCaching()
{
/*
@@ -226,6 +199,36 @@ public function testPrimaryAndFallbackCataloguesContainTheSameMessagesRegardless
$this->assertTrue($fallback->defines('foo'));
}
+ public function testDifferentCacheFilesAreUsedForDifferentSetsOfFallbackLocales()
+ {
+ /*
+ * Because the cache file contains a catalogue including all of its fallback
+ * catalogues, we must take the set of fallback locales into consideration when
+ * loading a catalogue from the cache.
+ */
+ $translator = new Translator('a', null, $this->tmpDir);
+ $translator->setFallbackLocales(array('b'));
+
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('foo' => 'foo (a)'), 'a');
+ $translator->addResource('array', array('bar' => 'bar (b)'), 'b');
+
+ $this->assertEquals('bar (b)', $translator->trans('bar'));
+
+ // Remove fallback locale
+ $translator->setFallbackLocales(array());
+ $this->assertEquals('bar', $translator->trans('bar'));
+
+ // Use a fresh translator with no fallback locales, result should be the same
+ $translator = new Translator('a', null, $this->tmpDir);
+
+ $translator->addLoader('array', new ArrayLoader());
+ $translator->addResource('array', array('foo' => 'foo (a)'), 'a');
+ $translator->addResource('array', array('bar' => 'bar (b)'), 'b');
+
+ $this->assertEquals('bar', $translator->trans('bar'));
+ }
+
public function testRefreshCacheWhenResourcesAreNoLongerFresh()
{
$resource = $this->getMock('Symfony\Component\Config\Resource\SelfCheckingResourceInterface');
diff --git a/src/Symfony/Component/Translation/Tests/TranslatorTest.php b/src/Symfony/Component/Translation/Tests/TranslatorTest.php
index e8e967139dac2..f44dada758b45 100644
--- a/src/Symfony/Component/Translation/Tests/TranslatorTest.php
+++ b/src/Symfony/Component/Translation/Tests/TranslatorTest.php
@@ -12,9 +12,9 @@
namespace Symfony\Component\Translation\Tests;
use Symfony\Component\Translation\Translator;
-use Symfony\Component\Translation\MessageSelector;
use Symfony\Component\Translation\Loader\ArrayLoader;
use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\MessageCatalogueProvider\ResourceMessageCatalogueProvider;
class TranslatorTest extends \PHPUnit_Framework_TestCase
{
@@ -24,7 +24,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
*/
public function testConstructorInvalidLocale($locale)
{
- $translator = new Translator($locale, new MessageSelector());
+ $translator = $this->getTranslator($locale);
}
/**
@@ -32,21 +32,21 @@ public function testConstructorInvalidLocale($locale)
*/
public function testConstructorValidLocale($locale)
{
- $translator = new Translator($locale, new MessageSelector());
+ $translator = $this->getTranslator($locale);
$this->assertEquals($locale, $translator->getLocale());
}
public function testConstructorWithoutLocale()
{
- $translator = new Translator(null, new MessageSelector());
+ $translator = $this->getTranslator(null);
$this->assertNull($translator->getLocale());
}
public function testSetGetLocale()
{
- $translator = new Translator('en');
+ $translator = $this->getTranslator('en');
$this->assertEquals('en', $translator->getLocale());
@@ -60,7 +60,7 @@ public function testSetGetLocale()
*/
public function testSetInvalidLocale($locale)
{
- $translator = new Translator('fr', new MessageSelector());
+ $translator = $this->getTranslator('fr');
$translator->setLocale($locale);
}
@@ -69,7 +69,7 @@ public function testSetInvalidLocale($locale)
*/
public function testSetValidLocale($locale)
{
- $translator = new Translator($locale, new MessageSelector());
+ $translator = $this->getTranslator($locale);
$translator->setLocale($locale);
$this->assertEquals($locale, $translator->getLocale());
@@ -77,7 +77,7 @@ public function testSetValidLocale($locale)
public function testGetCatalogue()
{
- $translator = new Translator('en');
+ $translator = $this->getTranslator('en');
$this->assertEquals(new MessageCatalogue('en'), $translator->getCatalogue());
@@ -94,11 +94,16 @@ public function testGetCatalogueReturnsConsolidatedCatalogue()
*/
$locale = 'whatever';
- $translator = new Translator($locale);
- $translator->addLoader('loader-a', new ArrayLoader());
- $translator->addLoader('loader-b', new ArrayLoader());
- $translator->addResource('loader-a', array('foo' => 'foofoo'), $locale, 'domain-a');
- $translator->addResource('loader-b', array('bar' => 'foobar'), $locale, 'domain-b');
+ $loaders = array(
+ 'loader-a' => new ArrayLoader(),
+ 'loader-b' => new ArrayLoader(),
+ );
+ $resources = array(
+ array('loader-a', array('foo' => 'foofoo'), $locale, 'domain-a'),
+ array('loader-b', array('bar' => 'foobar'), $locale, 'domain-b'),
+ );
+
+ $translator = $this->getTranslator($locale, $loaders, $resources);
/*
* Test that we get a single catalogue comprising messages
@@ -109,178 +114,14 @@ public function testGetCatalogueReturnsConsolidatedCatalogue()
$this->assertTrue($catalogue->defines('bar', 'domain-b'));
}
- public function testSetFallbackLocales()
- {
- $translator = new Translator('en');
- $translator->addLoader('array', new ArrayLoader());
- $translator->addResource('array', array('foo' => 'foofoo'), 'en');
- $translator->addResource('array', array('bar' => 'foobar'), 'fr');
-
- // force catalogue loading
- $translator->trans('bar');
-
- $translator->setFallbackLocales(array('fr'));
- $this->assertEquals('foobar', $translator->trans('bar'));
- }
-
- public function testSetFallbackLocalesMultiple()
- {
- $translator = new Translator('en');
- $translator->addLoader('array', new ArrayLoader());
- $translator->addResource('array', array('foo' => 'foo (en)'), 'en');
- $translator->addResource('array', array('bar' => 'bar (fr)'), 'fr');
-
- // force catalogue loading
- $translator->trans('bar');
-
- $translator->setFallbackLocales(array('fr_FR', 'fr'));
- $this->assertEquals('bar (fr)', $translator->trans('bar'));
- }
-
- /**
- * @dataProvider getInvalidLocalesTests
- * @expectedException \InvalidArgumentException
- */
- public function testSetFallbackInvalidLocales($locale)
- {
- $translator = new Translator('fr', new MessageSelector());
- $translator->setFallbackLocales(array('fr', $locale));
- }
-
- /**
- * @dataProvider getValidLocalesTests
- */
- public function testSetFallbackValidLocales($locale)
- {
- $translator = new Translator($locale, new MessageSelector());
- $translator->setFallbackLocales(array('fr', $locale));
- // no assertion. this method just asserts that no exception is thrown
- }
-
- public function testTransWithFallbackLocale()
- {
- $translator = new Translator('fr_FR');
- $translator->setFallbackLocales(array('en'));
-
- $translator->addLoader('array', new ArrayLoader());
- $translator->addResource('array', array('bar' => 'foobar'), 'en');
-
- $this->assertEquals('foobar', $translator->trans('bar'));
- }
-
- /**
- * @dataProvider getInvalidLocalesTests
- * @expectedException \InvalidArgumentException
- */
- public function testAddResourceInvalidLocales($locale)
- {
- $translator = new Translator('fr', new MessageSelector());
- $translator->addResource('array', array('foo' => 'foofoo'), $locale);
- }
-
- /**
- * @dataProvider getValidLocalesTests
- */
- public function testAddResourceValidLocales($locale)
- {
- $translator = new Translator('fr', new MessageSelector());
- $translator->addResource('array', array('foo' => 'foofoo'), $locale);
- // no assertion. this method just asserts that no exception is thrown
- }
-
- public function testAddResourceAfterTrans()
- {
- $translator = new Translator('fr');
- $translator->addLoader('array', new ArrayLoader());
-
- $translator->setFallbackLocales(array('en'));
-
- $translator->addResource('array', array('foo' => 'foofoo'), 'en');
- $this->assertEquals('foofoo', $translator->trans('foo'));
-
- $translator->addResource('array', array('bar' => 'foobar'), 'en');
- $this->assertEquals('foobar', $translator->trans('bar'));
- }
-
- /**
- * @dataProvider getTransFileTests
- * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
- */
- public function testTransWithoutFallbackLocaleFile($format, $loader)
- {
- $loaderClass = 'Symfony\\Component\\Translation\\Loader\\'.$loader;
- $translator = new Translator('en');
- $translator->addLoader($format, new $loaderClass());
- $translator->addResource($format, __DIR__.'/fixtures/non-existing', 'en');
- $translator->addResource($format, __DIR__.'/fixtures/resources.'.$format, 'en');
-
- // force catalogue loading
- $translator->trans('foo');
- }
-
- /**
- * @dataProvider getTransFileTests
- */
- public function testTransWithFallbackLocaleFile($format, $loader)
- {
- $loaderClass = 'Symfony\\Component\\Translation\\Loader\\'.$loader;
- $translator = new Translator('en_GB');
- $translator->addLoader($format, new $loaderClass());
- $translator->addResource($format, __DIR__.'/fixtures/non-existing', 'en_GB');
- $translator->addResource($format, __DIR__.'/fixtures/resources.'.$format, 'en', 'resources');
-
- $this->assertEquals('bar', $translator->trans('foo', array(), 'resources'));
- }
-
- public function testTransWithFallbackLocaleBis()
- {
- $translator = new Translator('en_US');
- $translator->addLoader('array', new ArrayLoader());
- $translator->addResource('array', array('foo' => 'foofoo'), 'en_US');
- $translator->addResource('array', array('bar' => 'foobar'), 'en');
- $this->assertEquals('foobar', $translator->trans('bar'));
- }
-
- public function testTransWithFallbackLocaleTer()
- {
- $translator = new Translator('fr_FR');
- $translator->addLoader('array', new ArrayLoader());
- $translator->addResource('array', array('foo' => 'foo (en_US)'), 'en_US');
- $translator->addResource('array', array('bar' => 'bar (en)'), 'en');
-
- $translator->setFallbackLocales(array('en_US', 'en'));
-
- $this->assertEquals('foo (en_US)', $translator->trans('foo'));
- $this->assertEquals('bar (en)', $translator->trans('bar'));
- }
-
- public function testTransNonExistentWithFallback()
- {
- $translator = new Translator('fr');
- $translator->setFallbackLocales(array('en'));
- $translator->addLoader('array', new ArrayLoader());
- $this->assertEquals('non-existent', $translator->trans('non-existent'));
- }
-
- /**
- * @expectedException \RuntimeException
- */
- public function testWhenAResourceHasNoRegisteredLoader()
- {
- $translator = new Translator('en');
- $translator->addResource('array', array('foo' => 'foofoo'), 'en');
-
- $translator->trans('foo');
- }
-
/**
* @dataProvider getTransTests
*/
public function testTrans($expected, $id, $translation, $parameters, $locale, $domain)
{
- $translator = new Translator('en');
- $translator->addLoader('array', new ArrayLoader());
- $translator->addResource('array', array((string) $id => $translation), $locale, $domain);
+ $loaders = array('array' => new ArrayLoader());
+ $resources = array(array('array', array((string) $id => $translation), $locale, $domain));
+ $translator = $this->getTranslator('en', $loaders, $resources);
$this->assertEquals($expected, $translator->trans($id, $parameters, $domain, $locale));
}
@@ -291,9 +132,9 @@ public function testTrans($expected, $id, $translation, $parameters, $locale, $d
*/
public function testTransInvalidLocale($locale)
{
- $translator = new Translator('en', new MessageSelector());
- $translator->addLoader('array', new ArrayLoader());
- $translator->addResource('array', array('foo' => 'foofoo'), 'en');
+ $loaders = array('array' => new ArrayLoader());
+ $resources = array(array('array', array('foo' => 'foofoo'), 'en'));
+ $translator = $this->getTranslator('en', $loaders, $resources);
$translator->trans('foo', array(), '', $locale);
}
@@ -303,9 +144,9 @@ public function testTransInvalidLocale($locale)
*/
public function testTransValidLocale($locale)
{
- $translator = new Translator($locale, new MessageSelector());
- $translator->addLoader('array', new ArrayLoader());
- $translator->addResource('array', array('test' => 'OK'), $locale);
+ $loaders = array('array' => new ArrayLoader());
+ $resources = array(array('array', array('test' => 'OK'), $locale));
+ $translator = $this->getTranslator($locale, $loaders, $resources);
$this->assertEquals('OK', $translator->trans('test'));
$this->assertEquals('OK', $translator->trans('test', array(), null, $locale));
@@ -316,9 +157,9 @@ public function testTransValidLocale($locale)
*/
public function testFlattenedTrans($expected, $messages, $id)
{
- $translator = new Translator('en');
- $translator->addLoader('array', new ArrayLoader());
- $translator->addResource('array', $messages, 'fr', '');
+ $loaders = array('array' => new ArrayLoader());
+ $resources = array(array('array', $messages, 'fr', ''));
+ $translator = $this->getTranslator('en', $loaders, $resources);
$this->assertEquals($expected, $translator->trans($id, array(), '', 'fr'));
}
@@ -328,9 +169,9 @@ public function testFlattenedTrans($expected, $messages, $id)
*/
public function testTransChoice($expected, $id, $translation, $number, $parameters, $locale, $domain)
{
- $translator = new Translator('en');
- $translator->addLoader('array', new ArrayLoader());
- $translator->addResource('array', array((string) $id => $translation), $locale, $domain);
+ $loaders = array('array' => new ArrayLoader());
+ $resources = array(array('array', array((string) $id => $translation), $locale, $domain));
+ $translator = $this->getTranslator('en', $loaders, $resources);
$this->assertEquals($expected, $translator->transChoice($id, $number, $parameters, $domain, $locale));
}
@@ -341,9 +182,9 @@ public function testTransChoice($expected, $id, $translation, $number, $paramete
*/
public function testTransChoiceInvalidLocale($locale)
{
- $translator = new Translator('en', new MessageSelector());
- $translator->addLoader('array', new ArrayLoader());
- $translator->addResource('array', array('foo' => 'foofoo'), 'en');
+ $loaders = array('array' => new ArrayLoader());
+ $resources = array(array('array', array('foo' => 'foofoo'), 'en'));
+ $translator = $this->getTranslator('en', $loaders, $resources);
$translator->transChoice('foo', 1, array(), '', $locale);
}
@@ -353,29 +194,14 @@ public function testTransChoiceInvalidLocale($locale)
*/
public function testTransChoiceValidLocale($locale)
{
- $translator = new Translator('en', new MessageSelector());
- $translator->addLoader('array', new ArrayLoader());
- $translator->addResource('array', array('foo' => 'foofoo'), 'en');
+ $loaders = array('array' => new ArrayLoader());
+ $resources = array(array('array', array('foo' => 'foofoo'), 'en'));
+ $translator = $this->getTranslator('en', $loaders, $resources);
$translator->transChoice('foo', 1, array(), '', $locale);
// no assertion. this method just asserts that no exception is thrown
}
- public function getTransFileTests()
- {
- return array(
- array('csv', 'CsvFileLoader'),
- array('ini', 'IniFileLoader'),
- array('mo', 'MoFileLoader'),
- array('po', 'PoFileLoader'),
- array('php', 'PhpFileLoader'),
- array('ts', 'QtFileLoader'),
- array('xlf', 'XliffFileLoader'),
- array('yml', 'YamlFileLoader'),
- array('json', 'JsonFileLoader'),
- );
- }
-
public function getTransTests()
{
return array(
@@ -467,148 +293,37 @@ public function getValidLocalesTests()
public function testTransChoiceFallback()
{
- $translator = new Translator('ru');
- $translator->setFallbackLocales(array('en'));
- $translator->addLoader('array', new ArrayLoader());
- $translator->addResource('array', array('some_message2' => 'one thing|%count% things'), 'en');
+ $loaders = array('array' => new ArrayLoader());
+ $resources = array(array('array', array('some_message2' => 'one thing|%count% things'), 'en'));
+ $translator = $this->getTranslator('ru', $loaders, $resources, array('en'));
$this->assertEquals('10 things', $translator->transChoice('some_message2', 10, array('%count%' => 10)));
}
public function testTransChoiceFallbackBis()
{
- $translator = new Translator('ru');
- $translator->setFallbackLocales(array('en_US', 'en'));
- $translator->addLoader('array', new ArrayLoader());
- $translator->addResource('array', array('some_message2' => 'one thing|%count% things'), 'en_US');
+ $loaders = array('array' => new ArrayLoader());
+ $resources = array(array('array', array('some_message2' => 'one thing|%count% things'), 'en_US'));
+ $translator = $this->getTranslator('ru', $loaders, $resources, array('en_US', 'en'));
$this->assertEquals('10 things', $translator->transChoice('some_message2', 10, array('%count%' => 10)));
}
public function testTransChoiceFallbackWithNoTranslation()
{
- $translator = new Translator('ru');
- $translator->setFallbackLocales(array('en'));
- $translator->addLoader('array', new ArrayLoader());
+ $loaders = array('array' => new ArrayLoader());
+ $translator = $this->getTranslator('ru', $loaders, array(), array('en'));
// consistent behavior with Translator::trans(), which returns the string
// unchanged if it can't be found
$this->assertEquals('some_message2', $translator->transChoice('some_message2', 10, array('%count%' => 10)));
}
- /**
- * @group legacy
- * @dataProvider dataProviderGetMessages
- */
- public function testLegacyGetMessages($resources, $locale, $expected)
- {
- $locales = array_keys($resources);
- $_locale = null !== $locale ? $locale : reset($locales);
- $locales = array_slice($locales, 0, array_search($_locale, $locales));
-
- $translator = new Translator($_locale, new MessageSelector());
- $translator->setFallbackLocales(array_reverse($locales));
- $translator->addLoader('array', new ArrayLoader());
- foreach ($resources as $_locale => $domainMessages) {
- foreach ($domainMessages as $domain => $messages) {
- $translator->addResource('array', $messages, $_locale, $domain);
- }
- }
- $result = $translator->getMessages($locale);
-
- $this->assertEquals($expected, $result);
- }
-
- public function dataProviderGetMessages()
+ protected function getTranslator($locale, $loaders = array(), $resources = array(), $fallbackLocales = array())
{
- $resources = array(
- 'en' => array(
- 'jsmessages' => array(
- 'foo' => 'foo (EN)',
- 'bar' => 'bar (EN)',
- ),
- 'messages' => array(
- 'foo' => 'foo messages (EN)',
- ),
- 'validators' => array(
- 'int' => 'integer (EN)',
- ),
- ),
- 'pt-PT' => array(
- 'messages' => array(
- 'foo' => 'foo messages (PT)',
- ),
- 'validators' => array(
- 'str' => 'integer (PT)',
- ),
- ),
- 'pt_BR' => array(
- 'validators' => array(
- 'int' => 'integer (BR)',
- ),
- ),
- );
+ $resourceCatalogue = new ResourceMessageCatalogueProvider($loaders, $resources, $fallbackLocales);
- return array(
- array($resources, null,
- array(
- 'jsmessages' => array(
- 'foo' => 'foo (EN)',
- 'bar' => 'bar (EN)',
- ),
- 'messages' => array(
- 'foo' => 'foo messages (EN)',
- ),
- 'validators' => array(
- 'int' => 'integer (EN)',
- ),
- ),
- ),
- array($resources, 'en',
- array(
- 'jsmessages' => array(
- 'foo' => 'foo (EN)',
- 'bar' => 'bar (EN)',
- ),
- 'messages' => array(
- 'foo' => 'foo messages (EN)',
- ),
- 'validators' => array(
- 'int' => 'integer (EN)',
- ),
- ),
- ),
- array($resources, 'pt-PT',
- array(
- 'jsmessages' => array(
- 'foo' => 'foo (EN)',
- 'bar' => 'bar (EN)',
- ),
- 'messages' => array(
- 'foo' => 'foo messages (PT)',
- ),
- 'validators' => array(
- 'int' => 'integer (EN)',
- 'str' => 'integer (PT)',
- ),
- ),
- ),
- array($resources, 'pt_BR',
- array(
- 'jsmessages' => array(
- 'foo' => 'foo (EN)',
- 'bar' => 'bar (EN)',
- ),
- 'messages' => array(
- 'foo' => 'foo messages (PT)',
- ),
- 'validators' => array(
- 'int' => 'integer (BR)',
- 'str' => 'integer (PT)',
- ),
- ),
- ),
- );
+ return new Translator($locale, $resourceCatalogue);
}
}
diff --git a/src/Symfony/Component/Translation/Translator.php b/src/Symfony/Component/Translation/Translator.php
index ef8c7bed3a91f..5f93c8c14684c 100644
--- a/src/Symfony/Component/Translation/Translator.php
+++ b/src/Symfony/Component/Translation/Translator.php
@@ -11,9 +11,11 @@
namespace Symfony\Component\Translation;
+use Symfony\Component\Translation\MessageCatalogueProvider\MessageCatalogueProviderInterface;
+use Symfony\Component\Translation\MessageCatalogueProvider\ResourceMessageCatalogueProvider;
+use Symfony\Component\Translation\MessageCatalogueProvider\CachedMessageCatalogueProvider;
use Symfony\Component\Translation\Loader\LoaderInterface;
use Symfony\Component\Translation\Exception\NotFoundResourceException;
-use Symfony\Component\Config\ConfigCacheInterface;
use Symfony\Component\Config\ConfigCacheFactoryInterface;
use Symfony\Component\Config\ConfigCacheFactory;
@@ -26,6 +28,8 @@ class Translator implements TranslatorInterface, TranslatorBagInterface
{
/**
* @var MessageCatalogueInterface[]
+ *
+ * Deprecated since version 2.8, to be removed in 3.0. Use Translator::getCatalogue instead.
*/
protected $catalogues = array();
@@ -34,21 +38,6 @@ class Translator implements TranslatorInterface, TranslatorBagInterface
*/
protected $locale;
- /**
- * @var array
- */
- private $fallbackLocales = array();
-
- /**
- * @var LoaderInterface[]
- */
- private $loaders = array();
-
- /**
- * @var array
- */
- private $resources = array();
-
/**
* @var MessageSelector
*/
@@ -65,36 +54,68 @@ class Translator implements TranslatorInterface, TranslatorBagInterface
private $debug;
/**
- * @var ConfigCacheFactoryInterface|null
+ * @var MessageCatalogueProviderInterface
*/
- private $configCacheFactory;
+ private $messageCatalogueProvider;
/**
- * Constructor.
- *
- * @param string $locale The locale
- * @param MessageSelector|null $selector The message selector for pluralization
- * @param string|null $cacheDir The directory to use for the cache
- * @param bool $debug Use cache in debug mode ?
+ * @var ResourceMessageCatalogueProvider
+ */
+ private $resourceMessageCatalogueProvider;
+
+ /**
+ * @var CachedMessageCatalogueProvider
+ */
+ private $cacheMessageCatalogueProvider;
+
+ /**
+ * @param string $locale The locale
+ * @param MessageCatalogueProviderInterface|MessageSelector|null $messageCatalogueProvider The MessageCatalogueProviderInterface or MessageSelector
+ * Passing the MessageSelector or null as a second parameter is deprecated since version 2.8.
+ * @param MessageSelector|string|null $selector The MessageSelector or cache directory
+ * Passing the cache directory as a third parameter is deprecated since version 2.8.
+ * @param bool $debug Use cache in debug mode ?
+ * Deprecated since version 2.8, to be removed in 3.0.
*
* @throws \InvalidArgumentException If a locale contains invalid characters
*/
- public function __construct($locale, MessageSelector $selector = null, $cacheDir = null, $debug = false)
+ public function __construct($locale, $messageCatalogueProvider = null, $selector = null, $debug = false)
{
$this->setLocale($locale);
- $this->selector = $selector ?: new MessageSelector();
- $this->cacheDir = $cacheDir;
- $this->debug = $debug;
+
+ if ($messageCatalogueProvider instanceof MessageCatalogueProviderInterface) {
+ $this->messageCatalogueProvider = $messageCatalogueProvider;
+ $this->selector = $selector ?: new MessageSelector();
+ } else {
+ @trigger_error('The '.__CLASS__.' constructor will require a MessageCatalogueProviderInterface for its second argument since 3.0.', E_USER_DEPRECATED);
+
+ // Parameters are shifted of one offset
+ $this->selector = $messageCatalogueProvider ?: new MessageSelector();
+ $this->cacheDir = $selector;
+ $this->debug = $debug;
+ }
+
+ if (!$this->selector instanceof MessageSelector) {
+ throw new \InvalidArgumentException(sprintf('The message selector "%s" must be an instance of MessageSelector.', get_class($this->selector)));
+ }
+
+ if ($this->isMethodOverwritten('assertValidLocale')) {
+ @trigger_error('The Translator::assertValidLocale method is deprecated since version 2.8 and will be removed in 3.0. Use Translator::assertLocale method instead.', E_USER_DEPRECATED);
+ }
}
/**
* Sets the ConfigCache factory to use.
*
* @param ConfigCacheFactoryInterface $configCacheFactory
+ *
+ * @deprecated since version 2.8, to be removed in 3.0. Rely on CachedMessageCatalogueProvider instead.
*/
public function setConfigCacheFactory(ConfigCacheFactoryInterface $configCacheFactory)
{
- $this->configCacheFactory = $configCacheFactory;
+ @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0. Rely on CachedMessageCatalogueProvider instead.', E_USER_DEPRECATED);
+
+ $this->getCachedMessageCatalogueProvider()->getConfigCacheFactory($configCacheFactory);
}
/**
@@ -102,10 +123,14 @@ public function setConfigCacheFactory(ConfigCacheFactoryInterface $configCacheFa
*
* @param string $format The name of the loader (@see addResource())
* @param LoaderInterface $loader A LoaderInterface instance
+ *
+ * @deprecated since version 2.8, to be removed in 3.0. Use ResourceMessageCatalogueProvider::addLoader instead.
*/
public function addLoader($format, LoaderInterface $loader)
{
- $this->loaders[$format] = $loader;
+ @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0. Use ResourceMessageCatalogueProvider::addLoader instead.', E_USER_DEPRECATED);
+
+ $this->getResourceMessageCatalogueProvider()->addLoader($format, $loader);
}
/**
@@ -117,22 +142,14 @@ public function addLoader($format, LoaderInterface $loader)
* @param string $domain The domain
*
* @throws \InvalidArgumentException If the locale contains invalid characters
+ *
+ * @deprecated since version 2.8, to be removed in 3.0. Use ResourceMessageCatalogueProvider::addResource instead.
*/
public function addResource($format, $resource, $locale, $domain = null)
{
- if (null === $domain) {
- $domain = 'messages';
- }
-
- $this->assertValidLocale($locale);
-
- $this->resources[$locale][] = array($format, $resource, $domain);
+ @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0. Use ResourceMessageCatalogueProvider::addResource instead.', E_USER_DEPRECATED);
- if (in_array($locale, $this->fallbackLocales)) {
- $this->catalogues = array();
- } else {
- unset($this->catalogues[$locale]);
- }
+ $this->getResourceMessageCatalogueProvider()->addResource($format, $resource, $locale, $domain);
}
/**
@@ -174,27 +191,28 @@ public function setFallbackLocale($locales)
* @param array $locales The fallback locales
*
* @throws \InvalidArgumentException If a locale contains invalid characters
+ *
+ * @deprecated since version 2.8, to be removed in 3.0. Use ResourceMessageCatalogueProvider::setFallbackLocales instead.
*/
public function setFallbackLocales(array $locales)
{
- // needed as the fallback locales are linked to the already loaded catalogues
- $this->catalogues = array();
+ @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0. Use ResourceMessageCatalogueProvider::setFallbackLocales instead.', E_USER_DEPRECATED);
- foreach ($locales as $locale) {
- $this->assertValidLocale($locale);
- }
-
- $this->fallbackLocales = $locales;
+ $this->getResourceMessageCatalogueProvider()->setFallbackLocales($locales);
}
/**
* Gets the fallback locales.
*
* @return array $locales The fallback locales
+ *
+ * @deprecated since version 2.8, to be removed in 3.0. Use ResourceMessageCatalogueProvider::getFallbackLocales instead.
*/
public function getFallbackLocales()
{
- return $this->fallbackLocales;
+ @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0. Use ResourceMessageCatalogueProvider::getFallbackLocales instead.', E_USER_DEPRECATED);
+
+ return $this->getResourceMessageCatalogueProvider()->getFallbackLocales();
}
/**
@@ -244,21 +262,38 @@ public function getCatalogue($locale = null)
$this->assertValidLocale($locale);
}
- if (!isset($this->catalogues[$locale])) {
+ // check if the Translator class is overwritten
+ if ('Symfony\Component\Translation\Translator' !== get_class($this) && !$this->messageCatalogueProvider) {
+ if (isset($this->catalogues[$locale])) {
+ return $this->catalogues[$locale];
+ }
+
+ if ($this->isMethodOverwritten('loadCatalogue')) {
+ @trigger_error('The Translator::loadCatalogue method is deprecated since version 2.8 and will be removed in 3.0. Rely on MessageCatalogueProviderInterface::getCatalogue() instead.', E_USER_DEPRECATED);
+ }
+
+ if ($this->isMethodOverwritten('getLoaders')) {
+ @trigger_error('The Translator::getLoaders method is deprecated since version 2.8 and will be removed in 3.0. Rely on ResourceMessageCatalogueProvider::getLoaders instead.', E_USER_DEPRECATED);
+ }
+
$this->loadCatalogue($locale);
+
+ return $this->catalogues[$locale];
}
- return $this->catalogues[$locale];
+ return $this->catalogues[$locale] = $this->getMessageCatalogueProvider()->getCatalogue($locale);
}
/**
* Gets the loaders.
*
* @return array LoaderInterface[]
+ *
+ * @deprecated since version 2.8, to be removed in 3.0. Rely on ResourceMessageCatalogueProvider::getLoaders instead.
*/
protected function getLoaders()
{
- return $this->loaders;
+ return $this->getResourceMessageCatalogueProvider()->getLoaders();
}
/**
@@ -268,11 +303,11 @@ protected function getLoaders()
*
* @return array[array] indexed by catalog
*
- * @deprecated since version 2.8, to be removed in 3.0. Use TranslatorBagInterface::getCatalogue() method instead.
+ * @deprecated since version 2.8, to be removed in 3.0. Use ResourceMessageCatalogueProviderInterface::getCatalogue() method instead.
*/
public function getMessages($locale = null)
{
- @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0. Use TranslatorBagInterface::getCatalogue() method instead.', E_USER_DEPRECATED);
+ @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0. Use ResourceMessageCatalogueProviderInterface::getCatalogue() method instead.', E_USER_DEPRECATED);
$catalogue = $this->getCatalogue($locale);
$messages = $catalogue->all();
@@ -285,9 +320,15 @@ public function getMessages($locale = null)
/**
* @param string $locale
+ *
+ * @deprecated since version 2.8, to be removed in 3.0. Rely on MessageCatalogueProviderInterface::getCatalogue instead.
*/
protected function loadCatalogue($locale)
{
+ if ($this->isMethodOverwritten('initializeCatalogue')) {
+ @trigger_error('The Translator::initializeCatalogue method is deprecated since version 2.8 and will be removed in 3.0. Rely on MessageCatalogueProviderInterface::getCatalogue() instead.', E_USER_DEPRECATED);
+ }
+
if (null === $this->cacheDir) {
$this->initializeCatalogue($locale);
} else {
@@ -297,19 +338,47 @@ protected function loadCatalogue($locale)
/**
* @param string $locale
+ *
+ * @deprecated since version 2.8, to be removed in 3.0. Rely on MessageCatalogueProviderInterface::getCatalogue instead.
*/
protected function initializeCatalogue($locale)
{
$this->assertValidLocale($locale);
+ if ($this->isMethodOverwritten('computeFallbackLocales')) {
+ @trigger_error('The Translator::computeFallbackLocales method is deprecated since version 2.8 and will be removed in 3.0. Rely on ResourceMessageCatalogueProvider instead.', E_USER_DEPRECATED);
+ }
+
try {
- $this->doLoadCatalogue($locale);
+ $this->catalogues[$locale] = $this->getResourceMessageCatalogueProvider()->loadCatalogue($locale);
} catch (NotFoundResourceException $e) {
if (!$this->computeFallbackLocales($locale)) {
throw $e;
}
}
- $this->loadFallbackCatalogues($locale);
+ // load Fallback Catalogues
+ $current = $this->catalogues[$locale];
+ foreach ($this->computeFallbackLocales($locale) as $fallback) {
+ if (!isset($this->catalogues[$fallback])) {
+ $this->catalogues[$fallback] = $this->getResourceMessageCatalogueProvider()->loadCatalogue($fallback);
+ }
+
+ $fallbackCatalogue = new MessageCatalogue($fallback, $this->catalogues[$fallback]->all());
+ $current->addFallbackCatalogue($fallbackCatalogue);
+ $current = $fallbackCatalogue;
+ }
+ }
+
+ /**
+ * This method is public because it needs to be callable from a closure in PHP 5.3. It should be removed in 3.0.
+ *
+ * @internal
+ */
+ public function initializeAndGetCatalogue($locale)
+ {
+ $this->initializeCatalogue($locale);
+
+ return $this->catalogues[$locale];
}
/**
@@ -323,12 +392,10 @@ private function initializeCacheCatalogue($locale)
}
$this->assertValidLocale($locale);
- $self = $this; // required for PHP 5.3 where "$this" cannot be use()d in anonymous functions. Change in Symfony 3.0.
- $cache = $this->getConfigCacheFactory()->cache($this->getCatalogueCachePath($locale),
- function (ConfigCacheInterface $cache) use ($self, $locale) {
- $self->dumpCatalogue($locale, $cache);
- }
- );
+ $self = $this; // required for PHP 5.3 where "$this" cannot be used in anonymous functions. Change in Symfony 3.0.
+ $cache = $this->getCachedMessageCatalogueProvider()->cache($locale, function () use ($self, $locale) {
+ return $self->initializeAndGetCatalogue($locale);
+ });
if (isset($this->catalogues[$locale])) {
/* Catalogue has been initialized as it was written out to cache. */
@@ -336,147 +403,78 @@ function (ConfigCacheInterface $cache) use ($self, $locale) {
}
/* Read catalogue from cache. */
- $this->catalogues[$locale] = include $cache->getPath();
+ $this->catalogues[$locale] = $cache;
}
/**
- * This method is public because it needs to be callable from a closure in PHP 5.3. It should be made protected (or even private, if possible) in 3.0.
- *
- * @internal
+ * @deprecated since version 2.8, to be removed in 3.0. Rely on ResourceMessageCatalogueProvider instead.
*/
- public function dumpCatalogue($locale, ConfigCacheInterface $cache)
- {
- $this->initializeCatalogue($locale);
- $fallbackContent = $this->getFallbackContent($this->catalogues[$locale]);
-
- $content = sprintf(<<catalogues[$locale]->all(), true),
- $fallbackContent
- );
-
- $cache->write($content, $this->catalogues[$locale]->getResources());
- }
-
- private function getFallbackContent(MessageCatalogue $catalogue)
+ protected function computeFallbackLocales($locale)
{
- $fallbackContent = '';
- $current = '';
- $replacementPattern = '/[^a-z0-9_]/i';
- $fallbackCatalogue = $catalogue->getFallbackCatalogue();
- while ($fallbackCatalogue) {
- $fallback = $fallbackCatalogue->getLocale();
- $fallbackSuffix = ucfirst(preg_replace($replacementPattern, '_', $fallback));
- $currentSuffix = ucfirst(preg_replace($replacementPattern, '_', $current));
-
- $fallbackContent .= sprintf(<<addFallbackCatalogue(\$catalogue%s);
-
-EOF
- ,
- $fallbackSuffix,
- $fallback,
- var_export($fallbackCatalogue->all(), true),
- $currentSuffix,
- $fallbackSuffix
- );
- $current = $fallbackCatalogue->getLocale();
- $fallbackCatalogue = $fallbackCatalogue->getFallbackCatalogue();
- }
-
- return $fallbackContent;
+ return $this->getResourceMessageCatalogueProvider()->computeFallbackLocales($locale);
}
- private function getCatalogueCachePath($locale)
+ /**
+ * Asserts that the locale is valid, throws an Exception if not.
+ *
+ * @param string $locale Locale to tests
+ *
+ * @throws \InvalidArgumentException If the locale contains invalid characters
+ */
+ protected function assertValidLocale($locale)
{
- return $this->cacheDir.'/catalogue.'.$locale.'.'.sha1(serialize($this->fallbackLocales)).'.php';
+ self::assertLocale($locale);
}
- private function doLoadCatalogue($locale)
+ /**
+ * Asserts that the locale is valid, throws an Exception if not.
+ *
+ * @param string $locale Locale to tests
+ *
+ * @throws \InvalidArgumentException If the locale contains invalid characters
+ */
+ public static function assertLocale($locale)
{
- $this->catalogues[$locale] = new MessageCatalogue($locale);
-
- if (isset($this->resources[$locale])) {
- foreach ($this->resources[$locale] as $resource) {
- if (!isset($this->loaders[$resource[0]])) {
- throw new \RuntimeException(sprintf('The "%s" translation loader is not registered.', $resource[0]));
- }
- $this->catalogues[$locale]->addCatalogue($this->loaders[$resource[0]]->load($resource[1], $locale, $resource[2]));
- }
+ if (1 !== preg_match('/^[a-z0-9@_\\.\\-]*$/i', $locale)) {
+ throw new \InvalidArgumentException(sprintf('Invalid "%s" locale.', $locale));
}
}
- private function loadFallbackCatalogues($locale)
+ private function getMessageCatalogueProvider()
{
- $current = $this->catalogues[$locale];
-
- foreach ($this->computeFallbackLocales($locale) as $fallback) {
- if (!isset($this->catalogues[$fallback])) {
- $this->doLoadCatalogue($fallback);
- }
+ if ($this->messageCatalogueProvider) {
+ return $this->messageCatalogueProvider;
+ }
- $fallbackCatalogue = new MessageCatalogue($fallback, $this->catalogues[$fallback]->all());
- $current->addFallbackCatalogue($fallbackCatalogue);
- $current = $fallbackCatalogue;
+ if (null !== $this->cacheDir) {
+ return $this->getCachedMessageCatalogueProvider();
}
+
+ return $this->getResourceMessageCatalogueProvider();
}
- protected function computeFallbackLocales($locale)
+ private function getResourceMessageCatalogueProvider()
{
- $locales = array();
- foreach ($this->fallbackLocales as $fallback) {
- if ($fallback === $locale) {
- continue;
- }
-
- $locales[] = $fallback;
+ if ($this->resourceMessageCatalogueProvider) {
+ return $this->resourceMessageCatalogueProvider;
}
- if (strrchr($locale, '_') !== false) {
- array_unshift($locales, substr($locale, 0, -strlen(strrchr($locale, '_'))));
- }
-
- return array_unique($locales);
+ return $this->resourceMessageCatalogueProvider = new ResourceMessageCatalogueProvider();
}
- /**
- * Asserts that the locale is valid, throws an Exception if not.
- *
- * @param string $locale Locale to tests
- *
- * @throws \InvalidArgumentException If the locale contains invalid characters
- */
- protected function assertValidLocale($locale)
+ private function getCachedMessageCatalogueProvider()
{
- if (1 !== preg_match('/^[a-z0-9@_\\.\\-]*$/i', $locale)) {
- throw new \InvalidArgumentException(sprintf('Invalid "%s" locale.', $locale));
+ if ($this->cacheMessageCatalogueProvider) {
+ return $this->cacheMessageCatalogueProvider;
}
+
+ return $this->cacheMessageCatalogueProvider = new CachedMessageCatalogueProvider($this->getResourceMessageCatalogueProvider(), new ConfigCacheFactory($this->debug), $this->cacheDir);
}
- /**
- * Provides the ConfigCache factory implementation, falling back to a
- * default implementation if necessary.
- *
- * @return ConfigCacheFactoryInterface $configCacheFactory
- */
- private function getConfigCacheFactory()
+ private function isMethodOverwritten($name)
{
- if (!$this->configCacheFactory) {
- $this->configCacheFactory = new ConfigCacheFactory($this->debug);
- }
+ $reflector = new \ReflectionMethod($this, $name);
- return $this->configCacheFactory;
+ return ($reflector->getDeclaringClass()->getName() !== 'Symfony\Component\Translation\Translator');
}
}
diff --git a/src/Symfony/Component/Translation/composer.json b/src/Symfony/Component/Translation/composer.json
index 71a7450002675..6130389e8c0f1 100644
--- a/src/Symfony/Component/Translation/composer.json
+++ b/src/Symfony/Component/Translation/composer.json
@@ -30,7 +30,8 @@
"suggest": {
"symfony/config": "",
"symfony/yaml": "",
- "psr/log": "To use logging capability in translator"
+ "psr/log": "To use logging capability in translator",
+ "symfony/dependency-injection": ""
},
"autoload": {
"psr-4": { "Symfony\\Component\\Translation\\": "" }