Update website

This commit is contained in:
Guilhem Lavaux 2024-11-19 08:02:04 +01:00
parent 4413528994
commit 1d90fbf296
6865 changed files with 1091082 additions and 0 deletions

View file

@ -0,0 +1,3 @@
vendor/
composer.lock
phpunit.xml

View file

@ -0,0 +1,5 @@
CHANGELOG
=========
The changelog is maintained for all Symfony contracts at the following URL:
https://github.com/symfony/contracts/blob/main/CHANGELOG.md

View file

@ -0,0 +1,19 @@
Copyright (c) 2018-present 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
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -0,0 +1,31 @@
<?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\Contracts\Translation;
interface LocaleAwareInterface
{
/**
* Sets the current locale.
*
* @param string $locale The locale
*
* @throws \InvalidArgumentException If the locale contains invalid characters
*/
public function setLocale(string $locale);
/**
* Returns the current locale.
*
* @return string
*/
public function getLocale();
}

View file

@ -0,0 +1,9 @@
Symfony Translation Contracts
=============================
A set of abstractions extracted out of the Symfony components.
Can be used to build on semantics that the Symfony components proved useful - and
that already have battle tested implementations.
See https://github.com/symfony/contracts/blob/main/README.md for more information.

View file

@ -0,0 +1,390 @@
<?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\Contracts\Translation\Test;
use PHPUnit\Framework\TestCase;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Contracts\Translation\TranslatorTrait;
/**
* Test should cover all languages mentioned on http://translate.sourceforge.net/wiki/l10n/pluralforms
* and Plural forms mentioned on http://www.gnu.org/software/gettext/manual/gettext.html#Plural-forms.
*
* See also https://developer.mozilla.org/en/Localization_and_Plurals which mentions 15 rules having a maximum of 6 forms.
* The mozilla code is also interesting to check for.
*
* As mentioned by chx http://drupal.org/node/1273968 we can cover all by testing number from 0 to 199
*
* The goal to cover all languages is to far fetched so this test case is smaller.
*
* @author Clemens Tolboom clemens@build2be.nl
*/
class TranslatorTest extends TestCase
{
private $defaultLocale;
protected function setUp(): void
{
$this->defaultLocale = \Locale::getDefault();
\Locale::setDefault('en');
}
protected function tearDown(): void
{
\Locale::setDefault($this->defaultLocale);
}
/**
* @return TranslatorInterface
*/
public function getTranslator()
{
return new class() implements TranslatorInterface {
use TranslatorTrait;
};
}
/**
* @dataProvider getTransTests
*/
public function testTrans($expected, $id, $parameters)
{
$translator = $this->getTranslator();
$this->assertEquals($expected, $translator->trans($id, $parameters));
}
/**
* @dataProvider getTransChoiceTests
*/
public function testTransChoiceWithExplicitLocale($expected, $id, $number)
{
$translator = $this->getTranslator();
$this->assertEquals($expected, $translator->trans($id, ['%count%' => $number]));
}
/**
* @requires extension intl
*
* @dataProvider getTransChoiceTests
*/
public function testTransChoiceWithDefaultLocale($expected, $id, $number)
{
$translator = $this->getTranslator();
$this->assertEquals($expected, $translator->trans($id, ['%count%' => $number]));
}
/**
* @dataProvider getTransChoiceTests
*/
public function testTransChoiceWithEnUsPosix($expected, $id, $number)
{
$translator = $this->getTranslator();
$translator->setLocale('en_US_POSIX');
$this->assertEquals($expected, $translator->trans($id, ['%count%' => $number]));
}
public function testGetSetLocale()
{
$translator = $this->getTranslator();
$this->assertEquals('en', $translator->getLocale());
}
/**
* @requires extension intl
*/
public function testGetLocaleReturnsDefaultLocaleIfNotSet()
{
$translator = $this->getTranslator();
\Locale::setDefault('pt_BR');
$this->assertEquals('pt_BR', $translator->getLocale());
\Locale::setDefault('en');
$this->assertEquals('en', $translator->getLocale());
}
public static function getTransTests()
{
return [
['Symfony is great!', 'Symfony is great!', []],
['Symfony is awesome!', 'Symfony is %what%!', ['%what%' => 'awesome']],
];
}
public static function getTransChoiceTests()
{
return [
['There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0],
['There is one apple', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 1],
['There are 10 apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10],
['There are 0 apples', 'There is 1 apple|There are %count% apples', 0],
['There is 1 apple', 'There is 1 apple|There are %count% apples', 1],
['There are 10 apples', 'There is 1 apple|There are %count% apples', 10],
// custom validation messages may be coded with a fixed value
['There are 2 apples', 'There are 2 apples', 2],
];
}
/**
* @dataProvider getInterval
*/
public function testInterval($expected, $number, $interval)
{
$translator = $this->getTranslator();
$this->assertEquals($expected, $translator->trans($interval.' foo|[1,Inf[ bar', ['%count%' => $number]));
}
public static function getInterval()
{
return [
['foo', 3, '{1,2, 3 ,4}'],
['bar', 10, '{1,2, 3 ,4}'],
['bar', 3, '[1,2]'],
['foo', 1, '[1,2]'],
['foo', 2, '[1,2]'],
['bar', 1, ']1,2['],
['bar', 2, ']1,2['],
['foo', log(0), '[-Inf,2['],
['foo', -log(0), '[-2,+Inf]'],
];
}
/**
* @dataProvider getChooseTests
*/
public function testChoose($expected, $id, $number, $locale = null)
{
$translator = $this->getTranslator();
$this->assertEquals($expected, $translator->trans($id, ['%count%' => $number], null, $locale));
}
public function testReturnMessageIfExactlyOneStandardRuleIsGiven()
{
$translator = $this->getTranslator();
$this->assertEquals('There are two apples', $translator->trans('There are two apples', ['%count%' => 2]));
}
/**
* @dataProvider getNonMatchingMessages
*/
public function testThrowExceptionIfMatchingMessageCannotBeFound($id, $number)
{
$this->expectException(\InvalidArgumentException::class);
$translator = $this->getTranslator();
$translator->trans($id, ['%count%' => $number]);
}
public static function getNonMatchingMessages()
{
return [
['{0} There are no apples|{1} There is one apple', 2],
['{1} There is one apple|]1,Inf] There are %count% apples', 0],
['{1} There is one apple|]2,Inf] There are %count% apples', 2],
['{0} There are no apples|There is one apple', 2],
];
}
public static function getChooseTests()
{
return [
['There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0],
['There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0],
['There are no apples', '{0}There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0],
['There is one apple', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 1],
['There are 10 apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10],
['There are 10 apples', '{0} There are no apples|{1} There is one apple|]1,Inf]There are %count% apples', 10],
['There are 10 apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10],
['There are 0 apples', 'There is one apple|There are %count% apples', 0],
['There is one apple', 'There is one apple|There are %count% apples', 1],
['There are 10 apples', 'There is one apple|There are %count% apples', 10],
['There are 0 apples', 'one: There is one apple|more: There are %count% apples', 0],
['There is one apple', 'one: There is one apple|more: There are %count% apples', 1],
['There are 10 apples', 'one: There is one apple|more: There are %count% apples', 10],
['There are no apples', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 0],
['There is one apple', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 1],
['There are 10 apples', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 10],
['', '{0}|{1} There is one apple|]1,Inf] There are %count% apples', 0],
['', '{0} There are no apples|{1}|]1,Inf] There are %count% apples', 1],
// Indexed only tests which are Gettext PoFile* compatible strings.
['There are 0 apples', 'There is one apple|There are %count% apples', 0],
['There is one apple', 'There is one apple|There are %count% apples', 1],
['There are 2 apples', 'There is one apple|There are %count% apples', 2],
// Tests for float numbers
['There is almost one apple', '{0} There are no apples|]0,1[ There is almost one apple|{1} There is one apple|[1,Inf] There is more than one apple', 0.7],
['There is one apple', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 1],
['There is more than one apple', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 1.7],
['There are no apples', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0],
['There are no apples', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0.0],
['There are no apples', '{0.0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0],
// Test texts with new-lines
// with double-quotes and \n in id & double-quotes and actual newlines in text
["This is a text with a\n new-line in it. Selector = 0.", '{0}This is a text with a
new-line in it. Selector = 0.|{1}This is a text with a
new-line in it. Selector = 1.|[1,Inf]This is a text with a
new-line in it. Selector > 1.', 0],
// with double-quotes and \n in id and single-quotes and actual newlines in text
["This is a text with a\n new-line in it. Selector = 1.", '{0}This is a text with a
new-line in it. Selector = 0.|{1}This is a text with a
new-line in it. Selector = 1.|[1,Inf]This is a text with a
new-line in it. Selector > 1.', 1],
["This is a text with a\n new-line in it. Selector > 1.", '{0}This is a text with a
new-line in it. Selector = 0.|{1}This is a text with a
new-line in it. Selector = 1.|[1,Inf]This is a text with a
new-line in it. Selector > 1.', 5],
// with double-quotes and id split across lines
['This is a text with a
new-line in it. Selector = 1.', '{0}This is a text with a
new-line in it. Selector = 0.|{1}This is a text with a
new-line in it. Selector = 1.|[1,Inf]This is a text with a
new-line in it. Selector > 1.', 1],
// with single-quotes and id split across lines
['This is a text with a
new-line in it. Selector > 1.', '{0}This is a text with a
new-line in it. Selector = 0.|{1}This is a text with a
new-line in it. Selector = 1.|[1,Inf]This is a text with a
new-line in it. Selector > 1.', 5],
// with single-quotes and \n in text
['This is a text with a\nnew-line in it. Selector = 0.', '{0}This is a text with a\nnew-line in it. Selector = 0.|{1}This is a text with a\nnew-line in it. Selector = 1.|[1,Inf]This is a text with a\nnew-line in it. Selector > 1.', 0],
// with double-quotes and id split across lines
["This is a text with a\nnew-line in it. Selector = 1.", "{0}This is a text with a\nnew-line in it. Selector = 0.|{1}This is a text with a\nnew-line in it. Selector = 1.|[1,Inf]This is a text with a\nnew-line in it. Selector > 1.", 1],
// escape pipe
['This is a text with | in it. Selector = 0.', '{0}This is a text with || in it. Selector = 0.|{1}This is a text with || in it. Selector = 1.', 0],
// Empty plural set (2 plural forms) from a .PO file
['', '|', 1],
// Empty plural set (3 plural forms) from a .PO file
['', '||', 1],
// Floating values
['1.5 liters', '%count% liter|%count% liters', 1.5],
['1.5 litre', '%count% litre|%count% litres', 1.5, 'fr'],
// Negative values
['-1 degree', '%count% degree|%count% degrees', -1],
['-1 degré', '%count% degré|%count% degrés', -1],
['-1.5 degrees', '%count% degree|%count% degrees', -1.5],
['-1.5 degré', '%count% degré|%count% degrés', -1.5, 'fr'],
['-2 degrees', '%count% degree|%count% degrees', -2],
['-2 degrés', '%count% degré|%count% degrés', -2],
];
}
/**
* @dataProvider failingLangcodes
*/
public function testFailedLangcodes($nplural, $langCodes)
{
$matrix = $this->generateTestData($langCodes);
$this->validateMatrix($nplural, $matrix, false);
}
/**
* @dataProvider successLangcodes
*/
public function testLangcodes($nplural, $langCodes)
{
$matrix = $this->generateTestData($langCodes);
$this->validateMatrix($nplural, $matrix);
}
/**
* This array should contain all currently known langcodes.
*
* As it is impossible to have this ever complete we should try as hard as possible to have it almost complete.
*
* @return array
*/
public static function successLangcodes()
{
return [
['1', ['ay', 'bo', 'cgg', 'dz', 'id', 'ja', 'jbo', 'ka', 'kk', 'km', 'ko', 'ky']],
['2', ['nl', 'fr', 'en', 'de', 'de_GE', 'hy', 'hy_AM', 'en_US_POSIX']],
['3', ['be', 'bs', 'cs', 'hr']],
['4', ['cy', 'mt', 'sl']],
['6', ['ar']],
];
}
/**
* This array should be at least empty within the near future.
*
* This both depends on a complete list trying to add above as understanding
* the plural rules of the current failing languages.
*
* @return array with nplural together with langcodes
*/
public static function failingLangcodes()
{
return [
['1', ['fa']],
['2', ['jbo']],
['3', ['cbs']],
['4', ['gd', 'kw']],
['5', ['ga']],
];
}
/**
* We validate only on the plural coverage. Thus the real rules is not tested.
*
* @param string $nplural Plural expected
* @param array $matrix Containing langcodes and their plural index values
* @param bool $expectSuccess
*/
protected function validateMatrix($nplural, $matrix, $expectSuccess = true)
{
foreach ($matrix as $langCode => $data) {
$indexes = array_flip($data);
if ($expectSuccess) {
$this->assertCount($nplural, $indexes, "Langcode '$langCode' has '$nplural' plural forms.");
} else {
$this->assertNotEquals((int) $nplural, \count($indexes), "Langcode '$langCode' has '$nplural' plural forms.");
}
}
}
protected function generateTestData($langCodes)
{
$translator = new class() {
use TranslatorTrait {
getPluralizationRule as public;
}
};
$matrix = [];
foreach ($langCodes as $langCode) {
for ($count = 0; $count < 200; ++$count) {
$plural = $translator->getPluralizationRule($count, $langCode);
$matrix[$langCode][$count] = $plural;
}
}
return $matrix;
}
}

View file

@ -0,0 +1,20 @@
<?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\Contracts\Translation;
/**
* @author Nicolas Grekas <p@tchwork.com>
*/
interface TranslatableInterface
{
public function trans(TranslatorInterface $translator, ?string $locale = null): string;
}

View file

@ -0,0 +1,67 @@
<?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\Contracts\Translation;
/**
* @author Fabien Potencier <fabien@symfony.com>
*
* @method string getLocale() Returns the default locale
*/
interface TranslatorInterface
{
/**
* Translates the given message.
*
* When a number is provided as a parameter named "%count%", the message is parsed for plural
* forms and a translation is chosen according to this number using the following rules:
*
* Given a message with different plural translations separated by a
* pipe (|), this method returns the correct portion of the message based
* on the given number, locale and the pluralization rules in the message
* itself.
*
* The message supports two different types of pluralization rules:
*
* interval: {0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples
* indexed: There is one apple|There are %count% apples
*
* The indexed solution can also contain labels (e.g. one: There is one apple).
* This is purely for making the translations more clear - it does not
* affect the functionality.
*
* The two methods can also be mixed:
* {0} There are no apples|one: There is one apple|more: There are %count% apples
*
* An interval can represent a finite set of numbers:
* {1,2,3,4}
*
* An interval 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.
*
* @see https://en.wikipedia.org/wiki/ISO_31-11
*
* @param string $id The message id (may also be an object that can be cast to string)
* @param array $parameters An array of parameters for the message
* @param string|null $domain The domain for the message or null to use the default
* @param string|null $locale The locale or null to use the default
*
* @return string
*
* @throws \InvalidArgumentException If the locale contains invalid characters
*/
public function trans(string $id, array $parameters = [], ?string $domain = null, ?string $locale = null);
}

View file

@ -0,0 +1,262 @@
<?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\Contracts\Translation;
use Symfony\Component\Translation\Exception\InvalidArgumentException;
/**
* A trait to help implement TranslatorInterface and LocaleAwareInterface.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
trait TranslatorTrait
{
private $locale;
/**
* {@inheritdoc}
*/
public function setLocale(string $locale)
{
$this->locale = $locale;
}
/**
* {@inheritdoc}
*
* @return string
*/
public function getLocale()
{
return $this->locale ?: (class_exists(\Locale::class) ? \Locale::getDefault() : 'en');
}
/**
* {@inheritdoc}
*/
public function trans(?string $id, array $parameters = [], ?string $domain = null, ?string $locale = null): string
{
if (null === $id || '' === $id) {
return '';
}
if (!isset($parameters['%count%']) || !is_numeric($parameters['%count%'])) {
return strtr($id, $parameters);
}
$number = (float) $parameters['%count%'];
$locale = $locale ?: $this->getLocale();
$parts = [];
if (preg_match('/^\|++$/', $id)) {
$parts = explode('|', $id);
} elseif (preg_match_all('/(?:\|\||[^\|])++/', $id, $matches)) {
$parts = $matches[0];
}
$intervalRegexp = <<<'EOF'
/^(?P<interval>
({\s*
(\-?\d+(\.\d+)?[\s*,\s*\-?\d+(\.\d+)?]*)
\s*})
|
(?P<left_delimiter>[\[\]])
\s*
(?P<left>-Inf|\-?\d+(\.\d+)?)
\s*,\s*
(?P<right>\+?Inf|\-?\d+(\.\d+)?)
\s*
(?P<right_delimiter>[\[\]])
)\s*(?P<message>.*?)$/xs
EOF;
$standardRules = [];
foreach ($parts as $part) {
$part = trim(str_replace('||', '|', $part));
// try to match an explicit rule, then fallback to the standard ones
if (preg_match($intervalRegexp, $part, $matches)) {
if ($matches[2]) {
foreach (explode(',', $matches[3]) as $n) {
if ($number == $n) {
return strtr($matches['message'], $parameters);
}
}
} else {
$leftNumber = '-Inf' === $matches['left'] ? -\INF : (float) $matches['left'];
$rightNumber = is_numeric($matches['right']) ? (float) $matches['right'] : \INF;
if (('[' === $matches['left_delimiter'] ? $number >= $leftNumber : $number > $leftNumber)
&& (']' === $matches['right_delimiter'] ? $number <= $rightNumber : $number < $rightNumber)
) {
return strtr($matches['message'], $parameters);
}
}
} elseif (preg_match('/^\w+\:\s*(.*?)$/', $part, $matches)) {
$standardRules[] = $matches[1];
} else {
$standardRules[] = $part;
}
}
$position = $this->getPluralizationRule($number, $locale);
if (!isset($standardRules[$position])) {
// when there's exactly one rule given, and that rule is a standard
// rule, use this rule
if (1 === \count($parts) && isset($standardRules[0])) {
return strtr($standardRules[0], $parameters);
}
$message = sprintf('Unable to choose a translation for "%s" with locale "%s" for value "%d". Double check that this translation has the correct plural options (e.g. "There is one apple|There are %%count%% apples").', $id, $locale, $number);
if (class_exists(InvalidArgumentException::class)) {
throw new InvalidArgumentException($message);
}
throw new \InvalidArgumentException($message);
}
return strtr($standardRules[$position], $parameters);
}
/**
* Returns the plural position to use for the given locale and number.
*
* 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)
*/
private function getPluralizationRule(float $number, string $locale): int
{
$number = abs($number);
switch ('pt_BR' !== $locale && 'en_US_POSIX' !== $locale && \strlen($locale) > 3 ? substr($locale, 0, strrpos($locale, '_')) : $locale) {
case 'af':
case 'bn':
case 'bg':
case 'ca':
case 'da':
case 'de':
case 'el':
case 'en':
case 'en_US_POSIX':
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 'oc':
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 (1 == $number) ? 0 : 1;
case 'am':
case 'bh':
case 'fil':
case 'fr':
case 'gun':
case 'hi':
case 'hy':
case 'ln':
case 'mg':
case 'nso':
case 'pt_BR':
case 'ti':
case 'wa':
return ($number < 2) ? 0 : 1;
case 'be':
case 'bs':
case 'hr':
case 'ru':
case 'sh':
case 'sr':
case 'uk':
return ((1 == $number % 10) && (11 != $number % 100)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
case 'cs':
case 'sk':
return (1 == $number) ? 0 : ((($number >= 2) && ($number <= 4)) ? 1 : 2);
case 'ga':
return (1 == $number) ? 0 : ((2 == $number) ? 1 : 2);
case 'lt':
return ((1 == $number % 10) && (11 != $number % 100)) ? 0 : ((($number % 10 >= 2) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
case 'sl':
return (1 == $number % 100) ? 0 : ((2 == $number % 100) ? 1 : (((3 == $number % 100) || (4 == $number % 100)) ? 2 : 3));
case 'mk':
return (1 == $number % 10) ? 0 : 1;
case 'mt':
return (1 == $number) ? 0 : (((0 == $number) || (($number % 100 > 1) && ($number % 100 < 11))) ? 1 : ((($number % 100 > 10) && ($number % 100 < 20)) ? 2 : 3));
case 'lv':
return (0 == $number) ? 0 : (((1 == $number % 10) && (11 != $number % 100)) ? 1 : 2);
case 'pl':
return (1 == $number) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 12) || ($number % 100 > 14))) ? 1 : 2);
case 'cy':
return (1 == $number) ? 0 : ((2 == $number) ? 1 : (((8 == $number) || (11 == $number)) ? 2 : 3));
case 'ro':
return (1 == $number) ? 0 : (((0 == $number) || (($number % 100 > 0) && ($number % 100 < 20))) ? 1 : 2);
case 'ar':
return (0 == $number) ? 0 : ((1 == $number) ? 1 : ((2 == $number) ? 2 : ((($number % 100 >= 3) && ($number % 100 <= 10)) ? 3 : ((($number % 100 >= 11) && ($number % 100 <= 99)) ? 4 : 5))));
default:
return 0;
}
}
}

View file

@ -0,0 +1,37 @@
{
"name": "symfony/translation-contracts",
"type": "library",
"description": "Generic abstractions related to translation",
"keywords": ["abstractions", "contracts", "decoupling", "interfaces", "interoperability", "standards"],
"homepage": "https://symfony.com",
"license": "MIT",
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"require": {
"php": ">=7.2.5"
},
"suggest": {
"symfony/translation-implementation": ""
},
"autoload": {
"psr-4": { "Symfony\\Contracts\\Translation\\": "" }
},
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-main": "2.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
}
}