diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml index f534b358be..95284306dd 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml @@ -13,6 +13,7 @@ Symfony\Component\Translation\Loader\XliffFileLoader Symfony\Component\Translation\Loader\QtTranslationsLoader Symfony\Component\Translation\Loader\CsvFileLoader + Symfony\Component\Translation\Loader\ResourceBundleLoader Symfony\Component\Translation\Dumper\PhpFileDumper Symfony\Component\Translation\Dumper\XliffFileDumper Symfony\Component\Translation\Dumper\YamlFileDumper diff --git a/src/Symfony/Component/Translation/Loader/ResourceBundleLoader.php b/src/Symfony/Component/Translation/Loader/ResourceBundleLoader.php new file mode 100644 index 0000000000..b68aeb52cb --- /dev/null +++ b/src/Symfony/Component/Translation/Loader/ResourceBundleLoader.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Loader; + +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Config\Resource\DirectoryResource; +use Symfony\Component\Config\Resource\FileResource; + +/** + * ResourceBundleLoader loads translations from a resource bundle. + * + * @author stealth35 + */ +class ResourceBundleLoader implements LoaderInterface +{ + /** + * {@inheritdoc} + */ + public function load($resource, $locale, $domain = 'messages') + { + $rb = new \ResourceBundle($locale, $resource); + + if (!$rb) { + throw new \RuntimeException("cannot load this resource : $rb"); + } elseif (intl_is_failure($rb->getErrorCode())) { + throw new \RuntimeException($rb->getErrorMessage(), $rb->getErrorCode()); + } + + $messages = $this->flatten($rb); + $catalogue = new MessageCatalogue($locale); + $catalogue->add($messages, $domain); + + if (is_dir($resource)) { + $catalogue->addResource(new DirectoryResource($resource)); + } elseif (is_file($resource.'.dat')) { + $catalogue->addResource(new FileResource($resource.'.dat')); + } + + return $catalogue; + } + + /** + * Flattens an ResourceBundle + * + * The scheme used is: + * key { key2 { key3 { "value" } } } + * Becomes: + * 'key.key2.key3' => 'value' + * + * This function takes an array by reference and will modify it + * + * @param array \ResourceBundle $rb the ResourceBundle that will be flattened + * @param array $messages used internally for recursive calls + * @param string $path current path being parsed, used internally for recursive calls + * + * @return array the flattened ResourceBundle + */ + private function flatten(\ResourceBundle $rb, array &$messages = array(), $path = null) + { + foreach ($rb as $key => $value) { + $nodePath = $path ? $path.'.'.$key : $key; + if ($value instanceof \ResourceBundle) { + $this->flatten($value, $messages, $nodePath); + } else { + $messages[$nodePath] = $value; + } + } + + return $messages; + } +} diff --git a/tests/Symfony/Tests/Component/Translation/Loader/LocalizedTestCase.php b/tests/Symfony/Tests/Component/Translation/Loader/LocalizedTestCase.php new file mode 100644 index 0000000000..3f08272d17 --- /dev/null +++ b/tests/Symfony/Tests/Component/Translation/Loader/LocalizedTestCase.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Tests\Component\Translation\Loader; + +abstract class LocalizedTestCase extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!extension_loaded('intl')) { + $this->markTestSkipped('The "intl" extension is not available'); + } + } +} \ No newline at end of file diff --git a/tests/Symfony/Tests/Component/Translation/Loader/ResourceBundleLoaderTest.php b/tests/Symfony/Tests/Component/Translation/Loader/ResourceBundleLoaderTest.php new file mode 100644 index 0000000000..0d62b8a9e4 --- /dev/null +++ b/tests/Symfony/Tests/Component/Translation/Loader/ResourceBundleLoaderTest.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Tests\Component\Translation\Loader; + +use Symfony\Component\Translation\Loader\ResourceBundleLoader; +use Symfony\Component\Config\Resource\DirectoryResource; +use Symfony\Component\Config\Resource\FileResource; + +class ResourceBundleFileLoaderTest extends LocalizedTestCase +{ + public function testLoad() + { + $loader = new ResourceBundleLoader(); + $resource = __DIR__.'/../fixtures/resourcebundle/res'; + $catalogue = $loader->load($resource, 'en', 'domain1'); + + $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1')); + $this->assertEquals('en', $catalogue->getLocale()); + $this->assertEquals(array(new DirectoryResource($resource)), $catalogue->getResources()); + } + + public function testDatEnglishLoad() + { + $loader = new ResourceBundleLoader(); + $resource = __DIR__.'/../fixtures/resourcebundle/dat/resources'; + $catalogue = $loader->load($resource, 'en', 'domain1'); + + $this->assertEquals(array('symfony' => 'Symfony 2 is great'), $catalogue->all('domain1')); + $this->assertEquals('en', $catalogue->getLocale()); + $this->assertEquals(array(new FileResource($resource.'.dat')), $catalogue->getResources()); + } + + public function testDatFrenchLoad() + { + $loader = new ResourceBundleLoader(); + $resource = __DIR__.'/../fixtures/resourcebundle/dat/resources'; + $catalogue = $loader->load($resource, 'fr', 'domain1'); + + $this->assertEquals(array('symfony' => 'Symfony 2 est génial'), $catalogue->all('domain1')); + $this->assertEquals('fr', $catalogue->getLocale()); + $this->assertEquals(array(new FileResource($resource.'.dat')), $catalogue->getResources()); + } + + /** + * @expectedException Exception + */ + public function testLoadInvalidResource() + { + $loader = new ResourceBundleLoader(); + $catalogue = $loader->load(__DIR__.'/../fixtures/resourcebundle/res/en.txt', 'en', 'domain1'); + } +} diff --git a/tests/Symfony/Tests/Component/Translation/fixtures/resourcebundle/dat/en.txt b/tests/Symfony/Tests/Component/Translation/fixtures/resourcebundle/dat/en.txt new file mode 100644 index 0000000000..c04a4e8522 --- /dev/null +++ b/tests/Symfony/Tests/Component/Translation/fixtures/resourcebundle/dat/en.txt @@ -0,0 +1,3 @@ +en{ + symfony{"Symfony 2 is great"} +} \ No newline at end of file diff --git a/tests/Symfony/Tests/Component/Translation/fixtures/resourcebundle/dat/fr.txt b/tests/Symfony/Tests/Component/Translation/fixtures/resourcebundle/dat/fr.txt new file mode 100644 index 0000000000..7e84f67ae0 --- /dev/null +++ b/tests/Symfony/Tests/Component/Translation/fixtures/resourcebundle/dat/fr.txt @@ -0,0 +1,3 @@ +fr{ + symfony{"Symfony 2 est génial"} +} \ No newline at end of file diff --git a/tests/Symfony/Tests/Component/Translation/fixtures/resourcebundle/dat/resources.dat b/tests/Symfony/Tests/Component/Translation/fixtures/resourcebundle/dat/resources.dat new file mode 100644 index 0000000000..b8b5b2bc5e Binary files /dev/null and b/tests/Symfony/Tests/Component/Translation/fixtures/resourcebundle/dat/resources.dat differ diff --git a/tests/Symfony/Tests/Component/Translation/fixtures/resourcebundle/res/en.res b/tests/Symfony/Tests/Component/Translation/fixtures/resourcebundle/res/en.res new file mode 100644 index 0000000000..ed962ca285 Binary files /dev/null and b/tests/Symfony/Tests/Component/Translation/fixtures/resourcebundle/res/en.res differ diff --git a/tests/Symfony/Tests/Component/Translation/fixtures/resourcebundle/res/en.txt b/tests/Symfony/Tests/Component/Translation/fixtures/resourcebundle/res/en.txt new file mode 100644 index 0000000000..a8a87e5f44 --- /dev/null +++ b/tests/Symfony/Tests/Component/Translation/fixtures/resourcebundle/res/en.txt @@ -0,0 +1,3 @@ +en { + foo { "bar" } +} \ No newline at end of file