update v 1.0.7.5

This commit is contained in:
Sujit Prasad
2016-06-13 20:41:55 +05:30
parent aa9786d829
commit 283d97e3ea
5078 changed files with 339851 additions and 175995 deletions

View File

@@ -1,6 +1,33 @@
CHANGELOG
=========
3.0.0
-----
* removed `FileDumper::format()` method.
* Changed the visibility of the locale property in `Translator` from protected to private.
2.8.0
-----
* deprecated FileDumper::format(), overwrite FileDumper::formatCatalogue() instead.
* deprecated Translator::getMessages(), rely on TranslatorBagInterface::getCatalogue() instead.
* added `FileDumper::formatCatalogue` which allows format the catalogue without dumping it into file.
* added option `json_encoding` to JsonFileDumper
* added options `as_tree`, `inline` to YamlFileDumper
* added support for XLIFF 2.0.
* added support for XLIFF target and tool attributes.
* added message parameters to DataCollectorTranslator.
* [DEPRECATION] The `DiffOperation` class has been deprecated and
will be removed in Symfony 3.0, since its operation has nothing to do with 'diff',
so the class name is misleading. The `TargetOperation` class should be used for
this use-case instead.
2.7.0
-----
* added DataCollectorTranslator for collecting the translated messages.
2.6.0
-----

View File

@@ -17,38 +17,60 @@ use Symfony\Component\Translation\MessageCatalogueInterface;
/**
* Base catalogues binary operation class.
*
* A catalogue binary operation performs operation on
* source (the left argument) and target (the right argument) catalogues.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*/
abstract class AbstractOperation implements OperationInterface
{
/**
* @var MessageCatalogueInterface
* @var MessageCatalogueInterface The source catalogue
*/
protected $source;
/**
* @var MessageCatalogueInterface
* @var MessageCatalogueInterface The target catalogue
*/
protected $target;
/**
* @var MessageCatalogue
* @var MessageCatalogue The result catalogue
*/
protected $result;
/**
* @var null|array
* @var null|array The domains affected by this operation
*/
private $domains;
/**
* @var array
* This array stores 'all', 'new' and 'obsolete' messages for all valid domains.
*
* The data structure of this array is as follows:
* ```php
* array(
* 'domain 1' => array(
* 'all' => array(...),
* 'new' => array(...),
* 'obsolete' => array(...)
* ),
* 'domain 2' => array(
* 'all' => array(...),
* 'new' => array(...),
* 'obsolete' => array(...)
* ),
* ...
* )
* ```
*
* @var array The array that stores 'all', 'new' and 'obsolete' messages
*/
protected $messages;
/**
* @param MessageCatalogueInterface $source
* @param MessageCatalogueInterface $target
* @param MessageCatalogueInterface $source The source catalogue
* @param MessageCatalogueInterface $target The target catalogue
*
* @throws \LogicException
*/
@@ -140,7 +162,10 @@ abstract class AbstractOperation implements OperationInterface
}
/**
* @param string $domain
* Performs operation on source and target catalogues for the given domain and
* stores the results.
*
* @param string $domain The domain which the operation will be performed for
*/
abstract protected function processDomain($domain);
}

View File

@@ -12,7 +12,11 @@
namespace Symfony\Component\Translation\Catalogue;
/**
* Merge operation between two catalogues.
* Merge operation between two catalogues as follows:
* all = source target = {x: x source x target}
* new = all source = {x: x target x source}
* obsolete = source all = {x: x source x source x target} =
* Basically, the result contains messages from both catalogues.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*/

View File

@@ -16,6 +16,20 @@ use Symfony\Component\Translation\MessageCatalogueInterface;
/**
* Represents an operation on catalogue(s).
*
* An instance of this interface performs an operation on one or more catalogues and
* stores intermediate and final results of the operation.
*
* The first catalogue in its argument(s) is called the 'source catalogue' or 'source' and
* the following results are stored:
*
* Messages: also called 'all', are valid messages for the given domain after the operation is performed.
*
* New Messages: also called 'new' (new = all source = {x: x all x source}).
*
* Obsolete Messages: also called 'obsolete' (obsolete = source all = {x: x source x all}).
*
* Result: also called 'result', is the resulting catalogue for the given domain that holds the same messages as 'all'.
*
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
*/
interface OperationInterface
@@ -28,7 +42,7 @@ interface OperationInterface
public function getDomains();
/**
* Returns all valid messages after operation.
* Returns all valid messages ('all') after operation.
*
* @param string $domain
*
@@ -37,7 +51,7 @@ interface OperationInterface
public function getMessages($domain);
/**
* Returns new messages after operation.
* Returns new messages ('new') after operation.
*
* @param string $domain
*
@@ -46,7 +60,7 @@ interface OperationInterface
public function getNewMessages($domain);
/**
* Returns obsolete messages after operation.
* Returns obsolete messages ('obsolete') after operation.
*
* @param string $domain
*
@@ -55,7 +69,7 @@ interface OperationInterface
public function getObsoleteMessages($domain);
/**
* Returns resulting catalogue.
* Returns resulting catalogue ('result').
*
* @return MessageCatalogueInterface
*/

View File

@@ -12,11 +12,16 @@
namespace Symfony\Component\Translation\Catalogue;
/**
* Diff operation between two catalogues.
* Target operation between two catalogues:
* intersection = source target = {x: x source x target}
* 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.
*
* @author Jean-François Simon <contact@jfsimon.fr>
* @author Michael Lee <michael.lee@zerustech.com>
*/
class DiffOperation extends AbstractOperation
class TargetOperation extends AbstractOperation
{
/**
* {@inheritdoc}
@@ -29,6 +34,15 @@ class DiffOperation extends AbstractOperation
'obsolete' => array(),
);
// 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}
foreach ($this->source->all($domain) as $id => $message) {
if ($this->target->has($id, $domain)) {
$this->messages[$domain]['all'][$id] = $message;

View File

@@ -0,0 +1,150 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Translation\DataCollector;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
use Symfony\Component\Translation\DataCollectorTranslator;
/**
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
*/
class TranslationDataCollector extends DataCollector implements LateDataCollectorInterface
{
/**
* @var DataCollectorTranslator
*/
private $translator;
/**
* @param DataCollectorTranslator $translator
*/
public function __construct(DataCollectorTranslator $translator)
{
$this->translator = $translator;
}
/**
* {@inheritdoc}
*/
public function lateCollect()
{
$messages = $this->sanitizeCollectedMessages($this->translator->getCollectedMessages());
$this->data = $this->computeCount($messages);
$this->data['messages'] = $messages;
}
/**
* {@inheritdoc}
*/
public function collect(Request $request, Response $response, \Exception $exception = null)
{
}
/**
* @return array
*/
public function getMessages()
{
return isset($this->data['messages']) ? $this->data['messages'] : array();
}
/**
* @return int
*/
public function getCountMissings()
{
return isset($this->data[DataCollectorTranslator::MESSAGE_MISSING]) ? $this->data[DataCollectorTranslator::MESSAGE_MISSING] : 0;
}
/**
* @return int
*/
public function getCountFallbacks()
{
return isset($this->data[DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK]) ? $this->data[DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK] : 0;
}
/**
* @return int
*/
public function getCountDefines()
{
return isset($this->data[DataCollectorTranslator::MESSAGE_DEFINED]) ? $this->data[DataCollectorTranslator::MESSAGE_DEFINED] : 0;
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'translation';
}
private function sanitizeCollectedMessages($messages)
{
$result = array();
foreach ($messages as $key => $message) {
$messageId = $message['locale'].$message['domain'].$message['id'];
if (!isset($result[$messageId])) {
$message['count'] = 1;
$message['parameters'] = !empty($message['parameters']) ? array($message['parameters']) : array();
$messages[$key]['translation'] = $this->sanitizeString($message['translation']);
$result[$messageId] = $message;
} else {
if (!empty($message['parameters'])) {
$result[$messageId]['parameters'][] = $message['parameters'];
}
++$result[$messageId]['count'];
}
unset($messages[$key]);
}
return $result;
}
private function computeCount($messages)
{
$count = array(
DataCollectorTranslator::MESSAGE_DEFINED => 0,
DataCollectorTranslator::MESSAGE_MISSING => 0,
DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK => 0,
);
foreach ($messages as $message) {
++$count[$message['state']];
}
return $count;
}
private function sanitizeString($string, $length = 80)
{
$string = trim(preg_replace('/\s+/', ' ', $string));
if (false !== $encoding = mb_detect_encoding($string, null, true)) {
if (mb_strlen($string, $encoding) > $length) {
return mb_substr($string, 0, $length - 3, $encoding).'...';
}
} elseif (strlen($string) > $length) {
return substr($string, 0, $length - 3).'...';
}
return $string;
}
}

View File

@@ -0,0 +1,152 @@
<?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;
/**
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
*/
class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInterface
{
const MESSAGE_DEFINED = 0;
const MESSAGE_MISSING = 1;
const MESSAGE_EQUALS_FALLBACK = 2;
/**
* @var TranslatorInterface|TranslatorBagInterface
*/
private $translator;
/**
* @var array
*/
private $messages = array();
/**
* @param TranslatorInterface $translator The translator must implement TranslatorBagInterface
*/
public function __construct(TranslatorInterface $translator)
{
if (!$translator instanceof TranslatorBagInterface) {
throw new \InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface and TranslatorBagInterface.', get_class($translator)));
}
$this->translator = $translator;
}
/**
* {@inheritdoc}
*/
public function trans($id, array $parameters = array(), $domain = null, $locale = null)
{
$trans = $this->translator->trans($id, $parameters, $domain, $locale);
$this->collectMessage($locale, $domain, $id, $trans, $parameters);
return $trans;
}
/**
* {@inheritdoc}
*/
public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null)
{
$trans = $this->translator->transChoice($id, $number, $parameters, $domain, $locale);
$this->collectMessage($locale, $domain, $id, $trans, $parameters, $number);
return $trans;
}
/**
* {@inheritdoc}
*/
public function setLocale($locale)
{
$this->translator->setLocale($locale);
}
/**
* {@inheritdoc}
*/
public function getLocale()
{
return $this->translator->getLocale();
}
/**
* {@inheritdoc}
*/
public function getCatalogue($locale = null)
{
return $this->translator->getCatalogue($locale);
}
/**
* 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 array
*/
public function getCollectedMessages()
{
return $this->messages;
}
/**
* @param string|null $locale
* @param string|null $domain
* @param string $id
* @param string $translation
* @param array|null $parameters
* @param int|null $number
*/
private function collectMessage($locale, $domain, $id, $translation, $parameters = array(), $number = null)
{
if (null === $domain) {
$domain = 'messages';
}
$id = (string) $id;
$catalogue = $this->translator->getCatalogue($locale);
$locale = $catalogue->getLocale();
if ($catalogue->defines($id, $domain)) {
$state = self::MESSAGE_DEFINED;
} elseif ($catalogue->has($id, $domain)) {
$state = self::MESSAGE_EQUALS_FALLBACK;
$fallbackCatalogue = $catalogue->getFallBackCatalogue();
while ($fallbackCatalogue) {
if ($fallbackCatalogue->defines($id, $domain)) {
$locale = $fallbackCatalogue->getLocale();
break;
}
$fallbackCatalogue = $fallbackCatalogue->getFallBackCatalogue();
}
} else {
$state = self::MESSAGE_MISSING;
}
$this->messages[] = array(
'locale' => $locale,
'domain' => $domain,
'id' => $id,
'translation' => $translation,
'parameters' => $parameters,
'transChoiceNumber' => $number,
'state' => $state,
);
}
}

View File

@@ -26,7 +26,7 @@ class CsvFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
public function format(MessageCatalogue $messages, $domain = 'messages')
public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
{
$handle = fopen('php://memory', 'rb+');

View File

@@ -82,7 +82,7 @@ abstract class FileDumper implements DumperInterface
}
}
// save file
file_put_contents($fullpath, $this->format($messages, $domain));
file_put_contents($fullpath, $this->formatCatalogue($messages, $domain, $options));
}
}
@@ -91,10 +91,11 @@ abstract class FileDumper implements DumperInterface
*
* @param MessageCatalogue $messages
* @param string $domain
* @param array $options
*
* @return string representation
*/
abstract protected function format(MessageCatalogue $messages, $domain);
abstract public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array());
/**
* Gets the file extension of the dumper.

View File

@@ -28,7 +28,7 @@ class IcuResFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
public function format(MessageCatalogue $messages, $domain = 'messages')
public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
{
$data = $indexes = $resources = '';
@@ -79,11 +79,7 @@ class IcuResFileDumper extends FileDumper
1, 4, 0, 0 // Unicode version
);
$output = $header
.$root
.$data;
return $output;
return $header.$root.$data;
}
private function writePadding($data)
@@ -97,9 +93,7 @@ class IcuResFileDumper extends FileDumper
private function getPosition($data)
{
$position = (strlen($data) + 28) / 4;
return $position;
return (strlen($data) + 28) / 4;
}
/**

View File

@@ -23,7 +23,7 @@ class IniFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
public function format(MessageCatalogue $messages, $domain = 'messages')
public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
{
$output = '';

View File

@@ -23,9 +23,15 @@ class JsonFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
public function format(MessageCatalogue $messages, $domain = 'messages')
public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
{
return json_encode($messages->all($domain), defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0);
if (isset($options['json_encoding'])) {
$flags = $options['json_encoding'];
} else {
$flags = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0;
}
return json_encode($messages->all($domain), $flags);
}
/**

View File

@@ -24,7 +24,7 @@ class MoFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
public function format(MessageCatalogue $messages, $domain = 'messages')
public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
{
$output = $sources = $targets = $sourceOffsets = $targetOffsets = '';
$offsets = array();

View File

@@ -23,11 +23,9 @@ class PhpFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
protected function format(MessageCatalogue $messages, $domain)
public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
{
$output = "<?php\n\nreturn ".var_export($messages->all($domain), true).";\n";
return $output;
return "<?php\n\nreturn ".var_export($messages->all($domain), true).";\n";
}
/**

View File

@@ -23,7 +23,7 @@ class PoFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
public function format(MessageCatalogue $messages, $domain = 'messages')
public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
{
$output = 'msgid ""'."\n";
$output .= 'msgstr ""'."\n";

View File

@@ -23,7 +23,7 @@ class QtFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
public function format(MessageCatalogue $messages, $domain)
public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
{
$dom = new \DOMDocument('1.0', 'utf-8');
$dom->formatOutput = true;

View File

@@ -0,0 +1,183 @@
<?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\Dumper;
use Symfony\Component\Translation\MessageCatalogue;
/**
* XliffFileDumper generates xliff files from a message catalogue.
*
* @author Michel Salib <michelsalib@hotmail.com>
*/
class XliffFileDumper extends FileDumper
{
/**
* {@inheritdoc}
*/
public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
{
$xliffVersion = '1.2';
if (array_key_exists('xliff_version', $options)) {
$xliffVersion = $options['xliff_version'];
}
if (array_key_exists('default_locale', $options)) {
$defaultLocale = $options['default_locale'];
} else {
$defaultLocale = \Locale::getDefault();
}
if ('1.2' === $xliffVersion) {
return $this->dumpXliff1($defaultLocale, $messages, $domain, $options);
}
if ('2.0' === $xliffVersion) {
return $this->dumpXliff2($defaultLocale, $messages, $domain, $options);
}
throw new \InvalidArgumentException(sprintf('No support implemented for dumping XLIFF version "%s".', $xliffVersion));
}
/**
* {@inheritdoc}
*/
protected function getExtension()
{
return 'xlf';
}
private function dumpXliff1($defaultLocale, MessageCatalogue $messages, $domain, array $options = array())
{
$toolInfo = array('tool-id' => 'symfony', 'tool-name' => 'Symfony');
if (array_key_exists('tool_info', $options)) {
$toolInfo = array_merge($toolInfo, $options['tool_info']);
}
$dom = new \DOMDocument('1.0', 'utf-8');
$dom->formatOutput = true;
$xliff = $dom->appendChild($dom->createElement('xliff'));
$xliff->setAttribute('version', '1.2');
$xliff->setAttribute('xmlns', 'urn:oasis:names:tc:xliff:document:1.2');
$xliffFile = $xliff->appendChild($dom->createElement('file'));
$xliffFile->setAttribute('source-language', str_replace('_', '-', $defaultLocale));
$xliffFile->setAttribute('target-language', str_replace('_', '-', $messages->getLocale()));
$xliffFile->setAttribute('datatype', 'plaintext');
$xliffFile->setAttribute('original', 'file.ext');
$xliffHead = $xliffFile->appendChild($dom->createElement('header'));
$xliffTool = $xliffHead->appendChild($dom->createElement('tool'));
foreach ($toolInfo as $id => $value) {
$xliffTool->setAttribute($id, $value);
}
$xliffBody = $xliffFile->appendChild($dom->createElement('body'));
foreach ($messages->all($domain) as $source => $target) {
$translation = $dom->createElement('trans-unit');
$translation->setAttribute('id', md5($source));
$translation->setAttribute('resname', $source);
$s = $translation->appendChild($dom->createElement('source'));
$s->appendChild($dom->createTextNode($source));
// Does the target contain characters requiring a CDATA section?
$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);
}
}
$t = $translation->appendChild($targetElement);
$t->appendChild($text);
if ($this->hasMetadataArrayInfo('notes', $metadata)) {
foreach ($metadata['notes'] as $note) {
if (!isset($note['content'])) {
continue;
}
$n = $translation->appendChild($dom->createElement('note'));
$n->appendChild($dom->createTextNode($note['content']));
if (isset($note['priority'])) {
$n->setAttribute('priority', $note['priority']);
}
if (isset($note['from'])) {
$n->setAttribute('from', $note['from']);
}
}
}
$xliffBody->appendChild($translation);
}
return $dom->saveXML();
}
private function dumpXliff2($defaultLocale, MessageCatalogue $messages, $domain, array $options = array())
{
$dom = new \DOMDocument('1.0', 'utf-8');
$dom->formatOutput = true;
$xliff = $dom->appendChild($dom->createElement('xliff'));
$xliff->setAttribute('xmlns', 'urn:oasis:names:tc:xliff:document:2.0');
$xliff->setAttribute('version', '2.0');
$xliff->setAttribute('srcLang', str_replace('_', '-', $defaultLocale));
$xliff->setAttribute('trgLang', str_replace('_', '-', $messages->getLocale()));
$xliffFile = $xliff->appendChild($dom->createElement('file'));
$xliffFile->setAttribute('id', $domain.'.'.$messages->getLocale());
foreach ($messages->all($domain) as $source => $target) {
$translation = $dom->createElement('unit');
$translation->setAttribute('id', md5($source));
$segment = $translation->appendChild($dom->createElement('segment'));
$s = $segment->appendChild($dom->createElement('source'));
$s->appendChild($dom->createTextNode($source));
// Does the target contain characters requiring a CDATA section?
$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);
}
}
$t = $segment->appendChild($targetElement);
$t->appendChild($text);
$xliffFile->appendChild($translation);
}
return $dom->saveXML();
}
/**
* @param string $key
* @param array|null $metadata
*
* @return bool
*/
private function hasMetadataArrayInfo($key, $metadata = null)
{
return null !== $metadata && array_key_exists($key, $metadata) && ($metadata[$key] instanceof \Traversable || is_array($metadata[$key]));
}
}

View File

@@ -12,6 +12,7 @@
namespace Symfony\Component\Translation\Dumper;
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\Util\ArrayConverter;
use Symfony\Component\Yaml\Yaml;
/**
@@ -24,13 +25,23 @@ class YamlFileDumper extends FileDumper
/**
* {@inheritdoc}
*/
protected function format(MessageCatalogue $messages, $domain)
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.');
}
return Yaml::dump($messages->all($domain));
$data = $messages->all($domain);
if (isset($options['as_tree']) && $options['as_tree']) {
$data = ArrayConverter::expandToTree($data);
}
if (isset($options['inline']) && ($inline = (int) $options['inline']) > 0) {
return Yaml::dump($data, $inline);
}
return Yaml::dump($data);
}
/**

View File

@@ -15,8 +15,6 @@ namespace Symfony\Component\Translation\Exception;
* Exception interface for all exceptions thrown by the component.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
interface ExceptionInterface
{

View File

@@ -15,8 +15,6 @@ namespace Symfony\Component\Translation\Exception;
* Thrown when a resource cannot be loaded.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
class InvalidResourceException extends \InvalidArgumentException implements ExceptionInterface
{

View File

@@ -15,8 +15,6 @@ namespace Symfony\Component\Translation\Exception;
* Thrown when a resource does not exist.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
class NotFoundResourceException extends \InvalidArgumentException implements ExceptionInterface
{

View File

@@ -0,0 +1,83 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Translation\Extractor;
/**
* Base class used by classes that extract translation messages from files.
*
* @author Marcos D. Sánchez <marcosdsanchez@gmail.com>
*/
abstract class AbstractFileExtractor
{
/**
* @param string|array $resource files, a file or a directory
*
* @return array
*/
protected function extractFiles($resource)
{
if (is_array($resource) || $resource instanceof \Traversable) {
$files = array();
foreach ($resource as $file) {
if ($this->canBeExtracted($file)) {
$files[] = $this->toSplFileInfo($file);
}
}
} elseif (is_file($resource)) {
$files = $this->canBeExtracted($resource) ? array($this->toSplFileInfo($resource)) : array();
} else {
$files = $this->extractFromDirectory($resource);
}
return $files;
}
/**
* @param string $file
*
* @return \SplFileInfo
*/
private function toSplFileInfo($file)
{
return ($file instanceof \SplFileInfo) ? $file : new \SplFileInfo($file);
}
/**
* @param string $file
*
* @return bool
*
* @throws \InvalidArgumentException
*/
protected function isFile($file)
{
if (!is_file($file)) {
throw new \InvalidArgumentException(sprintf('The "%s" file does not exist.', $file));
}
return true;
}
/**
* @param string $file
*
* @return bool
*/
abstract protected function canBeExtracted($file);
/**
* @param string|array $resource files, a file or a directory
*
* @return array files to be extracted
*/
abstract protected function extractFromDirectory($resource);
}

View File

@@ -14,7 +14,7 @@ namespace Symfony\Component\Translation\Extractor;
use Symfony\Component\Translation\MessageCatalogue;
/**
* Extracts translation messages from a template directory to the catalogue.
* Extracts translation messages from a directory or files to the catalogue.
* New found messages are injected to the catalogue using the prefix.
*
* @author Michel Salib <michelsalib@hotmail.com>
@@ -22,12 +22,12 @@ use Symfony\Component\Translation\MessageCatalogue;
interface ExtractorInterface
{
/**
* Extracts translation messages from a template directory to the catalogue.
* Extracts translation messages from files, a file or a directory to the catalogue.
*
* @param string $directory The path to look into
* @param string|array $resource files, a file or a directory
* @param MessageCatalogue $catalogue The catalogue
*/
public function extract($directory, MessageCatalogue $catalogue);
public function extract($resource, MessageCatalogue $catalogue);
/**
* Sets the prefix that should be used for new found messages.

View File

@@ -15,8 +15,6 @@ namespace Symfony\Component\Translation;
* IdentityTranslator does not translate anything.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
class IdentityTranslator implements TranslatorInterface
{
@@ -27,8 +25,6 @@ class IdentityTranslator implements TranslatorInterface
* Constructor.
*
* @param MessageSelector|null $selector The message selector for pluralization
*
* @api
*/
public function __construct(MessageSelector $selector = null)
{
@@ -37,8 +33,6 @@ class IdentityTranslator implements TranslatorInterface
/**
* {@inheritdoc}
*
* @api
*/
public function setLocale($locale)
{
@@ -47,8 +41,6 @@ class IdentityTranslator implements TranslatorInterface
/**
* {@inheritdoc}
*
* @api
*/
public function getLocale()
{
@@ -57,8 +49,6 @@ class IdentityTranslator implements TranslatorInterface
/**
* {@inheritdoc}
*
* @api
*/
public function trans($id, array $parameters = array(), $domain = null, $locale = null)
{
@@ -67,8 +57,6 @@ class IdentityTranslator implements TranslatorInterface
/**
* {@inheritdoc}
*
* @api
*/
public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null)
{

View File

@@ -1,4 +1,4 @@
Copyright (c) 2004-2015 Fabien Potencier
Copyright (c) 2004-2016 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

@@ -17,15 +17,11 @@ use Symfony\Component\Translation\MessageCatalogue;
* ArrayLoader loads translations from a PHP array.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
class ArrayLoader implements LoaderInterface
{
/**
* {@inheritdoc}
*
* @api
*/
public function load($resource, $locale, $domain = 'messages')
{

View File

@@ -11,18 +11,14 @@
namespace Symfony\Component\Translation\Loader;
use Symfony\Component\Translation\Exception\InvalidResourceException;
use Symfony\Component\Translation\Exception\NotFoundResourceException;
use Symfony\Component\Config\Resource\FileResource;
/**
* CsvFileLoader loads translations from CSV files.
*
* @author Saša Stamenković <umpirsky@gmail.com>
*
* @api
*/
class CsvFileLoader extends ArrayLoader
class CsvFileLoader extends FileLoader
{
private $delimiter = ';';
private $enclosure = '"';
@@ -30,19 +26,9 @@ class CsvFileLoader extends ArrayLoader
/**
* {@inheritdoc}
*
* @api
*/
public function load($resource, $locale, $domain = 'messages')
protected function loadResource($resource)
{
if (!stream_is_local($resource)) {
throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
}
if (!file_exists($resource)) {
throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
}
$messages = array();
try {
@@ -55,28 +41,12 @@ class CsvFileLoader extends ArrayLoader
$file->setCsvControl($this->delimiter, $this->enclosure, $this->escape);
foreach ($file as $data) {
if (substr($data[0], 0, 1) === '#') {
continue;
}
if (!isset($data[1])) {
continue;
}
if (count($data) == 2) {
if ('#' !== substr($data[0], 0, 1) && isset($data[1]) && 2 === count($data)) {
$messages[$data[0]] = $data[1];
} else {
continue;
}
}
$catalogue = parent::load($messages, $locale, $domain);
if (class_exists('Symfony\Component\Config\Resource\FileResource')) {
$catalogue->addResource(new FileResource($resource));
}
return $catalogue;
return $messages;
}
/**

View File

@@ -16,18 +16,12 @@ use Symfony\Component\Translation\Exception\NotFoundResourceException;
use Symfony\Component\Config\Resource\FileResource;
/**
* PhpFileLoader loads translations from PHP files returning an array of translations.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
*/
class PhpFileLoader extends ArrayLoader
abstract class FileLoader extends ArrayLoader
{
/**
* {@inheritdoc}
*
* @api
*/
public function load($resource, $locale, $domain = 'messages')
{
@@ -39,7 +33,17 @@ class PhpFileLoader extends ArrayLoader
throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
}
$messages = require $resource;
$messages = $this->loadResource($resource);
// empty resource
if (null === $messages) {
$messages = array();
}
// not an array
if (!is_array($messages)) {
throw new InvalidResourceException(sprintf('Unable to load file "%s".', $resource));
}
$catalogue = parent::load($messages, $locale, $domain);
@@ -49,4 +53,13 @@ class PhpFileLoader extends ArrayLoader
return $catalogue;
}
/*
* @param string $resource
*
* @return array
*
* @throws InvalidResourceException If stream content has an invalid format.
*/
abstract protected function loadResource($resource);
}

View File

@@ -0,0 +1,28 @@
<?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\Loader;
/**
* IniFileLoader loads translations from an ini file.
*
* @author stealth35
*/
class IniFileLoader extends FileLoader
{
/**
* {@inheritdoc}
*/
protected function loadResource($resource)
{
return parse_ini_file($resource, true);
}
}

View File

@@ -12,29 +12,19 @@
namespace Symfony\Component\Translation\Loader;
use Symfony\Component\Translation\Exception\InvalidResourceException;
use Symfony\Component\Translation\Exception\NotFoundResourceException;
use Symfony\Component\Config\Resource\FileResource;
/**
* JsonFileLoader loads translations from an json file.
*
* @author singles
*/
class JsonFileLoader extends ArrayLoader
class JsonFileLoader extends FileLoader
{
/**
* {@inheritdoc}
*/
public function load($resource, $locale, $domain = 'messages')
protected function loadResource($resource)
{
if (!stream_is_local($resource)) {
throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
}
if (!file_exists($resource)) {
throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
}
$messages = array();
if ($data = file_get_contents($resource)) {
$messages = json_decode($data, true);
@@ -44,14 +34,7 @@ class JsonFileLoader extends ArrayLoader
}
}
if (null === $messages) {
$messages = array();
}
$catalogue = parent::load($messages, $locale, $domain);
$catalogue->addResource(new FileResource($resource));
return $catalogue;
return $messages;
}
/**

View File

@@ -19,8 +19,6 @@ use Symfony\Component\Translation\Exception\NotFoundResourceException;
* LoaderInterface is the interface implemented by all translation loaders.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
interface LoaderInterface
{
@@ -33,8 +31,6 @@ interface LoaderInterface
*
* @return MessageCatalogue A MessageCatalogue instance
*
* @api
*
* @throws NotFoundResourceException when the resource cannot be found
* @throws InvalidResourceException when the resource cannot be loaded
*/

View File

@@ -12,13 +12,11 @@
namespace Symfony\Component\Translation\Loader;
use Symfony\Component\Translation\Exception\InvalidResourceException;
use Symfony\Component\Translation\Exception\NotFoundResourceException;
use Symfony\Component\Config\Resource\FileResource;
/**
* @copyright Copyright (c) 2010, Union of RAD http://union-of-rad.org (http://lithify.me/)
*/
class MoFileLoader extends ArrayLoader
class MoFileLoader extends FileLoader
{
/**
* Magic used for validating the format of a MO file as well as
@@ -43,48 +41,13 @@ class MoFileLoader extends ArrayLoader
*/
const MO_HEADER_SIZE = 28;
public function load($resource, $locale, $domain = 'messages')
{
if (!stream_is_local($resource)) {
throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
}
if (!file_exists($resource)) {
throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
}
$messages = $this->parse($resource);
// empty file
if (null === $messages) {
$messages = array();
}
// not an array
if (!is_array($messages)) {
throw new InvalidResourceException(sprintf('The file "%s" must contain a valid mo file.', $resource));
}
$catalogue = parent::load($messages, $locale, $domain);
if (class_exists('Symfony\Component\Config\Resource\FileResource')) {
$catalogue->addResource(new FileResource($resource));
}
return $catalogue;
}
/**
* Parses machine object (MO) format, independent of the machine's endian it
* was created on. Both 32bit and 64bit systems are supported.
*
* @param resource $resource
*
* @return array
*
* @throws InvalidResourceException If stream content has an invalid format.
* {@inheritdoc}
*/
private function parse($resource)
protected function loadResource($resource)
{
$stream = fopen($resource, 'r');

View File

@@ -0,0 +1,28 @@
<?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\Loader;
/**
* PhpFileLoader loads translations from PHP files returning an array of translations.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class PhpFileLoader extends FileLoader
{
/**
* {@inheritdoc}
*/
protected function loadResource($resource)
{
return require $resource;
}
}

View File

@@ -11,47 +11,12 @@
namespace Symfony\Component\Translation\Loader;
use Symfony\Component\Translation\Exception\InvalidResourceException;
use Symfony\Component\Translation\Exception\NotFoundResourceException;
use Symfony\Component\Config\Resource\FileResource;
/**
* @copyright Copyright (c) 2010, Union of RAD http://union-of-rad.org (http://lithify.me/)
* @copyright Copyright (c) 2012, Clemens Tolboom
*/
class PoFileLoader extends ArrayLoader
class PoFileLoader extends FileLoader
{
public function load($resource, $locale, $domain = 'messages')
{
if (!stream_is_local($resource)) {
throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
}
if (!file_exists($resource)) {
throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
}
$messages = $this->parse($resource);
// empty file
if (null === $messages) {
$messages = array();
}
// not an array
if (!is_array($messages)) {
throw new InvalidResourceException(sprintf('The file "%s" must contain a valid po file.', $resource));
}
$catalogue = parent::load($messages, $locale, $domain);
if (class_exists('Symfony\Component\Config\Resource\FileResource')) {
$catalogue->addResource(new FileResource($resource));
}
return $catalogue;
}
/**
* Parses portable object (PO) format.
*
@@ -93,11 +58,9 @@ class PoFileLoader extends ArrayLoader
*
* Items with an empty id are ignored.
*
* @param resource $resource
*
* @return array
* {@inheritdoc}
*/
private function parse($resource)
protected function loadResource($resource)
{
$stream = fopen($resource, 'r');
@@ -108,14 +71,20 @@ class PoFileLoader extends ArrayLoader
$messages = array();
$item = $defaults;
$flags = array();
while ($line = fgets($stream)) {
$line = trim($line);
if ($line === '') {
// Whitespace indicated current item is done
$this->addMessage($messages, $item);
if (!in_array('fuzzy', $flags)) {
$this->addMessage($messages, $item);
}
$item = $defaults;
$flags = array();
} elseif (substr($line, 0, 2) === '#,') {
$flags = array_map('trim', explode(',', substr($line, 2)));
} elseif (substr($line, 0, 7) === 'msgid "') {
// We start a new msg so save previous
// TODO: this fails when comments or contexts are added
@@ -141,7 +110,9 @@ class PoFileLoader extends ArrayLoader
}
}
// save last item
$this->addMessage($messages, $item);
if (!in_array('fuzzy', $flags)) {
$this->addMessage($messages, $item);
}
fclose($stream);
return $messages;

View File

@@ -21,15 +21,11 @@ use Symfony\Component\Config\Resource\FileResource;
* QtFileLoader loads translations from QT Translations XML files.
*
* @author Benjamin Eberlei <kontakt@beberlei.de>
*
* @api
*/
class QtFileLoader implements LoaderInterface
{
/**
* {@inheritdoc}
*
* @api
*/
public function load($resource, $locale, $domain = 'messages')
{

View File

@@ -0,0 +1,315 @@
<?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\Loader;
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;
/**
* XliffFileLoader loads translations from XLIFF files.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class XliffFileLoader implements LoaderInterface
{
/**
* {@inheritdoc}
*/
public function load($resource, $locale, $domain = 'messages')
{
if (!stream_is_local($resource)) {
throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
}
if (!file_exists($resource)) {
throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
}
$catalogue = new MessageCatalogue($locale);
$this->extract($resource, $catalogue, $domain);
if (class_exists('Symfony\Component\Config\Resource\FileResource')) {
$catalogue->addResource(new FileResource($resource));
}
return $catalogue;
}
private function extract($resource, MessageCatalogue $catalogue, $domain)
{
try {
$dom = XmlUtils::loadFile($resource);
} catch (\InvalidArgumentException $e) {
throw new InvalidResourceException(sprintf('Unable to load "%s": %s', $resource, $e->getMessage()), $e->getCode(), $e);
}
$xliffVersion = $this->getVersionNumber($dom);
$this->validateSchema($xliffVersion, $dom, $this->getSchema($xliffVersion));
if ('1.2' === $xliffVersion) {
$this->extractXliff1($dom, $catalogue, $domain);
}
if ('2.0' === $xliffVersion) {
$this->extractXliff2($dom, $catalogue, $domain);
}
}
/**
* Extract messages and metadata from DOMDocument into a MessageCatalogue.
*
* @param \DOMDocument $dom Source to extract messages and metadata
* @param MessageCatalogue $catalogue Catalogue where we'll collect messages and metadata
* @param string $domain The domain
*/
private function extractXliff1(\DOMDocument $dom, MessageCatalogue $catalogue, $domain)
{
$xml = simplexml_import_dom($dom);
$encoding = strtoupper($dom->encoding);
$xml->registerXPathNamespace('xliff', 'urn:oasis:names:tc:xliff:document:1.2');
foreach ($xml->xpath('//xliff:trans-unit') as $translation) {
$attributes = $translation->attributes();
if (!(isset($attributes['resname']) || isset($translation->source))) {
continue;
}
$source = isset($attributes['resname']) && $attributes['resname'] ? $attributes['resname'] : $translation->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($translation->target) ? $translation->target : $source), $encoding);
$catalogue->set((string) $source, $target, $domain);
$metadata = array();
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) {
$metadata['target-attributes'][$key] = (string) $value;
}
}
$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;
}
}
$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)
{
if ('UTF-8' !== $encoding && !empty($encoding)) {
return mb_convert_encoding($content, $encoding, 'UTF-8');
}
return $content;
}
/**
* 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)
{
$internalErrors = libxml_use_internal_errors(true);
if (!@$dom->schemaValidateSource($schema)) {
throw new InvalidResourceException(sprintf('Invalid resource provided: "%s"; Errors: %s', $file, implode("\n", $this->getXmlErrors($internalErrors))));
}
$dom->normalizeDocument();
libxml_clear_errors();
libxml_use_internal_errors($internalErrors);
}
private function getSchema($xliffVersion)
{
if ('1.2' === $xliffVersion) {
$schemaSource = file_get_contents(__DIR__.'/schema/dic/xliff-core/xliff-core-1.2-strict.xsd');
$xmlUri = 'http://www.w3.org/2001/xml.xsd';
} elseif ('2.0' === $xliffVersion) {
$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));
}
return $this->fixXmlLocation($schemaSource, $xmlUri);
}
/**
* 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)
{
$newPath = str_replace('\\', '/', __DIR__).'/schema/dic/xliff-core/xml.xsd';
$parts = explode('/', $newPath);
if (0 === stripos($newPath, 'phar://')) {
$tmpfile = tempnam(sys_get_temp_dir(), 'sf2');
if ($tmpfile) {
copy($newPath, $tmpfile);
$parts = explode('/', str_replace('\\', '/', $tmpfile));
}
}
$drive = '\\' === DIRECTORY_SEPARATOR ? array_shift($parts).'/' : '';
$newPath = 'file:///'.$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)
{
$errors = array();
foreach (libxml_get_errors() as $error) {
$errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)',
LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR',
$error->code,
trim($error->message),
$error->file ?: 'n/a',
$error->line,
$error->column
);
}
libxml_clear_errors();
libxml_use_internal_errors($internalErrors);
return $errors;
}
/**
* Gets xliff file version based on the root "version" attribute.
* Defaults to 1.2 for backwards compatibility.
*
* @param \DOMDocument $dom
*
* @throws \InvalidArgumentException
*
* @return string
*/
private function getVersionNumber(\DOMDocument $dom)
{
/** @var \DOMNode $xliff */
foreach ($dom->getElementsByTagName('xliff') as $xliff) {
$version = $xliff->attributes->getNamedItem('version');
if ($version) {
return $version->nodeValue;
}
$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));
}
return substr($namespace, 34);
}
}
// Falls back to v1.2
return '1.2';
}
/*
* @param \SimpleXMLElement|null $noteElement
* @param string|null $encoding
*
* @return array
*/
private function parseNotesMetadata(\SimpleXMLElement $noteElement = null, $encoding = null)
{
$notes = array();
if (null === $noteElement) {
return $notes;
}
foreach ($noteElement as $xmlNote) {
$noteAttributes = $xmlNote->attributes();
$note = array('content' => $this->utf8ToCharset((string) $xmlNote, $encoding));
if (isset($noteAttributes['priority'])) {
$note['priority'] = (int) $noteAttributes['priority'];
}
if (isset($noteAttributes['from'])) {
$note['from'] = (string) $noteAttributes['from'];
}
$notes[] = $note;
}
return $notes;
}
}

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\Loader;
use Symfony\Component\Translation\Exception\InvalidResourceException;
use Symfony\Component\Yaml\Parser as YamlParser;
use Symfony\Component\Yaml\Exception\ParseException;
/**
* YamlFileLoader loads translations from Yaml files.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class YamlFileLoader extends FileLoader
{
private $yamlParser;
/**
* {@inheritdoc}
*/
protected function loadResource($resource)
{
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.');
}
$this->yamlParser = new YamlParser();
}
try {
$messages = $this->yamlParser->parse(file_get_contents($resource));
} catch (ParseException $e) {
throw new InvalidResourceException(sprintf('Error parsing YAML, invalid file "%s"', $resource), 0, $e);
}
return $messages;
}
}

View File

@@ -0,0 +1,411 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
XLIFF Version 2.0
OASIS Standard
05 August 2014
Copyright (c) OASIS Open 2014. All rights reserved.
Source: http://docs.oasis-open.org/xliff/xliff-core/v2.0/os/schemas/
-->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified"
xmlns:xlf="urn:oasis:names:tc:xliff:document:2.0"
targetNamespace="urn:oasis:names:tc:xliff:document:2.0">
<!-- Import -->
<xs:import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="informativeCopiesOf3rdPartySchemas/w3c/xml.xsd"/>
<!-- Element Group -->
<xs:group name="inline">
<xs:choice>
<xs:element ref="xlf:cp"/>
<xs:element ref="xlf:ph"/>
<xs:element ref="xlf:pc"/>
<xs:element ref="xlf:sc"/>
<xs:element ref="xlf:ec"/>
<xs:element ref="xlf:mrk"/>
<xs:element ref="xlf:sm"/>
<xs:element ref="xlf:em"/>
</xs:choice>
</xs:group>
<!-- Attribute Types -->
<xs:simpleType name="yesNo">
<xs:restriction base="xs:string">
<xs:enumeration value="yes"/>
<xs:enumeration value="no"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="yesNoFirstNo">
<xs:restriction base="xs:string">
<xs:enumeration value="yes"/>
<xs:enumeration value="firstNo"/>
<xs:enumeration value="no"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="dirValue">
<xs:restriction base="xs:string">
<xs:enumeration value="ltr"/>
<xs:enumeration value="rtl"/>
<xs:enumeration value="auto"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="appliesTo">
<xs:restriction base="xs:string">
<xs:enumeration value="source"/>
<xs:enumeration value="target"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="userDefinedValue">
<xs:restriction base="xs:string">
<xs:pattern value="[^\s:]+:[^\s:]+"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="attrType_type">
<xs:restriction base="xs:string">
<xs:enumeration value="fmt"/>
<xs:enumeration value="ui"/>
<xs:enumeration value="quote"/>
<xs:enumeration value="link"/>
<xs:enumeration value="image"/>
<xs:enumeration value="other"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="typeForMrkValues">
<xs:restriction base="xs:NMTOKEN">
<xs:enumeration value="generic"/>
<xs:enumeration value="comment"/>
<xs:enumeration value="term"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="attrType_typeForMrk">
<xs:union memberTypes="xlf:typeForMrkValues xlf:userDefinedValue"/>
</xs:simpleType>
<xs:simpleType name="priorityValue">
<xs:restriction base="xs:positiveInteger">
<xs:minInclusive value="1"/>
<xs:maxInclusive value="10"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="stateType">
<xs:restriction base="xs:string">
<xs:enumeration value="initial"/>
<xs:enumeration value="translated"/>
<xs:enumeration value="reviewed"/>
<xs:enumeration value="final"/>
</xs:restriction>
</xs:simpleType>
<!-- Structural Elements -->
<xs:element name="xliff">
<xs:complexType mixed="false">
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="unbounded" ref="xlf:file"/>
</xs:sequence>
<xs:attribute name="version" use="required"/>
<xs:attribute name="srcLang" use="required"/>
<xs:attribute name="trgLang" use="optional"/>
<xs:attribute ref="xml:space" use="optional" default="default"/>
<xs:anyAttribute namespace="##other" processContents="lax"/>
</xs:complexType>
</xs:element>
<xs:element name="file">
<xs:complexType mixed="false">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" ref="xlf:skeleton"/>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"
processContents="lax"/>
<xs:element minOccurs="0" maxOccurs="1" ref="xlf:notes"/>
<xs:choice minOccurs="1" maxOccurs="unbounded">
<xs:element ref="xlf:unit"/>
<xs:element ref="xlf:group"/>
</xs:choice>
</xs:sequence>
<xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
<xs:attribute name="canResegment" use="optional" type="xlf:yesNo" default="yes"/>
<xs:attribute name="original" use="optional"/>
<xs:attribute name="translate" use="optional" type="xlf:yesNo" default="yes"/>
<xs:attribute name="srcDir" use="optional" type="xlf:dirValue" default="auto"/>
<xs:attribute name="trgDir" use="optional" type="xlf:dirValue" default="auto"/>
<xs:attribute ref="xml:space" use="optional"/>
<xs:anyAttribute namespace="##other" processContents="lax"/>
</xs:complexType>
</xs:element>
<xs:element name="skeleton">
<xs:complexType mixed="true">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"
processContents="lax"/>
</xs:sequence>
<xs:attribute name="href" use="optional"/>
</xs:complexType>
</xs:element>
<xs:element name="group">
<xs:complexType mixed="false">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"
processContents="lax"/>
<xs:element minOccurs="0" maxOccurs="1" ref="xlf:notes"/>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element ref="xlf:unit"/>
<xs:element ref="xlf:group"/>
</xs:choice>
</xs:sequence>
<xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
<xs:attribute name="name" use="optional"/>
<xs:attribute name="canResegment" use="optional" type="xlf:yesNo"/>
<xs:attribute name="translate" use="optional" type="xlf:yesNo"/>
<xs:attribute name="srcDir" use="optional" type="xlf:dirValue"/>
<xs:attribute name="trgDir" use="optional" type="xlf:dirValue"/>
<xs:attribute name="type" use="optional" type="xlf:userDefinedValue"/>
<xs:attribute ref="xml:space" use="optional"/>
<xs:anyAttribute namespace="##other" processContents="lax"/>
</xs:complexType>
</xs:element>
<xs:element name="unit">
<xs:complexType mixed="false">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"
processContents="lax"/>
<xs:element minOccurs="0" maxOccurs="1" ref="xlf:notes"/>
<xs:element minOccurs="0" maxOccurs="1" ref="xlf:originalData"/>
<xs:choice minOccurs="1" maxOccurs="unbounded">
<xs:element ref="xlf:segment"/>
<xs:element ref="xlf:ignorable"/>
</xs:choice>
</xs:sequence>
<xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
<xs:attribute name="name" use="optional"/>
<xs:attribute name="canResegment" use="optional" type="xlf:yesNo"/>
<xs:attribute name="translate" use="optional" type="xlf:yesNo"/>
<xs:attribute name="srcDir" use="optional" type="xlf:dirValue"/>
<xs:attribute name="trgDir" use="optional" type="xlf:dirValue"/>
<xs:attribute ref="xml:space" use="optional"/>
<xs:attribute name="type" use="optional" type="xlf:userDefinedValue"/>
<xs:anyAttribute namespace="##other" processContents="lax"/>
</xs:complexType>
</xs:element>
<xs:element name="segment">
<xs:complexType mixed="false">
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="1" ref="xlf:source"/>
<xs:element minOccurs="0" maxOccurs="1" ref="xlf:target"/>
</xs:sequence>
<xs:attribute name="id" use="optional" type="xs:NMTOKEN"/>
<xs:attribute name="canResegment" use="optional" type="xlf:yesNo"/>
<xs:attribute name="state" use="optional" type="xlf:stateType" default="initial"/>
<xs:attribute name="subState" use="optional"/>
</xs:complexType>
</xs:element>
<xs:element name="ignorable">
<xs:complexType mixed="false">
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="1" ref="xlf:source"/>
<xs:element minOccurs="0" maxOccurs="1" ref="xlf:target"/>
</xs:sequence>
<xs:attribute name="id" use="optional" type="xs:NMTOKEN"/>
</xs:complexType>
</xs:element>
<xs:element name="notes">
<xs:complexType mixed="false">
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="unbounded" ref="xlf:note"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="note">
<xs:complexType mixed="true">
<xs:attribute name="id" use="optional" type="xs:NMTOKEN"/>
<xs:attribute name="appliesTo" use="optional" type="xlf:appliesTo"/>
<xs:attribute name="category" use="optional"/>
<xs:attribute name="priority" use="optional" type="xlf:priorityValue" default="1"/>
<xs:anyAttribute namespace="##other" processContents="lax"/>
</xs:complexType>
</xs:element>
<xs:element name="originalData">
<xs:complexType mixed="false">
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="unbounded" ref="xlf:data"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="data">
<xs:complexType mixed="true">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="xlf:cp"/>
</xs:sequence>
<xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
<xs:attribute name="dir" use="optional" type="xlf:dirValue" default="auto"/>
<xs:attribute ref="xml:space" use="optional" fixed="preserve"/>
</xs:complexType>
</xs:element>
<xs:element name="source">
<xs:complexType mixed="true">
<xs:group ref="xlf:inline" minOccurs="0" maxOccurs="unbounded"/>
<xs:attribute ref="xml:lang" use="optional"/>
<xs:attribute ref="xml:space" use="optional"/>
</xs:complexType>
</xs:element>
<xs:element name="target">
<xs:complexType mixed="true">
<xs:group ref="xlf:inline" minOccurs="0" maxOccurs="unbounded"/>
<xs:attribute ref="xml:lang" use="optional"/>
<xs:attribute ref="xml:space" use="optional"/>
<xs:attribute name="order" use="optional" type="xs:positiveInteger"/>
</xs:complexType>
</xs:element>
<!-- Inline Elements -->
<xs:element name="cp">
<!-- Code Point -->
<xs:complexType mixed="false">
<xs:attribute name="hex" use="required" type="xs:hexBinary"/>
</xs:complexType>
</xs:element>
<xs:element name="ph">
<!-- Placeholder -->
<xs:complexType mixed="false">
<xs:attribute name="canCopy" use="optional" type="xlf:yesNo" default="yes"/>
<xs:attribute name="canDelete" use="optional" type="xlf:yesNo" default="yes"/>
<xs:attribute name="canReorder" use="optional" type="xlf:yesNoFirstNo" default="yes"/>
<xs:attribute name="copyOf" use="optional" type="xs:NMTOKEN"/>
<xs:attribute name="disp" use="optional"/>
<xs:attribute name="equiv" use="optional"/>
<xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
<xs:attribute name="dataRef" use="optional" type="xs:NMTOKEN"/>
<xs:attribute name="subFlows" use="optional" type="xs:NMTOKENS"/>
<xs:attribute name="subType" use="optional" type="xlf:userDefinedValue"/>
<xs:attribute name="type" use="optional" type="xlf:attrType_type"/>
<xs:anyAttribute namespace="##other" processContents="lax"/>
</xs:complexType>
</xs:element>
<xs:element name="pc">
<!-- Paired Code -->
<xs:complexType mixed="true">
<xs:group ref="xlf:inline" minOccurs="0" maxOccurs="unbounded"/>
<xs:attribute name="canCopy" use="optional" type="xlf:yesNo" default="yes"/>
<xs:attribute name="canDelete" use="optional" type="xlf:yesNo" default="yes"/>
<xs:attribute name="canOverlap" use="optional" type="xlf:yesNo"/>
<xs:attribute name="canReorder" use="optional" type="xlf:yesNoFirstNo" default="yes"/>
<xs:attribute name="copyOf" use="optional" type="xs:NMTOKEN"/>
<xs:attribute name="dispEnd" use="optional"/>
<xs:attribute name="dispStart" use="optional"/>
<xs:attribute name="equivEnd" use="optional"/>
<xs:attribute name="equivStart" use="optional"/>
<xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
<xs:attribute name="dataRefEnd" use="optional" type="xs:NMTOKEN"/>
<xs:attribute name="dataRefStart" use="optional" type="xs:NMTOKEN"/>
<xs:attribute name="subFlowsEnd" use="optional" type="xs:NMTOKENS"/>
<xs:attribute name="subFlowsStart" use="optional" type="xs:NMTOKENS"/>
<xs:attribute name="subType" use="optional" type="xlf:userDefinedValue"/>
<xs:attribute name="type" use="optional" type="xlf:attrType_type"/>
<xs:attribute name="dir" use="optional" type="xlf:dirValue"/>
<xs:anyAttribute namespace="##other" processContents="lax"/>
</xs:complexType>
</xs:element>
<xs:element name="sc">
<!-- Start Code -->
<xs:complexType mixed="false">
<xs:attribute name="canCopy" use="optional" type="xlf:yesNo" default="yes"/>
<xs:attribute name="canDelete" use="optional" type="xlf:yesNo" default="yes"/>
<xs:attribute name="canOverlap" use="optional" type="xlf:yesNo" default="yes"/>
<xs:attribute name="canReorder" use="optional" type="xlf:yesNoFirstNo" default="yes"/>
<xs:attribute name="copyOf" use="optional" type="xs:NMTOKEN"/>
<xs:attribute name="dataRef" use="optional" type="xs:NMTOKEN"/>
<xs:attribute name="dir" use="optional" type="xlf:dirValue"/>
<xs:attribute name="disp" use="optional"/>
<xs:attribute name="equiv" use="optional"/>
<xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
<xs:attribute name="isolated" use="optional" type="xlf:yesNo" default="no"/>
<xs:attribute name="subFlows" use="optional" type="xs:NMTOKENS"/>
<xs:attribute name="subType" use="optional" type="xlf:userDefinedValue"/>
<xs:attribute name="type" use="optional" type="xlf:attrType_type"/>
<xs:anyAttribute namespace="##other" processContents="lax"/>
</xs:complexType>
</xs:element>
<xs:element name="ec">
<!-- End Code -->
<xs:complexType mixed="false">
<xs:attribute name="canCopy" use="optional" type="xlf:yesNo" default="yes"/>
<xs:attribute name="canDelete" use="optional" type="xlf:yesNo" default="yes"/>
<xs:attribute name="canOverlap" use="optional" type="xlf:yesNo" default="yes"/>
<xs:attribute name="canReorder" use="optional" type="xlf:yesNoFirstNo" default="yes"/>
<xs:attribute name="copyOf" use="optional" type="xs:NMTOKEN"/>
<xs:attribute name="dataRef" use="optional" type="xs:NMTOKEN"/>
<xs:attribute name="dir" use="optional" type="xlf:dirValue"/>
<xs:attribute name="disp" use="optional"/>
<xs:attribute name="equiv" use="optional"/>
<xs:attribute name="id" use="optional" type="xs:NMTOKEN"/>
<xs:attribute name="isolated" use="optional" type="xlf:yesNo" default="no"/>
<xs:attribute name="startRef" use="optional" type="xs:NMTOKEN"/>
<xs:attribute name="subFlows" use="optional" type="xs:NMTOKENS"/>
<xs:attribute name="subType" use="optional" type="xlf:userDefinedValue"/>
<xs:attribute name="type" use="optional" type="xlf:attrType_type"/>
<xs:anyAttribute namespace="##other" processContents="lax"/>
</xs:complexType>
</xs:element>
<xs:element name="mrk">
<!-- Annotation Marker -->
<xs:complexType mixed="true">
<xs:group ref="xlf:inline" minOccurs="0" maxOccurs="unbounded"/>
<xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
<xs:attribute name="translate" use="optional" type="xlf:yesNo"/>
<xs:attribute name="type" use="optional" type="xlf:attrType_typeForMrk"/>
<xs:attribute name="ref" use="optional" type="xs:anyURI"/>
<xs:attribute name="value" use="optional"/>
<xs:anyAttribute namespace="##other" processContents="lax"/>
</xs:complexType>
</xs:element>
<xs:element name="sm">
<!-- Start Annotation Marker -->
<xs:complexType mixed="false">
<xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
<xs:attribute name="translate" use="optional" type="xlf:yesNo"/>
<xs:attribute name="type" use="optional" type="xlf:attrType_typeForMrk"/>
<xs:attribute name="ref" use="optional" type="xs:anyURI"/>
<xs:attribute name="value" use="optional"/>
<xs:anyAttribute namespace="##other" processContents="lax"/>
</xs:complexType>
</xs:element>
<xs:element name="em">
<!-- End Annotation Marker -->
<xs:complexType mixed="false">
<xs:attribute name="startRef" use="required" type="xs:NMTOKEN"/>
</xs:complexType>
</xs:element>
</xs:schema>

View File

@@ -66,8 +66,6 @@ class LoggingTranslator implements TranslatorInterface, TranslatorBagInterface
/**
* {@inheritdoc}
*
* @api
*/
public function setLocale($locale)
{
@@ -76,8 +74,6 @@ class LoggingTranslator implements TranslatorInterface, TranslatorBagInterface
/**
* {@inheritdoc}
*
* @api
*/
public function getLocale()
{

View File

@@ -17,8 +17,6 @@ use Symfony\Component\Config\Resource\ResourceInterface;
* MessageCatalogue.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterface
{
@@ -34,8 +32,6 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
*
* @param string $locale The locale
* @param array $messages An array of messages classified by domain
*
* @api
*/
public function __construct($locale, array $messages = array())
{
@@ -45,8 +41,6 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*
* @api
*/
public function getLocale()
{
@@ -55,8 +49,6 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*
* @api
*/
public function getDomains()
{
@@ -65,8 +57,6 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*
* @api
*/
public function all($domain = null)
{
@@ -79,8 +69,6 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*
* @api
*/
public function set($id, $translation, $domain = 'messages')
{
@@ -89,8 +77,6 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*
* @api
*/
public function has($id, $domain = 'messages')
{
@@ -115,8 +101,6 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*
* @api
*/
public function get($id, $domain = 'messages')
{
@@ -133,8 +117,6 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*
* @api
*/
public function replace($messages, $domain = 'messages')
{
@@ -145,8 +127,6 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*
* @api
*/
public function add($messages, $domain = 'messages')
{
@@ -159,8 +139,6 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*
* @api
*/
public function addCatalogue(MessageCatalogueInterface $catalogue)
{
@@ -184,12 +162,17 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*
* @api
*/
public function addFallbackCatalogue(MessageCatalogueInterface $catalogue)
{
// detect circular references
$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()));
}
}
$c = $this;
do {
if ($c->getLocale() === $catalogue->getLocale()) {
@@ -207,8 +190,6 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*
* @api
*/
public function getFallbackCatalogue()
{
@@ -217,8 +198,6 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*
* @api
*/
public function getResources()
{
@@ -227,8 +206,6 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
/**
* {@inheritdoc}
*
* @api
*/
public function addResource(ResourceInterface $resource)
{

View File

@@ -17,8 +17,6 @@ use Symfony\Component\Config\Resource\ResourceInterface;
* MessageCatalogueInterface.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
interface MessageCatalogueInterface
{
@@ -26,8 +24,6 @@ interface MessageCatalogueInterface
* Gets the catalogue locale.
*
* @return string The locale
*
* @api
*/
public function getLocale();
@@ -35,8 +31,6 @@ interface MessageCatalogueInterface
* Gets the domains.
*
* @return array An array of domains
*
* @api
*/
public function getDomains();
@@ -48,8 +42,6 @@ interface MessageCatalogueInterface
* @param string $domain The domain name
*
* @return array An array of messages
*
* @api
*/
public function all($domain = null);
@@ -59,8 +51,6 @@ interface MessageCatalogueInterface
* @param string $id The message id
* @param string $translation The messages translation
* @param string $domain The domain name
*
* @api
*/
public function set($id, $translation, $domain = 'messages');
@@ -71,8 +61,6 @@ interface MessageCatalogueInterface
* @param string $domain The domain name
*
* @return bool true if the message has a translation, false otherwise
*
* @api
*/
public function has($id, $domain = 'messages');
@@ -83,8 +71,6 @@ interface MessageCatalogueInterface
* @param string $domain The domain name
*
* @return bool true if the message has a translation, false otherwise
*
* @api
*/
public function defines($id, $domain = 'messages');
@@ -95,8 +81,6 @@ interface MessageCatalogueInterface
* @param string $domain The domain name
*
* @return string The message translation
*
* @api
*/
public function get($id, $domain = 'messages');
@@ -105,8 +89,6 @@ interface MessageCatalogueInterface
*
* @param array $messages An array of translations
* @param string $domain The domain name
*
* @api
*/
public function replace($messages, $domain = 'messages');
@@ -115,8 +97,6 @@ interface MessageCatalogueInterface
*
* @param array $messages An array of translations
* @param string $domain The domain name
*
* @api
*/
public function add($messages, $domain = 'messages');
@@ -126,8 +106,6 @@ interface MessageCatalogueInterface
* The two catalogues must have the same locale.
*
* @param MessageCatalogueInterface $catalogue A MessageCatalogueInterface instance
*
* @api
*/
public function addCatalogue(MessageCatalogueInterface $catalogue);
@@ -138,8 +116,6 @@ interface MessageCatalogueInterface
* This is used to provide default translations when they do not exist for the current locale.
*
* @param MessageCatalogueInterface $catalogue A MessageCatalogueInterface instance
*
* @api
*/
public function addFallbackCatalogue(MessageCatalogueInterface $catalogue);
@@ -147,8 +123,6 @@ interface MessageCatalogueInterface
* Gets the fallback catalogue.
*
* @return MessageCatalogueInterface|null A MessageCatalogueInterface instance or null when no fallback has been set
*
* @api
*/
public function getFallbackCatalogue();
@@ -156,8 +130,6 @@ interface MessageCatalogueInterface
* Returns an array of resources loaded to build this collection.
*
* @return ResourceInterface[] An array of resources
*
* @api
*/
public function getResources();
@@ -165,8 +137,6 @@ interface MessageCatalogueInterface
* Adds a resource for this collection.
*
* @param ResourceInterface $resource A resource instance
*
* @api
*/
public function addResource(ResourceInterface $resource);
}

View File

@@ -16,8 +16,6 @@ namespace Symfony\Component\Translation;
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @api
*/
class MessageSelector
{
@@ -46,8 +44,6 @@ class MessageSelector
* @return string
*
* @throws \InvalidArgumentException
*
* @api
*/
public function choose($message, $number, $locale)
{

View File

@@ -131,6 +131,7 @@ class PluralizationRules
case 'fr':
case 'gun':
case 'hi':
case 'hy':
case 'ln':
case 'mg':
case 'nso':
@@ -189,12 +190,10 @@ class PluralizationRules
/**
* Overrides the default plural rule for a given locale.
*
* @param string $rule A PHP callable
* @param string $locale The locale
*
* @throws \LogicException
* @param callable $rule A PHP callable
* @param string $locale The locale
*/
public static function set($rule, $locale)
public static function set(callable $rule, $locale)
{
if ('pt_BR' === $locale) {
// temporary set a locale for brazilian
@@ -205,10 +204,6 @@ class PluralizationRules
$locale = substr($locale, 0, -strlen(strrchr($locale, '_')));
}
if (!is_callable($rule)) {
throw new \LogicException('The given rule can not be called');
}
self::$rules[$locale] = $rule;
}
}

13
vendor/symfony/translation/README.md vendored Normal file
View File

@@ -0,0 +1,13 @@
Translation Component
=====================
The Translation component provides tools to internationalize your application.
Resources
---------
* [Documentation](https://symfony.com/doc/current/components/translation/index.html)
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
* [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls)
in the [main Symfony repository](https://github.com/symfony/symfony)

View File

@@ -1,109 +0,0 @@
<?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\Dumper;
use Symfony\Component\Translation\MessageCatalogue;
/**
* XliffFileDumper generates xliff files from a message catalogue.
*
* @author Michel Salib <michelsalib@hotmail.com>
*/
class XliffFileDumper extends FileDumper
{
/**
* @var string
*/
private $defaultLocale;
/**
* {@inheritdoc}
*/
public function dump(MessageCatalogue $messages, $options = array())
{
if (array_key_exists('default_locale', $options)) {
$this->defaultLocale = $options['default_locale'];
} else {
$this->defaultLocale = \Locale::getDefault();
}
parent::dump($messages, $options);
}
/**
* {@inheritdoc}
*/
protected function format(MessageCatalogue $messages, $domain)
{
$dom = new \DOMDocument('1.0', 'utf-8');
$dom->formatOutput = true;
$xliff = $dom->appendChild($dom->createElement('xliff'));
$xliff->setAttribute('version', '1.2');
$xliff->setAttribute('xmlns', 'urn:oasis:names:tc:xliff:document:1.2');
$xliffFile = $xliff->appendChild($dom->createElement('file'));
$xliffFile->setAttribute('source-language', str_replace('_', '-', $this->defaultLocale));
$xliffFile->setAttribute('target-language', str_replace('_', '-', $messages->getLocale()));
$xliffFile->setAttribute('datatype', 'plaintext');
$xliffFile->setAttribute('original', 'file.ext');
$xliffBody = $xliffFile->appendChild($dom->createElement('body'));
foreach ($messages->all($domain) as $source => $target) {
$translation = $dom->createElement('trans-unit');
$translation->setAttribute('id', md5($source));
$translation->setAttribute('resname', $source);
$s = $translation->appendChild($dom->createElement('source'));
$s->appendChild($dom->createTextNode($source));
// Does the target contain characters requiring a CDATA section?
$text = 1 === preg_match('/[&<>]/', $target) ? $dom->createCDATASection($target) : $dom->createTextNode($target);
$t = $translation->appendChild($dom->createElement('target'));
$t->appendChild($text);
$metadata = $messages->getMetadata($source, $domain);
if (null !== $metadata && array_key_exists('notes', $metadata) && is_array($metadata['notes'])) {
foreach ($metadata['notes'] as $note) {
if (!isset($note['content'])) {
continue;
}
$n = $translation->appendChild($dom->createElement('note'));
$n->appendChild($dom->createTextNode($note['content']));
if (isset($note['priority'])) {
$n->setAttribute('priority', $note['priority']);
}
if (isset($note['from'])) {
$n->setAttribute('from', $note['from']);
}
}
}
$xliffBody->appendChild($translation);
}
return $dom->saveXML();
}
/**
* {@inheritdoc}
*/
protected function getExtension()
{
return 'xlf';
}
}

View File

@@ -1,48 +0,0 @@
<?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\Loader;
use Symfony\Component\Translation\Exception\InvalidResourceException;
use Symfony\Component\Translation\Exception\NotFoundResourceException;
use Symfony\Component\Config\Resource\FileResource;
/**
* IniFileLoader loads translations from an ini file.
*
* @author stealth35
*/
class IniFileLoader extends ArrayLoader
{
/**
* {@inheritdoc}
*/
public function load($resource, $locale, $domain = 'messages')
{
if (!stream_is_local($resource)) {
throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
}
if (!file_exists($resource)) {
throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
}
$messages = parse_ini_file($resource, true);
$catalogue = parent::load($messages, $locale, $domain);
if (class_exists('Symfony\Component\Config\Resource\FileResource')) {
$catalogue->addResource(new FileResource($resource));
}
return $catalogue;
}
}

View File

@@ -1,188 +0,0 @@
<?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\Loader;
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;
/**
* XliffFileLoader loads translations from XLIFF files.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
class XliffFileLoader implements LoaderInterface
{
/**
* {@inheritdoc}
*
* @api
*/
public function load($resource, $locale, $domain = 'messages')
{
if (!stream_is_local($resource)) {
throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
}
if (!file_exists($resource)) {
throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
}
list($xml, $encoding) = $this->parseFile($resource);
$xml->registerXPathNamespace('xliff', 'urn:oasis:names:tc:xliff:document:1.2');
$catalogue = new MessageCatalogue($locale);
foreach ($xml->xpath('//xliff:trans-unit') as $translation) {
$attributes = $translation->attributes();
if (!(isset($attributes['resname']) || isset($translation->source)) || !isset($translation->target)) {
continue;
}
$source = isset($attributes['resname']) && $attributes['resname'] ? $attributes['resname'] : $translation->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) $translation->target, $encoding);
$catalogue->set((string) $source, $target, $domain);
if (isset($translation->note)) {
$notes = array();
foreach ($translation->note as $xmlNote) {
$noteAttributes = $xmlNote->attributes();
$note = array('content' => $this->utf8ToCharset((string) $xmlNote, $encoding));
if (isset($noteAttributes['priority'])) {
$note['priority'] = (int) $noteAttributes['priority'];
}
if (isset($noteAttributes['from'])) {
$note['from'] = (string) $noteAttributes['from'];
}
$notes[] = $note;
}
$catalogue->setMetadata((string) $source, array('notes' => $notes), $domain);
}
}
if (class_exists('Symfony\Component\Config\Resource\FileResource')) {
$catalogue->addResource(new FileResource($resource));
}
return $catalogue;
}
/**
* 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)
{
if ('UTF-8' !== $encoding && !empty($encoding)) {
if (function_exists('mb_convert_encoding')) {
return mb_convert_encoding($content, $encoding, 'UTF-8');
}
if (function_exists('iconv')) {
return iconv('UTF-8', $encoding, $content);
}
throw new \RuntimeException('No suitable convert encoding function (use UTF-8 as your encoding or install the iconv or mbstring extension).');
}
return $content;
}
/**
* Validates and parses the given file into a SimpleXMLElement.
*
* @param string $file
*
* @throws \RuntimeException
*
* @return \SimpleXMLElement
*
* @throws InvalidResourceException
*/
private function parseFile($file)
{
try {
$dom = XmlUtils::loadFile($file);
} catch (\InvalidArgumentException $e) {
throw new InvalidResourceException(sprintf('Unable to load "%s": %s', $file, $e->getMessage()), $e->getCode(), $e);
}
$internalErrors = libxml_use_internal_errors(true);
$location = str_replace('\\', '/', __DIR__).'/schema/dic/xliff-core/xml.xsd';
$parts = explode('/', $location);
if (0 === stripos($location, 'phar://')) {
$tmpfile = tempnam(sys_get_temp_dir(), 'sf2');
if ($tmpfile) {
copy($location, $tmpfile);
$parts = explode('/', str_replace('\\', '/', $tmpfile));
}
}
$drive = '\\' === DIRECTORY_SEPARATOR ? array_shift($parts).'/' : '';
$location = 'file:///'.$drive.implode('/', array_map('rawurlencode', $parts));
$source = file_get_contents(__DIR__.'/schema/dic/xliff-core/xliff-core-1.2-strict.xsd');
$source = str_replace('http://www.w3.org/2001/xml.xsd', $location, $source);
if (!@$dom->schemaValidateSource($source)) {
throw new InvalidResourceException(sprintf('Invalid resource provided: "%s"; Errors: %s', $file, implode("\n", $this->getXmlErrors($internalErrors))));
}
$dom->normalizeDocument();
libxml_clear_errors();
libxml_use_internal_errors($internalErrors);
return array(simplexml_import_dom($dom), strtoupper($dom->encoding));
}
/**
* Returns the XML errors of the internal XML parser.
*
* @param bool $internalErrors
*
* @return array An array of errors
*/
private function getXmlErrors($internalErrors)
{
$errors = array();
foreach (libxml_get_errors() as $error) {
$errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)',
LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR',
$error->code,
trim($error->message),
$error->file ?: 'n/a',
$error->line,
$error->column
);
}
libxml_clear_errors();
libxml_use_internal_errors($internalErrors);
return $errors;
}
}

View File

@@ -1,78 +0,0 @@
<?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\Loader;
use Symfony\Component\Translation\Exception\InvalidResourceException;
use Symfony\Component\Translation\Exception\NotFoundResourceException;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Yaml\Parser as YamlParser;
use Symfony\Component\Yaml\Exception\ParseException;
/**
* YamlFileLoader loads translations from Yaml files.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
class YamlFileLoader extends ArrayLoader
{
private $yamlParser;
/**
* {@inheritdoc}
*
* @api
*/
public function load($resource, $locale, $domain = 'messages')
{
if (!stream_is_local($resource)) {
throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
}
if (!file_exists($resource)) {
throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
}
if (!class_exists('Symfony\Component\Yaml\Parser')) {
throw new \LogicException('Loading translations from the YAML format requires the Symfony Yaml component.');
}
if (null === $this->yamlParser) {
$this->yamlParser = new YamlParser();
}
try {
$messages = $this->yamlParser->parse(file_get_contents($resource));
} catch (ParseException $e) {
throw new InvalidResourceException(sprintf('Error parsing YAML, invalid file "%s"', $resource), 0, $e);
}
// empty file
if (null === $messages) {
$messages = array();
}
// not an array
if (!is_array($messages)) {
throw new InvalidResourceException(sprintf('The file "%s" must contain a YAML array.', $resource));
}
$catalogue = parent::load($messages, $locale, $domain);
if (class_exists('Symfony\Component\Config\Resource\FileResource')) {
$catalogue->addResource(new FileResource($resource));
}
return $catalogue;
}
}

View File

@@ -1,37 +0,0 @@
Translation Component
=====================
Translation provides tools for loading translation files and generating
translated strings from these including support for pluralization.
```php
use Symfony\Component\Translation\Translator;
use Symfony\Component\Translation\MessageSelector;
use Symfony\Component\Translation\Loader\ArrayLoader;
$translator = new Translator('fr_FR', new MessageSelector());
$translator->setFallbackLocales(array('fr'));
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', array(
'Hello World!' => 'Bonjour',
), 'fr');
echo $translator->trans('Hello World!')."\n";
```
Resources
---------
Silex integration:
https://github.com/fabpot/Silex/blob/master/src/Silex/Provider/TranslationServiceProvider.php
Documentation:
https://symfony.com/doc/2.6/book/translation.html
You can run the unit tests with the following command:
$ cd path/to/Symfony/Component/Translation/
$ composer install
$ phpunit

View File

@@ -1,41 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Translation\Tests\Dumper;
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\Dumper\XliffFileDumper;
class XliffFileDumperTest extends \PHPUnit_Framework_TestCase
{
public function testDump()
{
$catalogue = new MessageCatalogue('en_US');
$catalogue->add(array(
'foo' => 'bar',
'key' => '',
'key.with.cdata' => '<source> & <target>',
));
$catalogue->setMetadata('foo', array('notes' => array(array('priority' => 1, 'from' => 'bar', 'content' => 'baz'))));
$catalogue->setMetadata('key', array('notes' => array(array('content' => 'baz'), array('content' => 'qux'))));
$tempDir = sys_get_temp_dir();
$dumper = new XliffFileDumper();
$dumper->dump($catalogue, array('path' => $tempDir, 'default_locale' => 'fr_FR'));
$this->assertEquals(
file_get_contents(__DIR__.'/../fixtures/resources-clean.xlf'),
file_get_contents($tempDir.'/messages.en_US.xlf')
);
unlink($tempDir.'/messages.en_US.xlf');
}
}

View File

@@ -1,32 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Translation\Tests\Dumper;
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\Dumper\YamlFileDumper;
class YamlFileDumperTest extends \PHPUnit_Framework_TestCase
{
public function testDump()
{
$catalogue = new MessageCatalogue('en');
$catalogue->add(array('foo' => 'bar'));
$tempDir = sys_get_temp_dir();
$dumper = new YamlFileDumper();
$dumper->dump($catalogue, array('path' => $tempDir));
$this->assertEquals(file_get_contents(__DIR__.'/../fixtures/resources.yml'), file_get_contents($tempDir.'/messages.en.yml'));
unlink($tempDir.'/messages.en.yml');
}
}

View File

@@ -11,11 +11,11 @@
namespace Symfony\Component\Translation\Tests\Catalogue;
use Symfony\Component\Translation\Catalogue\DiffOperation;
use Symfony\Component\Translation\Catalogue\TargetOperation;
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\MessageCatalogueInterface;
class DiffOperationTest extends AbstractOperationTest
class TargetOperationTest extends AbstractOperationTest
{
public function testGetMessagesFromSingleDomain()
{
@@ -77,6 +77,6 @@ class DiffOperationTest extends AbstractOperationTest
protected function createOperation(MessageCatalogueInterface $source, MessageCatalogueInterface $target)
{
return new DiffOperation($source, $target);
return new TargetOperation($source, $target);
}
}

View File

@@ -0,0 +1,148 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Translation\Tests\DataCollector;
use Symfony\Component\Translation\DataCollectorTranslator;
use Symfony\Component\Translation\DataCollector\TranslationDataCollector;
class TranslationDataCollectorTest extends \PHPUnit_Framework_TestCase
{
protected function setUp()
{
if (!class_exists('Symfony\Component\HttpKernel\DataCollector\DataCollector')) {
$this->markTestSkipped('The "DataCollector" is not available');
}
}
public function testCollectEmptyMessages()
{
$translator = $this->getTranslator();
$translator->expects($this->any())->method('getCollectedMessages')->will($this->returnValue(array()));
$dataCollector = new TranslationDataCollector($translator);
$dataCollector->lateCollect();
$this->assertEquals(0, $dataCollector->getCountMissings());
$this->assertEquals(0, $dataCollector->getCountFallbacks());
$this->assertEquals(0, $dataCollector->getCountDefines());
$this->assertEquals(array(), $dataCollector->getMessages());
}
public function testCollect()
{
$collectedMessages = array(
array(
'id' => 'foo',
'translation' => 'foo (en)',
'locale' => 'en',
'domain' => 'messages',
'state' => DataCollectorTranslator::MESSAGE_DEFINED,
'parameters' => array(),
'transChoiceNumber' => null,
),
array(
'id' => 'bar',
'translation' => 'bar (fr)',
'locale' => 'fr',
'domain' => 'messages',
'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK,
'parameters' => array(),
'transChoiceNumber' => null,
),
array(
'id' => 'choice',
'translation' => 'choice',
'locale' => 'en',
'domain' => 'messages',
'state' => DataCollectorTranslator::MESSAGE_MISSING,
'parameters' => array('%count%' => 3),
'transChoiceNumber' => 3,
),
array(
'id' => 'choice',
'translation' => 'choice',
'locale' => 'en',
'domain' => 'messages',
'state' => DataCollectorTranslator::MESSAGE_MISSING,
'parameters' => array('%count%' => 3),
'transChoiceNumber' => 3,
),
array(
'id' => 'choice',
'translation' => 'choice',
'locale' => 'en',
'domain' => 'messages',
'state' => DataCollectorTranslator::MESSAGE_MISSING,
'parameters' => array('%count%' => 4, '%foo%' => 'bar'),
'transChoiceNumber' => 4,
),
);
$expectedMessages = array(
array(
'id' => 'foo',
'translation' => 'foo (en)',
'locale' => 'en',
'domain' => 'messages',
'state' => DataCollectorTranslator::MESSAGE_DEFINED,
'count' => 1,
'parameters' => array(),
'transChoiceNumber' => null,
),
array(
'id' => 'bar',
'translation' => 'bar (fr)',
'locale' => 'fr',
'domain' => 'messages',
'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK,
'count' => 1,
'parameters' => array(),
'transChoiceNumber' => null,
),
array(
'id' => 'choice',
'translation' => 'choice',
'locale' => 'en',
'domain' => 'messages',
'state' => DataCollectorTranslator::MESSAGE_MISSING,
'count' => 3,
'parameters' => array(
array('%count%' => 3),
array('%count%' => 3),
array('%count%' => 4, '%foo%' => 'bar'),
),
'transChoiceNumber' => 3,
),
);
$translator = $this->getTranslator();
$translator->expects($this->any())->method('getCollectedMessages')->will($this->returnValue($collectedMessages));
$dataCollector = new TranslationDataCollector($translator);
$dataCollector->lateCollect();
$this->assertEquals(1, $dataCollector->getCountMissings());
$this->assertEquals(1, $dataCollector->getCountFallbacks());
$this->assertEquals(1, $dataCollector->getCountDefines());
$this->assertEquals($expectedMessages, array_values($dataCollector->getMessages()));
}
private function getTranslator()
{
$translator = $this
->getMockBuilder('Symfony\Component\Translation\DataCollectorTranslator')
->disableOriginalConstructor()
->getMock()
;
return $translator;
}
}

View File

@@ -0,0 +1,93 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Translation\Tests;
use Symfony\Component\Translation\Translator;
use Symfony\Component\Translation\DataCollectorTranslator;
use Symfony\Component\Translation\Loader\ArrayLoader;
class DataCollectorTranslatorTest extends \PHPUnit_Framework_TestCase
{
public function testCollectMessages()
{
$collector = $this->createCollector();
$collector->setFallbackLocales(array('fr', 'ru'));
$collector->trans('foo');
$collector->trans('bar');
$collector->transChoice('choice', 0);
$collector->trans('bar_ru');
$collector->trans('bar_ru', array('foo' => 'bar'));
$expectedMessages = array();
$expectedMessages[] = array(
'id' => 'foo',
'translation' => 'foo (en)',
'locale' => 'en',
'domain' => 'messages',
'state' => DataCollectorTranslator::MESSAGE_DEFINED,
'parameters' => array(),
'transChoiceNumber' => null,
);
$expectedMessages[] = array(
'id' => 'bar',
'translation' => 'bar (fr)',
'locale' => 'fr',
'domain' => 'messages',
'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK,
'parameters' => array(),
'transChoiceNumber' => null,
);
$expectedMessages[] = array(
'id' => 'choice',
'translation' => 'choice',
'locale' => 'en',
'domain' => 'messages',
'state' => DataCollectorTranslator::MESSAGE_MISSING,
'parameters' => array(),
'transChoiceNumber' => 0,
);
$expectedMessages[] = array(
'id' => 'bar_ru',
'translation' => 'bar (ru)',
'locale' => 'ru',
'domain' => 'messages',
'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK,
'parameters' => array(),
'transChoiceNumber' => null,
);
$expectedMessages[] = array(
'id' => 'bar_ru',
'translation' => 'bar (ru)',
'locale' => 'ru',
'domain' => 'messages',
'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK,
'parameters' => array('foo' => 'bar'),
'transChoiceNumber' => null,
);
$this->assertEquals($expectedMessages, $collector->getCollectedMessages());
}
private function createCollector()
{
$translator = new Translator('en');
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', array('foo' => 'foo (en)'), 'en');
$translator->addResource('array', array('bar' => 'bar (fr)'), 'fr');
$translator->addResource('array', array('bar_ru' => 'bar (ru)'), 'ru');
$collector = new DataCollectorTranslator($translator);
return $collector;
}
}

View File

@@ -16,18 +16,14 @@ use Symfony\Component\Translation\Dumper\CsvFileDumper;
class CsvFileDumperTest extends \PHPUnit_Framework_TestCase
{
public function testDump()
public function testFormatCatalogue()
{
$catalogue = new MessageCatalogue('en');
$catalogue->add(array('foo' => 'bar', 'bar' => 'foo
foo', 'foo;foo' => 'bar'));
$tempDir = sys_get_temp_dir();
$dumper = new CsvFileDumper();
$dumper->dump($catalogue, array('path' => $tempDir));
$this->assertEquals(file_get_contents(__DIR__.'/../fixtures/valid.csv'), file_get_contents($tempDir.'/messages.en.csv'));
unlink($tempDir.'/messages.en.csv');
$this->assertStringEqualsFile(__DIR__.'/../fixtures/valid.csv', $dumper->formatCatalogue($catalogue, 'messages'));
}
}

View File

@@ -16,6 +16,19 @@ use Symfony\Component\Translation\Dumper\FileDumper;
class FileDumperTest extends \PHPUnit_Framework_TestCase
{
public function testDump()
{
$tempDir = sys_get_temp_dir();
$catalogue = new MessageCatalogue('en');
$catalogue->add(array('foo' => 'bar'));
$dumper = new ConcreteFileDumper();
$dumper->dump($catalogue, array('path' => $tempDir));
$this->assertTrue(file_exists($tempDir.'/messages.en.concrete'));
}
public function testDumpBackupsFileIfExisting()
{
$tempDir = sys_get_temp_dir();
@@ -58,7 +71,7 @@ class FileDumperTest extends \PHPUnit_Framework_TestCase
class ConcreteFileDumper extends FileDumper
{
protected function format(MessageCatalogue $messages, $domain)
public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array())
{
return '';
}

View File

@@ -16,23 +16,13 @@ use Symfony\Component\Translation\Dumper\IcuResFileDumper;
class IcuResFileDumperTest extends \PHPUnit_Framework_TestCase
{
public function testDump()
public function testFormatCatalogue()
{
if (!function_exists('mb_convert_encoding')) {
$this->markTestSkipped('This test requires mbstring to work.');
}
$catalogue = new MessageCatalogue('en');
$catalogue->add(array('foo' => 'bar'));
$tempDir = sys_get_temp_dir().'/IcuResFileDumperTest';
$dumper = new IcuResFileDumper();
$dumper->dump($catalogue, array('path' => $tempDir));
$this->assertEquals(file_get_contents(__DIR__.'/../fixtures/resourcebundle/res/en.res'), file_get_contents($tempDir.'/messages/en.res'));
@unlink($tempDir.'/messages/en.res');
@rmdir($tempDir.'/messages');
@rmdir($tempDir);
$this->assertStringEqualsFile(__DIR__.'/../fixtures/resourcebundle/res/en.res', $dumper->formatCatalogue($catalogue, 'messages'));
}
}

View File

@@ -16,17 +16,13 @@ use Symfony\Component\Translation\Dumper\IniFileDumper;
class IniFileDumperTest extends \PHPUnit_Framework_TestCase
{
public function testDump()
public function testFormatCatalogue()
{
$catalogue = new MessageCatalogue('en');
$catalogue->add(array('foo' => 'bar'));
$tempDir = sys_get_temp_dir();
$dumper = new IniFileDumper();
$dumper->dump($catalogue, array('path' => $tempDir));
$this->assertEquals(file_get_contents(__DIR__.'/../fixtures/resources.ini'), file_get_contents($tempDir.'/messages.en.ini'));
unlink($tempDir.'/messages.en.ini');
$this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.ini', $dumper->formatCatalogue($catalogue, 'messages'));
}
}

View File

@@ -16,21 +16,23 @@ use Symfony\Component\Translation\Dumper\JsonFileDumper;
class JsonFileDumperTest extends \PHPUnit_Framework_TestCase
{
public function testDump()
public function testFormatCatalogue()
{
if (PHP_VERSION_ID < 50400) {
$this->markTestIncomplete('PHP below 5.4 doesn\'t support JSON pretty printing');
}
$catalogue = new MessageCatalogue('en');
$catalogue->add(array('foo' => 'bar'));
$tempDir = sys_get_temp_dir();
$dumper = new JsonFileDumper();
$dumper->dump($catalogue, array('path' => $tempDir));
$this->assertEquals(file_get_contents(__DIR__.'/../fixtures/resources.json'), file_get_contents($tempDir.'/messages.en.json'));
$this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.json', $dumper->formatCatalogue($catalogue, 'messages'));
}
unlink($tempDir.'/messages.en.json');
public function testDumpWithCustomEncoding()
{
$catalogue = new MessageCatalogue('en');
$catalogue->add(array('foo' => '"bar"'));
$dumper = new JsonFileDumper();
$this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.dump.json', $dumper->formatCatalogue($catalogue, 'messages', array('json_encoding' => JSON_HEX_QUOT)));
}
}

View File

@@ -16,16 +16,13 @@ use Symfony\Component\Translation\Dumper\MoFileDumper;
class MoFileDumperTest extends \PHPUnit_Framework_TestCase
{
public function testDump()
public function testFormatCatalogue()
{
$catalogue = new MessageCatalogue('en');
$catalogue->add(array('foo' => 'bar'));
$tempDir = sys_get_temp_dir();
$dumper = new MoFileDumper();
$dumper->dump($catalogue, array('path' => $tempDir));
$this->assertEquals(file_get_contents(__DIR__.'/../fixtures/resources.mo'), file_get_contents($tempDir.'/messages.en.mo'));
unlink($tempDir.'/messages.en.mo');
$this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.mo', $dumper->formatCatalogue($catalogue, 'messages'));
}
}

View File

@@ -16,17 +16,13 @@ use Symfony\Component\Translation\Dumper\PhpFileDumper;
class PhpFileDumperTest extends \PHPUnit_Framework_TestCase
{
public function testDump()
public function testFormatCatalogue()
{
$catalogue = new MessageCatalogue('en');
$catalogue->add(array('foo' => 'bar'));
$tempDir = sys_get_temp_dir();
$dumper = new PhpFileDumper();
$dumper->dump($catalogue, array('path' => $tempDir));
$this->assertEquals(file_get_contents(__DIR__.'/../fixtures/resources.php'), file_get_contents($tempDir.'/messages.en.php'));
unlink($tempDir.'/messages.en.php');
$this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.php', $dumper->formatCatalogue($catalogue, 'messages'));
}
}

View File

@@ -16,16 +16,13 @@ use Symfony\Component\Translation\Dumper\PoFileDumper;
class PoFileDumperTest extends \PHPUnit_Framework_TestCase
{
public function testDump()
public function testFormatCatalogue()
{
$catalogue = new MessageCatalogue('en');
$catalogue->add(array('foo' => 'bar'));
$tempDir = sys_get_temp_dir();
$dumper = new PoFileDumper();
$dumper->dump($catalogue, array('path' => $tempDir));
$this->assertEquals(file_get_contents(__DIR__.'/../fixtures/resources.po'), file_get_contents($tempDir.'/messages.en.po'));
unlink($tempDir.'/messages.en.po');
$this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.po', $dumper->formatCatalogue($catalogue, 'messages'));
}
}

View File

@@ -16,17 +16,13 @@ use Symfony\Component\Translation\Dumper\QtFileDumper;
class QtFileDumperTest extends \PHPUnit_Framework_TestCase
{
public function testDump()
public function testFormatCatalogue()
{
$catalogue = new MessageCatalogue('en');
$catalogue->add(array('foo' => 'bar'), 'resources');
$tempDir = sys_get_temp_dir();
$dumper = new QtFileDumper();
$dumper->dump($catalogue, array('path' => $tempDir));
$this->assertEquals(file_get_contents(__DIR__.'/../fixtures/resources.ts'), file_get_contents($tempDir.'/resources.en.ts'));
unlink($tempDir.'/resources.en.ts');
$this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.ts', $dumper->formatCatalogue($catalogue, 'resources'));
}
}

View File

@@ -0,0 +1,89 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Translation\Tests\Dumper;
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\Dumper\XliffFileDumper;
class XliffFileDumperTest extends \PHPUnit_Framework_TestCase
{
public function testFormatCatalogue()
{
$catalogue = new MessageCatalogue('en_US');
$catalogue->add(array(
'foo' => 'bar',
'key' => '',
'key.with.cdata' => '<source> & <target>',
));
$catalogue->setMetadata('foo', array('notes' => array(array('priority' => 1, 'from' => 'bar', 'content' => 'baz'))));
$catalogue->setMetadata('key', array('notes' => array(array('content' => 'baz'), array('content' => 'qux'))));
$dumper = new XliffFileDumper();
$this->assertStringEqualsFile(
__DIR__.'/../fixtures/resources-clean.xlf',
$dumper->formatCatalogue($catalogue, 'messages', array('default_locale' => 'fr_FR'))
);
}
public function testFormatCatalogueXliff2()
{
$catalogue = new MessageCatalogue('en_US');
$catalogue->add(array(
'foo' => 'bar',
'key' => '',
'key.with.cdata' => '<source> & <target>',
));
$catalogue->setMetadata('key', array('target-attributes' => array('order' => 1)));
$dumper = new XliffFileDumper();
$this->assertStringEqualsFile(
__DIR__.'/../fixtures/resources-2.0-clean.xlf',
$dumper->formatCatalogue($catalogue, 'messages', array('default_locale' => 'fr_FR', 'xliff_version' => '2.0'))
);
}
public function testFormatCatalogueWithCustomToolInfo()
{
$options = array(
'default_locale' => 'en_US',
'tool_info' => array('tool-id' => 'foo', 'tool-name' => 'foo', 'tool-version' => '0.0', 'tool-company' => 'Foo'),
);
$catalogue = new MessageCatalogue('en_US');
$catalogue->add(array('foo' => 'bar'));
$dumper = new XliffFileDumper();
$this->assertStringEqualsFile(
__DIR__.'/../fixtures/resources-tool-info.xlf',
$dumper->formatCatalogue($catalogue, 'messages', $options)
);
}
public function testFormatCatalogueWithTargetAttributesMetadata()
{
$catalogue = new MessageCatalogue('en_US');
$catalogue->add(array(
'foo' => 'bar',
));
$catalogue->setMetadata('foo', array('target-attributes' => array('state' => 'needs-translation')));
$dumper = new XliffFileDumper();
$this->assertStringEqualsFile(
__DIR__.'/../fixtures/resources-target-attributes.xlf',
$dumper->formatCatalogue($catalogue, 'messages', array('default_locale' => 'fr_FR'))
);
}
}

View File

@@ -0,0 +1,46 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Translation\Tests\Dumper;
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\Dumper\YamlFileDumper;
class YamlFileDumperTest extends \PHPUnit_Framework_TestCase
{
public function testTreeFormatCatalogue()
{
$catalogue = new MessageCatalogue('en');
$catalogue->add(
array(
'foo.bar1' => 'value1',
'foo.bar2' => 'value2',
));
$dumper = new YamlFileDumper();
$this->assertStringEqualsFile(__DIR__.'/../fixtures/messages.yml', $dumper->formatCatalogue($catalogue, 'messages', array('as_tree' => true, 'inline' => 999)));
}
public function testLinearFormatCatalogue()
{
$catalogue = new MessageCatalogue('en');
$catalogue->add(
array(
'foo.bar1' => 'value1',
'foo.bar2' => 'value2',
));
$dumper = new YamlFileDumper();
$this->assertStringEqualsFile(__DIR__.'/../fixtures/messages_linear.yml', $dumper->formatCatalogue($catalogue, 'messages'));
}
}

View File

@@ -14,15 +14,11 @@ namespace Symfony\Component\Translation\Tests\Loader;
use Symfony\Component\Translation\Loader\IcuDatFileLoader;
use Symfony\Component\Config\Resource\FileResource;
/**
* @requires extension intl
*/
class IcuDatFileLoaderTest extends LocalizedTestCase
{
protected function setUp()
{
if (!extension_loaded('intl')) {
$this->markTestSkipped('This test requires intl extension to work.');
}
}
/**
* @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
*/

View File

@@ -14,15 +14,11 @@ namespace Symfony\Component\Translation\Tests\Loader;
use Symfony\Component\Translation\Loader\IcuResFileLoader;
use Symfony\Component\Config\Resource\DirectoryResource;
/**
* @requires extension intl
*/
class IcuResFileLoaderTest extends LocalizedTestCase
{
protected function setUp()
{
if (!extension_loaded('intl')) {
$this->markTestSkipped('This test requires intl extension to work.');
}
}
public function testLoad()
{
// resource is build using genrb command

View File

@@ -16,13 +16,6 @@ use Symfony\Component\Config\Resource\FileResource;
class JsonFileLoaderTest extends \PHPUnit_Framework_TestCase
{
protected function setUp()
{
if (!class_exists('Symfony\Component\Config\Loader\Loader')) {
$this->markTestSkipped('The "Config" component is not available');
}
}
public function testLoad()
{
$loader = new JsonFileLoader();

View File

@@ -16,7 +16,7 @@ abstract class LocalizedTestCase extends \PHPUnit_Framework_TestCase
protected function setUp()
{
if (!extension_loaded('intl')) {
$this->markTestSkipped('The "intl" extension is not available');
$this->markTestSkipped('Extension intl is required.');
}
}
}

View File

@@ -93,4 +93,16 @@ class PoFileLoaderTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('escaped "bar"', $messages['escaped "foo"']);
$this->assertEquals('escaped "bar"|escaped "bars"', $messages['escaped "foos"']);
}
public function testSkipFuzzyTranslations()
{
$loader = new PoFileLoader();
$resource = __DIR__.'/../fixtures/fuzzy-translations.po';
$catalogue = $loader->load($resource, 'en', 'domain1');
$messages = $catalogue->all('domain1');
$this->assertArrayHasKey('foo1', $messages);
$this->assertArrayNotHasKey('foo2', $messages);
$this->assertArrayHasKey('foo3', $messages);
}
}

View File

@@ -25,11 +25,12 @@ class XliffFileLoaderTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('en', $catalogue->getLocale());
$this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
$this->assertSame(array(), libxml_get_errors());
$this->assertContainsOnly('string', $catalogue->all('domain1'));
}
public function testLoadWithInternalErrorsEnabled()
{
libxml_use_internal_errors(true);
$internalErrors = libxml_use_internal_errors(true);
$this->assertSame(array(), libxml_get_errors());
@@ -40,6 +41,9 @@ class XliffFileLoaderTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('en', $catalogue->getLocale());
$this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
$this->assertSame(array(), libxml_get_errors());
libxml_clear_errors();
libxml_use_internal_errors($internalErrors);
}
public function testLoadWithResname()
@@ -55,16 +59,11 @@ class XliffFileLoaderTest extends \PHPUnit_Framework_TestCase
$loader = new XliffFileLoader();
$catalogue = $loader->load(__DIR__.'/../fixtures/resources.xlf', 'en', 'domain1');
$this->assertEquals(array('foo' => 'bar', 'key' => '', 'test' => 'with'), $catalogue->all('domain1'));
$this->assertFalse($catalogue->has('extra', 'domain1'));
$this->assertEquals(array('foo' => 'bar', 'extra' => 'extra', 'key' => '', 'test' => 'with'), $catalogue->all('domain1'));
}
public function testEncoding()
{
if (!function_exists('iconv') && !function_exists('mb_convert_encoding')) {
$this->markTestSkipped('The iconv and mbstring extensions are not available.');
}
$loader = new XliffFileLoader();
$catalogue = $loader->load(__DIR__.'/../fixtures/encoding.xlf', 'en', 'domain1');
@@ -73,6 +72,15 @@ class XliffFileLoaderTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(array('notes' => array(array('content' => utf8_decode('bäz')))), $catalogue->getMetadata('foo', 'domain1'));
}
public function testTargetAttributesAreStoredCorrectly()
{
$loader = new XliffFileLoader();
$catalogue = $loader->load(__DIR__.'/../fixtures/with-attributes.xlf', 'en', 'domain1');
$metadata = $catalogue->getMetadata('foo', 'domain1');
$this->assertEquals('translated', $metadata['target-attributes']['state']);
}
/**
* @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
*/
@@ -136,7 +144,26 @@ class XliffFileLoaderTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(array('notes' => array(array('priority' => 1, 'content' => 'foo'))), $catalogue->getMetadata('foo', 'domain1'));
// message without target
$this->assertNull($catalogue->getMetadata('extra', 'domain1'));
$this->assertEquals(array('notes' => array(array('content' => 'bar', 'from' => 'foo'))), $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'));
}
public function testLoadVersion2()
{
$loader = new XliffFileLoader();
$resource = __DIR__.'/../fixtures/resources-2.0.xlf';
$catalogue = $loader->load($resource, 'en', 'domain1');
$this->assertEquals('en', $catalogue->getLocale());
$this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
$this->assertSame(array(), libxml_get_errors());
$domains = $catalogue->all();
$this->assertCount(3, $domains['domain1']);
$this->assertContainsOnly('string', $catalogue->all('domain1'));
// target attributes
$this->assertEquals(array('target-attributes' => array('order' => 1)), $catalogue->getMetadata('bar', 'domain1'));
}
}

View File

@@ -17,13 +17,6 @@ use Symfony\Component\Translation\Loader\ArrayLoader;
class LoggingTranslatorTest extends \PHPUnit_Framework_TestCase
{
protected function setUp()
{
if (!interface_exists('Psr\Log\LoggerInterface')) {
$this->markTestSkipped('The "LoggerInterface" is not available');
}
}
public function testTransWithNoTranslationIsLogged()
{
$logger = $this->getMock('Psr\Log\LoggerInterface');

View File

@@ -127,7 +127,7 @@ class MessageCatalogueTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException \LogicException
*/
public function testAddFallbackCatalogueWithCircularReference()
public function testAddFallbackCatalogueWithParentCircularReference()
{
$main = new MessageCatalogue('en_US');
$fallback = new MessageCatalogue('fr_FR');
@@ -136,6 +136,20 @@ class MessageCatalogueTest extends \PHPUnit_Framework_TestCase
$main->addFallbackCatalogue($fallback);
}
/**
* @expectedException \LogicException
*/
public function testAddFallbackCatalogueWithFallbackCircularReference()
{
$fr = new MessageCatalogue('fr');
$en = new MessageCatalogue('en');
$es = new MessageCatalogue('es');
$fr->addFallbackCatalogue($en);
$es->addFallbackCatalogue($en);
$en->addFallbackCatalogue($fr);
}
/**
* @expectedException \LogicException
*/
@@ -178,10 +192,10 @@ class MessageCatalogueTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(array(), $catalogue->getMetadata('key2', 'messages'), 'Metadata key2 is array');
$catalogue->deleteMetadata('key2', 'messages');
$this->assertEquals(null, $catalogue->getMetadata('key2', 'messages'), 'Metadata key2 should is deleted.');
$this->assertNull($catalogue->getMetadata('key2', 'messages'), 'Metadata key2 should is deleted.');
$catalogue->deleteMetadata('key2', 'domain');
$this->assertEquals(null, $catalogue->getMetadata('key2', 'domain'), 'Metadata key2 should is deleted.');
$this->assertNull($catalogue->getMetadata('key2', 'domain'), 'Metadata key2 should is deleted.');
}
public function testMetadataMerge()

View File

@@ -60,10 +60,10 @@ class PluralizationRulesTest extends \PHPUnit_Framework_TestCase
public function successLangcodes()
{
return array(
array('1', array('ay','bo', 'cgg','dz','id', 'ja', 'jbo', 'ka','kk','km','ko','ky')),
array('2', array('nl', 'fr', 'en', 'de', 'de_GE')),
array('3', array('be','bs','cs','hr')),
array('4', array('cy','mt', 'sl')),
array('1', array('ay', 'bo', 'cgg', 'dz', 'id', 'ja', 'jbo', 'ka', 'kk', 'km', 'ko', 'ky')),
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')),
);
@@ -83,7 +83,7 @@ class PluralizationRulesTest extends \PHPUnit_Framework_TestCase
array('1', array('fa')),
array('2', array('jbo')),
array('3', array('cbs')),
array('4', array('gd','kw')),
array('4', array('gd', 'kw')),
array('5', array('ga')),
array('6', array()),
);

View File

@@ -11,7 +11,7 @@
namespace Symfony\Component\Translation\Tests;
use Symfony\Component\Config\Resource\ResourceInterface;
use Symfony\Component\Config\Resource\SelfCheckingResourceInterface;
use Symfony\Component\Translation\Loader\ArrayLoader;
use Symfony\Component\Translation\Loader\LoaderInterface;
use Symfony\Component\Translation\Translator;
@@ -228,7 +228,7 @@ class TranslatorCacheTest extends \PHPUnit_Framework_TestCase
public function testRefreshCacheWhenResourcesAreNoLongerFresh()
{
$resource = $this->getMock('Symfony\Component\Config\Resource\ResourceInterface');
$resource = $this->getMock('Symfony\Component\Config\Resource\SelfCheckingResourceInterface');
$loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface');
$resource->method('isFresh')->will($this->returnValue(false));
$loader
@@ -281,7 +281,7 @@ class TranslatorCacheTest extends \PHPUnit_Framework_TestCase
}
}
class StaleResource implements ResourceInterface
class StaleResource implements SelfCheckingResourceInterface
{
public function isFresh($timestamp)
{

View File

@@ -273,6 +273,26 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
$translator->trans('foo');
}
public function testFallbackCatalogueResources()
{
$translator = new Translator('en_GB', new MessageSelector());
$translator->addLoader('yml', new \Symfony\Component\Translation\Loader\YamlFileLoader());
$translator->addResource('yml', __DIR__.'/fixtures/empty.yml', 'en_GB');
$translator->addResource('yml', __DIR__.'/fixtures/resources.yml', 'en');
// force catalogue loading
$this->assertEquals('bar', $translator->trans('foo', array()));
$resources = $translator->getCatalogue('en')->getResources();
$this->assertCount(1, $resources);
$this->assertContains( __DIR__.DIRECTORY_SEPARATOR.'fixtures'.DIRECTORY_SEPARATOR.'resources.yml', $resources);
$resources = $translator->getCatalogue('en_GB')->getResources();
$this->assertCount(2, $resources);
$this->assertContains( __DIR__.DIRECTORY_SEPARATOR.'fixtures'.DIRECTORY_SEPARATOR.'empty.yml', $resources);
$this->assertContains( __DIR__.DIRECTORY_SEPARATOR.'fixtures'.DIRECTORY_SEPARATOR.'resources.yml', $resources);
}
/**
* @dataProvider getTransTests
*/
@@ -495,120 +515,6 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
// unchanged if it can't be found
$this->assertEquals('some_message2', $translator->transChoice('some_message2', 10, array('%count%' => 10)));
}
/**
* @dataProvider dataProviderGetMessages
*/
public function testGetMessages($resources, $locale, $expected)
{
$locales = array_keys($resources);
$_locale = null !== $locale ? $locale : reset($locales);
$locales = array_slice($locales, 0, array_search($_locale, $locales));
$translator = new Translator($_locale, new MessageSelector());
$translator->setFallbackLocales(array_reverse($locales));
$translator->addLoader('array', new ArrayLoader());
foreach ($resources as $_locale => $domainMessages) {
foreach ($domainMessages as $domain => $messages) {
$translator->addResource('array', $messages, $_locale, $domain);
}
}
$result = $translator->getMessages($locale);
$this->assertEquals($expected, $result);
}
public function dataProviderGetMessages()
{
$resources = array(
'en' => array(
'jsmessages' => array(
'foo' => 'foo (EN)',
'bar' => 'bar (EN)',
),
'messages' => array(
'foo' => 'foo messages (EN)',
),
'validators' => array(
'int' => 'integer (EN)',
),
),
'pt-PT' => array(
'messages' => array(
'foo' => 'foo messages (PT)',
),
'validators' => array(
'str' => 'integer (PT)',
),
),
'pt_BR' => array(
'validators' => array(
'int' => 'integer (BR)',
),
),
);
return array(
array($resources, null,
array(
'jsmessages' => array(
'foo' => 'foo (EN)',
'bar' => 'bar (EN)',
),
'messages' => array(
'foo' => 'foo messages (EN)',
),
'validators' => array(
'int' => 'integer (EN)',
),
),
),
array($resources, 'en',
array(
'jsmessages' => array(
'foo' => 'foo (EN)',
'bar' => 'bar (EN)',
),
'messages' => array(
'foo' => 'foo messages (EN)',
),
'validators' => array(
'int' => 'integer (EN)',
),
),
),
array($resources, 'pt-PT',
array(
'jsmessages' => array(
'foo' => 'foo (EN)',
'bar' => 'bar (EN)',
),
'messages' => array(
'foo' => 'foo messages (PT)',
),
'validators' => array(
'int' => 'integer (EN)',
'str' => 'integer (PT)',
),
),
),
array($resources, 'pt_BR',
array(
'jsmessages' => array(
'foo' => 'foo (EN)',
'bar' => 'bar (EN)',
),
'messages' => array(
'foo' => 'foo messages (PT)',
),
'validators' => array(
'int' => 'integer (BR)',
'str' => 'integer (PT)',
),
),
),
);
}
}
class StringClass

View File

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

View File

@@ -0,0 +1,65 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Translation\Tests\Writer;
use Symfony\Component\Translation\Dumper\DumperInterface;
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\Writer\TranslationWriter;
class TranslationWriterTest extends \PHPUnit_Framework_TestCase
{
public function testWriteTranslations()
{
$dumper = $this->getMock('Symfony\Component\Translation\Dumper\DumperInterface');
$dumper
->expects($this->once())
->method('dump');
$writer = new TranslationWriter();
$writer->addDumper('test', $dumper);
$writer->writeTranslations(new MessageCatalogue(array()), 'test');
}
public function testDisableBackup()
{
$nonBackupDumper = new NonBackupDumper();
$backupDumper = new BackupDumper();
$writer = new TranslationWriter();
$writer->addDumper('non_backup', $nonBackupDumper);
$writer->addDumper('backup', $backupDumper);
$writer->disableBackup();
$this->assertFalse($backupDumper->backup, 'backup can be disabled if setBackup() method does exist');
}
}
class NonBackupDumper implements DumperInterface
{
public function dump(MessageCatalogue $messages, $options = array())
{
}
}
class BackupDumper implements DumperInterface
{
public $backup = true;
public function dump(MessageCatalogue $messages, $options = array())
{
}
public function setBackup($backup)
{
$this->backup = $backup;
}
}

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