This commit is contained in:
Xes
2025-08-14 22:41:49 +02:00
parent 2de81ccc46
commit 8ce45119b6
39774 changed files with 4309466 additions and 0 deletions

View File

@@ -0,0 +1,74 @@
<?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\Catalogue;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\MessageCatalogueInterface;
abstract class AbstractOperationTest extends TestCase
{
public function testGetEmptyDomains()
{
$this->assertEquals(
[],
$this->createOperation(
new MessageCatalogue('en'),
new MessageCatalogue('en')
)->getDomains()
);
}
public function testGetMergedDomains()
{
$this->assertEquals(
['a', 'b', 'c'],
$this->createOperation(
new MessageCatalogue('en', ['a' => [], 'b' => []]),
new MessageCatalogue('en', ['b' => [], 'c' => []])
)->getDomains()
);
}
public function testGetMessagesFromUnknownDomain()
{
$this->expectException('InvalidArgumentException');
$this->createOperation(
new MessageCatalogue('en'),
new MessageCatalogue('en')
)->getMessages('domain');
}
public function testGetEmptyMessages()
{
$this->assertEquals(
[],
$this->createOperation(
new MessageCatalogue('en', ['a' => []]),
new MessageCatalogue('en')
)->getMessages('a')
);
}
public function testGetEmptyResult()
{
$this->assertEquals(
new MessageCatalogue('en'),
$this->createOperation(
new MessageCatalogue('en'),
new MessageCatalogue('en')
)->getResult()
);
}
abstract protected function createOperation(MessageCatalogueInterface $source, MessageCatalogueInterface $target);
}

View File

@@ -0,0 +1,83 @@
<?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\Catalogue;
use Symfony\Component\Translation\Catalogue\MergeOperation;
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\MessageCatalogueInterface;
class MergeOperationTest extends AbstractOperationTest
{
public function testGetMessagesFromSingleDomain()
{
$operation = $this->createOperation(
new MessageCatalogue('en', ['messages' => ['a' => 'old_a', 'b' => 'old_b']]),
new MessageCatalogue('en', ['messages' => ['a' => 'new_a', 'c' => 'new_c']])
);
$this->assertEquals(
['a' => 'old_a', 'b' => 'old_b', 'c' => 'new_c'],
$operation->getMessages('messages')
);
$this->assertEquals(
['c' => 'new_c'],
$operation->getNewMessages('messages')
);
$this->assertEquals(
[],
$operation->getObsoleteMessages('messages')
);
}
public function testGetResultFromSingleDomain()
{
$this->assertEquals(
new MessageCatalogue('en', [
'messages' => ['a' => 'old_a', 'b' => 'old_b', 'c' => 'new_c'],
]),
$this->createOperation(
new MessageCatalogue('en', ['messages' => ['a' => 'old_a', 'b' => 'old_b']]),
new MessageCatalogue('en', ['messages' => ['a' => 'new_a', 'c' => 'new_c']])
)->getResult()
);
}
public function testGetResultWithMetadata()
{
$leftCatalogue = new MessageCatalogue('en', ['messages' => ['a' => 'old_a', 'b' => 'old_b']]);
$leftCatalogue->setMetadata('a', 'foo', 'messages');
$leftCatalogue->setMetadata('b', 'bar', 'messages');
$rightCatalogue = new MessageCatalogue('en', ['messages' => ['b' => 'new_b', 'c' => 'new_c']]);
$rightCatalogue->setMetadata('b', 'baz', 'messages');
$rightCatalogue->setMetadata('c', 'qux', 'messages');
$mergedCatalogue = new MessageCatalogue('en', ['messages' => ['a' => 'old_a', 'b' => 'old_b', 'c' => 'new_c']]);
$mergedCatalogue->setMetadata('a', 'foo', 'messages');
$mergedCatalogue->setMetadata('b', 'bar', 'messages');
$mergedCatalogue->setMetadata('c', 'qux', 'messages');
$this->assertEquals(
$mergedCatalogue,
$this->createOperation(
$leftCatalogue,
$rightCatalogue
)->getResult()
);
}
protected function createOperation(MessageCatalogueInterface $source, MessageCatalogueInterface $target)
{
return new MergeOperation($source, $target);
}
}

View File

@@ -0,0 +1,82 @@
<?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\Catalogue;
use Symfony\Component\Translation\Catalogue\TargetOperation;
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\MessageCatalogueInterface;
class TargetOperationTest extends AbstractOperationTest
{
public function testGetMessagesFromSingleDomain()
{
$operation = $this->createOperation(
new MessageCatalogue('en', ['messages' => ['a' => 'old_a', 'b' => 'old_b']]),
new MessageCatalogue('en', ['messages' => ['a' => 'new_a', 'c' => 'new_c']])
);
$this->assertEquals(
['a' => 'old_a', 'c' => 'new_c'],
$operation->getMessages('messages')
);
$this->assertEquals(
['c' => 'new_c'],
$operation->getNewMessages('messages')
);
$this->assertEquals(
['b' => 'old_b'],
$operation->getObsoleteMessages('messages')
);
}
public function testGetResultFromSingleDomain()
{
$this->assertEquals(
new MessageCatalogue('en', [
'messages' => ['a' => 'old_a', 'c' => 'new_c'],
]),
$this->createOperation(
new MessageCatalogue('en', ['messages' => ['a' => 'old_a', 'b' => 'old_b']]),
new MessageCatalogue('en', ['messages' => ['a' => 'new_a', 'c' => 'new_c']])
)->getResult()
);
}
public function testGetResultWithMetadata()
{
$leftCatalogue = new MessageCatalogue('en', ['messages' => ['a' => 'old_a', 'b' => 'old_b']]);
$leftCatalogue->setMetadata('a', 'foo', 'messages');
$leftCatalogue->setMetadata('b', 'bar', 'messages');
$rightCatalogue = new MessageCatalogue('en', ['messages' => ['b' => 'new_b', 'c' => 'new_c']]);
$rightCatalogue->setMetadata('b', 'baz', 'messages');
$rightCatalogue->setMetadata('c', 'qux', 'messages');
$diffCatalogue = new MessageCatalogue('en', ['messages' => ['b' => 'old_b', 'c' => 'new_c']]);
$diffCatalogue->setMetadata('b', 'bar', 'messages');
$diffCatalogue->setMetadata('c', 'qux', 'messages');
$this->assertEquals(
$diffCatalogue,
$this->createOperation(
$leftCatalogue,
$rightCatalogue
)->getResult()
);
}
protected function createOperation(MessageCatalogueInterface $source, MessageCatalogueInterface $target)
{
return new TargetOperation($source, $target);
}
}

View File

@@ -0,0 +1,150 @@
<?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\DataCollector;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\DataCollector\TranslationDataCollector;
use Symfony\Component\Translation\DataCollectorTranslator;
class TranslationDataCollectorTest extends TestCase
{
protected function setUp()
{
if (!class_exists('Symfony\Component\HttpKernel\DataCollector\DataCollector')) {
$this->markTestSkipped('The "DataCollector" is not available');
}
}
public function testCollectEmptyMessages()
{
$translator = $this->getTranslator();
$translator->expects($this->any())->method('getCollectedMessages')->willReturn([]);
$dataCollector = new TranslationDataCollector($translator);
$dataCollector->lateCollect();
$this->assertEquals(0, $dataCollector->getCountMissings());
$this->assertEquals(0, $dataCollector->getCountFallbacks());
$this->assertEquals(0, $dataCollector->getCountDefines());
$this->assertEquals([], $dataCollector->getMessages()->getValue());
}
public function testCollect()
{
$collectedMessages = [
[
'id' => 'foo',
'translation' => 'foo (en)',
'locale' => 'en',
'domain' => 'messages',
'state' => DataCollectorTranslator::MESSAGE_DEFINED,
'parameters' => [],
'transChoiceNumber' => null,
],
[
'id' => 'bar',
'translation' => 'bar (fr)',
'locale' => 'fr',
'domain' => 'messages',
'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK,
'parameters' => [],
'transChoiceNumber' => null,
],
[
'id' => 'choice',
'translation' => 'choice',
'locale' => 'en',
'domain' => 'messages',
'state' => DataCollectorTranslator::MESSAGE_MISSING,
'parameters' => ['%count%' => 3],
'transChoiceNumber' => 3,
],
[
'id' => 'choice',
'translation' => 'choice',
'locale' => 'en',
'domain' => 'messages',
'state' => DataCollectorTranslator::MESSAGE_MISSING,
'parameters' => ['%count%' => 3],
'transChoiceNumber' => 3,
],
[
'id' => 'choice',
'translation' => 'choice',
'locale' => 'en',
'domain' => 'messages',
'state' => DataCollectorTranslator::MESSAGE_MISSING,
'parameters' => ['%count%' => 4, '%foo%' => 'bar'],
'transChoiceNumber' => 4,
],
];
$expectedMessages = [
[
'id' => 'foo',
'translation' => 'foo (en)',
'locale' => 'en',
'domain' => 'messages',
'state' => DataCollectorTranslator::MESSAGE_DEFINED,
'count' => 1,
'parameters' => [],
'transChoiceNumber' => null,
],
[
'id' => 'bar',
'translation' => 'bar (fr)',
'locale' => 'fr',
'domain' => 'messages',
'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK,
'count' => 1,
'parameters' => [],
'transChoiceNumber' => null,
],
[
'id' => 'choice',
'translation' => 'choice',
'locale' => 'en',
'domain' => 'messages',
'state' => DataCollectorTranslator::MESSAGE_MISSING,
'count' => 3,
'parameters' => [
['%count%' => 3],
['%count%' => 3],
['%count%' => 4, '%foo%' => 'bar'],
],
'transChoiceNumber' => 3,
],
];
$translator = $this->getTranslator();
$translator->expects($this->any())->method('getCollectedMessages')->willReturn($collectedMessages);
$dataCollector = new TranslationDataCollector($translator);
$dataCollector->lateCollect();
$this->assertEquals(1, $dataCollector->getCountMissings());
$this->assertEquals(1, $dataCollector->getCountFallbacks());
$this->assertEquals(1, $dataCollector->getCountDefines());
$this->assertEquals($expectedMessages, array_values($dataCollector->getMessages()->getValue(true)));
}
private function getTranslator()
{
$translator = $this
->getMockBuilder('Symfony\Component\Translation\DataCollectorTranslator')
->disableOriginalConstructor()
->getMock()
;
return $translator;
}
}

View File

@@ -0,0 +1,97 @@
<?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;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\DataCollectorTranslator;
use Symfony\Component\Translation\Loader\ArrayLoader;
use Symfony\Component\Translation\Translator;
class DataCollectorTranslatorTest extends TestCase
{
public function testCollectMessages()
{
$collector = $this->createCollector();
$collector->setFallbackLocales(['fr', 'ru']);
$collector->trans('foo');
$collector->trans('bar');
$collector->transChoice('choice', 0);
$collector->trans('bar_ru');
$collector->trans('bar_ru', ['foo' => 'bar']);
$expectedMessages = [];
$expectedMessages[] = [
'id' => 'foo',
'translation' => 'foo (en)',
'locale' => 'en',
'fallbackLocale' => null,
'domain' => 'messages',
'state' => DataCollectorTranslator::MESSAGE_DEFINED,
'parameters' => [],
'transChoiceNumber' => null,
];
$expectedMessages[] = [
'id' => 'bar',
'translation' => 'bar (fr)',
'locale' => 'en',
'fallbackLocale' => 'fr',
'domain' => 'messages',
'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK,
'parameters' => [],
'transChoiceNumber' => null,
];
$expectedMessages[] = [
'id' => 'choice',
'translation' => 'choice',
'locale' => 'en',
'fallbackLocale' => null,
'domain' => 'messages',
'state' => DataCollectorTranslator::MESSAGE_MISSING,
'parameters' => [],
'transChoiceNumber' => 0,
];
$expectedMessages[] = [
'id' => 'bar_ru',
'translation' => 'bar (ru)',
'locale' => 'en',
'fallbackLocale' => 'ru',
'domain' => 'messages',
'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK,
'parameters' => [],
'transChoiceNumber' => null,
];
$expectedMessages[] = [
'id' => 'bar_ru',
'translation' => 'bar (ru)',
'locale' => 'en',
'fallbackLocale' => 'ru',
'domain' => 'messages',
'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK,
'parameters' => ['foo' => 'bar'],
'transChoiceNumber' => null,
];
$this->assertEquals($expectedMessages, $collector->getCollectedMessages());
}
private function createCollector()
{
$translator = new Translator('en');
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', ['foo' => 'foo (en)'], 'en');
$translator->addResource('array', ['bar' => 'bar (fr)'], 'fr');
$translator->addResource('array', ['bar_ru' => 'bar (ru)'], 'ru');
return new DataCollectorTranslator($translator);
}
}

View File

@@ -0,0 +1,48 @@
<?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\DependencyInjection;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\Translation\DependencyInjection\TranslationDumperPass;
class TranslationDumperPassTest extends TestCase
{
public function testProcess()
{
$container = new ContainerBuilder();
$writerDefinition = $container->register('translation.writer');
$container->register('foo.id')
->addTag('translation.dumper', ['alias' => 'bar.alias']);
$translationDumperPass = new TranslationDumperPass();
$translationDumperPass->process($container);
$this->assertEquals([['addDumper', ['bar.alias', new Reference('foo.id')]]], $writerDefinition->getMethodCalls());
}
public function testProcessNoDefinitionFound()
{
$container = new ContainerBuilder();
$definitionsBefore = \count($container->getDefinitions());
$aliasesBefore = \count($container->getAliases());
$translationDumperPass = new TranslationDumperPass();
$translationDumperPass->process($container);
// the container is untouched (i.e. no new definitions or aliases)
$this->assertCount($definitionsBefore, $container->getDefinitions());
$this->assertCount($aliasesBefore, $container->getAliases());
}
}

View File

@@ -0,0 +1,61 @@
<?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\DependencyInjection;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\Translation\DependencyInjection\TranslationExtractorPass;
class TranslationExtractorPassTest extends TestCase
{
public function testProcess()
{
$container = new ContainerBuilder();
$extractorDefinition = $container->register('translation.extractor');
$container->register('foo.id')
->addTag('translation.extractor', ['alias' => 'bar.alias']);
$translationDumperPass = new TranslationExtractorPass();
$translationDumperPass->process($container);
$this->assertEquals([['addExtractor', ['bar.alias', new Reference('foo.id')]]], $extractorDefinition->getMethodCalls());
}
public function testProcessNoDefinitionFound()
{
$container = new ContainerBuilder();
$definitionsBefore = \count($container->getDefinitions());
$aliasesBefore = \count($container->getAliases());
$translationDumperPass = new TranslationExtractorPass();
$translationDumperPass->process($container);
// the container is untouched (i.e. no new definitions or aliases)
$this->assertCount($definitionsBefore, $container->getDefinitions());
$this->assertCount($aliasesBefore, $container->getAliases());
}
public function testProcessMissingAlias()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\RuntimeException');
$this->expectExceptionMessage('The alias for the tag "translation.extractor" of service "foo.id" must be set.');
$container = new ContainerBuilder();
$container->register('translation.extractor');
$container->register('foo.id')
->addTag('translation.extractor', []);
$translationDumperPass = new TranslationExtractorPass();
$translationDumperPass->process($container);
}
}

View File

@@ -0,0 +1,103 @@
<?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\DependencyInjection;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\Translation\DependencyInjection\TranslatorPass;
class TranslationPassTest extends TestCase
{
public function testValidCollector()
{
$loader = (new Definition())
->addTag('translation.loader', ['alias' => 'xliff', 'legacy-alias' => 'xlf']);
$reader = new Definition();
$translator = (new Definition())
->setArguments([null, null, null, null]);
$container = new ContainerBuilder();
$container->setDefinition('translator.default', $translator);
$container->setDefinition('translation.reader', $reader);
$container->setDefinition('translation.xliff_loader', $loader);
$pass = new TranslatorPass('translator.default', 'translation.reader');
$pass->process($container);
$expectedReader = (new Definition())
->addMethodCall('addLoader', ['xliff', new Reference('translation.xliff_loader')])
->addMethodCall('addLoader', ['xlf', new Reference('translation.xliff_loader')])
;
$this->assertEquals($expectedReader, $reader);
$expectedLoader = (new Definition())
->addTag('translation.loader', ['alias' => 'xliff', 'legacy-alias' => 'xlf'])
;
$this->assertEquals($expectedLoader, $loader);
$this->assertSame(['translation.xliff_loader' => ['xliff', 'xlf']], $translator->getArgument(3));
$expected = ['translation.xliff_loader' => new ServiceClosureArgument(new Reference('translation.xliff_loader'))];
$this->assertEquals($expected, $container->getDefinition((string) $translator->getArgument(0))->getArgument(0));
}
/**
* @group legacy
* @expectedDeprecation The default value for $readerServiceId in "Symfony\Component\Translation\DependencyInjection\TranslatorPass::__construct()" will change in 4.0 to "translation.reader".
*
* A test that verifies the deprecated "translation.loader" gets the LoaderInterfaces added.
*
* This test should be removed in 4.0.
*/
public function testValidCollectorWithDeprecatedTranslationLoader()
{
$loader = (new Definition())
->addTag('translation.loader', ['alias' => 'xliff', 'legacy-alias' => 'xlf']);
$legacyReader = new Definition();
$reader = new Definition();
$translator = (new Definition())
->setArguments([null, null, null, null]);
$container = new ContainerBuilder();
$container->setDefinition('translator.default', $translator);
$container->setDefinition('translation.loader', $legacyReader);
$container->setDefinition('translation.reader', $reader);
$container->setDefinition('translation.xliff_loader', $loader);
$pass = new TranslatorPass();
$pass->process($container);
$expectedReader = (new Definition())
->addMethodCall('addLoader', ['xliff', new Reference('translation.xliff_loader')])
->addMethodCall('addLoader', ['xlf', new Reference('translation.xliff_loader')])
;
$this->assertEquals($expectedReader, $legacyReader);
$this->assertEquals($expectedReader, $reader);
$expectedLoader = (new Definition())
->addTag('translation.loader', ['alias' => 'xliff', 'legacy-alias' => 'xlf'])
;
$this->assertEquals($expectedLoader, $loader);
$this->assertSame(['translation.xliff_loader' => ['xliff', 'xlf']], $translator->getArgument(3));
$expected = ['translation.xliff_loader' => new ServiceClosureArgument(new Reference('translation.xliff_loader'))];
$this->assertEquals($expected, $container->getDefinition((string) $translator->getArgument(0))->getArgument(0));
}
}

View File

@@ -0,0 +1,30 @@
<?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\Dumper;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Dumper\CsvFileDumper;
use Symfony\Component\Translation\MessageCatalogue;
class CsvFileDumperTest extends TestCase
{
public function testFormatCatalogue()
{
$catalogue = new MessageCatalogue('en');
$catalogue->add(['foo' => 'bar', 'bar' => 'foo
foo', 'foo;foo' => 'bar']);
$dumper = new CsvFileDumper();
$this->assertStringEqualsFile(__DIR__.'/../fixtures/valid.csv', $dumper->formatCatalogue($catalogue, 'messages'));
}
}

View File

@@ -0,0 +1,89 @@
<?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\Dumper;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Dumper\FileDumper;
use Symfony\Component\Translation\MessageCatalogue;
class FileDumperTest extends TestCase
{
public function testDump()
{
$tempDir = sys_get_temp_dir();
$catalogue = new MessageCatalogue('en');
$catalogue->add(['foo' => 'bar']);
$dumper = new ConcreteFileDumper();
$dumper->dump($catalogue, ['path' => $tempDir]);
$this->assertFileExists($tempDir.'/messages.en.concrete');
@unlink($tempDir.'/messages.en.concrete');
}
/**
* @group legacy
*/
public function testDumpBackupsFileIfExisting()
{
$tempDir = sys_get_temp_dir();
$file = $tempDir.'/messages.en.concrete';
$backupFile = $file.'~';
@touch($file);
$catalogue = new MessageCatalogue('en');
$catalogue->add(['foo' => 'bar']);
$dumper = new ConcreteFileDumper();
$dumper->dump($catalogue, ['path' => $tempDir]);
$this->assertFileExists($backupFile);
@unlink($file);
@unlink($backupFile);
}
public function testDumpCreatesNestedDirectoriesAndFile()
{
$tempDir = sys_get_temp_dir();
$translationsDir = $tempDir.'/test/translations';
$file = $translationsDir.'/messages.en.concrete';
$catalogue = new MessageCatalogue('en');
$catalogue->add(['foo' => 'bar']);
$dumper = new ConcreteFileDumper();
$dumper->setRelativePathTemplate('test/translations/%domain%.%locale%.%extension%');
$dumper->dump($catalogue, ['path' => $tempDir]);
$this->assertFileExists($file);
@unlink($file);
@rmdir($translationsDir);
}
}
class ConcreteFileDumper extends FileDumper
{
public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = [])
{
return '';
}
protected function getExtension()
{
return 'concrete';
}
}

View File

@@ -0,0 +1,29 @@
<?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\Dumper;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Dumper\IcuResFileDumper;
use Symfony\Component\Translation\MessageCatalogue;
class IcuResFileDumperTest extends TestCase
{
public function testFormatCatalogue()
{
$catalogue = new MessageCatalogue('en');
$catalogue->add(['foo' => 'bar']);
$dumper = new IcuResFileDumper();
$this->assertStringEqualsFile(__DIR__.'/../fixtures/resourcebundle/res/en.res', $dumper->formatCatalogue($catalogue, 'messages'));
}
}

View File

@@ -0,0 +1,29 @@
<?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\Dumper;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Dumper\IniFileDumper;
use Symfony\Component\Translation\MessageCatalogue;
class IniFileDumperTest extends TestCase
{
public function testFormatCatalogue()
{
$catalogue = new MessageCatalogue('en');
$catalogue->add(['foo' => 'bar']);
$dumper = new IniFileDumper();
$this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.ini', $dumper->formatCatalogue($catalogue, 'messages'));
}
}

View File

@@ -0,0 +1,39 @@
<?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\Dumper;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Dumper\JsonFileDumper;
use Symfony\Component\Translation\MessageCatalogue;
class JsonFileDumperTest extends TestCase
{
public function testFormatCatalogue()
{
$catalogue = new MessageCatalogue('en');
$catalogue->add(['foo' => 'bar']);
$dumper = new JsonFileDumper();
$this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.json', $dumper->formatCatalogue($catalogue, 'messages'));
}
public function testDumpWithCustomEncoding()
{
$catalogue = new MessageCatalogue('en');
$catalogue->add(['foo' => '"bar"']);
$dumper = new JsonFileDumper();
$this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.dump.json', $dumper->formatCatalogue($catalogue, 'messages', ['json_encoding' => \JSON_HEX_QUOT]));
}
}

View File

@@ -0,0 +1,29 @@
<?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\Dumper;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Dumper\MoFileDumper;
use Symfony\Component\Translation\MessageCatalogue;
class MoFileDumperTest extends TestCase
{
public function testFormatCatalogue()
{
$catalogue = new MessageCatalogue('en');
$catalogue->add(['foo' => 'bar']);
$dumper = new MoFileDumper();
$this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.mo', $dumper->formatCatalogue($catalogue, 'messages'));
}
}

View File

@@ -0,0 +1,29 @@
<?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\Dumper;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Dumper\PhpFileDumper;
use Symfony\Component\Translation\MessageCatalogue;
class PhpFileDumperTest extends TestCase
{
public function testFormatCatalogue()
{
$catalogue = new MessageCatalogue('en');
$catalogue->add(['foo' => 'bar']);
$dumper = new PhpFileDumper();
$this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.php', $dumper->formatCatalogue($catalogue, 'messages'));
}
}

View File

@@ -0,0 +1,29 @@
<?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\Dumper;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Dumper\PoFileDumper;
use Symfony\Component\Translation\MessageCatalogue;
class PoFileDumperTest extends TestCase
{
public function testFormatCatalogue()
{
$catalogue = new MessageCatalogue('en');
$catalogue->add(['foo' => 'bar', 'bar' => 'foo']);
$dumper = new PoFileDumper();
$this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.po', $dumper->formatCatalogue($catalogue, 'messages'));
}
}

View File

@@ -0,0 +1,29 @@
<?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\Dumper;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Dumper\QtFileDumper;
use Symfony\Component\Translation\MessageCatalogue;
class QtFileDumperTest extends TestCase
{
public function testFormatCatalogue()
{
$catalogue = new MessageCatalogue('en');
$catalogue->add(['foo' => 'bar'], 'resources');
$dumper = new QtFileDumper();
$this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.ts', $dumper->formatCatalogue($catalogue, 'resources'));
}
}

View File

@@ -0,0 +1,115 @@
<?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\Dumper;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Dumper\XliffFileDumper;
use Symfony\Component\Translation\MessageCatalogue;
class XliffFileDumperTest extends TestCase
{
public function testFormatCatalogue()
{
$catalogue = new MessageCatalogue('en_US');
$catalogue->add([
'foo' => 'bar',
'key' => '',
'key.with.cdata' => '<source> & <target>',
]);
$catalogue->setMetadata('foo', ['notes' => [['priority' => 1, 'from' => 'bar', 'content' => 'baz']]]);
$catalogue->setMetadata('key', ['notes' => [['content' => 'baz'], ['content' => 'qux']]]);
$dumper = new XliffFileDumper();
$this->assertStringEqualsFile(
__DIR__.'/../fixtures/resources-clean.xlf',
$dumper->formatCatalogue($catalogue, 'messages', ['default_locale' => 'fr_FR'])
);
}
public function testFormatCatalogueXliff2()
{
$catalogue = new MessageCatalogue('en_US');
$catalogue->add([
'foo' => 'bar',
'key' => '',
'key.with.cdata' => '<source> & <target>',
]);
$catalogue->setMetadata('key', ['target-attributes' => ['order' => 1]]);
$dumper = new XliffFileDumper();
$this->assertStringEqualsFile(
__DIR__.'/../fixtures/resources-2.0-clean.xlf',
$dumper->formatCatalogue($catalogue, 'messages', ['default_locale' => 'fr_FR', 'xliff_version' => '2.0'])
);
}
public function testFormatCatalogueWithCustomToolInfo()
{
$options = [
'default_locale' => 'en_US',
'tool_info' => ['tool-id' => 'foo', 'tool-name' => 'foo', 'tool-version' => '0.0', 'tool-company' => 'Foo'],
];
$catalogue = new MessageCatalogue('en_US');
$catalogue->add(['foo' => 'bar']);
$dumper = new XliffFileDumper();
$this->assertStringEqualsFile(
__DIR__.'/../fixtures/resources-tool-info.xlf',
$dumper->formatCatalogue($catalogue, 'messages', $options)
);
}
public function testFormatCatalogueWithTargetAttributesMetadata()
{
$catalogue = new MessageCatalogue('en_US');
$catalogue->add([
'foo' => 'bar',
]);
$catalogue->setMetadata('foo', ['target-attributes' => ['state' => 'needs-translation']]);
$dumper = new XliffFileDumper();
$this->assertStringEqualsFile(
__DIR__.'/../fixtures/resources-target-attributes.xlf',
$dumper->formatCatalogue($catalogue, 'messages', ['default_locale' => 'fr_FR'])
);
}
public function testFormatCatalogueWithNotesMetadata()
{
$catalogue = new MessageCatalogue('en_US');
$catalogue->add([
'foo' => 'bar',
'baz' => 'biz',
]);
$catalogue->setMetadata('foo', ['notes' => [
['category' => 'state', 'content' => 'new'],
['category' => 'approved', 'content' => 'true'],
['category' => 'section', 'content' => 'user login', 'priority' => '1'],
]]);
$catalogue->setMetadata('baz', ['notes' => [
['id' => 'x', 'content' => 'x_content'],
['appliesTo' => 'target', 'category' => 'quality', 'content' => 'Fuzzy'],
]]);
$dumper = new XliffFileDumper();
$this->assertStringEqualsFile(
__DIR__.'/../fixtures/resources-notes-meta.xlf',
$dumper->formatCatalogue($catalogue, 'messages', ['default_locale' => 'fr_FR', 'xliff_version' => '2.0'])
);
}
}

View File

@@ -0,0 +1,45 @@
<?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\Dumper;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Dumper\YamlFileDumper;
use Symfony\Component\Translation\MessageCatalogue;
class YamlFileDumperTest extends TestCase
{
public function testTreeFormatCatalogue()
{
$catalogue = new MessageCatalogue('en');
$catalogue->add([
'foo.bar1' => 'value1',
'foo.bar2' => 'value2',
]);
$dumper = new YamlFileDumper();
$this->assertStringEqualsFile(__DIR__.'/../fixtures/messages.yml', $dumper->formatCatalogue($catalogue, 'messages', ['as_tree' => true, 'inline' => 999]));
}
public function testLinearFormatCatalogue()
{
$catalogue = new MessageCatalogue('en');
$catalogue->add([
'foo.bar1' => 'value1',
'foo.bar2' => 'value2',
]);
$dumper = new YamlFileDumper();
$this->assertStringEqualsFile(__DIR__.'/../fixtures/messages_linear.yml', $dumper->formatCatalogue($catalogue, 'messages'));
}
}

View File

@@ -0,0 +1,97 @@
<?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\Extractor;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Extractor\PhpExtractor;
use Symfony\Component\Translation\MessageCatalogue;
class PhpExtractorTest extends TestCase
{
/**
* @dataProvider resourcesProvider
*
* @param array|string $resource
*/
public function testExtraction($resource)
{
// Arrange
$extractor = new PhpExtractor();
$extractor->setPrefix('prefix');
$catalogue = new MessageCatalogue('en');
// Act
$extractor->extract($resource, $catalogue);
$expectedHeredoc = <<<EOF
heredoc key with whitespace and escaped \$\n sequences
EOF;
$expectedNowdoc = <<<'EOF'
nowdoc key with whitespace and nonescaped \$\n sequences
EOF;
// Assert
$expectedCatalogue = [
'messages' => [
'single-quoted key' => 'prefixsingle-quoted key',
'double-quoted key' => 'prefixdouble-quoted key',
'heredoc key' => 'prefixheredoc key',
'nowdoc key' => 'prefixnowdoc key',
"double-quoted key with whitespace and escaped \$\n\" sequences" => "prefixdouble-quoted key with whitespace and escaped \$\n\" sequences",
'single-quoted key with whitespace and nonescaped \$\n\' sequences' => 'prefixsingle-quoted key with whitespace and nonescaped \$\n\' sequences',
'single-quoted key with "quote mark at the end"' => 'prefixsingle-quoted key with "quote mark at the end"',
$expectedHeredoc => 'prefix'.$expectedHeredoc,
$expectedNowdoc => 'prefix'.$expectedNowdoc,
'{0} There is no apples|{1} There is one apple|]1,Inf[ There are %count% apples' => 'prefix{0} There is no apples|{1} There is one apple|]1,Inf[ There are %count% apples',
'concatenated message with heredoc and nowdoc' => 'prefixconcatenated message with heredoc and nowdoc',
'default domain' => 'prefixdefault domain',
],
'not_messages' => [
'other-domain-test-no-params-short-array' => 'prefixother-domain-test-no-params-short-array',
'other-domain-test-no-params-long-array' => 'prefixother-domain-test-no-params-long-array',
'other-domain-test-params-short-array' => 'prefixother-domain-test-params-short-array',
'other-domain-test-params-long-array' => 'prefixother-domain-test-params-long-array',
'other-domain-test-trans-choice-short-array-%count%' => 'prefixother-domain-test-trans-choice-short-array-%count%',
'other-domain-test-trans-choice-long-array-%count%' => 'prefixother-domain-test-trans-choice-long-array-%count%',
'typecast' => 'prefixtypecast',
'msg1' => 'prefixmsg1',
'msg2' => 'prefixmsg2',
],
];
$actualCatalogue = $catalogue->all();
$this->assertEquals($expectedCatalogue, $actualCatalogue);
}
public function resourcesProvider()
{
$directory = __DIR__.'/../fixtures/extractor/';
$splFiles = [];
foreach (new \DirectoryIterator($directory) as $fileInfo) {
if ($fileInfo->isDot()) {
continue;
}
if ('translation.html.php' === $fileInfo->getBasename()) {
$phpFile = $fileInfo->getPathname();
}
$splFiles[] = $fileInfo->getFileInfo();
}
return [
[$directory],
[$phpFile],
[glob($directory.'*')],
[$splFiles],
[new \ArrayObject(glob($directory.'*'))],
[new \ArrayObject($splFiles)],
];
}
}

View File

@@ -0,0 +1,82 @@
<?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\Formatter;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Formatter\MessageFormatter;
class MessageFormatterTest extends TestCase
{
/**
* @dataProvider getTransMessages
*/
public function testFormat($expected, $message, $parameters = [])
{
$this->assertEquals($expected, $this->getMessageFormatter()->format($message, 'en', $parameters));
}
/**
* @dataProvider getTransChoiceMessages
*/
public function testFormatPlural($expected, $message, $number, $parameters)
{
$this->assertEquals($expected, $this->getMessageFormatter()->choiceFormat($message, $number, 'fr', $parameters));
}
public function getTransMessages()
{
return [
[
'There is one apple',
'There is one apple',
],
[
'There are 5 apples',
'There are %count% apples',
['%count%' => 5],
],
[
'There are 5 apples',
'There are {{count}} apples',
['{{count}}' => 5],
],
];
}
public function getTransChoiceMessages()
{
return [
['Il y a 0 pomme', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 0, ['%count%' => 0]],
['Il y a 1 pomme', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 1, ['%count%' => 1]],
['Il y a 10 pommes', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 10, ['%count%' => 10]],
['Il y a 0 pomme', 'Il y a %count% pomme|Il y a %count% pommes', 0, ['%count%' => 0]],
['Il y a 1 pomme', 'Il y a %count% pomme|Il y a %count% pommes', 1, ['%count%' => 1]],
['Il y a 10 pommes', 'Il y a %count% pomme|Il y a %count% pommes', 10, ['%count%' => 10]],
['Il y a 0 pomme', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 0, ['%count%' => 0]],
['Il y a 1 pomme', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 1, ['%count%' => 1]],
['Il y a 10 pommes', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 10, ['%count%' => 10]],
['Il n\'y a aucune pomme', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 0, ['%count%' => 0]],
['Il y a 1 pomme', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 1, ['%count%' => 1]],
['Il y a 10 pommes', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 10, ['%count%' => 10]],
['Il y a 0 pomme', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 0, ['%count%' => 0]],
];
}
private function getMessageFormatter()
{
return new MessageFormatter();
}
}

View File

@@ -0,0 +1,112 @@
<?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;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Intl\Util\IntlTestHelper;
use Symfony\Component\Translation\IdentityTranslator;
class IdentityTranslatorTest extends TestCase
{
private $defaultLocale;
protected function setUp()
{
parent::setUp();
$this->defaultLocale = \Locale::getDefault();
}
protected function tearDown()
{
parent::tearDown();
\Locale::setDefault($this->defaultLocale);
}
/**
* @dataProvider getTransTests
*/
public function testTrans($expected, $id, $parameters)
{
$translator = new IdentityTranslator();
$this->assertEquals($expected, $translator->trans($id, $parameters));
}
/**
* @dataProvider getTransChoiceTests
*/
public function testTransChoiceWithExplicitLocale($expected, $id, $number, $parameters)
{
$translator = new IdentityTranslator();
$translator->setLocale('en');
$this->assertEquals($expected, $translator->transChoice($id, $number, $parameters));
}
/**
* @dataProvider getTransChoiceTests
*/
public function testTransChoiceWithDefaultLocale($expected, $id, $number, $parameters)
{
\Locale::setDefault('en');
$translator = new IdentityTranslator();
$this->assertEquals($expected, $translator->transChoice($id, $number, $parameters));
}
public function testGetSetLocale()
{
$translator = new IdentityTranslator();
$translator->setLocale('en');
$this->assertEquals('en', $translator->getLocale());
}
public function testGetLocaleReturnsDefaultLocaleIfNotSet()
{
// in order to test with "pt_BR"
IntlTestHelper::requireFullIntl($this, false);
$translator = new IdentityTranslator();
\Locale::setDefault('en');
$this->assertEquals('en', $translator->getLocale());
\Locale::setDefault('pt_BR');
$this->assertEquals('pt_BR', $translator->getLocale());
}
public function getTransTests()
{
return [
['Symfony is great!', 'Symfony is great!', []],
['Symfony is awesome!', 'Symfony is %what%!', ['%what%' => 'awesome']],
];
}
public function getTransChoiceTests()
{
return [
['There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0, ['%count%' => 0]],
['There is one apple', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 1, ['%count%' => 1]],
['There are 10 apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10, ['%count%' => 10]],
['There are 0 apples', 'There is 1 apple|There are %count% apples', 0, ['%count%' => 0]],
['There is 1 apple', 'There is 1 apple|There are %count% apples', 1, ['%count%' => 1]],
['There are 10 apples', 'There is 1 apple|There are %count% apples', 10, ['%count%' => 10]],
// custom validation messages may be coded with a fixed value
['There are 2 apples', 'There are 2 apples', 2, ['%count%' => 2]],
];
}
}

View File

@@ -0,0 +1,47 @@
<?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;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Interval;
class IntervalTest extends TestCase
{
/**
* @dataProvider getTests
*/
public function testTest($expected, $number, $interval)
{
$this->assertEquals($expected, Interval::test($number, $interval));
}
public function testTestException()
{
$this->expectException('Symfony\Component\Translation\Exception\InvalidArgumentException');
Interval::test(1, 'foobar');
}
public function getTests()
{
return [
[true, 3, '{1,2, 3 ,4}'],
[false, 10, '{1,2, 3 ,4}'],
[false, 3, '[1,2]'],
[true, 1, '[1,2]'],
[true, 2, '[1,2]'],
[false, 1, ']1,2['],
[false, 2, ']1,2['],
[true, log(0), '[-Inf,2['],
[true, -log(0), '[-2,+Inf]'],
];
}
}

View File

@@ -0,0 +1,57 @@
<?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\Loader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Translation\Loader\CsvFileLoader;
class CsvFileLoaderTest extends TestCase
{
public function testLoad()
{
$loader = new CsvFileLoader();
$resource = __DIR__.'/../fixtures/resources.csv';
$catalogue = $loader->load($resource, 'en', 'domain1');
$this->assertEquals(['foo' => 'bar'], $catalogue->all('domain1'));
$this->assertEquals('en', $catalogue->getLocale());
$this->assertEquals([new FileResource($resource)], $catalogue->getResources());
}
public function testLoadDoesNothingIfEmpty()
{
$loader = new CsvFileLoader();
$resource = __DIR__.'/../fixtures/empty.csv';
$catalogue = $loader->load($resource, 'en', 'domain1');
$this->assertEquals([], $catalogue->all('domain1'));
$this->assertEquals('en', $catalogue->getLocale());
$this->assertEquals([new FileResource($resource)], $catalogue->getResources());
}
public function testLoadNonExistingResource()
{
$this->expectException('Symfony\Component\Translation\Exception\NotFoundResourceException');
$loader = new CsvFileLoader();
$resource = __DIR__.'/../fixtures/not-exists.csv';
$loader->load($resource, 'en', 'domain1');
}
public function testLoadNonLocalResource()
{
$this->expectException('Symfony\Component\Translation\Exception\InvalidResourceException');
$loader = new CsvFileLoader();
$resource = 'http://example.com/resources.csv';
$loader->load($resource, 'en', 'domain1');
}
}

View File

@@ -0,0 +1,60 @@
<?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\Loader;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Translation\Loader\IcuDatFileLoader;
/**
* @requires extension intl
*/
class IcuDatFileLoaderTest extends LocalizedTestCase
{
public function testLoadInvalidResource()
{
$this->expectException('Symfony\Component\Translation\Exception\InvalidResourceException');
$loader = new IcuDatFileLoader();
$loader->load(__DIR__.'/../fixtures/resourcebundle/corrupted/resources', 'es', 'domain2');
}
public function testDatEnglishLoad()
{
// bundled resource is build using pkgdata command which at least in ICU 4.2 comes in extremely! buggy form
// you must specify an temporary build directory which is not the same as current directory and
// MUST reside on the same partition. pkgdata -p resources -T /srv -d.packagelist.txt
$loader = new IcuDatFileLoader();
$resource = __DIR__.'/../fixtures/resourcebundle/dat/resources';
$catalogue = $loader->load($resource, 'en', 'domain1');
$this->assertEquals(['symfony' => 'Symfony 2 is great'], $catalogue->all('domain1'));
$this->assertEquals('en', $catalogue->getLocale());
$this->assertEquals([new FileResource($resource.'.dat')], $catalogue->getResources());
}
public function testDatFrenchLoad()
{
$loader = new IcuDatFileLoader();
$resource = __DIR__.'/../fixtures/resourcebundle/dat/resources';
$catalogue = $loader->load($resource, 'fr', 'domain1');
$this->assertEquals(['symfony' => 'Symfony 2 est génial'], $catalogue->all('domain1'));
$this->assertEquals('fr', $catalogue->getLocale());
$this->assertEquals([new FileResource($resource.'.dat')], $catalogue->getResources());
}
public function testLoadNonExistingResource()
{
$this->expectException('Symfony\Component\Translation\Exception\NotFoundResourceException');
$loader = new IcuDatFileLoader();
$loader->load(__DIR__.'/../fixtures/non-existing.txt', 'en', 'domain1');
}
}

View File

@@ -0,0 +1,47 @@
<?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\Loader;
use Symfony\Component\Config\Resource\DirectoryResource;
use Symfony\Component\Translation\Loader\IcuResFileLoader;
/**
* @requires extension intl
*/
class IcuResFileLoaderTest extends LocalizedTestCase
{
public function testLoad()
{
// resource is build using genrb command
$loader = new IcuResFileLoader();
$resource = __DIR__.'/../fixtures/resourcebundle/res';
$catalogue = $loader->load($resource, 'en', 'domain1');
$this->assertEquals(['foo' => 'bar'], $catalogue->all('domain1'));
$this->assertEquals('en', $catalogue->getLocale());
$this->assertEquals([new DirectoryResource($resource)], $catalogue->getResources());
}
public function testLoadNonExistingResource()
{
$this->expectException('Symfony\Component\Translation\Exception\NotFoundResourceException');
$loader = new IcuResFileLoader();
$loader->load(__DIR__.'/../fixtures/non-existing.txt', 'en', 'domain1');
}
public function testLoadInvalidResource()
{
$this->expectException('Symfony\Component\Translation\Exception\InvalidResourceException');
$loader = new IcuResFileLoader();
$loader->load(__DIR__.'/../fixtures/resourcebundle/corrupted', 'en', 'domain1');
}
}

View File

@@ -0,0 +1,49 @@
<?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\Loader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Translation\Loader\IniFileLoader;
class IniFileLoaderTest extends TestCase
{
public function testLoad()
{
$loader = new IniFileLoader();
$resource = __DIR__.'/../fixtures/resources.ini';
$catalogue = $loader->load($resource, 'en', 'domain1');
$this->assertEquals(['foo' => 'bar'], $catalogue->all('domain1'));
$this->assertEquals('en', $catalogue->getLocale());
$this->assertEquals([new FileResource($resource)], $catalogue->getResources());
}
public function testLoadDoesNothingIfEmpty()
{
$loader = new IniFileLoader();
$resource = __DIR__.'/../fixtures/empty.ini';
$catalogue = $loader->load($resource, 'en', 'domain1');
$this->assertEquals([], $catalogue->all('domain1'));
$this->assertEquals('en', $catalogue->getLocale());
$this->assertEquals([new FileResource($resource)], $catalogue->getResources());
}
public function testLoadNonExistingResource()
{
$this->expectException('Symfony\Component\Translation\Exception\NotFoundResourceException');
$loader = new IniFileLoader();
$resource = __DIR__.'/../fixtures/non-existing.ini';
$loader->load($resource, 'en', 'domain1');
}
}

View File

@@ -0,0 +1,58 @@
<?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\Loader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Translation\Loader\JsonFileLoader;
class JsonFileLoaderTest extends TestCase
{
public function testLoad()
{
$loader = new JsonFileLoader();
$resource = __DIR__.'/../fixtures/resources.json';
$catalogue = $loader->load($resource, 'en', 'domain1');
$this->assertEquals(['foo' => 'bar'], $catalogue->all('domain1'));
$this->assertEquals('en', $catalogue->getLocale());
$this->assertEquals([new FileResource($resource)], $catalogue->getResources());
}
public function testLoadDoesNothingIfEmpty()
{
$loader = new JsonFileLoader();
$resource = __DIR__.'/../fixtures/empty.json';
$catalogue = $loader->load($resource, 'en', 'domain1');
$this->assertEquals([], $catalogue->all('domain1'));
$this->assertEquals('en', $catalogue->getLocale());
$this->assertEquals([new FileResource($resource)], $catalogue->getResources());
}
public function testLoadNonExistingResource()
{
$this->expectException('Symfony\Component\Translation\Exception\NotFoundResourceException');
$loader = new JsonFileLoader();
$resource = __DIR__.'/../fixtures/non-existing.json';
$loader->load($resource, 'en', 'domain1');
}
public function testParseException()
{
$this->expectException('Symfony\Component\Translation\Exception\InvalidResourceException');
$this->expectExceptionMessage('Error parsing JSON: Syntax error, malformed JSON');
$loader = new JsonFileLoader();
$resource = __DIR__.'/../fixtures/malformed.json';
$loader->load($resource, 'en', 'domain1');
}
}

View File

@@ -0,0 +1,24 @@
<?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\Loader;
use PHPUnit\Framework\TestCase;
abstract class LocalizedTestCase extends TestCase
{
protected function setUp()
{
if (!\extension_loaded('intl')) {
$this->markTestSkipped('Extension intl is required.');
}
}
}

View File

@@ -0,0 +1,71 @@
<?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\Loader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Translation\Loader\MoFileLoader;
class MoFileLoaderTest extends TestCase
{
public function testLoad()
{
$loader = new MoFileLoader();
$resource = __DIR__.'/../fixtures/resources.mo';
$catalogue = $loader->load($resource, 'en', 'domain1');
$this->assertEquals(['foo' => 'bar'], $catalogue->all('domain1'));
$this->assertEquals('en', $catalogue->getLocale());
$this->assertEquals([new FileResource($resource)], $catalogue->getResources());
}
public function testLoadPlurals()
{
$loader = new MoFileLoader();
$resource = __DIR__.'/../fixtures/plurals.mo';
$catalogue = $loader->load($resource, 'en', 'domain1');
$this->assertEquals([
'foo|foos' => 'bar|bars',
'{0} no foos|one foo|%count% foos' => '{0} no bars|one bar|%count% bars',
], $catalogue->all('domain1'));
$this->assertEquals('en', $catalogue->getLocale());
$this->assertEquals([new FileResource($resource)], $catalogue->getResources());
}
public function testLoadNonExistingResource()
{
$this->expectException('Symfony\Component\Translation\Exception\NotFoundResourceException');
$loader = new MoFileLoader();
$resource = __DIR__.'/../fixtures/non-existing.mo';
$loader->load($resource, 'en', 'domain1');
}
public function testLoadInvalidResource()
{
$this->expectException('Symfony\Component\Translation\Exception\InvalidResourceException');
$loader = new MoFileLoader();
$resource = __DIR__.'/../fixtures/empty.mo';
$loader->load($resource, 'en', 'domain1');
}
public function testLoadEmptyTranslation()
{
$loader = new MoFileLoader();
$resource = __DIR__.'/../fixtures/empty-translation.mo';
$catalogue = $loader->load($resource, 'en', 'message');
$this->assertEquals([], $catalogue->all('message'));
$this->assertEquals('en', $catalogue->getLocale());
$this->assertEquals([new FileResource($resource)], $catalogue->getResources());
}
}

View File

@@ -0,0 +1,46 @@
<?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\Loader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Translation\Loader\PhpFileLoader;
class PhpFileLoaderTest extends TestCase
{
public function testLoad()
{
$loader = new PhpFileLoader();
$resource = __DIR__.'/../fixtures/resources.php';
$catalogue = $loader->load($resource, 'en', 'domain1');
$this->assertEquals(['foo' => 'bar'], $catalogue->all('domain1'));
$this->assertEquals('en', $catalogue->getLocale());
$this->assertEquals([new FileResource($resource)], $catalogue->getResources());
}
public function testLoadNonExistingResource()
{
$this->expectException('Symfony\Component\Translation\Exception\NotFoundResourceException');
$loader = new PhpFileLoader();
$resource = __DIR__.'/../fixtures/non-existing.php';
$loader->load($resource, 'en', 'domain1');
}
public function testLoadThrowsAnExceptionIfFileNotLocal()
{
$this->expectException('Symfony\Component\Translation\Exception\InvalidResourceException');
$loader = new PhpFileLoader();
$resource = 'http://example.com/resources.php';
$loader->load($resource, 'en', 'domain1');
}
}

View File

@@ -0,0 +1,120 @@
<?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\Loader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Translation\Loader\PoFileLoader;
class PoFileLoaderTest extends TestCase
{
public function testLoad()
{
$loader = new PoFileLoader();
$resource = __DIR__.'/../fixtures/resources.po';
$catalogue = $loader->load($resource, 'en', 'domain1');
$this->assertEquals(['foo' => 'bar', 'bar' => 'foo'], $catalogue->all('domain1'));
$this->assertEquals('en', $catalogue->getLocale());
$this->assertEquals([new FileResource($resource)], $catalogue->getResources());
}
public function testLoadPlurals()
{
$loader = new PoFileLoader();
$resource = __DIR__.'/../fixtures/plurals.po';
$catalogue = $loader->load($resource, 'en', 'domain1');
$this->assertEquals([
'foo|foos' => 'bar|bars',
'{0} no foos|one foo|%count% foos' => '{0} no bars|one bar|%count% bars',
], $catalogue->all('domain1'));
$this->assertEquals('en', $catalogue->getLocale());
$this->assertEquals([new FileResource($resource)], $catalogue->getResources());
}
public function testLoadDoesNothingIfEmpty()
{
$loader = new PoFileLoader();
$resource = __DIR__.'/../fixtures/empty.po';
$catalogue = $loader->load($resource, 'en', 'domain1');
$this->assertEquals([], $catalogue->all('domain1'));
$this->assertEquals('en', $catalogue->getLocale());
$this->assertEquals([new FileResource($resource)], $catalogue->getResources());
}
public function testLoadNonExistingResource()
{
$this->expectException('Symfony\Component\Translation\Exception\NotFoundResourceException');
$loader = new PoFileLoader();
$resource = __DIR__.'/../fixtures/non-existing.po';
$loader->load($resource, 'en', 'domain1');
}
public function testLoadEmptyTranslation()
{
$loader = new PoFileLoader();
$resource = __DIR__.'/../fixtures/empty-translation.po';
$catalogue = $loader->load($resource, 'en', 'domain1');
$this->assertEquals(['foo' => ''], $catalogue->all('domain1'));
$this->assertEquals('en', $catalogue->getLocale());
$this->assertEquals([new FileResource($resource)], $catalogue->getResources());
}
public function testEscapedId()
{
$loader = new PoFileLoader();
$resource = __DIR__.'/../fixtures/escaped-id.po';
$catalogue = $loader->load($resource, 'en', 'domain1');
$messages = $catalogue->all('domain1');
$this->assertArrayHasKey('escaped "foo"', $messages);
$this->assertEquals('escaped "bar"', $messages['escaped "foo"']);
}
public function testEscapedIdPlurals()
{
$loader = new PoFileLoader();
$resource = __DIR__.'/../fixtures/escaped-id-plurals.po';
$catalogue = $loader->load($resource, 'en', 'domain1');
$messages = $catalogue->all('domain1');
$this->assertArrayHasKey('escaped "foo"|escaped "foos"', $messages);
$this->assertEquals('escaped "bar"|escaped "bars"', $messages['escaped "foo"|escaped "foos"']);
}
public function testSkipFuzzyTranslations()
{
$loader = new PoFileLoader();
$resource = __DIR__.'/../fixtures/fuzzy-translations.po';
$catalogue = $loader->load($resource, 'en', 'domain1');
$messages = $catalogue->all('domain1');
$this->assertArrayHasKey('foo1', $messages);
$this->assertArrayNotHasKey('foo2', $messages);
$this->assertArrayHasKey('foo3', $messages);
}
public function testMissingPlurals()
{
$loader = new PoFileLoader();
$resource = __DIR__.'/../fixtures/missing-plurals.po';
$catalogue = $loader->load($resource, 'en', 'domain1');
$this->assertEquals([
'foo|foos' => '-|bar|-|bars',
], $catalogue->all('domain1'));
$this->assertEquals('en', $catalogue->getLocale());
}
}

View File

@@ -0,0 +1,65 @@
<?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\Loader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Translation\Loader\QtFileLoader;
class QtFileLoaderTest extends TestCase
{
public function testLoad()
{
$loader = new QtFileLoader();
$resource = __DIR__.'/../fixtures/resources.ts';
$catalogue = $loader->load($resource, 'en', 'resources');
$this->assertEquals(['foo' => 'bar'], $catalogue->all('resources'));
$this->assertEquals('en', $catalogue->getLocale());
$this->assertEquals([new FileResource($resource)], $catalogue->getResources());
}
public function testLoadNonExistingResource()
{
$this->expectException('Symfony\Component\Translation\Exception\NotFoundResourceException');
$loader = new QtFileLoader();
$resource = __DIR__.'/../fixtures/non-existing.ts';
$loader->load($resource, 'en', 'domain1');
}
public function testLoadNonLocalResource()
{
$this->expectException('Symfony\Component\Translation\Exception\InvalidResourceException');
$loader = new QtFileLoader();
$resource = 'http://domain1.com/resources.ts';
$loader->load($resource, 'en', 'domain1');
}
public function testLoadInvalidResource()
{
$this->expectException('Symfony\Component\Translation\Exception\InvalidResourceException');
$loader = new QtFileLoader();
$resource = __DIR__.'/../fixtures/invalid-xml-resources.xlf';
$loader->load($resource, 'en', 'domain1');
}
public function testLoadEmptyResource()
{
$loader = new QtFileLoader();
$resource = __DIR__.'/../fixtures/empty.xlf';
$this->expectException('Symfony\Component\Translation\Exception\InvalidResourceException');
$this->expectExceptionMessage(sprintf('Unable to load "%s".', $resource));
$loader->load($resource, 'en', 'domain1');
}
}

View File

@@ -0,0 +1,250 @@
<?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\Loader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Translation\Loader\XliffFileLoader;
class XliffFileLoaderTest extends TestCase
{
public function testLoad()
{
$loader = new XliffFileLoader();
$resource = __DIR__.'/../fixtures/resources.xlf';
$catalogue = $loader->load($resource, 'en', 'domain1');
$this->assertEquals('en', $catalogue->getLocale());
$this->assertEquals([new FileResource($resource)], $catalogue->getResources());
$this->assertSame([], libxml_get_errors());
$this->assertContainsOnly('string', $catalogue->all('domain1'));
}
public function testLoadWithInternalErrorsEnabled()
{
$internalErrors = libxml_use_internal_errors(true);
$this->assertSame([], libxml_get_errors());
$loader = new XliffFileLoader();
$resource = __DIR__.'/../fixtures/resources.xlf';
$catalogue = $loader->load($resource, 'en', 'domain1');
$this->assertEquals('en', $catalogue->getLocale());
$this->assertEquals([new FileResource($resource)], $catalogue->getResources());
$this->assertSame([], libxml_get_errors());
libxml_clear_errors();
libxml_use_internal_errors($internalErrors);
}
public function testLoadWithExternalEntitiesDisabled()
{
if (\LIBXML_VERSION < 20900) {
$disableEntities = libxml_disable_entity_loader(true);
}
$loader = new XliffFileLoader();
$resource = __DIR__.'/../fixtures/resources.xlf';
$catalogue = $loader->load($resource, 'en', 'domain1');
if (\LIBXML_VERSION < 20900) {
libxml_disable_entity_loader($disableEntities);
}
$this->assertEquals('en', $catalogue->getLocale());
$this->assertEquals([new FileResource($resource)], $catalogue->getResources());
}
public function testLoadWithResname()
{
$loader = new XliffFileLoader();
$catalogue = $loader->load(__DIR__.'/../fixtures/resname.xlf', 'en', 'domain1');
$this->assertEquals(['foo' => 'bar', 'bar' => 'baz', 'baz' => 'foo', 'qux' => 'qux source'], $catalogue->all('domain1'));
}
public function testIncompleteResource()
{
$loader = new XliffFileLoader();
$catalogue = $loader->load(__DIR__.'/../fixtures/resources.xlf', 'en', 'domain1');
$this->assertEquals(['foo' => 'bar', 'extra' => 'extra', 'key' => '', 'test' => 'with'], $catalogue->all('domain1'));
}
public function testEncoding()
{
$loader = new XliffFileLoader();
$catalogue = $loader->load(__DIR__.'/../fixtures/encoding.xlf', 'en', 'domain1');
$this->assertEquals(utf8_decode('föö'), $catalogue->get('bar', 'domain1'));
$this->assertEquals(utf8_decode('bär'), $catalogue->get('foo', 'domain1'));
$this->assertEquals(['notes' => [['content' => utf8_decode('bäz')]], 'id' => '1'], $catalogue->getMetadata('foo', 'domain1'));
}
public function testTargetAttributesAreStoredCorrectly()
{
$loader = new XliffFileLoader();
$catalogue = $loader->load(__DIR__.'/../fixtures/with-attributes.xlf', 'en', 'domain1');
$metadata = $catalogue->getMetadata('foo', 'domain1');
$this->assertEquals('translated', $metadata['target-attributes']['state']);
}
public function testLoadInvalidResource()
{
$this->expectException('Symfony\Component\Translation\Exception\InvalidResourceException');
$loader = new XliffFileLoader();
$loader->load(__DIR__.'/../fixtures/resources.php', 'en', 'domain1');
}
public function testLoadResourceDoesNotValidate()
{
$this->expectException('Symfony\Component\Translation\Exception\InvalidResourceException');
$loader = new XliffFileLoader();
$loader->load(__DIR__.'/../fixtures/non-valid.xlf', 'en', 'domain1');
}
public function testLoadNonExistingResource()
{
$this->expectException('Symfony\Component\Translation\Exception\NotFoundResourceException');
$loader = new XliffFileLoader();
$resource = __DIR__.'/../fixtures/non-existing.xlf';
$loader->load($resource, 'en', 'domain1');
}
public function testLoadThrowsAnExceptionIfFileNotLocal()
{
$this->expectException('Symfony\Component\Translation\Exception\InvalidResourceException');
$loader = new XliffFileLoader();
$resource = 'http://example.com/resources.xlf';
$loader->load($resource, 'en', 'domain1');
}
public function testDocTypeIsNotAllowed()
{
$this->expectException('Symfony\Component\Translation\Exception\InvalidResourceException');
$this->expectExceptionMessage('Document types are not allowed.');
$loader = new XliffFileLoader();
$loader->load(__DIR__.'/../fixtures/withdoctype.xlf', 'en', 'domain1');
}
public function testParseEmptyFile()
{
$loader = new XliffFileLoader();
$resource = __DIR__.'/../fixtures/empty.xlf';
$this->expectException('Symfony\Component\Translation\Exception\InvalidResourceException');
$this->expectExceptionMessage(sprintf('Unable to load "%s":', $resource));
$loader->load($resource, 'en', 'domain1');
}
public function testLoadNotes()
{
$loader = new XliffFileLoader();
$catalogue = $loader->load(__DIR__.'/../fixtures/withnote.xlf', 'en', 'domain1');
$this->assertEquals(['notes' => [['priority' => 1, 'content' => 'foo']], 'id' => '1'], $catalogue->getMetadata('foo', 'domain1'));
// message without target
$this->assertEquals(['notes' => [['content' => 'bar', 'from' => 'foo']], 'id' => '2'], $catalogue->getMetadata('extra', 'domain1'));
// message with empty target
$this->assertEquals(['notes' => [['content' => 'baz'], ['priority' => 2, 'from' => 'bar', 'content' => 'qux']], 'id' => '123'], $catalogue->getMetadata('key', 'domain1'));
}
public function testLoadVersion2()
{
$loader = new XliffFileLoader();
$resource = __DIR__.'/../fixtures/resources-2.0.xlf';
$catalogue = $loader->load($resource, 'en', 'domain1');
$this->assertEquals('en', $catalogue->getLocale());
$this->assertEquals([new FileResource($resource)], $catalogue->getResources());
$this->assertSame([], libxml_get_errors());
$domains = $catalogue->all();
$this->assertCount(3, $domains['domain1']);
$this->assertContainsOnly('string', $catalogue->all('domain1'));
// target attributes
$this->assertEquals(['target-attributes' => ['order' => 1]], $catalogue->getMetadata('bar', 'domain1'));
}
public function testLoadVersion2WithNoteMeta()
{
$loader = new XliffFileLoader();
$resource = __DIR__.'/../fixtures/resources-notes-meta.xlf';
$catalogue = $loader->load($resource, 'en', 'domain1');
$this->assertEquals('en', $catalogue->getLocale());
$this->assertEquals([new FileResource($resource)], $catalogue->getResources());
$this->assertSame([], libxml_get_errors());
// test for "foo" metadata
$this->assertTrue($catalogue->defines('foo', 'domain1'));
$metadata = $catalogue->getMetadata('foo', 'domain1');
$this->assertNotEmpty($metadata);
$this->assertCount(3, $metadata['notes']);
$this->assertEquals('state', $metadata['notes'][0]['category']);
$this->assertEquals('new', $metadata['notes'][0]['content']);
$this->assertEquals('approved', $metadata['notes'][1]['category']);
$this->assertEquals('true', $metadata['notes'][1]['content']);
$this->assertEquals('section', $metadata['notes'][2]['category']);
$this->assertEquals('1', $metadata['notes'][2]['priority']);
$this->assertEquals('user login', $metadata['notes'][2]['content']);
// test for "baz" metadata
$this->assertTrue($catalogue->defines('baz', 'domain1'));
$metadata = $catalogue->getMetadata('baz', 'domain1');
$this->assertNotEmpty($metadata);
$this->assertCount(2, $metadata['notes']);
$this->assertEquals('x', $metadata['notes'][0]['id']);
$this->assertEquals('x_content', $metadata['notes'][0]['content']);
$this->assertEquals('target', $metadata['notes'][1]['appliesTo']);
$this->assertEquals('quality', $metadata['notes'][1]['category']);
$this->assertEquals('Fuzzy', $metadata['notes'][1]['content']);
}
public function testLoadVersion2WithMultiSegmentUnit()
{
$loader = new XliffFileLoader();
$resource = __DIR__.'/../fixtures/resources-2.0-multi-segment-unit.xlf';
$catalog = $loader->load($resource, 'en', 'domain1');
$this->assertSame('en', $catalog->getLocale());
$this->assertEquals([new FileResource($resource)], $catalog->getResources());
$this->assertFalse(libxml_get_last_error());
// test for "foo" metadata
$this->assertTrue($catalog->defines('foo', 'domain1'));
$metadata = $catalog->getMetadata('foo', 'domain1');
$this->assertNotEmpty($metadata);
$this->assertCount(1, $metadata['notes']);
$this->assertSame('processed', $metadata['notes'][0]['category']);
$this->assertSame('true', $metadata['notes'][0]['content']);
// test for "bar" metadata
$this->assertTrue($catalog->defines('bar', 'domain1'));
$metadata = $catalog->getMetadata('bar', 'domain1');
$this->assertNotEmpty($metadata);
$this->assertCount(1, $metadata['notes']);
$this->assertSame('processed', $metadata['notes'][0]['category']);
$this->assertSame('true', $metadata['notes'][0]['content']);
}
}

View File

@@ -0,0 +1,65 @@
<?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\Loader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Translation\Loader\YamlFileLoader;
class YamlFileLoaderTest extends TestCase
{
public function testLoad()
{
$loader = new YamlFileLoader();
$resource = __DIR__.'/../fixtures/resources.yml';
$catalogue = $loader->load($resource, 'en', 'domain1');
$this->assertEquals(['foo' => 'bar'], $catalogue->all('domain1'));
$this->assertEquals('en', $catalogue->getLocale());
$this->assertEquals([new FileResource($resource)], $catalogue->getResources());
}
public function testLoadDoesNothingIfEmpty()
{
$loader = new YamlFileLoader();
$resource = __DIR__.'/../fixtures/empty.yml';
$catalogue = $loader->load($resource, 'en', 'domain1');
$this->assertEquals([], $catalogue->all('domain1'));
$this->assertEquals('en', $catalogue->getLocale());
$this->assertEquals([new FileResource($resource)], $catalogue->getResources());
}
public function testLoadNonExistingResource()
{
$this->expectException('Symfony\Component\Translation\Exception\NotFoundResourceException');
$loader = new YamlFileLoader();
$resource = __DIR__.'/../fixtures/non-existing.yml';
$loader->load($resource, 'en', 'domain1');
}
public function testLoadThrowsAnExceptionIfFileNotLocal()
{
$this->expectException('Symfony\Component\Translation\Exception\InvalidResourceException');
$loader = new YamlFileLoader();
$resource = 'http://example.com/resources.yml';
$loader->load($resource, 'en', 'domain1');
}
public function testLoadThrowsAnExceptionIfNotAnArray()
{
$this->expectException('Symfony\Component\Translation\Exception\InvalidResourceException');
$loader = new YamlFileLoader();
$resource = __DIR__.'/../fixtures/non-valid.yml';
$loader->load($resource, 'en', 'domain1');
}
}

View File

@@ -0,0 +1,50 @@
<?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;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Loader\ArrayLoader;
use Symfony\Component\Translation\LoggingTranslator;
use Symfony\Component\Translation\Translator;
class LoggingTranslatorTest extends TestCase
{
public function testTransWithNoTranslationIsLogged()
{
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$logger->expects($this->exactly(2))
->method('warning')
->with('Translation not found.')
;
$translator = new Translator('ar');
$loggableTranslator = new LoggingTranslator($translator, $logger);
$loggableTranslator->transChoice('some_message2', 10, ['%count%' => 10]);
$loggableTranslator->trans('bar');
}
public function testTransChoiceFallbackIsLogged()
{
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$logger->expects($this->once())
->method('debug')
->with('Translation use fallback catalogue.')
;
$translator = new Translator('ar');
$translator->setFallbackLocales(['en']);
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', ['some_message2' => 'one thing|%count% things'], 'en');
$loggableTranslator = new LoggingTranslator($translator, $logger);
$loggableTranslator->transChoice('some_message2', 10, ['%count%' => 10]);
}
}

View File

@@ -0,0 +1,216 @@
<?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;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\MessageCatalogue;
class MessageCatalogueTest extends TestCase
{
public function testGetLocale()
{
$catalogue = new MessageCatalogue('en');
$this->assertEquals('en', $catalogue->getLocale());
}
public function testGetDomains()
{
$catalogue = new MessageCatalogue('en', ['domain1' => [], 'domain2' => []]);
$this->assertEquals(['domain1', 'domain2'], $catalogue->getDomains());
}
public function testAll()
{
$catalogue = new MessageCatalogue('en', $messages = ['domain1' => ['foo' => 'foo'], 'domain2' => ['bar' => 'bar']]);
$this->assertEquals(['foo' => 'foo'], $catalogue->all('domain1'));
$this->assertEquals([], $catalogue->all('domain88'));
$this->assertEquals($messages, $catalogue->all());
}
public function testHas()
{
$catalogue = new MessageCatalogue('en', ['domain1' => ['foo' => 'foo'], 'domain2' => ['bar' => 'bar']]);
$this->assertTrue($catalogue->has('foo', 'domain1'));
$this->assertFalse($catalogue->has('bar', 'domain1'));
$this->assertFalse($catalogue->has('foo', 'domain88'));
}
public function testGetSet()
{
$catalogue = new MessageCatalogue('en', ['domain1' => ['foo' => 'foo'], 'domain2' => ['bar' => 'bar']]);
$catalogue->set('foo1', 'foo1', 'domain1');
$this->assertEquals('foo', $catalogue->get('foo', 'domain1'));
$this->assertEquals('foo1', $catalogue->get('foo1', 'domain1'));
}
public function testAdd()
{
$catalogue = new MessageCatalogue('en', ['domain1' => ['foo' => 'foo'], 'domain2' => ['bar' => 'bar']]);
$catalogue->add(['foo1' => 'foo1'], 'domain1');
$this->assertEquals('foo', $catalogue->get('foo', 'domain1'));
$this->assertEquals('foo1', $catalogue->get('foo1', 'domain1'));
$catalogue->add(['foo' => 'bar'], 'domain1');
$this->assertEquals('bar', $catalogue->get('foo', 'domain1'));
$this->assertEquals('foo1', $catalogue->get('foo1', 'domain1'));
$catalogue->add(['foo' => 'bar'], 'domain88');
$this->assertEquals('bar', $catalogue->get('foo', 'domain88'));
}
public function testReplace()
{
$catalogue = new MessageCatalogue('en', ['domain1' => ['foo' => 'foo'], 'domain2' => ['bar' => 'bar']]);
$catalogue->replace($messages = ['foo1' => 'foo1'], 'domain1');
$this->assertEquals($messages, $catalogue->all('domain1'));
}
public function testAddCatalogue()
{
$r = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
$r->expects($this->any())->method('__toString')->willReturn('r');
$r1 = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
$r1->expects($this->any())->method('__toString')->willReturn('r1');
$catalogue = new MessageCatalogue('en', ['domain1' => ['foo' => 'foo'], 'domain2' => ['bar' => 'bar']]);
$catalogue->addResource($r);
$catalogue1 = new MessageCatalogue('en', ['domain1' => ['foo1' => 'foo1']]);
$catalogue1->addResource($r1);
$catalogue->addCatalogue($catalogue1);
$this->assertEquals('foo', $catalogue->get('foo', 'domain1'));
$this->assertEquals('foo1', $catalogue->get('foo1', 'domain1'));
$this->assertEquals([$r, $r1], $catalogue->getResources());
}
public function testAddFallbackCatalogue()
{
$r = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
$r->expects($this->any())->method('__toString')->willReturn('r');
$r1 = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
$r1->expects($this->any())->method('__toString')->willReturn('r1');
$r2 = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
$r2->expects($this->any())->method('__toString')->willReturn('r2');
$catalogue = new MessageCatalogue('fr_FR', ['domain1' => ['foo' => 'foo'], 'domain2' => ['bar' => 'bar']]);
$catalogue->addResource($r);
$catalogue1 = new MessageCatalogue('fr', ['domain1' => ['foo' => 'bar', 'foo1' => 'foo1']]);
$catalogue1->addResource($r1);
$catalogue2 = new MessageCatalogue('en');
$catalogue2->addResource($r2);
$catalogue->addFallbackCatalogue($catalogue1);
$catalogue1->addFallbackCatalogue($catalogue2);
$this->assertEquals('foo', $catalogue->get('foo', 'domain1'));
$this->assertEquals('foo1', $catalogue->get('foo1', 'domain1'));
$this->assertEquals([$r, $r1, $r2], $catalogue->getResources());
}
public function testAddFallbackCatalogueWithParentCircularReference()
{
$this->expectException('Symfony\Component\Translation\Exception\LogicException');
$main = new MessageCatalogue('en_US');
$fallback = new MessageCatalogue('fr_FR');
$fallback->addFallbackCatalogue($main);
$main->addFallbackCatalogue($fallback);
}
public function testAddFallbackCatalogueWithFallbackCircularReference()
{
$this->expectException('Symfony\Component\Translation\Exception\LogicException');
$fr = new MessageCatalogue('fr');
$en = new MessageCatalogue('en');
$es = new MessageCatalogue('es');
$fr->addFallbackCatalogue($en);
$es->addFallbackCatalogue($en);
$en->addFallbackCatalogue($fr);
}
public function testAddCatalogueWhenLocaleIsNotTheSameAsTheCurrentOne()
{
$this->expectException('Symfony\Component\Translation\Exception\LogicException');
$catalogue = new MessageCatalogue('en');
$catalogue->addCatalogue(new MessageCatalogue('fr', []));
}
public function testGetAddResource()
{
$catalogue = new MessageCatalogue('en');
$r = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
$r->expects($this->any())->method('__toString')->willReturn('r');
$catalogue->addResource($r);
$catalogue->addResource($r);
$r1 = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
$r1->expects($this->any())->method('__toString')->willReturn('r1');
$catalogue->addResource($r1);
$this->assertEquals([$r, $r1], $catalogue->getResources());
}
public function testMetadataDelete()
{
$catalogue = new MessageCatalogue('en');
$this->assertEquals([], $catalogue->getMetadata('', ''), 'Metadata is empty');
$catalogue->deleteMetadata('key', 'messages');
$catalogue->deleteMetadata('', 'messages');
$catalogue->deleteMetadata();
}
public function testMetadataSetGetDelete()
{
$catalogue = new MessageCatalogue('en');
$catalogue->setMetadata('key', 'value');
$this->assertEquals('value', $catalogue->getMetadata('key', 'messages'), "Metadata 'key' = 'value'");
$catalogue->setMetadata('key2', []);
$this->assertEquals([], $catalogue->getMetadata('key2', 'messages'), 'Metadata key2 is array');
$catalogue->deleteMetadata('key2', 'messages');
$this->assertNull($catalogue->getMetadata('key2', 'messages'), 'Metadata key2 should is deleted.');
$catalogue->deleteMetadata('key2', 'domain');
$this->assertNull($catalogue->getMetadata('key2', 'domain'), 'Metadata key2 should is deleted.');
}
public function testMetadataMerge()
{
$cat1 = new MessageCatalogue('en');
$cat1->setMetadata('a', 'b');
$this->assertEquals(['messages' => ['a' => 'b']], $cat1->getMetadata('', ''), 'Cat1 contains messages metadata.');
$cat2 = new MessageCatalogue('en');
$cat2->setMetadata('b', 'c', 'domain');
$this->assertEquals(['domain' => ['b' => 'c']], $cat2->getMetadata('', ''), 'Cat2 contains domain metadata.');
$cat1->addCatalogue($cat2);
$this->assertEquals(['messages' => ['a' => 'b'], 'domain' => ['b' => 'c']], $cat1->getMetadata('', ''), 'Cat1 contains merged metadata.');
}
}

View File

@@ -0,0 +1,137 @@
<?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;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\MessageSelector;
class MessageSelectorTest extends TestCase
{
/**
* @dataProvider getChooseTests
*/
public function testChoose($expected, $id, $number)
{
$selector = new MessageSelector();
$this->assertEquals($expected, $selector->choose($id, $number, 'en'));
}
public function testReturnMessageIfExactlyOneStandardRuleIsGiven()
{
$selector = new MessageSelector();
$this->assertEquals('There are two apples', $selector->choose('There are two apples', 2, 'en'));
}
/**
* @dataProvider getNonMatchingMessages
*/
public function testThrowExceptionIfMatchingMessageCannotBeFound($id, $number)
{
$this->expectException('Symfony\Component\Translation\Exception\InvalidArgumentException');
$selector = new MessageSelector();
$selector->choose($id, $number, 'en');
}
public function getNonMatchingMessages()
{
return [
['{0} There are no apples|{1} There is one apple', 2],
['{1} There is one apple|]1,Inf] There are %count% apples', 0],
['{1} There is one apple|]2,Inf] There are %count% apples', 2],
['{0} There are no apples|There is one apple', 2],
];
}
public function getChooseTests()
{
return [
['There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0],
['There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0],
['There are no apples', '{0}There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0],
['There is one apple', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 1],
['There are %count% apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10],
['There are %count% apples', '{0} There are no apples|{1} There is one apple|]1,Inf]There are %count% apples', 10],
['There are %count% apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10],
['There are %count% apples', 'There is one apple|There are %count% apples', 0],
['There is one apple', 'There is one apple|There are %count% apples', 1],
['There are %count% apples', 'There is one apple|There are %count% apples', 10],
['There are %count% apples', 'one: There is one apple|more: There are %count% apples', 0],
['There is one apple', 'one: There is one apple|more: There are %count% apples', 1],
['There are %count% apples', 'one: There is one apple|more: There are %count% apples', 10],
['There are no apples', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 0],
['There is one apple', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 1],
['There are %count% apples', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 10],
['', '{0}|{1} There is one apple|]1,Inf] There are %count% apples', 0],
['', '{0} There are no apples|{1}|]1,Inf] There are %count% apples', 1],
// Indexed only tests which are Gettext PoFile* compatible strings.
['There are %count% apples', 'There is one apple|There are %count% apples', 0],
['There is one apple', 'There is one apple|There are %count% apples', 1],
['There are %count% apples', 'There is one apple|There are %count% apples', 2],
// Tests for float numbers
['There is almost one apple', '{0} There are no apples|]0,1[ There is almost one apple|{1} There is one apple|[1,Inf] There is more than one apple', 0.7],
['There is one apple', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 1],
['There is more than one apple', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 1.7],
['There are no apples', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0],
['There are no apples', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0.0],
['There are no apples', '{0.0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0],
// Test texts with new-lines
// with double-quotes and \n in id & double-quotes and actual newlines in text
["This is a text with a\n new-line in it. Selector = 0.", '{0}This is a text with a
new-line in it. Selector = 0.|{1}This is a text with a
new-line in it. Selector = 1.|[1,Inf]This is a text with a
new-line in it. Selector > 1.', 0],
// with double-quotes and \n in id and single-quotes and actual newlines in text
["This is a text with a\n new-line in it. Selector = 1.", '{0}This is a text with a
new-line in it. Selector = 0.|{1}This is a text with a
new-line in it. Selector = 1.|[1,Inf]This is a text with a
new-line in it. Selector > 1.', 1],
["This is a text with a\n new-line in it. Selector > 1.", '{0}This is a text with a
new-line in it. Selector = 0.|{1}This is a text with a
new-line in it. Selector = 1.|[1,Inf]This is a text with a
new-line in it. Selector > 1.', 5],
// with double-quotes and id split accros lines
['This is a text with a
new-line in it. Selector = 1.', '{0}This is a text with a
new-line in it. Selector = 0.|{1}This is a text with a
new-line in it. Selector = 1.|[1,Inf]This is a text with a
new-line in it. Selector > 1.', 1],
// with single-quotes and id split accros lines
['This is a text with a
new-line in it. Selector > 1.', '{0}This is a text with a
new-line in it. Selector = 0.|{1}This is a text with a
new-line in it. Selector = 1.|[1,Inf]This is a text with a
new-line in it. Selector > 1.', 5],
// with single-quotes and \n in text
['This is a text with a\nnew-line in it. Selector = 0.', '{0}This is a text with a\nnew-line in it. Selector = 0.|{1}This is a text with a\nnew-line in it. Selector = 1.|[1,Inf]This is a text with a\nnew-line in it. Selector > 1.', 0],
// with double-quotes and id split accros lines
["This is a text with a\nnew-line in it. Selector = 1.", "{0}This is a text with a\nnew-line in it. Selector = 0.|{1}This is a text with a\nnew-line in it. Selector = 1.|[1,Inf]This is a text with a\nnew-line in it. Selector > 1.", 1],
// esacape pipe
['This is a text with | in it. Selector = 0.', '{0}This is a text with || in it. Selector = 0.|{1}This is a text with || in it. Selector = 1.', 0],
// Empty plural set (2 plural forms) from a .PO file
['', '|', 1],
// Empty plural set (3 plural forms) from a .PO file
['', '||', 1],
];
}
}

View File

@@ -0,0 +1,122 @@
<?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;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\PluralizationRules;
/**
* Test should cover all languages mentioned on http://translate.sourceforge.net/wiki/l10n/pluralforms
* and Plural forms mentioned on http://www.gnu.org/software/gettext/manual/gettext.html#Plural-forms.
*
* See also https://developer.mozilla.org/en/Localization_and_Plurals which mentions 15 rules having a maximum of 6 forms.
* The mozilla code is also interesting to check for.
*
* As mentioned by chx http://drupal.org/node/1273968 we can cover all by testing number from 0 to 199
*
* The goal to cover all languages is to far fetched so this test case is smaller.
*
* @author Clemens Tolboom clemens@build2be.nl
*/
class PluralizationRulesTest extends TestCase
{
/**
* We test failed langcode here.
*
* TODO: The languages mentioned in the data provide need to get fixed somehow within PluralizationRules.
*
* @dataProvider failingLangcodes
*/
public function testFailedLangcodes($nplural, $langCodes)
{
$matrix = $this->generateTestData($langCodes);
$this->validateMatrix($nplural, $matrix, false);
}
/**
* @dataProvider successLangcodes
*/
public function testLangcodes($nplural, $langCodes)
{
$matrix = $this->generateTestData($langCodes);
$this->validateMatrix($nplural, $matrix);
}
/**
* This array should contain all currently known langcodes.
*
* As it is impossible to have this ever complete we should try as hard as possible to have it almost complete.
*
* @return array
*/
public function successLangcodes()
{
return [
['1', ['ay', 'bo', 'cgg', 'dz', 'id', 'ja', 'jbo', 'ka', 'kk', 'km', 'ko', 'ky']],
['2', ['nl', 'fr', 'en', 'de', 'de_GE', 'hy', 'hy_AM']],
['3', ['be', 'bs', 'cs', 'hr']],
['4', ['cy', 'mt', 'sl']],
['6', ['ar']],
];
}
/**
* This array should be at least empty within the near future.
*
* This both depends on a complete list trying to add above as understanding
* the plural rules of the current failing languages.
*
* @return array with nplural together with langcodes
*/
public function failingLangcodes()
{
return [
['1', ['fa']],
['2', ['jbo']],
['3', ['cbs']],
['4', ['gd', 'kw']],
['5', ['ga']],
];
}
/**
* We validate only on the plural coverage. Thus the real rules is not tested.
*
* @param string $nplural Plural expected
* @param array $matrix Containing langcodes and their plural index values
* @param bool $expectSuccess
*/
protected function validateMatrix($nplural, $matrix, $expectSuccess = true)
{
foreach ($matrix as $langCode => $data) {
$indexes = array_flip($data);
if ($expectSuccess) {
$this->assertCount((int) $nplural, $indexes, "Langcode '$langCode' has '$nplural' plural forms.");
} else {
$this->assertNotCount((int) $nplural, $indexes, "Langcode '$langCode' has '$nplural' plural forms.");
}
}
}
protected function generateTestData($langCodes)
{
$matrix = [];
foreach ($langCodes as $langCode) {
for ($count = 0; $count < 200; ++$count) {
$plural = PluralizationRules::get($count, $langCode);
$matrix[$langCode][$count] = $plural;
}
}
return $matrix;
}
}

View File

@@ -0,0 +1,312 @@
<?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;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Resource\SelfCheckingResourceInterface;
use Symfony\Component\Translation\Loader\ArrayLoader;
use Symfony\Component\Translation\Loader\LoaderInterface;
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\Translator;
class TranslatorCacheTest extends TestCase
{
protected $tmpDir;
protected function setUp()
{
$this->tmpDir = sys_get_temp_dir().'/sf2_translation';
$this->deleteTmpDir();
}
protected function tearDown()
{
$this->deleteTmpDir();
}
protected 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);
}
/**
* @dataProvider runForDebugAndProduction
*/
public function testThatACacheIsUsed($debug)
{
$locale = 'any_locale';
$format = 'some_format';
$msgid = 'test';
// Prime the cache
$translator = new Translator($locale, null, $this->tmpDir, $debug);
$translator->addLoader($format, new ArrayLoader());
$translator->addResource($format, [$msgid => 'OK'], $locale);
$translator->trans($msgid);
// Try again and see we get a valid result whilst no loader can be used
$translator = new Translator($locale, null, $this->tmpDir, $debug);
$translator->addLoader($format, $this->createFailingLoader());
$translator->addResource($format, [$msgid => 'OK'], $locale);
$this->assertEquals('OK', $translator->trans($msgid), '-> caching does not work in '.($debug ? 'debug' : 'production'));
}
public function testCatalogueIsReloadedWhenResourcesAreNoLongerFresh()
{
/*
* The testThatACacheIsUsed() test showed that we don't need the loader as long as the cache
* is fresh.
*
* Now we add a Resource that is never fresh and make sure that the
* cache is discarded (the loader is called twice).
*
* We need to run this for debug=true only because in production the cache
* will never be revalidated.
*/
$locale = 'any_locale';
$format = 'some_format';
$msgid = 'test';
$catalogue = new MessageCatalogue($locale, []);
$catalogue->addResource(new StaleResource()); // better use a helper class than a mock, because it gets serialized in the cache and re-loaded
/** @var LoaderInterface|MockObject $loader */
$loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock();
$loader
->expects($this->exactly(2))
->method('load')
->willReturn($catalogue)
;
// 1st pass
$translator = new Translator($locale, null, $this->tmpDir, true);
$translator->addLoader($format, $loader);
$translator->addResource($format, null, $locale);
$translator->trans($msgid);
// 2nd pass
$translator = new Translator($locale, null, $this->tmpDir, true);
$translator->addLoader($format, $loader);
$translator->addResource($format, null, $locale);
$translator->trans($msgid);
}
/**
* @dataProvider runForDebugAndProduction
*/
public function testDifferentTranslatorsForSameLocaleDoNotOverwriteEachOthersCache($debug)
{
/*
* Similar to the previous test. After we used the second translator, make
* sure there's still a usable cache for the first one.
*/
$locale = 'any_locale';
$format = 'some_format';
$msgid = 'test';
// Create a Translator and prime its cache
$translator = new Translator($locale, null, $this->tmpDir, $debug);
$translator->addLoader($format, new ArrayLoader());
$translator->addResource($format, [$msgid => 'OK'], $locale);
$translator->trans($msgid);
// Create another Translator with a different catalogue for the same locale
$translator = new Translator($locale, null, $this->tmpDir, $debug);
$translator->addLoader($format, new ArrayLoader());
$translator->addResource($format, [$msgid => 'FAIL'], $locale);
$translator->trans($msgid);
// Now the first translator must still have a usable cache.
$translator = new Translator($locale, null, $this->tmpDir, $debug);
$translator->addLoader($format, $this->createFailingLoader());
$translator->addResource($format, [$msgid => 'OK'], $locale);
$this->assertEquals('OK', $translator->trans($msgid), '-> the cache was overwritten by another translator instance in '.($debug ? 'debug' : 'production'));
}
public function testGeneratedCacheFilesAreOnlyBelongRequestedLocales()
{
$translator = new Translator('a', null, $this->tmpDir);
$translator->setFallbackLocales(['b']);
$translator->trans('bar');
$cachedFiles = glob($this->tmpDir.'/*.php');
$this->assertCount(1, $cachedFiles);
}
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(['b']);
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', ['foo' => 'foo (a)'], 'a');
$translator->addResource('array', ['bar' => 'bar (b)'], 'b');
$this->assertEquals('bar (b)', $translator->trans('bar'));
// Remove fallback locale
$translator->setFallbackLocales([]);
$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', ['foo' => 'foo (a)'], 'a');
$translator->addResource('array', ['bar' => 'bar (b)'], 'b');
$this->assertEquals('bar', $translator->trans('bar'));
}
public function testPrimaryAndFallbackCataloguesContainTheSameMessagesRegardlessOfCaching()
{
/*
* 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 behavior.
*/
/*
* Create a translator that loads two catalogues for two different locales.
* The catalogues contain distinct sets of messages.
*/
$translator = new Translator('a', null, $this->tmpDir);
$translator->setFallbackLocales(['b']);
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', ['foo' => 'foo (a)'], 'a');
$translator->addResource('array', ['foo' => 'foo (b)'], 'b');
$translator->addResource('array', ['bar' => 'bar (b)'], 'b');
$catalogue = $translator->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?
*/
$translator = new Translator('a', null, $this->tmpDir);
$translator->setFallbackLocales(['b']);
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', ['foo' => 'foo (a)'], 'a');
$translator->addResource('array', ['foo' => 'foo (b)'], 'b');
$translator->addResource('array', ['bar' => 'bar (b)'], 'b');
$catalogue = $translator->getCatalogue('a');
$this->assertFalse($catalogue->defines('bar'));
$fallback = $catalogue->getFallbackCatalogue();
$this->assertTrue($fallback->defines('foo'));
}
public function testRefreshCacheWhenResourcesAreNoLongerFresh()
{
$resource = $this->getMockBuilder('Symfony\Component\Config\Resource\SelfCheckingResourceInterface')->getMock();
$loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock();
$resource->method('isFresh')->willReturn(false);
$loader
->expects($this->exactly(2))
->method('load')
->willReturn($this->getCatalogue('fr', [], [$resource]));
// prime the cache
$translator = new Translator('fr', null, $this->tmpDir, true);
$translator->addLoader('loader', $loader);
$translator->addResource('loader', 'foo', 'fr');
$translator->trans('foo');
// prime the cache second time
$translator = new Translator('fr', null, $this->tmpDir, true);
$translator->addLoader('loader', $loader);
$translator->addResource('loader', 'foo', 'fr');
$translator->trans('foo');
}
protected function getCatalogue($locale, $messages, $resources = [])
{
$catalogue = new MessageCatalogue($locale);
foreach ($messages as $key => $translation) {
$catalogue->set($key, $translation);
}
foreach ($resources as $resource) {
$catalogue->addResource($resource);
}
return $catalogue;
}
public function runForDebugAndProduction()
{
return [[true], [false]];
}
/**
* @return LoaderInterface
*/
private function createFailingLoader()
{
$loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock();
$loader
->expects($this->never())
->method('load');
return $loader;
}
}
class StaleResource implements SelfCheckingResourceInterface
{
public function isFresh($timestamp)
{
return false;
}
public function getResource()
{
}
public function __toString()
{
return '';
}
}

View File

@@ -0,0 +1,581 @@
<?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;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Exception\RuntimeException;
use Symfony\Component\Translation\Loader\ArrayLoader;
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\Translator;
class TranslatorTest extends TestCase
{
/**
* @dataProvider getInvalidLocalesTests
*/
public function testConstructorInvalidLocale($locale)
{
$this->expectException('Symfony\Component\Translation\Exception\InvalidArgumentException');
new Translator($locale);
}
/**
* @dataProvider getValidLocalesTests
*/
public function testConstructorValidLocale($locale)
{
$translator = new Translator($locale);
$this->assertEquals($locale, $translator->getLocale());
}
public function testConstructorWithoutLocale()
{
$translator = new Translator(null);
$this->assertNull($translator->getLocale());
}
public function testSetGetLocale()
{
$translator = new Translator('en');
$this->assertEquals('en', $translator->getLocale());
$translator->setLocale('fr');
$this->assertEquals('fr', $translator->getLocale());
}
/**
* @dataProvider getInvalidLocalesTests
*/
public function testSetInvalidLocale($locale)
{
$this->expectException('Symfony\Component\Translation\Exception\InvalidArgumentException');
$translator = new Translator('fr');
$translator->setLocale($locale);
}
/**
* @dataProvider getValidLocalesTests
*/
public function testSetValidLocale($locale)
{
$translator = new Translator($locale);
$translator->setLocale($locale);
$this->assertEquals($locale, $translator->getLocale());
}
public function testGetCatalogue()
{
$translator = new Translator('en');
$this->assertEquals(new MessageCatalogue('en'), $translator->getCatalogue());
$translator->setLocale('fr');
$this->assertEquals(new MessageCatalogue('fr'), $translator->getCatalogue('fr'));
}
public function testGetCatalogueReturnsConsolidatedCatalogue()
{
/*
* This will be useful once we refactor so that different domains will be loaded lazily (on-demand).
* In that case, getCatalogue() will probably have to load all missing domains in order to return
* one complete catalogue.
*/
$locale = 'whatever';
$translator = new Translator($locale);
$translator->addLoader('loader-a', new ArrayLoader());
$translator->addLoader('loader-b', new ArrayLoader());
$translator->addResource('loader-a', ['foo' => 'foofoo'], $locale, 'domain-a');
$translator->addResource('loader-b', ['bar' => 'foobar'], $locale, 'domain-b');
/*
* Test that we get a single catalogue comprising messages
* from different loaders and different domains
*/
$catalogue = $translator->getCatalogue($locale);
$this->assertTrue($catalogue->defines('foo', 'domain-a'));
$this->assertTrue($catalogue->defines('bar', 'domain-b'));
}
public function testSetFallbackLocales()
{
$translator = new Translator('en');
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', ['foo' => 'foofoo'], 'en');
$translator->addResource('array', ['bar' => 'foobar'], 'fr');
// force catalogue loading
$translator->trans('bar');
$translator->setFallbackLocales(['fr']);
$this->assertEquals('foobar', $translator->trans('bar'));
}
public function testSetFallbackLocalesMultiple()
{
$translator = new Translator('en');
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', ['foo' => 'foo (en)'], 'en');
$translator->addResource('array', ['bar' => 'bar (fr)'], 'fr');
// force catalogue loading
$translator->trans('bar');
$translator->setFallbackLocales(['fr_FR', 'fr']);
$this->assertEquals('bar (fr)', $translator->trans('bar'));
}
/**
* @dataProvider getInvalidLocalesTests
*/
public function testSetFallbackInvalidLocales($locale)
{
$this->expectException('Symfony\Component\Translation\Exception\InvalidArgumentException');
$translator = new Translator('fr');
$translator->setFallbackLocales(['fr', $locale]);
}
/**
* @dataProvider getValidLocalesTests
*/
public function testSetFallbackValidLocales($locale)
{
$translator = new Translator($locale);
$translator->setFallbackLocales(['fr', $locale]);
// no assertion. this method just asserts that no exception is thrown
$this->addToAssertionCount(1);
}
public function testTransWithFallbackLocale()
{
$translator = new Translator('fr_FR');
$translator->setFallbackLocales(['en']);
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', ['bar' => 'foobar'], 'en');
$this->assertEquals('foobar', $translator->trans('bar'));
}
/**
* @dataProvider getInvalidLocalesTests
*/
public function testAddResourceInvalidLocales($locale)
{
$this->expectException('Symfony\Component\Translation\Exception\InvalidArgumentException');
$translator = new Translator('fr');
$translator->addResource('array', ['foo' => 'foofoo'], $locale);
}
/**
* @dataProvider getValidLocalesTests
*/
public function testAddResourceValidLocales($locale)
{
$translator = new Translator('fr');
$translator->addResource('array', ['foo' => 'foofoo'], $locale);
// no assertion. this method just asserts that no exception is thrown
$this->addToAssertionCount(1);
}
public function testAddResourceAfterTrans()
{
$translator = new Translator('fr');
$translator->addLoader('array', new ArrayLoader());
$translator->setFallbackLocales(['en']);
$translator->addResource('array', ['foo' => 'foofoo'], 'en');
$this->assertEquals('foofoo', $translator->trans('foo'));
$translator->addResource('array', ['bar' => 'foobar'], 'en');
$this->assertEquals('foobar', $translator->trans('bar'));
}
/**
* @dataProvider getTransFileTests
*/
public function testTransWithoutFallbackLocaleFile($format, $loader)
{
$this->expectException('Symfony\Component\Translation\Exception\NotFoundResourceException');
$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', [], 'resources'));
}
/**
* @dataProvider getFallbackLocales
*/
public function testTransWithFallbackLocaleBis($expectedLocale, $locale)
{
$translator = new Translator($locale);
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', ['foo' => 'foofoo'], $locale);
$translator->addResource('array', ['bar' => 'foobar'], $expectedLocale);
$this->assertEquals('foobar', $translator->trans('bar'));
}
public function getFallbackLocales()
{
$locales = [
['en', 'en_US'],
['en', 'en-US'],
['sl_Latn_IT', 'sl_Latn_IT_nedis'],
['sl_Latn', 'sl_Latn_IT'],
];
if (\function_exists('locale_parse')) {
$locales[] = ['sl_Latn_IT', 'sl-Latn-IT-nedis'];
$locales[] = ['sl_Latn', 'sl-Latn-IT'];
} else {
$locales[] = ['sl-Latn-IT', 'sl-Latn-IT-nedis'];
$locales[] = ['sl-Latn', 'sl-Latn-IT'];
}
return $locales;
}
public function testTransWithFallbackLocaleTer()
{
$translator = new Translator('fr_FR');
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', ['foo' => 'foo (en_US)'], 'en_US');
$translator->addResource('array', ['bar' => 'bar (en)'], 'en');
$translator->setFallbackLocales(['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(['en']);
$translator->addLoader('array', new ArrayLoader());
$this->assertEquals('non-existent', $translator->trans('non-existent'));
}
public function testWhenAResourceHasNoRegisteredLoader()
{
$this->expectException('Symfony\Component\Translation\Exception\RuntimeException');
$translator = new Translator('en');
$translator->addResource('array', ['foo' => 'foofoo'], 'en');
$translator->trans('foo');
}
public function testNestedFallbackCatalogueWhenUsingMultipleLocales()
{
$translator = new Translator('fr');
$translator->setFallbackLocales(['ru', 'en']);
$translator->getCatalogue('fr');
$this->assertNotNull($translator->getCatalogue('ru')->getFallbackCatalogue());
}
public function testFallbackCatalogueResources()
{
$translator = new Translator('en_GB');
$translator->addLoader('yml', new \Symfony\Component\Translation\Loader\YamlFileLoader());
$translator->addResource('yml', __DIR__.'/fixtures/empty.yml', 'en_GB');
$translator->addResource('yml', __DIR__.'/fixtures/resources.yml', 'en');
// force catalogue loading
$this->assertEquals('bar', $translator->trans('foo', []));
$resources = $translator->getCatalogue('en')->getResources();
$this->assertCount(1, $resources);
$this->assertContainsEquals(__DIR__.\DIRECTORY_SEPARATOR.'fixtures'.\DIRECTORY_SEPARATOR.'resources.yml', $resources);
$resources = $translator->getCatalogue('en_GB')->getResources();
$this->assertCount(2, $resources);
$this->assertContainsEquals(__DIR__.\DIRECTORY_SEPARATOR.'fixtures'.\DIRECTORY_SEPARATOR.'empty.yml', $resources);
$this->assertContainsEquals(__DIR__.\DIRECTORY_SEPARATOR.'fixtures'.\DIRECTORY_SEPARATOR.'resources.yml', $resources);
}
/**
* @dataProvider getTransTests
*/
public function testTrans($expected, $id, $translation, $parameters, $locale, $domain)
{
$translator = new Translator('en');
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', [(string) $id => $translation], $locale, $domain);
$this->assertEquals($expected, $translator->trans($id, $parameters, $domain, $locale));
}
/**
* @dataProvider getInvalidLocalesTests
*/
public function testTransInvalidLocale($locale)
{
$this->expectException('Symfony\Component\Translation\Exception\InvalidArgumentException');
$translator = new Translator('en');
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', ['foo' => 'foofoo'], 'en');
$translator->trans('foo', [], '', $locale);
}
/**
* @dataProvider getValidLocalesTests
*/
public function testTransValidLocale($locale)
{
$translator = new Translator($locale);
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', ['test' => 'OK'], $locale);
$this->assertEquals('OK', $translator->trans('test'));
$this->assertEquals('OK', $translator->trans('test', [], null, $locale));
}
/**
* @dataProvider getFlattenedTransTests
*/
public function testFlattenedTrans($expected, $messages, $id)
{
$translator = new Translator('en');
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', $messages, 'fr', '');
$this->assertEquals($expected, $translator->trans($id, [], '', 'fr'));
}
/**
* @dataProvider getTransChoiceTests
*/
public function testTransChoice($expected, $id, $translation, $number, $parameters, $locale, $domain)
{
$translator = new Translator('en');
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', [(string) $id => $translation], $locale, $domain);
$this->assertEquals($expected, $translator->transChoice($id, $number, $parameters, $domain, $locale));
}
/**
* @dataProvider getInvalidLocalesTests
*/
public function testTransChoiceInvalidLocale($locale)
{
$this->expectException('Symfony\Component\Translation\Exception\InvalidArgumentException');
$translator = new Translator('en');
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', ['foo' => 'foofoo'], 'en');
$translator->transChoice('foo', 1, [], '', $locale);
}
/**
* @dataProvider getValidLocalesTests
*/
public function testTransChoiceValidLocale($locale)
{
$translator = new Translator('en');
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', ['foo' => 'foofoo'], 'en');
$translator->transChoice('foo', 1, [], '', $locale);
// no assertion. this method just asserts that no exception is thrown
$this->addToAssertionCount(1);
}
public function getTransFileTests()
{
return [
['csv', 'CsvFileLoader'],
['ini', 'IniFileLoader'],
['mo', 'MoFileLoader'],
['po', 'PoFileLoader'],
['php', 'PhpFileLoader'],
['ts', 'QtFileLoader'],
['xlf', 'XliffFileLoader'],
['yml', 'YamlFileLoader'],
['json', 'JsonFileLoader'],
];
}
public function getTransTests()
{
return [
['Symfony est super !', 'Symfony is great!', 'Symfony est super !', [], 'fr', ''],
['Symfony est awesome !', 'Symfony is %what%!', 'Symfony est %what% !', ['%what%' => 'awesome'], 'fr', ''],
['Symfony est super !', new StringClass('Symfony is great!'), 'Symfony est super !', [], 'fr', ''],
];
}
public function getFlattenedTransTests()
{
$messages = [
'symfony' => [
'is' => [
'great' => 'Symfony est super!',
],
],
'foo' => [
'bar' => [
'baz' => 'Foo Bar Baz',
],
'baz' => 'Foo Baz',
],
];
return [
['Symfony est super!', $messages, 'symfony.is.great'],
['Foo Bar Baz', $messages, 'foo.bar.baz'],
['Foo Baz', $messages, 'foo.baz'],
];
}
public function getTransChoiceTests()
{
return [
['Il y a 0 pomme', '{0} There are no appless|{1} There is one apple|]1,Inf] There is %count% apples', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 0, [], 'fr', ''],
['Il y a 1 pomme', '{0} There are no appless|{1} There is one apple|]1,Inf] There is %count% apples', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 1, [], 'fr', ''],
['Il y a 10 pommes', '{0} There are no appless|{1} There is one apple|]1,Inf] There is %count% apples', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 10, [], 'fr', ''],
['Il y a 0 pomme', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 0, [], 'fr', ''],
['Il y a 1 pomme', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 1, [], 'fr', ''],
['Il y a 10 pommes', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 10, [], 'fr', ''],
['Il y a 0 pomme', 'one: There is one apple|more: There is %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 0, [], 'fr', ''],
['Il y a 1 pomme', 'one: There is one apple|more: There is %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 1, [], 'fr', ''],
['Il y a 10 pommes', 'one: There is one apple|more: There is %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 10, [], 'fr', ''],
['Il n\'y a aucune pomme', '{0} There are no apples|one: There is one apple|more: There is %count% apples', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 0, [], 'fr', ''],
['Il y a 1 pomme', '{0} There are no apples|one: There is one apple|more: There is %count% apples', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 1, [], 'fr', ''],
['Il y a 10 pommes', '{0} There are no apples|one: There is one apple|more: There is %count% apples', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 10, [], 'fr', ''],
['Il y a 0 pomme', new StringClass('{0} There are no appless|{1} There is one apple|]1,Inf] There is %count% apples'), '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 0, [], 'fr', ''],
// Override %count% with a custom value
['Il y a quelques pommes', 'one: There is one apple|more: There are %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 2, ['%count%' => 'quelques'], 'fr', ''],
];
}
public function getInvalidLocalesTests()
{
return [
['fr FR'],
['français'],
['fr+en'],
['utf#8'],
['fr&en'],
['fr~FR'],
[' fr'],
['fr '],
['fr*'],
['fr/FR'],
['fr\\FR'],
];
}
public function getValidLocalesTests()
{
return [
[''],
[null],
['fr'],
['francais'],
['FR'],
['frFR'],
['fr-FR'],
['fr_FR'],
['fr.FR'],
['fr-FR.UTF8'],
['sr@latin'],
];
}
public function testTransChoiceFallback()
{
$translator = new Translator('ru');
$translator->setFallbackLocales(['en']);
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', ['some_message2' => 'one thing|%count% things'], 'en');
$this->assertEquals('10 things', $translator->transChoice('some_message2', 10, ['%count%' => 10]));
}
public function testTransChoiceFallbackBis()
{
$translator = new Translator('ru');
$translator->setFallbackLocales(['en_US', 'en']);
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', ['some_message2' => 'one thing|%count% things'], 'en_US');
$this->assertEquals('10 things', $translator->transChoice('some_message2', 10, ['%count%' => 10]));
}
public function testTransChoiceFallbackWithNoTranslation()
{
$translator = new Translator('ru');
$translator->setFallbackLocales(['en']);
$translator->addLoader('array', new ArrayLoader());
// 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, ['%count%' => 10]));
}
public function testMissingLoaderForResourceError()
{
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('No loader is registered for the "twig" format when loading the "messages.en.twig" resource.');
$translator = new Translator('en');
$translator->addResource('twig', 'messages.en.twig', 'en');
$translator->getCatalogue('en');
}
}
class StringClass
{
protected $str;
public function __construct($str)
{
$this->str = $str;
}
public function __toString()
{
return $this->str;
}
}

View File

@@ -0,0 +1,74 @@
<?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 PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Util\ArrayConverter;
class ArrayConverterTest extends TestCase
{
/**
* @dataProvider messagesData
*/
public function testDump($input, $expectedOutput)
{
$this->assertEquals($expectedOutput, ArrayConverter::expandToTree($input));
}
public function messagesData()
{
return [
[
// input
[
'foo1' => 'bar',
'foo.bar' => 'value',
],
// expected output
[
'foo1' => 'bar',
'foo' => ['bar' => 'value'],
],
],
[
// input
[
'foo.bar' => 'value1',
'foo.bar.test' => 'value2',
],
// expected output
[
'foo' => [
'bar' => 'value1',
'bar.test' => 'value2',
],
],
],
[
// input
[
'foo.level2.level3.level4' => 'value1',
'foo.level2' => 'value2',
'foo.bar' => 'value3',
],
// expected output
[
'foo' => [
'level2' => 'value2',
'level2.level3.level4' => 'value1',
'bar' => 'value3',
],
],
],
];
}
}

View File

@@ -0,0 +1,82 @@
<?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\Writer;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Dumper\DumperInterface;
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\Writer\TranslationWriter;
class TranslationWriterTest extends TestCase
{
/**
* @group legacy
* @expectedDeprecation The "Symfony\Component\Translation\Writer\TranslationWriter::writeTranslations()" method is deprecated since Symfony 3.4 and will be removed in 4.0. Use write() instead.
*/
public function testWriteTranslations()
{
$dumper = $this->getMockBuilder('Symfony\Component\Translation\Dumper\DumperInterface')->getMock();
$dumper
->expects($this->once())
->method('dump');
$writer = new TranslationWriter();
$writer->addDumper('test', $dumper);
$writer->writeTranslations(new MessageCatalogue('en'), 'test');
}
public function testWrite()
{
$dumper = $this->getMockBuilder('Symfony\Component\Translation\Dumper\DumperInterface')->getMock();
$dumper
->expects($this->once())
->method('dump');
$writer = new TranslationWriter();
$writer->addDumper('test', $dumper);
$writer->write(new MessageCatalogue([]), 'test');
}
public function testDisableBackup()
{
$nonBackupDumper = new NonBackupDumper();
$backupDumper = new BackupDumper();
$writer = new TranslationWriter();
$writer->addDumper('non_backup', $nonBackupDumper);
$writer->addDumper('backup', $backupDumper);
$writer->disableBackup();
$this->assertFalse($backupDumper->backup, 'backup can be disabled if setBackup() method does exist');
}
}
class NonBackupDumper implements DumperInterface
{
public function dump(MessageCatalogue $messages, $options = [])
{
}
}
class BackupDumper implements DumperInterface
{
public $backup = true;
public function dump(MessageCatalogue $messages, $options = [])
{
}
public function setBackup($backup)
{
$this->backup = $backup;
}
}

Binary file not shown.

View File

@@ -0,0 +1,3 @@
msgid "foo"
msgstr ""

View File

View File

View File

View File

View File

View File

View File

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="1" resname="foo">
<source>foo</source>
<target>b<>r</target>
<note>b<>z</note>
</trans-unit>
<trans-unit id="2" resname="bar">
<source>bar</source>
<target>f<><66></target>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -0,0 +1,10 @@
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: en\n"
msgid "escaped \"foo\""
msgid_plural "escaped \"foos\""
msgstr[0] "escaped \"bar\""
msgstr[1] "escaped \"bars\""

View File

@@ -0,0 +1,8 @@
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: en\n"
msgid "escaped \"foo\""
msgstr "escaped \"bar\""

View File

@@ -0,0 +1,59 @@
This template is used for translation message extraction tests
<?php echo $view['translator']->trans('single-quoted key'); ?>
<?php echo $view['translator']->trans('double-quoted key'); ?>
<?php echo $view['translator']->trans(<<<EOF
heredoc key
EOF
); ?>
<?php echo $view['translator']->trans(<<<'EOF'
nowdoc key
EOF
); ?>
<?php echo $view['translator']->trans(
"double-quoted key with whitespace and escaped \$\n\" sequences"
); ?>
<?php echo $view['translator']->trans(
'single-quoted key with whitespace and nonescaped \$\n\' sequences'
); ?>
<?php echo $view['translator']->trans(<<<EOF
heredoc key with whitespace and escaped \$\n sequences
EOF
); ?>
<?php echo $view['translator']->trans(<<<'EOF'
nowdoc key with whitespace and nonescaped \$\n sequences
EOF
); ?>
<?php echo $view['translator']->trans('single-quoted key with "quote mark at the end"'); ?>
<?php echo $view['translator']->transChoice(
'{0} There is no apples|{1} There is one apple|]1,Inf[ There are %count% apples',
10,
['%count%' => 10]
); ?>
<?php echo $view['translator']->trans('concatenated'.' message'.<<<EOF
with heredoc
EOF
.<<<'EOF'
and nowdoc
EOF
); ?>
<?php echo $view['translator']->trans('other-domain-test-no-params-short-array', [], 'not_messages'); ?>
<?php echo $view['translator']->trans('other-domain-test-no-params-long-array', [], 'not_messages'); ?>
<?php echo $view['translator']->trans('other-domain-test-params-short-array', ['foo' => 'bar'], 'not_messages'); ?>
<?php echo $view['translator']->trans('other-domain-test-params-long-array', ['foo' => 'bar'], 'not_messages'); ?>
<?php echo $view['translator']->transChoice('other-domain-test-trans-choice-short-array-%count%', 10, ['%count%' => 10], 'not_messages'); ?>
<?php echo $view['translator']->transChoice('other-domain-test-trans-choice-long-array-%count%', 10, ['%count%' => 10], 'not_messages'); ?>
<?php echo $view['translator']->trans('typecast', ['a' => (int) '123'], 'not_messages'); ?>
<?php echo $view['translator']->transChoice('msg1', 10 + 1, [], 'not_messages'); ?>
<?php echo $view['translator']->transChoice('msg2', ceil(4.5), [], 'not_messages'); ?>
<?php echo $view['translator']->trans('default domain', [], null); ?>

View File

@@ -0,0 +1,10 @@
#, php-format
msgid "foo1"
msgstr "bar1"
#, fuzzy, php-format
msgid "foo2"
msgstr "fuzzy bar2"
msgid "foo3"
msgstr "bar3"

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="1">
<source>foo</source>
<target>bar
</trans-unit>
<trans-unit id="2">
<source>extra</source>
</trans-unit>
<trans-unit id="3">
<source>key</source>
<target></target>
</trans-unit>
<trans-unit id="4">
<source>test</source>
<target>with</target>
<note>note</note>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -0,0 +1,3 @@
{
"foo" "bar"
}

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,4 @@
msgid "foo"
msgid_plural "foos"
msgstr[3] "bars"
msgstr[1] "bar"

View File

@@ -0,0 +1,11 @@
<?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>
<source>foo</source>
<target>bar</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -0,0 +1 @@
foo

Binary file not shown.

View File

@@ -0,0 +1,7 @@
msgid "foo"
msgid_plural "foos"
msgstr[0] "bar"
msgstr[1] "bars"
msgid "{0} no foos|one foo|%count% foos"
msgstr "{0} no bars|one bar|%count% bars"

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="1" resname="foo">
<source></source>
<target>bar</target>
</trans-unit>
<trans-unit id="2" resname="bar">
<source>bar source</source>
<target>baz</target>
</trans-unit>
<trans-unit id="3">
<source>baz</source>
<target>foo</target>
</trans-unit>
<trans-unit id="4" resname="qux">
<source>qux source</source>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -0,0 +1 @@
XXX

Binary file not shown.

View File

@@ -0,0 +1,3 @@
en{
symfony{"Symfony is great"}
}

Binary file not shown.

View File

@@ -0,0 +1,3 @@
fr{
symfony{"Symfony est génial"}
}

View File

@@ -0,0 +1,2 @@
en.res
fr.res

Binary file not shown.

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:2.0" version="2.0" srcLang="fr-FR" trgLang="en-US">
<file id="messages.en_US">
<unit id="LCa0a2j">
<segment>
<source>foo</source>
<target>bar</target>
</segment>
</unit>
<unit id="LHDhK3o">
<segment>
<source>key</source>
<target order="1"></target>
</segment>
</unit>
<unit id="2DA_bnh">
<segment>
<source>key.with.cdata</source>
<target><![CDATA[<source> & <target>]]></target>
</segment>
</unit>
</file>
</xliff>

View File

@@ -0,0 +1,17 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:2.0" version="2.0" srcLang="en-US" trgLang="en-US">
<file id="f1">
<unit id="1">
<notes>
<note category="processed">true</note>
</notes>
<segment id="id-foo">
<source>foo</source>
<target>foo (translated)</target>
</segment>
<segment id="id-bar">
<source>bar</source>
<target>bar (translated)</target>
</segment>
</unit>
</file>
</xliff>

View File

@@ -0,0 +1,25 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:2.0" version="2.0" srcLang="en-US" trgLang="ja-JP">
<file id="f1" original="Graphic Example.psd">
<skeleton href="Graphic Example.psd.skl"/>
<unit id="1">
<segment>
<source>Quetzal</source>
<target>Quetzal</target>
</segment>
</unit>
<group id="1">
<unit id="2">
<segment>
<source>foo</source>
<target>XLIFF 文書を編集、または処理 するアプリケーションです。</target>
</segment>
</unit>
<unit id="3">
<segment>
<source>bar</source>
<target order="1">XLIFF データ・マネージャ</target>
</segment>
</unit>
</group>
</file>
</xliff>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="fr-FR" target-language="en-US" datatype="plaintext" original="file.ext">
<header>
<tool tool-id="symfony" tool-name="Symfony"/>
</header>
<body>
<trans-unit id="LCa0a2j" resname="foo">
<source>foo</source>
<target>bar</target>
<note priority="1" from="bar">baz</note>
</trans-unit>
<trans-unit id="LHDhK3o" resname="key">
<source>key</source>
<target></target>
<note>baz</note>
<note>qux</note>
</trans-unit>
<trans-unit id="2DA_bnh" resname="key.with.cdata">
<source>key.with.cdata</source>
<target><![CDATA[<source> & <target>]]></target>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:2.0" version="2.0" srcLang="fr-FR" trgLang="en-US">
<file id="messages.en_US">
<unit id="LCa0a2j">
<notes>
<note category="state">new</note>
<note category="approved">true</note>
<note category="section" priority="1">user login</note>
</notes>
<segment>
<source>foo</source>
<target>bar</target>
</segment>
</unit>
<unit id="uqWglk0">
<notes>
<note id="x">x_content</note>
<note appliesTo="target" category="quality">Fuzzy</note>
</notes>
<segment>
<source>baz</source>
<target>biz</target>
</segment>
</unit>
</file>
</xliff>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="fr-FR" target-language="en-US" datatype="plaintext" original="file.ext">
<header>
<tool tool-id="symfony" tool-name="Symfony"/>
</header>
<body>
<trans-unit id="LCa0a2j" resname="foo">
<source>foo</source>
<target state="needs-translation">bar</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en-US" target-language="en-US" datatype="plaintext" original="file.ext">
<header>
<tool tool-id="foo" tool-name="foo" tool-version="0.0" tool-company="Foo"/>
</header>
<body>
<trans-unit id="LCa0a2j" resname="foo">
<source>foo</source>
<target>bar</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -0,0 +1,4 @@
"foo"; "bar"
#"bar"; "foo"
"incorrect"; "number"; "columns"; "will"; "be"; "ignored"
"incorrect"
Can't render this file because it contains an unexpected character in line 2 and column 2.

View File

@@ -0,0 +1 @@
{"foo":"\u0022bar\u0022"}

View File

@@ -0,0 +1 @@
foo="bar"

View File

@@ -0,0 +1,3 @@
{
"foo": "bar"
}

Binary file not shown.

View File

@@ -0,0 +1,5 @@
<?php
return array (
'foo' => 'bar',
);

View File

@@ -0,0 +1,11 @@
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: en\n"
msgid "foo"
msgstr "bar"
msgid "bar"
msgstr "foo"

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<TS>
<context>
<name>resources</name>
<message>
<source>foo</source>
<translation>bar</translation>
</message>
</context>
</TS>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="1">
<source>foo</source>
<target>bar</target>
</trans-unit>
<trans-unit id="2">
<source>extra</source>
</trans-unit>
<trans-unit id="3">
<source>key</source>
<target></target>
</trans-unit>
<trans-unit id="4">
<source>test</source>
<target>with</target>
<note>note</note>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -0,0 +1 @@
foo: bar

View File

@@ -0,0 +1,4 @@
foo;bar
bar;"foo
foo"
"foo;foo";bar
1 foo bar
2 bar foo foo
3 foo;foo bar

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="1">
<source>foo</source>
<target state="translated">bar</target>
</trans-unit>
<trans-unit id="2">
<source>extra</source>
<target state="needs-translation">bar</target>
</trans-unit>
<trans-unit id="3">
<source>key</source>
<target></target>
<note>baz</note>
<note priority="2" from="bar">qux</note>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0"?>
<!DOCTYPE foo>
<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>foo</source>
<target>bar</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="1">
<source>foo</source>
<target>bar</target>
<note priority="1">foo</note>
</trans-unit>
<trans-unit id="2">
<source>extra</source>
<note from="foo">bar</note>
</trans-unit>
<trans-unit id="123">
<source>key</source>
<target></target>
<note>baz</note>
<note priority="2" from="bar">qux</note>
</trans-unit>
</body>
</file>
</xliff>