[Translation] added the component
This commit is contained in:
parent
6317ddfbe1
commit
a7537906b4
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace Symfony\Component\Translation\Loader;
|
||||
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony framework.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* ArrayLoader loads translations from a PHP array.
|
||||
*
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class ArrayLoader implements LoaderInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function load($resource, $locale, $domain = 'messages')
|
||||
{
|
||||
$catalogue = new MessageCatalogue($locale);
|
||||
$catalogue->addMessages($resource, $domain);
|
||||
|
||||
return $catalogue;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace Symfony\Component\Translation\Loader;
|
||||
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony framework.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* LoaderInterface is the interface implemented by all translation loaders.
|
||||
*
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
interface LoaderInterface
|
||||
{
|
||||
/**
|
||||
* Loads a locale.
|
||||
*
|
||||
* @param mixed $resource A resource
|
||||
* @param string $locale A locale
|
||||
* @param string $domain The domain
|
||||
*
|
||||
* @return MessageCatalogue A MessageCatalogue instance
|
||||
*/
|
||||
function load($resource, $locale, $domain = 'messages');
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace Symfony\Component\Translation\Loader;
|
||||
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
use Symfony\Component\Translation\Resource\FileResource;
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony framework.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* PhpFileLoader loads translations from PHP files returning an array of translations.
|
||||
*
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class PhpFileLoader implements LoaderInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function load($resource, $locale, $domain = 'messages')
|
||||
{
|
||||
$catalogue = new MessageCatalogue($locale);
|
||||
$catalogue->addMessages(require($resource), $domain);
|
||||
$catalogue->addResource(new FileResource($resource));
|
||||
|
||||
return $catalogue;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
|
||||
namespace Symfony\Component\Translation\Loader;
|
||||
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
use Symfony\Component\Translation\Resource\FileResource;
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony framework.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* XliffFileLoader loads translations from XLIFF files.
|
||||
*
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class XliffFileLoader implements LoaderInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function load($resource, $locale, $domain = 'messages')
|
||||
{
|
||||
$xml = $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) {
|
||||
$catalogue->setMessage((string) $translation->source, (string) $translation->target, $domain);
|
||||
}
|
||||
$catalogue->addResource(new FileResource($resource));
|
||||
|
||||
return $catalogue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and parses the given file into a SimpleXMLElement
|
||||
*
|
||||
* @param string $file
|
||||
* @return SimpleXMLElement
|
||||
*/
|
||||
protected function parseFile($file)
|
||||
{
|
||||
$dom = new \DOMDocument();
|
||||
$current = libxml_use_internal_errors(true);
|
||||
if (!@$dom->load($file, LIBXML_COMPACT)) {
|
||||
throw new \Exception(implode("\n", $this->getXmlErrors()));
|
||||
}
|
||||
|
||||
$parts = explode('/', str_replace('\\', '/', __DIR__).'/schema/dic/xliff-core/xml.xsd');
|
||||
$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 \Exception(implode("\n", $this->getXmlErrors()));
|
||||
}
|
||||
$dom->validateOnParse = true;
|
||||
$dom->normalizeDocument();
|
||||
libxml_use_internal_errors($current);
|
||||
|
||||
return simplexml_import_dom($dom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the XML errors of the internal XML parser
|
||||
*
|
||||
* @return array An array of errors
|
||||
*/
|
||||
protected function getXmlErrors()
|
||||
{
|
||||
$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 ? $error->file : 'n/a',
|
||||
$error->line,
|
||||
$error->column
|
||||
);
|
||||
}
|
||||
|
||||
libxml_clear_errors();
|
||||
libxml_use_internal_errors(false);
|
||||
|
||||
return $errors;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,309 @@
|
|||
<?xml version='1.0'?>
|
||||
<?xml-stylesheet href="../2008/09/xsd.xsl" type="text/xsl"?>
|
||||
<xs:schema targetNamespace="http://www.w3.org/XML/1998/namespace"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns ="http://www.w3.org/1999/xhtml"
|
||||
xml:lang="en">
|
||||
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
<div>
|
||||
<h1>About the XML namespace</h1>
|
||||
|
||||
<div class="bodytext">
|
||||
<p>
|
||||
|
||||
This schema document describes the XML namespace, in a form
|
||||
suitable for import by other schema documents.
|
||||
</p>
|
||||
<p>
|
||||
See <a href="http://www.w3.org/XML/1998/namespace.html">
|
||||
http://www.w3.org/XML/1998/namespace.html</a> and
|
||||
<a href="http://www.w3.org/TR/REC-xml">
|
||||
http://www.w3.org/TR/REC-xml</a> for information
|
||||
about this namespace.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Note that local names in this namespace are intended to be
|
||||
defined only by the World Wide Web Consortium or its subgroups.
|
||||
The names currently defined in this namespace are listed below.
|
||||
They should not be used with conflicting semantics by any Working
|
||||
Group, specification, or document instance.
|
||||
</p>
|
||||
<p>
|
||||
See further below in this document for more information about <a
|
||||
href="#usage">how to refer to this schema document from your own
|
||||
XSD schema documents</a> and about <a href="#nsversioning">the
|
||||
namespace-versioning policy governing this schema document</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
|
||||
<xs:attribute name="lang">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
<div>
|
||||
|
||||
<h3>lang (as an attribute name)</h3>
|
||||
<p>
|
||||
|
||||
denotes an attribute whose value
|
||||
is a language code for the natural language of the content of
|
||||
any element; its value is inherited. This name is reserved
|
||||
by virtue of its definition in the XML specification.</p>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<h4>Notes</h4>
|
||||
<p>
|
||||
Attempting to install the relevant ISO 2- and 3-letter
|
||||
codes as the enumerated possible values is probably never
|
||||
going to be a realistic possibility.
|
||||
</p>
|
||||
<p>
|
||||
|
||||
See BCP 47 at <a href="http://www.rfc-editor.org/rfc/bcp/bcp47.txt">
|
||||
http://www.rfc-editor.org/rfc/bcp/bcp47.txt</a>
|
||||
and the IANA language subtag registry at
|
||||
<a href="http://www.iana.org/assignments/language-subtag-registry">
|
||||
http://www.iana.org/assignments/language-subtag-registry</a>
|
||||
for further information.
|
||||
</p>
|
||||
<p>
|
||||
|
||||
The union allows for the 'un-declaration' of xml:lang with
|
||||
the empty string.
|
||||
</p>
|
||||
</div>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:simpleType>
|
||||
<xs:union memberTypes="xs:language">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value=""/>
|
||||
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:union>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
|
||||
<xs:attribute name="space">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
|
||||
<div>
|
||||
|
||||
<h3>space (as an attribute name)</h3>
|
||||
<p>
|
||||
denotes an attribute whose
|
||||
value is a keyword indicating what whitespace processing
|
||||
discipline is intended for the content of the element; its
|
||||
value is inherited. This name is reserved by virtue of its
|
||||
definition in the XML specification.</p>
|
||||
|
||||
</div>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:simpleType>
|
||||
|
||||
<xs:restriction base="xs:NCName">
|
||||
<xs:enumeration value="default"/>
|
||||
<xs:enumeration value="preserve"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
|
||||
<xs:attribute name="base" type="xs:anyURI"> <xs:annotation>
|
||||
<xs:documentation>
|
||||
|
||||
<div>
|
||||
|
||||
<h3>base (as an attribute name)</h3>
|
||||
<p>
|
||||
denotes an attribute whose value
|
||||
provides a URI to be used as the base for interpreting any
|
||||
relative URIs in the scope of the element on which it
|
||||
appears; its value is inherited. This name is reserved
|
||||
by virtue of its definition in the XML Base specification.</p>
|
||||
|
||||
<p>
|
||||
See <a
|
||||
href="http://www.w3.org/TR/xmlbase/">http://www.w3.org/TR/xmlbase/</a>
|
||||
for information about this attribute.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
|
||||
<xs:attribute name="id" type="xs:ID">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
<div>
|
||||
|
||||
<h3>id (as an attribute name)</h3>
|
||||
<p>
|
||||
|
||||
denotes an attribute whose value
|
||||
should be interpreted as if declared to be of type ID.
|
||||
This name is reserved by virtue of its definition in the
|
||||
xml:id specification.</p>
|
||||
|
||||
<p>
|
||||
See <a
|
||||
href="http://www.w3.org/TR/xml-id/">http://www.w3.org/TR/xml-id/</a>
|
||||
for information about this attribute.
|
||||
</p>
|
||||
</div>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
|
||||
</xs:attribute>
|
||||
|
||||
<xs:attributeGroup name="specialAttrs">
|
||||
<xs:attribute ref="xml:base"/>
|
||||
<xs:attribute ref="xml:lang"/>
|
||||
<xs:attribute ref="xml:space"/>
|
||||
<xs:attribute ref="xml:id"/>
|
||||
</xs:attributeGroup>
|
||||
|
||||
<xs:annotation>
|
||||
|
||||
<xs:documentation>
|
||||
<div>
|
||||
|
||||
<h3>Father (in any context at all)</h3>
|
||||
|
||||
<div class="bodytext">
|
||||
<p>
|
||||
denotes Jon Bosak, the chair of
|
||||
the original XML Working Group. This name is reserved by
|
||||
the following decision of the W3C XML Plenary and
|
||||
XML Coordination groups:
|
||||
</p>
|
||||
<blockquote>
|
||||
<p>
|
||||
|
||||
In appreciation for his vision, leadership and
|
||||
dedication the W3C XML Plenary on this 10th day of
|
||||
February, 2000, reserves for Jon Bosak in perpetuity
|
||||
the XML name "xml:Father".
|
||||
</p>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
|
||||
<div xml:id="usage" id="usage">
|
||||
<h2><a name="usage">About this schema document</a></h2>
|
||||
|
||||
<div class="bodytext">
|
||||
<p>
|
||||
This schema defines attributes and an attribute group suitable
|
||||
for use by schemas wishing to allow <code>xml:base</code>,
|
||||
<code>xml:lang</code>, <code>xml:space</code> or
|
||||
<code>xml:id</code> attributes on elements they define.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To enable this, such a schema must import this schema for
|
||||
the XML namespace, e.g. as follows:
|
||||
</p>
|
||||
<pre>
|
||||
<schema . . .>
|
||||
. . .
|
||||
<import namespace="http://www.w3.org/XML/1998/namespace"
|
||||
schemaLocation="http://www.w3.org/2001/xml.xsd"/>
|
||||
</pre>
|
||||
<p>
|
||||
or
|
||||
</p>
|
||||
<pre>
|
||||
|
||||
<import namespace="http://www.w3.org/XML/1998/namespace"
|
||||
schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
|
||||
</pre>
|
||||
<p>
|
||||
Subsequently, qualified reference to any of the attributes or the
|
||||
group defined below will have the desired effect, e.g.
|
||||
</p>
|
||||
<pre>
|
||||
<type . . .>
|
||||
. . .
|
||||
<attributeGroup ref="xml:specialAttrs"/>
|
||||
</pre>
|
||||
<p>
|
||||
will define a type which will schema-validate an instance element
|
||||
with any of those attributes.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
<div id="nsversioning" xml:id="nsversioning">
|
||||
<h2><a name="nsversioning">Versioning policy for this schema document</a></h2>
|
||||
|
||||
<div class="bodytext">
|
||||
<p>
|
||||
In keeping with the XML Schema WG's standard versioning
|
||||
policy, this schema document will persist at
|
||||
<a href="http://www.w3.org/2009/01/xml.xsd">
|
||||
http://www.w3.org/2009/01/xml.xsd</a>.
|
||||
</p>
|
||||
<p>
|
||||
At the date of issue it can also be found at
|
||||
<a href="http://www.w3.org/2001/xml.xsd">
|
||||
http://www.w3.org/2001/xml.xsd</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The schema document at that URI may however change in the future,
|
||||
in order to remain compatible with the latest version of XML
|
||||
Schema itself, or with the XML namespace itself. In other words,
|
||||
if the XML Schema or XML namespaces change, the version of this
|
||||
document at <a href="http://www.w3.org/2001/xml.xsd">
|
||||
http://www.w3.org/2001/xml.xsd
|
||||
</a>
|
||||
will change accordingly; the version at
|
||||
<a href="http://www.w3.org/2009/01/xml.xsd">
|
||||
http://www.w3.org/2009/01/xml.xsd
|
||||
</a>
|
||||
will not change.
|
||||
</p>
|
||||
<p>
|
||||
|
||||
Previous dated (and unchanging) versions of this schema
|
||||
document are at:
|
||||
</p>
|
||||
<ul>
|
||||
<li><a href="http://www.w3.org/2009/01/xml.xsd">
|
||||
http://www.w3.org/2009/01/xml.xsd</a></li>
|
||||
<li><a href="http://www.w3.org/2007/08/xml.xsd">
|
||||
http://www.w3.org/2007/08/xml.xsd</a></li>
|
||||
<li><a href="http://www.w3.org/2004/10/xml.xsd">
|
||||
|
||||
http://www.w3.org/2004/10/xml.xsd</a></li>
|
||||
<li><a href="http://www.w3.org/2001/03/xml.xsd">
|
||||
http://www.w3.org/2001/03/xml.xsd</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
|
||||
</xs:schema>
|
|
@ -0,0 +1,145 @@
|
|||
<?php
|
||||
|
||||
namespace Symfony\Component\Translation;
|
||||
|
||||
use Symfony\Component\Translation\Resource\ResourceInterface;
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony framework.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* MessageCatalogue.
|
||||
*
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class MessageCatalogue implements MessageCatalogueInterface
|
||||
{
|
||||
protected $messages = array();
|
||||
protected $locale;
|
||||
protected $resources;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $locale The locale
|
||||
* @param array $messages An array of messages classified by domain
|
||||
*/
|
||||
public function __construct($locale, array $messages = array())
|
||||
{
|
||||
$this->locale = $locale;
|
||||
$this->messages = $messages;
|
||||
$this->resources = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLocale()
|
||||
{
|
||||
return $this->locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDomains()
|
||||
{
|
||||
return array_keys($this->messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMessages($domain = null)
|
||||
{
|
||||
if (null === $domain) {
|
||||
return $this->messages;
|
||||
}
|
||||
|
||||
return isset($this->messages[$domain]) ? $this->messages[$domain] : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setMessage($id, $translation, $domain = 'messages')
|
||||
{
|
||||
$this->addMessages(array($id => $translation), $domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasMessage($id, $domain = 'messages')
|
||||
{
|
||||
return isset($this->messages[$domain][$id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMessage($id, $domain = 'messages')
|
||||
{
|
||||
return isset($this->messages[$domain][$id]) ? $this->messages[$domain][$id] : $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setMessages($messages, $domain = 'messages')
|
||||
{
|
||||
if (isset($this->messages[$domain])) {
|
||||
$this->messages[$domain] = array();
|
||||
}
|
||||
|
||||
$this->addMessages($messages, $domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addMessages($messages, $domain = 'messages')
|
||||
{
|
||||
if (!isset($this->messages[$domain])) {
|
||||
$this->messages[$domain] = $messages;
|
||||
} else {
|
||||
$this->messages[$domain] = array_replace($this->messages[$domain], $messages);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addCatalogue(MessageCatalogueInterface $catalogue)
|
||||
{
|
||||
foreach ($catalogue->getMessages() as $domain => $messages) {
|
||||
$this->addMessages($messages, $domain);
|
||||
}
|
||||
|
||||
foreach ($catalogue->getResources() as $resource) {
|
||||
$this->addResource($resource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getResources()
|
||||
{
|
||||
return array_unique($this->resources);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addResource(ResourceInterface $resource)
|
||||
{
|
||||
$this->resources[] = $resource;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
<?php
|
||||
|
||||
namespace Symfony\Component\Translation;
|
||||
|
||||
use Symfony\Component\Translation\Resource\ResourceInterface;
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony framework.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* MessageCatalogueInterface.
|
||||
*
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
interface MessageCatalogueInterface
|
||||
{
|
||||
/**
|
||||
* Gets the catalogue locale.
|
||||
*
|
||||
* @return string The locale
|
||||
*/
|
||||
function getLocale();
|
||||
|
||||
/**
|
||||
* Gets the domains.
|
||||
*
|
||||
* @param array An array of domains
|
||||
*/
|
||||
function getDomains();
|
||||
|
||||
/**
|
||||
* Gets the messages within a given domain.
|
||||
*
|
||||
* If $domain is null, it returns all messages.
|
||||
*
|
||||
* @param string $domain The domain name
|
||||
*
|
||||
* @param array An array of messages
|
||||
*/
|
||||
function getMessages($domain = null);
|
||||
|
||||
/**
|
||||
* Sets a message translation.
|
||||
*
|
||||
* @param string $id The message id
|
||||
* @param string $translation The messages translation
|
||||
* @param string $domain The domain name
|
||||
*/
|
||||
function setMessage($id, $translation, $domain = 'messages');
|
||||
|
||||
/**
|
||||
* Checks if a message has a translation.
|
||||
*
|
||||
* @param string $id The message id
|
||||
* @param string $domain The domain name
|
||||
*
|
||||
* @return Boolean true if the message has a translation, false otherwise
|
||||
*/
|
||||
function hasMessage($id, $domain = 'messages');
|
||||
|
||||
/**
|
||||
* Gets a message translation.
|
||||
*
|
||||
* @param string $id The message id
|
||||
* @param string $domain The domain name
|
||||
*
|
||||
* @return string The message translation
|
||||
*/
|
||||
function getMessage($id, $domain = 'messages');
|
||||
|
||||
/**
|
||||
* Sets translations for a given domain.
|
||||
*
|
||||
* @param string $messages An array of translations
|
||||
* @param string $domain The domain name
|
||||
*/
|
||||
function setMessages($messages, $domain = 'messages');
|
||||
|
||||
/**
|
||||
* Adds translations for a given domain.
|
||||
*
|
||||
* @param string $messages An array of translations
|
||||
* @param string $domain The domain name
|
||||
*/
|
||||
function addMessages($messages, $domain = 'messages');
|
||||
|
||||
/**
|
||||
* Merges translations from the given Catalogue into the current one.
|
||||
*
|
||||
* @param MessageCatalogueInterface $catalogue A MessageCatalogueInterface instance
|
||||
*/
|
||||
function addCatalogue(MessageCatalogueInterface $catalogue);
|
||||
|
||||
/**
|
||||
* Returns an array of resources loaded to build this collection.
|
||||
*
|
||||
* @return ResourceInterface[] An array of resources
|
||||
*/
|
||||
function getResources();
|
||||
|
||||
/**
|
||||
* Adds a resource for this collection.
|
||||
*
|
||||
* @param ResourceInterface $resource A resource instance
|
||||
*/
|
||||
function addResource(ResourceInterface $resource);
|
||||
}
|
|
@ -0,0 +1,214 @@
|
|||
<?php
|
||||
|
||||
namespace Symfony\Component\Translation;
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony framework.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns the plural rules for a given locale.
|
||||
*
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class PluralizationRules
|
||||
{
|
||||
static protected $rules = array();
|
||||
|
||||
/**
|
||||
* Returns the plural position to use for the given locale and number.
|
||||
*
|
||||
* @param integer $number The number
|
||||
* @param string $locale The locale
|
||||
*
|
||||
* @return integer The plural position
|
||||
*/
|
||||
static public function get($number, $locale)
|
||||
{
|
||||
if ($locale == "pt_BR") {
|
||||
// temporary set a locale for brasilian
|
||||
$locale = "xbr";
|
||||
}
|
||||
|
||||
if (strlen($locale) > 3) {
|
||||
$locale = substr($locale, 0, -strlen(strrchr($locale, '_')));
|
||||
}
|
||||
|
||||
if (isset(self::$rules[$locale])) {
|
||||
$return = call_user_func(self::$rules[$locale], $number);
|
||||
|
||||
if (!is_int($return) || $return < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The plural rules are derived from code of the Zend Framework (2010-09-25),
|
||||
* which is subject to the new BSD license (http://framework.zend.com/license/new-bsd).
|
||||
* Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
*/
|
||||
switch ($locale) {
|
||||
case 'bo':
|
||||
case 'dz':
|
||||
case 'id':
|
||||
case 'ja':
|
||||
case 'jv':
|
||||
case 'ka':
|
||||
case 'km':
|
||||
case 'kn':
|
||||
case 'ko':
|
||||
case 'ms':
|
||||
case 'th':
|
||||
case 'tr':
|
||||
case 'vi':
|
||||
case 'zh':
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case 'af':
|
||||
case 'az':
|
||||
case 'bn':
|
||||
case 'bg':
|
||||
case 'ca':
|
||||
case 'da':
|
||||
case 'de':
|
||||
case 'el':
|
||||
case 'en':
|
||||
case 'eo':
|
||||
case 'es':
|
||||
case 'et':
|
||||
case 'eu':
|
||||
case 'fa':
|
||||
case 'fi':
|
||||
case 'fo':
|
||||
case 'fur':
|
||||
case 'fy':
|
||||
case 'gl':
|
||||
case 'gu':
|
||||
case 'ha':
|
||||
case 'he':
|
||||
case 'hu':
|
||||
case 'is':
|
||||
case 'it':
|
||||
case 'ku':
|
||||
case 'lb':
|
||||
case 'ml':
|
||||
case 'mn':
|
||||
case 'mr':
|
||||
case 'nah':
|
||||
case 'nb':
|
||||
case 'ne':
|
||||
case 'nl':
|
||||
case 'nn':
|
||||
case 'no':
|
||||
case 'om':
|
||||
case 'or':
|
||||
case 'pa':
|
||||
case 'pap':
|
||||
case 'ps':
|
||||
case 'pt':
|
||||
case 'so':
|
||||
case 'sq':
|
||||
case 'sv':
|
||||
case 'sw':
|
||||
case 'ta':
|
||||
case 'te':
|
||||
case 'tk':
|
||||
case 'ur':
|
||||
case 'zu':
|
||||
return ($number == 1) ? 0 : 1;
|
||||
|
||||
case 'am':
|
||||
case 'bh':
|
||||
case 'fil':
|
||||
case 'fr':
|
||||
case 'gun':
|
||||
case 'hi':
|
||||
case 'ln':
|
||||
case 'mg':
|
||||
case 'nso':
|
||||
case 'xbr':
|
||||
case 'ti':
|
||||
case 'wa':
|
||||
return (($number == 0) || ($number == 1)) ? 0 : 1;
|
||||
|
||||
case 'be':
|
||||
case 'bs':
|
||||
case 'hr':
|
||||
case 'ru':
|
||||
case 'sr':
|
||||
case 'uk':
|
||||
return (($number % 10 == 1) && ($number % 100 != 11)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
|
||||
|
||||
case 'cs':
|
||||
case 'sk':
|
||||
return ($number == 1) ? 0 : ((($number >= 2) && ($number <= 4)) ? 1 : 2);
|
||||
|
||||
case 'ga':
|
||||
return ($number == 1) ? 0 : (($number == 2) ? 1 : 2);
|
||||
|
||||
case 'lt':
|
||||
return (($number % 10 == 1) && ($number % 100 != 11)) ? 0 : ((($number % 10 >= 2) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
|
||||
|
||||
case 'sl':
|
||||
return ($number % 100 == 1) ? 0 : (($number % 100 == 2) ? 1 : ((($number % 100 == 3) || ($number % 100 == 4)) ? 2 : 3));
|
||||
|
||||
case 'mk':
|
||||
return ($number % 10 == 1) ? 0 : 1;
|
||||
|
||||
case 'mt':
|
||||
return ($number == 1) ? 0 : ((($number == 0) || (($number % 100 > 1) && ($number % 100 < 11))) ? 1 : ((($number % 100 > 10) && ($number % 100 < 20)) ? 2 : 3));
|
||||
|
||||
case 'lv':
|
||||
return ($number == 0) ? 0 : ((($number % 10 == 1) && ($number % 100 != 11)) ? 1 : 2);
|
||||
|
||||
case 'pl':
|
||||
return ($number == 1) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 12) || ($number % 100 > 14))) ? 1 : 2);
|
||||
|
||||
case 'cy':
|
||||
return ($number == 1) ? 0 : (($number == 2) ? 1 : ((($number == 8) || ($number == 11)) ? 2 : 3));
|
||||
|
||||
case 'ro':
|
||||
return ($number == 1) ? 0 : ((($number == 0) || (($number % 100 > 0) && ($number % 100 < 20))) ? 1 : 2);
|
||||
|
||||
case 'ar':
|
||||
return ($number == 0) ? 0 : (($number == 1) ? 1 : (($number == 2) ? 2 : ((($number >= 3) && ($number <= 10)) ? 3 : ((($number >= 11) && ($number <= 99)) ? 4 : 5))));
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the default plural rule for a given locale.
|
||||
*
|
||||
* @param string $rule A PHP callable
|
||||
* @param string $locale The locale
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
static public function set($rule, $locale)
|
||||
{
|
||||
if ($locale == "pt_BR") {
|
||||
// temporary set a locale for brasilian
|
||||
$locale = "xbr";
|
||||
}
|
||||
|
||||
if (strlen($locale) > 3) {
|
||||
$locale = substr($locale, 0, -strlen(strrchr($locale, '_')));
|
||||
}
|
||||
|
||||
if (!is_callable($rule)) {
|
||||
throw new Exception('The given rule can not be called');
|
||||
}
|
||||
|
||||
self::$rules[$locale] = $rule;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
<?php
|
||||
|
||||
namespace Symfony\Component\Translation;
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony framework.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Range tests if a given number belongs to a given range.
|
||||
*
|
||||
* A range can represent a finite set of numbers:
|
||||
*
|
||||
* {1,2,3,4}
|
||||
*
|
||||
* A range can represent numbers between two numbers:
|
||||
*
|
||||
* [1, +Inf]
|
||||
* ]-1,2[
|
||||
*
|
||||
* The left delimiter can be [ (inclusive) or ] (exclusive).
|
||||
* The right delimiter can be [ (exclusive) or ] (inclusive).
|
||||
* Beside numbers, you can use -Inf and +Inf for the infinite.
|
||||
*
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Range
|
||||
{
|
||||
/**
|
||||
* Tests if the given number is in the range.
|
||||
*
|
||||
* @param integer $number A number
|
||||
* @param string $range A range of numbers
|
||||
*/
|
||||
static public function test($number, $range)
|
||||
{
|
||||
$range = trim($range);
|
||||
|
||||
if (!preg_match('/^'.self::getRangeRegexp().'$/x', $range, $matches)) {
|
||||
throw new \InvalidArgumentException(sprintf('"%s" is not a valid range expression.', $range));
|
||||
}
|
||||
|
||||
if ($matches[1]) {
|
||||
foreach (explode(',', $matches[2]) as $n) {
|
||||
if ($number == $n) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$leftNumber = self::convertNumber($matches['left']);
|
||||
$rightNumber = self::convertNumber($matches['right']);
|
||||
|
||||
return
|
||||
('[' === $matches['left_delimiter'] ? $number >= $leftNumber : $number > $leftNumber)
|
||||
&&
|
||||
(']' === $matches['right_delimiter'] ? $number <= $rightNumber : $number < $rightNumber)
|
||||
;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Regexp that matches valid ranges.
|
||||
*
|
||||
* @return string A Regexp (without the delimiters)
|
||||
*/
|
||||
static public function getRangeRegexp()
|
||||
{
|
||||
return <<<EOF
|
||||
({\s*
|
||||
(\-?\d+[\s*,\s*\-?\d+]*)
|
||||
\s*})
|
||||
|
||||
|
|
||||
|
||||
(?<left_delimiter>[\[\]])
|
||||
\s*
|
||||
(?<left>-Inf|\-?\d+)
|
||||
\s*,\s*
|
||||
(?<right>\+?Inf|\-?\d+)
|
||||
\s*
|
||||
(?<right_delimiter>[\[\]])
|
||||
EOF;
|
||||
}
|
||||
|
||||
static protected function convertNumber($number)
|
||||
{
|
||||
if ('-Inf' === $number) {
|
||||
return log(0);
|
||||
} elseif ('+Inf' === $number || 'Inf' === $number) {
|
||||
return -log(0);
|
||||
} else {
|
||||
return (int) $number;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace Symfony\Component\Translation\Resource;
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony framework.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* FileResource represents a resource stored on the filesystem.
|
||||
*
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class FileResource implements ResourceInterface
|
||||
{
|
||||
protected $resource;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $resource The file path to the resource
|
||||
*/
|
||||
public function __construct($resource)
|
||||
{
|
||||
$this->resource = realpath($resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the Resource.
|
||||
*
|
||||
* @return string A string representation of the Resource
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return (string) $this->resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resource tied to this Resource.
|
||||
*
|
||||
* @return mixed The resource
|
||||
*/
|
||||
public function getResource()
|
||||
{
|
||||
return $this->resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the resource has not been updated since the given timestamp.
|
||||
*
|
||||
* @param timestamp $timestamp The last time the resource was loaded
|
||||
*
|
||||
* @return Boolean true if the resource has not been updated, false otherwise
|
||||
*/
|
||||
public function isUptodate($timestamp)
|
||||
{
|
||||
if (!file_exists($this->resource)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return filemtime($this->resource) < $timestamp;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace Symfony\Component\Translation\Resource;
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony framework.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* ResourceInterface is the interface that must be implemented by all Resource classes.
|
||||
*
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
interface ResourceInterface
|
||||
{
|
||||
/**
|
||||
* Returns a string representation of the Resource.
|
||||
*
|
||||
* @return string A string representation of the Resource
|
||||
*/
|
||||
function __toString();
|
||||
|
||||
/**
|
||||
* Returns true if the resource has not been updated since the given timestamp.
|
||||
*
|
||||
* @param int $timestamp The last time the resource was loaded
|
||||
*
|
||||
* @return Boolean true if the resource has not been updated, false otherwise
|
||||
*/
|
||||
function isUptodate($timestamp);
|
||||
|
||||
/**
|
||||
* Returns the resource tied to this Resource.
|
||||
*
|
||||
* @return mixed The resource
|
||||
*/
|
||||
function getResource();
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
<?php
|
||||
|
||||
namespace Symfony\Component\Translation;
|
||||
|
||||
use Symfony\Component\Translation\Loader\LoaderInterface;
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony framework.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Translator.
|
||||
*
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Translator implements TranslatorInterface
|
||||
{
|
||||
protected $catalogues;
|
||||
protected $locale;
|
||||
protected $fallbackLocale;
|
||||
protected $loaders;
|
||||
protected $resources;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $locale The locale
|
||||
*/
|
||||
public function __construct($locale = null)
|
||||
{
|
||||
$this->locale = $locale;
|
||||
$this->loaders = array();
|
||||
$this->resources = array();
|
||||
$this->catalogues = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a Loader.
|
||||
*
|
||||
* @param string $format The name of the loader (@see addResource())
|
||||
* @param LoaderInterface $loader A LoaderInterface instance
|
||||
*/
|
||||
public function addLoader($format, LoaderInterface $loader)
|
||||
{
|
||||
$this->loaders[$format] = $loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a Resource.
|
||||
*
|
||||
* @param string $format The name of the loader (@see addLoader())
|
||||
* @param mixed $resource The resource name
|
||||
* @param string $locale The locale
|
||||
* @param string $domain The domain
|
||||
*/
|
||||
public function addResource($format, $resource, $locale, $domain = 'messages')
|
||||
{
|
||||
if (!isset($this->resources[$locale])) {
|
||||
$this->resources[$locale] = array();
|
||||
}
|
||||
|
||||
$this->resources[$locale][] = array($format, $resource, $domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setLocale($locale)
|
||||
{
|
||||
$this->locale = $locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the fallback locale.
|
||||
*
|
||||
* @param string $locale The fallback locale
|
||||
*/
|
||||
public function setFallbackLocale($locale)
|
||||
{
|
||||
if (null !== $this->fallbackLocale) {
|
||||
// needed as the fallback locale is used to fill-in non-yet translated messages
|
||||
$this->catalogues = array();
|
||||
}
|
||||
|
||||
$this->fallbackLocale = $locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function trans($id, array $parameters = array(), $domain = 'messages', $locale = null)
|
||||
{
|
||||
if (!isset($locale)) {
|
||||
$locale = $this->locale;
|
||||
}
|
||||
|
||||
if (!isset($this->catalogues[$locale])) {
|
||||
$this->loadCatalogue($locale);
|
||||
}
|
||||
|
||||
return strtr($this->catalogues[$locale]->getMessage($id, $domain), $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transChoice($id, $number, array $parameters = array(), $domain = 'messages', $locale = null)
|
||||
{
|
||||
if (!isset($locale)) {
|
||||
$locale = $this->locale;
|
||||
}
|
||||
|
||||
if (!isset($this->catalogues[$locale])) {
|
||||
$this->loadCatalogue($locale);
|
||||
}
|
||||
|
||||
return strtr($this->chooseMessage($this->catalogues[$locale]->getMessage($id, $domain), (int) $number, $locale), $parameters);
|
||||
}
|
||||
|
||||
protected function chooseMessage($message, $number, $locale)
|
||||
{
|
||||
$parts = explode('|', $message);
|
||||
$explicitRules = array();
|
||||
$standardRules = array();
|
||||
foreach ($parts as $part) {
|
||||
$part = trim($part);
|
||||
|
||||
if (preg_match('/^(?<range>'.Range::getRangeRegexp().')\s+(?<message>.+?)$/x', $part, $matches)) {
|
||||
$explicitRules[$matches['range']] = $matches['message'];
|
||||
} elseif (preg_match('/^\w+\: +(.+)$/', $part, $matches)) {
|
||||
$standardRules[] = $matches[1];
|
||||
} else {
|
||||
$standardRules[] = $part;
|
||||
}
|
||||
}
|
||||
|
||||
// try to match an explicit rule, then fallback to the standard ones
|
||||
foreach ($explicitRules as $range => $m) {
|
||||
if (Range::test($number, $range)) {
|
||||
return $m;
|
||||
}
|
||||
}
|
||||
|
||||
$position = PluralizationRules::get($number, $locale);
|
||||
if (!isset($standardRules[$position])) {
|
||||
throw new \InvalidArgumentException('Unable to choose a translation.');
|
||||
}
|
||||
|
||||
return $standardRules[$position];
|
||||
}
|
||||
|
||||
protected function loadCatalogue($locale)
|
||||
{
|
||||
if (isset($this->catalogues[$locale])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->catalogues[$locale] = new MessageCatalogue($locale);
|
||||
if (!isset($this->resources[$locale])) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->resources[$locale] as $resource) {
|
||||
if (!isset($this->loaders[$resource[0]])) {
|
||||
throw new \RuntimeException(sprintf('The "%s" translation loader is not registered.', $resource[0]));
|
||||
}
|
||||
$this->catalogues[$locale]->addCatalogue($this->loaders[$resource[0]]->load($resource[1], $locale, $resource[2]));
|
||||
}
|
||||
|
||||
$this->optimizeCatalogue($locale);
|
||||
}
|
||||
|
||||
protected function optimizeCatalogue($locale)
|
||||
{
|
||||
if (strlen($locale) > 3) {
|
||||
$fallback = substr($locale, 0, -strlen(strrchr($locale, '_')));
|
||||
} else {
|
||||
$fallback = $this->fallbackLocale;
|
||||
}
|
||||
|
||||
if (!isset($this->catalogues[$fallback])) {
|
||||
$this->loadCatalogue($fallback);
|
||||
}
|
||||
|
||||
foreach ($this->catalogues[$fallback]->getResources() as $resource) {
|
||||
$this->catalogues[$locale]->addResource($resource);
|
||||
}
|
||||
foreach ($this->catalogues[$fallback]->getDomains() as $domain) {
|
||||
foreach ($this->catalogues[$fallback]->getMessages($domain) as $id => $translation) {
|
||||
if (false === $this->catalogues[$locale]->hasMessage($id, $domain)) {
|
||||
$this->catalogues[$locale]->setMessage($id, $translation, $domain);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
namespace Symfony\Component\Translation;
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony framework.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* TranslatorInterface.
|
||||
*
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
interface TranslatorInterface
|
||||
{
|
||||
/**
|
||||
* Translates the given message.
|
||||
*
|
||||
* @param string $id The message id
|
||||
* @param array $parameters An array of parameters for the message
|
||||
* @param string $domain The domain for the message
|
||||
* @param string $locale The locale
|
||||
*
|
||||
* @return string The translated string
|
||||
*/
|
||||
function trans($id, array $parameters = array(), $domain = null, $locale = null);
|
||||
|
||||
/**
|
||||
* Translates the given choice message by choosing a translation according to a number.
|
||||
*
|
||||
* @param string $id The message id
|
||||
* @param integer $number The number to use to find the indice of the message
|
||||
* @param array $parameters An array of parameters for the message
|
||||
* @param string $domain The domain for the message
|
||||
* @param string $locale The locale
|
||||
*
|
||||
* @return string The translated string
|
||||
*/
|
||||
function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null);
|
||||
|
||||
/**
|
||||
* Sets the current locale.
|
||||
*
|
||||
* @param string $locale The locale
|
||||
*/
|
||||
function setLocale($locale);
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Tests\Component\Translation;
|
||||
|
||||
use Symfony\Component\Translation\Range;
|
||||
|
||||
class RangeTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider getTests
|
||||
*/
|
||||
public function testTest($expected, $number, $range)
|
||||
{
|
||||
$this->assertEquals($expected, Range::test($number, $range));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testTestException()
|
||||
{
|
||||
Range::test(1, 'foobar');
|
||||
}
|
||||
|
||||
public function getTests()
|
||||
{
|
||||
return array(
|
||||
array(true, 3, '{1,2, 3 ,4}'),
|
||||
array(false, 10, '{1,2, 3 ,4}'),
|
||||
array(false, 3, '[1,2]'),
|
||||
array(true, 1, '[1,2]'),
|
||||
array(true, 2, '[1,2]'),
|
||||
array(false, 1, ']1,2['),
|
||||
array(false, 2, ']1,2['),
|
||||
array(true, log(0), '[-Inf,2['),
|
||||
array(true, -log(0), '[-2,+Inf]'),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Tests\Component\Translation;
|
||||
|
||||
use Symfony\Component\Translation\Translator;
|
||||
use Symfony\Component\Translation\Loader\ArrayLoader;
|
||||
|
||||
class TranslatorTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider getTransTests
|
||||
*/
|
||||
public function testTrans($expected, $id, $translation, $parameters, $locale, $domain)
|
||||
{
|
||||
$translator = new Translator();
|
||||
$translator->addLoader('array', new ArrayLoader());
|
||||
$translator->addResource('array', array($id => $translation), $locale, $domain);
|
||||
|
||||
$this->assertEquals($expected, $translator->trans($id, $parameters, $domain, $locale));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getTransChoiceTests
|
||||
*/
|
||||
public function testTransChoice($expected, $id, $translation, $number, $parameters, $locale, $domain)
|
||||
{
|
||||
$translator = new Translator();
|
||||
$translator->addLoader('array', new ArrayLoader());
|
||||
$translator->addResource('array', array($id => $translation), $locale, $domain);
|
||||
|
||||
$this->assertEquals($expected, $translator->transChoice($id, $number, $parameters, $domain, $locale));
|
||||
}
|
||||
|
||||
public function getTransTests()
|
||||
{
|
||||
return array(
|
||||
array('Symfony2 est super !', 'Symfony2 is great!', 'Symfony2 est super !', array(), 'fr', ''),
|
||||
array('Symfony2 est awesome !', 'Symfony2 is %what%!', 'Symfony2 est %what% !', array('%what%' => 'awesome'), 'fr', ''),
|
||||
);
|
||||
}
|
||||
|
||||
public function getTransChoiceTests()
|
||||
{
|
||||
return array(
|
||||
array('Il y a 0 pomme', '{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 0, array('%count%' => 0), 'fr', ''),
|
||||
array('Il y a 1 pomme', '{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 1, array('%count%' => 1), 'fr', ''),
|
||||
array('Il y a 10 pommes', '{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 10, array('%count%' => 10), 'fr', ''),
|
||||
|
||||
array('Il y a 0 pomme', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 0, array('%count%' => 0), 'fr', ''),
|
||||
array('Il y a 1 pomme', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 1, array('%count%' => 1), 'fr', ''),
|
||||
array('Il y a 10 pommes', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 10, array('%count%' => 10), 'fr', ''),
|
||||
|
||||
array('Il y a 0 pomme', 'one: There is one apple|more: There is %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 0, array('%count%' => 0), 'fr', ''),
|
||||
array('Il y a 1 pomme', 'one: There is one apple|more: There is %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 1, array('%count%' => 1), 'fr', ''),
|
||||
array('Il y a 10 pommes', 'one: There is one apple|more: There is %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 10, array('%count%' => 10), 'fr', ''),
|
||||
|
||||
array('Il n\'y a aucune pomme', '{0} There is no apple|one: There is one apple|more: There is %count% apples', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 0, array('%count%' => 0), 'fr', ''),
|
||||
array('Il y a 1 pomme', '{0} There is no apple|one: There is one apple|more: There is %count% apples', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 1, array('%count%' => 1), 'fr', ''),
|
||||
array('Il y a 10 pommes', '{0} There is no apple|one: There is one apple|more: There is %count% apples', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 10, array('%count%' => 10), 'fr', ''),
|
||||
);
|
||||
}
|
||||
}
|
Reference in New Issue