[Translation] add options 'as_tree', 'inline' to YamlFileDumper to dump messages as tree instead of simple list.

Dump messages as tree based on '.' character as a delimeter in path.
For example this rray('foo.bar' => 'value') will be converted to array('foo' => array('bar' => 'value')).

Correctly process cases like this ['foo.bar' => 'test1', 'foo' => 'test2'].
This commit is contained in:
Gennady Telegin 2015-04-22 00:28:17 +03:00
parent 791e583b40
commit 29ec5caf8a
8 changed files with 241 additions and 6 deletions

View File

@ -5,6 +5,7 @@ CHANGELOG
-----
* deprecated Translator::getMessages(), use TranslatorBagInterface::getCatalogue() instead.
* added options 'as_tree', 'inline' to YamlFileDumper
2.7.0
-----

View File

@ -82,10 +82,26 @@ abstract class FileDumper implements DumperInterface
}
}
// save file
file_put_contents($fullpath, $this->format($messages, $domain));
file_put_contents($fullpath, $this->formatCatalogue($messages, $domain, $options));
}
}
/**
* Transforms a domain of a message catalogue to its string representation.
*
* Override this function in child class if $options is used for message formatting.
*
* @param MessageCatalogue $messages
* @param string $domain
* @param array $options
*
* @return string representation
*/
protected function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
{
return $this->format($messages, $domain);
}
/**
* Transforms a domain of a message catalogue to its string representation.
*

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\Translation\Dumper;
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\Util\ArrayConverter;
use Symfony\Component\Yaml\Yaml;
/**
@ -24,13 +25,31 @@ class YamlFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
protected function format(MessageCatalogue $messages, $domain)
protected function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
{
if (!class_exists('Symfony\Component\Yaml\Yaml')) {
throw new \LogicException('Dumping translations in the YAML format requires the Symfony Yaml component.');
}
return Yaml::dump($messages->all($domain));
$data = $messages->all($domain);
if (isset($options['as_tree']) && $options['as_tree']) {
$data = ArrayConverter::expandToTree($data);
}
if (isset($options['inline']) && ($inline = (int) $options['inline']) > 0) {
return Yaml::dump($data, $inline);
}
return Yaml::dump($data);
}
/**
* {@inheritdoc}
*/
protected function format(MessageCatalogue $messages, $domain)
{
return $this->formatCatalogue($messages, $domain);
}
/**

View File

@ -16,16 +16,38 @@ use Symfony\Component\Translation\Dumper\YamlFileDumper;
class YamlFileDumperTest extends \PHPUnit_Framework_TestCase
{
public function testDump()
public function testTreeDump()
{
$catalogue = new MessageCatalogue('en');
$catalogue->add(array('foo' => 'bar'));
$catalogue->add(
array(
'foo.bar1' => 'value1',
'foo.bar2' => 'value2',
));
$tempDir = sys_get_temp_dir();
$dumper = new YamlFileDumper();
$dumper->dump($catalogue, array('path' => $tempDir, 'as_tree' => true, 'inline' => 999));
$this->assertEquals(file_get_contents(__DIR__.'/../fixtures/messages.yml'), file_get_contents($tempDir.'/messages.en.yml'));
unlink($tempDir.'/messages.en.yml');
}
public function testLinearDump()
{
$catalogue = new MessageCatalogue('en');
$catalogue->add(
array(
'foo.bar1' => 'value1',
'foo.bar2' => 'value2',
));
$tempDir = sys_get_temp_dir();
$dumper = new YamlFileDumper();
$dumper->dump($catalogue, array('path' => $tempDir));
$this->assertEquals(file_get_contents(__DIR__.'/../fixtures/resources.yml'), file_get_contents($tempDir.'/messages.en.yml'));
$this->assertEquals(file_get_contents(__DIR__.'/../fixtures/messages_linear.yml'), file_get_contents($tempDir.'/messages.en.yml'));
unlink($tempDir.'/messages.en.yml');
}

View File

@ -0,0 +1,73 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Translation\Tests\Util;
use Symfony\Component\Translation\Util\ArrayConverter;
class ArrayConverterTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider messsagesData
*/
public function testDump($input, $expectedOutput)
{
$this->assertEquals($expectedOutput, ArrayConverter::expandToTree($input));
}
public function messsagesData()
{
return array(
array(
// input
array(
'foo1' => 'bar',
'foo.bar' => 'value',
),
// expected output
array(
'foo1' => 'bar',
'foo' => array('bar' => 'value'),
),
),
array(
// input
array(
'foo.bar' => 'value1',
'foo.bar.test' => 'value2',
),
// expected output
array(
'foo' => array(
'bar' => 'value1',
'bar.test' => 'value2',
),
),
),
array(
// input
array(
'foo.level2.level3.level4' => 'value1',
'foo.level2' => 'value2',
'foo.bar' => 'value3',
),
// expected output
array(
'foo' => array(
'level2' => 'value2',
'level2.level3.level4' => 'value1',
'bar' => 'value3',
),
),
),
);
}
}

View File

@ -0,0 +1,3 @@
foo:
bar1: value1
bar2: value2

View File

@ -0,0 +1,2 @@
foo.bar1: value1
foo.bar2: value2

View File

@ -0,0 +1,99 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Translation\Util;
/**
* ArrayConverter generates tree like structure from a message catalogue.
* e.g. this
* 'foo.bar1' => 'test1',
* 'foo.bar2' => 'test2'
* converts to follows:
* foo:
* bar1: test1
* bar2: test2.
*
* @author Gennady Telegin <gtelegin@gmail.com>
*/
class ArrayConverter
{
/**
* Converts linear messages array to tree-like array.
* For example this rray('foo.bar' => 'value') will be converted to array('foo' => array('bar' => 'value')).
*
* @param array $messages Linear messages array
*
* @return array Tree-like messages array
*/
public static function expandToTree(array $messages)
{
$tree = array();
foreach ($messages as $id => $value) {
$referenceToElement = &self::getElementByPath($tree, explode('.', $id));
$referenceToElement = $value;
unset($referenceToElement);
}
return $tree;
}
private static function &getElementByPath(array &$tree, array $parts)
{
$elem = &$tree;
$parentOfElem = null;
foreach ($parts as $i => $part) {
if (isset($elem[$part]) && is_string($elem[$part])) {
/* Process next case:
* 'foo': 'test1',
* 'foo.bar': 'test2'
*
* $tree['foo'] was string before we found array {bar: test2}.
* Treat new element as string too, e.g. add $tree['foo.bar'] = 'test2';
*/
$elem = &$elem[ implode('.', array_slice($parts, $i)) ];
break;
}
$parentOfElem = &$elem;
$elem = &$elem[$part];
}
if (is_array($elem) && count($elem) > 0 && $parentOfElem) {
/* Process next case:
* 'foo.bar': 'test1'
* 'foo': 'test2'
*
* $tree['foo'] was array = {bar: 'test1'} before we found string constant `foo`.
* Cancel treating $tree['foo'] as array and cancel back it expansion,
* e.g. make it $tree['foo.bar'] = 'test1' again.
*/
self::cancelExpand($parentOfElem, $part, $elem);
}
return $elem;
}
private static function cancelExpand(array &$tree, $prefix, array $node)
{
$prefix .= '.';
foreach ($node as $id => $value) {
if (is_string($value)) {
$tree[$prefix.$id] = $value;
} else {
self::cancelExpand($tree, $prefix.$id, $value);
}
}
}
}