added an IdentityTranslator to make it possible to always relies on the translator service, even if none is configured

This commit is contained in:
Fabien Potencier 2010-09-27 16:53:23 +02:00
parent 9c9edb3904
commit 707205410e
8 changed files with 165 additions and 67 deletions

View File

@ -100,9 +100,7 @@ class FrameworkExtension extends Extension
$this->registerUserConfiguration($config, $container); $this->registerUserConfiguration($config, $container);
} }
if (array_key_exists('translator', $config)) { $this->registerTranslatorConfiguration($config, $container);
$this->registerTranslatorConfiguration($config, $container);
}
$this->addCompiledClasses($container, array( $this->addCompiledClasses($container, array(
'Symfony\\Component\\HttpFoundation\\ParameterBag', 'Symfony\\Component\\HttpFoundation\\ParameterBag',
@ -234,40 +232,49 @@ class FrameworkExtension extends Extension
*/ */
protected function registerTranslatorConfiguration($config, ContainerBuilder $container) protected function registerTranslatorConfiguration($config, ContainerBuilder $container)
{ {
$config = $config['translator']; $first = false;
if (!$container->hasDefinition('translator')) {
$first = true;
$loader = new XmlFileLoader($container, array(__DIR__.'/../Resources/config', __DIR__.'/Resources/config'));
$loader->load('translation.xml');
}
$config = array_key_exists('translator', $config) ? $config['translator'] : array();
if (!is_array($config)) { if (!is_array($config)) {
$config = array(); $config = array();
} }
if (!$container->hasDefinition('translator')) { if (!isset($config['translator']['enabled']) || $config['translator']['enabled']) {
$loader = new XmlFileLoader($container, array(__DIR__.'/../Resources/config', __DIR__.'/Resources/config')); // use the "real" translator
$loader->load('translation.xml'); $container->setDefinition('translator', $container->findDefinition('translator.real'));
// translation directories if ($first) {
$dirs = array(); // translation directories
foreach (array_reverse($container->getParameter('kernel.bundles')) as $bundle) { $dirs = array();
$reflection = new \ReflectionClass($bundle); foreach (array_reverse($container->getParameter('kernel.bundles')) as $bundle) {
if (is_dir($dir = dirname($reflection->getFilename()).'/Resources/translations')) { $reflection = new \ReflectionClass($bundle);
if (is_dir($dir = dirname($reflection->getFilename()).'/Resources/translations')) {
$dirs[] = $dir;
}
}
if (is_dir($dir = $container->getParameter('kernel.root_dir').'/translations')) {
$dirs[] = $dir; $dirs[] = $dir;
} }
}
if (is_dir($dir = $container->getParameter('kernel.root_dir').'/translations')) {
$dirs[] = $dir;
}
// translation resources // translation resources
$resources = array(); $resources = array();
if ($dirs) { if ($dirs) {
$finder = new Finder(); $finder = new Finder();
$finder->files()->filter(function (\SplFileInfo $file) { return 2 === substr_count($file->getBasename(), '.'); })->in($dirs); $finder->files()->filter(function (\SplFileInfo $file) { return 2 === substr_count($file->getBasename(), '.'); })->in($dirs);
foreach ($finder as $file) { foreach ($finder as $file) {
// filename is domain.locale.format // filename is domain.locale.format
list($domain, $locale, $format) = explode('.', $file->getBasename()); list($domain, $locale, $format) = explode('.', $file->getBasename());
$resources[] = array($format, (string) $file, $locale, $domain); $resources[] = array($format, (string) $file, $locale, $domain);
}
} }
$container->setParameter('translation.resources', $resources);
} }
$container->setParameter('translation.resources', $resources);
} }
if (array_key_exists('fallback', $config)) { if (array_key_exists('fallback', $config)) {

View File

@ -6,14 +6,17 @@
<parameters> <parameters>
<parameter key="translator.class">Symfony\Bundle\FrameworkBundle\Translation\Translator</parameter> <parameter key="translator.class">Symfony\Bundle\FrameworkBundle\Translation\Translator</parameter>
<parameter key="translator.identity.class">Symfony\Component\Translation\IdentityTranslator</parameter>
<parameter key="translator.selector.class">Symfony\Component\Translation\MessageSelector</parameter>
<parameter key="translation.loader.php.class">Symfony\Component\Translation\Loader\PhpFileLoader</parameter> <parameter key="translation.loader.php.class">Symfony\Component\Translation\Loader\PhpFileLoader</parameter>
<parameter key="translation.loader.xliff.class">Symfony\Component\Translation\Loader\XliffFileLoader</parameter> <parameter key="translation.loader.xliff.class">Symfony\Component\Translation\Loader\XliffFileLoader</parameter>
<parameter key="translator.fallback_locale">en</parameter> <parameter key="translator.fallback_locale">en</parameter>
</parameters> </parameters>
<services> <services>
<service id="translator" class="%translator.class%"> <service id="translator.real" class="%translator.class%">
<argument type="service" id="service_container" /> <argument type="service" id="service_container" />
<argument type="service" id="translator.selector" />
<argument type="collection"> <argument type="collection">
<argument key="cache_dir">%kernel.cache_dir%/translations</argument> <argument key="cache_dir">%kernel.cache_dir%/translations</argument>
<argument key="debug">%kernel.debug%</argument> <argument key="debug">%kernel.debug%</argument>
@ -22,6 +25,12 @@
<call method="setFallbackLocale"><argument>%translator.fallback_locale%</argument></call> <call method="setFallbackLocale"><argument>%translator.fallback_locale%</argument></call>
</service> </service>
<service id="translator" class="%translator.identity.class%">
<argument type="service" id="translator.selector" />
</service>
<service id="translator.selector" class="%translator.selector.class%" />
<service id="translation.loader.php" class="%translation.loader.php.class%"> <service id="translation.loader.php" class="%translation.loader.php.class%">
<tag name="translation.loader" alias="php" /> <tag name="translation.loader" alias="php" />
</service> </service>

View File

@ -53,8 +53,9 @@ class FrameworkExtensionTest extends TestCase
'Symfony\\Framework' => __DIR__ . '/../../../Framework', 'Symfony\\Framework' => __DIR__ . '/../../../Framework',
), ),
'kernel.bundles' => array( 'kernel.bundles' => array(
'FrameworkBundle', 'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle',
), ),
'kernel.root_dir' => __DIR__,
'kernel.debug' => false, 'kernel.debug' => false,
'kernel.compiled_classes' => array(), 'kernel.compiled_classes' => array(),
))); )));

View File

@ -4,6 +4,7 @@ namespace Symfony\Bundle\FrameworkBundle\Translation;
use Symfony\Component\Translation\Translator as BaseTranslator; use Symfony\Component\Translation\Translator as BaseTranslator;
use Symfony\Component\Translation\Loader\LoaderInterface; use Symfony\Component\Translation\Loader\LoaderInterface;
use Symfony\Component\Translation\MessageSelector;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Session; use Symfony\Component\HttpFoundation\Session;
@ -35,13 +36,14 @@ class Translator extends BaseTranslator
* * debug: Whether to enable debugging or not (false by default) * * debug: Whether to enable debugging or not (false by default)
* *
* @param ContainerInterface $container A ContainerInterface instance * @param ContainerInterface $container A ContainerInterface instance
* @param MessageSelector $selector The message selector for pluralization
* @param array $options An array of options * @param array $options An array of options
* @param Session $session A Session instance * @param Session $session A Session instance
*/ */
public function __construct(ContainerInterface $container, array $options = array(), Session $session = null) public function __construct(ContainerInterface $container, MessageSelector $selector, array $options = array(), Session $session = null)
{ {
if (null !== $session) { if (null !== $session) {
parent::__construct($session->getLocale()); parent::__construct($session->getLocale(), $selector);
} }
$this->container = $container; $this->container = $container;

View File

@ -0,0 +1,55 @@
<?php
namespace Symfony\Component\Translation;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* IdentityTranslator does not translate anything.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class IdentityTranslator implements TranslatorInterface
{
protected $selector;
/**
* Constructor.
*
* @param MessageSelector $selector The message selector for pluralization
*/
public function __construct(MessageSelector $selector)
{
$this->selector = $selector;
}
/**
* {@inheritdoc}
*/
public function setLocale($locale)
{
}
/**
* {@inheritdoc}
*/
public function trans($id, array $parameters = array(), $domain = 'messages', $locale = null)
{
return strtr($id, $parameters);
}
/**
* {@inheritdoc}
*/
public function transChoice($id, $number, array $parameters = array(), $domain = 'messages', $locale = null)
{
return strtr($this->selector->choose($id, (int) $number, $locale), $parameters);
}
}

View File

@ -0,0 +1,52 @@
<?php
namespace Symfony\Component\Translation;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* MessageSelector.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class MessageSelector
{
public function choose($message, $number, $locale)
{
$parts = explode('|', $message);
$explicitRules = array();
$standardRules = array();
foreach ($parts as $part) {
$part = trim($part);
if (preg_match('/^(?<range>'.Range::getRangeRegexp().')\s+(?<message>.+?)$/x', $part, $matches)) {
$explicitRules[$matches['range']] = $matches['message'];
} elseif (preg_match('/^\w+\: +(.+)$/', $part, $matches)) {
$standardRules[] = $matches[1];
} else {
$standardRules[] = $part;
}
}
// try to match an explicit rule, then fallback to the standard ones
foreach ($explicitRules as $range => $m) {
if (Range::test($number, $range)) {
return $m;
}
}
$position = PluralizationRules::get($number, $locale);
if (!isset($standardRules[$position])) {
throw new \InvalidArgumentException('Unable to choose a translation.');
}
return $standardRules[$position];
}
}

View File

@ -25,15 +25,18 @@ class Translator implements TranslatorInterface
protected $fallbackLocale; protected $fallbackLocale;
protected $loaders; protected $loaders;
protected $resources; protected $resources;
protected $selector;
/** /**
* Constructor. * Constructor.
* *
* @param string $locale The locale * @param string $locale The locale
* @param MessageSelector $selector The message selector for pluralization
*/ */
public function __construct($locale = null) public function __construct($locale = null, MessageSelector $selector)
{ {
$this->locale = $locale; $this->locale = $locale;
$this->selector = $selector;
$this->loaders = array(); $this->loaders = array();
$this->resources = array(); $this->resources = array();
$this->catalogues = array(); $this->catalogues = array();
@ -119,39 +122,7 @@ class Translator implements TranslatorInterface
$this->loadCatalogue($locale); $this->loadCatalogue($locale);
} }
return strtr($this->chooseMessage($this->catalogues[$locale]->getMessage($id, $domain), (int) $number, $locale), $parameters); return strtr($this->selector->choose($this->catalogues[$locale]->getMessage($id, $domain), (int) $number, $locale), $parameters);
}
protected function chooseMessage($message, $number, $locale)
{
$parts = explode('|', $message);
$explicitRules = array();
$standardRules = array();
foreach ($parts as $part) {
$part = trim($part);
if (preg_match('/^(?<range>'.Range::getRangeRegexp().')\s+(?<message>.+?)$/x', $part, $matches)) {
$explicitRules[$matches['range']] = $matches['message'];
} elseif (preg_match('/^\w+\: +(.+)$/', $part, $matches)) {
$standardRules[] = $matches[1];
} else {
$standardRules[] = $part;
}
}
// try to match an explicit rule, then fallback to the standard ones
foreach ($explicitRules as $range => $m) {
if (Range::test($number, $range)) {
return $m;
}
}
$position = PluralizationRules::get($number, $locale);
if (!isset($standardRules[$position])) {
throw new \InvalidArgumentException('Unable to choose a translation.');
}
return $standardRules[$position];
} }
protected function loadCatalogue($locale) protected function loadCatalogue($locale)

View File

@ -12,6 +12,7 @@
namespace Symfony\Tests\Component\Translation; namespace Symfony\Tests\Component\Translation;
use Symfony\Component\Translation\Translator; use Symfony\Component\Translation\Translator;
use Symfony\Component\Translation\MessageSelector;
use Symfony\Component\Translation\Loader\ArrayLoader; use Symfony\Component\Translation\Loader\ArrayLoader;
class TranslatorTest extends \PHPUnit_Framework_TestCase class TranslatorTest extends \PHPUnit_Framework_TestCase
@ -21,7 +22,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
*/ */
public function testTrans($expected, $id, $translation, $parameters, $locale, $domain) public function testTrans($expected, $id, $translation, $parameters, $locale, $domain)
{ {
$translator = new Translator(); $translator = new Translator('en', new MessageSelector());
$translator->addLoader('array', new ArrayLoader()); $translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', array($id => $translation), $locale, $domain); $translator->addResource('array', array($id => $translation), $locale, $domain);
@ -33,7 +34,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
*/ */
public function testTransChoice($expected, $id, $translation, $number, $parameters, $locale, $domain) public function testTransChoice($expected, $id, $translation, $number, $parameters, $locale, $domain)
{ {
$translator = new Translator(); $translator = new Translator('en', new MessageSelector());
$translator->addLoader('array', new ArrayLoader()); $translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', array($id => $translation), $locale, $domain); $translator->addResource('array', array($id => $translation), $locale, $domain);