[Intl] Refactored Locale component into two new components Icu and Intl

This commit is contained in:
Bernhard Schussek 2013-03-01 18:49:04 +01:00
parent a7c9863b3a
commit 5917a2e0cb
1361 changed files with 15572 additions and 10294 deletions

View File

@ -14,5 +14,3 @@ before_script:
- echo '' > ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini
- echo "extension = apc.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
- COMPOSER_ROOT_VERSION=dev-master composer --prefer-source --dev install
- php src/Symfony/Component/Locale/Resources/data/build-data.php
- export USE_INTL_ICU_DATA_VERSION=1

View File

@ -10,8 +10,4 @@ $loader = require_once __DIR__.'/vendor/autoload.php';
use Doctrine\Common\Annotations\AnnotationRegistry;
if (!function_exists('intl_get_error_code')) {
require_once __DIR__.'/src/Symfony/Component/Locale/Resources/stubs/functions.php';
}
AnnotationRegistry::registerLoader(array($loader, 'loadClass'));

View File

@ -17,6 +17,7 @@
],
"require": {
"php": ">=5.3.3",
"symfony/icu": ">=1.0",
"doctrine/common": "~2.2",
"twig/twig": "~1.11",
"psr/log": "~1.0"
@ -37,6 +38,7 @@
"symfony/framework-bundle": "self.version",
"symfony/http-foundation": "self.version",
"symfony/http-kernel": "self.version",
"symfony/intl": "self.version",
"symfony/locale": "self.version",
"symfony/monolog-bridge": "self.version",
"symfony/options-resolver": "self.version",
@ -68,8 +70,9 @@
"psr-0": { "Symfony\\": "src/" },
"classmap": [
"src/Symfony/Component/HttpFoundation/Resources/stubs",
"src/Symfony/Component/Locale/Resources/stubs"
]
"src/Symfony/Component/Intl/Resources/stubs"
],
"files": [ "src/Symfony/Component/Intl/Resources/stubs/functions.php" ]
},
"minimum-stability": "dev",
"extra": {

3
src/Symfony/Component/Intl/.gitignore vendored Normal file
View File

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

View File

@ -0,0 +1,91 @@
Contributing to the Intl component
==================================
A very good way of contributing to the Intl component is by updating the
included data for the ICU version you have installed on your system.
Preparation
-----------
To prepare, you need to install the development dependencies of the component.
$ cd /path/to/Symfony/Component/Intl
$ composer.phar install --dev
Determining your ICU version
---------------------------
The ICU version installed in your PHP environment can be found by running
icu-version.php:
$ php Resources/bin/icu-version.php
Updating the ICU data
---------------------
To update the data files, run the update-icu-component.php script:
$ php Resources/bin/update-icu-component.php
The script needs the binaries "svn" and "make" to be available on your system.
It will download the latest version of the ICU sources for the ICU version
installed in your PHP environment. The script will then compile the "genrb"
binary and use it to compile the ICU data files to binaries. The binaries are
copied to the Resources/ directory of the Icu component found in the
vendor/symfony/icu/ directory.
Updating the stub data
----------------------
In the previous step you updated the Icu component for the ICU version
installed on your system. If you are using the latest ICU version, you should
also create the stub data files which will be used by people who don't have
the intl extension installed.
To update the stub files, run the update-stubs.php script:
$ php Resources/bin/update-stubs.php
The script will fail if you don't have the latest ICU version. If you want to
upgrade the ICU version, adjust the return value of the
`Intl::getStubIcuVersion()` before you run the script.
The script creates copies of the binary resource bundles in the Icu component
and stores them in the Resources/ directory of the Intl component. The copies
are made for the locale "en" only and are stored in .php files, so that they
can be read even if the intl extension is not available.
Creating a pull request
-----------------------
You need to create up to two pull requests:
* If you updated the Icu component, you need to push that change and create a
pull request in the `symfony/Icu` repository. Make sure to submit the pull
request to the correct master branch. If you updated the ICU data for version
4.8, your pull request goes to branch `48-master`, for version 49 to
`49-master` and so on.
* If you updated the stub files of the Intl component, you need to push that
change and create a pull request in the `symfony/symfony` repository. The
pull request should be based on the `master` branch.
Combining .res files to a .dat-package
--------------------------------------
The individual *.res files can be combined into a single .dat-file.
Unfortunately, PHP's `ResourceBundle` class is currently not able to handle
.dat-files.
Once it is, the following steps have to be followed to build the .dat-file:
1. Package the resource bundles into a single file
$ find . -name *.res | sed -e "s/\.\///g" > packagelist.txt
$ pkgdata -p region -T build -d . packagelist.txt
2. Clean up
$ rm -rf build packagelist.txt
3. You can now move region.dat to replace the version bundled with Symfony2.

View File

@ -0,0 +1,284 @@
<?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\Intl\Collator;
use Symfony\Component\Intl\Exception\MethodNotImplementedException;
use Symfony\Component\Intl\Exception\MethodArgumentValueNotImplementedException;
use Symfony\Component\Intl\Globals\StubIntlGlobals;
use Symfony\Component\Intl\Locale\StubLocale;
/**
* Provides a stub Collator for the 'en' locale.
*
* @author Igor Wiedler <igor@wiedler.ch>
*/
class StubCollator
{
/** Attribute constants */
const FRENCH_COLLATION = 0;
const ALTERNATE_HANDLING = 1;
const CASE_FIRST = 2;
const CASE_LEVEL = 3;
const NORMALIZATION_MODE = 4;
const STRENGTH = 5;
const HIRAGANA_QUATERNARY_MODE = 6;
const NUMERIC_COLLATION = 7;
/** Attribute constants values */
const DEFAULT_VALUE = -1;
const PRIMARY = 0;
const SECONDARY = 1;
const TERTIARY = 2;
const DEFAULT_STRENGTH = 2;
const QUATERNARY = 3;
const IDENTICAL = 15;
const OFF = 16;
const ON = 17;
const SHIFTED = 20;
const NON_IGNORABLE = 21;
const LOWER_FIRST = 24;
const UPPER_FIRST = 25;
/** Sorting options */
const SORT_REGULAR = 0;
const SORT_NUMERIC = 2;
const SORT_STRING = 1;
/**
* Constructor
*
* @param string $locale The locale code
*
* @throws MethodArgumentValueNotImplementedException When $locale different than 'en' is passed
*/
public function __construct($locale)
{
if ('en' != $locale) {
throw new MethodArgumentValueNotImplementedException(__METHOD__, 'locale', $locale, 'Only the \'en\' locale is supported');
}
}
/**
* Static constructor
*
* @param string $locale The locale code
*
* @return StubCollator
*
* @throws MethodArgumentValueNotImplementedException When $locale different than 'en' is passed
*/
public static function create($locale)
{
return new self($locale);
}
/**
* Sort array maintaining index association
*
* @param array &$array Input array
* @param integer $sortFlag Flags for sorting, can be one of the following:
* StubCollator::SORT_REGULAR - compare items normally (don't change types)
* StubCollator::SORT_NUMERIC - compare items numerically
* StubCollator::SORT_STRING - compare items as strings
*
* @return Boolean True on success or false on failure
*/
public function asort(&$array, $sortFlag = self::SORT_REGULAR)
{
$intlToPlainFlagMap = array(
self::SORT_REGULAR => \SORT_REGULAR,
self::SORT_NUMERIC => \SORT_NUMERIC,
self::SORT_STRING => \SORT_STRING,
);
$plainSortFlag = isset($intlToPlainFlagMap[$sortFlag]) ? $intlToPlainFlagMap[$sortFlag] : self::SORT_REGULAR;
return asort($array, $plainSortFlag);
}
/**
* Compare two Unicode strings
*
* @param string $str1 The first string to compare
* @param string $str2 The second string to compare
*
* @return Boolean|int Return the comparison result or false on failure:
* 1 if $str1 is greater than $str2
* 0 if $str1 is equal than $str2
* -1 if $str1 is less than $str2
*
* @see http://www.php.net/manual/en/collator.compare.php
*
* @throws MethodNotImplementedException
*/
public function compare($str1, $str2)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Get a value of an integer collator attribute
*
* @param int $attr An attribute specifier, one of the attribute constants
*
* @return Boolean|int The attribute value on success or false on error
*
* @see http://www.php.net/manual/en/collator.getattribute.php
*
* @throws MethodNotImplementedException
*/
public function getAttribute($attr)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Returns collator's last error code. Always returns the U_ZERO_ERROR class constant value
*
* @return int The error code from last collator call
*/
public function getErrorCode()
{
return StubIntlGlobals::U_ZERO_ERROR;
}
/**
* Returns collator's last error message. Always returns the U_ZERO_ERROR_MESSAGE class constant value
*
* @return string The error message from last collator call
*/
public function getErrorMessage()
{
return 'U_ZERO_ERROR';
}
/**
* Returns the collator's locale
*
* @param int $type The locale name type to return between valid or actual (StubLocale::VALID_LOCALE or StubLocale::ACTUAL_LOCALE, respectively)
*
* @return string The locale name used to create the collator
*/
public function getLocale($type = StubLocale::ACTUAL_LOCALE)
{
return 'en';
}
/**
* Get sorting key for a string
*
* @param string $string The string to produce the key from
*
* @return string The collation key for $string
*
* @see http://www.php.net/manual/en/collator.getsortkey.php
*
* @throws MethodNotImplementedException
*/
public function getSortKey($string)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Get current collator's strength
*
* @return Boolean|int The current collator's strength or false on failure
*
* @see http://www.php.net/manual/en/collator.getstrength.php
*
* @throws MethodNotImplementedException
*/
public function getStrength()
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Set a collator's attribute
*
* @param int $attr An attribute specifier, one of the attribute constants
* @param int $val The attribute value, one of the attribute value constants
*
* @return Boolean True on success or false on failure
*
* @see http://www.php.net/manual/en/collator.setattribute.php
*
* @throws MethodNotImplementedException
*/
public function setAttribute($attr, $val)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Set the collator's strength
*
* @param int $strength Strength to set, possible values:
* StubCollator::PRIMARY
* StubCollator::SECONDARY
* StubCollator::TERTIARY
* StubCollator::QUATERNARY
* StubCollator::IDENTICAL
* StubCollator::DEFAULT
*
* @return Boolean True on success or false on failure
*
* @see http://www.php.net/manual/en/collator.setstrength.php
*
* @throws MethodNotImplementedException
*/
public function setStrength($strength)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Sort array using specified collator and sort keys
*
* @param array &$arr Array of strings to sort
*
* @return Boolean True on success or false on failure
*
* @see http://www.php.net/manual/en/collator.sortwithsortkeys.php
*
* @throws MethodNotImplementedException
*/
public function sortWithSortKeys(&$arr)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Sort array using specified collator
*
* @param array &$arr Array of string to sort
* @param int $sortFlag Optional sorting type, one of the following:
* StubCollator::SORT_REGULAR
* StubCollator::SORT_NUMERIC
* StubCollator::SORT_STRING
*
* @return Boolean True on success or false on failure
*
* @see http://www.php.net/manual/en/collator.sort.php
*
* @throws MethodNotImplementedException
*/
public function sort(&$arr, $sortFlag = self::SORT_REGULAR)
{
throw new MethodNotImplementedException(__METHOD__);
}
}

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\Intl\DateFormatter\DateFormat;
/**
* Parser and formatter for AM/PM markers format
*
* @author Igor Wiedler <igor@wiedler.ch>
*/
class AmPmTransformer extends Transformer
{
/**
* {@inheritDoc}
*/
public function format(\DateTime $dateTime, $length)
{
return $dateTime->format('A');
}
/**
* {@inheritDoc}
*/
public function getReverseMatchingRegExp($length)
{
return 'AM|PM';
}
/**
* {@inheritDoc}
*/
public function extractDateOptions($matched, $length)
{
return array(
'marker' => $matched
);
}
}

View File

@ -0,0 +1,59 @@
<?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\Intl\DateFormatter\DateFormat;
/**
* Parser and formatter for day of week format
*
* @author Igor Wiedler <igor@wiedler.ch>
*/
class DayOfWeekTransformer extends Transformer
{
/**
* {@inheritDoc}
*/
public function format(\DateTime $dateTime, $length)
{
$dayOfWeek = $dateTime->format('l');
switch ($length) {
case 4:
return $dayOfWeek;
case 5:
return $dayOfWeek[0];
default:
return substr($dayOfWeek, 0, 3);
}
}
/**
* {@inheritDoc}
*/
public function getReverseMatchingRegExp($length)
{
switch ($length) {
case 4:
return 'Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday';
case 5:
return '[MTWFS]';
default:
return 'Mon|Tue|Wed|Thu|Fri|Sat|Sun';
}
}
/**
* {@inheritDoc}
*/
public function extractDateOptions($matched, $length)
{
return array();
}
}

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\Intl\DateFormatter\DateFormat;
/**
* Parser and formatter for day of year format
*
* @author Igor Wiedler <igor@wiedler.ch>
*/
class DayOfYearTransformer extends Transformer
{
/**
* {@inheritDoc}
*/
public function format(\DateTime $dateTime, $length)
{
$dayOfYear = $dateTime->format('z') + 1;
return $this->padLeft($dayOfYear, $length);
}
/**
* {@inheritDoc}
*/
public function getReverseMatchingRegExp($length)
{
return '\d{'.$length.'}';
}
/**
* {@inheritDoc}
*/
public function extractDateOptions($matched, $length)
{
return array();
}
}

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\Intl\DateFormatter\DateFormat;
/**
* Parser and formatter for day format
*
* @author Igor Wiedler <igor@wiedler.ch>
*/
class DayTransformer extends Transformer
{
/**
* {@inheritDoc}
*/
public function format(\DateTime $dateTime, $length)
{
return $this->padLeft($dateTime->format('j'), $length);
}
/**
* {@inheritDoc}
*/
public function getReverseMatchingRegExp($length)
{
return 1 === $length ? '\d{1,2}' : '\d{'.$length.'}';
}
/**
* {@inheritDoc}
*/
public function extractDateOptions($matched, $length)
{
return array(
'day' => (int) $matched,
);
}
}

View File

@ -0,0 +1,356 @@
<?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\Intl\DateFormatter\DateFormat;
use Symfony\Component\Intl\Exception\NotImplementedException;
use Symfony\Component\Intl\Globals\StubIntlGlobals;
use Symfony\Component\Intl\DateFormatter\DateFormat\MonthTransformer;
/**
* Parser and formatter for date formats
*
* @author Igor Wiedler <igor@wiedler.ch>
*/
class FullTransformer
{
private $quoteMatch = "'(?:[^']+|'')*'";
private $implementedChars = 'MLydQqhDEaHkKmsz';
private $notImplementedChars = 'GYuwWFgecSAZvVW';
private $regExp;
/**
* @var Transformer[]
*/
private $transformers;
private $pattern;
private $timezone;
/**
* Constructor
*
* @param string $pattern The pattern to be used to format and/or parse values
* @param string $timezone The timezone to perform the date/time calculations
*/
public function __construct($pattern, $timezone)
{
$this->pattern = $pattern;
$this->timezone = $timezone;
$implementedCharsMatch = $this->buildCharsMatch($this->implementedChars);
$notImplementedCharsMatch = $this->buildCharsMatch($this->notImplementedChars);
$this->regExp = "/($this->quoteMatch|$implementedCharsMatch|$notImplementedCharsMatch)/";
$this->transformers = array(
'M' => new MonthTransformer(),
'L' => new MonthTransformer(),
'y' => new YearTransformer(),
'd' => new DayTransformer(),
'q' => new QuarterTransformer(),
'Q' => new QuarterTransformer(),
'h' => new Hour1201Transformer(),
'D' => new DayOfYearTransformer(),
'E' => new DayOfWeekTransformer(),
'a' => new AmPmTransformer(),
'H' => new Hour2400Transformer(),
'K' => new Hour1200Transformer(),
'k' => new Hour2401Transformer(),
'm' => new MinuteTransformer(),
's' => new SecondTransformer(),
'z' => new TimeZoneTransformer(),
);
}
/**
* Return the array of Transformer objects
*
* @return Transformer[] Associative array of Transformer objects (format char => Transformer)
*/
public function getTransformers()
{
return $this->transformers;
}
/**
* Format a DateTime using ICU dateformat pattern
*
* @param \DateTime $dateTime A DateTime object to be used to generate the formatted value
*
* @return string The formatted value
*/
public function format(\DateTime $dateTime)
{
$that = $this;
$formatted = preg_replace_callback($this->regExp, function($matches) use ($that, $dateTime) {
return $that->formatReplace($matches[0], $dateTime);
}, $this->pattern);
return $formatted;
}
/**
* Return the formatted ICU value for the matched date characters
*
* @param string $dateChars The date characters to be replaced with a formatted ICU value
* @param DateTime $dateTime A DateTime object to be used to generate the formatted value
*
* @return string The formatted value
*
* @throws NotImplementedException When it encounters a not implemented date character
*/
public function formatReplace($dateChars, $dateTime)
{
$length = strlen($dateChars);
if ($this->isQuoteMatch($dateChars)) {
return $this->replaceQuoteMatch($dateChars);
}
if (isset($this->transformers[$dateChars[0]])) {
$transformer = $this->transformers[$dateChars[0]];
return $transformer->format($dateTime, $length);
}
// handle unimplemented characters
if (false !== strpos($this->notImplementedChars, $dateChars[0])) {
throw new NotImplementedException(sprintf("Unimplemented date character '%s' in format '%s'", $dateChars[0], $this->pattern));
}
}
/**
* Parse a pattern based string to a timestamp value
*
* @param \DateTime $dateTime A configured DateTime object to use to perform the date calculation
* @param string $value String to convert to a time value
*
* @return int The corresponding Unix timestamp
*
* @throws \InvalidArgumentException When the value can not be matched with pattern
*/
public function parse(\DateTime $dateTime, $value)
{
$reverseMatchingRegExp = $this->getReverseMatchingRegExp($this->pattern);
$reverseMatchingRegExp = '/^'.$reverseMatchingRegExp.'$/';
$options = array();
if (preg_match($reverseMatchingRegExp, $value, $matches)) {
$matches = $this->normalizeArray($matches);
foreach ($this->transformers as $char => $transformer) {
if (isset($matches[$char])) {
$length = strlen($matches[$char]['pattern']);
$options = array_merge($options, $transformer->extractDateOptions($matches[$char]['value'], $length));
}
}
// reset error code and message
StubIntlGlobals::setError(StubIntlGlobals::U_ZERO_ERROR);
return $this->calculateUnixTimestamp($dateTime, $options);
}
// behave like the intl extension
StubIntlGlobals::setError(StubIntlGlobals::U_PARSE_ERROR, 'Date parsing failed');
return false;
}
/**
* Retrieve a regular expression to match with a formatted value.
*
* @param string $pattern The pattern to create the reverse matching regular expression
*
* @return string The reverse matching regular expression with named captures being formed by the
* transformer index in the $transformer array
*/
public function getReverseMatchingRegExp($pattern)
{
$that = $this;
$escapedPattern = preg_quote($pattern, '/');
// ICU 4.8 recognizes slash ("/") in a value to be parsed as a dash ("-") and vice-versa
// when parsing a date/time value
$escapedPattern = preg_replace('/\\\[\-|\/]/', '[\/\-]', $escapedPattern);
$reverseMatchingRegExp = preg_replace_callback($this->regExp, function($matches) use ($that) {
$length = strlen($matches[0]);
$transformerIndex = $matches[0][0];
$dateChars = $matches[0];
if ($that->isQuoteMatch($dateChars)) {
return $that->replaceQuoteMatch($dateChars);
}
$transformers = $that->getTransformers();
if (isset($transformers[$transformerIndex])) {
$transformer = $transformers[$transformerIndex];
$captureName = str_repeat($transformerIndex, $length);
return "(?P<$captureName>".$transformer->getReverseMatchingRegExp($length).')';
}
}, $escapedPattern);
return $reverseMatchingRegExp;
}
/**
* Check if the first char of a string is a single quote
*
* @param string $quoteMatch The string to check
*
* @return Boolean true if matches, false otherwise
*/
public function isQuoteMatch($quoteMatch)
{
return ("'" === $quoteMatch[0]);
}
/**
* Replaces single quotes at the start or end of a string with two single quotes
*
* @param string $quoteMatch The string to replace the quotes
*
* @return string A string with the single quotes replaced
*/
public function replaceQuoteMatch($quoteMatch)
{
if (preg_match("/^'+$/", $quoteMatch)) {
return str_replace("''", "'", $quoteMatch);
}
return str_replace("''", "'", substr($quoteMatch, 1, -1));
}
/**
* Builds a chars match regular expression
*
* @param string $specialChars A string of chars to build the regular expression
*
* @return string The chars match regular expression
*/
protected function buildCharsMatch($specialChars)
{
$specialCharsArray = str_split($specialChars);
$specialCharsMatch = implode('|', array_map(function($char) {
return $char.'+';
}, $specialCharsArray));
return $specialCharsMatch;
}
/**
* Normalize a preg_replace match array, removing the numeric keys and returning an associative array
* with the value and pattern values for the matched Transformer
*
* @param array $data
*
* @return array
*/
protected function normalizeArray(array $data)
{
$ret = array();
foreach ($data as $key => $value) {
if (!is_string($key)) {
continue;
}
$ret[$key[0]] = array(
'value' => $value,
'pattern' => $key
);
}
return $ret;
}
/**
* Calculates the Unix timestamp based on the matched values by the reverse matching regular
* expression of parse()
*
* @param \DateTime $dateTime The DateTime object to be used to calculate the timestamp
* @param array $options An array with the matched values to be used to calculate the timestamp
*
* @return Boolean|int The calculated timestamp or false if matched date is invalid
*/
protected function calculateUnixTimestamp(\DateTime $dateTime, array $options)
{
$options = $this->getDefaultValueForOptions($options);
$year = $options['year'];
$month = $options['month'];
$day = $options['day'];
$hour = $options['hour'];
$hourInstance = $options['hourInstance'];
$minute = $options['minute'];
$second = $options['second'];
$marker = $options['marker'];
$timezone = $options['timezone'];
// If month is false, return immediately (intl behavior)
if (false === $month) {
StubIntlGlobals::setError(StubIntlGlobals::U_PARSE_ERROR, 'Date parsing failed');
return false;
}
// Normalize hour
if ($hourInstance instanceof HourTransformer) {
$hour = $hourInstance->normalizeHour($hour, $marker);
}
// Set the timezone if different from the default one
if (null !== $timezone && $timezone !== $this->timezone) {
$dateTime->setTimezone(new \DateTimeZone($timezone));
}
// Normalize yy year
preg_match_all($this->regExp, $this->pattern, $matches);
if (in_array('yy', $matches[0])) {
$dateTime->setTimestamp(time());
$year = $year > $dateTime->format('y') + 20 ? 1900 + $year : 2000 + $year;
}
$dateTime->setDate($year, $month, $day);
$dateTime->setTime($hour, $minute, $second);
return $dateTime->getTimestamp();
}
/**
* Add sensible default values for missing items in the extracted date/time options array. The values
* are base in the beginning of the Unix era
*
* @param array $options
*
* @return array
*/
private function getDefaultValueForOptions(array $options)
{
return array(
'year' => isset($options['year']) ? $options['year'] : 1970,
'month' => isset($options['month']) ? $options['month'] : 1,
'day' => isset($options['day']) ? $options['day'] : 1,
'hour' => isset($options['hour']) ? $options['hour'] : 0,
'hourInstance' => isset($options['hourInstance']) ? $options['hourInstance'] : null,
'minute' => isset($options['minute']) ? $options['minute'] : 0,
'second' => isset($options['second']) ? $options['second'] : 0,
'marker' => isset($options['marker']) ? $options['marker'] : null,
'timezone' => isset($options['timezone']) ? $options['timezone'] : null,
);
}
}

View File

@ -0,0 +1,62 @@
<?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\Intl\DateFormatter\DateFormat;
/**
* Parser and formatter for 12 hour format (0-11)
*
* @author Igor Wiedler <igor@wiedler.ch>
*/
class Hour1200Transformer extends HourTransformer
{
/**
* {@inheritDoc}
*/
public function format(\DateTime $dateTime, $length)
{
$hourOfDay = $dateTime->format('g');
$hourOfDay = '12' == $hourOfDay ? '0' : $hourOfDay;
return $this->padLeft($hourOfDay, $length);
}
/**
* {@inheritDoc}
*/
public function normalizeHour($hour, $marker = null)
{
if ('PM' === $marker) {
$hour += 12;
}
return $hour;
}
/**
* {@inheritDoc}
*/
public function getReverseMatchingRegExp($length)
{
return '\d{1,2}';
}
/**
* {@inheritDoc}
*/
public function extractDateOptions($matched, $length)
{
return array(
'hour' => (int) $matched,
'hourInstance' => $this
);
}
}

View File

@ -0,0 +1,62 @@
<?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\Intl\DateFormatter\DateFormat;
/**
* Parser and formatter for 12 hour format (1-12)
*
* @author Igor Wiedler <igor@wiedler.ch>
*/
class Hour1201Transformer extends HourTransformer
{
/**
* {@inheritDoc}
*/
public function format(\DateTime $dateTime, $length)
{
return $this->padLeft($dateTime->format('g'), $length);
}
/**
* {@inheritDoc}
*/
public function normalizeHour($hour, $marker = null)
{
if ('PM' !== $marker && 12 === $hour) {
$hour = 0;
} elseif ('PM' === $marker && 12 !== $hour) {
// If PM and hour is not 12 (1-12), sum 12 hour
$hour += 12;
}
return $hour;
}
/**
* {@inheritDoc}
*/
public function getReverseMatchingRegExp($length)
{
return '\d{1,2}';
}
/**
* {@inheritDoc}
*/
public function extractDateOptions($matched, $length)
{
return array(
'hour' => (int) $matched,
'hourInstance' => $this
);
}
}

View File

@ -0,0 +1,61 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\DateFormatter\DateFormat;
/**
* Parser and formatter for 24 hour format (0-23)
*
* @author Igor Wiedler <igor@wiedler.ch>
*/
class Hour2400Transformer extends HourTransformer
{
/**
* {@inheritDoc}
*/
public function format(\DateTime $dateTime, $length)
{
return $this->padLeft($dateTime->format('G'), $length);
}
/**
* {@inheritDoc}
*/
public function normalizeHour($hour, $marker = null)
{
if ('AM' == $marker) {
$hour = 0;
} elseif ('PM' == $marker) {
$hour = 12;
}
return $hour;
}
/**
* {@inheritDoc}
*/
public function getReverseMatchingRegExp($length)
{
return '\d{1,2}';
}
/**
* {@inheritDoc}
*/
public function extractDateOptions($matched, $length)
{
return array(
'hour' => (int) $matched,
'hourInstance' => $this
);
}
}

View File

@ -0,0 +1,64 @@
<?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\Intl\DateFormatter\DateFormat;
/**
* Parser and formatter for 24 hour format (1-24)
*
* @author Igor Wiedler <igor@wiedler.ch>
*/
class Hour2401Transformer extends HourTransformer
{
/**
* {@inheritDoc}
*/
public function format(\DateTime $dateTime, $length)
{
$hourOfDay = $dateTime->format('G');
$hourOfDay = ('0' == $hourOfDay) ? '24' : $hourOfDay;
return $this->padLeft($hourOfDay, $length);
}
/**
* {@inheritDoc}
*/
public function normalizeHour($hour, $marker = null)
{
if ((null === $marker && 24 === $hour) || 'AM' == $marker) {
$hour = 0;
} elseif ('PM' == $marker) {
$hour = 12;
}
return $hour;
}
/**
* {@inheritDoc}
*/
public function getReverseMatchingRegExp($length)
{
return '\d{1,2}';
}
/**
* {@inheritDoc}
*/
public function extractDateOptions($matched, $length)
{
return array(
'hour' => (int) $matched,
'hourInstance' => $this
);
}
}

View File

@ -0,0 +1,30 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\DateFormatter\DateFormat;
/**
* Base class for hour transformers
*
* @author Eriksen Costa <eriksen.costa@infranology.com.br>
*/
abstract class HourTransformer extends Transformer
{
/**
* Returns a normalized hour value suitable for the hour transformer type
*
* @param int $hour The hour value
* @param string $marker An optional AM/PM marker
*
* @return int The normalized hour value
*/
abstract public function normalizeHour($hour, $marker = null);
}

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\Intl\DateFormatter\DateFormat;
/**
* Parser and formatter for minute format
*
* @author Igor Wiedler <igor@wiedler.ch>
*/
class MinuteTransformer extends Transformer
{
/**
* {@inheritDoc}
*/
public function format(\DateTime $dateTime, $length)
{
$minuteOfHour = (int) $dateTime->format('i');
return $this->padLeft($minuteOfHour, $length);
}
/**
* {@inheritDoc}
*/
public function getReverseMatchingRegExp($length)
{
return 1 === $length ? '\d{1,2}' : '\d{'.$length.'}';
}
/**
* {@inheritDoc}
*/
public function extractDateOptions($matched, $length)
{
return array(
'minute' => (int) $matched,
);
}
}

View File

@ -0,0 +1,143 @@
<?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\Intl\DateFormatter\DateFormat;
/**
* Parser and formatter for month format
*
* @author Igor Wiedler <igor@wiedler.ch>
*/
class MonthTransformer extends Transformer
{
/**
* @var array
*/
protected static $months = array(
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December'
);
/**
* Short months names (first 3 letters)
* @var array
*/
protected static $shortMonths = array();
/**
* Flipped $months array, $name => $index
* @var array
*/
protected static $flippedMonths = array();
/**
* Flipped $shortMonths array, $name => $index
* @var array
*/
protected static $flippedShortMonths = array();
/**
* Constructor
*/
public function __construct()
{
if (0 === count(self::$shortMonths)) {
self::$shortMonths = array_map(function($month) {
return substr($month, 0, 3);
}, self::$months);
self::$flippedMonths = array_flip(self::$months);
self::$flippedShortMonths = array_flip(self::$shortMonths);
}
}
/**
* {@inheritDoc}
*/
public function format(\DateTime $dateTime, $length)
{
$matchLengthMap = array(
1 => 'n',
2 => 'm',
3 => 'M',
4 => 'F',
);
if (isset($matchLengthMap[$length])) {
return $dateTime->format($matchLengthMap[$length]);
}
if (5 === $length) {
return substr($dateTime->format('M'), 0, 1);
}
return $this->padLeft($dateTime->format('m'), $length);
}
/**
* {@inheritDoc}
*/
public function getReverseMatchingRegExp($length)
{
switch ($length) {
case 1:
$regExp = '\d{1,2}';
break;
case 3:
$regExp = implode('|', self::$shortMonths);
break;
case 4:
$regExp = implode('|', self::$months);
break;
case 5:
$regExp = '[JFMASOND]';
break;
default:
$regExp = '\d{'.$length.'}';
break;
}
return $regExp;
}
/**
* {@inheritDoc}
*/
public function extractDateOptions($matched, $length)
{
if (!is_numeric($matched)) {
if (3 === $length) {
$matched = self::$flippedShortMonths[$matched] + 1;
} elseif (4 === $length) {
$matched = self::$flippedMonths[$matched] + 1;
} elseif (5 === $length) {
// IntlDateFormatter::parse() always returns false for MMMMM or LLLLL
$matched = false;
}
} else {
$matched = (int) $matched;
}
return array(
'month' => $matched,
);
}
}

View File

@ -0,0 +1,64 @@
<?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\Intl\DateFormatter\DateFormat;
/**
* Parser and formatter for quarter format
*
* @author Igor Wiedler <igor@wiedler.ch>
*/
class QuarterTransformer extends Transformer
{
/**
* {@inheritDoc}
*/
public function format(\DateTime $dateTime, $length)
{
$month = (int) $dateTime->format('n');
$quarter = (int) floor(($month - 1) / 3) + 1;
switch ($length) {
case 1:
case 2:
return $this->padLeft($quarter, $length);
case 3:
return 'Q'.$quarter;
default:
$map = array(1 => '1st quarter', 2 => '2nd quarter', 3 => '3rd quarter', 4 => '4th quarter');
return $map[$quarter];
}
}
/**
* {@inheritDoc}
*/
public function getReverseMatchingRegExp($length)
{
switch ($length) {
case 1:
case 2:
return '\d{'.$length.'}';
case 3:
return 'Q\d';
default:
return '(?:1st|2nd|3rd|4th) quarter';
}
}
/**
* {@inheritDoc}
*/
public function extractDateOptions($matched, $length)
{
return array();
}
}

View File

@ -0,0 +1,48 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\DateFormatter\DateFormat;
/**
* Parser and formatter for the second format
*
* @author Igor Wiedler <igor@wiedler.ch>
*/
class SecondTransformer extends Transformer
{
/**
* {@inheritDoc}
*/
public function format(\DateTime $dateTime, $length)
{
$secondOfMinute = (int) $dateTime->format('s');
return $this->padLeft($secondOfMinute, $length);
}
/**
* {@inheritDoc}
*/
public function getReverseMatchingRegExp($length)
{
return 1 === $length ? '\d{1,2}' : '\d{'.$length.'}';
}
/**
* {@inheritDoc}
*/
public function extractDateOptions($matched, $length)
{
return array(
'second' => (int) $matched,
);
}
}

View File

@ -0,0 +1,99 @@
<?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\Intl\DateFormatter\DateFormat;
use Symfony\Component\Intl\Exception\NotImplementedException;
/**
* Parser and formatter for time zone format
*
* @author Igor Wiedler <igor@wiedler.ch>
*/
class TimeZoneTransformer extends Transformer
{
/**
* {@inheritDoc}
*
* @throws NotImplementedException When time zone is different than UTC or GMT (Etc/GMT)
*/
public function format(\DateTime $dateTime, $length)
{
$timeZone = substr($dateTime->getTimezone()->getName(), 0, 3);
if (!in_array($timeZone, array('Etc', 'UTC'))) {
throw new NotImplementedException('Time zone different than GMT or UTC is not supported as a formatting output.');
}
// From ICU >= 4.8, the zero offset is not more used, example: GMT instead of GMT+00:00
$format = (0 !== (int) $dateTime->format('O')) ? '\G\M\TP' : '\G\M\T';
return $dateTime->format($format);
}
/**
* {@inheritDoc}
*/
public function getReverseMatchingRegExp($length)
{
return 'GMT[+-]\d{2}:?\d{2}';
}
/**
* {@inheritDoc}
*/
public function extractDateOptions($matched, $length)
{
return array(
'timezone' => self::getEtcTimeZoneId($matched)
);
}
/**
* Get an Etc/GMT timezone identifier for the specified timezone
*
* The PHP documentation for timezones states to not use the 'Other' time zones because them exists
* "for backwards compatibility". However all Etc/GMT time zones are in the tz database 'etcetera' file,
* which indicates they are not deprecated (neither are old names).
*
* Only GMT, Etc/Universal, Etc/Zulu, Etc/Greenwich, Etc/GMT-0, Etc/GMT+0 and Etc/GMT0 are old names and
* are linked to Etc/GMT or Etc/UTC.
*
* @param string $formattedTimeZone A GMT timezone string (GMT-03:00, e.g.)
*
* @return string A timezone identifier
*
* @see http://php.net/manual/en/timezones.others.php
* @see http://www.twinsun.com/tz/tz-link.htm
*
* @throws NotImplementedException When the GMT time zone have minutes offset different than zero
* @throws \InvalidArgumentException When the value can not be matched with pattern
*/
public static function getEtcTimeZoneId($formattedTimeZone)
{
if (preg_match('/GMT(?P<signal>[+-])(?P<hours>\d{2}):?(?P<minutes>\d{2})/', $formattedTimeZone, $matches)) {
$hours = (int) $matches['hours'];
$minutes = (int) $matches['minutes'];
$signal = $matches['signal'] == '-' ? '+' : '-';
if (0 < $minutes) {
throw new NotImplementedException(sprintf(
'It is not possible to use a GMT time zone with minutes offset different than zero (0). GMT time zone tried: %s.',
$formattedTimeZone
));
}
return 'Etc/GMT'.($hours !== 0 ? $signal.$hours : '');
}
throw new \InvalidArgumentException('The GMT time zone \'%s\' does not match with the supported formats GMT[+-]HH:MM or GMT[+-]HHMM.');
}
}

View File

@ -0,0 +1,64 @@
<?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\Intl\DateFormatter\DateFormat;
/**
* Parser and formatter for date formats
*
* @author Igor Wiedler <igor@wiedler.ch>
*/
abstract class Transformer
{
/**
* Format a value using a configured DateTime as date/time source
*
*
* @param \DateTime $dateTime A DateTime object to be used to generate the formatted value
* @param int $length The formatted value string length
*
* @return string The formatted value
*/
abstract public function format(\DateTime $dateTime, $length);
/**
* Returns a reverse matching regular expression of a string generated by format()
*
* @param int $length The length of the value to be reverse matched
*
* @return string The reverse matching regular expression
*/
abstract public function getReverseMatchingRegExp($length);
/**
* Extract date options from a matched value returned by the processing of the reverse matching
* regular expression
*
* @param string $matched The matched value
* @param int $length The length of the Transformer pattern string
*
* @return array An associative array
*/
abstract public function extractDateOptions($matched, $length);
/**
* Pad a string with zeros to the left
*
* @param string $value The string to be padded
* @param int $length The length to pad
*
* @return string The padded string
*/
protected function padLeft($value, $length)
{
return str_pad($value, $length, '0', STR_PAD_LEFT);
}
}

View File

@ -0,0 +1,50 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\DateFormatter\DateFormat;
/**
* Parser and formatter for year format
*
* @author Igor Wiedler <igor@wiedler.ch>
*/
class YearTransformer extends Transformer
{
/**
* {@inheritDoc}
*/
public function format(\DateTime $dateTime, $length)
{
if (2 === $length) {
return $dateTime->format('y');
}
return $this->padLeft($dateTime->format('Y'), $length);
}
/**
* {@inheritDoc}
*/
public function getReverseMatchingRegExp($length)
{
return 2 === $length ? '\d{2}' : '\d{4}';
}
/**
* {@inheritDoc}
*/
public function extractDateOptions($matched, $length)
{
return array(
'year' => (int) $matched,
);
}
}

View File

@ -0,0 +1,601 @@
<?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\Intl\DateFormatter;
use Symfony\Component\Intl\Globals\StubIntlGlobals;
use Symfony\Component\Intl\DateFormatter\DateFormat\FullTransformer;
use Symfony\Component\Intl\Exception\MethodNotImplementedException;
use Symfony\Component\Intl\Exception\MethodArgumentNotImplementedException;
use Symfony\Component\Intl\Exception\MethodArgumentValueNotImplementedException;
use Symfony\Component\Intl\Locale\StubLocale;
/**
* Provides a stub IntlDateFormatter for the 'en' locale.
*
* @author Igor Wiedler <igor@wiedler.ch>
*/
class StubIntlDateFormatter
{
/**
* The error code from the last operation
*
* @var integer
*/
protected $errorCode = StubIntlGlobals::U_ZERO_ERROR;
/**
* The error message from the last operation
*
* @var string
*/
protected $errorMessage = 'U_ZERO_ERROR';
/* date/time format types */
const NONE = -1;
const FULL = 0;
const LONG = 1;
const MEDIUM = 2;
const SHORT = 3;
/* calendar formats */
const TRADITIONAL = 0;
const GREGORIAN = 1;
/**
* Patterns used to format the date when no pattern is provided
*
* @var array
*/
private $defaultDateFormats = array(
self::NONE => '',
self::FULL => 'EEEE, LLLL d, y',
self::LONG => 'LLLL d, y',
self::MEDIUM => 'LLL d, y',
self::SHORT => 'M/d/yy',
);
/**
* Patterns used to format the time when no pattern is provided
*
* @var array
*/
private $defaultTimeFormats = array(
self::FULL => 'h:mm:ss a zzzz',
self::LONG => 'h:mm:ss a z',
self::MEDIUM => 'h:mm:ss a',
self::SHORT => 'h:mm a',
);
/**
* @var int
*/
private $datetype;
/**
* @var int
*/
private $timetype;
/**
* @var string
*/
private $pattern;
/**
* @var \DateTimeZone
*/
private $dateTimeZone;
/**
* @var Boolean
*/
private $unitializedTimeZoneId = false;
/**
* @var string
*/
private $timeZoneId;
/**
* Constructor
*
* @param string $locale The locale code
* @param int $datetype Type of date formatting, one of the format type constants
* @param int $timetype Type of time formatting, one of the format type constants
* @param string $timezone Timezone identifier
* @param int $calendar Calendar to use for formatting or parsing; default is Gregorian.
* One of the calendar constants.
* @param string $pattern Optional pattern to use when formatting
*
* @see http://www.php.net/manual/en/intldateformatter.create.php
* @see http://userguide.icu-project.org/formatparse/datetime
*
* @throws MethodArgumentValueNotImplementedException When $locale different than 'en' is passed
* @throws MethodArgumentValueNotImplementedException When $calendar different than GREGORIAN is passed
*/
public function __construct($locale, $datetype, $timetype, $timezone = null, $calendar = self::GREGORIAN, $pattern = null)
{
if ('en' !== $locale) {
throw new MethodArgumentValueNotImplementedException(__METHOD__, 'locale', $locale, 'Only the \'en\' locale is supported');
}
if (self::GREGORIAN !== $calendar) {
throw new MethodArgumentValueNotImplementedException(__METHOD__, 'calendar', $calendar, 'Only the GREGORIAN calendar is supported');
}
$this->datetype = $datetype;
$this->timetype = $timetype;
$this->setPattern($pattern);
$this->setTimeZoneId($timezone);
}
/**
* Static constructor
*
* @param string $locale The locale code
* @param int $datetype Type of date formatting, one of the format type constants
* @param int $timetype Type of time formatting, one of the format type constants
* @param string $timezone Timezone identifier
* @param int $calendar Calendar to use for formatting or parsing; default is Gregorian.
* One of the calendar constants.
* @param string $pattern Optional pattern to use when formatting
*
* @return StubIntlDateFormatter
*
* @see http://www.php.net/manual/en/intldateformatter.create.php
* @see http://userguide.icu-project.org/formatparse/datetime
*
* @throws MethodArgumentValueNotImplementedException When $locale different than 'en' is passed
*/
public static function create($locale, $datetype, $timetype, $timezone = null, $calendar = self::GREGORIAN, $pattern = null)
{
return new self($locale, $datetype, $timetype, $timezone, $calendar, $pattern);
}
/**
* Format the date/time value (timestamp) as a string
*
* @param mixed $timestamp Unix timestamp to format
*
* @return string The formatted value
*
* @see http://www.php.net/manual/en/intldateformatter.format.php
*
* @throws MethodArgumentValueNotImplementedException If one of the formatting characters is not implemented
*/
public function format($timestamp)
{
// intl allows timestamps to be passed as arrays - we don't
if (is_array($timestamp)) {
$message = version_compare(PHP_VERSION, '5.3.4', '>=') ?
'Only integer unix timestamps and DateTime objects are supported' :
'Only integer unix timestamps are supported';
throw new MethodArgumentValueNotImplementedException(__METHOD__, 'timestamp', $timestamp, $message);
}
// behave like the intl extension
$argumentError = null;
if (version_compare(PHP_VERSION, '5.3.4', '<') && !is_int($timestamp)) {
$argumentError = 'datefmt_format: takes either an array or an integer timestamp value ';
} elseif (version_compare(PHP_VERSION, '5.3.4', '>=') && !is_int($timestamp) && !$timestamp instanceof \DateTime) {
$argumentError = 'datefmt_format: takes either an array or an integer timestamp value or a DateTime object';
if (version_compare(PHP_VERSION, '5.5.0-dev', '>=') && !is_int($timestamp)) {
$argumentError = sprintf('datefmt_format: string \'%s\' is not numeric, which would be required for it to be a valid date', $timestamp);
}
}
if (null !== $argumentError) {
StubIntlGlobals::setError(StubIntlGlobals::U_ILLEGAL_ARGUMENT_ERROR, $argumentError);
$this->errorCode = StubIntlGlobals::getErrorCode();
$this->errorMessage = StubIntlGlobals::getErrorMessage();
return false;
}
// As of PHP 5.3.4, IntlDateFormatter::format() accepts DateTime instances
if (version_compare(PHP_VERSION, '5.3.4', '>=') && $timestamp instanceof \DateTime) {
$timestamp = $timestamp->getTimestamp();
}
$transformer = new FullTransformer($this->getPattern(), $this->getTimeZoneId());
$formatted = $transformer->format($this->createDateTime($timestamp));
// behave like the intl extension
StubIntlGlobals::setError(StubIntlGlobals::U_ZERO_ERROR);
$this->errorCode = StubIntlGlobals::getErrorCode();
$this->errorMessage = StubIntlGlobals::getErrorMessage();
return $formatted;
}
/**
* Formats an object
*
* @param object $object
* @param mixed $format
* @param string $locale
*
* @return string The formatted value
*
* @see http://www.php.net/manual/en/intldateformatter.formatobject.php
*
* @throws MethodNotImplementedException
*/
public function formatObject($object, $format = null, $locale = null)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Returns the formatter's calendar
*
* @return int The calendar being used by the formatter
*
* @see http://www.php.net/manual/en/intldateformatter.getcalendar.php
*/
public function getCalendar()
{
return self::GREGORIAN;
}
/**
* Returns the formatter's calendar object
*
* @return object The calendar's object being used by the formatter
*
* @see http://www.php.net/manual/en/intldateformatter.getcalendarobject.php
*
* @throws MethodNotImplementedException
*/
public function getCalendarObject()
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Returns the formatter's datetype
*
* @return int The current value of the formatter
*
* @see http://www.php.net/manual/en/intldateformatter.getdatetype.php
*/
public function getDateType()
{
return $this->datetype;
}
/**
* Returns formatter's last error code. Always returns the U_ZERO_ERROR class constant value
*
* @return int The error code from last formatter call
*
* @see http://www.php.net/manual/en/intldateformatter.geterrorcode.php
*/
public function getErrorCode()
{
return $this->errorCode;
}
/**
* Returns formatter's last error message. Always returns the U_ZERO_ERROR_MESSAGE class constant value
*
* @return string The error message from last formatter call
*
* @see http://www.php.net/manual/en/intldateformatter.geterrormessage.php
*/
public function getErrorMessage()
{
return $this->errorMessage;
}
/**
* Returns the formatter's locale
*
* @param int $type The locale name type to return between valid or actual (StubLocale::VALID_LOCALE or StubLocale::ACTUAL_LOCALE, respectively)
*
* @return string The locale name used to create the formatter
*
* @see http://www.php.net/manual/en/intldateformatter.getlocale.php
*/
public function getLocale($type = StubLocale::ACTUAL_LOCALE)
{
return 'en';
}
/**
* Returns the formatter's pattern
*
* @return string The pattern string used by the formatter
*
* @see http://www.php.net/manual/en/intldateformatter.getpattern.php
*/
public function getPattern()
{
return $this->pattern;
}
/**
* Returns the formatter's time type
*
* @return string The time type used by the formatter
*
* @see http://www.php.net/manual/en/intldateformatter.gettimetype.php
*/
public function getTimeType()
{
return $this->timetype;
}
/**
* Returns the formatter's timezone identifier
*
* @return string The timezone identifier used by the formatter
*
* @see http://www.php.net/manual/en/intldateformatter.gettimezoneid.php
*/
public function getTimeZoneId()
{
if (!$this->unitializedTimeZoneId) {
return $this->timeZoneId;
}
// In PHP 5.5 default timezone depends on `date_default_timezone_get()` method
if (version_compare(PHP_VERSION, '5.5.0-dev', '>=')) {
return date_default_timezone_get();
}
return null;
}
/**
* Returns the formatter's timezone
*
* @return mixed The timezone used by the formatter
*
* @see http://www.php.net/manual/en/intldateformatter.gettimezone.php
*
* @throws MethodNotImplementedException
*/
public function getTimeZone()
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Returns whether the formatter is lenient
*
* @return Boolean
*
* @see http://www.php.net/manual/en/intldateformatter.islenient.php
*
* @throws MethodNotImplementedException
*/
public function isLenient()
{
return false;
}
/**
* Parse string to a field-based time value
*
* @param string $value String to convert to a time value
* @param int $position Position at which to start the parsing in $value (zero-based).
* If no error occurs before $value is consumed, $parse_pos will
* contain -1 otherwise it will contain the position at which parsing
* ended. If $parse_pos > strlen($value), the parse fails immediately.
*
* @return string Localtime compatible array of integers: contains 24 hour clock value in tm_hour field
*
* @see http://www.php.net/manual/en/intldateformatter.localtime.php
*
* @throws MethodNotImplementedException
*/
public function localtime($value, &$position = 0)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Parse string to a timestamp value
*
* @param string $value String to convert to a time value
* @param int $position Position at which to start the parsing in $value (zero-based).
* If no error occurs before $value is consumed, $parse_pos will
* contain -1 otherwise it will contain the position at which parsing
* ended. If $parse_pos > strlen($value), the parse fails immediately.
*
* @return string Parsed value as a timestamp
*
* @see http://www.php.net/manual/en/intldateformatter.parse.php
*
* @throws MethodArgumentNotImplementedException When $position different than null, behavior not implemented
*/
public function parse($value, &$position = null)
{
// We don't calculate the position when parsing the value
if (null !== $position) {
throw new MethodArgumentNotImplementedException(__METHOD__, 'position');
}
$dateTime = $this->createDateTime(0);
$transformer = new FullTransformer($this->getPattern(), $this->getTimeZoneId());
$timestamp = $transformer->parse($dateTime, $value);
// behave like the intl extension. FullTransformer::parse() set the proper error
$this->errorCode = StubIntlGlobals::getErrorCode();
$this->errorMessage = StubIntlGlobals::getErrorMessage();
return $timestamp;
}
/**
* Set the formatter's calendar
*
* @param string $calendar The calendar to use. Default is IntlDateFormatter::GREGORIAN.
*
* @return Boolean true on success or false on failure
*
* @see http://www.php.net/manual/en/intldateformatter.setcalendar.php
*
* @throws MethodNotImplementedException
*/
public function setCalendar($calendar)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Set the leniency of the parser
*
* Define if the parser is strict or lenient in interpreting inputs that do not match the pattern
* exactly. Enabling lenient parsing allows the parser to accept otherwise flawed date or time
* patterns, parsing as much as possible to obtain a value. Extra space, unrecognized tokens, or
* invalid values ("February 30th") are not accepted.
*
* @param Boolean $lenient Sets whether the parser is lenient or not, default is false (strict)
*
* @return Boolean true on success or false on failure
*
* @see http://www.php.net/manual/en/intldateformatter.setlenient.php
*
* @throws MethodArgumentValueNotImplementedException When $lenient is true
*/
public function setLenient($lenient)
{
if ($lenient) {
throw new MethodArgumentValueNotImplementedException(__METHOD__, 'lenient', $lenient, 'Only the strict parser is supported');
}
}
/**
* Set the formatter's pattern
*
* @param string $pattern A pattern string in conformance with the ICU IntlDateFormatter documentation
*
* @return Boolean true on success or false on failure
*
* @see http://www.php.net/manual/en/intldateformatter.setpattern.php
* @see http://userguide.icu-project.org/formatparse/datetime
*/
public function setPattern($pattern)
{
if (null === $pattern) {
$pattern = $this->getDefaultPattern();
}
$this->pattern = $pattern;
}
/**
* Set the formatter's timezone identifier
*
* @param string $timeZoneId The time zone ID string of the time zone to use.
* If NULL or the empty string, the default time zone for the
* runtime is used.
*
* @return Boolean true on success or false on failure
*
* @see http://www.php.net/manual/en/intldateformatter.settimezoneid.php
*/
public function setTimeZoneId($timeZoneId)
{
if (null === $timeZoneId) {
// In PHP 5.5 if $timeZoneId is null it fallbacks to `date_default_timezone_get()` method
if (version_compare(PHP_VERSION, '5.5.0-dev', '>=')) {
$timeZoneId = date_default_timezone_get();
} else {
// TODO: changes were made to ext/intl in PHP 5.4.4 release that need to be investigated since it will
// use ini's date.timezone when the time zone is not provided. As a not well tested workaround, uses UTC.
// See the first two items of the commit message for more information:
// https://github.com/php/php-src/commit/eb346ef0f419b90739aadfb6cc7b7436c5b521d9
$timeZoneId = getenv('TZ') ?: 'UTC';
}
$this->unitializedTimeZoneId = true;
}
// Backup original passed time zone
$timeZone = $timeZoneId;
// Get an Etc/GMT time zone that is accepted for \DateTimeZone
if ('GMT' !== $timeZoneId && 0 === strpos($timeZoneId, 'GMT')) {
try {
$timeZoneId = DateFormat\TimeZoneTransformer::getEtcTimeZoneId($timeZoneId);
} catch (\InvalidArgumentException $e) {
// Does nothing, will fallback to UTC
}
}
try {
$this->dateTimeZone = new \DateTimeZone($timeZoneId);
} catch (\Exception $e) {
$this->dateTimeZone = new \DateTimeZone('UTC');
}
$this->timeZoneId = $timeZone;
return true;
}
/**
* This method was added in PHP 5.5 as replacement for `setTimeZoneId()`
*
* @param mixed $timeZone
*
* @return Boolean true on success or false on failure
*
* @see http://www.php.net/manual/en/intldateformatter.settimezone.php
*/
public function setTimeZone($timeZone)
{
return $this->setTimeZoneId($timeZone);
}
/**
* Create and returns a DateTime object with the specified timestamp and with the
* current time zone
*
* @param int $timestamp
*
* @return \DateTime
*/
protected function createDateTime($timestamp)
{
$dateTime = new \DateTime();
$dateTime->setTimestamp($timestamp);
$dateTime->setTimezone($this->dateTimeZone);
return $dateTime;
}
/**
* Returns a pattern string based in the datetype and timetype values
*
* @return string
*/
protected function getDefaultPattern()
{
$patternParts = array();
if (self::NONE !== $this->datetype) {
$patternParts[] = $this->defaultDateFormats[$this->datetype];
}
if (self::NONE !== $this->timetype) {
$patternParts[] = $this->defaultTimeFormats[$this->timetype];
}
$pattern = implode(' ', $patternParts);
return $pattern;
}
}

View File

@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\Exception;
/**
* Base BadMethodCallException for the Intl component.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class BadMethodCallException extends \BadMethodCallException implements ExceptionInterface
{
}

View File

@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\Exception;
/**
* Base ExceptionInterface for the Intl component.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
interface ExceptionInterface
{
}

View File

@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\Exception;
/**
* InvalidArgumentException for the Intl component.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
{
}

View File

@ -0,0 +1,32 @@
<?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\Intl\Exception;
use Symfony\Component\Intl\Exception\NotImplementedException;
/**
* @author Eriksen Costa <eriksen.costa@infranology.com.br>
*/
class MethodArgumentNotImplementedException extends NotImplementedException
{
/**
* Constructor
*
* @param string $methodName The method name that raised the exception
* @param string $argName The argument name that is not implemented
*/
public function __construct($methodName, $argName)
{
$message = sprintf('The %s() method\'s argument $%s behavior is not implemented.', $methodName, $argName);
parent::__construct($message);
}
}

View File

@ -0,0 +1,41 @@
<?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\Intl\Exception;
use Symfony\Component\Intl\Exception\NotImplementedException;
/**
* @author Eriksen Costa <eriksen.costa@infranology.com.br>
*/
class MethodArgumentValueNotImplementedException extends NotImplementedException
{
/**
* Constructor
*
* @param string $methodName The method name that raised the exception
* @param string $argName The argument name
* @param string $argValue The argument value that is not implemented
* @param string $additionalMessage An optional additional message to append to the exception message
*/
public function __construct($methodName, $argName, $argValue, $additionalMessage = '')
{
$message = sprintf(
'The %s() method\'s argument $%s value %s behavior is not implemented.%s',
$methodName,
$argName,
var_export($argValue, true),
$additionalMessage !== '' ? ' '.$additionalMessage.'. ' : ''
);
parent::__construct($message);
}
}

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\Intl\Exception;
/**
* @author Eriksen Costa <eriksen.costa@infranology.com.br>
*/
class MethodNotImplementedException extends NotImplementedException
{
/**
* Constructor
*
* @param string $methodName The name of the method
*/
public function __construct($methodName)
{
parent::__construct(sprintf('The %s() is not implemented.', $methodName));
}
}

View File

@ -0,0 +1,32 @@
<?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\Intl\Exception;
/**
* Base exception class for not implemented behaviors of the intl extension in the Locale component.
*
* @author Eriksen Costa <eriksen.costa@infranology.com.br>
*/
class NotImplementedException extends RuntimeException
{
const INTL_INSTALL_MESSAGE = 'Please install the "intl" extension for full localization capabilities.';
/**
* Constructor
*
* @param string $message The exception message. A note to install the intl extension is appended to this string
*/
public function __construct($message)
{
parent::__construct($message.' '.self::INTL_INSTALL_MESSAGE);
}
}

View File

@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\Exception;
/**
* Base OutOfBoundsException for the Intl component.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class OutOfBoundsException extends \OutOfBoundsException implements ExceptionInterface
{
}

View File

@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\Exception;
/**
* RuntimeException for the Intl component.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}

View File

@ -0,0 +1,137 @@
<?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\Intl\Globals;
/**
* Provides fake static versions of the global functions in the intl extension
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
abstract class StubIntlGlobals
{
/**
* Indicates that no error occurred
*
* @var integer
*/
const U_ZERO_ERROR = 0;
/**
* Indicates that an invalid argument was passed
*
* @var integer
*/
const U_ILLEGAL_ARGUMENT_ERROR = 1;
/**
* Indicates that the parse() operation failed
*
* @var integer
*/
const U_PARSE_ERROR = 9;
/**
* All known error codes
*
* @var array
*/
private static $errorCodes = array(
self::U_ZERO_ERROR => 'U_ZERO_ERROR',
self::U_ILLEGAL_ARGUMENT_ERROR => 'U_ILLEGAL_ARGUMENT_ERROR',
self::U_PARSE_ERROR => 'U_PARSE_ERROR',
);
/**
* The error code of the last operation
*
* @var integer
*/
private static $errorCode = self::U_ZERO_ERROR;
/**
* The error code of the last operation
*
* @var integer
*/
private static $errorMessage = 'U_ZERO_ERROR';
/**
* Returns whether the error code indicates a failure
*
* @param integer $errorCode The error code returned by StubIntlGlobals::getErrorCode()
*
* @return Boolean
*/
public static function isFailure($errorCode)
{
return isset(self::$errorCodes[$errorCode])
&& $errorCode > self::U_ZERO_ERROR;
}
/**
* Returns the error code of the last operation
*
* Returns StubIntlGlobals::U_ZERO_ERROR if no error occurred.
*
* @return integer
*/
public static function getErrorCode()
{
return self::$errorCode;
}
/**
* Returns the error message of the last operation
*
* Returns "U_ZERO_ERROR" if no error occurred.
*
* @return string
*/
public static function getErrorMessage()
{
return self::$errorMessage;
}
/**
* Returns the symbolic name for a given error code
*
* @param integer $code The error code returned by StubIntlGlobals::getErrorCode()
*
* @return string
*/
public static function getErrorName($code)
{
if (isset(self::$errorCodes[$code])) {
return self::$errorCodes[$code];
}
return '[BOGUS UErrorCode]';
}
/**
* Sets the current error
*
* @param integer $code One of the error constants in this class
* @param string $message The ICU class error message
*
* @throws \InvalidArgumentException If the code is not one of the error constants in this class
*/
public static function setError($code, $message = '')
{
if (!isset(self::$errorCodes[$code])) {
throw new \InvalidArgumentException(sprintf('No such error code: "%s"', $code));
}
self::$errorMessage = $message ? sprintf('%s: %s', $message, self::$errorCodes[$code]) : self::$errorCodes[$code];
self::$errorCode = $code;
}
}

View File

@ -0,0 +1,332 @@
<?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\Intl;
use Symfony\Component\Icu\IcuCurrencyBundle;
use Symfony\Component\Icu\IcuData;
use Symfony\Component\Icu\IcuLanguageBundle;
use Symfony\Component\Icu\IcuLocaleBundle;
use Symfony\Component\Icu\IcuRegionBundle;
use Symfony\Component\Intl\Exception\InvalidArgumentException;
use Symfony\Component\Intl\ResourceBundle\Reader\BinaryBundleReader;
use Symfony\Component\Intl\ResourceBundle\Reader\BufferedReader;
use Symfony\Component\Intl\ResourceBundle\Reader\PhpBundleReader;
use Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReader;
use Symfony\Component\Intl\ResourceBundle\Stub\StubCurrencyBundle;
use Symfony\Component\Intl\ResourceBundle\Stub\StubLanguageBundle;
use Symfony\Component\Intl\ResourceBundle\Stub\StubLocaleBundle;
use Symfony\Component\Intl\ResourceBundle\Stub\StubRegionBundle;
/**
* Gives access to internationalization data.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class Intl
{
/**
* Load data from the Icu component.
*/
const ICU = 0;
/**
* Load data from the stub files of the Intl component.
*/
const STUB = 1;
/**
* The number of resource bundles to buffer. Loading the same resource
* bundle for n locales takes up n spots in the buffer.
*/
const BUFFER_SIZE = 10;
/**
* The accepted values for the {@link $dataSource} property.
*
* @var array
*/
private static $allowedDataSources = array(
self::ICU => 'Intl::ICU',
self::STUB => 'Intl::STUB',
);
/**
* @var integer
*/
private static $dataSource;
/**
* @var ResourceBundle\CurrencyBundleInterface
*/
private static $currencyBundle;
/**
* @var ResourceBundle\LanguageBundleInterface
*/
private static $languageBundle;
/**
* @var ResourceBundle\LocaleBundleInterface
*/
private static $localeBundle;
/**
* @var ResourceBundle\RegionBundleInterface
*/
private static $regionBundle;
/**
* @var string|Boolean|null
*/
private static $icuVersion = false;
/**
* @var string
*/
private static $icuDataVersion = false;
/**
* @var ResourceBundle\Reader\StructuredBundleReaderInterface
*/
private static $phpReader;
/**
* @var ResourceBundle\Reader\StructuredBundleReaderInterface
*/
private static $binaryReader;
/**
* Returns whether the intl extension is installed.
*
* @return Boolean Returns true if the intl extension is installed, false otherwise.
*/
public static function isExtensionLoaded()
{
return IcuData::isLoadable();
}
/**
* Sets the data source from which to load the resource bundles.
*
* @param integer $dataSource One of the constants {@link Intl::ICU} or
* {@link Intl::STUB}.
*
* @throws InvalidArgumentException If the data source is invalid.
*
* @see getData>Source
*/
public static function setDataSource($dataSource)
{
if (!isset(self::$allowedDataSources[$dataSource])) {
throw new InvalidArgumentException(sprintf(
'The data sources should be one of %s',
implode(', ', self::$allowedDataSources)
));
}
if (self::ICU === $dataSource && !IcuData::isLoadable()) {
throw new InvalidArgumentException(
'The data source cannot be set to Intl::ICU if the intl ' .
'extension is not installed.'
);
}
if ($dataSource !== self::$dataSource) {
self::$currencyBundle = null;
self::$languageBundle = null;
self::$localeBundle = null;
self::$regionBundle = null;
}
self::$dataSource = $dataSource;
}
/**
* Returns the data source from which to load the resource bundles.
*
* If {@link setDataSource()} has not been called, the data source will be
* chosen depending on whether the intl extension is installed or not:
*
* * If the extension is present, the bundles will be loaded from the Icu
* component;
* * Otherwise, the bundles will be loaded from the stub files in the
* Intl component.
*
* @return integer One of the constants {@link Intl::ICU} or
* {@link Intl::STUB}.
*/
public static function getDataSource()
{
if (null === self::$dataSource) {
self::$dataSource = IcuData::isLoadable() ? self::ICU : self::STUB;
}
return self::$dataSource;
}
/**
* Returns the bundle containing currency information.
*
* @return ResourceBundle\CurrencyBundleInterface The currency resource bundle.
*/
public static function getCurrencyBundle()
{
if (null === self::$currencyBundle) {
self::$currencyBundle = self::ICU === self::getDataSource()
? new IcuCurrencyBundle(self::getBinaryReader())
: new StubCurrencyBundle(self::getPhpReader());
}
return self::$currencyBundle;
}
/**
* Returns the bundle containing language information.
*
* @return ResourceBundle\LanguageBundleInterface The language resource bundle.
*/
public static function getLanguageBundle()
{
if (null === self::$languageBundle) {
self::$languageBundle = self::ICU === self::getDataSource()
? new IcuLanguageBundle(self::getBinaryReader())
: new StubLanguageBundle(self::getPhpReader());
}
return self::$languageBundle;
}
/**
* Returns the bundle containing locale information.
*
* @return ResourceBundle\LocaleBundleInterface The locale resource bundle.
*/
public static function getLocaleBundle()
{
if (null === self::$localeBundle) {
self::$localeBundle = self::ICU === self::getDataSource()
? new IcuLocaleBundle(self::getBinaryReader())
: new StubLocaleBundle(self::getPhpReader());
}
return self::$localeBundle;
}
/**
* Returns the bundle containing region information.
*
* @return ResourceBundle\RegionBundleInterface The region resource bundle.
*/
public static function getRegionBundle()
{
if (null === self::$regionBundle) {
self::$regionBundle = self::ICU === self::getDataSource()
? new IcuRegionBundle(self::getBinaryReader())
: new StubRegionBundle(self::getPhpReader());
}
return self::$regionBundle;
}
/**
* Returns the version of the installed ICU library.
*
* @return null|string The ICU version or NULL if it could not be determined.
*/
public static function getIcuVersion()
{
if (false === self::$icuVersion) {
if (defined('INTL_ICU_VERSION')) {
self::$icuVersion = INTL_ICU_VERSION;
} else {
try {
$reflector = new \ReflectionExtension('intl');
ob_start();
$reflector->info();
$output = strip_tags(ob_get_clean());
preg_match('/^ICU version (?:=>)?(.*)$/m', $output, $matches);
self::$icuVersion = trim($matches[1]);
} catch (\ReflectionException $e) {
self::$icuVersion = null;
}
}
}
return self::$icuVersion;
}
/**
* Returns the version of the installed ICU data.
*
* @return string The version of the installed ICU data.
*/
public static function getIcuDataVersion()
{
if (false === self::$icuDataVersion) {
self::$icuDataVersion = self::ICU === self::getDataSource()
? IcuData::getVersion()
: file_get_contents(__DIR__ . '/Resources/version.txt');
}
return self::$icuDataVersion;
}
/**
* Returns the ICU version that the stub classes mimic.
*
* @return string The ICU version of the stub classes.
*/
public static function getStubIcuVersion()
{
return '50.1.0';
}
/**
* Returns a resource bundle reader for .php resource bundle files.
*
* @return ResourceBundle\Reader\StructuredBundleReaderInterface The resource reader.
*/
private static function getPhpReader()
{
if (null === self::$phpReader) {
self::$phpReader = new StructuredBundleReader(new BufferedReader(
new PhpBundleReader(),
self::BUFFER_SIZE
));
}
return self::$phpReader;
}
/**
* Returns a resource bundle reader for binary .res resource bundle files.
*
* @return ResourceBundle\Reader\StructuredBundleReaderInterface The resource reader.
*/
private static function getBinaryReader()
{
if (null === self::$binaryReader) {
self::$binaryReader = new StructuredBundleReader(new BufferedReader(
new BinaryBundleReader(),
self::BUFFER_SIZE
));
}
return self::$binaryReader;
}
/**
* This class must not be instantiated.
*/
private function __construct() {}
}

View File

@ -0,0 +1,19 @@
Copyright (c) 2004-2013 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,317 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\Locale;
use Symfony\Component\Intl\Locale;
use Symfony\Component\Intl\Exception\NotImplementedException;
use Symfony\Component\Intl\Exception\MethodNotImplementedException;
/**
* Provides a stub Locale for the 'en' locale.
*
* @author Eriksen Costa <eriksen.costa@infranology.com.br>
*/
class StubLocale
{
const DEFAULT_LOCALE = null;
/** Locale method constants */
const ACTUAL_LOCALE = 0;
const VALID_LOCALE = 1;
/** Language tags constants */
const LANG_TAG = 'language';
const EXTLANG_TAG = 'extlang';
const SCRIPT_TAG = 'script';
const REGION_TAG = 'region';
const VARIANT_TAG = 'variant';
const GRANDFATHERED_LANG_TAG = 'grandfathered';
const PRIVATE_TAG = 'private';
/**
* Returns the best available locale based on HTTP "Accept-Language" header according to RFC 2616
*
* @param string $header The string containing the "Accept-Language" header value
*
* @return string The corresponding locale code
*
* @see http://www.php.net/manual/en/locale.acceptfromhttp.php
*
* @throws MethodNotImplementedException
*/
public static function acceptFromHttp($header)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Returns a correctly ordered and delimited locale code
*
* @param array $subtags A keyed array where the keys identify the particular locale code subtag
*
* @return string The corresponding locale code
*
* @see http://www.php.net/manual/en/locale.composelocale.php
*
* @throws MethodNotImplementedException
*/
public static function composeLocale(array $subtags)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Checks if a language tag filter matches with locale
*
* @param string $langtag The language tag to check
* @param string $locale The language range to check against
* @param Boolean $canonicalize
*
* @return string The corresponding locale code
*
* @see http://www.php.net/manual/en/locale.filtermatches.php
*
* @throws MethodNotImplementedException
*/
public static function filterMatches($langtag, $locale, $canonicalize = false)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Returns the variants for the input locale
*
* @param string $locale The locale to extract the variants from
*
* @return array The locale variants
*
* @see http://www.php.net/manual/en/locale.getallvariants.php
*
* @throws MethodNotImplementedException
*/
public static function getAllVariants($locale)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Returns the default locale
*
* @return string The default locale code. Always returns 'en'
*
* @see http://www.php.net/manual/en/locale.getdefault.php
*
* @throws MethodNotImplementedException
*/
public static function getDefault()
{
return 'en';
}
/**
* Returns the localized display name for the locale language
*
* @param string $locale The locale code to return the display language from
* @param string $inLocale Optional format locale code to use to display the language name
*
* @return string The localized language display name
*
* @see http://www.php.net/manual/en/locale.getdisplaylanguage.php
*
* @throws MethodNotImplementedException
*/
public static function getDisplayLanguage($locale, $inLocale = null)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Returns the localized display name for the locale
*
* @param string $locale The locale code to return the display locale name from
* @param string $inLocale Optional format locale code to use to display the locale name
*
* @return string The localized locale display name
*
* @see http://www.php.net/manual/en/locale.getdisplayname.php
*
* @throws MethodNotImplementedException
*/
public static function getDisplayName($locale, $inLocale = null)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Returns the localized display name for the locale region
*
* @param string $locale The locale code to return the display region from
* @param string $inLocale Optional format locale code to use to display the region name
*
* @return string The localized region display name
*
* @see http://www.php.net/manual/en/locale.getdisplayregion.php
*
* @throws MethodNotImplementedException
*/
public static function getDisplayRegion($locale, $inLocale = null)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Returns the localized display name for the locale script
*
* @param string $locale The locale code to return the display script from
* @param string $inLocale Optional format locale code to use to display the script name
*
* @return string The localized script display name
*
* @see http://www.php.net/manual/en/locale.getdisplayscript.php
*
* @throws MethodNotImplementedException
*/
public static function getDisplayScript($locale, $inLocale = null)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Returns the localized display name for the locale variant
*
* @param string $locale The locale code to return the display variant from
* @param string $inLocale Optional format locale code to use to display the variant name
*
* @return string The localized variant display name
*
* @see http://www.php.net/manual/en/locale.getdisplayvariant.php
*
* @throws MethodNotImplementedException
*/
public static function getDisplayVariant($locale, $inLocale = null)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Returns the keywords for the locale
*
* @param string $locale The locale code to extract the keywords from
*
* @return array Associative array with the extracted variants
*
* @see http://www.php.net/manual/en/locale.getkeywords.php
*
* @throws MethodNotImplementedException
*/
public static function getKeywords($locale)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Returns the primary language for the locale
*
* @param string $locale The locale code to extract the language code from
*
* @return string|null The extracted language code or null in case of error
*
* @see http://www.php.net/manual/en/locale.getprimarylanguage.php
*
* @throws MethodNotImplementedException
*/
public static function getPrimaryLanguage($locale)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Returns the region for the locale
*
* @param string $locale The locale code to extract the region code from
*
* @return string|null The extracted region code or null if not present
*
* @see http://www.php.net/manual/en/locale.getregion.php
*
* @throws MethodNotImplementedException
*/
public static function getRegion($locale)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Returns the script for the locale
*
* @param string $locale The locale code to extract the script code from
*
* @return string|null The extracted script code or null if not present
*
* @see http://www.php.net/manual/en/locale.getscript.php
*
* @throws MethodNotImplementedException
*/
public static function getScript($locale)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Returns the closest language tag for the locale
*
* @param array $langtag A list of the language tags to compare to locale
* @param string $locale The locale to use as the language range when matching
* @param Boolean $canonicalize If true, the arguments will be converted to canonical form before matching
* @param string $default The locale to use if no match is found
*
* @see http://www.php.net/manual/en/locale.lookup.php
*
* @throws MethodNotImplementedException
*/
public static function lookup(array $langtag, $locale, $canonicalize = false, $default = null)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Returns an associative array of locale identifier subtags
*
* @param string $locale The locale code to extract the subtag array from
*
* @return array Associative array with the extracted subtags
*
* @see http://www.php.net/manual/en/locale.parselocale.php
*
* @throws MethodNotImplementedException
*/
public static function parseLocale($locale)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Sets the default runtime locale
*
* @param string $locale The locale code
*
* @return Boolean true on success or false on failure
*
* @see http://www.php.net/manual/en/locale.parselocale.php
*
* @throws MethodNotImplementedException
*/
public static function setDefault($locale)
{
throw new MethodNotImplementedException(__METHOD__);
}
}

View File

@ -0,0 +1,869 @@
<?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\Intl\NumberFormatter;
use Symfony\Component\Intl\Exception\NotImplementedException;
use Symfony\Component\Intl\Exception\MethodNotImplementedException;
use Symfony\Component\Intl\Exception\MethodArgumentNotImplementedException;
use Symfony\Component\Intl\Exception\MethodArgumentValueNotImplementedException;
use Symfony\Component\Intl\Globals\StubIntlGlobals;
use Symfony\Component\Intl\Intl;
use Symfony\Component\Intl\Locale\StubLocale;
/**
* Provides a stub NumberFormatter for the 'en' locale.
*
* @author Eriksen Costa <eriksen.costa@infranology.com.br>
*/
class StubNumberFormatter
{
/** Format style constants */
const PATTERN_DECIMAL = 0;
const DECIMAL = 1;
const CURRENCY = 2;
const PERCENT = 3;
const SCIENTIFIC = 4;
const SPELLOUT = 5;
const ORDINAL = 6;
const DURATION = 7;
const PATTERN_RULEBASED = 9;
const IGNORE = 0;
const DEFAULT_STYLE = 1;
/** Format type constants */
const TYPE_DEFAULT = 0;
const TYPE_INT32 = 1;
const TYPE_INT64 = 2;
const TYPE_DOUBLE = 3;
const TYPE_CURRENCY = 4;
/** Numeric attribute constants */
const PARSE_INT_ONLY = 0;
const GROUPING_USED = 1;
const DECIMAL_ALWAYS_SHOWN = 2;
const MAX_INTEGER_DIGITS = 3;
const MIN_INTEGER_DIGITS = 4;
const INTEGER_DIGITS = 5;
const MAX_FRACTION_DIGITS = 6;
const MIN_FRACTION_DIGITS = 7;
const FRACTION_DIGITS = 8;
const MULTIPLIER = 9;
const GROUPING_SIZE = 10;
const ROUNDING_MODE = 11;
const ROUNDING_INCREMENT = 12;
const FORMAT_WIDTH = 13;
const PADDING_POSITION = 14;
const SECONDARY_GROUPING_SIZE = 15;
const SIGNIFICANT_DIGITS_USED = 16;
const MIN_SIGNIFICANT_DIGITS = 17;
const MAX_SIGNIFICANT_DIGITS = 18;
const LENIENT_PARSE = 19;
/** Text attribute constants */
const POSITIVE_PREFIX = 0;
const POSITIVE_SUFFIX = 1;
const NEGATIVE_PREFIX = 2;
const NEGATIVE_SUFFIX = 3;
const PADDING_CHARACTER = 4;
const CURRENCY_CODE = 5;
const DEFAULT_RULESET = 6;
const PUBLIC_RULESETS = 7;
/** Format symbol constants */
const DECIMAL_SEPARATOR_SYMBOL = 0;
const GROUPING_SEPARATOR_SYMBOL = 1;
const PATTERN_SEPARATOR_SYMBOL = 2;
const PERCENT_SYMBOL = 3;
const ZERO_DIGIT_SYMBOL = 4;
const DIGIT_SYMBOL = 5;
const MINUS_SIGN_SYMBOL = 6;
const PLUS_SIGN_SYMBOL = 7;
const CURRENCY_SYMBOL = 8;
const INTL_CURRENCY_SYMBOL = 9;
const MONETARY_SEPARATOR_SYMBOL = 10;
const EXPONENTIAL_SYMBOL = 11;
const PERMILL_SYMBOL = 12;
const PAD_ESCAPE_SYMBOL = 13;
const INFINITY_SYMBOL = 14;
const NAN_SYMBOL = 15;
const SIGNIFICANT_DIGIT_SYMBOL = 16;
const MONETARY_GROUPING_SEPARATOR_SYMBOL = 17;
/** Rounding mode values used by NumberFormatter::setAttribute() with NumberFormatter::ROUNDING_MODE attribute */
const ROUND_CEILING = 0;
const ROUND_FLOOR = 1;
const ROUND_DOWN = 2;
const ROUND_UP = 3;
const ROUND_HALFEVEN = 4;
const ROUND_HALFDOWN = 5;
const ROUND_HALFUP = 6;
/** Pad position values used by NumberFormatter::setAttribute() with NumberFormatter::PADDING_POSITION attribute */
const PAD_BEFORE_PREFIX = 0;
const PAD_AFTER_PREFIX = 1;
const PAD_BEFORE_SUFFIX = 2;
const PAD_AFTER_SUFFIX = 3;
/**
* The error code from the last operation
*
* @var integer
*/
protected $errorCode = StubIntlGlobals::U_ZERO_ERROR;
/**
* The error message from the last operation
*
* @var string
*/
protected $errorMessage = 'U_ZERO_ERROR';
/**
* @var string
*/
private $locale;
/**
* @var int
*/
private $style;
/**
* Default values for the en locale
*
* @var array
*/
private $attributes = array(
self::FRACTION_DIGITS => 0,
self::GROUPING_USED => 1,
self::ROUNDING_MODE => self::ROUND_HALFEVEN
);
/**
* Holds the initialized attributes code
*
* @var array
*/
private $initializedAttributes = array();
/**
* The supported styles to the constructor $styles argument
*
* @var array
*/
private static $supportedStyles = array(
'CURRENCY' => self::CURRENCY,
'DECIMAL' => self::DECIMAL
);
/**
* Supported attributes to the setAttribute() $attr argument
*
* @var array
*/
private static $supportedAttributes = array(
'FRACTION_DIGITS' => self::FRACTION_DIGITS,
'GROUPING_USED' => self::GROUPING_USED,
'ROUNDING_MODE' => self::ROUNDING_MODE
);
/**
* The available rounding modes for setAttribute() usage with
* StubNumberFormatter::ROUNDING_MODE. StubNumberFormatter::ROUND_DOWN
* and StubNumberFormatter::ROUND_UP does not have a PHP only equivalent
*
* @var array
*/
private static $roundingModes = array(
'ROUND_HALFEVEN' => self::ROUND_HALFEVEN,
'ROUND_HALFDOWN' => self::ROUND_HALFDOWN,
'ROUND_HALFUP' => self::ROUND_HALFUP
);
/**
* The mapping between NumberFormatter rounding modes to the available
* modes in PHP's round() function.
*
* @see http://www.php.net/manual/en/function.round.php
*
* @var array
*/
private static $phpRoundingMap = array(
self::ROUND_HALFDOWN => \PHP_ROUND_HALF_DOWN,
self::ROUND_HALFEVEN => \PHP_ROUND_HALF_EVEN,
self::ROUND_HALFUP => \PHP_ROUND_HALF_UP
);
/**
* The maximum values of the integer type in 32 bit platforms.
*
* @var array
*/
private static $int32Range = array(
'positive' => 2147483647,
'negative' => -2147483648
);
/**
* The maximum values of the integer type in 64 bit platforms.
*
* @var array
*/
private static $int64Range = array(
'positive' => 9223372036854775807,
'negative' => -9223372036854775808
);
/**
* Constructor
*
* @param string $locale The locale code
* @param int $style Style of the formatting, one of the format style constants
* @param string $pattern A pattern string in case $style is NumberFormat::PATTERN_DECIMAL or
* NumberFormat::PATTERN_RULEBASED. It must conform to the syntax
* described in the ICU DecimalFormat or ICU RuleBasedNumberFormat documentation
*
* @see http://www.php.net/manual/en/numberformatter.create.php
* @see http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details
* @see http://www.icu-project.org/apiref/icu4c/classRuleBasedNumberFormat.html#_details
*
* @throws MethodArgumentValueNotImplementedException When $locale different than 'en' is passed
* @throws MethodArgumentValueNotImplementedException When the $style is not supported
* @throws MethodArgumentNotImplementedException When the pattern value is different than null
*/
public function __construct($locale = 'en', $style = null, $pattern = null)
{
if ('en' != $locale) {
throw new MethodArgumentValueNotImplementedException(__METHOD__, 'locale', $locale, 'Only the \'en\' locale is supported');
}
if (!in_array($style, self::$supportedStyles)) {
$message = sprintf('The available styles are: %s.', implode(', ', array_keys(self::$supportedStyles)));
throw new MethodArgumentValueNotImplementedException(__METHOD__, 'style', $style, $message);
}
if (null !== $pattern) {
throw new MethodArgumentNotImplementedException(__METHOD__, 'pattern');
}
$this->locale = $locale;
$this->style = $style;
}
/**
* Static constructor
*
* @param string $locale The locale code
* @param int $style Style of the formatting, one of the format style constants
* @param string $pattern A pattern string in case $style is NumberFormat::PATTERN_DECIMAL or
* NumberFormat::PATTERN_RULEBASED. It must conform to the syntax
* described in the ICU DecimalFormat or ICU RuleBasedNumberFormat documentation
*
* @return StubNumberFormatter
*
* @see http://www.php.net/manual/en/numberformatter.create.php
* @see http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details
* @see http://www.icu-project.org/apiref/icu4c/classRuleBasedNumberFormat.html#_details
*
* @throws MethodArgumentValueNotImplementedException When $locale different than 'en' is passed
* @throws MethodArgumentValueNotImplementedException When the $style is not supported
* @throws MethodArgumentNotImplementedException When the pattern value is different than null
*/
public static function create($locale = 'en', $style = null, $pattern = null)
{
return new self($locale, $style, $pattern);
}
/**
* Format a currency value
*
* @param float $value The numeric currency value
* @param string $currency The 3-letter ISO 4217 currency code indicating the currency to use
*
* @return string The formatted currency value
*
* @see http://www.php.net/manual/en/numberformatter.formatcurrency.php
* @see http://www.iso.org/iso/support/faqs/faqs_widely_used_standards/widely_used_standards_other/currency_codes/currency_codes_list-1.htm
*/
public function formatCurrency($value, $currency)
{
if ($this->style == self::DECIMAL) {
return $this->format($value);
}
$symbol = Intl::getCurrencyBundle()->getCurrencySymbol('en', $currency);
$fractionDigits = Intl::getCurrencyBundle()->getFractionDigits($currency);
$value = $this->roundCurrency($value, $currency);
$negative = false;
if (0 > $value) {
$negative = true;
$value *= -1;
}
$value = $this->formatNumber($value, $fractionDigits);
$ret = $symbol.$value;
return $negative ? '('.$ret.')' : $ret;
}
/**
* Format a number
*
* @param number $value The value to format
* @param int $type Type of the formatting, one of the format type constants
*
* @return Boolean|string The formatted value or false on error
*
* @see http://www.php.net/manual/en/numberformatter.format.php
*
* @throws \RuntimeException If the method is called with the class $style 'CURRENCY'
* @throws MethodArgumentNotImplementedException If the $type is different than TYPE_DEFAULT
*/
public function format($value, $type = self::TYPE_DEFAULT)
{
// The original NumberFormatter does not support this format type
if ($type == self::TYPE_CURRENCY) {
trigger_error(__METHOD__.'(): Unsupported format type '.$type, \E_USER_WARNING);
return false;
}
if ($this->style == self::CURRENCY) {
throw new NotImplementedException(sprintf(
'%s() method does not support the formatting of currencies (instance with CURRENCY style). %s',
__METHOD__, NotImplementedException::INTL_INSTALL_MESSAGE
));
}
// Only the default type is supported.
if ($type != self::TYPE_DEFAULT) {
throw new MethodArgumentValueNotImplementedException(__METHOD__, 'type', $type, 'Only TYPE_DEFAULT is supported');
}
$fractionDigits = $this->getAttribute(self::FRACTION_DIGITS);
$value = $this->round($value, $fractionDigits);
$value = $this->formatNumber($value, $fractionDigits);
// behave like the intl extension
$this->resetError();
return $value;
}
/**
* Returns an attribute value
*
* @param int $attr An attribute specifier, one of the numeric attribute constants
*
* @return Boolean|int The attribute value on success or false on error
*
* @see http://www.php.net/manual/en/numberformatter.getattribute.php
*/
public function getAttribute($attr)
{
return isset($this->attributes[$attr]) ? $this->attributes[$attr] : null;
}
/**
* Returns formatter's last error code. Always returns the U_ZERO_ERROR class constant value
*
* @return int The error code from last formatter call
*
* @see http://www.php.net/manual/en/numberformatter.geterrorcode.php
*/
public function getErrorCode()
{
return $this->errorCode;
}
/**
* Returns formatter's last error message. Always returns the U_ZERO_ERROR_MESSAGE class constant value
*
* @return string The error message from last formatter call
*
* @see http://www.php.net/manual/en/numberformatter.geterrormessage.php
*/
public function getErrorMessage()
{
return $this->errorMessage;
}
/**
* Returns the formatter's locale
*
* @param int $type The locale name type to return between valid or actual (StubLocale::VALID_LOCALE or StubLocale::ACTUAL_LOCALE, respectively)
*
* @return string The locale name used to create the formatter
*
* @see http://www.php.net/manual/en/numberformatter.getlocale.php
*/
public function getLocale($type = StubLocale::ACTUAL_LOCALE)
{
return $this->locale;
}
/**
* Returns the formatter's pattern
*
* @return Boolean|string The pattern string used by the formatter or false on error
*
* @see http://www.php.net/manual/en/numberformatter.getpattern.php
*
* @throws MethodNotImplementedException
*/
public function getPattern()
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Returns a formatter symbol value
*
* @param int $attr A symbol specifier, one of the format symbol constants
*
* @return Boolean|string The symbol value or false on error
*
* @see http://www.php.net/manual/en/numberformatter.getsymbol.php
*
* @throws MethodNotImplementedException
*/
public function getSymbol($attr)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Returns a formatter text attribute value
*
* @param int $attr An attribute specifier, one of the text attribute constants
*
* @return Boolean|string The attribute value or false on error
*
* @see http://www.php.net/manual/en/numberformatter.gettextattribute.php
*
* @throws MethodNotImplementedException
*/
public function getTextAttribute($attr)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Parse a currency number
*
* @param string $value The value to parse
* @param string $currency Parameter to receive the currency name (reference)
* @param int $position Offset to begin the parsing on return this value will hold the offset at which the parsing ended
*
* @return Boolean|string The parsed numeric value of false on error
*
* @see http://www.php.net/manual/en/numberformatter.parsecurrency.php
*
* @throws MethodNotImplementedException
*/
public function parseCurrency($value, &$currency, &$position = null)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Parse a number
*
* @param string $value The value to parse
* @param string $type Type of the formatting, one of the format type constants. NumberFormatter::TYPE_DOUBLE by default
* @param int $position Offset to begin the parsing on return this value will hold the offset at which the parsing ended
*
* @return Boolean|string The parsed value of false on error
*
* @see http://www.php.net/manual/en/numberformatter.parse.php
*
* @throws MethodArgumentNotImplementedException When $position different than null, behavior not implemented
*/
public function parse($value, $type = self::TYPE_DOUBLE, &$position = null)
{
if ($type == self::TYPE_DEFAULT || $type == self::TYPE_CURRENCY) {
trigger_error(__METHOD__.'(): Unsupported format type '.$type, \E_USER_WARNING);
return false;
}
// We don't calculate the position when parsing the value
if (null !== $position) {
throw new MethodArgumentNotImplementedException(__METHOD__, 'position');
}
preg_match('/^([^0-9\-]{0,})(.*)/', $value, $matches);
// Any string before the numeric value causes error in the parsing
if (isset($matches[1]) && !empty($matches[1])) {
StubIntlGlobals::setError(StubIntlGlobals::U_PARSE_ERROR, 'Number parsing failed');
$this->errorCode = StubIntlGlobals::getErrorCode();
$this->errorMessage = StubIntlGlobals::getErrorMessage();
return false;
}
// Remove everything that is not number or dot (.)
$value = preg_replace('/[^0-9\.\-]/', '', $value);
$value = $this->convertValueDataType($value, $type);
// behave like the intl extension
$this->resetError();
return $value;
}
/**
* Set an attribute
*
* @param int $attr An attribute specifier, one of the numeric attribute constants
* @param int $value The attribute value
*
* @return Boolean true on success or false on failure
*
* @see http://www.php.net/manual/en/numberformatter.setattribute.php
*
* @throws MethodArgumentValueNotImplementedException When the $attr is not supported
* @throws MethodArgumentValueNotImplementedException When the $value is not supported
*/
public function setAttribute($attr, $value)
{
if (!in_array($attr, self::$supportedAttributes)) {
$message = sprintf(
'The available attributes are: %s',
implode(', ', array_keys(self::$supportedAttributes))
);
throw new MethodArgumentValueNotImplementedException(__METHOD__, 'attr', $value, $message);
}
if (self::$supportedAttributes['ROUNDING_MODE'] == $attr && $this->isInvalidRoundingMode($value)) {
$message = sprintf(
'The supported values for ROUNDING_MODE are: %s',
implode(', ', array_keys(self::$roundingModes))
);
throw new MethodArgumentValueNotImplementedException(__METHOD__, 'attr', $value, $message);
}
if (self::$supportedAttributes['GROUPING_USED'] == $attr) {
$value = $this->normalizeGroupingUsedValue($value);
}
if (self::$supportedAttributes['FRACTION_DIGITS'] == $attr) {
$value = $this->normalizeFractionDigitsValue($value);
}
$this->attributes[$attr] = $value;
$this->initializedAttributes[$attr] = true;
return true;
}
/**
* Set the formatter's pattern
*
* @param string $pattern A pattern string in conformance with the ICU DecimalFormat documentation
*
* @return Boolean true on success or false on failure
*
* @see http://www.php.net/manual/en/numberformatter.setpattern.php
* @see http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details
*
* @throws MethodNotImplementedException
*/
public function setPattern($pattern)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Set the formatter's symbol
*
* @param int $attr A symbol specifier, one of the format symbol constants
* @param string $value The value for the symbol
*
* @return Boolean true on success or false on failure
*
* @see http://www.php.net/manual/en/numberformatter.setsymbol.php
*
* @throws MethodNotImplementedException
*/
public function setSymbol($attr, $value)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Set a text attribute
*
* @param int $attr An attribute specifier, one of the text attribute constants
* @param int $value The attribute value
*
* @return Boolean true on success or false on failure
*
* @see http://www.php.net/manual/en/numberformatter.settextattribute.php
*
* @throws MethodNotImplementedException
*/
public function setTextAttribute($attr, $value)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Set the error to the default U_ZERO_ERROR
*/
protected function resetError()
{
StubIntlGlobals::setError(StubIntlGlobals::U_ZERO_ERROR);
$this->errorCode = StubIntlGlobals::getErrorCode();
$this->errorMessage = StubIntlGlobals::getErrorMessage();
}
/**
* Rounds a currency value, applying increment rounding if applicable
*
* When a currency have a rounding increment, an extra round is made after the first one. The rounding factor is
* determined in the ICU data and is explained as of:
*
* "the rounding increment is given in units of 10^(-fraction_digits)"
*
* The only actual rounding data as of this writing, is CHF.
*
* @param float $value The numeric currency value
* @param string $currency The 3-letter ISO 4217 currency code indicating the currency to use
*
* @return string The rounded numeric currency value
*
* @see http://en.wikipedia.org/wiki/Swedish_rounding
* @see http://www.docjar.com/html/api/com/ibm/icu/util/Currency.java.html#1007
*/
private function roundCurrency($value, $currency)
{
$fractionDigits = Intl::getCurrencyBundle()->getFractionDigits($currency);
$roundingIncrement = Intl::getCurrencyBundle()->getRoundingIncrement($currency);
// Round with the formatter rounding mode
$value = $this->round($value, $fractionDigits);
// Swiss rounding
if (0 < $roundingIncrement && 0 < $fractionDigits) {
$roundingFactor = $roundingIncrement / pow(10, $fractionDigits);
$value = round($value / $roundingFactor) * $roundingFactor;
}
return $value;
}
/**
* Rounds a value.
*
* @param integer|float $value The value to round
* @param int $precision The number of decimal digits to round to
*
* @return integer|float The rounded value
*/
private function round($value, $precision)
{
$precision = $this->getUnitializedPrecision($value, $precision);
$roundingMode = self::$phpRoundingMap[$this->getAttribute(self::ROUNDING_MODE)];
$value = round($value, $precision, $roundingMode);
return $value;
}
/**
* Formats a number.
*
* @param integer|float $value The numeric value to format
* @param int $precision The number of decimal digits to use
*
* @return string The formatted number
*/
private function formatNumber($value, $precision)
{
$precision = $this->getUnitializedPrecision($value, $precision);
return number_format($value, $precision, '.', $this->getAttribute(self::GROUPING_USED) ? ',' : '');
}
/**
* Returns the precision value if the the DECIMAL style is being used and the FRACTION_DIGITS attribute is unitialized.
*
* @param integer|float $value The value to get the precision from if the FRACTION_DIGITS attribute is unitialized
* @param int $precision The precision value to returns if the FRACTION_DIGITS attribute is initialized
*
* @return int The precision value
*/
private function getUnitializedPrecision($value, $precision)
{
if ($this->style == self::CURRENCY) {
return $precision;
}
if (!$this->isInitializedAttribute(self::FRACTION_DIGITS)) {
preg_match('/.*\.(.*)/', (string) $value, $digits);
if (isset($digits[1])) {
$precision = strlen($digits[1]);
}
}
return $precision;
}
/**
* Check if the attribute is initialized (value set by client code).
*
* @param string $attr The attribute name
*
* @return Boolean true if the value was set by client, false otherwise
*/
private function isInitializedAttribute($attr)
{
return isset($this->initializedAttributes[$attr]);
}
/**
* Returns the numeric value using the $type to convert to the right data type.
*
* @param mixed $value The value to be converted
* @param int $type The type to convert. Can be TYPE_DOUBLE (float) or TYPE_INT32 (int)
*
* @return integer|float The converted value
*/
private function convertValueDataType($value, $type)
{
if ($type == self::TYPE_DOUBLE) {
$value = (float) $value;
} elseif ($type == self::TYPE_INT32) {
$value = $this->getInt32Value($value);
} elseif ($type == self::TYPE_INT64) {
$value = $this->getInt64Value($value);
}
return $value;
}
/**
* Convert the value data type to int or returns false if the value is out of the integer value range.
*
* @param mixed $value The value to be converted
*
* @return int The converted value
*/
private function getInt32Value($value)
{
if ($value > self::$int32Range['positive'] || $value < self::$int32Range['negative']) {
return false;
}
return (int) $value;
}
/**
* Convert the value data type to int or returns false if the value is out of the integer value range.
*
* @param mixed $value The value to be converted
*
* @return int|float The converted value
*
* @see https://bugs.php.net/bug.php?id=59597 Bug #59597
*/
private function getInt64Value($value)
{
if ($value > self::$int64Range['positive'] || $value < self::$int64Range['negative']) {
return false;
}
if (PHP_INT_SIZE !== 8 && ($value > self::$int32Range['positive'] || $value <= self::$int32Range['negative'])) {
// Bug #59597 was fixed on PHP 5.3.14 and 5.4.4
// The negative PHP_INT_MAX was being converted to float
if (
$value == self::$int32Range['negative'] &&
(
(version_compare(PHP_VERSION, '5.4.0', '<') && version_compare(PHP_VERSION, '5.3.14', '>=')) ||
version_compare(PHP_VERSION, '5.4.4', '>=')
)
) {
return (int) $value;
}
return (float) $value;
}
if (PHP_INT_SIZE === 8) {
// Bug #59597 was fixed on PHP 5.3.14 and 5.4.4
// A 32 bit integer was being generated instead of a 64 bit integer
if (
($value > self::$int32Range['positive'] || $value < self::$int32Range['negative']) &&
(
(version_compare(PHP_VERSION, '5.3.14', '<')) ||
(version_compare(PHP_VERSION, '5.4.0', '>=') && version_compare(PHP_VERSION, '5.4.4', '<'))
)
) {
$value = (-2147483648 - ($value % -2147483648)) * ($value / abs($value));
}
}
return (int) $value;
}
/**
* Check if the rounding mode is invalid.
*
* @param int $value The rounding mode value to check
*
* @return Boolean true if the rounding mode is invalid, false otherwise
*/
private function isInvalidRoundingMode($value)
{
if (in_array($value, self::$roundingModes, true)) {
return false;
}
return true;
}
/**
* Returns the normalized value for the GROUPING_USED attribute. Any value that can be converted to int will be
* cast to Boolean and then to int again. This way, negative values are converted to 1 and string values to 0.
*
* @param mixed $value The value to be normalized
*
* @return int The normalized value for the attribute (0 or 1)
*/
private function normalizeGroupingUsedValue($value)
{
return (int) (Boolean) (int) $value;
}
/**
* Returns the normalized value for the FRACTION_DIGITS attribute. The value is converted to int and if negative,
* the returned value will be 0.
*
* @param mixed $value The value to be normalized
*
* @return int The normalized value for the attribute
*/
private function normalizeFractionDigitsValue($value)
{
$value = (int) $value;
return (0 > $value) ? 0 : $value;
}
}

View File

@ -0,0 +1,195 @@
Intl Component
=============
A PHP replacement layer for the C intl extension that includes additional data
from the ICU library.
The replacement layer is limited to the locale "en". If you want to use other
locales, you should [install the intl extension] [10] instead.
Installation
------------
You can install the component in two different ways:
* Using the official Git repository (https://github.com/symfony/Intl);
* [Install it via Composer] [0] (`symfony/intl` on [Packagist] [1]).
If you install the component via Composer, the following classes and functions
of the intl extension will be automatically provided if the intl extension is
not loaded:
* [`\Locale`] [2]
* [`\NumberFormatter`] [3]
* [`\IntlDateFormatter`] [4]
* [`\Collator`] [5]
* [`intl_is_failure()`] [6]
* [`intl_get_error_code()`] [7]
* [`intl_get_error_message()`] [8]
* [`intl_error_name()`] [9]
If you don't use Composer but the Symfony ClassLoader component, you need to
load them manually by adding the following lines to your autoload code:
if (!function_exists('intl_is_failure')) {
require '/path/to/Icu/Resources/stubs/functions.php';
$loader->registerPrefixFallback('/path/to/Icu/Resources/stubs');
}
Stubbed Classes
---------------
The stubbed classes of the intl extension are limited to the locale "en" and
will throw an exception if you try to use a different locale. For using other
locales, [install the intl extension] [10] instead.
### Locale
The only method supported in the [´\Locale`] [2] class is `getDefault()` and
will always return "en". All other methods will throw an exception when used.
### NumberFormatter
Numbers can be formatted with the [`\NumberFormatter`] [3] class. The following
methods are supported. All other methods are not supported and will throw an
exception when used.
##### __construct($locale = 'en', $style = null, $pattern = null)
The only supported locale is "en". The supported styles are
`\NumberFormatter::DECIMAL` and `\NumberFormatter::CURRENCY`. The argument
`$pattern` may not be used.
##### ::create($locale = 'en', $style = null, $pattern = null)
See `__construct()`.
##### formatCurrency($value, $currency)
Fully supported.
##### format($value, $type = \NumberFormatter::TYPE_DEFAULT)
Only type `\NumberFormatter::TYPE_DEFAULT` is supported.
##### getAttribute($attr)
Fully supported.
##### getErrorCode()
Fully supported.
##### getErrorMessage()
Fully supported.
##### getLocale($type = \Locale::ACTUAL_LOCALE)
The parameter `$type` is ignored.
##### parse($value, $type = \NumberFormatter::TYPE_DOUBLE, &$position = null)
The supported types are `\NumberFormatter::TYPE_DOUBLE`,
`\NumberFormatter::TYPE_INT32` and `\NumberFormatter::TYPE_INT64`. The
parameter `$position` must always be `null`.
##### setAttribute($attr, $value)
The only supported attributes are `\NumberFormatter::FRACTION_DIGITS`,
`\NumberFormatter::GROUPING_USED` and `\NumberFormatter::ROUNDING_MODE`.
The only supported rounding modes are `\NumberFormatter::ROUND_HALFEVEN`,
`\NumberFormatter::ROUND_HALFDOWN` and `\NumberFormatter::ROUND_HALFUP`.
Included Resource Bundles
-------------------------
The ICU data is located in several "resource bundles". You can access a PHP
wrapper of these bundles through the static Intl class.
Languages and Scripts
~~~~~~~~~~~~~~~~~~~~~
The translations of language and script names can be found in the language
bundle.
$languages = Intl::getLanguageBundle()->getLanguageNames('en');
// => array('ab' => 'Abkhazian', ...)
$language = Intl::getLanguageBundle()->getLanguageName('en', 'de');
// => 'German'
$language = Intl::getLanguageBundle()->getLanguageName('en', 'de', 'AT);
// => 'Austrian German'
$scripts = Intl::getLanguageBundle()->getScriptNames('en');
// => array('Arab' => 'Arabic', ...)
$script = Intl::getLanguageBundle()->getScriptName('en', 'Hans');
// => 'Simplified'
Countries
~~~~~~~~~
The translations of country names can be found in the region bundle.
$countries = Intl::getRegionBundle()->getCountryNames('en');
// => array('AF' => 'Afghanistan', ...)
$country = Intl::getRegionBundle()->getCountryName('en', 'GB');
// => 'United Kingdom'
Locales
~~~~~~~
The translations of locale names can be found in the locale bundle.
$locales = Intl::getLocaleBundle()->getLocaleNames('en');
// => array('af' => 'Afrikaans', ...)
$locale = Intl::getLocaleBundle()->getLocaleName('en', 'zh_Hans_MO');
// => 'Chinese (Simplified, Macau SAR China)'
Currencies
~~~~~~~~~~
The translations of currency names and other currency-related information can
be found in the currency bundle.
$currencies = Intl::getCurrencyBundle()->getCurrencyNames('en');
// => array('AFN' => 'Afghan Afghani', ...)
$currency = Intl::getCurrencyBundle()->getCurrencyNames('en', 'INR');
// => 'Indian Rupee'
$symbol = Intl::getCurrencyBundle()->getCurrencyNames('en', 'INR');
// => '₹'
$fractionDigits = Intl::getCurrencyBundle()->getFractionDigits('INR');
// => 2
$roundingIncrement = Intl::getCurrencyBundle()->getRoundingIncrement('INR');
// => 0
Resources
---------
You can run the unit tests with the following command:
$ cd path/to/Symfony/Component/Intl/
$ composer.phar install --dev
$ phpunit
[0]: /components/using_components
[1]: https://packagist.org/packages/symfony/intl
[2]: http://www.php.net/manual/en/class.locale.php
[3]: http://www.php.net/manual/en/class.numberformatter.php
[4]: http://www.php.net/manual/en/class.intldateformatter.php
[5]: http://www.php.net/manual/en/class.collator.php
[6]: http://www.php.net/manual/en/function.intl-error-name.php
[7]: http://www.php.net/manual/en/function.intl-get-error-code.php
[8]: http://www.php.net/manual/en/function.intl-get-error-message.php
[9]: http://www.php.net/manual/en/function.intl-is-failure.php
[10]: http://www.php.net/manual/en/intl.setup.php

View File

@ -0,0 +1,71 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\ResourceBundle;
use Symfony\Component\Intl\Intl;
use Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReaderInterface;
/**
* Base class for {@link ResourceBundleInterface} implementations.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
abstract class AbstractBundle implements ResourceBundleInterface
{
/**
* @var string
*/
private $path;
/**
* @var StructuredBundleReaderInterface
*/
private $reader;
/**
* Creates a bundle at the given path using the given reader for reading
* bundle entries.
*
* @param string $path The path to the bundle.
* @param StructuredBundleReaderInterface $reader The reader for reading
* the bundle.
*/
public function __construct($path, StructuredBundleReaderInterface $reader)
{
$this->path = $path;
$this->reader = $reader;
}
/**
* {@inheritdoc}
*/
public function getLocales()
{
return $this->reader->getLocales($this->path);
}
/**
* Proxy method for {@link StructuredBundleReaderInterface#read}.
*/
protected function read($locale)
{
return $this->reader->read($this->path, $locale);
}
/**
* Proxy method for {@link StructuredBundleReaderInterface#readEntry}.
*/
protected function readEntry($locale, array $indices, $mergeFallback = false)
{
return $this->reader->readEntry($this->path, $locale, $indices, $mergeFallback);
}
}

View File

@ -0,0 +1,69 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\ResourceBundle\Compiler;
use Symfony\Component\Intl\Exception\RuntimeException;
/**
* Compiles .txt resource bundles to binary .res files.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class BundleCompiler implements ResourceBundleCompilerInterface
{
/**
* @var string The path to the "genrb" executable.
*/
private $genrb;
/**
* Creates a new compiler based on the "genrb" executable.
*
* @param string $genrb Optional. The path to the "genrb" executable.
*
* @throws RuntimeException If the "genrb" cannot be found.
*/
public function __construct($genrb = 'genrb')
{
exec('which ' . $genrb, $output, $status);
if (0 !== $status) {
throw new RuntimeException(sprintf(
'The command "%s" is not installed',
$genrb
));
}
$this->genrb = $genrb;
}
/**
* {@inheritdoc}
*/
public function compile($sourcePath, $targetDir)
{
if (is_dir($sourcePath)) {
$sourcePath .= '/*.txt';
}
exec($this->genrb.' --quiet -e UTF-8 -d '.$targetDir.' '.$sourcePath, $output, $status);
if ($status !== 0) {
throw new RuntimeException(sprintf(
'genrb failed with status %d while compiling %s to %s.',
$status,
$sourcePath,
$targetDir
));
}
}
}

View File

@ -0,0 +1,29 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\ResourceBundle\Compiler;
/**
* Compiles a resource bundle.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
interface ResourceBundleCompilerInterface
{
/**
* Compiles a resource bundle at the given source to the given target
* directory.
*
* @param string $sourcePath
* @param string $targetDir
*/
public function compile($sourcePath, $targetDir);
}

View File

@ -0,0 +1,82 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\ResourceBundle;
/**
* Default implementation of {@link CurrencyBundleInterface}.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class CurrencyBundle extends AbstractBundle implements CurrencyBundleInterface
{
const INDEX_NAME = 0;
const INDEX_SYMBOL = 1;
const INDEX_FRACTION_DIGITS = 2;
const INDEX_ROUNDING_INCREMENT = 3;
/**
* {@inheritdoc}
*/
public function getCurrencySymbol($locale, $currency)
{
return $this->readEntry($locale, array('Currencies', $currency, static::INDEX_SYMBOL));
}
/**
* {@inheritdoc}
*/
public function getCurrencyName($locale, $currency)
{
return $this->readEntry($locale, array('Currencies', $currency, static::INDEX_NAME));
}
/**
* {@inheritdoc}
*/
public function getCurrencyNames($locale)
{
if (null === ($currencies = $this->readEntry($locale, array('Currencies')))) {
return array();
}
if ($currencies instanceof \Traversable) {
$currencies = iterator_to_array($currencies);
}
$index = static::INDEX_NAME;
array_walk($currencies, function (&$value) use ($index) {
$value = $value[$index];
});
return $currencies;
}
/**
* {@inheritdoc}
*/
public function getFractionDigits($currency)
{
return $this->readEntry('en', array('Currencies', $currency, static::INDEX_FRACTION_DIGITS));
}
/**
* {@inheritdoc}
*/
public function getRoundingIncrement($currency)
{
return $this->readEntry('en', array('Currencies', $currency, static::INDEX_ROUNDING_INCREMENT));
}
}

View File

@ -0,0 +1,71 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\ResourceBundle;
/**
* Gives access to currency-related ICU data.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
interface CurrencyBundleInterface extends ResourceBundleInterface
{
/**
* Returns the symbol used for a currency.
*
* @param string $locale The locale to return the result in.
* @param string $currency A currency code (e.g. "EUR").
*
* @return string|null The currency symbol or NULL if not found.
*/
public function getCurrencySymbol($locale, $currency);
/**
* Returns the name of a currency.
*
* @param string $locale The locale to return the name in.
* @param string $currency A currency code (e.g. "EUR").
*
* @return string|null The name of the currency or NULL if not found.
*/
public function getCurrencyName($locale, $currency);
/**
* Returns the names of all known currencies.
*
* @param string $locale The locale to return the names in.
*
* @return string[] A list of currency names indexed by currency codes.
*/
public function getCurrencyNames($locale);
/**
* Returns the number of digits after the comma of a currency.
*
* @param string $currency A currency code (e.g. "EUR").
*
* @return integer|null The number of digits after the comma or NULL if not found.
*/
public function getFractionDigits($currency);
/**
* Returns the rounding increment of a currency.
*
* The rounding increment indicates to which number a currency is rounded.
* For example, 1230 rounded to the nearest 50 is 1250. 1.234 rounded to the
* nearest 0.65 is 1.3.
*
* @param string $currency A currency code (e.g. "EUR").
*
* @return float|integer|null The rounding increment or NULL if not found.
*/
public function getRoundingIncrement($currency);
}

View File

@ -0,0 +1,99 @@
<?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\Intl\ResourceBundle;
/**
* Default implementation of {@link LanguageBundleInterface}.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class LanguageBundle extends AbstractBundle implements LanguageBundleInterface
{
/**
* {@inheritdoc}
*/
public function getLanguageName($locale, $lang, $region = null)
{
if (null === ($languages = $this->readEntry($locale, array('Languages')))) {
return array();
}
// Some languages are translated together with their region,
// i.e. "en_GB" is translated as "British English"
if (null !== $region && isset($languages[$lang.'_'.$region])) {
return $languages[$lang.'_'.$region];
}
return $languages[$lang];
}
/**
* {@inheritdoc}
*/
public function getLanguageNames($locale)
{
if (null === ($languages = $this->readEntry($locale, array('Languages')))) {
return array();
}
if ($languages instanceof \Traversable) {
$languages = iterator_to_array($languages);
}
return $languages;
}
/**
* {@inheritdoc}
*/
public function getScriptName($locale, $script, $lang = null)
{
$data = $this->read($locale);
// Some languages are translated together with their script,
// e.g. "zh_Hans" is translated as "Simplified Chinese"
if (null !== $lang && isset($data['Languages'][$lang.'_'.$script])) {
$langName = $data['Languages'][$lang.'_'.$script];
// If the script is appended in braces, extract it, e.g. "zh_Hans"
// is translated as "Chinesisch (vereinfacht)" in locale "de"
if (strpos($langName, '(') !== false) {
list($langName, $scriptName) = preg_split('/[\s()]/', $langName, null, PREG_SPLIT_NO_EMPTY);
return $scriptName;
}
}
// "af" (Afrikaans) has no "Scripts" block
if (!isset($data['Scripts'][$script])) {
return null;
}
return $data['Scripts'][$script];
}
/**
* {@inheritdoc}
*/
public function getScriptNames($locale)
{
if (null === ($scripts = $this->readEntry($locale, array('Scripts')))) {
return array();
}
if ($scripts instanceof \Traversable) {
$scripts = iterator_to_array($scripts);
}
return $scripts;
}
}

View File

@ -0,0 +1,60 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\ResourceBundle;
/**
* Gives access to language-related ICU data.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
interface LanguageBundleInterface extends ResourceBundleInterface
{
/**
* Returns the name of a language.
*
* @param string $locale The locale to return the name in.
* @param string $lang A language code (e.g. "en").
* @param string|null $region Optional. A region code (e.g. "US").
*
* @return string|null The name of the language or NULL if not found.
*/
public function getLanguageName($locale, $lang, $region = null);
/**
* Returns the names of all known languages.
*
* @param string $locale The locale to return the names in.
*
* @return string[] A list of language names indexed by language codes.
*/
public function getLanguageNames($locale);
/**
* Returns the name of a script.
*
* @param string $locale The locale to return the name in.
* @param string $script A script code (e.g. "Hans").
* @param string $lang Optional. A language code (e.g. "zh").
*
* @return string|null The name of the script or NULL if not found.
*/
public function getScriptName($locale, $script, $lang = null);
/**
* Returns the names of all known scripts.
*
* @param string $locale The locale to return the names in.
*
* @return string[] A list of script names indexed by script codes.
*/
public function getScriptNames($locale);
}

View File

@ -0,0 +1,44 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\ResourceBundle;
/**
* Default implementation of {@link LocaleBundleInterface}.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class LocaleBundle extends AbstractBundle implements LocaleBundleInterface
{
/**
* {@inheritdoc}
*/
public function getLocaleName($locale, $ofLocale)
{
return $this->readEntry($locale, array('Locales', $ofLocale));
}
/**
* {@inheritdoc}
*/
public function getLocaleNames($locale)
{
if (null === ($locales = $this->readEntry($locale, array('Locales')))) {
return array();
}
if ($locales instanceof \Traversable) {
$locales = iterator_to_array($locales);
}
return $locales;
}
}

View File

@ -0,0 +1,39 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\ResourceBundle;
/**
* Gives access to locale-related ICU data.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
interface LocaleBundleInterface extends ResourceBundleInterface
{
/**
* Returns the name of a locale.
*
* @param string $locale The locale to return the name in.
* @param string $ofLocale The locale to return the name of (e.g. "de_AT").
*
* @return string|null The name of the locale or NULL if not found.
*/
public function getLocaleName($locale, $ofLocale);
/**
* Returns the names of all known locales.
*
* @param string $locale The locale to return the name in.
*
* @return string[] A list of locale names indexed by locale codes.
*/
public function getLocaleNames($locale);
}

View File

@ -0,0 +1,42 @@
<?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\Intl\ResourceBundle\Reader;
/**
* Base class for {@link BundleReaderInterface} implementations.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
abstract class AbstractBundleReader implements BundleReaderInterface
{
/**
* {@inheritdoc}
*/
public function getLocales($path)
{
$extension = '.' . $this->getFileExtension();
$locales = glob($path . '/*' . $extension);
// Remove file extension and sort
array_walk($locales, function (&$locale) use ($extension) { $locale = basename($locale, $extension); });
sort($locales);
return $locales;
}
/**
* Returns the extension of locale files in this bundle.
*
* @return string The file extension (without leading dot).
*/
abstract protected function getFileExtension();
}

View File

@ -0,0 +1,51 @@
<?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\Intl\ResourceBundle\Reader;
use Symfony\Component\Intl\Exception\RuntimeException;
use Symfony\Component\Intl\ResourceBundle\Util\ArrayAccessibleResourceBundle;
/**
* Reads binary .res resource bundles.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class BinaryBundleReader extends AbstractBundleReader implements BundleReaderInterface
{
/**
* {@inheritdoc}
*/
public function read($path, $locale)
{
// Point for future extension: Modify this class so that it works also
// if the \ResourceBundle class is not available.
$bundle = new \ResourceBundle($locale, $path);
if (null === $bundle) {
throw new RuntimeException(sprintf(
'Could not load the resource bundle "%s/%s.res".',
$path,
$locale
));
}
return new ArrayAccessibleResourceBundle($bundle);
}
/**
* {@inheritdoc}
*/
protected function getFileExtension()
{
return 'res';
}
}

View File

@ -0,0 +1,63 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\ResourceBundle\Reader;
use Symfony\Component\Intl\ResourceBundle\Util\RingBuffer;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class BufferedReader implements BundleReaderInterface
{
/**
* @var BundleReaderInterface
*/
private $reader;
private $buffer;
/**
* Buffers a given reader.
*
* @param BundleReaderInterface $reader The reader to buffer.
* @param integer $bufferSize The number of entries to store
* in the buffer.
*/
public function __construct(BundleReaderInterface $reader, $bufferSize)
{
$this->reader = $reader;
$this->buffer = new RingBuffer($bufferSize);
}
/**
* {@inheritdoc}
*/
public function read($path, $locale)
{
$hash = $path . '//' . $locale;
if (!isset($this->buffer[$hash])) {
$this->buffer[$hash] = $this->reader->read($path, $locale);
}
return $this->buffer[$hash];
}
/**
* {@inheritdoc}
*/
public function getLocales($path)
{
return $this->reader->getLocales($path);
}
}

View File

@ -0,0 +1,40 @@
<?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\Intl\ResourceBundle\Reader;
/**
* Reads resource bundle files.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
interface BundleReaderInterface
{
/**
* Reads a resource bundle.
*
* @param string $path The path to the resource bundle.
* @param string $locale The locale to read.
*
* @return mixed Returns an array or {@link \ArrayAccess} instance for
* complex data, a scalar value otherwise.
*/
public function read($path, $locale);
/**
* Reads the available locales of a resource bundle.
*
* @param string $path The path to the resource bundle.
*
* @return string[] A list of supported locale codes.
*/
public function getLocales($path);
}

View File

@ -0,0 +1,61 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\ResourceBundle\Reader;
use Symfony\Component\Intl\Exception\InvalidArgumentException;
use Symfony\Component\Intl\Exception\RuntimeException;
/**
* Reads .php resource bundles.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class PhpBundleReader extends AbstractBundleReader implements BundleReaderInterface
{
/**
* {@inheritdoc}
*/
public function read($path, $locale)
{
if ('en' !== $locale) {
throw new InvalidArgumentException('Only the locale "en" is supported.');
}
$fileName = $path . '/' . $locale . '.php';
if (!file_exists($fileName)) {
throw new RuntimeException(sprintf(
'The resource bundle "%s/%s.php" does not exist.',
$path,
$locale
));
}
if (!is_file($fileName)) {
throw new RuntimeException(sprintf(
'The resource bundle "%s/%s.php" is not a file.',
$path,
$locale
));
}
return include $fileName;
}
/**
* {@inheritdoc}
*/
protected function getFileExtension()
{
return 'php';
}
}

View File

@ -0,0 +1,113 @@
<?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\Intl\ResourceBundle\Reader;
use Symfony\Component\Intl\ResourceBundle\Util\RecursiveArrayAccess;
/**
* A structured reader wrapping an existing resource bundle reader.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @see StructuredResourceBundleBundleReaderInterface
*/
class StructuredBundleReader implements StructuredBundleReaderInterface
{
/**
* @var BundleReaderInterface
*/
private $reader;
/**
* Creates an entry reader based on the given resource bundle reader.
*
* @param BundleReaderInterface $reader A resource bundle reader to use.
*/
public function __construct(BundleReaderInterface $reader)
{
$this->reader = $reader;
}
/**
* {@inheritdoc}
*/
public function read($path, $locale)
{
return $this->reader->read($path, $locale);
}
/**
* {@inheritdoc}
*/
public function getLocales($path)
{
return $this->reader->getLocales($path);
}
/**
* {@inheritdoc}
*/
public function readEntry($path, $locale, array $indices, $mergeFallback = true)
{
$data = $this->reader->read($path, $locale);
$entry = RecursiveArrayAccess::get($data, $indices);
$multivalued = is_array($entry) || $entry instanceof \Traversable;
if (!($mergeFallback && (null === $entry || $multivalued))) {
return $entry;
}
if (null !== ($fallbackLocale = $this->getFallbackLocale($locale))) {
$parentEntry = $this->readEntry($path, $fallbackLocale, $indices, true);
if ($entry || $parentEntry) {
$multivalued = $multivalued || is_array($parentEntry) || $parentEntry instanceof \Traversable;
if ($multivalued) {
if ($entry instanceof \Traversable) {
$entry = iterator_to_array($entry);
}
if ($parentEntry instanceof \Traversable) {
$parentEntry = iterator_to_array($parentEntry);
}
$entry = array_merge(
$parentEntry ?: array(),
$entry ?: array()
);
} else {
$entry = null === $entry ? $parentEntry : $entry;
}
}
}
return $entry;
}
/**
* Returns the fallback locale for a given locale, if any
*
* @param string $locale The locale to find the fallback for.
*
* @return string|null The fallback locale, or null if no parent exists
*/
private function getFallbackLocale($locale)
{
if (false === $pos = strrpos($locale, '_')) {
return null;
}
return substr($locale, 0, $pos);
}
}

View File

@ -0,0 +1,49 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\ResourceBundle\Reader;
/**
* Reads individual entries of a resource file.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
interface StructuredBundleReaderInterface extends BundleReaderInterface
{
/**
* Reads an entry from a resource bundle.
*
* An entry can be selected from the resource bundle by passing the path
* to that entry in the bundle. For example, if the bundle is structured
* like this:
*
* TopLevel
* NestedLevel
* Entry: Value
*
* Then the value can be read by calling:
*
* $reader->readEntry('...', 'en', array('TopLevel', 'NestedLevel', 'Entry'));
*
* @param string $path The path to the resource bundle.
* @param string $locale The locale to read.
* @param string[] $indices The indices to read from the bundle.
* @param Boolean $mergeFallback Whether to merge the value with the value
* from the fallback locale (e.g. "en" for
* "en_GB"). Only applicable if the result
* is multivalued (array, \ArrayAccess).
*
* @return mixed Returns an array or {@link \ArrayAccess} instance for
* complex data, a scalar value for simple data and NULL
* if the given path could not be accessed.
*/
public function readEntry($path, $locale, array $indices, $mergeFallback = true);
}

View File

@ -0,0 +1,44 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\ResourceBundle;
/**
* Default implementation of {@link RegionBundleInterface}.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class RegionBundle extends AbstractBundle implements RegionBundleInterface
{
/**
* {@inheritdoc}
*/
public function getCountryName($locale, $country)
{
return $this->readEntry($locale, array('Countries', $country));
}
/**
* {@inheritdoc}
*/
public function getCountryNames($locale)
{
if (null === ($countries = $this->readEntry($locale, array('Countries')))) {
return array();
}
if ($countries instanceof \Traversable) {
$countries = iterator_to_array($countries);
}
return $countries;
}
}

View File

@ -0,0 +1,39 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\ResourceBundle;
/**
* Gives access to region-related ICU data.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
interface RegionBundleInterface extends ResourceBundleInterface
{
/**
* Returns the name of a country.
*
* @param string $locale The locale to return the name in.
* @param string $country A country code (e.g. "US").
*
* @return string|null The name of the country or NULL if not found.
*/
public function getCountryName($locale, $country);
/**
* Returns the names of all known countries.
*
* @param string $locale The locale to return the names in.
*
* @return string[] A list of country names indexed by country codes.
*/
public function getCountryNames($locale);
}

View File

@ -0,0 +1,27 @@
<?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\Intl\ResourceBundle;
/**
* Gives access to ICU data.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
interface ResourceBundleInterface
{
/**
* Returns the list of locales that this bundle supports.
*
* @return string[] A list of locale codes.
*/
public function getLocales();
}

View File

@ -0,0 +1,26 @@
<?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\Intl\ResourceBundle\Stub;
use Symfony\Component\Intl\ResourceBundle\CurrencyBundle;
use Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReaderInterface;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class StubCurrencyBundle extends CurrencyBundle
{
public function __construct(StructuredBundleReaderInterface $reader)
{
parent::__construct(realpath(__DIR__ . '/../../Resources/data/curr'), $reader);
}
}

View File

@ -0,0 +1,26 @@
<?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\Intl\ResourceBundle\Stub;
use Symfony\Component\Intl\ResourceBundle\LanguageBundle;
use Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReaderInterface;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class StubLanguageBundle extends LanguageBundle
{
public function __construct(StructuredBundleReaderInterface $reader)
{
parent::__construct(realpath(__DIR__ . '/../../Resources/data/lang'), $reader);
}
}

View File

@ -0,0 +1,26 @@
<?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\Intl\ResourceBundle\Stub;
use Symfony\Component\Intl\ResourceBundle\LocaleBundle;
use Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReaderInterface;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class StubLocaleBundle extends LocaleBundle
{
public function __construct(StructuredBundleReaderInterface $reader)
{
parent::__construct(realpath(__DIR__ . '/../../Resources/data/locales'), $reader);
}
}

View File

@ -0,0 +1,26 @@
<?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\Intl\ResourceBundle\Stub;
use Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReaderInterface;
use Symfony\Component\Intl\ResourceBundle\RegionBundle;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class StubRegionBundle extends RegionBundle
{
public function __construct(StructuredBundleReaderInterface $reader)
{
parent::__construct(realpath(__DIR__ . '/../../Resources/data/region'), $reader);
}
}

View File

@ -0,0 +1,96 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\ResourceBundle\Transformer;
use Symfony\Component\Intl\Exception\RuntimeException;
use Symfony\Component\Intl\ResourceBundle\Transformer\Rule\TransformationRuleInterface;
use Symfony\Component\Intl\ResourceBundle\Writer\PhpBundleWriter;
/**
* Compiles a number of resource bundles based on predefined compilation rules.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class BundleTransformer
{
/**
* @var TransformationRuleInterface[]
*/
private $rules = array();
/**
* Adds a new compilation rule.
*
* @param TransformationRuleInterface $rule The compilation rule.
*/
public function addRule(TransformationRuleInterface $rule)
{
$this->rules[] = $rule;
}
/**
* Runs the compilation with the given compilation context.
*
* @param CompilationContextInterface $context The context storing information
* needed to run the compilation.
*
* @throws RuntimeException If any of the files to be compiled by the loaded
* compilation rules does not exist.
*/
public function compileBundles(CompilationContextInterface $context)
{
$filesystem = $context->getFilesystem();
$compiler = $context->getCompiler();
$filesystem->remove($context->getBinaryDir());
$filesystem->mkdir($context->getBinaryDir());
foreach ($this->rules as $rule) {
$filesystem->mkdir($context->getBinaryDir() . '/' . $rule->getBundleName());
$resources = (array) $rule->beforeCompile($context);
foreach ($resources as $resource) {
if (!file_exists($resource)) {
throw new RuntimeException(sprintf(
'The file "%s" to be compiled by %s does not exist.',
$resource,
get_class($rule)
));
}
$compiler->compile($resource, $context->getBinaryDir() . '/' . $rule->getBundleName());
}
$rule->afterCompile($context);
}
}
public function createStubs(StubbingContextInterface $context)
{
$filesystem = $context->getFilesystem();
$phpWriter = new PhpBundleWriter();
$filesystem->remove($context->getStubDir());
$filesystem->mkdir($context->getStubDir());
foreach ($this->rules as $rule) {
$filesystem->mkdir($context->getStubDir() . '/' . $rule->getBundleName());
$data = $rule->beforeCreateStub($context);
$phpWriter->write($context->getStubDir() . '/' . $rule->getBundleName(), 'en', $data);
$rule->afterCreateStub($context);
}
}
}

View File

@ -0,0 +1,97 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\ResourceBundle\Transformer;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Intl\ResourceBundle\Compiler\ResourceBundleCompilerInterface;
/**
* Default implementation of {@link CompilationContextInterface}.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class CompilationContext implements CompilationContextInterface
{
/**
* @var string
*/
private $sourceDir;
/**
* @var string
*/
private $binaryDir;
/**
* @var FileSystem
*/
private $filesystem;
/**
* @var ResourceBundleCompilerInterface
*/
private $compiler;
/**
* @var string
*/
private $icuVersion;
public function __construct($sourceDir, $binaryDir, Filesystem $filesystem, ResourceBundleCompilerInterface $compiler, $icuVersion)
{
$this->sourceDir = $sourceDir;
$this->binaryDir = $binaryDir;
$this->filesystem = $filesystem;
$this->compiler = $compiler;
$this->icuVersion = $icuVersion;
}
/**
* {@inheritdoc}
*/
public function getSourceDir()
{
return $this->sourceDir;
}
/**
* {@inheritdoc}
*/
public function getBinaryDir()
{
return $this->binaryDir;
}
/**
* {@inheritdoc}
*/
public function getFilesystem()
{
return $this->filesystem;
}
/**
* {@inheritdoc}
*/
public function getCompiler()
{
return $this->compiler;
}
/**
* {@inheritdoc}
*/
public function getIcuVersion()
{
return $this->icuVersion;
}
}

View File

@ -0,0 +1,56 @@
<?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\Intl\ResourceBundle\Transformer;
/**
* Stores contextual information for resource bundle compilation.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
interface CompilationContextInterface
{
/**
* Returns the directory where the source versions of the resource bundles
* are stored.
*
* @return string An absolute path to a directory.
*/
public function getSourceDir();
/**
* Returns the directory where the binary resource bundles are stored.
*
* @return string An absolute path to a directory.
*/
public function getBinaryDir();
/**
* Returns a tool for manipulating the filesystem.
*
* @return \Symfony\Component\Filesystem\Filesystem The filesystem manipulator.
*/
public function getFilesystem();
/**
* Returns a resource bundle compiler.
*
* @return \Symfony\Component\Intl\ResourceBundle\Compiler\ResourceBundleCompilerInterface The loaded resource bundle compiler.
*/
public function getCompiler();
/**
* Returns the ICU version of the bundles being converted.
*
* @return string The ICU version string.
*/
public function getIcuVersion();
}

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\Intl\ResourceBundle\Transformer\Rule;
use Symfony\Component\Intl\Intl;
use Symfony\Component\Intl\ResourceBundle\CurrencyBundle;
use Symfony\Component\Intl\ResourceBundle\Transformer\CompilationContextInterface;
use Symfony\Component\Intl\ResourceBundle\Transformer\StubbingContextInterface;
/**
* The rule for compiling the currency bundle.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class CurrencyBundleTransformationRule implements TransformationRuleInterface
{
/**
* {@inheritdoc}
*/
public function getBundleName()
{
return 'curr';
}
/**
* {@inheritdoc}
*/
public function beforeCompile(CompilationContextInterface $context)
{
// The currency data is contained in the locales and misc bundles
// in ICU <= 4.2
if (version_compare($context->getIcuVersion(), '4.2', '<=')) {
return array(
$context->getSourceDir() . '/misc/supplementalData.txt',
$context->getSourceDir() . '/locales'
);
}
return $context->getSourceDir() . '/curr';
}
/**
* {@inheritdoc}
*/
public function afterCompile(CompilationContextInterface $context)
{
// \ResourceBundle does not like locale names with uppercase chars, so rename
// the resource file
// See: http://bugs.php.net/bug.php?id=54025
$fileName = $context->getBinaryDir() . '/curr/supplementalData.res';
$fileNameLower = $context->getBinaryDir() . '/curr/supplementaldata.res';
$context->getFilesystem()->rename($fileName, $fileNameLower);
}
/**
* {@inheritdoc}
*/
public function beforeCreateStub(StubbingContextInterface $context)
{
$currencies = array();
$currencyBundle = Intl::getCurrencyBundle();
foreach ($currencyBundle->getCurrencyNames('en') as $code => $name) {
$currencies[$code] = array(
CurrencyBundle::INDEX_NAME => $name,
CurrencyBundle::INDEX_SYMBOL => $currencyBundle->getCurrencySymbol('en', $code),
CurrencyBundle::INDEX_FRACTION_DIGITS => $currencyBundle->getFractionDigits($code),
CurrencyBundle::INDEX_ROUNDING_INCREMENT => $currencyBundle->getRoundingIncrement($code),
);
}
return array(
'Currencies' => $currencies,
);
}
/**
* {@inheritdoc}
*/
public function afterCreateStub(StubbingContextInterface $context)
{
}
}

View File

@ -0,0 +1,70 @@
<?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\Intl\ResourceBundle\Transformer\Rule;
use Symfony\Component\Intl\Intl;
use Symfony\Component\Intl\ResourceBundle\Transformer\CompilationContextInterface;
use Symfony\Component\Intl\ResourceBundle\Transformer\StubbingContextInterface;
/**
* The rule for compiling the language bundle.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class LanguageBundleTransformationRule implements TransformationRuleInterface
{
/**
* {@inheritdoc}
*/
public function getBundleName()
{
return 'lang';
}
/**
* {@inheritdoc}
*/
public function beforeCompile(CompilationContextInterface $context)
{
// The language data is contained in the locales bundle in ICU <= 4.2
if (version_compare($context->getIcuVersion(), '4.2', '<=')) {
return $context->getSourceDir() . '/locales';
}
return $context->getSourceDir() . '/lang';
}
/**
* {@inheritdoc}
*/
public function afterCompile(CompilationContextInterface $context)
{
}
/**
* {@inheritdoc}
*/
public function beforeCreateStub(StubbingContextInterface $context)
{
return array(
'Languages' => Intl::getLanguageBundle()->getLanguageNames('en'),
'Scripts' => Intl::getLanguageBundle()->getScriptNames('en'),
);
}
/**
* {@inheritdoc}
*/
public function afterCreateStub(StubbingContextInterface $context)
{
}
}

View File

@ -0,0 +1,251 @@
<?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\Intl\ResourceBundle\Transformer\Rule;
use Symfony\Component\Intl\Exception\RuntimeException;
use Symfony\Component\Intl\Intl;
use Symfony\Component\Intl\ResourceBundle\Transformer\CompilationContextInterface;
use Symfony\Component\Intl\ResourceBundle\Transformer\StubbingContextInterface;
use Symfony\Component\Intl\ResourceBundle\Writer\TextBundleWriter;
/**
* The rule for compiling the locale bundle.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class LocaleBundleTransformationRule implements TransformationRuleInterface
{
/**
* @var \Symfony\Component\Intl\ResourceBundle\LanguageBundleInterface
*/
private $languageBundle;
/**
* @var \Symfony\Component\Intl\ResourceBundle\RegionBundleInterface
*/
private $regionBundle;
public function __construct()
{
$this->languageBundle = Intl::getLanguageBundle();
$this->regionBundle = Intl::getRegionBundle();
}
/**
* {@inheritdoc}
*/
public function getBundleName()
{
return 'locales';
}
/**
* {@inheritdoc}
*/
public function beforeCompile(CompilationContextInterface $context)
{
$tempDir = sys_get_temp_dir() . '/icu-data-locales';
$context->getFilesystem()->remove($tempDir);
$context->getFilesystem()->mkdir($tempDir);
$this->generateTextFiles($tempDir, $this->scanLocales($context));
return $tempDir;
}
/**
* {@inheritdoc}
*/
public function afterCompile(CompilationContextInterface $context)
{
$context->getFilesystem()->remove(sys_get_temp_dir() . '/icu-data-locales');
}
/**
* {@inheritdoc}
*/
public function beforeCreateStub(StubbingContextInterface $context)
{
return array(
'Locales' => Intl::getLocaleBundle()->getLocaleNames('en'),
);
}
/**
* {@inheritdoc}
*/
public function afterCreateStub(StubbingContextInterface $context)
{
}
private function scanLocales(CompilationContextInterface $context)
{
$tempDir = sys_get_temp_dir() . '/icu-data-locales-source';
$context->getFilesystem()->remove($tempDir);
$context->getFilesystem()->mkdir($tempDir);
// Temporarily generate the resource bundles
$context->getCompiler()->compile($context->getSourceDir() . '/locales', $tempDir);
// Discover the list of supported locales, which are the names of the resource
// bundles in the "locales" directory
$locales = glob($tempDir . '/*.res');
// Remove file extension and sort
array_walk($locales, function (&$locale) { $locale = basename($locale, '.res'); });
sort($locales);
// Delete unneeded locales
foreach ($locales as $key => $locale) {
// Delete all aliases from the list
// i.e., "az_AZ" is an alias for "az_Latn_AZ"
$content = file_get_contents($context->getSourceDir() . '/locales/' . $locale . '.txt');
// The key "%%ALIAS" is not accessible through the \ResourceBundle class,
// so look in the original .txt file instead
if (strpos($content, '%%ALIAS') !== false) {
unset($locales[$key]);
}
// Delete locales that have no content (i.e. only "Version" key)
$bundle = new \ResourceBundle($locale, $tempDir);
if (null === $bundle) {
throw new RuntimeException('The resource bundle for locale ' . $locale . ' could not be loaded from directory ' . $tempDir);
}
// There seems to be no other way for identifying all keys in this specific
// resource bundle
if (array_keys(iterator_to_array($bundle)) === array('Version')) {
unset($locales[$key]);
}
}
$context->getFilesystem()->remove($tempDir);
return $locales;
}
private function generateTextFiles($targetDirectory, array $locales)
{
$displayLocales = array_unique(array_merge(
$this->languageBundle->getLocales(),
$this->regionBundle->getLocales()
));
$txtWriter = new TextBundleWriter();
// Generate a list of locale names in the language of each display locale
// Each locale name has the form: "Language (Script, Region, Variant1, ...)
// Script, Region and Variants are optional. If none of them is available,
// the braces are not printed.
foreach ($displayLocales as $displayLocale) {
// Don't include ICU's root resource bundle
if ('root' === $displayLocale) {
continue;
}
$names = array();
foreach ($locales as $locale) {
// Don't include ICU's root resource bundle
if ($locale === 'root') {
continue;
}
if (null !== ($name = $this->generateLocaleName($locale, $displayLocale))) {
$names[$locale] = $name;
}
}
// If no names could be generated for the current locale, skip it
if (0 === count($names)) {
continue;
}
$txtWriter->write($targetDirectory, $displayLocale, array('Locales' => $names));
}
}
private function generateLocaleName($locale, $displayLocale)
{
$name = null;
$lang = \Locale::getPrimaryLanguage($locale);
$script = \Locale::getScript($locale);
$region = \Locale::getRegion($locale);
$variants = \Locale::getAllVariants($locale);
// Currently the only available variant is POSIX, which we don't want
// to include in the list
if (count($variants) > 0) {
return null;
}
// Some languages are translated together with their region,
// i.e. "en_GB" is translated as "British English"
// we don't include these languages though because they mess up
// the name sorting
// $name = $this->langBundle->getLanguageName($displayLocale, $lang, $region);
// Some languages are simply not translated
// Example: "az" (Azerbaijani) has no translation in "af" (Afrikaans)
if (null === ($name = $this->languageBundle->getLanguageName($displayLocale, $lang))) {
return null;
}
// "as" (Assamese) has no "Variants" block
//if (!$langBundle->get('Variants')) {
// continue;
//}
$extras = array();
// Discover the name of the script part of the locale
// i.e. in zh_Hans_MO, "Hans" is the script
if ($script) {
// Some scripts are not translated into every language
if (null === ($scriptName = $this->languageBundle->getScriptName($displayLocale, $script, $lang))) {
return null;
}
$extras[] = $scriptName;
}
// Discover the name of the region part of the locale
// i.e. in de_AT, "AT" is the region
if ($region) {
// Some regions are not translated into every language
if (null === ($regionName = $this->regionBundle->getCountryName($displayLocale, $region))) {
return null;
}
$extras[] = $regionName;
}
if (count($extras) > 0) {
// Remove any existing extras
// For example, in German, zh_Hans is "Chinesisch (vereinfacht)".
// The latter is the script part which is already included in the
// extras and will be appended again with the other extras.
if (preg_match('/^(.+)\s+\([^\)]+\)$/', $name, $matches)) {
$name = $matches[1];
}
$name .= ' ('.implode(', ', $extras).')';
}
return $name;
}
}

View File

@ -0,0 +1,69 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\ResourceBundle\Transformer\Rule;
use Symfony\Component\Intl\Intl;
use Symfony\Component\Intl\ResourceBundle\Transformer\CompilationContextInterface;
use Symfony\Component\Intl\ResourceBundle\Transformer\StubbingContextInterface;
/**
* The rule for compiling the region bundle.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class RegionBundleTransformationRule implements TransformationRuleInterface
{
/**
* {@inheritdoc}
*/
public function getBundleName()
{
return 'region';
}
/**
* {@inheritdoc}
*/
public function beforeCompile(CompilationContextInterface $context)
{
// The region data is contained in the locales bundle in ICU <= 4.2
if (version_compare($context->getIcuVersion(), '4.2', '<=')) {
return $context->getSourceDir() . '/locales';
}
return $context->getSourceDir() . '/region';
}
/**
* {@inheritdoc}
*/
public function afterCompile(CompilationContextInterface $context)
{
}
/**
* {@inheritdoc}
*/
public function beforeCreateStub(StubbingContextInterface $context)
{
return array(
'Countries' => Intl::getRegionBundle()->getCountryNames('en'),
);
}
/**
* {@inheritdoc}
*/
public function afterCreateStub(StubbingContextInterface $context)
{
}
}

View File

@ -0,0 +1,70 @@
<?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\Intl\ResourceBundle\Transformer\Rule;
use Symfony\Component\Intl\ResourceBundle\Transformer\CompilationContextInterface;
use Symfony\Component\Intl\ResourceBundle\Transformer\StubbingContextInterface;
/**
* Contains instruction for compiling a resource bundle.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
interface TransformationRuleInterface
{
/**
* Returns the name of the compiled resource bundle.
*
* @return string The name of the bundle.
*/
public function getBundleName();
/**
* Runs instructions to be executed before compiling the sources of the
* resource bundle.
*
* @param CompilationContextInterface $context The contextual information of
* the compilation.
*
* @return string[] The source directories/files of the bundle.
*/
public function beforeCompile(CompilationContextInterface $context);
/**
* Runs instructions to be executed after compiling the sources of the
* resource bundle.
*
* @param CompilationContextInterface $context The contextual information of
* the compilation.
*/
public function afterCompile(CompilationContextInterface $context);
/**
* Runs instructions to be executed before creating the stub version of the
* resource bundle.
*
* @param StubbingContextInterface $context The contextual information of
* the compilation.
*
* @return mixed The data to include in the stub version.
*/
public function beforeCreateStub(StubbingContextInterface $context);
/**
* Runs instructions to be executed after creating the stub version of the
* resource bundle.
*
* @param StubbingContextInterface $context The contextual information of
* the compilation.
*/
public function afterCreateStub(StubbingContextInterface $context);
}

View File

@ -0,0 +1,80 @@
<?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\Intl\ResourceBundle\Transformer;
use Symfony\Component\Filesystem\Filesystem;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class StubbingContext implements StubbingContextInterface
{
/**
* @var string
*/
private $binaryDir;
/**
* @var string
*/
private $stubDir;
/**
* @var Filesystem
*/
private $filesystem;
/**
* @var string
*/
private $icuVersion;
public function __construct($binaryDir, $stubDir, Filesystem $filesystem, $icuVersion)
{
$this->binaryDir = $binaryDir;
$this->stubDir = $stubDir;
$this->filesystem = $filesystem;
$this->icuVersion = $icuVersion;
}
/**
* {@inheritdoc}
*/
public function getBinaryDir()
{
return $this->binaryDir;
}
/**
* {@inheritdoc}
*/
public function getStubDir()
{
return $this->stubDir;
}
/**
* {@inheritdoc}
*/
public function getFilesystem()
{
return $this->filesystem;
}
/**
* {@inheritdoc}
*/
public function getIcuVersion()
{
return $this->icuVersion;
}
}

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\Intl\ResourceBundle\Transformer;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
interface StubbingContextInterface
{
/**
* Returns the directory where the binary resource bundles are stored.
*
* @return string An absolute path to a directory.
*/
public function getBinaryDir();
/**
* Returns the directory where the stub resource bundles are stored.
*
* @return string An absolute path to a directory.
*/
public function getStubDir();
/**
* Returns a tool for manipulating the filesystem.
*
* @return \Symfony\Component\Filesystem\Filesystem The filesystem manipulator.
*/
public function getFilesystem();
/**
* Returns the ICU version of the bundles being converted.
*
* @return string The ICU version string.
*/
public function getIcuVersion();
}

View File

@ -0,0 +1,79 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\ResourceBundle\Util;
use Symfony\Component\Intl\Exception\BadMethodCallException;
/**
* Work-around for a bug in PHP's \ResourceBundle implementation.
*
* More information can be found on https://bugs.php.net/bug.php?id=64356.
* This class can be removed once that bug is fixed.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class ArrayAccessibleResourceBundle implements \ArrayAccess, \IteratorAggregate, \Countable
{
private $bundleImpl;
public function __construct(\ResourceBundle $bundleImpl)
{
$this->bundleImpl = $bundleImpl;
}
public function get($offset, $fallback = null)
{
$value = $this->bundleImpl->get($offset, $fallback);
return $value instanceof \ResourceBundle ? new static($value) : $value;
}
public function offsetExists($offset)
{
return null !== $this->bundleImpl[$offset];
}
public function offsetGet($offset)
{
return $this->get($offset);
}
public function offsetSet($offset, $value)
{
throw new BadMethodCallException('Resource bundles cannot be modified.');
}
public function offsetUnset($offset)
{
throw new BadMethodCallException('Resource bundles cannot be modified.');
}
public function getIterator()
{
return $this->bundleImpl;
}
public function count()
{
return $this->bundleImpl->count();
}
public function getErrorCode()
{
return $this->bundleImpl->getErrorCode();
}
public function getErrorMessage()
{
return $this->bundleImpl->getErrorMessage();
}
}

View File

@ -0,0 +1,33 @@
<?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\Intl\ResourceBundle\Util;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class RecursiveArrayAccess
{
public static function get($array, array $indices)
{
foreach ($indices as $index) {
if (!$array instanceof \ArrayAccess && !is_array($array)) {
return null;
}
$array = $array[$index];
}
return $array;
}
private function __construct() {}
}

View File

@ -0,0 +1,88 @@
<?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\Intl\ResourceBundle\Util;
use Symfony\Component\Intl\Exception\OutOfBoundsException;
/**
* Implements a ring buffer.
*
* A ring buffer is an array-like structure with a fixed size. If the buffer
* is full, the next written element overwrites the first bucket in the buffer,
* then the second and so on.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class RingBuffer implements \ArrayAccess
{
private $values = array();
private $indices = array();
private $cursor = 0;
private $size;
public function __construct($size)
{
$this->size = $size;
}
/**
* {@inheritdoc}
*/
public function offsetExists($key)
{
return isset($this->indices[$key]);
}
/**
* {@inheritdoc}
*/
public function offsetGet($key)
{
if (!isset($this->indices[$key])) {
throw new OutOfBoundsException(sprintf(
'The index "%s" does not exist.',
$key
));
}
return $this->values[$this->indices[$key]];
}
/**
* {@inheritdoc}
*/
public function offsetSet($key, $value)
{
if (false !== ($keyToRemove = array_search($this->cursor, $this->indices))) {
unset($this->indices[$keyToRemove]);
}
$this->values[$this->cursor] = $value;
$this->indices[$key] = $this->cursor;
$this->cursor = ($this->cursor + 1) % $this->size;
}
/**
* {@inheritdoc}
*/
public function offsetUnset($key)
{
if (isset($this->indices[$key])) {
$this->values[$this->indices[$key]] = null;
unset($this->indices[$key]);
}
}
}

View File

@ -0,0 +1,29 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\ResourceBundle\Writer;
/**
* Writes resource bundle files.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
interface BundleWriterInterface
{
/**
* Writes data to a resource bundle.
*
* @param string $path The path to the resource bundle.
* @param string $locale The locale to (over-)write.
* @param mixed $data The data to write.
*/
public function write($path, $locale, $data);
}

View File

@ -0,0 +1,50 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\ResourceBundle\Writer;
/**
* Writes .php resource bundles.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class PhpBundleWriter implements BundleWriterInterface
{
/**
* {@inheritdoc}
*/
function write($path, $locale, $data)
{
$template = <<<TEMPLATE
<?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.
*/
return %s;
TEMPLATE;
$data = var_export($data, true);
$data = preg_replace('/array \(/', 'array(', $data);
$data = preg_replace('/\n {1,10}array\(/', 'array(', $data);
$data = preg_replace('/ /', ' ', $data);
$data = sprintf($template, $data);
file_put_contents($path.'/'.$locale.'.php', $data);
}
}

View File

@ -0,0 +1,202 @@
<?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\Intl\ResourceBundle\Writer;
/**
* Writes .txt resource bundles.
*
* The resulting files can be converted to binary .res files using the
* {@link \Symfony\Component\Intl\ResourceBundle\Transformer\BundleCompiler}.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @see http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt
*/
class TextBundleWriter implements BundleWriterInterface
{
/**
* {@inheritdoc}
*/
public function write($path, $locale, $data)
{
$file = fopen($path.'/'.$locale.'.txt', 'w');
$this->writeResourceBundle($file, $locale, $data);
fclose($file);
}
/**
* Writes a "resourceBundle" node.
*
* @param resource $file The file handle to write to.
* @param string $bundleName The name of the bundle.
* @param mixed $value The value of the node.
*
* @see http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt
*/
private function writeResourceBundle($file, $bundleName, $value)
{
fwrite($file, $bundleName);
$this->writeTable($file, $value, 0);
fwrite($file, "\n");
}
/**
* Writes a "resource" node.
*
* @param resource $file The file handle to write to.
* @param mixed $value The value of the node.
* @param integer $indentation The number of levels to indent.
* @param Boolean $requireBraces Whether to require braces to be printed
* around the value.
*
* @see http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt
*/
private function writeResource($file, $value, $indentation, $requireBraces = true)
{
if (is_int($value)) {
$this->writeInteger($file, $value);
return;
}
if (is_array($value)) {
if (count($value) === count(array_filter($value, 'is_int'))) {
$this->writeIntVector($file, $value, $indentation);
return;
}
$keys = array_keys($value);
if (count($keys) === count(array_filter($keys, 'is_int'))) {
$this->writeArray($file, $value, $indentation);
return;
}
$this->writeTable($file, $value, $indentation);
return;
}
if (is_bool($value)) {
$value = $value ? 'true' : 'false';
}
$this->writeString($file, (string) $value, $requireBraces);
}
/**
* Writes an "integer" node.
*
* @param resource $file The file handle to write to.
* @param integer $value The value of the node.
*
* @see http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt
*/
private function writeInteger($file, $value)
{
fprintf($file, ':int{%d}', $value);
}
/**
* Writes an "intvector" node.
*
* @param resource $file The file handle to write to.
* @param array $value The value of the node.
* @param integer $indentation The number of levels to indent.
*
* @see http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt
*/
private function writeIntVector($file, array $value, $indentation)
{
fwrite($file, ":intvector{\n");
foreach ($value as $int) {
fprintf($file, "%s%d,\n", str_repeat(' ', $indentation + 1), $int);
}
fprintf($file, "%s}", str_repeat(' ', $indentation));
}
/**
* Writes a "string" node.
*
* @param resource $file The file handle to write to.
* @param string $value The value of the node.
* @param Boolean $requireBraces Whether to require braces to be printed
* around the value.
*
* @see http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt
*/
private function writeString($file, $value, $requireBraces = true)
{
if ($requireBraces) {
fprintf($file, '{"%s"}', $value);
return;
}
fprintf($file, '"%s"', $value);
}
/**
* Writes an "array" node.
*
* @param resource $file The file handle to write to.
* @param array $value The value of the node.
* @param integer $indentation The number of levels to indent.
*
* @see http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt
*/
private function writeArray($file, array $value, $indentation)
{
fwrite($file, "{\n");
foreach ($value as $entry) {
fwrite($file, str_repeat(' ', $indentation + 1));
$this->writeResource($file, $entry, $indentation + 1, false);
fwrite($file, ",\n");
}
fprintf($file, '%s}', str_repeat(' ', $indentation));
}
/**
* Writes a "table" node.
*
* @param resource $file The file handle to write to.
* @param array $value The value of the node.
* @param integer $indentation The number of levels to indent.
*/
private function writeTable($file, array $value, $indentation)
{
fwrite($file, "{\n");
foreach ($value as $key => $entry) {
fwrite($file, str_repeat(' ', $indentation + 1));
fwrite($file, $key);
$this->writeResource($file, $entry, $indentation + 1);
fwrite($file, "\n");
}
fprintf($file, '%s}', str_repeat(' ', $indentation));
}
}

View File

@ -0,0 +1,69 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
define('LINE_WIDTH', 75);
define('LINE', str_repeat('-', LINE_WIDTH) . "\n");
function bailout($message)
{
echo wordwrap($message, LINE_WIDTH) . " Aborting.\n";
exit(1);
}
function strip_minor_versions($version)
{
preg_match('/^(?P<version>[0-9]\.[0-9]|[0-9]{2,})/', $version, $matches);
return $matches['version'];
}
function centered($text)
{
$padding = (int) ((LINE_WIDTH - strlen($text))/2);
return str_repeat(' ', $padding) . $text;
}
function cd($dir)
{
if (false === chdir($dir)) {
bailout("Could not switch to directory $dir.");
}
}
function run($command)
{
exec($command, $output, $status);
if (0 !== $status) {
$output = implode("\n", $output);
echo "Error:\n" . LINE . "$output\n" . LINE;
bailout("\"$command\" failed.");
}
}
function get_icu_version_from_genrb($genrb)
{
exec($genrb . ' --version 2>&1', $output, $status);
if (0 !== $status) {
bailout($genrb . ' failed.');
}
if (!preg_match('/ICU version ([\d\.]+)/', implode('', $output), $matches)) {
return null;
}
return $matches[1];
}

View File

@ -0,0 +1,18 @@
<?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.
*/
use Symfony\Component\Intl\Intl;
require_once __DIR__ . '/common.php';
require_once __DIR__ . '/autoload.php';
echo "ICU version: ";
echo Intl::getIcuVersion() . "\n";

View File

@ -1,9 +1,9 @@
; ICU data source URLs
; We use always the latest release of a major version.
4.0 = http://source.icu-project.org/repos/icu/icu/tags/release-4-0-1/source/data
4.2 = http://source.icu-project.org/repos/icu/icu/tags/release-4-2-1/source/data
4.4 = http://source.icu-project.org/repos/icu/icu/tags/release-4-4-2/source/data
4.6 = http://source.icu-project.org/repos/icu/icu/tags/release-4-6-1/source/data
4.8 = http://source.icu-project.org/repos/icu/icu/tags/release-4-8-1-1/source/data
49 = http://source.icu-project.org/repos/icu/icu/tags/release-49-1-2/source/data
50 = http://source.icu-project.org/repos/icu/icu/tags/release-50-1/source/data
4.0 = http://source.icu-project.org/repos/icu/icu/tags/release-4-0-1/source
4.2 = http://source.icu-project.org/repos/icu/icu/tags/release-4-2-1/source
4.4 = http://source.icu-project.org/repos/icu/icu/tags/release-4-4-2/source
4.6 = http://source.icu-project.org/repos/icu/icu/tags/release-4-6-1/source
4.8 = http://source.icu-project.org/repos/icu/icu/tags/release-4-8-1-1/source
49 = http://source.icu-project.org/repos/icu/icu/tags/release-49-1-2/source
50 = http://source.icu-project.org/repos/icu/icu/tags/release-50-1/source

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.
*/
use Symfony\Component\Icu\IcuData;
use Symfony\Component\Intl\Intl;
use Symfony\Component\Intl\ResourceBundle\Compiler\BundleCompiler;
use Symfony\Component\Intl\ResourceBundle\Transformer\BundleTransformer;
use Symfony\Component\Intl\ResourceBundle\Transformer\CompilationContext;
use Symfony\Component\Intl\ResourceBundle\Transformer\Rule\CurrencyBundleTransformationRule;
use Symfony\Component\Intl\ResourceBundle\Transformer\Rule\LanguageBundleTransformationRule;
use Symfony\Component\Intl\ResourceBundle\Transformer\Rule\LocaleBundleTransformationRule;
use Symfony\Component\Intl\ResourceBundle\Transformer\Rule\RegionBundleTransformationRule;
use Symfony\Component\Intl\Util\SvnRepository;
use Symfony\Component\Filesystem\Filesystem;
require_once __DIR__ . '/common.php';
require_once __DIR__ . '/autoload.php';
if (1 !== $GLOBALS['argc']) {
bailout(<<<MESSAGE
Usage: php update-icu-component.php
Updates the ICU data for Symfony2 to the latest version of the ICU version
included in the intl extension. For example, if your intl extension includes
ICU 4.8, the script will download the latest data available for ICU 4.8.
For running this script, the intl extension must be loaded and all vendors
must have been installed through composer:
composer install --dev
MESSAGE
);
}
echo LINE;
echo centered("ICU Resource Bundle Compilation") . "\n";
echo LINE;
if (!Intl::isExtensionLoaded()) {
bailout('The intl extension for PHP is not installed.');
}
if (!class_exists('\Symfony\Component\Icu\IcuData')) {
bailout('You must run "composer update --dev" before running this script.');
}
$icuVersionInPhp = Intl::getIcuVersion();
echo "Found intl extension with ICU version $icuVersionInPhp.\n";
$shortIcuVersion = strip_minor_versions($icuVersionInPhp);
$urls = parse_ini_file(__DIR__ . '/icu.ini');
if (!isset($urls[$shortIcuVersion])) {
bailout('The version ' . $shortIcuVersion . ' is not available in the icu.ini file.');
}
echo "icu.ini parsed. Available versions:\n";
foreach ($urls as $urlVersion => $url) {
echo " $urlVersion\n";
}
echo "Starting SVN checkout for version $shortIcuVersion. This may take a while...\n";
$svn = SvnRepository::download($urls[$shortIcuVersion], $shortIcuVersion);
echo "SVN checkout to {$svn->getPath()} complete.\n";
// Always build genrb so that we can determine the ICU version of the
// download by running genrb --version
echo "Building genrb.\n";
cd($svn->getPath());
echo "Running make clean...\n";
run('make clean');
echo "Running configure...\n";
run('./configure 2>&1');
cd($svn->getPath() . '/tools');
echo "Running make...\n";
run('make 2>&1');
$genrb = $svn->getPath() . '/bin/genrb';
echo "Using $genrb.\n";
$icuVersionInDownload = get_icu_version_from_genrb($genrb);
echo "Preparing resource bundle compilation (version $icuVersionInDownload)...\n";
$context = new CompilationContext(
$svn->getPath() . '/data',
IcuData::getResourceDirectory(),
new Filesystem(),
new BundleCompiler($genrb),
$icuVersionInDownload
);
$transformer = new BundleTransformer();
$transformer->addRule(new LanguageBundleTransformationRule());
$transformer->addRule(new RegionBundleTransformationRule());
$transformer->addRule(new CurrencyBundleTransformationRule());
$transformer->addRule(new LocaleBundleTransformationRule());
echo "Starting resource bundle compilation. This may take a while...\n";
$transformer->compileBundles($context);
echo "Resource bundle compilation complete.\n";
$svnInfo = <<<SVN_INFO
SVN information
===============
URL: {$svn->getUrl()}
Revision: {$svn->getLastCommit()->getRevision()}
Author: {$svn->getLastCommit()->getAuthor()}
Date: {$svn->getLastCommit()->getDate()}
SVN_INFO;
$svnInfoFile = $context->getBinaryDir() . '/svn-info.txt';
file_put_contents($svnInfoFile, $svnInfo);
echo "Wrote $svnInfoFile.\n";
$versionFile = $context->getBinaryDir() . '/version.txt';
file_put_contents($versionFile, "$icuVersionInDownload\n");
echo "Wrote $versionFile.\n";
echo "Done.\n";

View File

@ -0,0 +1,95 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Icu\IcuData;
use Symfony\Component\Intl\Intl;
use Symfony\Component\Intl\ResourceBundle\Transformer\BundleTransformer;
use Symfony\Component\Intl\ResourceBundle\Transformer\Rule\CurrencyBundleTransformationRule;
use Symfony\Component\Intl\ResourceBundle\Transformer\Rule\LanguageBundleTransformationRule;
use Symfony\Component\Intl\ResourceBundle\Transformer\Rule\LocaleBundleTransformationRule;
use Symfony\Component\Intl\ResourceBundle\Transformer\Rule\RegionBundleTransformationRule;
use Symfony\Component\Intl\ResourceBundle\Transformer\StubbingContext;
require_once __DIR__ . '/common.php';
require_once __DIR__ . '/autoload.php';
if (1 !== $GLOBALS['argc']) {
bailout(<<<MESSAGE
Usage: php update-stubs.php
Creates resource bundle stubs from the resource bundles in the Icu component.
For running this script, the intl extension must be loaded and all vendors
must have been installed through composer:
composer install --dev
MESSAGE
);
}
echo LINE;
echo centered("ICU Resource Bundle Stub Creation") . "\n";
echo LINE;
if (!Intl::isExtensionLoaded()) {
bailout('The intl extension for PHP is not installed.');
}
if (!class_exists('\Symfony\Component\Icu\IcuData')) {
bailout('You must run "composer update --dev" before running this script.');
}
$shortIcuVersionInPhp = strip_minor_versions(Intl::getIcuVersion());
$shortIcuVersionInIntlComponent = strip_minor_versions(Intl::getStubIcuVersion());
$shortIcuVersionInIcuComponent = strip_minor_versions(IcuData::getVersion());
if ($shortIcuVersionInPhp !== $shortIcuVersionInIcuComponent) {
bailout("The ICU version of the component ($shortIcuVersionInIcuComponent) does not match the ICU version in the intl extension ($shortIcuVersionInPhp).");
}
if ($shortIcuVersionInIntlComponent !== $shortIcuVersionInIcuComponent) {
bailout("The ICU version of the component ($shortIcuVersionInIcuComponent) does not match the ICU version of the stub classes in the Intl component ($shortIcuVersionInIntlComponent).");
}
$icuVersionInIcuComponent = IcuData::getVersion();
echo "Compiling stubs for ICU version $icuVersionInIcuComponent.";
echo "Preparing stub creation...\n";
$context = new StubbingContext(
IcuData::getResourceDirectory(),
realpath(__DIR__ . '/../data'),
new Filesystem(),
$icuVersionInIcuComponent
);
$transformer = new BundleTransformer();
$transformer->addRule(new LanguageBundleTransformationRule());
$transformer->addRule(new RegionBundleTransformationRule());
$transformer->addRule(new CurrencyBundleTransformationRule());
$transformer->addRule(new LocaleBundleTransformationRule());
echo "Starting stub creation...\n";
$transformer->createStubs($context);
echo "Stub creation complete.\n";
$versionFile = $context->getStubDir() . '/version.txt';
file_put_contents($versionFile, "$icuVersionInIcuComponent\n");
echo "Wrote $versionFile.\n";
echo "Done.\n";

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,707 @@
<?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.
*/
return array(
'Languages' => array(
'ab' => 'Abkhazian',
'ace' => 'Achinese',
'ach' => 'Acoli',
'ada' => 'Adangme',
'ady' => 'Adyghe',
'aa' => 'Afar',
'afh' => 'Afrihili',
'af' => 'Afrikaans',
'afa' => 'Afro-Asiatic Language',
'agq' => 'Aghem',
'ain' => 'Ainu',
'ak' => 'Akan',
'akk' => 'Akkadian',
'sq' => 'Albanian',
'ale' => 'Aleut',
'alg' => 'Algonquian Language',
'tut' => 'Altaic Language',
'am' => 'Amharic',
'egy' => 'Ancient Egyptian',
'grc' => 'Ancient Greek',
'anp' => 'Angika',
'apa' => 'Apache Language',
'ar' => 'Arabic',
'an' => 'Aragonese',
'arc' => 'Aramaic',
'arp' => 'Arapaho',
'arn' => 'Araucanian',
'arw' => 'Arawak',
'hy' => 'Armenian',
'rup' => 'Aromanian',
'art' => 'Artificial Language',
'as' => 'Assamese',
'ast' => 'Asturian',
'asa' => 'Asu',
'ath' => 'Athapascan Language',
'cch' => 'Atsam',
'en_AU' => 'Australian English',
'aus' => 'Australian Language',
'de_AT' => 'Austrian German',
'map' => 'Austronesian Language',
'av' => 'Avaric',
'ae' => 'Avestan',
'awa' => 'Awadhi',
'ay' => 'Aymara',
'az' => 'Azerbaijani',
'ksf' => 'Bafia',
'ban' => 'Balinese',
'bat' => 'Baltic Language',
'bal' => 'Baluchi',
'bm' => 'Bambara',
'bai' => 'Bamileke Language',
'bad' => 'Banda',
'bnt' => 'Bantu',
'bas' => 'Basaa',
'ba' => 'Bashkir',
'eu' => 'Basque',
'btk' => 'Batak',
'bej' => 'Beja',
'be' => 'Belarusian',
'bem' => 'Bemba',
'bez' => 'Bena',
'bn' => 'Bengali',
'ber' => 'Berber',
'bho' => 'Bhojpuri',
'bh' => 'Bihari',
'bik' => 'Bikol',
'bin' => 'Bini',
'bi' => 'Bislama',
'byn' => 'Blin',
'zbl' => 'Blissymbols',
'brx' => 'Bodo',
'bs' => 'Bosnian',
'bra' => 'Braj',
'pt_BR' => 'Brazilian Portuguese',
'br' => 'Breton',
'en_GB' => 'British English',
'bug' => 'Buginese',
'bg' => 'Bulgarian',
'bua' => 'Buriat',
'my' => 'Burmese',
'cad' => 'Caddo',
'en_CA' => 'Canadian English',
'fr_CA' => 'Canadian French',
'yue' => 'Cantonese',
'car' => 'Carib',
'ca' => 'Catalan',
'cau' => 'Caucasian Language',
'cay' => 'Cayuga',
'ceb' => 'Cebuano',
'cel' => 'Celtic Language',
'cai' => 'Central American Indian Language',
'tzm' => 'Central Morocco Tamazight',
'chg' => 'Chagatai',
'cmc' => 'Chamic Language',
'ch' => 'Chamorro',
'ce' => 'Chechen',
'chr' => 'Cherokee',
'chy' => 'Cheyenne',
'chb' => 'Chibcha',
'cgg' => 'Chiga',
'zh' => 'Chinese',
'chn' => 'Chinook Jargon',
'chp' => 'Chipewyan',
'cho' => 'Choctaw',
'cu' => 'Church Slavic',
'chk' => 'Chuukese',
'cv' => 'Chuvash',
'nwc' => 'Classical Newari',
'syc' => 'Classical Syriac',
'ksh' => 'Colognian',
'swb' => 'Comorian',
'swc' => 'Congo Swahili',
'cop' => 'Coptic',
'kw' => 'Cornish',
'co' => 'Corsican',
'cr' => 'Cree',
'mus' => 'Creek',
'crp' => 'Creole or Pidgin',
'crh' => 'Crimean Turkish',
'hr' => 'Croatian',
'cus' => 'Cushitic Language',
'cs' => 'Czech',
'dak' => 'Dakota',
'da' => 'Danish',
'dar' => 'Dargwa',
'day' => 'Dayak',
'del' => 'Delaware',
'din' => 'Dinka',
'dv' => 'Divehi',
'doi' => 'Dogri',
'dgr' => 'Dogrib',
'dra' => 'Dravidian Language',
'dua' => 'Duala',
'nl' => 'Dutch',
'dyu' => 'Dyula',
'dz' => 'Dzongkha',
'frs' => 'Eastern Frisian',
'efi' => 'Efik',
'eka' => 'Ekajuk',
'elx' => 'Elamite',
'ebu' => 'Embu',
'en' => 'English',
'cpe' => 'English-based Creole or Pidgin',
'myv' => 'Erzya',
'eo' => 'Esperanto',
'et' => 'Estonian',
'ee' => 'Ewe',
'ewo' => 'Ewondo',
'fan' => 'Fang',
'fat' => 'Fanti',
'fo' => 'Faroese',
'fj' => 'Fijian',
'fil' => 'Filipino',
'fi' => 'Finnish',
'fiu' => 'Finno-Ugrian Language',
'nl_BE' => 'Flemish',
'fon' => 'Fon',
'fr' => 'French',
'cpf' => 'French-based Creole or Pidgin',
'fur' => 'Friulian',
'ff' => 'Fulah',
'gaa' => 'Ga',
'gl' => 'Galician',
'lg' => 'Ganda',
'gay' => 'Gayo',
'gba' => 'Gbaya',
'gez' => 'Geez',
'ka' => 'Georgian',
'de' => 'German',
'gem' => 'Germanic Language',
'gil' => 'Gilbertese',
'gon' => 'Gondi',
'gor' => 'Gorontalo',
'got' => 'Gothic',
'grb' => 'Grebo',
'el' => 'Greek',
'gn' => 'Guarani',
'gu' => 'Gujarati',
'guz' => 'Gusii',
'gwi' => 'Gwichʼin',
'hai' => 'Haida',
'ht' => 'Haitian',
'ha' => 'Hausa',
'haw' => 'Hawaiian',
'he' => 'Hebrew',
'hz' => 'Herero',
'hil' => 'Hiligaynon',
'him' => 'Himachali',
'hi' => 'Hindi',
'ho' => 'Hiri Motu',
'hit' => 'Hittite',
'hmn' => 'Hmong',
'hu' => 'Hungarian',
'hup' => 'Hupa',
'iba' => 'Iban',
'pt_PT' => 'Iberian Portuguese',
'es_ES' => 'Iberian Spanish',
'is' => 'Icelandic',
'io' => 'Ido',
'ig' => 'Igbo',
'ijo' => 'Ijo',
'ilo' => 'Iloko',
'smn' => 'Inari Sami',
'inc' => 'Indic Language',
'ine' => 'Indo-European Language',
'id' => 'Indonesian',
'inh' => 'Ingush',
'ia' => 'Interlingua',
'ie' => 'Interlingue',
'iu' => 'Inuktitut',
'ik' => 'Inupiaq',
'ira' => 'Iranian Language',
'ga' => 'Irish',
'iro' => 'Iroquoian Language',
'it' => 'Italian',
'ja' => 'Japanese',
'jv' => 'Javanese',
'kaj' => 'Jju',
'dyo' => 'Jola-Fonyi',
'jrb' => 'Judeo-Arabic',
'jpr' => 'Judeo-Persian',
'kbd' => 'Kabardian',
'kea' => 'Kabuverdianu',
'kab' => 'Kabyle',
'kac' => 'Kachin',
'kl' => 'Kalaallisut',
'kln' => 'Kalenjin',
'xal' => 'Kalmyk',
'kam' => 'Kamba',
'kn' => 'Kannada',
'kr' => 'Kanuri',
'kaa' => 'Kara-Kalpak',
'krc' => 'Karachay-Balkar',
'krl' => 'Karelian',
'kar' => 'Karen',
'ks' => 'Kashmiri',
'csb' => 'Kashubian',
'kaw' => 'Kawi',
'kk' => 'Kazakh',
'kha' => 'Khasi',
'km' => 'Khmer',
'khi' => 'Khoisan Language',
'kho' => 'Khotanese',
'ki' => 'Kikuyu',
'kmb' => 'Kimbundu',
'rw' => 'Kinyarwanda',
'ky' => 'Kirghiz',
'tlh' => 'Klingon',
'kv' => 'Komi',
'kg' => 'Kongo',
'kok' => 'Konkani',
'ko' => 'Korean',
'kfo' => 'Koro',
'kos' => 'Kosraean',
'khq' => 'Koyra Chiini',
'ses' => 'Koyraboro Senni',
'kpe' => 'Kpelle',
'kro' => 'Kru',
'kj' => 'Kuanyama',
'kum' => 'Kumyk',
'ku' => 'Kurdish',
'kru' => 'Kurukh',
'kut' => 'Kutenai',
'nmg' => 'Kwasio',
'lad' => 'Ladino',
'lah' => 'Lahnda',
'lam' => 'Lamba',
'lag' => 'Langi',
'lo' => 'Lao',
'la' => 'Latin',
'es_419' => 'Latin American Spanish',
'lv' => 'Latvian',
'lez' => 'Lezghian',
'li' => 'Limburgish',
'ln' => 'Lingala',
'lt' => 'Lithuanian',
'jbo' => 'Lojban',
'nds' => 'Low German',
'dsb' => 'Lower Sorbian',
'loz' => 'Lozi',
'lu' => 'Luba-Katanga',
'lua' => 'Luba-Lulua',
'lui' => 'Luiseno',
'smj' => 'Lule Sami',
'lun' => 'Lunda',
'luo' => 'Luo',
'lus' => 'Lushai',
'lb' => 'Luxembourgish',
'luy' => 'Luyia',
'mk' => 'Macedonian',
'jmc' => 'Machame',
'mad' => 'Madurese',
'mag' => 'Magahi',
'mai' => 'Maithili',
'mak' => 'Makasar',
'mgh' => 'Makhuwa-Meetto',
'kde' => 'Makonde',
'mg' => 'Malagasy',
'ms' => 'Malay',
'ml' => 'Malayalam',
'mt' => 'Maltese',
'mnc' => 'Manchu',
'mdr' => 'Mandar',
'man' => 'Mandingo',
'mni' => 'Manipuri',
'mno' => 'Manobo Language',
'gv' => 'Manx',
'mi' => 'Maori',
'mr' => 'Marathi',
'chm' => 'Mari',
'mh' => 'Marshallese',
'mwr' => 'Marwari',
'mas' => 'Masai',
'myn' => 'Mayan Language',
'men' => 'Mende',
'mer' => 'Meru',
'mic' => 'Micmac',
'dum' => 'Middle Dutch',
'enm' => 'Middle English',
'frm' => 'Middle French',
'gmh' => 'Middle High German',
'mga' => 'Middle Irish',
'min' => 'Minangkabau',
'mwl' => 'Mirandese',
'mis' => 'Miscellaneous Language',
'moh' => 'Mohawk',
'mdf' => 'Moksha',
'mo' => 'Moldavian',
'mkh' => 'Mon-Khmer Language',
'lol' => 'Mongo',
'mn' => 'Mongolian',
'mfe' => 'Morisyen',
'mos' => 'Mossi',
'mun' => 'Munda Language',
'mua' => 'Mundang',
'nqo' => 'NKo',
'nah' => 'Nahuatl',
'naq' => 'Nama',
'na' => 'Nauru',
'nv' => 'Navajo',
'ng' => 'Ndonga',
'nap' => 'Neapolitan',
'ne' => 'Nepali',
'new' => 'Newari',
'nia' => 'Nias',
'nic' => 'Niger-Kordofanian Language',
'ssa' => 'Nilo-Saharan Language',
'niu' => 'Niuean',
'zxx' => 'No linguistic content',
'nog' => 'Nogai',
'nai' => 'North American Indian Language',
'nd' => 'North Ndebele',
'frr' => 'Northern Frisian',
'se' => 'Northern Sami',
'nso' => 'Northern Sotho',
'no' => 'Norwegian',
'nb' => 'Norwegian Bokmål',
'nn' => 'Norwegian Nynorsk',
'nub' => 'Nubian Language',
'nus' => 'Nuer',
'nym' => 'Nyamwezi',
'ny' => 'Nyanja',
'nyn' => 'Nyankole',
'tog' => 'Nyasa Tonga',
'nyo' => 'Nyoro',
'nzi' => 'Nzima',
'oc' => 'Occitan',
'oj' => 'Ojibwa',
'ang' => 'Old English',
'fro' => 'Old French',
'goh' => 'Old High German',
'sga' => 'Old Irish',
'non' => 'Old Norse',
'peo' => 'Old Persian',
'pro' => 'Old Provençal',
'or' => 'Oriya',
'om' => 'Oromo',
'osa' => 'Osage',
'os' => 'Ossetic',
'oto' => 'Otomian Language',
'ota' => 'Ottoman Turkish',
'pal' => 'Pahlavi',
'pau' => 'Palauan',
'pi' => 'Pali',
'pam' => 'Pampanga',
'pag' => 'Pangasinan',
'pap' => 'Papiamento',
'paa' => 'Papuan Language',
'ps' => 'Pashto',
'fa' => 'Persian',
'phi' => 'Philippine Language',
'phn' => 'Phoenician',
'pon' => 'Pohnpeian',
'pl' => 'Polish',
'pt' => 'Portuguese',
'cpp' => 'Portuguese-based Creole or Pidgin',
'pra' => 'Prakrit Language',
'pa' => 'Punjabi',
'qu' => 'Quechua',
'raj' => 'Rajasthani',
'rap' => 'Rapanui',
'rar' => 'Rarotongan',
'roa' => 'Romance Language',
'ro' => 'Romanian',
'rm' => 'Romansh',
'rom' => 'Romany',
'rof' => 'Rombo',
'root' => 'Root',
'rn' => 'Rundi',
'ru' => 'Russian',
'rwk' => 'Rwa',
'ssy' => 'Saho',
'sah' => 'Sakha',
'sal' => 'Salishan Language',
'sam' => 'Samaritan Aramaic',
'saq' => 'Samburu',
'smi' => 'Sami Language',
'sm' => 'Samoan',
'sad' => 'Sandawe',
'sg' => 'Sango',
'sbp' => 'Sangu',
'sa' => 'Sanskrit',
'sat' => 'Santali',
'sc' => 'Sardinian',
'sas' => 'Sasak',
'sco' => 'Scots',
'gd' => 'Scottish Gaelic',
'sel' => 'Selkup',
'sem' => 'Semitic Language',
'seh' => 'Sena',
'see' => 'Seneca',
'sr' => 'Serbian',
'sh' => 'Serbo-Croatian',
'srr' => 'Serer',
'ksb' => 'Shambala',
'shn' => 'Shan',
'sn' => 'Shona',
'ii' => 'Sichuan Yi',
'scn' => 'Sicilian',
'sid' => 'Sidamo',
'sgn' => 'Sign Language',
'bla' => 'Siksika',
'zh_Hans' => 'Simplified Chinese',
'sd' => 'Sindhi',
'si' => 'Sinhala',
'sit' => 'Sino-Tibetan Language',
'sio' => 'Siouan Language',
'sms' => 'Skolt Sami',
'den' => 'Slave',
'sla' => 'Slavic Language',
'sk' => 'Slovak',
'sl' => 'Slovenian',
'xog' => 'Soga',
'sog' => 'Sogdien',
'so' => 'Somali',
'son' => 'Songhai',
'snk' => 'Soninke',
'wen' => 'Sorbian Language',
'sai' => 'South American Indian Language',
'nr' => 'South Ndebele',
'alt' => 'Southern Altai',
'sma' => 'Southern Sami',
'st' => 'Southern Sotho',
'es' => 'Spanish',
'srn' => 'Sranan Tongo',
'suk' => 'Sukuma',
'sux' => 'Sumerian',
'su' => 'Sundanese',
'sus' => 'Susu',
'sw' => 'Swahili',
'ss' => 'Swati',
'sv' => 'Swedish',
'fr_CH' => 'Swiss French',
'gsw' => 'Swiss German',
'de_CH' => 'Swiss High German',
'syr' => 'Syriac',
'shi' => 'Tachelhit',
'tl' => 'Tagalog',
'ty' => 'Tahitian',
'tai' => 'Tai Language',
'dav' => 'Taita',
'tg' => 'Tajik',
'tmh' => 'Tamashek',
'ta' => 'Tamil',
'trv' => 'Taroko',
'twq' => 'Tasawaq',
'tt' => 'Tatar',
'te' => 'Telugu',
'ter' => 'Tereno',
'teo' => 'Teso',
'tet' => 'Tetum',
'th' => 'Thai',
'bo' => 'Tibetan',
'tig' => 'Tigre',
'ti' => 'Tigrinya',
'tem' => 'Timne',
'tiv' => 'Tiv',
'tli' => 'Tlingit',
'tpi' => 'Tok Pisin',
'tkl' => 'Tokelau',
'to' => 'Tongan',
'zh_Hant' => 'Traditional Chinese',
'tsi' => 'Tsimshian',
'ts' => 'Tsonga',
'tn' => 'Tswana',
'tum' => 'Tumbuka',
'tup' => 'Tupi Language',
'tr' => 'Turkish',
'tk' => 'Turkmen',
'tvl' => 'Tuvalu',
'tyv' => 'Tuvinian',
'tw' => 'Twi',
'kcg' => 'Tyap',
'en_US' => 'U.S. English',
'udm' => 'Udmurt',
'uga' => 'Ugaritic',
'ug' => 'Uighur',
'uk' => 'Ukrainian',
'umb' => 'Umbundu',
'und' => 'Unknown Language',
'hsb' => 'Upper Sorbian',
'ur' => 'Urdu',
'uz' => 'Uzbek',
'vai' => 'Vai',
've' => 'Venda',
'vi' => 'Vietnamese',
'vo' => 'Volapük',
'vot' => 'Votic',
'vun' => 'Vunjo',
'wak' => 'Wakashan Language',
'wal' => 'Walamo',
'wa' => 'Walloon',
'wae' => 'Walser',
'war' => 'Waray',
'was' => 'Washo',
'cy' => 'Welsh',
'fy' => 'Western Frisian',
'wo' => 'Wolof',
'xh' => 'Xhosa',
'yav' => 'Yangben',
'yao' => 'Yao',
'yap' => 'Yapese',
'yi' => 'Yiddish',
'yo' => 'Yoruba',
'ypk' => 'Yupik Language',
'znd' => 'Zande',
'zap' => 'Zapotec',
'dje' => 'Zarma',
'zza' => 'Zaza',
'zen' => 'Zenaga',
'za' => 'Zhuang',
'zu' => 'Zulu',
'zun' => 'Zuni',
),
'Scripts' => array(
'Arab' => 'Arabic',
'Armn' => 'Armenian',
'Avst' => 'Avestan',
'Bali' => 'Balinese',
'Bamu' => 'Bamum',
'Batk' => 'Batak',
'Beng' => 'Bengali',
'Blis' => 'Blissymbols',
'Phlv' => 'Book Pahlavi',
'Bopo' => 'Bopomofo',
'Brah' => 'Brahmi',
'Brai' => 'Braille',
'Bugi' => 'Buginese',
'Buhd' => 'Buhid',
'Cari' => 'Carian',
'Cakm' => 'Chakma',
'Cham' => 'Cham',
'Cher' => 'Cherokee',
'Cirt' => 'Cirth',
'Zyyy' => 'Common',
'Copt' => 'Coptic',
'Cprt' => 'Cypriot',
'Cyrl' => 'Cyrillic',
'Dsrt' => 'Deseret',
'Deva' => 'Devanagari',
'Syrn' => 'Eastern Syriac',
'Egyd' => 'Egyptian demotic',
'Egyh' => 'Egyptian hieratic',
'Egyp' => 'Egyptian hieroglyphs',
'Syre' => 'Estrangelo Syriac',
'Ethi' => 'Ethiopic',
'Latf' => 'Fraktur Latin',
'Lisu' => 'Fraser',
'Latg' => 'Gaelic Latin',
'Geor' => 'Georgian',
'Geok' => 'Georgian Khutsuri',
'Glag' => 'Glagolitic',
'Goth' => 'Gothic',
'Gran' => 'Grantha',
'Grek' => 'Greek',
'Gujr' => 'Gujarati',
'Guru' => 'Gurmukhi',
'Hani' => 'Han',
'Hang' => 'Hangul',
'Hano' => 'Hanunoo',
'Hebr' => 'Hebrew',
'Hira' => 'Hiragana',
'Armi' => 'Imperial Aramaic',
'Inds' => 'Indus',
'Zinh' => 'Inherited',
'Phli' => 'Inscriptional Pahlavi',
'Prti' => 'Inscriptional Parthian',
'Jpan' => 'Japanese',
'Java' => 'Javanese',
'Kthi' => 'Kaithi',
'Knda' => 'Kannada',
'Kana' => 'Katakana',
'Hrkt' => 'Katakana or Hiragana',
'Kali' => 'Kayah Li',
'Khar' => 'Kharoshthi',
'Khmr' => 'Khmer',
'Kore' => 'Korean',
'Lana' => 'Lanna',
'Laoo' => 'Lao',
'Latn' => 'Latin',
'Lepc' => 'Lepcha',
'Limb' => 'Limbu',
'Lina' => 'Linear A',
'Linb' => 'Linear B',
'Lyci' => 'Lycian',
'Lydi' => 'Lydian',
'Mlym' => 'Malayalam',
'Mand' => 'Mandaean',
'Mani' => 'Manichaean',
'Zmth' => 'Mathematical Notation',
'Maya' => 'Mayan hieroglyphs',
'Mtei' => 'Meitei Mayek',
'Mero' => 'Meroitic',
'Merc' => 'Meroitic Cursive',
'Mong' => 'Mongolian',
'Moon' => 'Moon',
'Mymr' => 'Myanmar',
'Nkoo' => 'NKo',
'Nkgb' => 'Naxi Geba',
'Talu' => 'New Tai Lue',
'Ogam' => 'Ogham',
'Olck' => 'Ol Chiki',
'Cyrs' => 'Old Church Slavonic Cyrillic',
'Hung' => 'Old Hungarian',
'Ital' => 'Old Italic',
'Perm' => 'Old Permic',
'Xpeo' => 'Old Persian',
'Sarb' => 'Old South Arabian',
'Orya' => 'Oriya',
'Orkh' => 'Orkhon',
'Osma' => 'Osmanya',
'Hmng' => 'Pahawh Hmong',
'Phag' => 'Phags-pa',
'Phnx' => 'Phoenician',
'Plrd' => 'Pollard Phonetic',
'Phlp' => 'Psalter Pahlavi',
'Rjng' => 'Rejang',
'Roro' => 'Rongorongo',
'Runr' => 'Runic',
'Samr' => 'Samaritan',
'Sara' => 'Sarati',
'Saur' => 'Saurashtra',
'Shaw' => 'Shavian',
'Sgnw' => 'SignWriting',
'Hans' => 'Simplified',
'Sinh' => 'Sinhala',
'Xsux' => 'Sumero-Akkadian Cuneiform',
'Sund' => 'Sundanese',
'Sylo' => 'Syloti Nagri',
'Zsym' => 'Symbols',
'Syrc' => 'Syriac',
'Tglg' => 'Tagalog',
'Tagb' => 'Tagbanwa',
'Tale' => 'Tai Le',
'Tavt' => 'Tai Viet',
'Taml' => 'Tamil',
'Telu' => 'Telugu',
'Teng' => 'Tengwar',
'Thaa' => 'Thaana',
'Thai' => 'Thai',
'Tibt' => 'Tibetan',
'Tfng' => 'Tifinagh',
'Hant' => 'Traditional',
'Ugar' => 'Ugaritic',
'Cans' => 'Unified Canadian Aboriginal Syllabics',
'Zzzz' => 'Unknown Script',
'Zxxx' => 'Unwritten',
'Vaii' => 'Vai',
'Wara' => 'Varang Kshiti',
'Visp' => 'Visible Speech',
'Syrj' => 'Western Syriac',
'Yiii' => 'Yi',
),
);

View File

@ -0,0 +1,279 @@
<?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.
*/
return array(
'Locales' => array(
'af' => 'Afrikaans',
'af_NA' => 'Afrikaans (Namibia)',
'agq' => 'Aghem',
'ak' => 'Akan',
'sq' => 'Albanian',
'am' => 'Amharic',
'ar' => 'Arabic',
'ar_DZ' => 'Arabic (Algeria)',
'ar_JO' => 'Arabic (Jordan)',
'ar_LB' => 'Arabic (Lebanon)',
'ar_MA' => 'Arabic (Morocco)',
'ar_QA' => 'Arabic (Qatar)',
'ar_SA' => 'Arabic (Saudi Arabia)',
'ar_SY' => 'Arabic (Syria)',
'ar_TN' => 'Arabic (Tunisia)',
'ar_YE' => 'Arabic (Yemen)',
'hy' => 'Armenian',
'as' => 'Assamese',
'asa' => 'Asu',
'az' => 'Azerbaijani',
'az_Cyrl' => 'Azerbaijani (Cyrillic)',
'az_Latn' => 'Azerbaijani (Latin)',
'ksf' => 'Bafia',
'bm' => 'Bambara',
'bas' => 'Basaa',
'eu' => 'Basque',
'be' => 'Belarusian',
'bem' => 'Bemba',
'bez' => 'Bena',
'bn' => 'Bengali',
'bn_IN' => 'Bengali (India)',
'brx' => 'Bodo',
'bs' => 'Bosnian',
'br' => 'Breton',
'bg' => 'Bulgarian',
'my' => 'Burmese',
'my_MM' => 'Burmese (Myanmar [Burma])',
'ca' => 'Catalan',
'tzm' => 'Central Morocco Tamazight',
'tzm_Latn' => 'Central Morocco Tamazight (Latin)',
'chr' => 'Cherokee',
'chr_US' => 'Cherokee (United States)',
'cgg' => 'Chiga',
'zh' => 'Chinese',
'zh_Hans_HK' => 'Chinese (Simplified, Hong Kong SAR China)',
'zh_Hans_MO' => 'Chinese (Simplified, Macau SAR China)',
'zh_Hans_SG' => 'Chinese (Simplified, Singapore)',
'zh_Hans' => 'Chinese (Simplified)',
'zh_Hant_HK' => 'Chinese (Traditional, Hong Kong SAR China)',
'zh_Hant_MO' => 'Chinese (Traditional, Macau SAR China)',
'zh_Hant' => 'Chinese (Traditional)',
'swc' => 'Congo Swahili',
'kw' => 'Cornish',
'hr' => 'Croatian',
'cs' => 'Czech',
'da' => 'Danish',
'dua' => 'Duala',
'nl' => 'Dutch',
'nl_BE' => 'Dutch (Belgium)',
'ebu' => 'Embu',
'en' => 'English',
'en_AU' => 'English (Australia)',
'en_BE' => 'English (Belgium)',
'en_BZ' => 'English (Belize)',
'en_BW' => 'English (Botswana)',
'en_CA' => 'English (Canada)',
'en_HK' => 'English (Hong Kong SAR China)',
'en_IN' => 'English (India)',
'en_IE' => 'English (Ireland)',
'en_JM' => 'English (Jamaica)',
'en_MT' => 'English (Malta)',
'en_NA' => 'English (Namibia)',
'en_NZ' => 'English (New Zealand)',
'en_PK' => 'English (Pakistan)',
'en_PH' => 'English (Philippines)',
'en_SG' => 'English (Singapore)',
'en_ZA' => 'English (South Africa)',
'en_TT' => 'English (Trinidad and Tobago)',
'en_GB' => 'English (United Kingdom)',
'en_US' => 'English (United States)',
'en_ZW' => 'English (Zimbabwe)',
'eo' => 'Esperanto',
'et' => 'Estonian',
'ee' => 'Ewe',
'ewo' => 'Ewondo',
'fo' => 'Faroese',
'fil' => 'Filipino',
'fil_PH' => 'Filipino (Philippines)',
'fi' => 'Finnish',
'fr' => 'French',
'fr_BE' => 'French (Belgium)',
'fr_CA' => 'French (Canada)',
'fr_LU' => 'French (Luxembourg)',
'fr_CH' => 'French (Switzerland)',
'ff' => 'Fulah',
'gl' => 'Galician',
'lg' => 'Ganda',
'ka' => 'Georgian',
'de' => 'German',
'de_AT' => 'German (Austria)',
'de_LI' => 'German (Liechtenstein)',
'de_CH' => 'German (Switzerland)',
'el' => 'Greek',
'el_CY' => 'Greek (Cyprus)',
'gu' => 'Gujarati',
'guz' => 'Gusii',
'ha' => 'Hausa',
'ha_Latn' => 'Hausa (Latin)',
'haw' => 'Hawaiian',
'haw_US' => 'Hawaiian (United States)',
'he' => 'Hebrew',
'hi' => 'Hindi',
'hu' => 'Hungarian',
'is' => 'Icelandic',
'ig' => 'Igbo',
'id' => 'Indonesian',
'ga' => 'Irish',
'it' => 'Italian',
'it_CH' => 'Italian (Switzerland)',
'ja' => 'Japanese',
'dyo' => 'Jola-Fonyi',
'kea' => 'Kabuverdianu',
'kab' => 'Kabyle',
'kl' => 'Kalaallisut',
'kln' => 'Kalenjin',
'kam' => 'Kamba',
'kn' => 'Kannada',
'kk' => 'Kazakh',
'kk_Cyrl' => 'Kazakh (Cyrillic)',
'km' => 'Khmer',
'ki' => 'Kikuyu',
'rw' => 'Kinyarwanda',
'kok' => 'Konkani',
'ko' => 'Korean',
'khq' => 'Koyra Chiini',
'ses' => 'Koyraboro Senni',
'nmg' => 'Kwasio',
'lag' => 'Langi',
'lv' => 'Latvian',
'ln' => 'Lingala',
'lt' => 'Lithuanian',
'lu' => 'Luba-Katanga',
'luo' => 'Luo',
'luy' => 'Luyia',
'mk' => 'Macedonian',
'jmc' => 'Machame',
'mgh' => 'Makhuwa-Meetto',
'kde' => 'Makonde',
'mg' => 'Malagasy',
'ms' => 'Malay',
'ms_BN' => 'Malay (Brunei)',
'ml' => 'Malayalam',
'mt' => 'Maltese',
'gv' => 'Manx',
'mr' => 'Marathi',
'mas' => 'Masai',
'mer' => 'Meru',
'mfe' => 'Morisyen',
'mua' => 'Mundang',
'naq' => 'Nama',
'ne' => 'Nepali',
'ne_IN' => 'Nepali (India)',
'nd' => 'North Ndebele',
'nb' => 'Norwegian Bokmål',
'nn' => 'Norwegian Nynorsk',
'nus' => 'Nuer',
'nyn' => 'Nyankole',
'or' => 'Oriya',
'om' => 'Oromo',
'ps' => 'Pashto',
'fa' => 'Persian',
'fa_AF' => 'Persian (Afghanistan)',
'pl' => 'Polish',
'pt' => 'Portuguese',
'pt_AO' => 'Portuguese (Angola)',
'pt_GW' => 'Portuguese (Guinea-Bissau)',
'pt_MZ' => 'Portuguese (Mozambique)',
'pt_PT' => 'Portuguese (Portugal)',
'pt_ST' => 'Portuguese (São Tomé and Príncipe)',
'pa' => 'Punjabi',
'pa_Arab' => 'Punjabi (Arabic)',
'pa_Guru' => 'Punjabi (Gurmukhi)',
'ro' => 'Romanian',
'rm' => 'Romansh',
'rof' => 'Rombo',
'rn' => 'Rundi',
'ru' => 'Russian',
'ru_UA' => 'Russian (Ukraine)',
'rwk' => 'Rwa',
'saq' => 'Samburu',
'sg' => 'Sango',
'sbp' => 'Sangu',
'seh' => 'Sena',
'sr' => 'Serbian',
'sr_Cyrl_BA' => 'Serbian (Cyrillic, Bosnia and Herzegovina)',
'sr_Cyrl' => 'Serbian (Cyrillic)',
'sr_Latn_ME' => 'Serbian (Latin, Montenegro)',
'sr_Latn' => 'Serbian (Latin)',
'ksb' => 'Shambala',
'sn' => 'Shona',
'ii' => 'Sichuan Yi',
'si' => 'Sinhala',
'sk' => 'Slovak',
'sl' => 'Slovenian',
'xog' => 'Soga',
'so' => 'Somali',
'es' => 'Spanish',
'es_AR' => 'Spanish (Argentina)',
'es_BO' => 'Spanish (Bolivia)',
'es_CL' => 'Spanish (Chile)',
'es_CO' => 'Spanish (Colombia)',
'es_CR' => 'Spanish (Costa Rica)',
'es_DO' => 'Spanish (Dominican Republic)',
'es_EC' => 'Spanish (Ecuador)',
'es_SV' => 'Spanish (El Salvador)',
'es_GQ' => 'Spanish (Equatorial Guinea)',
'es_GT' => 'Spanish (Guatemala)',
'es_HN' => 'Spanish (Honduras)',
'es_MX' => 'Spanish (Mexico)',
'es_NI' => 'Spanish (Nicaragua)',
'es_PA' => 'Spanish (Panama)',
'es_PY' => 'Spanish (Paraguay)',
'es_PE' => 'Spanish (Peru)',
'es_PR' => 'Spanish (Puerto Rico)',
'es_US' => 'Spanish (United States)',
'es_UY' => 'Spanish (Uruguay)',
'es_VE' => 'Spanish (Venezuela)',
'sw' => 'Swahili',
'sw_KE' => 'Swahili (Kenya)',
'sv' => 'Swedish',
'sv_FI' => 'Swedish (Finland)',
'gsw' => 'Swiss German',
'shi' => 'Tachelhit',
'shi_Tfng' => 'Tachelhit (Tifinagh)',
'dav' => 'Taita',
'ta' => 'Tamil',
'twq' => 'Tasawaq',
'te' => 'Telugu',
'teo' => 'Teso',
'th' => 'Thai',
'bo' => 'Tibetan',
'ti' => 'Tigrinya',
'ti_ER' => 'Tigrinya (Eritrea)',
'to' => 'Tongan',
'tr' => 'Turkish',
'uk' => 'Ukrainian',
'ur' => 'Urdu',
'ur_IN' => 'Urdu (India)',
'uz' => 'Uzbek',
'uz_Arab' => 'Uzbek (Arabic)',
'uz_Cyrl' => 'Uzbek (Cyrillic)',
'uz_Latn' => 'Uzbek (Latin)',
'vai' => 'Vai',
'vai_Latn_LR' => 'Vai (Latin, Liberia)',
'vai_Latn' => 'Vai (Latin)',
'vai_Vaii_LR' => 'Vai (Vai, Liberia)',
'vai_Vaii' => 'Vai (Vai)',
'vi' => 'Vietnamese',
'vun' => 'Vunjo',
'cy' => 'Welsh',
'yav' => 'Yangben',
'yo' => 'Yoruba',
'dje' => 'Zarma',
'zu' => 'Zulu',
),
);

View File

@ -0,0 +1,272 @@
<?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.
*/
return array(
'Countries' => array(
'AF' => 'Afghanistan',
'AX' => 'Åland Islands',
'AL' => 'Albania',
'DZ' => 'Algeria',
'AS' => 'American Samoa',
'AD' => 'Andorra',
'AO' => 'Angola',
'AI' => 'Anguilla',
'AQ' => 'Antarctica',
'AG' => 'Antigua and Barbuda',
'AR' => 'Argentina',
'AM' => 'Armenia',
'AW' => 'Aruba',
'AC' => 'Ascension Island',
'AU' => 'Australia',
'AT' => 'Austria',
'AZ' => 'Azerbaijan',
'BS' => 'Bahamas',
'BH' => 'Bahrain',
'BD' => 'Bangladesh',
'BB' => 'Barbados',
'BY' => 'Belarus',
'BE' => 'Belgium',
'BZ' => 'Belize',
'BJ' => 'Benin',
'BM' => 'Bermuda',
'BT' => 'Bhutan',
'BO' => 'Bolivia',
'BA' => 'Bosnia and Herzegovina',
'BW' => 'Botswana',
'BV' => 'Bouvet Island',
'BR' => 'Brazil',
'IO' => 'British Indian Ocean Territory',
'VG' => 'British Virgin Islands',
'BN' => 'Brunei',
'BG' => 'Bulgaria',
'BF' => 'Burkina Faso',
'BI' => 'Burundi',
'KH' => 'Cambodia',
'CM' => 'Cameroon',
'CA' => 'Canada',
'IC' => 'Canary Islands',
'CV' => 'Cape Verde',
'KY' => 'Cayman Islands',
'CF' => 'Central African Republic',
'EA' => 'Ceuta and Melilla',
'TD' => 'Chad',
'CL' => 'Chile',
'CN' => 'China',
'CX' => 'Christmas Island',
'CP' => 'Clipperton Island',
'CC' => 'Cocos [Keeling] Islands',
'CO' => 'Colombia',
'KM' => 'Comoros',
'CG' => 'Congo - Brazzaville',
'CD' => 'Congo - Kinshasa',
'CK' => 'Cook Islands',
'CR' => 'Costa Rica',
'CI' => 'Côte dIvoire',
'HR' => 'Croatia',
'CU' => 'Cuba',
'CW' => 'Curaçao',
'CY' => 'Cyprus',
'CZ' => 'Czech Republic',
'DK' => 'Denmark',
'DG' => 'Diego Garcia',
'DJ' => 'Djibouti',
'DM' => 'Dominica',
'DO' => 'Dominican Republic',
'EC' => 'Ecuador',
'EG' => 'Egypt',
'SV' => 'El Salvador',
'GQ' => 'Equatorial Guinea',
'ER' => 'Eritrea',
'EE' => 'Estonia',
'ET' => 'Ethiopia',
'EU' => 'European Union',
'FK' => 'Falkland Islands',
'FO' => 'Faroe Islands',
'FJ' => 'Fiji',
'FI' => 'Finland',
'FR' => 'France',
'GF' => 'French Guiana',
'PF' => 'French Polynesia',
'TF' => 'French Southern Territories',
'GA' => 'Gabon',
'GM' => 'Gambia',
'GE' => 'Georgia',
'DE' => 'Germany',
'GH' => 'Ghana',
'GI' => 'Gibraltar',
'GR' => 'Greece',
'GL' => 'Greenland',
'GD' => 'Grenada',
'GP' => 'Guadeloupe',
'GU' => 'Guam',
'GT' => 'Guatemala',
'GG' => 'Guernsey',
'GN' => 'Guinea',
'GW' => 'Guinea-Bissau',
'GY' => 'Guyana',
'HT' => 'Haiti',
'HM' => 'Heard Island and McDonald Islands',
'HN' => 'Honduras',
'HK' => 'Hong Kong SAR China',
'HU' => 'Hungary',
'IS' => 'Iceland',
'IN' => 'India',
'ID' => 'Indonesia',
'IR' => 'Iran',
'IQ' => 'Iraq',
'IE' => 'Ireland',
'IM' => 'Isle of Man',
'IL' => 'Israel',
'IT' => 'Italy',
'JM' => 'Jamaica',
'JP' => 'Japan',
'JE' => 'Jersey',
'JO' => 'Jordan',
'KZ' => 'Kazakhstan',
'KE' => 'Kenya',
'KI' => 'Kiribati',
'KW' => 'Kuwait',
'KG' => 'Kyrgyzstan',
'LA' => 'Laos',
'LV' => 'Latvia',
'LB' => 'Lebanon',
'LS' => 'Lesotho',
'LR' => 'Liberia',
'LY' => 'Libya',
'LI' => 'Liechtenstein',
'LT' => 'Lithuania',
'LU' => 'Luxembourg',
'MO' => 'Macau SAR China',
'MK' => 'Macedonia',
'MG' => 'Madagascar',
'MW' => 'Malawi',
'MY' => 'Malaysia',
'MV' => 'Maldives',
'ML' => 'Mali',
'MT' => 'Malta',
'MH' => 'Marshall Islands',
'MQ' => 'Martinique',
'MR' => 'Mauritania',
'MU' => 'Mauritius',
'YT' => 'Mayotte',
'MX' => 'Mexico',
'FM' => 'Micronesia',
'MD' => 'Moldova',
'MC' => 'Monaco',
'MN' => 'Mongolia',
'ME' => 'Montenegro',
'MS' => 'Montserrat',
'MA' => 'Morocco',
'MZ' => 'Mozambique',
'MM' => 'Myanmar [Burma]',
'NA' => 'Namibia',
'NR' => 'Nauru',
'NP' => 'Nepal',
'NL' => 'Netherlands',
'AN' => 'Netherlands Antilles',
'NC' => 'New Caledonia',
'NZ' => 'New Zealand',
'NI' => 'Nicaragua',
'NE' => 'Niger',
'NG' => 'Nigeria',
'NU' => 'Niue',
'NF' => 'Norfolk Island',
'KP' => 'North Korea',
'MP' => 'Northern Mariana Islands',
'NO' => 'Norway',
'OM' => 'Oman',
'QO' => 'Outlying Oceania',
'PK' => 'Pakistan',
'PW' => 'Palau',
'PS' => 'Palestinian Territories',
'PA' => 'Panama',
'PG' => 'Papua New Guinea',
'PY' => 'Paraguay',
'PE' => 'Peru',
'PH' => 'Philippines',
'PN' => 'Pitcairn Islands',
'PL' => 'Poland',
'PT' => 'Portugal',
'PR' => 'Puerto Rico',
'QA' => 'Qatar',
'RE' => 'Réunion',
'RO' => 'Romania',
'RU' => 'Russia',
'RW' => 'Rwanda',
'BL' => 'Saint Barthélemy',
'SH' => 'Saint Helena',
'KN' => 'Saint Kitts and Nevis',
'LC' => 'Saint Lucia',
'MF' => 'Saint Martin',
'PM' => 'Saint Pierre and Miquelon',
'VC' => 'Saint Vincent and the Grenadines',
'WS' => 'Samoa',
'SM' => 'San Marino',
'ST' => 'São Tomé and Príncipe',
'SA' => 'Saudi Arabia',
'SN' => 'Senegal',
'RS' => 'Serbia',
'CS' => 'Serbia and Montenegro',
'SC' => 'Seychelles',
'SL' => 'Sierra Leone',
'SG' => 'Singapore',
'SX' => 'Sint Maarten',
'SK' => 'Slovakia',
'SI' => 'Slovenia',
'SB' => 'Solomon Islands',
'SO' => 'Somalia',
'ZA' => 'South Africa',
'GS' => 'South Georgia and the South Sandwich Islands',
'KR' => 'South Korea',
'ES' => 'Spain',
'LK' => 'Sri Lanka',
'SD' => 'Sudan',
'SR' => 'Suriname',
'SJ' => 'Svalbard and Jan Mayen',
'SZ' => 'Swaziland',
'SE' => 'Sweden',
'CH' => 'Switzerland',
'SY' => 'Syria',
'TW' => 'Taiwan',
'TJ' => 'Tajikistan',
'TZ' => 'Tanzania',
'TH' => 'Thailand',
'TL' => 'Timor-Leste',
'TG' => 'Togo',
'TK' => 'Tokelau',
'TO' => 'Tonga',
'TT' => 'Trinidad and Tobago',
'TA' => 'Tristan da Cunha',
'TN' => 'Tunisia',
'TR' => 'Turkey',
'TM' => 'Turkmenistan',
'TC' => 'Turks and Caicos Islands',
'TV' => 'Tuvalu',
'UM' => 'U.S. Minor Outlying Islands',
'VI' => 'U.S. Virgin Islands',
'UG' => 'Uganda',
'UA' => 'Ukraine',
'AE' => 'United Arab Emirates',
'GB' => 'United Kingdom',
'US' => 'United States',
'UY' => 'Uruguay',
'UZ' => 'Uzbekistan',
'VU' => 'Vanuatu',
'VA' => 'Vatican City',
'VE' => 'Venezuela',
'VN' => 'Vietnam',
'WF' => 'Wallis and Futuna',
'EH' => 'Western Sahara',
'YE' => 'Yemen',
'ZM' => 'Zambia',
'ZW' => 'Zimbabwe',
),
);

View File

@ -0,0 +1 @@
4.8.1.1

View File

@ -9,15 +9,15 @@
* file that was distributed with this source code.
*/
use Symfony\Component\Intl\Collator\StubCollator;
/**
* Stub implementation for the Collator class of the intl extension
*
* @author Bernhard Schussek <bschussek@gmail.com>
* @see Symfony\Component\Collator\Stub\StubCollator
*
* @see Symfony\Component\Intl\Collator\StubCollator
*/
use Symfony\Component\Locale\Stub\StubCollator;
class Collator extends StubCollator
{
}

View File

@ -9,15 +9,15 @@
* file that was distributed with this source code.
*/
use Symfony\Component\Intl\DateFormatter\StubIntlDateFormatter;
/**
* Stub implementation for the IntlDateFormatter class of the intl extension
*
* @author Bernhard Schussek <bschussek@gmail.com>
* @see Symfony\Component\IntlDateFormatter\Stub\StubIntlDateFormatter
*
* @see Symfony\Component\Intl\DateFormatter\StubIntlDateFormatter
*/
use Symfony\Component\Locale\Stub\StubIntlDateFormatter;
class IntlDateFormatter extends StubIntlDateFormatter
{
}

View File

@ -9,15 +9,15 @@
* file that was distributed with this source code.
*/
use Symfony\Component\Intl\Locale\StubLocale;
/**
* Stub implementation for the Locale class of the intl extension
*
* @author Bernhard Schussek <bschussek@gmail.com>
* @see Symfony\Component\Locale\Stub\StubLocale
*
* @see Symfony\Component\Intl\Locale\StubLocale
*/
use Symfony\Component\Locale\Stub\StubLocale;
class Locale extends StubLocale
{
}

View File

@ -9,15 +9,15 @@
* file that was distributed with this source code.
*/
use Symfony\Component\Intl\NumberFormatter\StubNumberFormatter;
/**
* Stub implementation for the NumberFormatter class of the intl extension
*
* @author Bernhard Schussek <bschussek@gmail.com>
* @see Symfony\Component\Locale\Stub\StubNumberFormatter
*
* @see Symfony\Component\Intl\NumberFormatter\StubNumberFormatter
*/
use Symfony\Component\Locale\Stub\StubNumberFormatter;
class NumberFormatter extends StubNumberFormatter
{
}

View File

@ -0,0 +1,80 @@
<?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.
*/
use Symfony\Component\Intl\Globals\StubIntlGlobals;
if (!function_exists('intl_is_failure')) {
/**
* Stub implementation for the {@link intl_is_failure()} function of the intl
* extension.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @param integer $errorCode The error code returned by intl_get_error_code().
*
* @return Boolean Whether the error code indicates an error.
*
* @see \Symfony\Component\Intl\Globals\StubIntlGlobals::isFailure
*/
function intl_is_failure($errorCode)
{
return StubIntlGlobals::isFailure($errorCode);
}
/**
* Stub implementation for the {@link intl_get_error_code()} function of the
* intl extension.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @return Boolean The error code of the last intl function call or
* StubIntlGlobals::U_ZERO_ERROR if no error occurred.
*
* @see \Symfony\Component\Intl\Globals\StubIntlGlobals::getErrorCode
*/
function intl_get_error_code()
{
return StubIntlGlobals::getErrorCode();
}
/**
* Stub implementation for the {@link intl_get_error_code()} function of the
* intl extension.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @return Boolean The error message of the last intl function call or
* "U_ZERO_ERROR" if no error occurred.
*
* @see \Symfony\Component\Intl\Globals\StubIntlGlobals::getErrorMessage
*/
function intl_get_error_message()
{
return StubIntlGlobals::getErrorMessage();
}
/**
* Stub implementation for the {@link intl_error_name()} function of the intl
* extension.
*
* @param integer $errorCode The error code.
*
* @return string The name of the error code constant.
*
* @see \Symfony\Component\Intl\Globals\StubIntlGlobals::getErrorName
*/
function intl_error_name($errorCode)
{
return StubIntlGlobals::getErrorName($errorCode);
}
}

View File

@ -0,0 +1,63 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\Tests\Collator;
use Symfony\Component\Intl\Collator\StubCollator;
use Symfony\Component\Intl\Locale;
use Symfony\Component\Intl\Tests\IntlTestCase;
/**
* Test case for Collator implementations.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
abstract class AbstractCollatorTest extends IntlTestCase
{
/**
* @dataProvider asortProvider
*/
public function testAsort($array, $sortFlag, $expected)
{
$collator = $this->getCollator('en');
$collator->asort($array, $sortFlag);
$this->assertSame($expected, $array);
}
public function asortProvider()
{
return array(
/* array, sortFlag, expected */
array(
array('a', 'b', 'c'),
StubCollator::SORT_REGULAR,
array('a', 'b', 'c'),
),
array(
array('c', 'b', 'a'),
StubCollator::SORT_REGULAR,
array(2 => 'a', 1 => 'b', 0 => 'c'),
),
array(
array('b', 'c', 'a'),
StubCollator::SORT_REGULAR,
array(2 => 'a', 0 => 'b', 1 => 'c'),
),
);
}
/**
* @param string $locale
*
* @return \Collator
*/
abstract protected function getCollator($locale);
}

View File

@ -0,0 +1,109 @@
<?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\Intl\Tests\Collator;
use Symfony\Component\Intl\Collator\StubCollator;
use Symfony\Component\Intl\Globals\StubIntlGlobals;
class StubCollatorTest extends AbstractCollatorTest
{
/**
* @expectedException \Symfony\Component\Intl\Exception\MethodArgumentValueNotImplementedException
*/
public function testConstructorWithUnsupportedLocale()
{
new StubCollator('pt_BR');
}
/**
* @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
*/
public function testCompare()
{
$collator = $this->getCollator('en');
$collator->compare('a', 'b');
}
/**
* @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
*/
public function testGetAttribute()
{
$collator = $this->getCollator('en');
$collator->getAttribute(StubCollator::NUMERIC_COLLATION);
}
public function testGetErrorCode()
{
$collator = $this->getCollator('en');
$this->assertEquals(StubIntlGlobals::U_ZERO_ERROR, $collator->getErrorCode());
}
public function testGetErrorMessage()
{
$collator = $this->getCollator('en');
$this->assertEquals('U_ZERO_ERROR', $collator->getErrorMessage());
}
public function testGetLocale()
{
$collator = $this->getCollator('en');
$this->assertEquals('en', $collator->getLocale());
}
/**
* @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
*/
public function testGetSortKey()
{
$collator = $this->getCollator('en');
$collator->getSortKey('Hello');
}
/**
* @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
*/
public function testGetStrength()
{
$collator = $this->getCollator('en');
$collator->getStrength();
}
/**
* @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
*/
public function testSetAttribute()
{
$collator = $this->getCollator('en');
$collator->setAttribute(StubCollator::NUMERIC_COLLATION, StubCollator::ON);
}
/**
* @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
*/
public function testSetStrength()
{
$collator = $this->getCollator('en');
$collator->setStrength(StubCollator::PRIMARY);
}
public function testStaticCreate()
{
$collator = StubCollator::create('en');
$this->assertInstanceOf('Symfony\Component\Intl\Collator\StubCollator', $collator);
}
protected function getCollator($locale)
{
return new StubCollator($locale);
}
}

View File

@ -0,0 +1,37 @@
<?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\Intl\Tests\Collator\Verification;
use Symfony\Component\Intl\Locale;
use Symfony\Component\Intl\Tests\Collator\AbstractCollatorTest;
/**
* Verifies that {@link AbstractCollatorTest} matches the behavior of the
* {@link \Collator} class in a specific version of ICU.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class CollatorTest extends AbstractCollatorTest
{
protected function setUp()
{
$this->skipIfIntlExtensionNotLoaded();
$this->skipIfInsufficientIcuVersion();
parent::setUp();
}
protected function getCollator($locale)
{
return new \Collator($locale);
}
}

View File

@ -0,0 +1,965 @@
<?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\Intl\Tests\DateFormatter;
use Symfony\Component\Intl\DateFormatter\StubIntlDateFormatter;
use Symfony\Component\Intl\Globals\StubIntlGlobals;
use Symfony\Component\Intl\Intl;
use Symfony\Component\Intl\Tests\IntlTestCase;
use Symfony\Component\Intl\Util\IcuVersion;
use Symfony\Component\Intl\Util\Version;
/**
* Test case for IntlDateFormatter implementations.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
abstract class AbstractIntlDateFormatterTest extends IntlTestCase
{
/**
* When a time zone is not specified, it uses the system default however it returns null in the getter method
* @covers Symfony\Component\Intl\Stub\StubIntlDateFormatter::getTimeZoneId
* @covers Symfony\Component\Intl\Stub\StubIntlDateFormatter::setTimeZoneId
* @see StubIntlDateFormatterTest::testDefaultTimeZoneIntl()
*/
public function testConstructorDefaultTimeZone()
{
$formatter = $this->getDateFormatter('en', StubIntlDateFormatter::MEDIUM, StubIntlDateFormatter::SHORT);
// In PHP 5.5 default timezone depends on `date_default_timezone_get()` method
if (version_compare(PHP_VERSION, '5.5.0-dev', '>=')) {
$this->assertEquals(date_default_timezone_get(), $formatter->getTimeZoneId());
} else {
$this->assertNull($formatter->getTimeZoneId());
}
}
/**
* @dataProvider formatProvider
*/
public function testFormat($pattern, $timestamp, $expected)
{
$errorCode = StubIntlGlobals::U_ZERO_ERROR;
$errorMessage = 'U_ZERO_ERROR';
$formatter = $this->getDefaultDateFormatter($pattern);
$this->assertSame($expected, $formatter->format($timestamp));
$this->assertIsIntlSuccess($formatter, $errorMessage, $errorCode);
}
public function formatProvider()
{
$formatData = array(
/* general */
array('y-M-d', 0, '1970-1-1'),
array("EEE, MMM d, ''yy", 0, "Thu, Jan 1, '70"),
array('h:mm a', 0, '12:00 AM'),
array('yyyyy.MMMM.dd hh:mm aaa', 0, '01970.January.01 12:00 AM'),
/* escaping */
array("'M'", 0, 'M'),
array("'yy'", 0, 'yy'),
array("'''yy'", 0, "'yy"),
array("''y", 0, "'1970"),
array("''yy", 0, "'70"),
array("H 'o'' clock'", 0, "0 o' clock"),
/* month */
array('M', 0, '1'),
array('MM', 0, '01'),
array('MMM', 0, 'Jan'),
array('MMMM', 0, 'January'),
array('MMMMM', 0, 'J'),
array('MMMMMM', 0, '000001'),
array('L', 0, '1'),
array('LL', 0, '01'),
array('LLL', 0, 'Jan'),
array('LLLL', 0, 'January'),
array('LLLLL', 0, 'J'),
array('LLLLLL', 0, '000001'),
/* year */
array('y', 0, '1970'),
array('yy', 0, '70'),
array('yyy', 0, '1970'),
array('yyyy', 0, '1970'),
array('yyyyy', 0, '01970'),
array('yyyyyy', 0, '001970'),
/* day */
array('d', 0, '1'),
array('dd', 0, '01'),
array('ddd', 0, '001'),
/* quarter */
array('Q', 0, '1'),
array('QQ', 0, '01'),
array('QQQ', 0, 'Q1'),
array('QQQQ', 0, '1st quarter'),
array('QQQQQ', 0, '1st quarter'),
array('q', 0, '1'),
array('qq', 0, '01'),
array('qqq', 0, 'Q1'),
array('qqqq', 0, '1st quarter'),
array('qqqqq', 0, '1st quarter'),
// 4 months
array('Q', 7776000, '2'),
array('QQ', 7776000, '02'),
array('QQQ', 7776000, 'Q2'),
array('QQQQ', 7776000, '2nd quarter'),
// 7 months
array('QQQQ', 15638400, '3rd quarter'),
// 10 months
array('QQQQ', 23587200, '4th quarter'),
/* 12-hour (1-12) */
array('h', 0, '12'),
array('hh', 0, '12'),
array('hhh', 0, '012'),
array('h', 1, '12'),
array('h', 3600, '1'),
array('h', 43200, '12'), // 12 hours
/* day of year */
array('D', 0, '1'),
array('D', 86400, '2'), // 1 day
array('D', 31536000, '1'), // 1 year
array('D', 31622400, '2'), // 1 year + 1 day
/* day of week */
array('E', 0, 'Thu'),
array('EE', 0, 'Thu'),
array('EEE', 0, 'Thu'),
array('EEEE', 0, 'Thursday'),
array('EEEEE', 0, 'T'),
array('EEEEEE', 0, 'Thu'),
array('E', 1296540000, 'Tue'), // 2011-02-01
array('E', 1296950400, 'Sun'), // 2011-02-06
/* am/pm marker */
array('a', 0, 'AM'),
array('aa', 0, 'AM'),
array('aaa', 0, 'AM'),
array('aaaa', 0, 'AM'),
// 12 hours
array('a', 43200, 'PM'),
array('aa', 43200, 'PM'),
array('aaa', 43200, 'PM'),
array('aaaa', 43200, 'PM'),
/* 24-hour (0-23) */
array('H', 0, '0'),
array('HH', 0, '00'),
array('HHH', 0, '000'),
array('H', 1, '0'),
array('H', 3600, '1'),
array('H', 43200, '12'),
array('H', 46800, '13'),
/* 24-hour (1-24) */
array('k', 0, '24'),
array('kk', 0, '24'),
array('kkk', 0, '024'),
array('k', 1, '24'),
array('k', 3600, '1'),
array('k', 43200, '12'),
array('k', 46800, '13'),
/* 12-hour (0-11) */
array('K', 0, '0'),
array('KK', 0, '00'),
array('KKK', 0, '000'),
array('K', 1, '0'),
array('K', 3600, '1'),
array('K', 43200, '0'), // 12 hours
/* minute */
array('m', 0, '0'),
array('mm', 0, '00'),
array('mmm', 0, '000'),
array('m', 1, '0'),
array('m', 60, '1'),
array('m', 120, '2'),
array('m', 180, '3'),
array('m', 3600, '0'),
array('m', 3660, '1'),
array('m', 43200, '0'), // 12 hours
/* second */
array('s', 0, '0'),
array('ss', 0, '00'),
array('sss', 0, '000'),
array('s', 1, '1'),
array('s', 2, '2'),
array('s', 5, '5'),
array('s', 30, '30'),
array('s', 59, '59'),
array('s', 60, '0'),
array('s', 120, '0'),
array('s', 180, '0'),
array('s', 3600, '0'),
array('s', 3601, '1'),
array('s', 3630, '30'),
array('s', 43200, '0'), // 12 hours
);
// Timezone
if (Intl::isExtensionLoaded() && IcuVersion::compare(Intl::getIcuVersion(), '4.8', '>=')) {
// general
$formatData[] = array("yyyy.MM.dd 'at' HH:mm:ss zzz", 0, '1970.01.01 at 00:00:00 GMT');
$formatData[] = array('K:mm a, z', 0, '0:00 AM, GMT');
// timezone
$formatData[] = array('z', 0, 'GMT');
$formatData[] = array('zz', 0, 'GMT');
$formatData[] = array('zzz', 0, 'GMT');
$formatData[] = array('zzzz', 0, 'GMT');
$formatData[] = array('zzzzz', 0, 'GMT');
}
// As of PHP 5.3.4, IntlDateFormatter::format() accepts DateTime instances
if (version_compare(PHP_VERSION, '5.3.4', '>=')) {
$dateTime = new \DateTime('@0');
/* general, DateTime */
$formatData[] = array('y-M-d', $dateTime, '1970-1-1');
$formatData[] = array("EEE, MMM d, ''yy", $dateTime, "Thu, Jan 1, '70");
$formatData[] = array('h:mm a', $dateTime, '12:00 AM');
$formatData[] = array('yyyyy.MMMM.dd hh:mm aaa', $dateTime, '01970.January.01 12:00 AM');
if (Intl::isExtensionLoaded() && IcuVersion::compare(Intl::getIcuVersion(), '4.8', '>=')) {
$formatData[] = array("yyyy.MM.dd 'at' HH:mm:ss zzz", $dateTime, '1970.01.01 at 00:00:00 GMT');
$formatData[] = array('K:mm a, z', $dateTime, '0:00 AM, GMT');
}
}
return $formatData;
}
/**
* @dataProvider formatErrorProvider
*/
public function testFormatIllegalArgumentError($pattern, $timestamp, $errorMessage)
{
$errorCode = StubIntlGlobals::U_ILLEGAL_ARGUMENT_ERROR;
$formatter = $this->getDefaultDateFormatter($pattern);
$this->assertFalse($formatter->format($timestamp));
$this->assertIsIntlFailure($formatter, $errorMessage, $errorCode);
}
public function formatErrorProvider()
{
// With PHP 5.5 IntlDateFormatter accepts empty values ('0')
if (version_compare(PHP_VERSION, '5.5.0-dev', '>=')) {
return array(
array('y-M-d', 'foobar', 'datefmt_format: string \'foobar\' is not numeric, which would be required for it to be a valid date: U_ILLEGAL_ARGUMENT_ERROR')
);
}
$message = 'datefmt_format: takes either an array or an integer timestamp value : U_ILLEGAL_ARGUMENT_ERROR';
if (version_compare(PHP_VERSION, '5.3.4', '>=')) {
$message = 'datefmt_format: takes either an array or an integer timestamp value or a DateTime object: U_ILLEGAL_ARGUMENT_ERROR';
}
return array(
array('y-M-d', '0', $message),
array('y-M-d', 'foobar', $message),
);
}
/**
* @dataProvider formatWithTimezoneProvider
*/
public function testFormatWithTimezone($timestamp, $timezone, $expected)
{
$pattern = 'yyyy-MM-dd HH:mm:ss';
$formatter = $this->getDateFormatter('en', StubIntlDateFormatter::MEDIUM, StubIntlDateFormatter::SHORT, $timezone, StubIntlDateFormatter::GREGORIAN, $pattern);
$this->assertSame($expected, $formatter->format($timestamp));
}
public function formatWithTimezoneProvider()
{
$data = array(
array(0, 'UTC', '1970-01-01 00:00:00'),
array(0, 'GMT', '1970-01-01 00:00:00'),
array(0, 'GMT-03:00', '1969-12-31 21:00:00'),
array(0, 'GMT+03:00', '1970-01-01 03:00:00'),
array(0, 'Europe/Zurich', '1970-01-01 01:00:00'),
array(0, 'Europe/Paris', '1970-01-01 01:00:00'),
array(0, 'Africa/Cairo', '1970-01-01 02:00:00'),
array(0, 'Africa/Casablanca', '1970-01-01 00:00:00'),
array(0, 'Africa/Djibouti', '1970-01-01 03:00:00'),
array(0, 'Africa/Johannesburg', '1970-01-01 02:00:00'),
array(0, 'America/Antigua', '1969-12-31 20:00:00'),
array(0, 'America/Toronto', '1969-12-31 19:00:00'),
array(0, 'America/Vancouver', '1969-12-31 16:00:00'),
array(0, 'Asia/Aqtau', '1970-01-01 05:00:00'),
array(0, 'Asia/Bangkok', '1970-01-01 07:00:00'),
array(0, 'Asia/Dubai', '1970-01-01 04:00:00'),
array(0, 'Australia/Brisbane', '1970-01-01 10:00:00'),
array(0, 'Australia/Eucla', '1970-01-01 08:45:00'),
array(0, 'Australia/Melbourne', '1970-01-01 10:00:00'),
array(0, 'Europe/Berlin', '1970-01-01 01:00:00'),
array(0, 'Europe/Dublin', '1970-01-01 01:00:00'),
array(0, 'Europe/Warsaw', '1970-01-01 01:00:00'),
array(0, 'Pacific/Fiji', '1970-01-01 12:00:00'),
);
// As of PHP 5.5, intl ext no longer fallbacks invalid time zones to UTC
if (!version_compare(PHP_VERSION, '5.5.0-dev', '>=')) {
// When time zone not exists, uses UTC by default
$data[] = array(0, 'Foo/Bar', '1970-01-01 00:00:00');
$data[] = array(0, 'UTC+04:30', '1970-01-01 00:00:00');
$data[] = array(0, 'UTC+04:AA', '1970-01-01 00:00:00');
}
return $data;
}
public function testFormatWithGmtTimezone()
{
$formatter = $this->getDefaultDateFormatter('zzzz');
if (version_compare(PHP_VERSION, '5.5.0-dev', '>=')) {
$formatter->setTimeZone('GMT+03:00');
} else {
$formatter->setTimeZoneId('GMT+03:00');
}
$this->assertEquals('GMT+03:00', $formatter->format(0));
}
public function testFormatWithGmtTimeZoneAndMinutesOffset()
{
$formatter = $this->getDefaultDateFormatter('zzzz');
if (version_compare(PHP_VERSION, '5.5.0-dev', '>=')) {
$formatter->setTimeZone('GMT+00:30');
} else {
$formatter->setTimeZoneId('GMT+00:30');
}
$this->assertEquals('GMT+00:30', $formatter->format(0));
}
public function testFormatWithNonStandardTimezone()
{
$formatter = $this->getDefaultDateFormatter('zzzz');
if (version_compare(PHP_VERSION, '5.5.0-dev', '>=')) {
$formatter->setTimeZone('Pacific/Fiji');
} else {
$formatter->setTimeZoneId('Pacific/Fiji');
}
$this->assertEquals('Fiji Standard Time', $formatter->format(0));
}
public function testFormatWithConstructorTimezone()
{
$formatter = $this->getDateFormatter('en', StubIntlDateFormatter::MEDIUM, StubIntlDateFormatter::SHORT, 'UTC');
$formatter->setPattern('yyyy-MM-dd HH:mm:ss');
$this->assertEquals(
$this->getDateTime(0)->format('Y-m-d H:i:s'),
$formatter->format(0)
);
}
public function testFormatWithTimezoneFromEnvironmentVariable()
{
if (version_compare(PHP_VERSION, '5.5.0-dev', '>=')) {
$this->markTestSkipped('IntlDateFormatter in PHP 5.5 no longer depends on TZ environment.');
}
$tz = getenv('TZ');
putenv('TZ=Europe/London');
$formatter = $this->getDateFormatter('en', StubIntlDateFormatter::MEDIUM, StubIntlDateFormatter::SHORT);
$formatter->setPattern('yyyy-MM-dd HH:mm:ss');
$this->assertEquals(
$this->getDateTime(0)->format('Y-m-d H:i:s'),
$formatter->format(0)
);
$this->assertEquals('Europe/London', getenv('TZ'));
// Restores TZ.
putenv('TZ='.$tz);
}
public function testFormatWithTimezoneFromPhp()
{
if (!version_compare(PHP_VERSION, '5.5.0-dev', '>=')) {
$this->markTestSkipped('Only in PHP 5.5 IntlDateFormatter depends on default timezone (`date_default_timezone_get()`).');
}
$tz = date_default_timezone_get();
date_default_timezone_set('Europe/London');
$formatter = $this->getDateFormatter('en', StubIntlDateFormatter::MEDIUM, StubIntlDateFormatter::SHORT);
$formatter->setPattern('yyyy-MM-dd HH:mm:ss');
$this->assertEquals(
$this->getDateTime(0)->format('Y-m-d H:i:s'),
$formatter->format(0)
);
$this->assertEquals('Europe/London', date_default_timezone_get());
// Restores TZ.
date_default_timezone_set($tz);
}
/**
* @dataProvider dateAndTimeTypeProvider
*/
public function testDateAndTimeType($timestamp, $datetype, $timetype, $expected)
{
$formatter = $this->getDateFormatter('en', $datetype, $timetype, 'UTC');
$this->assertSame($expected, $formatter->format($timestamp));
}
public function dateAndTimeTypeProvider()
{
$data = array(
array(0, StubIntlDateFormatter::FULL, StubIntlDateFormatter::NONE, 'Thursday, January 1, 1970'),
array(0, StubIntlDateFormatter::LONG, StubIntlDateFormatter::NONE, 'January 1, 1970'),
array(0, StubIntlDateFormatter::MEDIUM, StubIntlDateFormatter::NONE, 'Jan 1, 1970'),
array(0, StubIntlDateFormatter::SHORT, StubIntlDateFormatter::NONE, '1/1/70'),
);
if (Intl::isExtensionLoaded() && IcuVersion::compare(Intl::getIcuVersion(), '4.8', '>=')) {
$data[] = array(0, StubIntlDateFormatter::NONE, StubIntlDateFormatter::FULL, '12:00:00 AM GMT');
$data[] = array(0, StubIntlDateFormatter::NONE, StubIntlDateFormatter::LONG, '12:00:00 AM GMT');
}
$data[] = array(0, StubIntlDateFormatter::NONE, StubIntlDateFormatter::MEDIUM, '12:00:00 AM');
$data[] = array(0, StubIntlDateFormatter::NONE, StubIntlDateFormatter::SHORT, '12:00 AM');
return $data;
}
public function testGetCalendar()
{
$formatter = $this->getDefaultDateFormatter();
$this->assertEquals(StubIntlDateFormatter::GREGORIAN, $formatter->getCalendar());
}
public function testGetDateType()
{
$formatter = $this->getDateFormatter('en', StubIntlDateFormatter::FULL, StubIntlDateFormatter::NONE);
$this->assertEquals(StubIntlDateFormatter::FULL, $formatter->getDateType());
}
public function testGetLocale()
{
$formatter = $this->getDefaultDateFormatter();
$this->assertEquals('en', $formatter->getLocale());
}
public function testGetPattern()
{
$formatter = $this->getDateFormatter('en', StubIntlDateFormatter::FULL, StubIntlDateFormatter::NONE, 'UTC', StubIntlDateFormatter::GREGORIAN, 'yyyy-MM-dd');
$this->assertEquals('yyyy-MM-dd', $formatter->getPattern());
}
public function testGetTimeType()
{
$formatter = $this->getDateFormatter('en', StubIntlDateFormatter::NONE, StubIntlDateFormatter::FULL);
$this->assertEquals(StubIntlDateFormatter::FULL, $formatter->getTimeType());
}
/**
* @dataProvider parseProvider
*/
public function testParse($pattern, $value, $expected)
{
$errorCode = StubIntlGlobals::U_ZERO_ERROR;
$errorMessage = 'U_ZERO_ERROR';
$formatter = $this->getDefaultDateFormatter($pattern);
$this->assertSame($expected, $formatter->parse($value));
$this->assertIsIntlSuccess($formatter, $errorMessage, $errorCode);
}
public function parseProvider()
{
return array_merge(
$this->parseYearProvider(),
$this->parseQuarterProvider(),
$this->parseMonthProvider(),
$this->parseStandaloneMonthProvider(),
$this->parseDayProvider(),
$this->parseDayOfWeekProvider(),
$this->parseDayOfYearProvider(),
$this->parseHour12ClockOneBasedProvider(),
$this->parseHour12ClockZeroBasedProvider(),
$this->parseHour24ClockOneBasedProvider(),
$this->parseHour24ClockZeroBasedProvider(),
$this->parseMinuteProvider(),
$this->parseSecondProvider(),
$this->parseTimezoneProvider(),
$this->parseAmPmProvider(),
$this->parseStandaloneAmPmProvider(),
$this->parseRegexMetaCharsProvider(),
$this->parseQuoteCharsProvider(),
$this->parseDashSlashProvider()
);
}
public function parseYearProvider()
{
return array(
array('y-M-d', '1970-1-1', 0),
array('yy-M-d', '70-1-1', 0),
);
}
public function parseQuarterProvider()
{
return array(
array('Q', '1', 0),
array('QQ', '01', 0),
array('QQQ', 'Q1', 0),
array('QQQQ', '1st quarter', 0),
array('QQQQQ', '1st quarter', 0),
array('Q', '2', 7776000),
array('QQ', '02', 7776000),
array('QQQ', 'Q2', 7776000),
array('QQQQ', '2nd quarter', 7776000),
array('QQQQQ', '2nd quarter', 7776000),
array('q', '1', 0),
array('qq', '01', 0),
array('qqq', 'Q1', 0),
array('qqqq', '1st quarter', 0),
array('qqqqq', '1st quarter', 0),
);
}
public function parseMonthProvider()
{
return array(
array('y-M-d', '1970-1-1', 0),
array('y-MMM-d', '1970-Jan-1', 0),
array('y-MMMM-d', '1970-January-1', 0),
);
}
public function parseStandaloneMonthProvider()
{
return array(
array('y-L-d', '1970-1-1', 0),
array('y-LLL-d', '1970-Jan-1', 0),
array('y-LLLL-d', '1970-January-1', 0),
);
}
public function parseDayProvider()
{
return array(
array('y-M-d', '1970-1-1', 0),
array('y-M-dd', '1970-1-01', 0),
array('y-M-ddd', '1970-1-001', 0),
);
}
public function parseDayOfWeekProvider()
{
return array(
array('E', 'Thu', 0),
array('EE', 'Thu', 0),
array('EEE', 'Thu', 0),
array('EEEE', 'Thursday', 0),
array('EEEEE', 'T', 432000),
array('EEEEEE', 'Thu', 0),
);
}
public function parseDayOfYearProvider()
{
return array(
array('D', '1', 0),
array('D', '2', 86400),
);
}
public function parseHour12ClockOneBasedProvider()
{
return array(
// 12 hours (1-12)
array('y-M-d h', '1970-1-1 1', 3600),
array('y-M-d h', '1970-1-1 10', 36000),
array('y-M-d hh', '1970-1-1 11', 39600),
array('y-M-d hh', '1970-1-1 12', 0),
array('y-M-d hh a', '1970-1-1 0 AM', 0),
array('y-M-d hh a', '1970-1-1 1 AM', 3600),
array('y-M-d hh a', '1970-1-1 10 AM', 36000),
array('y-M-d hh a', '1970-1-1 11 AM', 39600),
array('y-M-d hh a', '1970-1-1 12 AM', 0),
array('y-M-d hh a', '1970-1-1 23 AM', 82800),
array('y-M-d hh a', '1970-1-1 24 AM', 86400),
array('y-M-d hh a', '1970-1-1 0 PM', 43200),
array('y-M-d hh a', '1970-1-1 1 PM', 46800),
array('y-M-d hh a', '1970-1-1 10 PM', 79200),
array('y-M-d hh a', '1970-1-1 11 PM', 82800),
array('y-M-d hh a', '1970-1-1 12 PM', 43200),
array('y-M-d hh a', '1970-1-1 23 PM', 126000),
array('y-M-d hh a', '1970-1-1 24 PM', 129600),
);
}
public function parseHour12ClockZeroBasedProvider()
{
return array(
// 12 hours (0-11)
array('y-M-d K', '1970-1-1 1', 3600),
array('y-M-d K', '1970-1-1 10', 36000),
array('y-M-d KK', '1970-1-1 11', 39600),
array('y-M-d KK', '1970-1-1 12', 43200),
array('y-M-d KK a', '1970-1-1 0 AM', 0),
array('y-M-d KK a', '1970-1-1 1 AM', 3600),
array('y-M-d KK a', '1970-1-1 10 AM', 36000),
array('y-M-d KK a', '1970-1-1 11 AM', 39600),
array('y-M-d KK a', '1970-1-1 12 AM', 43200),
array('y-M-d KK a', '1970-1-1 23 AM', 82800),
array('y-M-d KK a', '1970-1-1 24 AM', 86400),
array('y-M-d KK a', '1970-1-1 0 PM', 43200),
array('y-M-d KK a', '1970-1-1 1 PM', 46800),
array('y-M-d KK a', '1970-1-1 10 PM', 79200),
array('y-M-d KK a', '1970-1-1 11 PM', 82800),
array('y-M-d KK a', '1970-1-1 12 PM', 86400),
array('y-M-d KK a', '1970-1-1 23 PM', 126000),
array('y-M-d KK a', '1970-1-1 24 PM', 129600),
);
}
public function parseHour24ClockOneBasedProvider()
{
return array(
// 24 hours (1-24)
array('y-M-d k', '1970-1-1 1', 3600),
array('y-M-d k', '1970-1-1 10', 36000),
array('y-M-d kk', '1970-1-1 11', 39600),
array('y-M-d kk', '1970-1-1 12', 43200),
array('y-M-d kk', '1970-1-1 23', 82800),
array('y-M-d kk', '1970-1-1 24', 0),
array('y-M-d kk a', '1970-1-1 0 AM', 0),
array('y-M-d kk a', '1970-1-1 1 AM', 0),
array('y-M-d kk a', '1970-1-1 10 AM', 0),
array('y-M-d kk a', '1970-1-1 11 AM', 0),
array('y-M-d kk a', '1970-1-1 12 AM', 0),
array('y-M-d kk a', '1970-1-1 23 AM', 0),
array('y-M-d kk a', '1970-1-1 24 AM', 0),
array('y-M-d kk a', '1970-1-1 0 PM', 43200),
array('y-M-d kk a', '1970-1-1 1 PM', 43200),
array('y-M-d kk a', '1970-1-1 10 PM', 43200),
array('y-M-d kk a', '1970-1-1 11 PM', 43200),
array('y-M-d kk a', '1970-1-1 12 PM', 43200),
array('y-M-d kk a', '1970-1-1 23 PM', 43200),
array('y-M-d kk a', '1970-1-1 24 PM', 43200),
);
}
public function parseHour24ClockZeroBasedProvider()
{
return array(
// 24 hours (0-23)
array('y-M-d H', '1970-1-1 0', 0),
array('y-M-d H', '1970-1-1 1', 3600),
array('y-M-d H', '1970-1-1 10', 36000),
array('y-M-d HH', '1970-1-1 11', 39600),
array('y-M-d HH', '1970-1-1 12', 43200),
array('y-M-d HH', '1970-1-1 23', 82800),
array('y-M-d HH a', '1970-1-1 0 AM', 0),
array('y-M-d HH a', '1970-1-1 1 AM', 0),
array('y-M-d HH a', '1970-1-1 10 AM', 0),
array('y-M-d HH a', '1970-1-1 11 AM', 0),
array('y-M-d HH a', '1970-1-1 12 AM', 0),
array('y-M-d HH a', '1970-1-1 23 AM', 0),
array('y-M-d HH a', '1970-1-1 24 AM', 0),
array('y-M-d HH a', '1970-1-1 0 PM', 43200),
array('y-M-d HH a', '1970-1-1 1 PM', 43200),
array('y-M-d HH a', '1970-1-1 10 PM', 43200),
array('y-M-d HH a', '1970-1-1 11 PM', 43200),
array('y-M-d HH a', '1970-1-1 12 PM', 43200),
array('y-M-d HH a', '1970-1-1 23 PM', 43200),
array('y-M-d HH a', '1970-1-1 24 PM', 43200),
);
}
public function parseMinuteProvider()
{
return array(
array('y-M-d HH:m', '1970-1-1 0:1', 60),
array('y-M-d HH:mm', '1970-1-1 0:10', 600),
);
}
public function parseSecondProvider()
{
return array(
array('y-M-d HH:mm:s', '1970-1-1 00:01:1', 61),
array('y-M-d HH:mm:ss', '1970-1-1 00:01:10', 70),
);
}
public function parseTimezoneProvider()
{
return array(
array('y-M-d HH:mm:ss zzzz', '1970-1-1 00:00:00 GMT-03:00', 10800),
array('y-M-d HH:mm:ss zzzz', '1970-1-1 00:00:00 GMT-04:00', 14400),
array('y-M-d HH:mm:ss zzzz', '1970-1-1 00:00:00 GMT-00:00', 0),
array('y-M-d HH:mm:ss zzzz', '1970-1-1 00:00:00 GMT+03:00', -10800),
array('y-M-d HH:mm:ss zzzz', '1970-1-1 00:00:00 GMT+04:00', -14400),
array('y-M-d HH:mm:ss zzzz', '1970-1-1 00:00:00 GMT-0300', 10800),
array('y-M-d HH:mm:ss zzzz', '1970-1-1 00:00:00 GMT+0300', -10800),
// a previous timezone parsing should not change the timezone for the next parsing
array('y-M-d HH:mm:ss', '1970-1-1 00:00:00', 0),
);
}
public function parseAmPmProvider()
{
return array(
// AM/PM (already covered by hours tests)
array('y-M-d HH:mm:ss a', '1970-1-1 00:00:00 AM', 0),
array('y-M-d HH:mm:ss a', '1970-1-1 00:00:00 PM', 43200),
);
}
public function parseStandaloneAmPmProvider()
{
return array(
array('a', 'AM', 0),
array('a', 'PM', 43200),
);
}
public function parseRegexMetaCharsProvider()
{
return array(
// regexp meta chars in the pattern string
array('y[M-d', '1970[1-1', 0),
array('y[M/d', '1970[1/1', 0),
);
}
public function parseQuoteCharsProvider()
{
return array(
array("'M'", 'M', 0),
array("'yy'", 'yy', 0),
array("'''yy'", "'yy", 0),
array("''y", "'1970", 0),
array("H 'o'' clock'", "0 o' clock", 0),
);
}
public function parseDashSlashProvider()
{
return array(
array('y-M-d', '1970/1/1', 0),
array('yy-M-d', '70/1/1', 0),
array('y/M/d', '1970-1-1', 0),
array('yy/M/d', '70-1-1', 0),
);
}
/**
* @dataProvider parseErrorProvider
*/
public function testParseError($pattern, $value)
{
$errorCode = StubIntlGlobals::U_PARSE_ERROR;
$errorMessage = 'Date parsing failed: U_PARSE_ERROR';
$formatter = $this->getDefaultDateFormatter($pattern);
$this->assertFalse($formatter->parse($value));
$this->assertIsIntlFailure($formatter, $errorMessage, $errorCode);
}
public function parseErrorProvider()
{
$data = array(
// 1 char month
array('y-MMMMM-d', '1970-J-1'),
array('y-MMMMM-d', '1970-S-1'),
// standalone 1 char month
array('y-LLLLL-d', '1970-J-1'),
array('y-LLLLL-d', '1970-S-1'),
);
if (!Intl::isExtensionLoaded() || IcuVersion::compare(Intl::getIcuVersion(), '4.8', '<')) {
$data[] = array('y-M-d', '1970/1/1');
$data[] = array('yy-M-d', '70/1/1');
}
return $data;
}
/*
* https://github.com/symfony/symfony/issues/4242
*/
public function testParseAfterError()
{
$this->testParseError('y-MMMMM-d', '1970-J-1');
$this->testParse('y-M-d', '1970-1-1', 0);
}
public function testParseWithNullPositionValue()
{
$position = null;
$formatter = $this->getDefaultDateFormatter('y');
$this->assertSame(0, $formatter->parse('1970', $position));
$this->assertNull($position);
}
public function testSetPattern()
{
$formatter = $this->getDefaultDateFormatter();
$formatter->setPattern('yyyy-MM-dd');
$this->assertEquals('yyyy-MM-dd', $formatter->getPattern());
}
/**
* @covers Symfony\Component\Intl\Stub\StubIntlDateFormatter::getTimeZoneId
* @dataProvider setTimeZoneIdProvider
*/
public function testSetTimeZoneId($timeZoneId, $expectedTimeZoneId)
{
$formatter = $this->getDefaultDateFormatter();
if (version_compare(PHP_VERSION, '5.5.0-dev', '>=')) {
$formatter->setTimeZone($timeZoneId);
} else {
$formatter->setTimeZoneId($timeZoneId);
}
$this->assertEquals($expectedTimeZoneId, $formatter->getTimeZoneId());
}
public function setTimeZoneIdProvider()
{
$data = array(
array('UTC', 'UTC'),
array('GMT', 'GMT'),
array('GMT-03:00', 'GMT-03:00'),
array('Europe/Zurich', 'Europe/Zurich'),
);
// When time zone not exists, uses UTC by default
if (version_compare(PHP_VERSION, '5.5.0-dev', '>=')) {
$data[] = array('GMT-0300', 'UTC');
$data[] = array('Foo/Bar', 'UTC');
$data[] = array('GMT+00:AA', 'UTC');
$data[] = array('GMT+00AA', 'UTC');
} else {
$data[] = array('GMT-0300', 'GMT-0300');
$data[] = array('Foo/Bar', 'Foo/Bar');
$data[] = array('GMT+00:AA', 'GMT+00:AA');
$data[] = array('GMT+00AA', 'GMT+00AA');
}
return $data;
}
protected function getDefaultDateFormatter($pattern = null)
{
return $this->getDateFormatter('en', StubIntlDateFormatter::MEDIUM, StubIntlDateFormatter::SHORT, 'UTC', StubIntlDateFormatter::GREGORIAN, $pattern);
}
protected function getDateTime($timestamp = null)
{
if (version_compare(PHP_VERSION, '5.5.0-dev', '>=')) {
$timeZone = date_default_timezone_get();
} else {
$timeZone = getenv('TZ') ?: 'UTC';
}
$dateTime = new \DateTime();
$dateTime->setTimestamp(null === $timestamp ? time() : $timestamp);
$dateTime->setTimeZone(new \DateTimeZone($timeZone));
return $dateTime;
}
protected function assertIsIntlFailure($formatter, $errorMessage, $errorCode)
{
$this->assertSame($errorMessage, $this->getIntlErrorMessage());
$this->assertSame($errorCode, $this->getIntlErrorCode());
$this->assertTrue($this->isIntlFailure($this->getIntlErrorCode()));
$this->assertSame($errorMessage, $formatter->getErrorMessage());
$this->assertSame($errorCode, $formatter->getErrorCode());
$this->assertTrue($this->isIntlFailure($formatter->getErrorCode()));
}
protected function assertIsIntlSuccess($formatter, $errorMessage, $errorCode)
{
/* @var StubIntlDateFormatter $formatter */
$this->assertSame($errorMessage, $this->getIntlErrorMessage());
$this->assertSame($errorCode, $this->getIntlErrorCode());
$this->assertFalse($this->isIntlFailure($this->getIntlErrorCode()));
$this->assertSame($errorMessage, $formatter->getErrorMessage());
$this->assertSame($errorCode, $formatter->getErrorCode());
$this->assertFalse($this->isIntlFailure($formatter->getErrorCode()));
}
/**
* @param $locale
* @param $datetype
* @param $timetype
* @param null $timezone
* @param int $calendar
* @param null $pattern
*
* @return mixed
*/
abstract protected function getDateFormatter($locale, $datetype, $timetype, $timezone = null, $calendar = StubIntlDateFormatter::GREGORIAN, $pattern = null);
/**
* @return string
*/
abstract protected function getIntlErrorMessage();
/**
* @return integer
*/
abstract protected function getIntlErrorCode();
/**
* @param integer $errorCode
*
* @return Boolean
*/
abstract protected function isIntlFailure($errorCode);
}

View File

@ -0,0 +1,221 @@
<?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\Intl\Tests\DateFormatter;
use Symfony\Component\Intl\DateFormatter\StubIntlDateFormatter;
use Symfony\Component\Intl\Globals\StubIntlGlobals;
use Symfony\Component\Intl\Util\Version;
class StubIntlDateFormatterTest extends AbstractIntlDateFormatterTest
{
public function testConstructor()
{
$formatter = new StubIntlDateFormatter('en', StubIntlDateFormatter::MEDIUM, StubIntlDateFormatter::SHORT, 'UTC', StubIntlDateFormatter::GREGORIAN, 'y-M-d');
$this->assertEquals('y-M-d', $formatter->getPattern());
}
/**
* @expectedException \Symfony\Component\Intl\Exception\MethodArgumentValueNotImplementedException
*/
public function testConstructorWithUnsupportedLocale()
{
new StubIntlDateFormatter('pt_BR', StubIntlDateFormatter::MEDIUM, StubIntlDateFormatter::SHORT);
}
public function testStaticCreate()
{
$formatter = StubIntlDateFormatter::create('en', StubIntlDateFormatter::MEDIUM, StubIntlDateFormatter::SHORT);
$this->assertInstanceOf('Symfony\Component\Intl\DateFormatter\StubIntlDateFormatter', $formatter);
}
public function testFormatWithUnsupportedTimestampArgument()
{
$formatter = $this->getDefaultDateFormatter();
$localtime = array(
'tm_sec' => 59,
'tm_min' => 3,
'tm_hour' => 15,
'tm_mday' => 15,
'tm_mon' => 3,
'tm_year' => 112,
'tm_wday' => 0,
'tm_yday' => 105,
'tm_isdst' => 0
);
try {
$formatter->format($localtime);
} catch (\Exception $e) {
$this->assertInstanceOf('Symfony\Component\Intl\Exception\MethodArgumentValueNotImplementedException', $e);
if (version_compare(PHP_VERSION, '5.3.4', '>=')) {
$this->assertStringEndsWith('Only integer unix timestamps and DateTime objects are supported. Please install the "intl" extension for full localization capabilities.', $e->getMessage());
} else {
$this->assertStringEndsWith('Only integer unix timestamps are supported. Please install the "intl" extension for full localization capabilities.', $e->getMessage());
}
}
}
/**
* @expectedException \Symfony\Component\Intl\Exception\NotImplementedException
*/
public function testFormatWithUnimplementedChars()
{
$pattern = 'Y';
$formatter = new StubIntlDateFormatter('en', StubIntlDateFormatter::MEDIUM, StubIntlDateFormatter::SHORT, 'UTC', StubIntlDateFormatter::GREGORIAN, $pattern);
$formatter->format(0);
}
/**
* @expectedException \Symfony\Component\Intl\Exception\NotImplementedException
*/
public function testFormatWithNonIntegerTimestamp()
{
$formatter = $this->getDefaultDateFormatter();
$formatter->format(array());
}
public function testGetErrorCode()
{
$formatter = $this->getDefaultDateFormatter();
$this->assertEquals(StubIntlGlobals::getErrorCode(), $formatter->getErrorCode());
}
public function testGetErrorMessage()
{
$formatter = $this->getDefaultDateFormatter();
$this->assertEquals(StubIntlGlobals::getErrorMessage(), $formatter->getErrorMessage());
}
public function testIsLenient()
{
$formatter = $this->getDefaultDateFormatter();
$this->assertFalse($formatter->isLenient());
}
/**
* @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
*/
public function testLocaltime()
{
$formatter = $this->getDefaultDateFormatter();
$formatter->localtime('Wednesday, December 31, 1969 4:00:00 PM PT');
}
/**
* @expectedException \Symfony\Component\Intl\Exception\MethodArgumentNotImplementedException
*/
public function testParseWithNotNullPositionValue()
{
$position = 0;
$formatter = $this->getDefaultDateFormatter('y');
$this->assertSame(0, $formatter->parse('1970', $position));
}
/**
* @expectedException \Symfony\Component\Intl\Exception\MethodNotImplementedException
*/
public function testSetCalendar()
{
$formatter = $this->getDefaultDateFormatter();
$formatter->setCalendar(StubIntlDateFormatter::GREGORIAN);
}
/**
* @expectedException \Symfony\Component\Intl\Exception\MethodArgumentValueNotImplementedException
*/
public function testSetLenient()
{
$formatter = $this->getDefaultDateFormatter();
$formatter->setLenient(true);
}
/**
* @expectedException \Symfony\Component\Intl\Exception\NotImplementedException
*/
public function testFormatWithGmtTimeZoneAndMinutesOffset()
{
parent::testFormatWithGmtTimeZoneAndMinutesOffset();
}
/**
* @expectedException \Symfony\Component\Intl\Exception\NotImplementedException
*/
public function testFormatWithNonStandardTimezone()
{
parent::testFormatWithNonStandardTimezone();
}
public function parseStandaloneAmPmProvider()
{
return $this->notImplemented(parent::parseStandaloneAmPmProvider());
}
public function parseDayOfWeekProvider()
{
return $this->notImplemented(parent::parseDayOfWeekProvider());
}
public function parseDayOfYearProvider()
{
return $this->notImplemented(parent::parseDayOfYearProvider());
}
public function parseQuarterProvider()
{
return $this->notImplemented(parent::parseQuarterProvider());
}
protected function getDateFormatter($locale, $datetype, $timetype, $timezone = null, $calendar = StubIntlDateFormatter::GREGORIAN, $pattern = null)
{
return new StubIntlDateFormatter($locale, $datetype, $timetype, $timezone, $calendar, $pattern);
}
protected function getIntlErrorMessage()
{
return StubIntlGlobals::getErrorMessage();
}
protected function getIntlErrorCode()
{
return StubIntlGlobals::getErrorCode();
}
protected function isIntlFailure($errorCode)
{
return StubIntlGlobals::isFailure($errorCode);
}
/**
* Just to document the differences between the stub and the intl
* implementations. The intl can parse any of the tested formats alone. The
* stub does not implement them as it would be needed to add more
* abstraction, passing more context to the transformers objects. Any of the
* formats are ignored alone or with date/time data (years, months, days,
* hours, minutes and seconds).
*
* Also in intl, format like 'ss E' for '10 2' (2nd day of year
* + 10 seconds) are added, then we have 86,400 seconds (24h * 60min * 60s)
* + 10 seconds
*
* @param array $dataSets
*
* @return array
*/
private function notImplemented(array $dataSets)
{
return array_map(function ($row) {
return array($row[0], $row[1], 0);
}, $dataSets);
}
}

View File

@ -0,0 +1,64 @@
<?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\Intl\Tests\DateFormatter\Verification;
use Symfony\Component\Intl\DateFormatter\StubIntlDateFormatter;
use Symfony\Component\Intl\Tests\DateFormatter\AbstractIntlDateFormatterTest;
/**
* Verifies that {@link AbstractIntlDateFormatterTest} matches the behavior of
* the {@link \IntlDateFormatter} class in a specific version of ICU.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class IntlDateFormatterTest extends AbstractIntlDateFormatterTest
{
protected function setUp()
{
$this->skipIfIntlExtensionNotLoaded();
$this->skipIfInsufficientIcuVersion();
parent::setUp();
}
/**
* It seems IntlDateFormatter caches the timezone id when not explicitly set via constructor or by the
* setTimeZoneId() method. Since testFormatWithDefaultTimezoneIntl() runs using the default environment
* time zone, this test would use it too if not running in a separated process.
*
* @runInSeparateProcess
*/
public function testFormatWithTimezoneFromEnvironmentVariable()
{
parent::testFormatWithTimezoneFromEnvironmentVariable();
}
protected function getDateFormatter($locale, $datetype, $timetype, $timezone = null, $calendar = StubIntlDateFormatter::GREGORIAN, $pattern = null)
{
return new \IntlDateFormatter($locale, $datetype, $timetype, $timezone, $calendar, $pattern);
}
protected function getIntlErrorMessage()
{
return intl_get_error_message();
}
protected function getIntlErrorCode()
{
return intl_get_error_code();
}
protected function isIntlFailure($errorCode)
{
return intl_is_failure($errorCode);
}
}

View File

@ -0,0 +1,43 @@
<?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\Intl\Tests\Globals;
use Symfony\Component\Intl\Tests\IntlTestCase;
/**
* Test case for intl function implementations.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
abstract class AbstractIntlGlobalsTest extends IntlTestCase
{
public function errorNameProvider()
{
return array (
array(-129, '[BOGUS UErrorCode]'),
array(0, 'U_ZERO_ERROR'),
array(1, 'U_ILLEGAL_ARGUMENT_ERROR'),
array(9, 'U_PARSE_ERROR'),
array(129, '[BOGUS UErrorCode]'),
);
}
/**
* @dataProvider errorNameProvider
*/
public function testGetErrorName($errorCode, $errorName)
{
$this->assertSame($errorName, $this->getIntlErrorName($errorCode));
}
abstract protected function getIntlErrorName($errorCode);
}

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