[FrameworkBundle] added support for the Translation component
This commit is contained in:
parent
d6f55c31d1
commit
d3aca1c04a
@ -8,6 +8,7 @@ use Symfony\Component\DependencyInjection\Resource\FileResource;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony framework.
|
||||
@ -99,6 +100,10 @@ class FrameworkExtension extends Extension
|
||||
$this->registerUserConfiguration($config, $container);
|
||||
}
|
||||
|
||||
if (array_key_exists('translator', $config)) {
|
||||
$this->registerTranslatorConfiguration($config, $container);
|
||||
}
|
||||
|
||||
$this->addCompiledClasses($container, array(
|
||||
'Symfony\\Component\\HttpFoundation\\ParameterBag',
|
||||
'Symfony\\Component\\HttpFoundation\\HeaderBag',
|
||||
@ -221,6 +226,55 @@ class FrameworkExtension extends Extension
|
||||
$loader->load('test.xml');
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the translator configuration.
|
||||
*
|
||||
* @param array $config A configuration array
|
||||
* @param ContainerBuilder $container A ContainerBuilder instance
|
||||
*/
|
||||
protected function registerTranslatorConfiguration($config, ContainerBuilder $container)
|
||||
{
|
||||
$config = $config['translator'];
|
||||
if (!is_array($config)) {
|
||||
$config = array();
|
||||
}
|
||||
|
||||
if (!$container->hasDefinition('translator')) {
|
||||
$loader = new XmlFileLoader($container, array(__DIR__.'/../Resources/config', __DIR__.'/Resources/config'));
|
||||
$loader->load('translation.xml');
|
||||
|
||||
// translation directories
|
||||
$dirs = array();
|
||||
foreach (array_reverse($container->getParameter('kernel.bundles')) as $bundle) {
|
||||
$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;
|
||||
}
|
||||
|
||||
// translation resources
|
||||
$resources = array();
|
||||
if ($dirs) {
|
||||
$finder = new Finder();
|
||||
$finder->files()->filter(function (\SplFileInfo $file) { return 2 === substr_count($file->getBasename(), '.'); })->in($dirs);
|
||||
foreach ($finder as $file) {
|
||||
// filename is domain.locale.format
|
||||
list($domain, $locale, $format) = explode('.', $file->getBasename());
|
||||
|
||||
$resources[] = array($format, (string) $file, $locale, $domain);
|
||||
}
|
||||
}
|
||||
$container->setParameter('translation.resources', $resources);
|
||||
}
|
||||
|
||||
if (array_key_exists('fallback', $config)) {
|
||||
$container->setParameter('translator.fallback_locale', $config['fallback']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the session configuration.
|
||||
*
|
||||
|
@ -14,6 +14,7 @@
|
||||
<xsd:element name="profiler" type="profiler" minOccurs="0" maxOccurs="1" />
|
||||
<xsd:element name="user" type="user" minOccurs="0" maxOccurs="1" />
|
||||
<xsd:element name="templating" type="templating" minOccurs="0" maxOccurs="1" />
|
||||
<xsd:element name="translator" type="translator" minOccurs="0" maxOccurs="1" />
|
||||
</xsd:sequence>
|
||||
|
||||
<xsd:attribute name="ide" type="xsd:string" />
|
||||
@ -75,4 +76,8 @@
|
||||
<xsd:attribute name="path" type="xsd:string" />
|
||||
<xsd:attribute name="cache" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="translator">
|
||||
<xsd:attribute name="fallback" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:schema>
|
||||
|
@ -18,6 +18,7 @@
|
||||
<parameter key="templating.helper.request.class">Symfony\Bundle\FrameworkBundle\Templating\Helper\RequestHelper</parameter>
|
||||
<parameter key="templating.helper.session.class">Symfony\Bundle\FrameworkBundle\Templating\Helper\SessionHelper</parameter>
|
||||
<parameter key="templating.helper.code.class">Symfony\Bundle\FrameworkBundle\Templating\Helper\CodeHelper</parameter>
|
||||
<parameter key="templating.helper.translator.class">Symfony\Bundle\FrameworkBundle\Templating\Helper\TranslatorHelper</parameter>
|
||||
<parameter key="templating.output_escaper">false</parameter>
|
||||
<parameter key="templating.assets.version">null</parameter>
|
||||
<parameter key="templating.assets.base_urls" type="collection"></parameter>
|
||||
@ -95,6 +96,11 @@
|
||||
<argument>%kernel.root_dir%</argument>
|
||||
</service>
|
||||
|
||||
<service id="templating.helper.translator" class="%templating.helper.translator.class%">
|
||||
<tag name="templating.helper" alias="translator" />
|
||||
<argument type="service" id="translator" />
|
||||
</service>
|
||||
|
||||
<service id="templating.loader" alias="templating.loader.filesystem" />
|
||||
|
||||
<service id="templating" alias="templating.engine" />
|
||||
|
@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" ?>
|
||||
|
||||
<container xmlns="http://www.symfony-project.org/schema/dic/services"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.symfony-project.org/schema/dic/services http://www.symfony-project.org/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<parameters>
|
||||
<parameter key="translator.class">Symfony\Bundle\FrameworkBundle\Translation\Translator</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="translator.fallback_locale">en</parameter>
|
||||
</parameters>
|
||||
|
||||
<services>
|
||||
<service id="translator" class="%translator.class%">
|
||||
<argument type="service" id="service_container" />
|
||||
<argument type="collection">
|
||||
<argument key="cache_dir">%kernel.cache_dir%/translations</argument>
|
||||
<argument key="debug">%kernel.debug%</argument>
|
||||
</argument>
|
||||
<argument type="service" id="session" on-invalid="ignore" />
|
||||
<call method="setFallbackLocale"><argument>%translator.fallback_locale%</argument></call>
|
||||
</service>
|
||||
|
||||
<service id="translation.loader.php" class="%translation.loader.php.class%">
|
||||
<tag name="translation.loader" alias="php" />
|
||||
</service>
|
||||
|
||||
<service id="translation.loader.xliff" class="%translation.loader.xliff.class%">
|
||||
<tag name="translation.loader" alias="xliff" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
@ -0,0 +1,115 @@
|
||||
<?xml version="1.0"?>
|
||||
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<file source-language="en" datatype="plaintext" original="file.ext">
|
||||
<body>
|
||||
<trans-unit id="1">
|
||||
<source>This value should be false</source>
|
||||
<target></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="2">
|
||||
<source>This value should be true</source>
|
||||
<target></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="3">
|
||||
<source>This value should be of type {{ type }}</source>
|
||||
<target></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="4">
|
||||
<source>This value should be blank</source>
|
||||
<target></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="5">
|
||||
<source>This value should be one of the given choices</source>
|
||||
<target></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="6">
|
||||
<source>You should select at least {{ limit }} choices</source>
|
||||
<target></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="7">
|
||||
<source>You should select at most {{ limit }} choices</source>
|
||||
<target></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="8">
|
||||
<source>The fields {{ fields }} were not expected</source>
|
||||
<target></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="9">
|
||||
<source>The fields {{ fields }} are missing</source>
|
||||
<target></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="10">
|
||||
<source>This value is not a valid date</source>
|
||||
<target></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="11">
|
||||
<source>This value is not a valid datetime</source>
|
||||
<target></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="12">
|
||||
<source>This value is not a valid email address</source>
|
||||
<target></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="13">
|
||||
<source>The file could not be found</source>
|
||||
<target></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="14">
|
||||
<source>The file is not readable</source>
|
||||
<target></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="15">
|
||||
<source>The file is too large ({{ size }}). Allowed maximum size is {{ limit }}</source>
|
||||
<target></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="16">
|
||||
<source>The mime type of the file is invalid ({{ type }}). Allowed mime types are {{ types }}</source>
|
||||
<target></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="17">
|
||||
<source>This value should be {{ limit }} or less</source>
|
||||
<target></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="18">
|
||||
<source>This value is too long. It should have {{ limit }} characters or less</source>
|
||||
<target></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="19">
|
||||
<source>This value should be {{ limit }} or more</source>
|
||||
<target></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="20">
|
||||
<source>This value is too short. It should have {{ limit }} characters or more</source>
|
||||
<target></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="21">
|
||||
<source>This value should not be blank</source>
|
||||
<target></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="22">
|
||||
<source>This value should not be null</source>
|
||||
<target></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="23">
|
||||
<source>This value should be null</source>
|
||||
<target></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="24">
|
||||
<source>This value is not valid</source>
|
||||
<target></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="25">
|
||||
<source>This value is not a valid time</source>
|
||||
<target></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="26">
|
||||
<source>This value is not a valid URL</source>
|
||||
<target></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="27">
|
||||
<source>This value should be instance of class {{ class }}</source>
|
||||
<target></target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Templating\Helper;
|
||||
|
||||
use Symfony\Component\Templating\Helper\Helper;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* TranslatorHelper.
|
||||
*
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class TranslatorHelper extends Helper
|
||||
{
|
||||
protected $translator;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param TranslatorInterface $translator A TranslatorInterface instance
|
||||
*/
|
||||
public function __construct(TranslatorInterface $translator)
|
||||
{
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see TranslatorInterface::trans()
|
||||
*/
|
||||
public function trans($id, array $parameters = array(), $domain = 'messages', $locale = null)
|
||||
{
|
||||
return $this->translator->trans($id, $parameters, $domain, $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see TranslatorInterface::transChoice()
|
||||
*/
|
||||
public function transChoice($id, $number, array $parameters = array(), $domain = 'messages', $locale = null)
|
||||
{
|
||||
return $this->translator->transChoice($id, $number, $parameters, $domain, $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the canonical name of this helper.
|
||||
*
|
||||
* @return string The canonical name
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'translator';
|
||||
}
|
||||
}
|
164
src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php
Normal file
164
src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php
Normal file
@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Translation;
|
||||
|
||||
use Symfony\Component\Translation\Translator as BaseTranslator;
|
||||
use Symfony\Component\Translation\Loader\LoaderInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Session;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Translator.
|
||||
*
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Translator extends BaseTranslator
|
||||
{
|
||||
protected $container;
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* Available options:
|
||||
*
|
||||
* * cache_dir: The cache directory (or null to disable caching)
|
||||
* * debug: Whether to enable debugging or not (false by default)
|
||||
*
|
||||
* @param ContainerInterface $container A ContainerInterface instance
|
||||
* @param array $options An array of options
|
||||
* @param Session $session A Session instance
|
||||
*/
|
||||
public function __construct(ContainerInterface $container, array $options = array(), Session $session = null)
|
||||
{
|
||||
if (null !== $session) {
|
||||
parent::__construct($session->getLocale());
|
||||
}
|
||||
|
||||
$this->container = $container;
|
||||
|
||||
$this->options = array(
|
||||
'cache_dir' => null,
|
||||
'debug' => false,
|
||||
);
|
||||
|
||||
// check option names
|
||||
if ($diff = array_diff(array_keys($options), array_keys($this->options))) {
|
||||
throw new \InvalidArgumentException(sprintf('The Router does not support the following options: \'%s\'.', implode('\', \'', $diff)));
|
||||
}
|
||||
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadCatalogue($locale)
|
||||
{
|
||||
if (isset($this->catalogues[$locale])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (null === $this->options['cache_dir']) {
|
||||
$this->initialize();
|
||||
|
||||
return parent::loadCatalogue($locale);
|
||||
}
|
||||
|
||||
if ($this->needsReload($locale)) {
|
||||
$this->initialize();
|
||||
|
||||
parent::loadCatalogue($locale);
|
||||
|
||||
$this->updateCache($locale);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->catalogues[$locale] = include $this->getCacheFile($locale);
|
||||
}
|
||||
|
||||
protected function initialize()
|
||||
{
|
||||
foreach ($this->container->findTaggedServiceIds('translation.loader') as $id => $attributes) {
|
||||
$this->addLoader($attributes[0]['alias'], $this->container->get($id));
|
||||
}
|
||||
|
||||
foreach ($this->container->getParameter('translation.resources') as $resource) {
|
||||
$this->addResource($resource[0], $resource[1], $resource[2], $resource[3]);
|
||||
}
|
||||
}
|
||||
|
||||
protected function updateCache($locale)
|
||||
{
|
||||
$this->writeCacheFile($this->getCacheFile($locale), sprintf(
|
||||
"<?php use Symfony\Component\Translation\MessageCatalogue; return new MessageCatalogue('%s', %s);",
|
||||
$locale,
|
||||
var_export($this->catalogues[$locale]->getMessages(), true)
|
||||
));
|
||||
|
||||
if ($this->options['debug']) {
|
||||
$this->writeCacheFile($this->getCacheFile($locale, 'meta'), serialize($this->catalogues[$locale]->getResources()));
|
||||
}
|
||||
}
|
||||
|
||||
protected function needsReload($locale)
|
||||
{
|
||||
$file = $this->getCacheFile($locale);
|
||||
if (!file_exists($file)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!$this->options['debug']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$metadata = $this->getCacheFile($locale, 'meta');
|
||||
if (!file_exists($metadata)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$time = filemtime($file);
|
||||
$meta = unserialize(file_get_contents($metadata));
|
||||
foreach ($meta as $resource) {
|
||||
if (!$resource->isUptodate($time)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function getCacheFile($locale, $extension = 'php')
|
||||
{
|
||||
return $this->options['cache_dir'].'/catalogue.'.$locale.'.'.$extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \RuntimeException When cache file can't be wrote
|
||||
*/
|
||||
protected function writeCacheFile($file, $content)
|
||||
{
|
||||
if (!is_dir(dirname($file))) {
|
||||
@mkdir(dirname($file), 0777, true);
|
||||
}
|
||||
|
||||
$tmpFile = tempnam(dirname($file), basename($file));
|
||||
if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $file)) {
|
||||
chmod($file, 0644);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw new \RuntimeException(sprintf('Failed to write cache file "%s".', $file));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user