1
0

提交代码

This commit is contained in:
2020-08-06 14:50:07 +08:00
parent 9d0d5f4be9
commit d7a848c824
11299 changed files with 1321854 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,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\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 testGetResultFromIntlDomain()
{
$this->assertEquals(
new MessageCatalogue('en', [
'messages' => ['a' => 'old_a', 'b' => 'old_b'],
'messages+intl-icu' => ['d' => 'old_d', 'c' => 'new_c'],
]),
$this->createOperation(
new MessageCatalogue('en', ['messages' => ['a' => 'old_a', 'b' => 'old_b'], 'messages+intl-icu' => ['d' => 'old_d']]),
new MessageCatalogue('en', ['messages+intl-icu' => ['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,96 @@
<?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 testGetResultFromIntlDomain()
{
$this->assertEquals(
new MessageCatalogue('en', [
'messages' => ['a' => 'old_a'],
'messages+intl-icu' => ['c' => 'new_c'],
]),
$this->createOperation(
new MessageCatalogue('en', ['messages' => ['a' => 'old_a'], 'messages+intl-icu' => ['b' => 'old_b']]),
new MessageCatalogue('en', ['messages' => ['a' => 'new_a'], 'messages+intl-icu' => ['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,205 @@
<?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\Command;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\Translation\Command\XliffLintCommand;
/**
* Tests the XliffLintCommand.
*
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
*/
class XliffLintCommandTest extends TestCase
{
private $files;
public function testLintCorrectFile()
{
$tester = $this->createCommandTester();
$filename = $this->createFile();
$tester->execute(
['filename' => $filename],
['verbosity' => OutputInterface::VERBOSITY_VERBOSE, 'decorated' => false]
);
$this->assertEquals(0, $tester->getStatusCode(), 'Returns 0 in case of success');
$this->assertStringContainsString('OK', trim($tester->getDisplay()));
}
public function testLintCorrectFiles()
{
$tester = $this->createCommandTester();
$filename1 = $this->createFile();
$filename2 = $this->createFile();
$tester->execute(
['filename' => [$filename1, $filename2]],
['verbosity' => OutputInterface::VERBOSITY_VERBOSE, 'decorated' => false]
);
$this->assertEquals(0, $tester->getStatusCode(), 'Returns 0 in case of success');
$this->assertStringContainsString('OK', trim($tester->getDisplay()));
}
/**
* @dataProvider provideStrictFilenames
*/
public function testStrictFilenames($requireStrictFileNames, $fileNamePattern, $targetLanguage, $mustFail)
{
$tester = $this->createCommandTester($requireStrictFileNames);
$filename = $this->createFile('note', $targetLanguage, $fileNamePattern);
$tester->execute(
['filename' => $filename],
['verbosity' => OutputInterface::VERBOSITY_VERBOSE, 'decorated' => false]
);
$this->assertEquals($mustFail ? 1 : 0, $tester->getStatusCode());
$this->assertStringContainsString($mustFail ? '[WARNING] 0 XLIFF files have valid syntax and 1 contain errors.' : '[OK] All 1 XLIFF files contain valid syntax.', $tester->getDisplay());
}
public function testLintIncorrectXmlSyntax()
{
$tester = $this->createCommandTester();
$filename = $this->createFile('note <target>');
$tester->execute(['filename' => $filename], ['decorated' => false]);
$this->assertEquals(1, $tester->getStatusCode(), 'Returns 1 in case of error');
$this->assertStringContainsString('Opening and ending tag mismatch: target line 6 and source', trim($tester->getDisplay()));
}
public function testLintIncorrectTargetLanguage()
{
$tester = $this->createCommandTester();
$filename = $this->createFile('note', 'es');
$tester->execute(['filename' => $filename], ['decorated' => false]);
$this->assertEquals(1, $tester->getStatusCode(), 'Returns 1 in case of error');
$this->assertStringContainsString('There is a mismatch between the language included in the file name ("messages.en.xlf") and the "es" value used in the "target-language" attribute of the file.', trim($tester->getDisplay()));
}
public function testLintTargetLanguageIsCaseInsensitive()
{
$tester = $this->createCommandTester();
$filename = $this->createFile('note', 'zh-cn', 'messages.zh_CN.xlf');
$tester->execute(['filename' => $filename], ['decorated' => false]);
$this->assertEquals(0, $tester->getStatusCode());
$this->assertStringContainsString('[OK] All 1 XLIFF files contain valid syntax.', trim($tester->getDisplay()));
}
public function testLintFileNotReadable()
{
$this->expectException('RuntimeException');
$tester = $this->createCommandTester();
$filename = $this->createFile();
unlink($filename);
$tester->execute(['filename' => $filename], ['decorated' => false]);
}
public function testGetHelp()
{
$command = new XliffLintCommand();
$expected = <<<EOF
Or of a whole directory:
<info>php %command.full_name% dirname</info>
<info>php %command.full_name% dirname --format=json</info>
EOF;
$this->assertStringContainsString($expected, $command->getHelp());
}
/**
* @return string Path to the new file
*/
private function createFile($sourceContent = 'note', $targetLanguage = 'en', $fileNamePattern = 'messages.%locale%.xlf')
{
$xliffContent = <<<XLIFF
<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" target-language="$targetLanguage" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="note">
<source>$sourceContent</source>
<target>NOTE</target>
</trans-unit>
</body>
</file>
</xliff>
XLIFF;
$filename = sprintf('%s/translation-xliff-lint-test/%s', sys_get_temp_dir(), str_replace('%locale%', 'en', $fileNamePattern));
file_put_contents($filename, $xliffContent);
$this->files[] = $filename;
return $filename;
}
/**
* @return CommandTester
*/
private function createCommandTester($requireStrictFileNames = true, $application = null)
{
if (!$application) {
$application = new Application();
$application->add(new XliffLintCommand(null, null, null, $requireStrictFileNames));
}
$command = $application->find('lint:xliff');
if ($application) {
$command->setApplication($application);
}
return new CommandTester($command);
}
protected function setUp(): void
{
$this->files = [];
@mkdir(sys_get_temp_dir().'/translation-xliff-lint-test');
}
protected function tearDown(): void
{
foreach ($this->files as $file) {
if (file_exists($file)) {
unlink($file);
}
}
rmdir(sys_get_temp_dir().'/translation-xliff-lint-test');
}
public function provideStrictFilenames()
{
yield [false, 'messages.%locale%.xlf', 'en', false];
yield [false, 'messages.%locale%.xlf', 'es', true];
yield [false, '%locale%.messages.xlf', 'en', false];
yield [false, '%locale%.messages.xlf', 'es', true];
yield [true, 'messages.%locale%.xlf', 'en', false];
yield [true, 'messages.%locale%.xlf', 'es', true];
yield [true, '%locale%.messages.xlf', 'en', true];
yield [true, '%locale%.messages.xlf', 'es', true];
}
}

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(): void
{
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,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\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->trans('choice', ['%count%' => 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' => ['%count%' => 0],
'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());
}
/**
* @group legacy
*/
public function testCollectMessagesTransChoice()
{
$collector = $this->createCollector();
$collector->setFallbackLocales(['fr', 'ru']);
$collector->transChoice('choice', 0);
$expectedMessages = [];
$expectedMessages[] = [
'id' => 'choice',
'translation' => 'choice',
'locale' => 'en',
'fallbackLocale' => null,
'domain' => 'messages',
'state' => DataCollectorTranslator::MESSAGE_MISSING,
'parameters' => ['%count%' => 0],
'transChoiceNumber' => 0,
];
$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,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\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));
}
public function testValidCommandsViewPathsArgument()
{
$container = new ContainerBuilder();
$container->register('translator.default')
->setArguments([null, null, null, null])
;
$debugCommand = $container->register('console.command.translation_debug')
->setArguments([null, null, null, null, null, [], []])
;
$updateCommand = $container->register('console.command.translation_update')
->setArguments([null, null, null, null, null, null, [], []])
;
$container->register('twig.template_iterator')
->setArguments([null, null, ['other/templates' => null, 'tpl' => 'App']])
;
$container->setParameter('twig.default_path', 'templates');
$pass = new TranslatorPass('translator.default');
$pass->process($container);
$expectedViewPaths = ['other/templates', 'tpl'];
$this->assertSame('templates', $debugCommand->getArgument(4));
$this->assertSame('templates', $updateCommand->getArgument(5));
$this->assertSame($expectedViewPaths, $debugCommand->getArgument(6));
$this->assertSame($expectedViewPaths, $updateCommand->getArgument(7));
}
public function testCommandsViewPathsArgumentsAreIgnoredWithOldServiceDefinitions()
{
$container = new ContainerBuilder();
$container->register('translator.default')
->setArguments([null, null, null, null])
;
$debugCommand = $container->register('console.command.translation_debug')
->setArguments([
new Reference('translator'),
new Reference('translation.reader'),
new Reference('translation.extractor'),
'%translator.default_path%',
null,
])
;
$updateCommand = $container->register('console.command.translation_update')
->setArguments([
new Reference('translation.writer'),
new Reference('translation.reader'),
new Reference('translation.extractor'),
'%kernel.default_locale%',
'%translator.default_path%',
null,
])
;
$container->register('twig.template_iterator')
->setArguments([null, null, ['other/templates' => null, 'tpl' => 'App']])
;
$container->setParameter('twig.default_path', 'templates');
$pass = new TranslatorPass('translator.default');
$pass->process($container);
$this->assertSame('templates', $debugCommand->getArgument(4));
$this->assertSame('templates', $updateCommand->getArgument(5));
}
}

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\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\DependencyInjection\ServiceLocator;
use Symfony\Component\Translation\DependencyInjection\TranslatorPathsPass;
use Symfony\Component\Translation\Tests\DependencyInjection\fixtures\ControllerArguments;
use Symfony\Component\Translation\Tests\DependencyInjection\fixtures\ServiceArguments;
use Symfony\Component\Translation\Tests\DependencyInjection\fixtures\ServiceMethodCalls;
use Symfony\Component\Translation\Tests\DependencyInjection\fixtures\ServiceProperties;
use Symfony\Component\Translation\Tests\DependencyInjection\fixtures\ServiceSubscriber;
class TranslationPathsPassTest extends TestCase
{
public function testProcess()
{
$container = new ContainerBuilder();
$container->register('translator');
$debugCommand = $container->register('console.command.translation_debug')
->setArguments([null, null, null, null, null, [], []])
;
$updateCommand = $container->register('console.command.translation_update')
->setArguments([null, null, null, null, null, null, [], []])
;
$container->register(ControllerArguments::class, ControllerArguments::class)
->setTags(['controller.service_arguments'])
;
$container->register(ServiceArguments::class, ServiceArguments::class)
->setArguments([new Reference('translator')])
;
$container->register(ServiceProperties::class, ServiceProperties::class)
->setProperties([new Reference('translator')])
;
$container->register(ServiceMethodCalls::class, ServiceMethodCalls::class)
->setMethodCalls([['setTranslator', [new Reference('translator')]]])
;
$container->register('service_rc')
->setArguments([new Definition(), new Reference(ServiceMethodCalls::class)])
;
$serviceLocator1 = $container->register('.service_locator.foo', ServiceLocator::class)
->setArguments([new ServiceClosureArgument(new Reference('translator'))])
;
$serviceLocator2 = (new Definition(ServiceLocator::class))
->setArguments([ServiceSubscriber::class, new Reference('service_container')])
->setFactory([$serviceLocator1, 'withContext'])
;
$container->register('service_subscriber', ServiceSubscriber::class)
->setArguments([$serviceLocator2])
;
$container->register('.service_locator.bar', ServiceLocator::class)
->setArguments([[
ControllerArguments::class.'::index' => new ServiceClosureArgument(new Reference('.service_locator.foo')),
ControllerArguments::class.'::__invoke' => new ServiceClosureArgument(new Reference('.service_locator.foo')),
ControllerArguments::class => new ServiceClosureArgument(new Reference('.service_locator.foo')),
]])
;
$container->register('argument_resolver.service')
->setArguments([new Reference('.service_locator.bar')])
;
$pass = new TranslatorPathsPass('translator', 'console.command.translation_debug', 'console.command.translation_update', 'argument_resolver.service');
$pass->process($container);
$expectedPaths = [
$container->getReflectionClass(ServiceArguments::class)->getFileName(),
$container->getReflectionClass(ServiceProperties::class)->getFileName(),
$container->getReflectionClass(ServiceMethodCalls::class)->getFileName(),
$container->getReflectionClass(ControllerArguments::class)->getFileName(),
$container->getReflectionClass(ServiceSubscriber::class)->getFileName(),
];
$this->assertSame($expectedPaths, $debugCommand->getArgument(6));
$this->assertSame($expectedPaths, $updateCommand->getArgument(7));
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Symfony\Component\Translation\Tests\DependencyInjection\fixtures;
use Symfony\Contracts\Translation\TranslatorInterface;
class ControllerArguments
{
public function __invoke(TranslatorInterface $translator)
{
}
public function index(TranslatorInterface $translator)
{
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace Symfony\Component\Translation\Tests\DependencyInjection\fixtures;
use Symfony\Contracts\Translation\TranslatorInterface;
class ServiceArguments
{
public function __construct(TranslatorInterface $translator)
{
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace Symfony\Component\Translation\Tests\DependencyInjection\fixtures;
use Symfony\Contracts\Translation\TranslatorInterface;
class ServiceMethodCalls
{
public function setTranslator(TranslatorInterface $translator)
{
}
}

View File

@@ -0,0 +1,8 @@
<?php
namespace Symfony\Component\Translation\Tests\DependencyInjection\fixtures;
class ServiceProperties
{
public $translator;
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Symfony\Component\Translation\Tests\DependencyInjection\fixtures;
use Psr\Container\ContainerInterface;
use Symfony\Contracts\Service\ServiceSubscriberInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class ServiceSubscriber implements ServiceSubscriberInterface
{
public function __construct(ContainerInterface $container)
{
}
public static function getSubscribedServices()
{
return ['translator' => TranslatorInterface::class];
}
}

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,90 @@
<?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');
}
public function testDumpIntl()
{
$tempDir = sys_get_temp_dir();
$catalogue = new MessageCatalogue('en');
$catalogue->add(['foo' => 'bar'], 'd1');
$catalogue->add(['bar' => 'foo'], 'd1+intl-icu');
$catalogue->add(['bar' => 'foo'], 'd2+intl-icu');
$dumper = new ConcreteFileDumper();
@unlink($tempDir.'/d2.en.concrete');
$dumper->dump($catalogue, ['path' => $tempDir]);
$this->assertStringEqualsFile($tempDir.'/d1.en.concrete', 'foo=bar');
@unlink($tempDir.'/d1.en.concrete');
$this->assertStringEqualsFile($tempDir.'/d1+intl-icu.en.concrete', 'bar=foo');
@unlink($tempDir.'/d1+intl-icu.en.concrete');
$this->assertFileNotExists($tempDir.'/d2.en.concrete');
$this->assertStringEqualsFile($tempDir.'/d2+intl-icu.en.concrete', 'bar=foo');
@unlink($tempDir.'/d2+intl-icu.en.concrete');
}
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 http_build_query($messages->all($domain), '', '&');
}
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,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\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', 'foo_bar' => 'foobar', 'bar_foo' => 'barfoo']);
$catalogue->setMetadata('foo_bar', [
'comments' => [
'Comment 1',
'Comment 2',
],
'flags' => [
'fuzzy',
'another',
],
'sources' => [
'src/file_1',
'src/file_2:50',
],
]);
$catalogue->setMetadata('bar_foo', [
'comments' => 'Comment',
'flags' => 'fuzzy',
'sources' => 'src/file_1',
]);
$dumper = new PoFileDumper();
$this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.po', $dumper->formatCatalogue($catalogue, 'messages'));
}
}

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\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', 'foo_bar' => 'foobar', 'bar_foo' => 'barfoo'], 'resources');
$catalogue->setMetadata('foo_bar', [
'comments' => [
'Comment 1',
'Comment 2',
],
'flags' => [
'fuzzy',
'another',
],
'sources' => [
'src/file_1',
'src/file_2:50',
],
], 'resources');
$catalogue->setMetadata('bar_foo', [
'comments' => 'Comment',
'flags' => 'fuzzy',
'sources' => 'src/file_1',
], 'resources');
$dumper = new QtFileDumper();
$this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.ts', $dumper->formatCatalogue($catalogue, 'resources'));
}
}

View File

@@ -0,0 +1,130 @@
<?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 testFormatIcuCatalogueXliff2()
{
$catalogue = new MessageCatalogue('en_US');
$catalogue->add([
'foo' => 'bar',
], 'messages'.MessageCatalogue::INTL_DOMAIN_SUFFIX);
$dumper = new XliffFileDumper();
$this->assertStringEqualsFile(
__DIR__.'/../fixtures/resources-2.0+intl-icu.xlf',
$dumper->formatCatalogue($catalogue, 'messages'.MessageCatalogue::INTL_DOMAIN_SUFFIX, ['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,101 @@
<?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);
$filename = str_replace(\DIRECTORY_SEPARATOR, '/', __DIR__).'/../fixtures/extractor/translation.html.php';
$this->assertEquals(['sources' => [$filename.':2']], $catalogue->getMetadata('single-quoted key'));
$this->assertEquals(['sources' => [$filename.':43']], $catalogue->getMetadata('other-domain-test-no-params-short-array', 'not_messages'));
}
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,101 @@
<?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 Symfony\Component\Translation\Exception\InvalidArgumentException;
use Symfony\Component\Translation\Formatter\IntlFormatter;
use Symfony\Component\Translation\Formatter\IntlFormatterInterface;
/**
* @requires extension intl
*/
class IntlFormatterTest extends \PHPUnit\Framework\TestCase
{
/**
* @dataProvider provideDataForFormat
*/
public function testFormat($expected, $message, $arguments)
{
$this->assertEquals($expected, trim((new IntlFormatter())->formatIntl($message, 'en', $arguments)));
}
public function testInvalidFormat()
{
$this->expectException(InvalidArgumentException::class);
(new IntlFormatter())->formatIntl('{foo', 'en', [2]);
}
public function testFormatWithNamedArguments()
{
if (version_compare(INTL_ICU_VERSION, '4.8', '<')) {
$this->markTestSkipped('Format with named arguments can only be run with ICU 4.8 or higher and PHP >= 5.5');
}
$chooseMessage = <<<'_MSG_'
{gender_of_host, select,
female {{num_guests, plural, offset:1
=0 {{host} does not give a party.}
=1 {{host} invites {guest} to her party.}
=2 {{host} invites {guest} and one other person to her party.}
other {{host} invites {guest} as one of the # people invited to her party.}}}
male {{num_guests, plural, offset:1
=0 {{host} does not give a party.}
=1 {{host} invites {guest} to his party.}
=2 {{host} invites {guest} and one other person to his party.}
other {{host} invites {guest} as one of the # people invited to his party.}}}
other {{num_guests, plural, offset:1
=0 {{host} does not give a party.}
=1 {{host} invites {guest} to their party.}
=2 {{host} invites {guest} and one other person to their party.}
other {{host} invites {guest} as one of the # people invited to their party.}}}}
_MSG_;
$message = (new IntlFormatter())->formatIntl($chooseMessage, 'en', [
'gender_of_host' => 'male',
'num_guests' => 10,
'host' => 'Fabien',
'guest' => 'Guilherme',
]);
$this->assertEquals('Fabien invites Guilherme as one of the 9 people invited to his party.', $message);
}
public function provideDataForFormat()
{
return [
[
'There is one apple',
'There is one apple',
[],
],
[
'4,560 monkeys on 123 trees make 37.073 monkeys per tree',
'{0,number,integer} monkeys on {1,number,integer} trees make {2,number} monkeys per tree',
[4560, 123, 4560 / 123],
],
[
'',
'',
[],
],
];
}
public function testPercentsAndBracketsAreTrimmed()
{
$formatter = new IntlFormatter();
$this->assertInstanceof(IntlFormatterInterface::class, $formatter);
$this->assertSame('Hello Fab', $formatter->formatIntl('Hello {name}', 'en', ['name' => 'Fab']));
$this->assertSame('Hello Fab', $formatter->formatIntl('Hello {name}', 'en', ['%name%' => 'Fab']));
$this->assertSame('Hello Fab', $formatter->formatIntl('Hello {name}', 'en', ['{{ name }}' => 'Fab']));
}
}

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\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
* @group legacy
*/
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,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;
use Symfony\Component\Translation\IdentityTranslator;
use Symfony\Contracts\Translation\Test\TranslatorTest;
class IdentityTranslatorTest extends TranslatorTest
{
private $defaultLocale;
protected function setUp(): void
{
parent::setUp();
$this->defaultLocale = \Locale::getDefault();
}
protected function tearDown(): void
{
parent::tearDown();
\Locale::setDefault($this->defaultLocale);
}
public function getTranslator()
{
return new IdentityTranslator();
}
}

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\Interval;
/**
* @group legacy
*/
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(): void
{
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,69 @@
<?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',
'foo_bar' => 'foobar',
'bar_foo' => 'barfoo',
], $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,317 @@
<?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()
{
$disableEntities = libxml_disable_entity_loader(true);
$loader = new XliffFileLoader();
$resource = __DIR__.'/../fixtures/resources.xlf';
$catalogue = $loader->load($resource, 'en', 'domain1');
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(
[
'source' => 'foo',
'notes' => [['content' => utf8_decode('bäz')]],
'id' => '1',
'file' => [
'original' => 'file.ext',
],
],
$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(
[
'source' => 'foo',
'notes' => [['priority' => 1, 'content' => 'foo']],
'id' => '1',
'file' => [
'original' => 'file.ext',
],
],
$catalogue->getMetadata('foo', 'domain1')
);
// message without target
$this->assertEquals(
[
'source' => 'extrasource',
'notes' => [['content' => 'bar', 'from' => 'foo']],
'id' => '2',
'file' => [
'original' => 'file.ext',
],
],
$catalogue->getMetadata('extra', 'domain1')
);
// message with empty target
$this->assertEquals(
[
'source' => 'key',
'notes' => [
['content' => 'baz'],
['priority' => 2, 'from' => 'bar', 'content' => 'qux'],
],
'id' => '123',
'file' => [
'original' => 'file.ext',
],
],
$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']);
}
public function testLoadWithMultipleFileNodes()
{
$loader = new XliffFileLoader();
$catalogue = $loader->load(__DIR__.'/../fixtures/resources-multi-files.xlf', 'en', 'domain1');
$this->assertEquals(
[
'source' => 'foo',
'id' => '1',
'file' => [
'original' => 'file.ext',
],
],
$catalogue->getMetadata('foo', 'domain1')
);
$this->assertEquals(
[
'source' => 'test',
'notes' => [['content' => 'note']],
'id' => '4',
'file' => [
'original' => 'otherfile.ext',
],
],
$catalogue->getMetadata('test', 'domain1')
);
}
}

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,68 @@
<?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(1))
->method('warning')
->with('Translation not found.')
;
$translator = new Translator('ar');
$loggableTranslator = new LoggingTranslator($translator, $logger);
$loggableTranslator->trans('bar');
}
/**
* @group legacy
*/
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]);
}
/**
* @group legacy
*/
public function testTransChoiceWithNoTranslationIsLogged()
{
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$logger->expects($this->exactly(1))
->method('warning')
->with('Translation not found.')
;
$translator = new Translator('ar');
$loggableTranslator = new LoggingTranslator($translator, $logger);
$loggableTranslator->transChoice('some_message2', 10, ['%count%' => 10]);
}
}

View File

@@ -0,0 +1,237 @@
<?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' => [], 'domain2+intl-icu' => [], 'domain3+intl-icu' => []]);
$this->assertEquals(['domain1', 'domain2', 'domain3'], $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());
$messages = ['domain1+intl-icu' => ['foo' => 'bar']] + $messages + [
'domain2+intl-icu' => ['bar' => 'foo'],
'domain3+intl-icu' => ['biz' => 'biz'],
];
$catalogue = new MessageCatalogue('en', $messages);
$this->assertEquals(['foo' => 'bar'], $catalogue->all('domain1'));
$this->assertEquals(['bar' => 'foo'], $catalogue->all('domain2'));
$this->assertEquals(['biz' => 'biz'], $catalogue->all('domain3'));
$messages = [
'domain1' => ['foo' => 'bar'],
'domain2' => ['bar' => 'foo'],
'domain3' => ['biz' => 'biz'],
];
$this->assertEquals($messages, $catalogue->all());
}
public function testHas()
{
$catalogue = new MessageCatalogue('en', ['domain1' => ['foo' => 'foo'], 'domain2+intl-icu' => ['bar' => 'bar']]);
$this->assertTrue($catalogue->has('foo', 'domain1'));
$this->assertTrue($catalogue->has('bar', 'domain2'));
$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'], 'domain2+intl-icu' => ['bar' => 'foo']]);
$catalogue->set('foo1', 'foo1', 'domain1');
$this->assertEquals('foo', $catalogue->get('foo', 'domain1'));
$this->assertEquals('foo1', $catalogue->get('foo1', 'domain1'));
$this->assertEquals('foo', $catalogue->get('bar', 'domain2'));
}
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'], 'domain1+intl-icu' => ['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']]);
$catalogue->addResource($r);
$catalogue1 = new MessageCatalogue('en', ['domain1' => ['foo1' => 'foo1'], 'domain2+intl-icu' => ['bar' => 'bar']]);
$catalogue1->addResource($r1);
$catalogue->addCatalogue($catalogue1);
$this->assertEquals('foo', $catalogue->get('foo', 'domain1'));
$this->assertEquals('foo1', $catalogue->get('foo1', 'domain1'));
$this->assertEquals('bar', $catalogue->get('bar', 'domain2'));
$this->assertEquals('bar', $catalogue->get('bar', 'domain2+intl-icu'));
$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,140 @@
<?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;
/**
* @group legacy
*/
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,124 @@
<?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
*
* @group legacy
*/
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->assertEquals($nplural, \count($indexes), "Langcode '$langCode' has '$nplural' plural forms.");
} else {
$this->assertNotEquals((int) $nplural, \count($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,319 @@
<?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(): void
{
$this->tmpDir = sys_get_temp_dir().'/sf_translation';
$this->deleteTmpDir();
}
protected function tearDown(): void
{
$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->addResource($format, [$msgid.'+intl' => 'OK'], $locale, 'messages+intl-icu');
$translator->trans($msgid);
$translator->trans($msgid.'+intl', [], 'messages+intl-icu');
// 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);
$translator->addResource($format, [$msgid.'+intl' => 'OK'], $locale, 'messages+intl-icu');
$this->assertEquals('OK', $translator->trans($msgid), '-> caching does not work in '.($debug ? 'debug' : 'production'));
$this->assertEquals('OK', $translator->trans($msgid.'+intl', [], 'messages+intl-icu'));
}
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');
$translator->addResource('array', ['baz' => 'baz (b)'], 'b', 'messages+intl-icu');
$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');
$translator->addResource('array', ['baz' => 'baz (b)'], 'b', 'messages+intl-icu');
$catalogue = $translator->getCatalogue('a');
$this->assertFalse($catalogue->defines('bar'));
$fallback = $catalogue->getFallbackCatalogue();
$this->assertTrue($fallback->defines('foo'));
$this->assertTrue($fallback->defines('baz', 'messages+intl-icu'));
}
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,610 @@
<?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\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'));
}
public function testTransWithIcuFallbackLocale()
{
$translator = new Translator('en_GB');
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', ['foo' => 'foofoo'], 'en_GB');
$translator->addResource('array', ['bar' => 'foobar'], 'en_001');
$translator->addResource('array', ['baz' => 'foobaz'], 'en');
$this->assertSame('foofoo', $translator->trans('foo'));
$this->assertSame('foobar', $translator->trans('bar'));
$this->assertSame('foobaz', $translator->trans('baz'));
}
public function testTransWithIcuVariantFallbackLocale()
{
$translator = new Translator('en_GB_scouse');
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', ['foo' => 'foofoo'], 'en_GB_scouse');
$translator->addResource('array', ['bar' => 'foobar'], 'en_GB');
$translator->addResource('array', ['baz' => 'foobaz'], 'en_001');
$translator->addResource('array', ['qux' => 'fooqux'], 'en');
$this->assertSame('foofoo', $translator->trans('foo'));
$this->assertSame('foobar', $translator->trans('bar'));
$this->assertSame('foobaz', $translator->trans('baz'));
$this->assertSame('fooqux', $translator->trans('qux'));
}
public function testTransWithIcuRootFallbackLocale()
{
$translator = new Translator('az_Cyrl');
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', ['foo' => 'foofoo'], 'az_Cyrl');
$translator->addResource('array', ['bar' => 'foobar'], 'az');
$this->assertSame('foofoo', $translator->trans('foo'));
$this->assertSame('bar', $translator->trans('bar'));
}
public function testTransWithFallbackLocaleBis()
{
$translator = new Translator('en_US');
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', ['foo' => 'foofoo'], 'en_US');
$translator->addResource('array', ['bar' => 'foobar'], 'en');
$this->assertEquals('foobar', $translator->trans('bar'));
}
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->assertContains(__DIR__.\DIRECTORY_SEPARATOR.'fixtures'.\DIRECTORY_SEPARATOR.'resources.yml', $resources);
$resources = $translator->getCatalogue('en_GB')->getResources();
$this->assertCount(2, $resources);
$this->assertContains(__DIR__.\DIRECTORY_SEPARATOR.'fixtures'.\DIRECTORY_SEPARATOR.'empty.yml', $resources);
$this->assertContains(__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
* @group legacy
*/
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
* @group legacy
*/
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
* @group legacy
*/
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 quelques 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'],
];
}
/**
* @requires extension intl
*/
public function testIntlFormattedDomain()
{
$translator = new Translator('en');
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', ['some_message' => 'Hello %name%'], 'en');
$this->assertSame('Hello Bob', $translator->trans('some_message', ['%name%' => 'Bob']));
$translator->addResource('array', ['some_message' => 'Hi {name}'], 'en', 'messages+intl-icu');
$this->assertSame('Hi Bob', $translator->trans('some_message', ['%name%' => 'Bob']));
}
/**
* @group legacy
*/
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]));
}
/**
* @group legacy
*/
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]));
}
/**
* @group legacy
*/
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]));
}
}
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,69 @@
<?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
{
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('en'), 'test');
}
/**
* @group legacy
*/
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,11 @@
<?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" name="foo">
<segment>
<source>foo</source>
<target>bar</target>
</segment>
</unit>
</file>
</xliff>

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" name="foo">
<segment>
<source>foo</source>
<target>bar</target>
</segment>
</unit>
<unit id="LHDhK3o" name="key">
<segment>
<source>key</source>
<target order="1"></target>
</segment>
</unit>
<unit id="2DA_bnh" name="key.with.cdata">
<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,27 @@
<?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>
</body>
</file>
<file source-language="en" datatype="plaintext" original="otherfile.ext">
<body>
<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,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" name="foo">
<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" name="baz">
<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',
);

Some files were not shown because too many files have changed in this diff Show More