Laravel version update

Laravel version update
This commit is contained in:
Manish Verma
2018-08-06 18:48:58 +05:30
parent d143048413
commit 126fbb0255
13678 changed files with 1031482 additions and 778530 deletions

View File

@@ -1,6 +1,45 @@
CHANGELOG
=========
4.1.0
-----
* The `FileDumper::setBackup()` method is deprecated.
* The `TranslationWriter::disableBackup()` method is deprecated.
* The `XliffFileDumper` will write "name" on the "unit" node when dumping XLIFF 2.0.
4.0.0
-----
* removed the backup feature of the `FileDumper` class
* removed `TranslationWriter::writeTranslations()` method
* removed support for passing `MessageSelector` instances to the constructor of the `Translator` class
3.4.0
-----
* Added `TranslationDumperPass`
* Added `TranslationExtractorPass`
* Added `TranslatorPass`
* Added `TranslationReader` and `TranslationReaderInterface`
* Added `<notes>` section to the Xliff 2.0 dumper.
* Improved Xliff 2.0 loader to load `<notes>` section.
* Added `TranslationWriterInterface`
* Deprecated `TranslationWriter::writeTranslations` in favor of `TranslationWriter::write`
* added support for adding custom message formatter and decoupling the default one.
* Added `PhpExtractor`
* Added `PhpStringTokenParser`
3.2.0
-----
* Added support for escaping `|` in plural translations with double pipe.
3.1.0
-----
* Deprecated the backup feature of the file dumper classes.
3.0.0
-----

View File

@@ -11,6 +11,8 @@
namespace Symfony\Component\Translation\Catalogue;
use Symfony\Component\Translation\Exception\InvalidArgumentException;
use Symfony\Component\Translation\Exception\LogicException;
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\MessageCatalogueInterface;
@@ -24,19 +26,8 @@ use Symfony\Component\Translation\MessageCatalogueInterface;
*/
abstract class AbstractOperation implements OperationInterface
{
/**
* @var MessageCatalogueInterface The source catalogue
*/
protected $source;
/**
* @var MessageCatalogueInterface The target catalogue
*/
protected $target;
/**
* @var MessageCatalogue The result catalogue
*/
protected $result;
/**
@@ -69,21 +60,17 @@ abstract class AbstractOperation implements OperationInterface
protected $messages;
/**
* @param MessageCatalogueInterface $source The source catalogue
* @param MessageCatalogueInterface $target The target catalogue
*
* @throws \LogicException
* @throws LogicException
*/
public function __construct(MessageCatalogueInterface $source, MessageCatalogueInterface $target)
{
if ($source->getLocale() !== $target->getLocale()) {
throw new \LogicException('Operated catalogues must belong to the same locale.');
throw new LogicException('Operated catalogues must belong to the same locale.');
}
$this->source = $source;
$this->target = $target;
$this->result = new MessageCatalogue($source->getLocale());
$this->domains = null;
$this->messages = array();
}
@@ -104,8 +91,8 @@ abstract class AbstractOperation implements OperationInterface
*/
public function getMessages($domain)
{
if (!in_array($domain, $this->getDomains())) {
throw new \InvalidArgumentException(sprintf('Invalid domain: %s.', $domain));
if (!\in_array($domain, $this->getDomains())) {
throw new InvalidArgumentException(sprintf('Invalid domain: %s.', $domain));
}
if (!isset($this->messages[$domain]['all'])) {
@@ -120,8 +107,8 @@ abstract class AbstractOperation implements OperationInterface
*/
public function getNewMessages($domain)
{
if (!in_array($domain, $this->getDomains())) {
throw new \InvalidArgumentException(sprintf('Invalid domain: %s.', $domain));
if (!\in_array($domain, $this->getDomains())) {
throw new InvalidArgumentException(sprintf('Invalid domain: %s.', $domain));
}
if (!isset($this->messages[$domain]['new'])) {
@@ -136,8 +123,8 @@ abstract class AbstractOperation implements OperationInterface
*/
public function getObsoleteMessages($domain)
{
if (!in_array($domain, $this->getDomains())) {
throw new \InvalidArgumentException(sprintf('Invalid domain: %s.', $domain));
if (!\in_array($domain, $this->getDomains())) {
throw new InvalidArgumentException(sprintf('Invalid domain: %s.', $domain));
}
if (!isset($this->messages[$domain]['obsolete'])) {

View File

@@ -17,7 +17,7 @@ namespace Symfony\Component\Translation\Catalogue;
* all = intersection (target intersection) = target
* new = all source = {x: x ∈ target ∧ x ∉ source}
* obsolete = source all = source target = {x: x ∈ source ∧ x ∉ target}
* Basically, the result contains messages from the target catalogue.
* Basically, the result contains messages from the target catalogue.
*
* @author Michael Lee <michael.lee@zerustech.com>
*/
@@ -34,12 +34,12 @@ class TargetOperation extends AbstractOperation
'obsolete' => array(),
);
// For 'all' messages, the code can't be simplified as ``$this->messages[$domain]['all'] = $target->all($domain);``,
// For 'all' messages, the code can't be simplified as ``$this->messages[$domain]['all'] = $target->all($domain);``,
// because doing so will drop messages like {x: x ∈ source ∧ x ∉ target.all ∧ x ∈ target.fallback}
//
// For 'new' messages, the code can't be simplied as ``array_diff_assoc($this->target->all($domain), $this->source->all($domain));``
// because doing so will not exclude messages like {x: x ∈ target ∧ x ∉ source.all ∧ x ∈ source.fallback}
//
//
// For 'obsolete' messages, the code can't be simplifed as ``array_diff_assoc($this->source->all($domain), $this->target->all($domain))``
// because doing so will not exclude messages like {x: x ∈ source ∧ x ∉ target.all ∧ x ∈ target.fallback}

View File

@@ -0,0 +1,270 @@
<?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\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
/**
* Validates XLIFF files syntax and outputs encountered errors.
*
* @author Grégoire Pineau <lyrixx@lyrixx.info>
* @author Robin Chalas <robin.chalas@gmail.com>
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
*/
class XliffLintCommand extends Command
{
protected static $defaultName = 'lint:xliff';
private $format;
private $displayCorrectFiles;
private $directoryIteratorProvider;
private $isReadableProvider;
public function __construct(string $name = null, callable $directoryIteratorProvider = null, callable $isReadableProvider = null)
{
parent::__construct($name);
$this->directoryIteratorProvider = $directoryIteratorProvider;
$this->isReadableProvider = $isReadableProvider;
}
/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->setDescription('Lints a XLIFF file and outputs encountered errors')
->addArgument('filename', null, 'A file or a directory or STDIN')
->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format', 'txt')
->setHelp(<<<EOF
The <info>%command.name%</info> command lints a XLIFF file and outputs to STDOUT
the first encountered syntax error.
You can validates XLIFF contents passed from STDIN:
<info>cat filename | php %command.full_name%</info>
You can also validate the syntax of a file:
<info>php %command.full_name% filename</info>
Or of a whole directory:
<info>php %command.full_name% dirname</info>
<info>php %command.full_name% dirname --format=json</info>
EOF
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$io = new SymfonyStyle($input, $output);
$filename = $input->getArgument('filename');
$this->format = $input->getOption('format');
$this->displayCorrectFiles = $output->isVerbose();
if (!$filename) {
if (!$stdin = $this->getStdin()) {
throw new RuntimeException('Please provide a filename or pipe file content to STDIN.');
}
return $this->display($io, array($this->validate($stdin)));
}
if (!$this->isReadable($filename)) {
throw new RuntimeException(sprintf('File or directory "%s" is not readable.', $filename));
}
$filesInfo = array();
foreach ($this->getFiles($filename) as $file) {
$filesInfo[] = $this->validate(file_get_contents($file), $file);
}
return $this->display($io, $filesInfo);
}
private function validate($content, $file = null)
{
$errors = array();
// Avoid: Warning DOMDocument::loadXML(): Empty string supplied as input
if ('' === trim($content)) {
return array('file' => $file, 'valid' => true);
}
libxml_use_internal_errors(true);
$document = new \DOMDocument();
$document->loadXML($content);
if (null !== $targetLanguage = $this->getTargetLanguageFromFile($document)) {
$expectedFileExtension = sprintf('%s.xlf', str_replace('-', '_', $targetLanguage));
$realFileExtension = explode('.', basename($file), 2)[1] ?? '';
if ($expectedFileExtension !== $realFileExtension) {
$errors[] = array(
'line' => -1,
'column' => -1,
'message' => sprintf('There is a mismatch between the file extension ("%s") and the "%s" value used in the "target-language" attribute of the file.', $realFileExtension, $targetLanguage),
);
}
}
$document->schemaValidate(__DIR__.'/../Resources/schemas/xliff-core-1.2-strict.xsd');
foreach (libxml_get_errors() as $xmlError) {
$errors[] = array(
'line' => $xmlError->line,
'column' => $xmlError->column,
'message' => trim($xmlError->message),
);
}
libxml_clear_errors();
libxml_use_internal_errors(false);
return array('file' => $file, 'valid' => 0 === \count($errors), 'messages' => $errors);
}
private function display(SymfonyStyle $io, array $files)
{
switch ($this->format) {
case 'txt':
return $this->displayTxt($io, $files);
case 'json':
return $this->displayJson($io, $files);
default:
throw new InvalidArgumentException(sprintf('The format "%s" is not supported.', $this->format));
}
}
private function displayTxt(SymfonyStyle $io, array $filesInfo)
{
$countFiles = \count($filesInfo);
$erroredFiles = 0;
foreach ($filesInfo as $info) {
if ($info['valid'] && $this->displayCorrectFiles) {
$io->comment('<info>OK</info>'.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
} elseif (!$info['valid']) {
++$erroredFiles;
$io->text('<error> ERROR </error>'.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
$io->listing(array_map(function ($error) {
// general document errors have a '-1' line number
return -1 === $error['line'] ? $error['message'] : sprintf('Line %d, Column %d: %s', $error['line'], $error['column'], $error['message']);
}, $info['messages']));
}
}
if (0 === $erroredFiles) {
$io->success(sprintf('All %d XLIFF files contain valid syntax.', $countFiles));
} else {
$io->warning(sprintf('%d XLIFF files have valid syntax and %d contain errors.', $countFiles - $erroredFiles, $erroredFiles));
}
return min($erroredFiles, 1);
}
private function displayJson(SymfonyStyle $io, array $filesInfo)
{
$errors = 0;
array_walk($filesInfo, function (&$v) use (&$errors) {
$v['file'] = (string) $v['file'];
if (!$v['valid']) {
++$errors;
}
});
$io->writeln(json_encode($filesInfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
return min($errors, 1);
}
private function getFiles($fileOrDirectory)
{
if (is_file($fileOrDirectory)) {
yield new \SplFileInfo($fileOrDirectory);
return;
}
foreach ($this->getDirectoryIterator($fileOrDirectory) as $file) {
if (!\in_array($file->getExtension(), array('xlf', 'xliff'))) {
continue;
}
yield $file;
}
}
private function getStdin()
{
if (0 !== ftell(STDIN)) {
return;
}
$inputs = '';
while (!feof(STDIN)) {
$inputs .= fread(STDIN, 1024);
}
return $inputs;
}
private function getDirectoryIterator($directory)
{
$default = function ($directory) {
return new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS),
\RecursiveIteratorIterator::LEAVES_ONLY
);
};
if (null !== $this->directoryIteratorProvider) {
return \call_user_func($this->directoryIteratorProvider, $directory, $default);
}
return $default($directory);
}
private function isReadable($fileOrDirectory)
{
$default = function ($fileOrDirectory) {
return is_readable($fileOrDirectory);
};
if (null !== $this->isReadableProvider) {
return \call_user_func($this->isReadableProvider, $fileOrDirectory, $default);
}
return $default($fileOrDirectory);
}
private function getTargetLanguageFromFile(\DOMDocument $xliffContents): ?string
{
foreach ($xliffContents->getElementsByTagName('file')[0]->attributes ?? array() as $attribute) {
if ('target-language' === $attribute->nodeName) {
return $attribute->nodeValue;
}
}
return null;
}
}

View File

@@ -22,14 +22,8 @@ use Symfony\Component\Translation\DataCollectorTranslator;
*/
class TranslationDataCollector extends DataCollector implements LateDataCollectorInterface
{
/**
* @var DataCollectorTranslator
*/
private $translator;
/**
* @param DataCollectorTranslator $translator
*/
public function __construct(DataCollectorTranslator $translator)
{
$this->translator = $translator;
@@ -44,6 +38,11 @@ class TranslationDataCollector extends DataCollector implements LateDataCollecto
$this->data = $this->computeCount($messages);
$this->data['messages'] = $messages;
$this->data['locale'] = $this->translator->getLocale();
$this->data['fallback_locales'] = $this->translator->getFallbackLocales();
$this->data = $this->cloneVar($this->data);
}
/**
@@ -53,6 +52,14 @@ class TranslationDataCollector extends DataCollector implements LateDataCollecto
{
}
/**
* {@inheritdoc}
*/
public function reset()
{
$this->data = array();
}
/**
* @return array
*/
@@ -85,6 +92,16 @@ class TranslationDataCollector extends DataCollector implements LateDataCollecto
return isset($this->data[DataCollectorTranslator::MESSAGE_DEFINED]) ? $this->data[DataCollectorTranslator::MESSAGE_DEFINED] : 0;
}
public function getLocale()
{
return !empty($this->data['locale']) ? $this->data['locale'] : null;
}
public function getFallbackLocales()
{
return (isset($this->data['fallback_locales']) && \count($this->data['fallback_locales']) > 0) ? $this->data['fallback_locales'] : array();
}
/**
* {@inheritdoc}
*/
@@ -141,7 +158,7 @@ class TranslationDataCollector extends DataCollector implements LateDataCollecto
if (mb_strlen($string, $encoding) > $length) {
return mb_substr($string, 0, $length - 3, $encoding).'...';
}
} elseif (strlen($string) > $length) {
} elseif (\strlen($string) > $length) {
return substr($string, 0, $length - 3).'...';
}

View File

@@ -11,6 +11,8 @@
namespace Symfony\Component\Translation;
use Symfony\Component\Translation\Exception\InvalidArgumentException;
/**
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
*/
@@ -25,9 +27,6 @@ class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInter
*/
private $translator;
/**
* @var array
*/
private $messages = array();
/**
@@ -36,7 +35,7 @@ class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInter
public function __construct(TranslatorInterface $translator)
{
if (!$translator instanceof TranslatorBagInterface) {
throw new \InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface and TranslatorBagInterface.', get_class($translator)));
throw new InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface and TranslatorBagInterface.', \get_class($translator)));
}
$this->translator = $translator;
@@ -88,12 +87,26 @@ class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInter
return $this->translator->getCatalogue($locale);
}
/**
* Gets the fallback locales.
*
* @return array $locales The fallback locales
*/
public function getFallbackLocales()
{
if ($this->translator instanceof Translator || method_exists($this->translator, 'getFallbackLocales')) {
return $this->translator->getFallbackLocales();
}
return array();
}
/**
* Passes through all unknown calls onto the translator object.
*/
public function __call($method, $args)
{
return call_user_func_array(array($this->translator, $method), $args);
return \call_user_func_array(array($this->translator, $method), $args);
}
/**

View File

@@ -0,0 +1,44 @@
<?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\DependencyInjection;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* Adds tagged translation.formatter services to translation writer.
*/
class TranslationDumperPass implements CompilerPassInterface
{
private $writerServiceId;
private $dumperTag;
public function __construct(string $writerServiceId = 'translation.writer', string $dumperTag = 'translation.dumper')
{
$this->writerServiceId = $writerServiceId;
$this->dumperTag = $dumperTag;
}
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition($this->writerServiceId)) {
return;
}
$definition = $container->getDefinition($this->writerServiceId);
foreach ($container->findTaggedServiceIds($this->dumperTag, true) as $id => $attributes) {
$definition->addMethodCall('addDumper', array($attributes[0]['alias'], new Reference($id)));
}
}
}

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\DependencyInjection;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\Reference;
/**
* Adds tagged translation.extractor services to translation extractor.
*/
class TranslationExtractorPass implements CompilerPassInterface
{
private $extractorServiceId;
private $extractorTag;
public function __construct(string $extractorServiceId = 'translation.extractor', string $extractorTag = 'translation.extractor')
{
$this->extractorServiceId = $extractorServiceId;
$this->extractorTag = $extractorTag;
}
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition($this->extractorServiceId)) {
return;
}
$definition = $container->getDefinition($this->extractorServiceId);
foreach ($container->findTaggedServiceIds($this->extractorTag, true) as $id => $attributes) {
if (!isset($attributes[0]['alias'])) {
throw new RuntimeException(sprintf('The alias for the tag "translation.extractor" of service "%s" must be set.', $id));
}
$definition->addMethodCall('addExtractor', array($attributes[0]['alias'], new Reference($id)));
}
}
}

View File

@@ -0,0 +1,79 @@
<?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\DependencyInjection;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
class TranslatorPass implements CompilerPassInterface
{
private $translatorServiceId;
private $readerServiceId;
private $loaderTag;
private $debugCommandServiceId;
private $updateCommandServiceId;
public function __construct(string $translatorServiceId = 'translator.default', string $readerServiceId = 'translation.reader', string $loaderTag = 'translation.loader', string $debugCommandServiceId = 'console.command.translation_debug', string $updateCommandServiceId = 'console.command.translation_update')
{
$this->translatorServiceId = $translatorServiceId;
$this->readerServiceId = $readerServiceId;
$this->loaderTag = $loaderTag;
$this->debugCommandServiceId = $debugCommandServiceId;
$this->updateCommandServiceId = $updateCommandServiceId;
}
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition($this->translatorServiceId)) {
return;
}
$loaders = array();
$loaderRefs = array();
foreach ($container->findTaggedServiceIds($this->loaderTag, true) as $id => $attributes) {
$loaderRefs[$id] = new Reference($id);
$loaders[$id][] = $attributes[0]['alias'];
if (isset($attributes[0]['legacy-alias'])) {
$loaders[$id][] = $attributes[0]['legacy-alias'];
}
}
if ($container->hasDefinition($this->readerServiceId)) {
$definition = $container->getDefinition($this->readerServiceId);
foreach ($loaders as $id => $formats) {
foreach ($formats as $format) {
$definition->addMethodCall('addLoader', array($format, $loaderRefs[$id]));
}
}
}
$container
->findDefinition($this->translatorServiceId)
->replaceArgument(0, ServiceLocatorTagPass::register($container, $loaderRefs))
->replaceArgument(3, $loaders)
;
if (!$container->hasParameter('twig.default_path')) {
return;
}
if ($container->hasDefinition($this->debugCommandServiceId)) {
$container->getDefinition($this->debugCommandServiceId)->replaceArgument(4, $container->getParameter('twig.default_path'));
}
if ($container->hasDefinition($this->updateCommandServiceId)) {
$container->getDefinition($this->updateCommandServiceId)->replaceArgument(5, $container->getParameter('twig.default_path'));
}
}
}

View File

@@ -44,8 +44,8 @@ class CsvFileDumper extends FileDumper
/**
* Sets the delimiter and escape character for CSV.
*
* @param string $delimiter delimiter character
* @param string $enclosure enclosure character
* @param string $delimiter Delimiter character
* @param string $enclosure Enclosure character
*/
public function setCsvControl($delimiter = ';', $enclosure = '"')
{

View File

@@ -11,11 +11,12 @@
namespace Symfony\Component\Translation\Dumper;
use Symfony\Component\Translation\Exception\InvalidArgumentException;
use Symfony\Component\Translation\Exception\RuntimeException;
use Symfony\Component\Translation\MessageCatalogue;
/**
* FileDumper is an implementation of DumperInterface that dump a message catalogue to file(s).
* Performs backup of already existing files.
*
* Options:
* - path (mandatory): the directory where the files should be saved
@@ -31,13 +32,6 @@ abstract class FileDumper implements DumperInterface
*/
protected $relativePathTemplate = '%domain%.%locale%.%extension%';
/**
* Make file backup before the dump.
*
* @var bool
*/
private $backup = true;
/**
* Sets the template for the relative paths to files.
*
@@ -52,10 +46,16 @@ abstract class FileDumper implements DumperInterface
* Sets backup flag.
*
* @param bool
*
* @deprecated since Symfony 4.1
*/
public function setBackup($backup)
{
$this->backup = $backup;
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1.', __METHOD__), E_USER_DEPRECATED);
if (false !== $backup) {
throw new \LogicException('The backup feature is no longer supported.');
}
}
/**
@@ -64,21 +64,16 @@ abstract class FileDumper implements DumperInterface
public function dump(MessageCatalogue $messages, $options = array())
{
if (!array_key_exists('path', $options)) {
throw new \InvalidArgumentException('The file dumper needs a path option.');
throw new InvalidArgumentException('The file dumper needs a path option.');
}
// save a file for each domain
foreach ($messages->getDomains() as $domain) {
// backup
$fullpath = $options['path'].'/'.$this->getRelativePath($domain, $messages->getLocale());
if (file_exists($fullpath)) {
if ($this->backup) {
copy($fullpath, $fullpath.'~');
}
} else {
$directory = dirname($fullpath);
if (!file_exists($fullpath)) {
$directory = \dirname($fullpath);
if (!file_exists($directory) && !@mkdir($directory, 0777, true)) {
throw new \RuntimeException(sprintf('Unable to create directory "%s".', $directory));
throw new RuntimeException(sprintf('Unable to create directory "%s".', $directory));
}
}
// save file
@@ -106,13 +101,8 @@ abstract class FileDumper implements DumperInterface
/**
* Gets the relative file path using the template.
*
* @param string $domain The domain
* @param string $locale The locale
*
* @return string The relative file path
*/
private function getRelativePath($domain, $locale)
private function getRelativePath(string $domain, string $locale): string
{
return strtr($this->relativePathTemplate, array(
'%domain%' => $domain,

View File

@@ -33,8 +33,8 @@ class IcuResFileDumper extends FileDumper
$data = $indexes = $resources = '';
foreach ($messages->all($domain) as $source => $target) {
$indexes .= pack('v', strlen($data) + 28);
$data .= $source."\0";
$indexes .= pack('v', \strlen($data) + 28);
$data .= $source."\0";
}
$data .= $this->writePadding($data);
@@ -44,7 +44,7 @@ class IcuResFileDumper extends FileDumper
foreach ($messages->all($domain) as $source => $target) {
$resources .= pack('V', $this->getPosition($data));
$data .= pack('V', strlen($target))
$data .= pack('V', \strlen($target))
.mb_convert_encoding($target."\0", 'UTF-16LE', 'UTF-8')
.$this->writePadding($data)
;
@@ -52,7 +52,7 @@ class IcuResFileDumper extends FileDumper
$resOffset = $this->getPosition($data);
$data .= pack('v', count($messages))
$data .= pack('v', \count($messages->all($domain)))
.$indexes
.$this->writePadding($data)
.$resources
@@ -63,11 +63,11 @@ class IcuResFileDumper extends FileDumper
$root = pack('V7',
$resOffset + (2 << 28), // Resource Offset + Resource Type
6, // Index length
$keyTop, // Index keys top
$bundleTop, // Index resources top
$bundleTop, // Index bundle top
count($messages), // Index max table length
0 // Index attributes
$keyTop, // Index keys top
$bundleTop, // Index resources top
$bundleTop, // Index bundle top
\count($messages->all($domain)), // Index max table length
0 // Index attributes
);
$header = pack('vC2v4C12@32',
@@ -84,7 +84,7 @@ class IcuResFileDumper extends FileDumper
private function writePadding($data)
{
$padding = strlen($data) % 4;
$padding = \strlen($data) % 4;
if ($padding) {
return str_repeat("\xAA", 4 - $padding);
@@ -93,7 +93,7 @@ class IcuResFileDumper extends FileDumper
private function getPosition($data)
{
return (strlen($data) + 28) / 4;
return (\strlen($data) + 28) / 4;
}
/**

View File

@@ -28,7 +28,7 @@ class JsonFileDumper extends FileDumper
if (isset($options['json_encoding'])) {
$flags = $options['json_encoding'];
} else {
$flags = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0;
$flags = JSON_PRETTY_PRINT;
}
return json_encode($messages->all($domain), $flags);

View File

@@ -11,8 +11,8 @@
namespace Symfony\Component\Translation\Dumper;
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\Loader\MoFileLoader;
use Symfony\Component\Translation\MessageCatalogue;
/**
* MoFileDumper generates a gettext formatted string representation of a message catalogue.
@@ -26,7 +26,7 @@ class MoFileDumper extends FileDumper
*/
public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
{
$output = $sources = $targets = $sourceOffsets = $targetOffsets = '';
$sources = $targets = $sourceOffsets = $targetOffsets = '';
$offsets = array();
$size = 0;
@@ -47,7 +47,7 @@ class MoFileDumper extends FileDumper
'offsetHashes' => MoFileLoader::MO_HEADER_SIZE + (16 * $size),
);
$sourcesSize = strlen($sources);
$sourcesSize = \strlen($sources);
$sourcesStart = $header['offsetHashes'] + 1;
foreach ($offsets as $offset) {

View File

@@ -11,6 +11,7 @@
namespace Symfony\Component\Translation\Dumper;
use Symfony\Component\Translation\Exception\InvalidArgumentException;
use Symfony\Component\Translation\MessageCatalogue;
/**
@@ -43,7 +44,7 @@ class XliffFileDumper extends FileDumper
return $this->dumpXliff2($defaultLocale, $messages, $domain, $options);
}
throw new \InvalidArgumentException(sprintf('No support implemented for dumping XLIFF version "%s".', $xliffVersion));
throw new InvalidArgumentException(sprintf('No support implemented for dumping XLIFF version "%s".', $xliffVersion));
}
/**
@@ -84,7 +85,7 @@ class XliffFileDumper extends FileDumper
foreach ($messages->all($domain) as $source => $target) {
$translation = $dom->createElement('trans-unit');
$translation->setAttribute('id', md5($source));
$translation->setAttribute('id', strtr(substr(base64_encode(hash('sha256', $source, true)), 0, 7), '/+', '._'));
$translation->setAttribute('resname', $source);
$s = $translation->appendChild($dom->createElement('source'));
@@ -144,7 +145,29 @@ class XliffFileDumper extends FileDumper
foreach ($messages->all($domain) as $source => $target) {
$translation = $dom->createElement('unit');
$translation->setAttribute('id', md5($source));
$translation->setAttribute('id', strtr(substr(base64_encode(hash('sha256', $source, true)), 0, 7), '/+', '._'));
$name = $source;
if (\strlen($source) > 80) {
$name = substr(md5($source), -7);
}
$translation->setAttribute('name', $name);
$metadata = $messages->getMetadata($source, $domain);
// Add notes section
if ($this->hasMetadataArrayInfo('notes', $metadata)) {
$notesElement = $dom->createElement('notes');
foreach ($metadata['notes'] as $note) {
$n = $dom->createElement('note');
$n->appendChild($dom->createTextNode(isset($note['content']) ? $note['content'] : ''));
unset($note['content']);
foreach ($note as $name => $value) {
$n->setAttribute($name, $value);
}
$notesElement->appendChild($n);
}
$translation->appendChild($notesElement);
}
$segment = $translation->appendChild($dom->createElement('segment'));
@@ -155,7 +178,6 @@ class XliffFileDumper extends FileDumper
$text = 1 === preg_match('/[&<>]/', $target) ? $dom->createCDATASection($target) : $dom->createTextNode($target);
$targetElement = $dom->createElement('target');
$metadata = $messages->getMetadata($source, $domain);
if ($this->hasMetadataArrayInfo('target-attributes', $metadata)) {
foreach ($metadata['target-attributes'] as $name => $value) {
$targetElement->setAttribute($name, $value);
@@ -178,6 +200,6 @@ class XliffFileDumper extends FileDumper
*/
private function hasMetadataArrayInfo($key, $metadata = null)
{
return null !== $metadata && array_key_exists($key, $metadata) && ($metadata[$key] instanceof \Traversable || is_array($metadata[$key]));
return null !== $metadata && array_key_exists($key, $metadata) && ($metadata[$key] instanceof \Traversable || \is_array($metadata[$key]));
}
}

View File

@@ -11,6 +11,7 @@
namespace Symfony\Component\Translation\Dumper;
use Symfony\Component\Translation\Exception\LogicException;
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\Util\ArrayConverter;
use Symfony\Component\Yaml\Yaml;
@@ -22,13 +23,20 @@ use Symfony\Component\Yaml\Yaml;
*/
class YamlFileDumper extends FileDumper
{
private $extension;
public function __construct(string $extension = 'yml')
{
$this->extension = $extension;
}
/**
* {@inheritdoc}
*/
public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
{
if (!class_exists('Symfony\Component\Yaml\Yaml')) {
throw new \LogicException('Dumping translations in the YAML format requires the Symfony Yaml component.');
throw new LogicException('Dumping translations in the YAML format requires the Symfony Yaml component.');
}
$data = $messages->all($domain);
@@ -49,6 +57,6 @@ class YamlFileDumper extends FileDumper
*/
protected function getExtension()
{
return 'yml';
return $this->extension;
}
}

View File

@@ -0,0 +1,21 @@
<?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\Exception;
/**
* Base InvalidArgumentException for the Translation component.
*
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
*/
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,21 @@
<?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\Exception;
/**
* Base LogicException for Translation component.
*
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
*/
class LogicException extends \LogicException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,21 @@
<?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\Exception;
/**
* Base RuntimeException for the Translation component.
*
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
*/
class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}

View File

@@ -11,6 +11,8 @@
namespace Symfony\Component\Translation\Extractor;
use Symfony\Component\Translation\Exception\InvalidArgumentException;
/**
* Base class used by classes that extract translation messages from files.
*
@@ -19,13 +21,13 @@ namespace Symfony\Component\Translation\Extractor;
abstract class AbstractFileExtractor
{
/**
* @param string|array $resource files, a file or a directory
* @param string|array $resource Files, a file or a directory
*
* @return array
*/
protected function extractFiles($resource)
{
if (is_array($resource) || $resource instanceof \Traversable) {
if (\is_array($resource) || $resource instanceof \Traversable) {
$files = array();
foreach ($resource as $file) {
if ($this->canBeExtracted($file)) {
@@ -41,14 +43,9 @@ abstract class AbstractFileExtractor
return $files;
}
/**
* @param string $file
*
* @return \SplFileInfo
*/
private function toSplFileInfo($file)
private function toSplFileInfo(string $file): \SplFileInfo
{
return ($file instanceof \SplFileInfo) ? $file : new \SplFileInfo($file);
return new \SplFileInfo($file);
}
/**
@@ -56,12 +53,12 @@ abstract class AbstractFileExtractor
*
* @return bool
*
* @throws \InvalidArgumentException
* @throws InvalidArgumentException
*/
protected function isFile($file)
{
if (!is_file($file)) {
throw new \InvalidArgumentException(sprintf('The "%s" file does not exist.', $file));
throw new InvalidArgumentException(sprintf('The "%s" file does not exist.', $file));
}
return true;
@@ -75,7 +72,7 @@ abstract class AbstractFileExtractor
abstract protected function canBeExtracted($file);
/**
* @param string|array $resource files, a file or a directory
* @param string|array $resource Files, a file or a directory
*
* @return array files to be extracted
*/

View File

@@ -24,7 +24,7 @@ interface ExtractorInterface
/**
* Extracts translation messages from files, a file or a directory to the catalogue.
*
* @param string|array $resource files, a file or a directory
* @param string|array $resource Files, a file or a directory
* @param MessageCatalogue $catalogue The catalogue
*/
public function extract($resource, MessageCatalogue $catalogue);

View File

@@ -0,0 +1,256 @@
<?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\Extractor;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Translation\MessageCatalogue;
/**
* PhpExtractor extracts translation messages from a PHP template.
*
* @author Michel Salib <michelsalib@hotmail.com>
*/
class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface
{
const MESSAGE_TOKEN = 300;
const METHOD_ARGUMENTS_TOKEN = 1000;
const DOMAIN_TOKEN = 1001;
/**
* Prefix for new found message.
*
* @var string
*/
private $prefix = '';
/**
* The sequence that captures translation messages.
*
* @var array
*/
protected $sequences = array(
array(
'->',
'trans',
'(',
self::MESSAGE_TOKEN,
',',
self::METHOD_ARGUMENTS_TOKEN,
',',
self::DOMAIN_TOKEN,
),
array(
'->',
'transChoice',
'(',
self::MESSAGE_TOKEN,
',',
self::METHOD_ARGUMENTS_TOKEN,
',',
self::METHOD_ARGUMENTS_TOKEN,
',',
self::DOMAIN_TOKEN,
),
array(
'->',
'trans',
'(',
self::MESSAGE_TOKEN,
),
array(
'->',
'transChoice',
'(',
self::MESSAGE_TOKEN,
),
);
/**
* {@inheritdoc}
*/
public function extract($resource, MessageCatalogue $catalog)
{
$files = $this->extractFiles($resource);
foreach ($files as $file) {
$this->parseTokens(token_get_all(file_get_contents($file)), $catalog);
// PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098
gc_mem_caches();
}
}
/**
* {@inheritdoc}
*/
public function setPrefix($prefix)
{
$this->prefix = $prefix;
}
/**
* Normalizes a token.
*
* @param mixed $token
*
* @return string
*/
protected function normalizeToken($token)
{
if (isset($token[1]) && 'b"' !== $token) {
return $token[1];
}
return $token;
}
/**
* Seeks to a non-whitespace token.
*/
private function seekToNextRelevantToken(\Iterator $tokenIterator)
{
for (; $tokenIterator->valid(); $tokenIterator->next()) {
$t = $tokenIterator->current();
if (T_WHITESPACE !== $t[0]) {
break;
}
}
}
private function skipMethodArgument(\Iterator $tokenIterator)
{
$openBraces = 0;
for (; $tokenIterator->valid(); $tokenIterator->next()) {
$t = $tokenIterator->current();
if ('[' === $t[0] || '(' === $t[0]) {
++$openBraces;
}
if (']' === $t[0] || ')' === $t[0]) {
--$openBraces;
}
if ((0 === $openBraces && ',' === $t[0]) || (-1 === $openBraces && ')' === $t[0])) {
break;
}
}
}
/**
* Extracts the message from the iterator while the tokens
* match allowed message tokens.
*/
private function getValue(\Iterator $tokenIterator)
{
$message = '';
$docToken = '';
for (; $tokenIterator->valid(); $tokenIterator->next()) {
$t = $tokenIterator->current();
if (!isset($t[1])) {
break;
}
switch ($t[0]) {
case T_START_HEREDOC:
$docToken = $t[1];
break;
case T_ENCAPSED_AND_WHITESPACE:
case T_CONSTANT_ENCAPSED_STRING:
$message .= $t[1];
break;
case T_END_HEREDOC:
return PhpStringTokenParser::parseDocString($docToken, $message);
default:
break 2;
}
}
if ($message) {
$message = PhpStringTokenParser::parse($message);
}
return $message;
}
/**
* Extracts trans message from PHP tokens.
*
* @param array $tokens
* @param MessageCatalogue $catalog
*/
protected function parseTokens($tokens, MessageCatalogue $catalog)
{
$tokenIterator = new \ArrayIterator($tokens);
for ($key = 0; $key < $tokenIterator->count(); ++$key) {
foreach ($this->sequences as $sequence) {
$message = '';
$domain = 'messages';
$tokenIterator->seek($key);
foreach ($sequence as $sequenceKey => $item) {
$this->seekToNextRelevantToken($tokenIterator);
if ($this->normalizeToken($tokenIterator->current()) === $item) {
$tokenIterator->next();
continue;
} elseif (self::MESSAGE_TOKEN === $item) {
$message = $this->getValue($tokenIterator);
if (\count($sequence) === ($sequenceKey + 1)) {
break;
}
} elseif (self::METHOD_ARGUMENTS_TOKEN === $item) {
$this->skipMethodArgument($tokenIterator);
} elseif (self::DOMAIN_TOKEN === $item) {
$domain = $this->getValue($tokenIterator);
break;
} else {
break;
}
}
if ($message) {
$catalog->set($message, $this->prefix.$message, $domain);
break;
}
}
}
}
/**
* @param string $file
*
* @return bool
*
* @throws \InvalidArgumentException
*/
protected function canBeExtracted($file)
{
return $this->isFile($file) && 'php' === pathinfo($file, PATHINFO_EXTENSION);
}
/**
* @param string|array $directory
*
* @return array
*/
protected function extractFromDirectory($directory)
{
$finder = new Finder();
return $finder->files()->name('*.php')->in($directory);
}
}

View File

@@ -0,0 +1,142 @@
<?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\Extractor;
/*
* The following is derived from code at http://github.com/nikic/PHP-Parser
*
* Copyright (c) 2011 by Nikita Popov
*
* Some rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* * The names of the contributors may not be used to endorse or
* promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
class PhpStringTokenParser
{
protected static $replacements = array(
'\\' => '\\',
'$' => '$',
'n' => "\n",
'r' => "\r",
't' => "\t",
'f' => "\f",
'v' => "\v",
'e' => "\x1B",
);
/**
* Parses a string token.
*
* @param string $str String token content
*
* @return string The parsed string
*/
public static function parse($str)
{
$bLength = 0;
if ('b' === $str[0]) {
$bLength = 1;
}
if ('\'' === $str[$bLength]) {
return str_replace(
array('\\\\', '\\\''),
array('\\', '\''),
substr($str, $bLength + 1, -1)
);
} else {
return self::parseEscapeSequences(substr($str, $bLength + 1, -1), '"');
}
}
/**
* Parses escape sequences in strings (all string types apart from single quoted).
*
* @param string $str String without quotes
* @param null|string $quote Quote type
*
* @return string String with escape sequences parsed
*/
public static function parseEscapeSequences($str, $quote)
{
if (null !== $quote) {
$str = str_replace('\\'.$quote, $quote, $str);
}
return preg_replace_callback(
'~\\\\([\\\\$nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3})~',
array(__CLASS__, 'parseCallback'),
$str
);
}
private static function parseCallback($matches)
{
$str = $matches[1];
if (isset(self::$replacements[$str])) {
return self::$replacements[$str];
} elseif ('x' === $str[0] || 'X' === $str[0]) {
return \chr(hexdec($str));
} else {
return \chr(octdec($str));
}
}
/**
* Parses a constant doc string.
*
* @param string $startToken Doc string start token content (<<<SMTHG)
* @param string $str String token content
*
* @return string Parsed string
*/
public static function parseDocString($startToken, $str)
{
// strip last newline (thanks tokenizer for sticking it into the string!)
$str = preg_replace('~(\r\n|\n|\r)$~', '', $str);
// nowdoc string
if (false !== strpos($startToken, '\'')) {
return $str;
}
return self::parseEscapeSequences($str, null);
}
}

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\Formatter;
/**
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
*/
interface ChoiceMessageFormatterInterface
{
/**
* Formats a localized message pattern with given arguments.
*
* @param string $message The message (may also be an object that can be cast to string)
* @param int $number The number to use to find the indice of the message
* @param string $locale The message locale
* @param array $parameters An array of parameters for the message
*
* @return string
*/
public function choiceFormat($message, $number, $locale, array $parameters = array());
}

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\Formatter;
use Symfony\Component\Translation\MessageSelector;
/**
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
*/
class MessageFormatter implements MessageFormatterInterface, ChoiceMessageFormatterInterface
{
private $selector;
/**
* @param MessageSelector|null $selector The message selector for pluralization
*/
public function __construct(MessageSelector $selector = null)
{
$this->selector = $selector ?: new MessageSelector();
}
/**
* {@inheritdoc}
*/
public function format($message, $locale, array $parameters = array())
{
return strtr($message, $parameters);
}
/**
* {@inheritdoc}
*/
public function choiceFormat($message, $number, $locale, array $parameters = array())
{
$parameters = array_merge(array('%count%' => $number), $parameters);
return $this->format($this->selector->choose($message, (int) $number, $locale), $locale, $parameters);
}
}

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\Formatter;
/**
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
*/
interface MessageFormatterInterface
{
/**
* Formats a localized message pattern with given arguments.
*
* @param string $message The message (may also be an object that can be cast to string)
* @param string $locale The message locale
* @param array $parameters An array of parameters for the message
*
* @return string
*/
public function format($message, $locale, array $parameters = array());
}

View File

@@ -22,8 +22,6 @@ class IdentityTranslator implements TranslatorInterface
private $locale;
/**
* Constructor.
*
* @param MessageSelector|null $selector The message selector for pluralization
*/
public function __construct(MessageSelector $selector = null)

View File

@@ -11,6 +11,8 @@
namespace Symfony\Component\Translation;
use Symfony\Component\Translation\Exception\InvalidArgumentException;
/**
* Tests if a given number belongs to a given math interval.
*
@@ -41,14 +43,14 @@ class Interval
*
* @return bool
*
* @throws \InvalidArgumentException
* @throws InvalidArgumentException
*/
public static function test($number, $interval)
{
$interval = trim($interval);
if (!preg_match('/^'.self::getIntervalRegexp().'$/x', $interval, $matches)) {
throw new \InvalidArgumentException(sprintf('"%s" is not a valid interval.', $interval));
throw new InvalidArgumentException(sprintf('"%s" is not a valid interval.', $interval));
}
if ($matches[1]) {

View File

@@ -1,4 +1,4 @@
Copyright (c) 2004-2016 Fabien Potencier
Copyright (c) 2004-2018 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -52,7 +52,7 @@ class ArrayLoader implements LoaderInterface
$subnode = &$messages;
}
foreach ($subnode as $key => $value) {
if (is_array($value)) {
if (\is_array($value)) {
$nodePath = $path ? $path.'.'.$key : $key;
$this->flatten($messages, $value, $nodePath);
if (null === $path) {

View File

@@ -41,7 +41,7 @@ class CsvFileLoader extends FileLoader
$file->setCsvControl($this->delimiter, $this->enclosure, $this->escape);
foreach ($file as $data) {
if ('#' !== substr($data[0], 0, 1) && isset($data[1]) && 2 === count($data)) {
if ('#' !== substr($data[0], 0, 1) && isset($data[1]) && 2 === \count($data)) {
$messages[$data[0]] = $data[1];
}
}
@@ -52,9 +52,9 @@ class CsvFileLoader extends FileLoader
/**
* Sets the delimiter, enclosure, and escape character for CSV.
*
* @param string $delimiter delimiter character
* @param string $enclosure enclosure character
* @param string $escape escape character
* @param string $delimiter Delimiter character
* @param string $enclosure Enclosure character
* @param string $escape Escape character
*/
public function setCsvControl($delimiter = ';', $enclosure = '"', $escape = '\\')
{

View File

@@ -11,9 +11,9 @@
namespace Symfony\Component\Translation\Loader;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Translation\Exception\InvalidResourceException;
use Symfony\Component\Translation\Exception\NotFoundResourceException;
use Symfony\Component\Config\Resource\FileResource;
/**
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
@@ -41,7 +41,7 @@ abstract class FileLoader extends ArrayLoader
}
// not an array
if (!is_array($messages)) {
if (!\is_array($messages)) {
throw new InvalidResourceException(sprintf('Unable to load file "%s".', $resource));
}
@@ -54,12 +54,12 @@ abstract class FileLoader extends ArrayLoader
return $catalogue;
}
/*
/**
* @param string $resource
*
* @return array
*
* @throws InvalidResourceException If stream content has an invalid format.
* @throws InvalidResourceException if stream content has an invalid format
*/
abstract protected function loadResource($resource);
}

View File

@@ -11,10 +11,10 @@
namespace Symfony\Component\Translation\Loader;
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Translation\Exception\InvalidResourceException;
use Symfony\Component\Translation\Exception\NotFoundResourceException;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Translation\MessageCatalogue;
/**
* IcuResFileLoader loads translations from a resource bundle.
@@ -39,7 +39,6 @@ class IcuDatFileLoader extends IcuResFileLoader
try {
$rb = new \ResourceBundle($locale, $resource);
} catch (\Exception $e) {
// HHVM compatibility: constructor throws on invalid resource
$rb = null;
}

View File

@@ -11,10 +11,10 @@
namespace Symfony\Component\Translation\Loader;
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Config\Resource\DirectoryResource;
use Symfony\Component\Translation\Exception\InvalidResourceException;
use Symfony\Component\Translation\Exception\NotFoundResourceException;
use Symfony\Component\Config\Resource\DirectoryResource;
use Symfony\Component\Translation\MessageCatalogue;
/**
* IcuResFileLoader loads translations from a resource bundle.
@@ -39,7 +39,6 @@ class IcuResFileLoader implements LoaderInterface
try {
$rb = new \ResourceBundle($locale, $resource);
} catch (\Exception $e) {
// HHVM compatibility: constructor throws on invalid resource
$rb = null;
}
@@ -70,9 +69,9 @@ class IcuResFileLoader implements LoaderInterface
*
* This function takes an array by reference and will modify it
*
* @param \ResourceBundle $rb the ResourceBundle that will be flattened
* @param array $messages used internally for recursive calls
* @param string $path current path being parsed, used internally for recursive calls
* @param \ResourceBundle $rb The ResourceBundle that will be flattened
* @param array $messages Used internally for recursive calls
* @param string $path Current path being parsed, used internally for recursive calls
*
* @return array the flattened ResourceBundle
*/

View File

@@ -11,9 +11,9 @@
namespace Symfony\Component\Translation\Loader;
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\Exception\InvalidResourceException;
use Symfony\Component\Translation\Exception\NotFoundResourceException;
use Symfony\Component\Translation\MessageCatalogue;
/**
* LoaderInterface is the interface implemented by all translation loaders.

View File

@@ -21,23 +21,17 @@ class MoFileLoader extends FileLoader
/**
* Magic used for validating the format of a MO file as well as
* detecting if the machine used to create that file was little endian.
*
* @var float
*/
const MO_LITTLE_ENDIAN_MAGIC = 0x950412de;
/**
* Magic used for validating the format of a MO file as well as
* detecting if the machine used to create that file was big endian.
*
* @var float
*/
const MO_BIG_ENDIAN_MAGIC = 0xde120495;
/**
* The size of the header of a MO file in bytes.
*
* @var int Number of bytes
*/
const MO_HEADER_SIZE = 28;
@@ -59,9 +53,9 @@ class MoFileLoader extends FileLoader
$magic = unpack('V1', fread($stream, 4));
$magic = hexdec(substr(dechex(current($magic)), -8));
if ($magic == self::MO_LITTLE_ENDIAN_MAGIC) {
if (self::MO_LITTLE_ENDIAN_MAGIC == $magic) {
$isBigEndian = false;
} elseif ($magic == self::MO_BIG_ENDIAN_MAGIC) {
} elseif (self::MO_BIG_ENDIAN_MAGIC == $magic) {
$isBigEndian = true;
} else {
throw new InvalidResourceException('MO stream content has an invalid format.');
@@ -80,7 +74,7 @@ class MoFileLoader extends FileLoader
$messages = array();
for ($i = 0; $i < $count; ++$i) {
$singularId = $pluralId = null;
$pluralId = null;
$translated = null;
fseek($stream, $offsetId + $i * 8);
@@ -95,7 +89,7 @@ class MoFileLoader extends FileLoader
fseek($stream, $offset);
$singularId = fread($stream, $length);
if (strpos($singularId, "\000") !== false) {
if (false !== strpos($singularId, "\000")) {
list($singularId, $pluralId) = explode("\000", $singularId);
}
@@ -110,14 +104,14 @@ class MoFileLoader extends FileLoader
fseek($stream, $offset);
$translated = fread($stream, $length);
if (strpos($translated, "\000") !== false) {
if (false !== strpos($translated, "\000")) {
$translated = explode("\000", $translated);
}
$ids = array('singular' => $singularId, 'plural' => $pluralId);
$item = compact('ids', 'translated');
if (is_array($item['translated'])) {
if (\is_array($item['translated'])) {
$messages[$item['ids']['singular']] = stripcslashes($item['translated'][0]);
if (isset($item['ids']['plural'])) {
$plurals = array();
@@ -137,14 +131,11 @@ class MoFileLoader extends FileLoader
}
/**
* Reads an unsigned long from stream respecting endianess.
* Reads an unsigned long from stream respecting endianness.
*
* @param resource $stream
* @param bool $isBigEndian
*
* @return int
*/
private function readLong($stream, $isBigEndian)
private function readLong($stream, bool $isBigEndian): int
{
$result = unpack($isBigEndian ? 'N1' : 'V1', fread($stream, 4));
$result = current($result);

View File

@@ -76,41 +76,41 @@ class PoFileLoader extends FileLoader
while ($line = fgets($stream)) {
$line = trim($line);
if ($line === '') {
if ('' === $line) {
// Whitespace indicated current item is done
if (!in_array('fuzzy', $flags)) {
if (!\in_array('fuzzy', $flags)) {
$this->addMessage($messages, $item);
}
$item = $defaults;
$flags = array();
} elseif (substr($line, 0, 2) === '#,') {
} elseif ('#,' === substr($line, 0, 2)) {
$flags = array_map('trim', explode(',', substr($line, 2)));
} elseif (substr($line, 0, 7) === 'msgid "') {
} elseif ('msgid "' === substr($line, 0, 7)) {
// We start a new msg so save previous
// TODO: this fails when comments or contexts are added
$this->addMessage($messages, $item);
$item = $defaults;
$item['ids']['singular'] = substr($line, 7, -1);
} elseif (substr($line, 0, 8) === 'msgstr "') {
} elseif ('msgstr "' === substr($line, 0, 8)) {
$item['translated'] = substr($line, 8, -1);
} elseif ($line[0] === '"') {
} elseif ('"' === $line[0]) {
$continues = isset($item['translated']) ? 'translated' : 'ids';
if (is_array($item[$continues])) {
if (\is_array($item[$continues])) {
end($item[$continues]);
$item[$continues][key($item[$continues])] .= substr($line, 1, -1);
} else {
$item[$continues] .= substr($line, 1, -1);
}
} elseif (substr($line, 0, 14) === 'msgid_plural "') {
} elseif ('msgid_plural "' === substr($line, 0, 14)) {
$item['ids']['plural'] = substr($line, 14, -1);
} elseif (substr($line, 0, 7) === 'msgstr[') {
} elseif ('msgstr[' === substr($line, 0, 7)) {
$size = strpos($line, ']');
$item['translated'][(int) substr($line, 7, 1)] = substr($line, $size + 3, -1);
}
}
// save last item
if (!in_array('fuzzy', $flags)) {
if (!\in_array('fuzzy', $flags)) {
$this->addMessage($messages, $item);
}
fclose($stream);
@@ -123,13 +123,10 @@ class PoFileLoader extends FileLoader
*
* A .po file could contain by error missing plural indexes. We need to
* fix these before saving them.
*
* @param array $messages
* @param array $item
*/
private function addMessage(array &$messages, array $item)
{
if (is_array($item['translated'])) {
if (\is_array($item['translated'])) {
$messages[stripcslashes($item['ids']['singular'])] = stripcslashes($item['translated'][0]);
if (isset($item['ids']['plural'])) {
$plurals = $item['translated'];

View File

@@ -11,11 +11,11 @@
namespace Symfony\Component\Translation\Loader;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Config\Util\XmlUtils;
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\Exception\InvalidResourceException;
use Symfony\Component\Translation\Exception\NotFoundResourceException;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Translation\MessageCatalogue;
/**
* QtFileLoader loads translations from QT Translations XML files.
@@ -50,7 +50,7 @@ class QtFileLoader implements LoaderInterface
$nodes = $xpath->evaluate('//TS/context/name[text()="'.$domain.'"]');
$catalogue = new MessageCatalogue($locale);
if ($nodes->length == 1) {
if (1 == $nodes->length) {
$translations = $nodes->item(0)->nextSibling->parentNode->parentNode->getElementsByTagName('message');
foreach ($translations as $translation) {
$translationValue = (string) $translation->getElementsByTagName('translation')->item(0)->nodeValue;

View File

@@ -11,11 +11,12 @@
namespace Symfony\Component\Translation\Loader;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Config\Util\XmlUtils;
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\Exception\InvalidArgumentException;
use Symfony\Component\Translation\Exception\InvalidResourceException;
use Symfony\Component\Translation\Exception\NotFoundResourceException;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Translation\MessageCatalogue;
/**
* XliffFileLoader loads translations from XLIFF files.
@@ -74,7 +75,7 @@ class XliffFileLoader implements LoaderInterface
* @param MessageCatalogue $catalogue Catalogue where we'll collect messages and metadata
* @param string $domain The domain
*/
private function extractXliff1(\DOMDocument $dom, MessageCatalogue $catalogue, $domain)
private function extractXliff1(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain)
{
$xml = simplexml_import_dom($dom);
$encoding = strtoupper($dom->encoding);
@@ -98,6 +99,7 @@ class XliffFileLoader implements LoaderInterface
if ($notes = $this->parseNotesMetadata($translation->note, $encoding)) {
$metadata['notes'] = $notes;
}
if (isset($translation->target) && $translation->target->attributes()) {
$metadata['target-attributes'] = array();
foreach ($translation->target->attributes() as $key => $value) {
@@ -105,52 +107,60 @@ class XliffFileLoader implements LoaderInterface
}
}
$catalogue->setMetadata((string) $source, $metadata, $domain);
}
}
/**
* @param \DOMDocument $dom
* @param MessageCatalogue $catalogue
* @param string $domain
*/
private function extractXliff2(\DOMDocument $dom, MessageCatalogue $catalogue, $domain)
{
$xml = simplexml_import_dom($dom);
$encoding = strtoupper($dom->encoding);
$xml->registerXPathNamespace('xliff', 'urn:oasis:names:tc:xliff:document:2.0');
foreach ($xml->xpath('//xliff:unit/xliff:segment') as $segment) {
$source = $segment->source;
// If the xlf file has another encoding specified, try to convert it because
// simple_xml will always return utf-8 encoded values
$target = $this->utf8ToCharset((string) (isset($segment->target) ? $segment->target : $source), $encoding);
$catalogue->set((string) $source, $target, $domain);
$metadata = array();
if (isset($segment->target) && $segment->target->attributes()) {
$metadata['target-attributes'] = array();
foreach ($segment->target->attributes() as $key => $value) {
$metadata['target-attributes'][$key] = (string) $value;
}
if (isset($attributes['id'])) {
$metadata['id'] = (string) $attributes['id'];
}
$catalogue->setMetadata((string) $source, $metadata, $domain);
}
}
private function extractXliff2(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain)
{
$xml = simplexml_import_dom($dom);
$encoding = strtoupper($dom->encoding);
$xml->registerXPathNamespace('xliff', 'urn:oasis:names:tc:xliff:document:2.0');
foreach ($xml->xpath('//xliff:unit') as $unit) {
foreach ($unit->segment as $segment) {
$source = $segment->source;
// If the xlf file has another encoding specified, try to convert it because
// simple_xml will always return utf-8 encoded values
$target = $this->utf8ToCharset((string) (isset($segment->target) ? $segment->target : $source), $encoding);
$catalogue->set((string) $source, $target, $domain);
$metadata = array();
if (isset($segment->target) && $segment->target->attributes()) {
$metadata['target-attributes'] = array();
foreach ($segment->target->attributes() as $key => $value) {
$metadata['target-attributes'][$key] = (string) $value;
}
}
if (isset($unit->notes)) {
$metadata['notes'] = array();
foreach ($unit->notes->note as $noteNode) {
$note = array();
foreach ($noteNode->attributes() as $key => $value) {
$note[$key] = (string) $value;
}
$note['content'] = (string) $noteNode;
$metadata['notes'][] = $note;
}
}
$catalogue->setMetadata((string) $source, $metadata, $domain);
}
}
}
/**
* Convert a UTF8 string to the specified encoding.
*
* @param string $content String to decode
* @param string $encoding Target encoding
*
* @return string
*/
private function utf8ToCharset($content, $encoding = null)
private function utf8ToCharset(string $content, string $encoding = null): string
{
if ('UTF-8' !== $encoding && !empty($encoding)) {
return mb_convert_encoding($content, $encoding, 'UTF-8');
@@ -162,13 +172,9 @@ class XliffFileLoader implements LoaderInterface
/**
* Validates and parses the given file into a DOMDocument.
*
* @param string $file
* @param \DOMDocument $dom
* @param string $schema source of the schema
*
* @throws InvalidResourceException
*/
private function validateSchema($file, \DOMDocument $dom, $schema)
private function validateSchema(string $file, \DOMDocument $dom, string $schema)
{
$internalErrors = libxml_use_internal_errors(true);
@@ -197,7 +203,7 @@ class XliffFileLoader implements LoaderInterface
$schemaSource = file_get_contents(__DIR__.'/schema/dic/xliff-core/xliff-core-2.0.xsd');
$xmlUri = 'informativeCopiesOf3rdPartySchemas/w3c/xml.xsd';
} else {
throw new \InvalidArgumentException(sprintf('No support implemented for loading XLIFF version "%s".', $xliffVersion));
throw new InvalidArgumentException(sprintf('No support implemented for loading XLIFF version "%s".', $xliffVersion));
}
return $this->fixXmlLocation($schemaSource, $xmlUri);
@@ -205,37 +211,33 @@ class XliffFileLoader implements LoaderInterface
/**
* Internally changes the URI of a dependent xsd to be loaded locally.
*
* @param string $schemaSource Current content of schema file
* @param string $xmlUri External URI of XML to convert to local
*
* @return string
*/
private function fixXmlLocation($schemaSource, $xmlUri)
private function fixXmlLocation(string $schemaSource, string $xmlUri): string
{
$newPath = str_replace('\\', '/', __DIR__).'/schema/dic/xliff-core/xml.xsd';
$parts = explode('/', $newPath);
$locationstart = 'file:///';
if (0 === stripos($newPath, 'phar://')) {
$tmpfile = tempnam(sys_get_temp_dir(), 'sf2');
$tmpfile = tempnam(sys_get_temp_dir(), 'symfony');
if ($tmpfile) {
copy($newPath, $tmpfile);
$parts = explode('/', str_replace('\\', '/', $tmpfile));
} else {
array_shift($parts);
$locationstart = 'phar:///';
}
}
$drive = '\\' === DIRECTORY_SEPARATOR ? array_shift($parts).'/' : '';
$newPath = 'file:///'.$drive.implode('/', array_map('rawurlencode', $parts));
$drive = '\\' === \DIRECTORY_SEPARATOR ? array_shift($parts).'/' : '';
$newPath = $locationstart.$drive.implode('/', array_map('rawurlencode', $parts));
return str_replace($xmlUri, $newPath, $schemaSource);
}
/**
* Returns the XML errors of the internal XML parser.
*
* @param bool $internalErrors
*
* @return array An array of errors
*/
private function getXmlErrors($internalErrors)
private function getXmlErrors(bool $internalErrors): array
{
$errors = array();
foreach (libxml_get_errors() as $error) {
@@ -259,13 +261,9 @@ class XliffFileLoader implements LoaderInterface
* Gets xliff file version based on the root "version" attribute.
* Defaults to 1.2 for backwards compatibility.
*
* @param \DOMDocument $dom
*
* @throws \InvalidArgumentException
*
* @return string
* @throws InvalidArgumentException
*/
private function getVersionNumber(\DOMDocument $dom)
private function getVersionNumber(\DOMDocument $dom): string
{
/** @var \DOMNode $xliff */
foreach ($dom->getElementsByTagName('xliff') as $xliff) {
@@ -276,8 +274,8 @@ class XliffFileLoader implements LoaderInterface
$namespace = $xliff->attributes->getNamedItem('xmlns');
if ($namespace) {
if (substr_compare('urn:oasis:names:tc:xliff:document:', $namespace->nodeValue, 0, 34) !== 0) {
throw new \InvalidArgumentException(sprintf('Not a valid XLIFF namespace "%s"', $namespace));
if (0 !== substr_compare('urn:oasis:names:tc:xliff:document:', $namespace->nodeValue, 0, 34)) {
throw new InvalidArgumentException(sprintf('Not a valid XLIFF namespace "%s"', $namespace));
}
return substr($namespace, 34);
@@ -288,13 +286,7 @@ class XliffFileLoader implements LoaderInterface
return '1.2';
}
/*
* @param \SimpleXMLElement|null $noteElement
* @param string|null $encoding
*
* @return array
*/
private function parseNotesMetadata(\SimpleXMLElement $noteElement = null, $encoding = null)
private function parseNotesMetadata(\SimpleXMLElement $noteElement = null, string $encoding = null): array
{
$notes = array();
@@ -302,6 +294,7 @@ class XliffFileLoader implements LoaderInterface
return $notes;
}
/** @var \SimpleXMLElement $xmlNote */
foreach ($noteElement as $xmlNote) {
$noteAttributes = $xmlNote->attributes();
$note = array('content' => $this->utf8ToCharset((string) $xmlNote, $encoding));

View File

@@ -12,8 +12,10 @@
namespace Symfony\Component\Translation\Loader;
use Symfony\Component\Translation\Exception\InvalidResourceException;
use Symfony\Component\Yaml\Parser as YamlParser;
use Symfony\Component\Translation\Exception\LogicException;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Parser as YamlParser;
use Symfony\Component\Yaml\Yaml;
/**
* YamlFileLoader loads translations from Yaml files.
@@ -31,14 +33,14 @@ class YamlFileLoader extends FileLoader
{
if (null === $this->yamlParser) {
if (!class_exists('Symfony\Component\Yaml\Parser')) {
throw new \LogicException('Loading translations from the YAML format requires the Symfony Yaml component.');
throw new LogicException('Loading translations from the YAML format requires the Symfony Yaml component.');
}
$this->yamlParser = new YamlParser();
}
try {
$messages = $this->yamlParser->parse(file_get_contents($resource));
$messages = $this->yamlParser->parseFile($resource, Yaml::PARSE_CONSTANT);
} catch (ParseException $e) {
throw new InvalidResourceException(sprintf('Error parsing YAML, invalid file "%s"', $resource), 0, $e);
}

View File

@@ -12,6 +12,7 @@
namespace Symfony\Component\Translation;
use Psr\Log\LoggerInterface;
use Symfony\Component\Translation\Exception\InvalidArgumentException;
/**
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
@@ -23,9 +24,6 @@ class LoggingTranslator implements TranslatorInterface, TranslatorBagInterface
*/
private $translator;
/**
* @var LoggerInterface
*/
private $logger;
/**
@@ -35,7 +33,7 @@ class LoggingTranslator implements TranslatorInterface, TranslatorBagInterface
public function __construct(TranslatorInterface $translator, LoggerInterface $logger)
{
if (!$translator instanceof TranslatorBagInterface) {
throw new \InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface and TranslatorBagInterface.', get_class($translator)));
throw new InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface and TranslatorBagInterface.', \get_class($translator)));
}
$this->translator = $translator;
@@ -88,12 +86,26 @@ class LoggingTranslator implements TranslatorInterface, TranslatorBagInterface
return $this->translator->getCatalogue($locale);
}
/**
* Gets the fallback locales.
*
* @return array $locales The fallback locales
*/
public function getFallbackLocales()
{
if ($this->translator instanceof Translator || method_exists($this->translator, 'getFallbackLocales')) {
return $this->translator->getFallbackLocales();
}
return array();
}
/**
* Passes through all unknown calls onto the translator object.
*/
public function __call($method, $args)
{
return call_user_func_array(array($this->translator, $method), $args);
return \call_user_func_array(array($this->translator, $method), $args);
}
/**

View File

@@ -12,10 +12,9 @@
namespace Symfony\Component\Translation;
use Symfony\Component\Config\Resource\ResourceInterface;
use Symfony\Component\Translation\Exception\LogicException;
/**
* MessageCatalogue.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterface
@@ -28,12 +27,10 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
private $parent;
/**
* Constructor.
*
* @param string $locale The locale
* @param array $messages An array of messages classified by domain
*/
public function __construct($locale, array $messages = array())
public function __construct(?string $locale, array $messages = array())
{
$this->locale = $locale;
$this->messages = $messages;
@@ -143,7 +140,7 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
public function addCatalogue(MessageCatalogueInterface $catalogue)
{
if ($catalogue->getLocale() !== $this->locale) {
throw new \LogicException(sprintf('Cannot add a catalogue for locale "%s" as the current locale for this catalogue is "%s"', $catalogue->getLocale(), $this->locale));
throw new LogicException(sprintf('Cannot add a catalogue for locale "%s" as the current locale for this catalogue is "%s"', $catalogue->getLocale(), $this->locale));
}
foreach ($catalogue->all() as $domain => $messages) {
@@ -169,14 +166,18 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
$c = $catalogue;
while ($c = $c->getFallbackCatalogue()) {
if ($c->getLocale() === $this->getLocale()) {
throw new \LogicException(sprintf('Circular reference detected when adding a fallback catalogue for locale "%s".', $catalogue->getLocale()));
throw new LogicException(sprintf('Circular reference detected when adding a fallback catalogue for locale "%s".', $catalogue->getLocale()));
}
}
$c = $this;
do {
if ($c->getLocale() === $catalogue->getLocale()) {
throw new \LogicException(sprintf('Circular reference detected when adding a fallback catalogue for locale "%s".', $catalogue->getLocale()));
throw new LogicException(sprintf('Circular reference detected when adding a fallback catalogue for locale "%s".', $catalogue->getLocale()));
}
foreach ($catalogue->getResources() as $resource) {
$c->addResource($resource);
}
} while ($c = $c->parent);

View File

@@ -104,25 +104,21 @@ interface MessageCatalogueInterface
* Merges translations from the given Catalogue into the current one.
*
* The two catalogues must have the same locale.
*
* @param MessageCatalogueInterface $catalogue A MessageCatalogueInterface instance
*/
public function addCatalogue(MessageCatalogueInterface $catalogue);
public function addCatalogue(self $catalogue);
/**
* Merges translations from the given Catalogue into the current one
* only when the translation does not exist.
*
* This is used to provide default translations when they do not exist for the current locale.
*
* @param MessageCatalogueInterface $catalogue A MessageCatalogueInterface instance
*/
public function addFallbackCatalogue(MessageCatalogueInterface $catalogue);
public function addFallbackCatalogue(self $catalogue);
/**
* Gets the fallback catalogue.
*
* @return MessageCatalogueInterface|null A MessageCatalogueInterface instance or null when no fallback has been set
* @return self|null A MessageCatalogueInterface instance or null when no fallback has been set
*/
public function getFallbackCatalogue();
@@ -135,8 +131,6 @@ interface MessageCatalogueInterface
/**
* Adds a resource for this collection.
*
* @param ResourceInterface $resource A resource instance
*/
public function addResource(ResourceInterface $resource);
}

View File

@@ -11,6 +11,8 @@
namespace Symfony\Component\Translation;
use Symfony\Component\Translation\Exception\InvalidArgumentException;
/**
* MessageSelector.
*
@@ -43,15 +45,21 @@ class MessageSelector
*
* @return string
*
* @throws \InvalidArgumentException
* @throws InvalidArgumentException
*/
public function choose($message, $number, $locale)
{
$parts = explode('|', $message);
$parts = array();
if (preg_match('/^\|++$/', $message)) {
$parts = explode('|', $message);
} elseif (preg_match_all('/(?:\|\||[^\|])++/', $message, $matches)) {
$parts = $matches[0];
}
$explicitRules = array();
$standardRules = array();
foreach ($parts as $part) {
$part = trim($part);
$part = trim(str_replace('||', '|', $part));
if (preg_match('/^(?P<interval>'.Interval::getIntervalRegexp().')\s*(?P<message>.*?)$/xs', $part, $matches)) {
$explicitRules[$matches['interval']] = $matches['message'];
@@ -74,11 +82,11 @@ class MessageSelector
if (!isset($standardRules[$position])) {
// when there's exactly one rule given, and that rule is a standard
// rule, use this rule
if (1 === count($parts) && isset($standardRules[0])) {
if (1 === \count($parts) && isset($standardRules[0])) {
return $standardRules[0];
}
throw new \InvalidArgumentException(sprintf('Unable to choose a translation for "%s" with locale "%s" for value "%d". Double check that this translation has the correct plural options (e.g. "There is one apple|There are %%count%% apples").', $message, $locale, $number));
throw new InvalidArgumentException(sprintf('Unable to choose a translation for "%s" with locale "%s" for value "%d". Double check that this translation has the correct plural options (e.g. "There is one apple|There are %%count%% apples").', $message, $locale, $number));
}
return $standardRules[$position];

View File

@@ -35,14 +35,14 @@ class PluralizationRules
$locale = 'xbr';
}
if (strlen($locale) > 3) {
$locale = substr($locale, 0, -strlen(strrchr($locale, '_')));
if (\strlen($locale) > 3) {
$locale = substr($locale, 0, -\strlen(strrchr($locale, '_')));
}
if (isset(self::$rules[$locale])) {
$return = call_user_func(self::$rules[$locale], $number);
$return = \call_user_func(self::$rules[$locale], $number);
if (!is_int($return) || $return < 0) {
if (!\is_int($return) || $return < 0) {
return 0;
}
@@ -71,7 +71,6 @@ class PluralizationRules
case 'vi':
case 'zh':
return 0;
break;
case 'af':
case 'bn':
@@ -108,6 +107,7 @@ class PluralizationRules
case 'nl':
case 'nn':
case 'no':
case 'oc':
case 'om':
case 'or':
case 'pa':
@@ -123,7 +123,7 @@ class PluralizationRules
case 'tk':
case 'ur':
case 'zu':
return ($number == 1) ? 0 : 1;
return (1 == $number) ? 0 : 1;
case 'am':
case 'bh':
@@ -138,49 +138,50 @@ class PluralizationRules
case 'xbr':
case 'ti':
case 'wa':
return (($number == 0) || ($number == 1)) ? 0 : 1;
return ((0 == $number) || (1 == $number)) ? 0 : 1;
case 'be':
case 'bs':
case 'hr':
case 'ru':
case 'sh':
case 'sr':
case 'uk':
return (($number % 10 == 1) && ($number % 100 != 11)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
return ((1 == $number % 10) && (11 != $number % 100)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
case 'cs':
case 'sk':
return ($number == 1) ? 0 : ((($number >= 2) && ($number <= 4)) ? 1 : 2);
return (1 == $number) ? 0 : ((($number >= 2) && ($number <= 4)) ? 1 : 2);
case 'ga':
return ($number == 1) ? 0 : (($number == 2) ? 1 : 2);
return (1 == $number) ? 0 : ((2 == $number) ? 1 : 2);
case 'lt':
return (($number % 10 == 1) && ($number % 100 != 11)) ? 0 : ((($number % 10 >= 2) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
return ((1 == $number % 10) && (11 != $number % 100)) ? 0 : ((($number % 10 >= 2) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
case 'sl':
return ($number % 100 == 1) ? 0 : (($number % 100 == 2) ? 1 : ((($number % 100 == 3) || ($number % 100 == 4)) ? 2 : 3));
return (1 == $number % 100) ? 0 : ((2 == $number % 100) ? 1 : (((3 == $number % 100) || (4 == $number % 100)) ? 2 : 3));
case 'mk':
return ($number % 10 == 1) ? 0 : 1;
return (1 == $number % 10) ? 0 : 1;
case 'mt':
return ($number == 1) ? 0 : ((($number == 0) || (($number % 100 > 1) && ($number % 100 < 11))) ? 1 : ((($number % 100 > 10) && ($number % 100 < 20)) ? 2 : 3));
return (1 == $number) ? 0 : (((0 == $number) || (($number % 100 > 1) && ($number % 100 < 11))) ? 1 : ((($number % 100 > 10) && ($number % 100 < 20)) ? 2 : 3));
case 'lv':
return ($number == 0) ? 0 : ((($number % 10 == 1) && ($number % 100 != 11)) ? 1 : 2);
return (0 == $number) ? 0 : (((1 == $number % 10) && (11 != $number % 100)) ? 1 : 2);
case 'pl':
return ($number == 1) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 12) || ($number % 100 > 14))) ? 1 : 2);
return (1 == $number) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 12) || ($number % 100 > 14))) ? 1 : 2);
case 'cy':
return ($number == 1) ? 0 : (($number == 2) ? 1 : ((($number == 8) || ($number == 11)) ? 2 : 3));
return (1 == $number) ? 0 : ((2 == $number) ? 1 : (((8 == $number) || (11 == $number)) ? 2 : 3));
case 'ro':
return ($number == 1) ? 0 : ((($number == 0) || (($number % 100 > 0) && ($number % 100 < 20))) ? 1 : 2);
return (1 == $number) ? 0 : (((0 == $number) || (($number % 100 > 0) && ($number % 100 < 20))) ? 1 : 2);
case 'ar':
return ($number == 0) ? 0 : (($number == 1) ? 1 : (($number == 2) ? 2 : ((($number % 100 >= 3) && ($number % 100 <= 10)) ? 3 : ((($number % 100 >= 11) && ($number % 100 <= 99)) ? 4 : 5))));
return (0 == $number) ? 0 : ((1 == $number) ? 1 : ((2 == $number) ? 2 : ((($number % 100 >= 3) && ($number % 100 <= 10)) ? 3 : ((($number % 100 >= 11) && ($number % 100 <= 99)) ? 4 : 5))));
default:
return 0;
@@ -200,8 +201,8 @@ class PluralizationRules
$locale = 'xbr';
}
if (strlen($locale) > 3) {
$locale = substr($locale, 0, -strlen(strrchr($locale, '_')));
if (\strlen($locale) > 3) {
$locale = substr($locale, 0, -\strlen(strrchr($locale, '_')));
}
self::$rules[$locale] = $rule;

View File

@@ -0,0 +1,63 @@
<?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\Reader;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Translation\Loader\LoaderInterface;
use Symfony\Component\Translation\MessageCatalogue;
/**
* TranslationReader reads translation messages from translation files.
*
* @author Michel Salib <michelsalib@hotmail.com>
*/
class TranslationReader implements TranslationReaderInterface
{
/**
* Loaders used for import.
*
* @var array
*/
private $loaders = array();
/**
* Adds a loader to the translation extractor.
*
* @param string $format The format of the loader
* @param LoaderInterface $loader
*/
public function addLoader($format, LoaderInterface $loader)
{
$this->loaders[$format] = $loader;
}
/**
* {@inheritdoc}
*/
public function read($directory, MessageCatalogue $catalogue)
{
if (!is_dir($directory)) {
return;
}
foreach ($this->loaders as $format => $loader) {
// load any existing translation files
$finder = new Finder();
$extension = $catalogue->getLocale().'.'.$format;
$files = $finder->files()->name('*.'.$extension)->in($directory);
foreach ($files as $file) {
$domain = substr($file->getFilename(), 0, -1 * \strlen($extension) - 1);
$catalogue->addCatalogue($loader->load($file->getPathname(), $catalogue->getLocale(), $domain));
}
}
}
}

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\Reader;
use Symfony\Component\Translation\MessageCatalogue;
/**
* TranslationReader reads translation messages from translation files.
*
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
interface TranslationReaderInterface
{
/**
* Reads translation messages from a directory to the catalogue.
*
* @param string $directory
* @param MessageCatalogue $catalogue
*/
public function read($directory, MessageCatalogue $catalogue);
}

File diff suppressed because it is too large Load Diff

View File

@@ -11,10 +11,11 @@
namespace Symfony\Component\Translation\Tests\Catalogue;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\MessageCatalogueInterface;
abstract class AbstractOperationTest extends \PHPUnit_Framework_TestCase
abstract class AbstractOperationTest extends TestCase
{
public function testGetEmptyDomains()
{
@@ -40,7 +41,7 @@ abstract class AbstractOperationTest extends \PHPUnit_Framework_TestCase
public function testGetMessagesFromUnknownDomain()
{
$this->setExpectedException('InvalidArgumentException');
$this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('InvalidArgumentException');
$this->createOperation(
new MessageCatalogue('en'),
new MessageCatalogue('en')

View File

@@ -0,0 +1,163 @@
<?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(
array('filename' => $filename),
array('verbosity' => OutputInterface::VERBOSITY_VERBOSE, 'decorated' => false)
);
$this->assertEquals(0, $tester->getStatusCode(), 'Returns 0 in case of success');
$this->assertContains('OK', trim($tester->getDisplay()));
}
public function testLintIncorrectXmlSyntax()
{
$tester = $this->createCommandTester();
$filename = $this->createFile('note <target>');
$tester->execute(array('filename' => $filename), array('decorated' => false));
$this->assertEquals(1, $tester->getStatusCode(), 'Returns 1 in case of error');
$this->assertContains('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(array('filename' => $filename), array('decorated' => false));
$this->assertEquals(1, $tester->getStatusCode(), 'Returns 1 in case of error');
$this->assertContains('There is a mismatch between the file extension ("en.xlf") and the "es" value used in the "target-language" attribute of the file.', trim($tester->getDisplay()));
}
/**
* @expectedException \RuntimeException
*/
public function testLintFileNotReadable()
{
$tester = $this->createCommandTester();
$filename = $this->createFile();
unlink($filename);
$tester->execute(array('filename' => $filename), array('decorated' => false));
}
public function testGetHelp()
{
$command = new XliffLintCommand();
$expected = <<<EOF
The <info>%command.name%</info> command lints a XLIFF file and outputs to STDOUT
the first encountered syntax error.
You can validates XLIFF contents passed from STDIN:
<info>cat filename | php %command.full_name%</info>
You can also validate the syntax of a file:
<info>php %command.full_name% filename</info>
Or of a whole directory:
<info>php %command.full_name% dirname</info>
<info>php %command.full_name% dirname --format=json</info>
EOF;
$this->assertEquals($expected, $command->getHelp());
}
/**
* @return string Path to the new file
*/
private function createFile($sourceContent = 'note', $targetLanguage = 'en')
{
$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/messages.en.xlf', sys_get_temp_dir());
file_put_contents($filename, $xliffContent);
$this->files[] = $filename;
return $filename;
}
/**
* @return CommandTester
*/
private function createCommandTester($application = null)
{
if (!$application) {
$application = new Application();
$application->add(new XliffLintCommand());
}
$command = $application->find('lint:xliff');
if ($application) {
$command->setApplication($application);
}
return new CommandTester($command);
}
protected function setUp()
{
$this->files = array();
@mkdir(sys_get_temp_dir().'/translation-xliff-lint-test');
}
protected function tearDown()
{
foreach ($this->files as $file) {
if (file_exists($file)) {
unlink($file);
}
}
rmdir(sys_get_temp_dir().'/translation-xliff-lint-test');
}
}

View File

@@ -11,10 +11,11 @@
namespace Symfony\Component\Translation\Tests\DataCollector;
use Symfony\Component\Translation\DataCollectorTranslator;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\DataCollector\TranslationDataCollector;
use Symfony\Component\Translation\DataCollectorTranslator;
class TranslationDataCollectorTest extends \PHPUnit_Framework_TestCase
class TranslationDataCollectorTest extends TestCase
{
protected function setUp()
{
@@ -34,7 +35,7 @@ class TranslationDataCollectorTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(0, $dataCollector->getCountMissings());
$this->assertEquals(0, $dataCollector->getCountFallbacks());
$this->assertEquals(0, $dataCollector->getCountDefines());
$this->assertEquals(array(), $dataCollector->getMessages());
$this->assertEquals(array(), $dataCollector->getMessages()->getValue());
}
public function testCollect()
@@ -132,7 +133,8 @@ class TranslationDataCollectorTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(1, $dataCollector->getCountMissings());
$this->assertEquals(1, $dataCollector->getCountFallbacks());
$this->assertEquals(1, $dataCollector->getCountDefines());
$this->assertEquals($expectedMessages, array_values($dataCollector->getMessages()));
$this->assertEquals($expectedMessages, array_values($dataCollector->getMessages()->getValue(true)));
}
private function getTranslator()

View File

@@ -11,11 +11,12 @@
namespace Symfony\Component\Translation\Tests;
use Symfony\Component\Translation\Translator;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\DataCollectorTranslator;
use Symfony\Component\Translation\Loader\ArrayLoader;
use Symfony\Component\Translation\Translator;
class DataCollectorTranslatorTest extends \PHPUnit_Framework_TestCase
class DataCollectorTranslatorTest extends TestCase
{
public function testCollectMessages()
{
@@ -86,8 +87,6 @@ class DataCollectorTranslatorTest extends \PHPUnit_Framework_TestCase
$translator->addResource('array', array('bar' => 'bar (fr)'), 'fr');
$translator->addResource('array', array('bar_ru' => 'bar (ru)'), 'ru');
$collector = new DataCollectorTranslator($translator);
return $collector;
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', array('alias' => 'bar.alias'));
$translationDumperPass = new TranslationDumperPass();
$translationDumperPass->process($container);
$this->assertEquals(array(array('addDumper', array('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,66 @@
<?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', array('alias' => 'bar.alias'));
$translationDumperPass = new TranslationExtractorPass();
$translationDumperPass->process($container);
$this->assertEquals(array(array('addExtractor', array('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());
}
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
* @expectedExceptionMessage The alias for the tag "translation.extractor" of service "foo.id" must be set.
*/
public function testProcessMissingAlias()
{
$definition = $this->getMockBuilder('Symfony\Component\DependencyInjection\Definition')->disableOriginalConstructor()->getMock();
$container = new ContainerBuilder();
$container->register('translation.extractor');
$container->register('foo.id')
->addTag('translation.extractor', array());
$definition->expects($this->never())->method('addMethodCall');
$translationDumperPass = new TranslationExtractorPass();
$translationDumperPass->process($container);
}
}

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\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', array('alias' => 'xliff', 'legacy-alias' => 'xlf'));
$reader = new Definition();
$translator = (new Definition())
->setArguments(array(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', array('xliff', new Reference('translation.xliff_loader')))
->addMethodCall('addLoader', array('xlf', new Reference('translation.xliff_loader')))
;
$this->assertEquals($expectedReader, $reader);
$expectedLoader = (new Definition())
->addTag('translation.loader', array('alias' => 'xliff', 'legacy-alias' => 'xlf'))
;
$this->assertEquals($expectedLoader, $loader);
$this->assertSame(array('translation.xliff_loader' => array('xliff', 'xlf')), $translator->getArgument(3));
$expected = array('translation.xliff_loader' => new ServiceClosureArgument(new Reference('translation.xliff_loader')));
$this->assertEquals($expected, $container->getDefinition((string) $translator->getArgument(0))->getArgument(0));
}
}

View File

@@ -11,10 +11,11 @@
namespace Symfony\Component\Translation\Tests\Dumper;
use Symfony\Component\Translation\MessageCatalogue;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Dumper\CsvFileDumper;
use Symfony\Component\Translation\MessageCatalogue;
class CsvFileDumperTest extends \PHPUnit_Framework_TestCase
class CsvFileDumperTest extends TestCase
{
public function testFormatCatalogue()
{

View File

@@ -11,10 +11,11 @@
namespace Symfony\Component\Translation\Tests\Dumper;
use Symfony\Component\Translation\MessageCatalogue;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Dumper\FileDumper;
use Symfony\Component\Translation\MessageCatalogue;
class FileDumperTest extends \PHPUnit_Framework_TestCase
class FileDumperTest extends TestCase
{
public function testDump()
{
@@ -26,27 +27,9 @@ class FileDumperTest extends \PHPUnit_Framework_TestCase
$dumper = new ConcreteFileDumper();
$dumper->dump($catalogue, array('path' => $tempDir));
$this->assertTrue(file_exists($tempDir.'/messages.en.concrete'));
}
$this->assertFileExists($tempDir.'/messages.en.concrete');
public function testDumpBackupsFileIfExisting()
{
$tempDir = sys_get_temp_dir();
$file = $tempDir.'/messages.en.concrete';
$backupFile = $file.'~';
@touch($file);
$catalogue = new MessageCatalogue('en');
$catalogue->add(array('foo' => 'bar'));
$dumper = new ConcreteFileDumper();
$dumper->dump($catalogue, array('path' => $tempDir));
$this->assertTrue(file_exists($backupFile));
@unlink($file);
@unlink($backupFile);
@unlink($tempDir.'/messages.en.concrete');
}
public function testDumpCreatesNestedDirectoriesAndFile()
@@ -62,7 +45,7 @@ class FileDumperTest extends \PHPUnit_Framework_TestCase
$dumper->setRelativePathTemplate('test/translations/%domain%.%locale%.%extension%');
$dumper->dump($catalogue, array('path' => $tempDir));
$this->assertTrue(file_exists($file));
$this->assertFileExists($file);
@unlink($file);
@rmdir($translationsDir);

View File

@@ -11,10 +11,11 @@
namespace Symfony\Component\Translation\Tests\Dumper;
use Symfony\Component\Translation\MessageCatalogue;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Dumper\IcuResFileDumper;
use Symfony\Component\Translation\MessageCatalogue;
class IcuResFileDumperTest extends \PHPUnit_Framework_TestCase
class IcuResFileDumperTest extends TestCase
{
public function testFormatCatalogue()
{

View File

@@ -11,10 +11,11 @@
namespace Symfony\Component\Translation\Tests\Dumper;
use Symfony\Component\Translation\MessageCatalogue;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Dumper\IniFileDumper;
use Symfony\Component\Translation\MessageCatalogue;
class IniFileDumperTest extends \PHPUnit_Framework_TestCase
class IniFileDumperTest extends TestCase
{
public function testFormatCatalogue()
{

View File

@@ -11,10 +11,11 @@
namespace Symfony\Component\Translation\Tests\Dumper;
use Symfony\Component\Translation\MessageCatalogue;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Dumper\JsonFileDumper;
use Symfony\Component\Translation\MessageCatalogue;
class JsonFileDumperTest extends \PHPUnit_Framework_TestCase
class JsonFileDumperTest extends TestCase
{
public function testFormatCatalogue()
{

View File

@@ -11,10 +11,11 @@
namespace Symfony\Component\Translation\Tests\Dumper;
use Symfony\Component\Translation\MessageCatalogue;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Dumper\MoFileDumper;
use Symfony\Component\Translation\MessageCatalogue;
class MoFileDumperTest extends \PHPUnit_Framework_TestCase
class MoFileDumperTest extends TestCase
{
public function testFormatCatalogue()
{

View File

@@ -11,10 +11,11 @@
namespace Symfony\Component\Translation\Tests\Dumper;
use Symfony\Component\Translation\MessageCatalogue;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Dumper\PhpFileDumper;
use Symfony\Component\Translation\MessageCatalogue;
class PhpFileDumperTest extends \PHPUnit_Framework_TestCase
class PhpFileDumperTest extends TestCase
{
public function testFormatCatalogue()
{

View File

@@ -11,10 +11,11 @@
namespace Symfony\Component\Translation\Tests\Dumper;
use Symfony\Component\Translation\MessageCatalogue;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Dumper\PoFileDumper;
use Symfony\Component\Translation\MessageCatalogue;
class PoFileDumperTest extends \PHPUnit_Framework_TestCase
class PoFileDumperTest extends TestCase
{
public function testFormatCatalogue()
{

View File

@@ -11,10 +11,11 @@
namespace Symfony\Component\Translation\Tests\Dumper;
use Symfony\Component\Translation\MessageCatalogue;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Dumper\QtFileDumper;
use Symfony\Component\Translation\MessageCatalogue;
class QtFileDumperTest extends \PHPUnit_Framework_TestCase
class QtFileDumperTest extends TestCase
{
public function testFormatCatalogue()
{

View File

@@ -11,10 +11,11 @@
namespace Symfony\Component\Translation\Tests\Dumper;
use Symfony\Component\Translation\MessageCatalogue;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Dumper\XliffFileDumper;
use Symfony\Component\Translation\MessageCatalogue;
class XliffFileDumperTest extends \PHPUnit_Framework_TestCase
class XliffFileDumperTest extends TestCase
{
public function testFormatCatalogue()
{
@@ -86,4 +87,29 @@ class XliffFileDumperTest extends \PHPUnit_Framework_TestCase
$dumper->formatCatalogue($catalogue, 'messages', array('default_locale' => 'fr_FR'))
);
}
public function testFormatCatalogueWithNotesMetadata()
{
$catalogue = new MessageCatalogue('en_US');
$catalogue->add(array(
'foo' => 'bar',
'baz' => 'biz',
));
$catalogue->setMetadata('foo', array('notes' => array(
array('category' => 'state', 'content' => 'new'),
array('category' => 'approved', 'content' => 'true'),
array('category' => 'section', 'content' => 'user login', 'priority' => '1'),
)));
$catalogue->setMetadata('baz', array('notes' => array(
array('id' => 'x', 'content' => 'x_content'),
array('appliesTo' => 'target', 'category' => 'quality', 'content' => 'Fuzzy'),
)));
$dumper = new XliffFileDumper();
$this->assertStringEqualsFile(
__DIR__.'/../fixtures/resources-notes-meta.xlf',
$dumper->formatCatalogue($catalogue, 'messages', array('default_locale' => 'fr_FR', 'xliff_version' => '2.0'))
);
}
}

View File

@@ -11,10 +11,11 @@
namespace Symfony\Component\Translation\Tests\Dumper;
use Symfony\Component\Translation\MessageCatalogue;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Dumper\YamlFileDumper;
use Symfony\Component\Translation\MessageCatalogue;
class YamlFileDumperTest extends \PHPUnit_Framework_TestCase
class YamlFileDumperTest extends TestCase
{
public function testTreeFormatCatalogue()
{

View File

@@ -0,0 +1,95 @@
<?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 = array(
'messages' => array(
'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',
),
'not_messages' => array(
'other-domain-test-no-params-short-array' => 'prefixother-domain-test-no-params-short-array',
'other-domain-test-no-params-long-array' => 'prefixother-domain-test-no-params-long-array',
'other-domain-test-params-short-array' => 'prefixother-domain-test-params-short-array',
'other-domain-test-params-long-array' => 'prefixother-domain-test-params-long-array',
'other-domain-test-trans-choice-short-array-%count%' => 'prefixother-domain-test-trans-choice-short-array-%count%',
'other-domain-test-trans-choice-long-array-%count%' => 'prefixother-domain-test-trans-choice-long-array-%count%',
'typecast' => 'prefixtypecast',
'msg1' => 'prefixmsg1',
'msg2' => 'prefixmsg2',
),
);
$actualCatalogue = $catalogue->all();
$this->assertEquals($expectedCatalogue, $actualCatalogue);
}
public function resourcesProvider()
{
$directory = __DIR__.'/../fixtures/extractor/';
$splFiles = array();
foreach (new \DirectoryIterator($directory) as $fileInfo) {
if ($fileInfo->isDot()) {
continue;
}
if ('translation.html.php' === $fileInfo->getBasename()) {
$phpFile = $fileInfo->getPathname();
}
$splFiles[] = $fileInfo->getFileInfo();
}
return array(
array($directory),
array($phpFile),
array(glob($directory.'*')),
array($splFiles),
array(new \ArrayObject(glob($directory.'*'))),
array(new \ArrayObject($splFiles)),
);
}
}

View File

@@ -0,0 +1,82 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Translation\Tests\Formatter;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Formatter\MessageFormatter;
class MessageFormatterTest extends TestCase
{
/**
* @dataProvider getTransMessages
*/
public function testFormat($expected, $message, $parameters = array())
{
$this->assertEquals($expected, $this->getMessageFormatter()->format($message, 'en', $parameters));
}
/**
* @dataProvider getTransChoiceMessages
*/
public function testFormatPlural($expected, $message, $number, $parameters)
{
$this->assertEquals($expected, $this->getMessageFormatter()->choiceFormat($message, $number, 'fr', $parameters));
}
public function getTransMessages()
{
return array(
array(
'There is one apple',
'There is one apple',
),
array(
'There are 5 apples',
'There are %count% apples',
array('%count%' => 5),
),
array(
'There are 5 apples',
'There are {{count}} apples',
array('{{count}}' => 5),
),
);
}
public function getTransChoiceMessages()
{
return array(
array('Il y a 0 pomme', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 0, array('%count%' => 0)),
array('Il y a 1 pomme', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 1, array('%count%' => 1)),
array('Il y a 10 pommes', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 10, array('%count%' => 10)),
array('Il y a 0 pomme', 'Il y a %count% pomme|Il y a %count% pommes', 0, array('%count%' => 0)),
array('Il y a 1 pomme', 'Il y a %count% pomme|Il y a %count% pommes', 1, array('%count%' => 1)),
array('Il y a 10 pommes', 'Il y a %count% pomme|Il y a %count% pommes', 10, array('%count%' => 10)),
array('Il y a 0 pomme', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 0, array('%count%' => 0)),
array('Il y a 1 pomme', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 1, array('%count%' => 1)),
array('Il y a 10 pommes', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 10, array('%count%' => 10)),
array('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, array('%count%' => 0)),
array('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, array('%count%' => 1)),
array('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, array('%count%' => 10)),
array('Il y a 0 pomme', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 0, array('%count%' => 0)),
);
}
private function getMessageFormatter()
{
return new MessageFormatter();
}
}

View File

@@ -11,10 +11,11 @@
namespace Symfony\Component\Translation\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Intl\Util\IntlTestHelper;
use Symfony\Component\Translation\IdentityTranslator;
class IdentityTranslatorTest extends \PHPUnit_Framework_TestCase
class IdentityTranslatorTest extends TestCase
{
/**
* @dataProvider getTransTests
@@ -60,7 +61,7 @@ class IdentityTranslatorTest extends \PHPUnit_Framework_TestCase
public function testGetLocaleReturnsDefaultLocaleIfNotSet()
{
// in order to test with "pt_BR"
IntlTestHelper::requireFullIntl($this);
IntlTestHelper::requireFullIntl($this, false);
$translator = new IdentityTranslator();

View File

@@ -11,9 +11,10 @@
namespace Symfony\Component\Translation\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Interval;
class IntervalTest extends \PHPUnit_Framework_TestCase
class IntervalTest extends TestCase
{
/**
* @dataProvider getTests
@@ -24,7 +25,7 @@ class IntervalTest extends \PHPUnit_Framework_TestCase
}
/**
* @expectedException \InvalidArgumentException
* @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
*/
public function testTestException()
{

View File

@@ -11,10 +11,11 @@
namespace Symfony\Component\Translation\Tests\Loader;
use Symfony\Component\Translation\Loader\CsvFileLoader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Translation\Loader\CsvFileLoader;
class CsvFileLoaderTest extends \PHPUnit_Framework_TestCase
class CsvFileLoaderTest extends TestCase
{
public function testLoad()
{

View File

@@ -11,8 +11,8 @@
namespace Symfony\Component\Translation\Tests\Loader;
use Symfony\Component\Translation\Loader\IcuDatFileLoader;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Translation\Loader\IcuDatFileLoader;
/**
* @requires extension intl

View File

@@ -11,8 +11,8 @@
namespace Symfony\Component\Translation\Tests\Loader;
use Symfony\Component\Translation\Loader\IcuResFileLoader;
use Symfony\Component\Config\Resource\DirectoryResource;
use Symfony\Component\Translation\Loader\IcuResFileLoader;
/**
* @requires extension intl

View File

@@ -11,10 +11,11 @@
namespace Symfony\Component\Translation\Tests\Loader;
use Symfony\Component\Translation\Loader\IniFileLoader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Translation\Loader\IniFileLoader;
class IniFileLoaderTest extends \PHPUnit_Framework_TestCase
class IniFileLoaderTest extends TestCase
{
public function testLoad()
{

View File

@@ -11,10 +11,11 @@
namespace Symfony\Component\Translation\Tests\Loader;
use Symfony\Component\Translation\Loader\JsonFileLoader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Translation\Loader\JsonFileLoader;
class JsonFileLoaderTest extends \PHPUnit_Framework_TestCase
class JsonFileLoaderTest extends TestCase
{
public function testLoad()
{

View File

@@ -11,11 +11,13 @@
namespace Symfony\Component\Translation\Tests\Loader;
abstract class LocalizedTestCase extends \PHPUnit_Framework_TestCase
use PHPUnit\Framework\TestCase;
abstract class LocalizedTestCase extends TestCase
{
protected function setUp()
{
if (!extension_loaded('intl')) {
if (!\extension_loaded('intl')) {
$this->markTestSkipped('Extension intl is required.');
}
}

View File

@@ -11,10 +11,11 @@
namespace Symfony\Component\Translation\Tests\Loader;
use Symfony\Component\Translation\Loader\MoFileLoader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Translation\Loader\MoFileLoader;
class MoFileLoaderTest extends \PHPUnit_Framework_TestCase
class MoFileLoaderTest extends TestCase
{
public function testLoad()
{

View File

@@ -11,10 +11,11 @@
namespace Symfony\Component\Translation\Tests\Loader;
use Symfony\Component\Translation\Loader\PhpFileLoader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Translation\Loader\PhpFileLoader;
class PhpFileLoaderTest extends \PHPUnit_Framework_TestCase
class PhpFileLoaderTest extends TestCase
{
public function testLoad()
{

View File

@@ -11,10 +11,11 @@
namespace Symfony\Component\Translation\Tests\Loader;
use Symfony\Component\Translation\Loader\PoFileLoader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Translation\Loader\PoFileLoader;
class PoFileLoaderTest extends \PHPUnit_Framework_TestCase
class PoFileLoaderTest extends TestCase
{
public function testLoad()
{

View File

@@ -11,10 +11,11 @@
namespace Symfony\Component\Translation\Tests\Loader;
use Symfony\Component\Translation\Loader\QtFileLoader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Translation\Loader\QtFileLoader;
class QtFileLoaderTest extends \PHPUnit_Framework_TestCase
class QtFileLoaderTest extends TestCase
{
public function testLoad()
{
@@ -61,7 +62,14 @@ class QtFileLoaderTest extends \PHPUnit_Framework_TestCase
{
$loader = new QtFileLoader();
$resource = __DIR__.'/../fixtures/empty.xlf';
$this->setExpectedException('Symfony\Component\Translation\Exception\InvalidResourceException', sprintf('Unable to load "%s".', $resource));
if (method_exists($this, 'expectException')) {
$this->expectException('Symfony\Component\Translation\Exception\InvalidResourceException');
$this->expectExceptionMessage(sprintf('Unable to load "%s".', $resource));
} else {
$this->setExpectedException('Symfony\Component\Translation\Exception\InvalidResourceException', sprintf('Unable to load "%s".', $resource));
}
$loader->load($resource, 'en', 'domain1');
}
}

View File

@@ -11,10 +11,11 @@
namespace Symfony\Component\Translation\Tests\Loader;
use Symfony\Component\Translation\Loader\XliffFileLoader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Translation\Loader\XliffFileLoader;
class XliffFileLoaderTest extends \PHPUnit_Framework_TestCase
class XliffFileLoaderTest extends TestCase
{
public function testLoad()
{
@@ -83,7 +84,7 @@ class XliffFileLoaderTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(utf8_decode('föö'), $catalogue->get('bar', 'domain1'));
$this->assertEquals(utf8_decode('bär'), $catalogue->get('foo', 'domain1'));
$this->assertEquals(array('notes' => array(array('content' => utf8_decode('bäz')))), $catalogue->getMetadata('foo', 'domain1'));
$this->assertEquals(array('notes' => array(array('content' => utf8_decode('bäz'))), 'id' => '1'), $catalogue->getMetadata('foo', 'domain1'));
}
public function testTargetAttributesAreStoredCorrectly()
@@ -147,7 +148,14 @@ class XliffFileLoaderTest extends \PHPUnit_Framework_TestCase
{
$loader = new XliffFileLoader();
$resource = __DIR__.'/../fixtures/empty.xlf';
$this->setExpectedException('Symfony\Component\Translation\Exception\InvalidResourceException', sprintf('Unable to load "%s":', $resource));
if (method_exists($this, 'expectException')) {
$this->expectException('Symfony\Component\Translation\Exception\InvalidResourceException');
$this->expectExceptionMessage(sprintf('Unable to load "%s":', $resource));
} else {
$this->setExpectedException('Symfony\Component\Translation\Exception\InvalidResourceException', sprintf('Unable to load "%s":', $resource));
}
$loader->load($resource, 'en', 'domain1');
}
@@ -156,11 +164,11 @@ class XliffFileLoaderTest extends \PHPUnit_Framework_TestCase
$loader = new XliffFileLoader();
$catalogue = $loader->load(__DIR__.'/../fixtures/withnote.xlf', 'en', 'domain1');
$this->assertEquals(array('notes' => array(array('priority' => 1, 'content' => 'foo'))), $catalogue->getMetadata('foo', 'domain1'));
$this->assertEquals(array('notes' => array(array('priority' => 1, 'content' => 'foo')), 'id' => '1'), $catalogue->getMetadata('foo', 'domain1'));
// message without target
$this->assertEquals(array('notes' => array(array('content' => 'bar', 'from' => 'foo'))), $catalogue->getMetadata('extra', 'domain1'));
$this->assertEquals(array('notes' => array(array('content' => 'bar', 'from' => 'foo')), 'id' => '2'), $catalogue->getMetadata('extra', 'domain1'));
// message with empty target
$this->assertEquals(array('notes' => array(array('content' => 'baz'), array('priority' => 2, 'from' => 'bar', 'content' => 'qux'))), $catalogue->getMetadata('key', 'domain1'));
$this->assertEquals(array('notes' => array(array('content' => 'baz'), array('priority' => 2, 'from' => 'bar', 'content' => 'qux')), 'id' => '123'), $catalogue->getMetadata('key', 'domain1'));
}
public function testLoadVersion2()
@@ -180,4 +188,73 @@ class XliffFileLoaderTest extends \PHPUnit_Framework_TestCase
// target attributes
$this->assertEquals(array('target-attributes' => array('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(array(new FileResource($resource)), $catalogue->getResources());
$this->assertSame(array(), 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(array(new FileResource($resource)), $catalog->getResources());
$this->assertFalse(libxml_get_last_error());
// test for "foo" metadata
$this->assertTrue($catalog->defines('foo', 'domain1'));
$metadata = $catalog->getMetadata('foo', 'domain1');
$this->assertNotEmpty($metadata);
$this->assertCount(1, $metadata['notes']);
$this->assertSame('processed', $metadata['notes'][0]['category']);
$this->assertSame('true', $metadata['notes'][0]['content']);
// test for "bar" metadata
$this->assertTrue($catalog->defines('bar', 'domain1'));
$metadata = $catalog->getMetadata('bar', 'domain1');
$this->assertNotEmpty($metadata);
$this->assertCount(1, $metadata['notes']);
$this->assertSame('processed', $metadata['notes'][0]['category']);
$this->assertSame('true', $metadata['notes'][0]['content']);
}
}

View File

@@ -11,10 +11,11 @@
namespace Symfony\Component\Translation\Tests\Loader;
use Symfony\Component\Translation\Loader\YamlFileLoader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Translation\Loader\YamlFileLoader;
class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
class YamlFileLoaderTest extends TestCase
{
public function testLoad()
{

View File

@@ -11,15 +11,16 @@
namespace Symfony\Component\Translation\Tests;
use Symfony\Component\Translation\Translator;
use Symfony\Component\Translation\LoggingTranslator;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Loader\ArrayLoader;
use Symfony\Component\Translation\LoggingTranslator;
use Symfony\Component\Translation\Translator;
class LoggingTranslatorTest extends \PHPUnit_Framework_TestCase
class LoggingTranslatorTest extends TestCase
{
public function testTransWithNoTranslationIsLogged()
{
$logger = $this->getMock('Psr\Log\LoggerInterface');
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$logger->expects($this->exactly(2))
->method('warning')
->with('Translation not found.')
@@ -33,7 +34,7 @@ class LoggingTranslatorTest extends \PHPUnit_Framework_TestCase
public function testTransChoiceFallbackIsLogged()
{
$logger = $this->getMock('Psr\Log\LoggerInterface');
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$logger->expects($this->once())
->method('debug')
->with('Translation use fallback catalogue.')

View File

@@ -11,9 +11,10 @@
namespace Symfony\Component\Translation\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\MessageCatalogue;
class MessageCatalogueTest extends \PHPUnit_Framework_TestCase
class MessageCatalogueTest extends TestCase
{
public function testGetLocale()
{
@@ -82,10 +83,10 @@ class MessageCatalogueTest extends \PHPUnit_Framework_TestCase
public function testAddCatalogue()
{
$r = $this->getMock('Symfony\Component\Config\Resource\ResourceInterface');
$r = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
$r->expects($this->any())->method('__toString')->will($this->returnValue('r'));
$r1 = $this->getMock('Symfony\Component\Config\Resource\ResourceInterface');
$r1 = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
$r1->expects($this->any())->method('__toString')->will($this->returnValue('r1'));
$catalogue = new MessageCatalogue('en', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')));
@@ -104,28 +105,35 @@ class MessageCatalogueTest extends \PHPUnit_Framework_TestCase
public function testAddFallbackCatalogue()
{
$r = $this->getMock('Symfony\Component\Config\Resource\ResourceInterface');
$r = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
$r->expects($this->any())->method('__toString')->will($this->returnValue('r'));
$r1 = $this->getMock('Symfony\Component\Config\Resource\ResourceInterface');
$r1 = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
$r1->expects($this->any())->method('__toString')->will($this->returnValue('r1'));
$catalogue = new MessageCatalogue('en_US', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')));
$r2 = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
$r2->expects($this->any())->method('__toString')->will($this->returnValue('r2'));
$catalogue = new MessageCatalogue('fr_FR', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')));
$catalogue->addResource($r);
$catalogue1 = new MessageCatalogue('en', array('domain1' => array('foo' => 'bar', 'foo1' => 'foo1')));
$catalogue1 = new MessageCatalogue('fr', array('domain1' => array('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(array($r, $r1), $catalogue->getResources());
$this->assertEquals(array($r, $r1, $r2), $catalogue->getResources());
}
/**
* @expectedException \LogicException
* @expectedException \Symfony\Component\Translation\Exception\LogicException
*/
public function testAddFallbackCatalogueWithParentCircularReference()
{
@@ -137,7 +145,7 @@ class MessageCatalogueTest extends \PHPUnit_Framework_TestCase
}
/**
* @expectedException \LogicException
* @expectedException \Symfony\Component\Translation\Exception\LogicException
*/
public function testAddFallbackCatalogueWithFallbackCircularReference()
{
@@ -151,7 +159,7 @@ class MessageCatalogueTest extends \PHPUnit_Framework_TestCase
}
/**
* @expectedException \LogicException
* @expectedException \Symfony\Component\Translation\Exception\LogicException
*/
public function testAddCatalogueWhenLocaleIsNotTheSameAsTheCurrentOne()
{
@@ -162,11 +170,11 @@ class MessageCatalogueTest extends \PHPUnit_Framework_TestCase
public function testGetAddResource()
{
$catalogue = new MessageCatalogue('en');
$r = $this->getMock('Symfony\Component\Config\Resource\ResourceInterface');
$r = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
$r->expects($this->any())->method('__toString')->will($this->returnValue('r'));
$catalogue->addResource($r);
$catalogue->addResource($r);
$r1 = $this->getMock('Symfony\Component\Config\Resource\ResourceInterface');
$r1 = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
$r1->expects($this->any())->method('__toString')->will($this->returnValue('r1'));
$catalogue->addResource($r1);

View File

@@ -11,9 +11,10 @@
namespace Symfony\Component\Translation\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\MessageSelector;
class MessageSelectorTest extends \PHPUnit_Framework_TestCase
class MessageSelectorTest extends TestCase
{
/**
* @dataProvider getChooseTests
@@ -34,7 +35,7 @@ class MessageSelectorTest extends \PHPUnit_Framework_TestCase
/**
* @dataProvider getNonMatchingMessages
* @expectedException \InvalidArgumentException
* @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
*/
public function testThrowExceptionIfMatchingMessageCannotBeFound($id, $number)
{
@@ -125,6 +126,12 @@ class MessageSelectorTest extends \PHPUnit_Framework_TestCase
array('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
array("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
array('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
array('', '|', 1),
// Empty plural set (3 plural forms) from a .PO file
array('', '||', 1),
);
}
}

View File

@@ -11,6 +11,7 @@
namespace Symfony\Component\Translation\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\PluralizationRules;
/**
@@ -26,7 +27,7 @@ use Symfony\Component\Translation\PluralizationRules;
*
* @author Clemens Tolboom clemens@build2be.nl
*/
class PluralizationRulesTest extends \PHPUnit_Framework_TestCase
class PluralizationRulesTest extends TestCase
{
/**
* We test failed langcode here.
@@ -64,7 +65,6 @@ class PluralizationRulesTest extends \PHPUnit_Framework_TestCase
array('2', array('nl', 'fr', 'en', 'de', 'de_GE', 'hy', 'hy_AM')),
array('3', array('be', 'bs', 'cs', 'hr')),
array('4', array('cy', 'mt', 'sl')),
array('5', array()),
array('6', array('ar')),
);
}
@@ -85,15 +85,14 @@ class PluralizationRulesTest extends \PHPUnit_Framework_TestCase
array('3', array('cbs')),
array('4', array('gd', 'kw')),
array('5', array('ga')),
array('6', array()),
);
}
/**
* 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 string $nplural Plural expected
* @param array $matrix Containing langcodes and their plural index values
* @param bool $expectSuccess
*/
protected function validateMatrix($nplural, $matrix, $expectSuccess = true)
@@ -101,9 +100,9 @@ class PluralizationRulesTest extends \PHPUnit_Framework_TestCase
foreach ($matrix as $langCode => $data) {
$indexes = array_flip($data);
if ($expectSuccess) {
$this->assertEquals($nplural, count($indexes), "Langcode '$langCode' has '$nplural' plural forms.");
$this->assertEquals($nplural, \count($indexes), "Langcode '$langCode' has '$nplural' plural forms.");
} else {
$this->assertNotEquals((int) $nplural, count($indexes), "Langcode '$langCode' has '$nplural' plural forms.");
$this->assertNotEquals((int) $nplural, \count($indexes), "Langcode '$langCode' has '$nplural' plural forms.");
}
}
}

View File

@@ -11,13 +11,14 @@
namespace Symfony\Component\Translation\Tests;
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\Translator;
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\Translator;
class TranslatorCacheTest extends \PHPUnit_Framework_TestCase
class TranslatorCacheTest extends TestCase
{
protected $tmpDir;
@@ -95,7 +96,7 @@ class TranslatorCacheTest extends \PHPUnit_Framework_TestCase
$catalogue->addResource(new StaleResource()); // better use a helper class than a mock, because it gets serialized in the cache and re-loaded
/** @var LoaderInterface|\PHPUnit_Framework_MockObject_MockObject $loader */
$loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface');
$loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock();
$loader
->expects($this->exactly(2))
->method('load')
@@ -148,6 +149,17 @@ class TranslatorCacheTest extends \PHPUnit_Framework_TestCase
$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(array('b'));
$translator->trans('bar');
$cachedFiles = glob($this->tmpDir.'/*.php');
$this->assertCount(1, $cachedFiles);
}
public function testDifferentCacheFilesAreUsedForDifferentSetsOfFallbackLocales()
{
/*
@@ -228,8 +240,8 @@ class TranslatorCacheTest extends \PHPUnit_Framework_TestCase
public function testRefreshCacheWhenResourcesAreNoLongerFresh()
{
$resource = $this->getMock('Symfony\Component\Config\Resource\SelfCheckingResourceInterface');
$loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface');
$resource = $this->getMockBuilder('Symfony\Component\Config\Resource\SelfCheckingResourceInterface')->getMock();
$loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock();
$resource->method('isFresh')->will($this->returnValue(false));
$loader
->expects($this->exactly(2))
@@ -272,7 +284,7 @@ class TranslatorCacheTest extends \PHPUnit_Framework_TestCase
*/
private function createFailingLoader()
{
$loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface');
$loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock();
$loader
->expects($this->never())
->method('load');

View File

@@ -11,20 +11,20 @@
namespace Symfony\Component\Translation\Tests;
use Symfony\Component\Translation\Translator;
use Symfony\Component\Translation\MessageSelector;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Loader\ArrayLoader;
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\Translator;
class TranslatorTest extends \PHPUnit_Framework_TestCase
class TranslatorTest extends TestCase
{
/**
* @dataProvider getInvalidLocalesTests
* @expectedException \InvalidArgumentException
* @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
*/
public function testConstructorInvalidLocale($locale)
{
$translator = new Translator($locale, new MessageSelector());
new Translator($locale);
}
/**
@@ -32,14 +32,14 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
*/
public function testConstructorValidLocale($locale)
{
$translator = new Translator($locale, new MessageSelector());
$translator = new Translator($locale);
$this->assertEquals($locale, $translator->getLocale());
}
public function testConstructorWithoutLocale()
{
$translator = new Translator(null, new MessageSelector());
$translator = new Translator(null);
$this->assertNull($translator->getLocale());
}
@@ -56,11 +56,11 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
/**
* @dataProvider getInvalidLocalesTests
* @expectedException \InvalidArgumentException
* @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
*/
public function testSetInvalidLocale($locale)
{
$translator = new Translator('fr', new MessageSelector());
$translator = new Translator('fr');
$translator->setLocale($locale);
}
@@ -69,7 +69,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
*/
public function testSetValidLocale($locale)
{
$translator = new Translator($locale, new MessageSelector());
$translator = new Translator($locale);
$translator->setLocale($locale);
$this->assertEquals($locale, $translator->getLocale());
@@ -139,11 +139,11 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
/**
* @dataProvider getInvalidLocalesTests
* @expectedException \InvalidArgumentException
* @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
*/
public function testSetFallbackInvalidLocales($locale)
{
$translator = new Translator('fr', new MessageSelector());
$translator = new Translator('fr');
$translator->setFallbackLocales(array('fr', $locale));
}
@@ -152,9 +152,10 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
*/
public function testSetFallbackValidLocales($locale)
{
$translator = new Translator($locale, new MessageSelector());
$translator = new Translator($locale);
$translator->setFallbackLocales(array('fr', $locale));
// no assertion. this method just asserts that no exception is thrown
$this->addToAssertionCount(1);
}
public function testTransWithFallbackLocale()
@@ -170,11 +171,11 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
/**
* @dataProvider getInvalidLocalesTests
* @expectedException \InvalidArgumentException
* @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
*/
public function testAddResourceInvalidLocales($locale)
{
$translator = new Translator('fr', new MessageSelector());
$translator = new Translator('fr');
$translator->addResource('array', array('foo' => 'foofoo'), $locale);
}
@@ -183,9 +184,10 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
*/
public function testAddResourceValidLocales($locale)
{
$translator = new Translator('fr', new MessageSelector());
$translator = new Translator('fr');
$translator->addResource('array', array('foo' => 'foofoo'), $locale);
// no assertion. this method just asserts that no exception is thrown
$this->addToAssertionCount(1);
}
public function testAddResourceAfterTrans()
@@ -263,7 +265,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
}
/**
* @expectedException \RuntimeException
* @expectedException \Symfony\Component\Translation\Exception\RuntimeException
*/
public function testWhenAResourceHasNoRegisteredLoader()
{
@@ -273,9 +275,19 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
$translator->trans('foo');
}
public function testNestedFallbackCatalogueWhenUsingMultipleLocales()
{
$translator = new Translator('fr');
$translator->setFallbackLocales(array('ru', 'en'));
$translator->getCatalogue('fr');
$this->assertNotNull($translator->getCatalogue('ru')->getFallbackCatalogue());
}
public function testFallbackCatalogueResources()
{
$translator = new Translator('en_GB', new MessageSelector());
$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');
@@ -285,12 +297,12 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
$resources = $translator->getCatalogue('en')->getResources();
$this->assertCount(1, $resources);
$this->assertContains(__DIR__.DIRECTORY_SEPARATOR.'fixtures'.DIRECTORY_SEPARATOR.'resources.yml', $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);
$this->assertContains(__DIR__.\DIRECTORY_SEPARATOR.'fixtures'.\DIRECTORY_SEPARATOR.'empty.yml', $resources);
$this->assertContains(__DIR__.\DIRECTORY_SEPARATOR.'fixtures'.\DIRECTORY_SEPARATOR.'resources.yml', $resources);
}
/**
@@ -307,11 +319,11 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
/**
* @dataProvider getInvalidLocalesTests
* @expectedException \InvalidArgumentException
* @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
*/
public function testTransInvalidLocale($locale)
{
$translator = new Translator('en', new MessageSelector());
$translator = new Translator('en');
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', array('foo' => 'foofoo'), 'en');
@@ -323,7 +335,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
*/
public function testTransValidLocale($locale)
{
$translator = new Translator($locale, new MessageSelector());
$translator = new Translator($locale);
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', array('test' => 'OK'), $locale);
@@ -357,11 +369,11 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
/**
* @dataProvider getInvalidLocalesTests
* @expectedException \InvalidArgumentException
* @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
*/
public function testTransChoiceInvalidLocale($locale)
{
$translator = new Translator('en', new MessageSelector());
$translator = new Translator('en');
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', array('foo' => 'foofoo'), 'en');
@@ -373,12 +385,13 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
*/
public function testTransChoiceValidLocale($locale)
{
$translator = new Translator('en', new MessageSelector());
$translator = new Translator('en');
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', array('foo' => 'foofoo'), 'en');
$translator->transChoice('foo', 1, array(), '', $locale);
// no assertion. this method just asserts that no exception is thrown
$this->addToAssertionCount(1);
}
public function getTransFileTests()
@@ -431,23 +444,26 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
public function getTransChoiceTests()
{
return array(
array('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, array('%count%' => 0), 'fr', ''),
array('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, array('%count%' => 1), 'fr', ''),
array('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, array('%count%' => 10), 'fr', ''),
array('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, array(), 'fr', ''),
array('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, array(), 'fr', ''),
array('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, array(), 'fr', ''),
array('Il y a 0 pomme', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 0, array('%count%' => 0), 'fr', ''),
array('Il y a 1 pomme', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 1, array('%count%' => 1), 'fr', ''),
array('Il y a 10 pommes', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 10, array('%count%' => 10), 'fr', ''),
array('Il y a 0 pomme', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 0, array(), 'fr', ''),
array('Il y a 1 pomme', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 1, array(), 'fr', ''),
array('Il y a 10 pommes', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 10, array(), 'fr', ''),
array('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, array('%count%' => 0), 'fr', ''),
array('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, array('%count%' => 1), 'fr', ''),
array('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, array('%count%' => 10), 'fr', ''),
array('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, array(), 'fr', ''),
array('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, array(), 'fr', ''),
array('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, array(), 'fr', ''),
array('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, array('%count%' => 0), 'fr', ''),
array('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, array('%count%' => 1), 'fr', ''),
array('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, array('%count%' => 10), 'fr', ''),
array('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, array(), 'fr', ''),
array('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, array(), 'fr', ''),
array('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, array(), 'fr', ''),
array('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, array('%count%' => 0), 'fr', ''),
array('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, array(), 'fr', ''),
// Override %count% with a custom value
array('Il y a quelques pommes', 'one: There is one apple|more: There are %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 2, array('%count%' => 'quelques'), 'fr', ''),
);
}

View File

@@ -11,19 +11,20 @@
namespace Symfony\Component\Translation\Tests\Util;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Translation\Util\ArrayConverter;
class ArrayConverterTest extends \PHPUnit_Framework_TestCase
class ArrayConverterTest extends TestCase
{
/**
* @dataProvider messsagesData
* @dataProvider messagesData
*/
public function testDump($input, $expectedOutput)
{
$this->assertEquals($expectedOutput, ArrayConverter::expandToTree($input));
}
public function messsagesData()
public function messagesData()
{
return array(
array(

View File

@@ -11,24 +11,28 @@
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 \PHPUnit_Framework_TestCase
class TranslationWriterTest extends TestCase
{
public function testWriteTranslations()
public function testWrite()
{
$dumper = $this->getMock('Symfony\Component\Translation\Dumper\DumperInterface');
$dumper = $this->getMockBuilder('Symfony\Component\Translation\Dumper\DumperInterface')->getMock();
$dumper
->expects($this->once())
->method('dump');
$writer = new TranslationWriter();
$writer->addDumper('test', $dumper);
$writer->writeTranslations(new MessageCatalogue(array()), 'test');
$writer->write(new MessageCatalogue('en'), 'test');
}
/**
* @group legacy
*/
public function testDisableBackup()
{
$nonBackupDumper = new NonBackupDumper();

View File

@@ -0,0 +1,49 @@
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,
array('%count%' => 10)
); ?>
<?php echo $view['translator']->trans('other-domain-test-no-params-short-array', array(), 'not_messages'); ?>
<?php echo $view['translator']->trans('other-domain-test-no-params-long-array', array(), 'not_messages'); ?>
<?php echo $view['translator']->trans('other-domain-test-params-short-array', array('foo' => 'bar'), 'not_messages'); ?>
<?php echo $view['translator']->trans('other-domain-test-params-long-array', array('foo' => 'bar'), 'not_messages'); ?>
<?php echo $view['translator']->transChoice('other-domain-test-trans-choice-short-array-%count%', 10, array('%count%' => 10), 'not_messages'); ?>
<?php echo $view['translator']->transChoice('other-domain-test-trans-choice-long-array-%count%', 10, array('%count%' => 10), 'not_messages'); ?>
<?php echo $view['translator']->trans('typecast', array('a' => (int) '123'), 'not_messages'); ?>
<?php echo $view['translator']->transChoice('msg1', 10 + 1, array(), 'not_messages'); ?>
<?php echo $view['translator']->transChoice('msg2', ceil(4.5), array(), 'not_messages'); ?>

View File

@@ -1,19 +1,19 @@
<?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="acbd18db4cc2f85cedef654fccc4a4d8">
<unit id="LCa0a2j" name="foo">
<segment>
<source>foo</source>
<target>bar</target>
</segment>
</unit>
<unit id="3c6e0b8a9c15224a8228b9a98ca1531d">
<unit id="LHDhK3o" name="key">
<segment>
<source>key</source>
<target order="1"></target>
</segment>
</unit>
<unit id="18e6a493872558d949b4c16ea1fa6ab6">
<unit id="2DA_bnh" name="key.with.cdata">
<segment>
<source>key.with.cdata</source>
<target><![CDATA[<source> & <target>]]></target>

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

@@ -5,18 +5,18 @@
<tool tool-id="symfony" tool-name="Symfony"/>
</header>
<body>
<trans-unit id="acbd18db4cc2f85cedef654fccc4a4d8" resname="foo">
<trans-unit id="LCa0a2j" resname="foo">
<source>foo</source>
<target>bar</target>
<note priority="1" from="bar">baz</note>
</trans-unit>
<trans-unit id="3c6e0b8a9c15224a8228b9a98ca1531d" resname="key">
<trans-unit id="LHDhK3o" resname="key">
<source>key</source>
<target></target>
<note>baz</note>
<note>qux</note>
</trans-unit>
<trans-unit id="18e6a493872558d949b4c16ea1fa6ab6" resname="key.with.cdata">
<trans-unit id="2DA_bnh" resname="key.with.cdata">
<source>key.with.cdata</source>
<target><![CDATA[<source> & <target>]]></target>
</trans-unit>

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

@@ -5,7 +5,7 @@
<tool tool-id="symfony" tool-name="Symfony"/>
</header>
<body>
<trans-unit id="acbd18db4cc2f85cedef654fccc4a4d8" resname="foo">
<trans-unit id="LCa0a2j" resname="foo">
<source>foo</source>
<target state="needs-translation">bar</target>
</trans-unit>

View File

@@ -5,7 +5,7 @@
<tool tool-id="foo" tool-name="foo" tool-version="0.0" tool-company="Foo"/>
</header>
<body>
<trans-unit id="acbd18db4cc2f85cedef654fccc4a4d8" resname="foo">
<trans-unit id="LCa0a2j" resname="foo">
<source>foo</source>
<target>bar</target>
</trans-unit>

View File

@@ -11,7 +11,7 @@
<source>extra</source>
<note from="foo">bar</note>
</trans-unit>
<trans-unit id="3">
<trans-unit id="123">
<source>key</source>
<target></target>
<note>baz</note>

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