Actualización

This commit is contained in:
Xes
2025-04-10 12:24:57 +02:00
parent 8969cc929d
commit 45420b6f0d
39760 changed files with 4303286 additions and 0 deletions

3
vendor/symfony/intl/.gitignore vendored Normal file
View File

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

View File

@@ -0,0 +1,295 @@
<?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\MethodArgumentValueNotImplementedException;
use Symfony\Component\Intl\Exception\MethodNotImplementedException;
use Symfony\Component\Intl\Globals\IntlGlobals;
use Symfony\Component\Intl\Locale\Locale;
/**
* Replacement for PHP's native {@link \Collator} class.
*
* The only methods currently supported in this class are:
*
* - {@link \__construct}
* - {@link create}
* - {@link asort}
* - {@link getErrorCode}
* - {@link getErrorMessage}
* - {@link getLocale}
*
* @author Igor Wiedler <igor@wiedler.ch>
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class Collator
{
/* 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;
/**
* @param string|null $locale The locale code. The only currently supported locale is "en" (or null using the default locale, i.e. "en")
*
* @throws MethodArgumentValueNotImplementedException When $locale different than "en" or null is passed
*/
public function __construct($locale)
{
if ('en' !== $locale && null !== $locale) {
throw new MethodArgumentValueNotImplementedException(__METHOD__, 'locale', $locale, 'Only the locale "en" is supported');
}
}
/**
* Static constructor.
*
* @param string|null $locale The locale code. The only currently supported locale is "en" (or null using the default locale, i.e. "en")
*
* @return self
*
* @throws MethodArgumentValueNotImplementedException When $locale different than "en" or null is passed
*/
public static function create($locale)
{
return new self($locale);
}
/**
* Sort array maintaining index association.
*
* @param array &$array Input array
* @param int $sortFlag Flags for sorting, can be one of the following:
* Collator::SORT_REGULAR - compare items normally (don't change types)
* Collator::SORT_NUMERIC - compare items numerically
* Collator::SORT_STRING - compare items as strings
*
* @return bool True on success or false on failure
*/
public function asort(&$array, $sortFlag = self::SORT_REGULAR)
{
$intlToPlainFlagMap = [
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);
}
/**
* Not supported. Compare two Unicode strings.
*
* @param string $str1 The first string to compare
* @param string $str2 The second string to compare
*
* @return bool|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 https://php.net/collator.compare
*
* @throws MethodNotImplementedException
*/
public function compare($str1, $str2)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Get a value of an integer collator attribute.
*
* @param int $attr An attribute specifier, one of the attribute constants
*
* @return bool|int The attribute value on success or false on error
*
* @see https://php.net/collator.getattribute
*
* @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 IntlGlobals::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 Not supported. The locale name type to return (Locale::VALID_LOCALE or Locale::ACTUAL_LOCALE)
*
* @return string The locale used to create the collator. Currently always
* returns "en".
*/
public function getLocale($type = Locale::ACTUAL_LOCALE)
{
return 'en';
}
/**
* Not supported. Get sorting key for a string.
*
* @param string $string The string to produce the key from
*
* @return string The collation key for $string
*
* @see https://php.net/collator.getsortkey
*
* @throws MethodNotImplementedException
*/
public function getSortKey($string)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Get current collator's strength.
*
* @return bool|int The current collator's strength or false on failure
*
* @see https://php.net/collator.getstrength
*
* @throws MethodNotImplementedException
*/
public function getStrength()
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. 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 bool True on success or false on failure
*
* @see https://php.net/collator.setattribute
*
* @throws MethodNotImplementedException
*/
public function setAttribute($attr, $val)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Set the collator's strength.
*
* @param int $strength Strength to set, possible values:
* Collator::PRIMARY
* Collator::SECONDARY
* Collator::TERTIARY
* Collator::QUATERNARY
* Collator::IDENTICAL
* Collator::DEFAULT
*
* @return bool True on success or false on failure
*
* @see https://php.net/collator.setstrength
*
* @throws MethodNotImplementedException
*/
public function setStrength($strength)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Sort array using specified collator and sort keys.
*
* @param array &$arr Array of strings to sort
*
* @return bool True on success or false on failure
*
* @see https://php.net/collator.sortwithsortkeys
*
* @throws MethodNotImplementedException
*/
public function sortWithSortKeys(&$arr)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Sort array using specified collator.
*
* @param array &$arr Array of string to sort
* @param int $sortFlag Optional sorting type, one of the following:
* Collator::SORT_REGULAR
* Collator::SORT_NUMERIC
* Collator::SORT_STRING
*
* @return bool True on success or false on failure
*
* @see https://php.net/collator.sort
*
* @throws MethodNotImplementedException
*/
public function sort(&$arr, $sortFlag = self::SORT_REGULAR)
{
throw new MethodNotImplementedException(__METHOD__);
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\Data\Bundle\Compiler;
/**
* Compiles a resource bundle.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
interface BundleCompilerInterface
{
/**
* 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,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\Data\Bundle\Compiler;
use Symfony\Component\Intl\Exception\RuntimeException;
/**
* Compiles .txt resource bundles to binary .res files.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class GenrbCompiler implements BundleCompilerInterface
{
private $genrb;
/**
* Creates a new compiler based on the "genrb" executable.
*
* @param string $genrb Optional. The path to the "genrb" executable
* @param string $envVars Optional. Environment variables to be loaded when running "genrb".
*
* @throws RuntimeException if the "genrb" cannot be found
*/
public function __construct($genrb = 'genrb', $envVars = '')
{
exec('which '.$genrb, $output, $status);
if (0 !== $status) {
throw new RuntimeException(sprintf('The command "%s" is not installed.', $genrb));
}
$this->genrb = ($envVars ? $envVars.' ' : '').$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 (0 !== $status) {
throw new RuntimeException(sprintf('genrb failed with status %d while compiling "%s" to "%s".', $status, $sourcePath, $targetDir));
}
}
}

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\Data\Bundle\Reader;
use Symfony\Component\Intl\Data\Util\RingBuffer;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class BufferedBundleReader implements BundleReaderInterface
{
private $reader;
private $buffer;
/**
* Buffers a given reader.
*
* @param BundleReaderInterface $reader The reader to buffer
* @param int $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];
}
}

View File

@@ -0,0 +1,177 @@
<?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\Data\Bundle\Reader;
use Symfony\Component\Intl\Data\Util\RecursiveArrayAccess;
use Symfony\Component\Intl\Exception\MissingResourceException;
use Symfony\Component\Intl\Exception\OutOfBoundsException;
use Symfony\Component\Intl\Exception\ResourceBundleNotFoundException;
use Symfony\Component\Intl\Locale;
/**
* Default implementation of {@link BundleEntryReaderInterface}.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @see BundleEntryReaderInterface
*
* @internal
*/
class BundleEntryReader implements BundleEntryReaderInterface
{
private $reader;
/**
* A mapping of locale aliases to locales.
*/
private $localeAliases = [];
/**
* Creates an entry reader based on the given resource bundle reader.
*/
public function __construct(BundleReaderInterface $reader)
{
$this->reader = $reader;
}
/**
* Stores a mapping of locale aliases to locales.
*
* This mapping is used when reading entries and merging them with their
* fallback locales. If an entry is read for a locale alias (e.g. "mo")
* that points to a locale with a fallback locale ("ro_MD"), the reader
* can continue at the correct fallback locale ("ro").
*
* @param array $localeAliases A mapping of locale aliases to locales
*/
public function setLocaleAliases($localeAliases)
{
$this->localeAliases = $localeAliases;
}
/**
* {@inheritdoc}
*/
public function read($path, $locale)
{
return $this->reader->read($path, $locale);
}
/**
* {@inheritdoc}
*/
public function readEntry($path, $locale, array $indices, $fallback = true)
{
$entry = null;
$isMultiValued = false;
$readSucceeded = false;
$exception = null;
$currentLocale = $locale;
$testedLocales = [];
while (null !== $currentLocale) {
// Resolve any aliases to their target locales
if (isset($this->localeAliases[$currentLocale])) {
$currentLocale = $this->localeAliases[$currentLocale];
}
try {
$data = $this->reader->read($path, $currentLocale);
$currentEntry = RecursiveArrayAccess::get($data, $indices);
$readSucceeded = true;
$isCurrentTraversable = $currentEntry instanceof \Traversable;
$isCurrentMultiValued = $isCurrentTraversable || \is_array($currentEntry);
// Return immediately if fallback is disabled or we are dealing
// with a scalar non-null entry
if (!$fallback || (!$isCurrentMultiValued && null !== $currentEntry)) {
return $currentEntry;
}
// =========================================================
// Fallback is enabled, entry is either multi-valued or NULL
// =========================================================
// If entry is multi-valued, convert to array
if ($isCurrentTraversable) {
$currentEntry = iterator_to_array($currentEntry);
}
// If previously read entry was multi-valued too, merge them
if ($isCurrentMultiValued && $isMultiValued) {
$currentEntry = array_merge($currentEntry, $entry);
}
// Keep the previous entry if the current entry is NULL
if (null !== $currentEntry) {
$entry = $currentEntry;
}
// If this or the previous entry was multi-valued, we are dealing
// with a merged, multi-valued entry now
$isMultiValued = $isMultiValued || $isCurrentMultiValued;
} catch (ResourceBundleNotFoundException $e) {
// Continue if there is a fallback locale for the current
// locale
$exception = $e;
} catch (OutOfBoundsException $e) {
// Remember exception and rethrow if we cannot find anything in
// the fallback locales either
$exception = $e;
}
// Remember which locales we tried
$testedLocales[] = $currentLocale;
// Check whether fallback is allowed
if (!$fallback) {
break;
}
// Then determine fallback locale
$currentLocale = Locale::getFallback($currentLocale);
}
// Multi-valued entry was merged
if ($isMultiValued) {
return $entry;
}
// Entry is still NULL, but no read error occurred
if ($readSucceeded) {
return $entry;
}
// Entry is still NULL, read error occurred. Throw an exception
// containing the detailed path and locale
$errorMessage = sprintf(
'Couldn\'t read the indices [%s] for the locale "%s" in "%s".',
implode('][', $indices),
$locale,
$path
);
// Append fallback locales, if any
if (\count($testedLocales) > 1) {
// Remove original locale
array_shift($testedLocales);
$errorMessage .= sprintf(
' The indices also couldn\'t be found for the fallback locale(s) "%s".',
implode('", "', $testedLocales)
);
}
throw new MissingResourceException($errorMessage, 0, $exception);
}
}

View File

@@ -0,0 +1,55 @@
<?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\Data\Bundle\Reader;
use Symfony\Component\Intl\Exception\MissingResourceException;
/**
* Reads individual entries of a resource file.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
interface BundleEntryReaderInterface 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', ['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 bool $fallback 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
* (i.e. array or \ArrayAccess) or cannot be found
* in the requested locale.
*
* @return mixed returns an array or {@link \ArrayAccess} instance for
* complex data and a scalar value for simple data
*
* @throws MissingResourceException If the indices cannot be accessed
*/
public function readEntry($path, $locale, array $indices, $fallback = true);
}

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\Data\Bundle\Reader;
/**
* Reads resource bundle files.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
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);
}

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\Data\Bundle\Reader;
use Symfony\Component\Intl\Data\Util\ArrayAccessibleResourceBundle;
use Symfony\Component\Intl\Exception\ResourceBundleNotFoundException;
/**
* Reads binary .res resource bundles.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class IntlBundleReader 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.
try {
// Never enable fallback. We want to know if a bundle cannot be found
$bundle = new \ResourceBundle($locale, $path, false);
} catch (\Exception $e) {
// HHVM compatibility: constructor throws on invalid resource
$bundle = null;
}
// The bundle is NULL if the path does not look like a resource bundle
// (i.e. contain a bunch of *.res files)
if (null === $bundle) {
throw new ResourceBundleNotFoundException(sprintf('The resource bundle "%s/%s.res" could not be found.', $path, $locale));
}
// Other possible errors are U_USING_FALLBACK_WARNING and U_ZERO_ERROR,
// which are OK for us.
return new ArrayAccessibleResourceBundle($bundle);
}
}

View File

@@ -0,0 +1,54 @@
<?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\Data\Bundle\Reader;
use Symfony\Component\Intl\Exception\ResourceBundleNotFoundException;
use Symfony\Component\Intl\Exception\RuntimeException;
/**
* Reads .json resource bundles.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class JsonBundleReader implements BundleReaderInterface
{
/**
* {@inheritdoc}
*/
public function read($path, $locale)
{
$fileName = $path.'/'.$locale.'.json';
// prevent directory traversal attacks
if (\dirname($fileName) !== $path) {
throw new ResourceBundleNotFoundException(sprintf('The resource bundle "%s" does not exist.', $fileName));
}
if (!file_exists($fileName)) {
throw new ResourceBundleNotFoundException(sprintf('The resource bundle "%s" does not exist.', $fileName));
}
if (!is_file($fileName)) {
throw new RuntimeException(sprintf('The resource bundle "%s" is not a file.', $fileName));
}
$data = json_decode(file_get_contents($fileName), true);
if (null === $data) {
throw new RuntimeException(sprintf('The resource bundle "%s" contains invalid JSON: ', $fileName).json_last_error_msg());
}
return $data;
}
}

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\Data\Bundle\Reader;
use Symfony\Component\Intl\Exception\ResourceBundleNotFoundException;
use Symfony\Component\Intl\Exception\RuntimeException;
/**
* Reads .php resource bundles.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class PhpBundleReader implements BundleReaderInterface
{
/**
* {@inheritdoc}
*/
public function read($path, $locale)
{
$fileName = $path.'/'.$locale.'.php';
// prevent directory traversal attacks
if (\dirname($fileName) !== $path) {
throw new ResourceBundleNotFoundException(sprintf('The resource bundle "%s" does not exist.', $fileName));
}
if (!file_exists($fileName)) {
throw new ResourceBundleNotFoundException(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;
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\Data\Bundle\Writer;
/**
* Writes resource bundle files.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
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,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\Data\Bundle\Writer;
/**
* Writes .json resource bundles.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class JsonBundleWriter implements BundleWriterInterface
{
/**
* {@inheritdoc}
*/
public function write($path, $locale, $data)
{
if ($data instanceof \Traversable) {
$data = iterator_to_array($data);
}
array_walk_recursive($data, function (&$value) {
if ($value instanceof \Traversable) {
$value = iterator_to_array($value);
}
});
$contents = json_encode($data, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_UNICODE)."\n";
file_put_contents($path.'/'.$locale.'.json', $contents);
}
}

View File

@@ -0,0 +1,55 @@
<?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\Data\Bundle\Writer;
/**
* Writes .php resource bundles.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class PhpBundleWriter implements BundleWriterInterface
{
/**
* {@inheritdoc}
*/
public function write($path, $locale, $data)
{
$template = <<<'TEMPLATE'
<?php
return %s;
TEMPLATE;
if ($data instanceof \Traversable) {
$data = iterator_to_array($data);
}
array_walk_recursive($data, function (&$value) {
if ($value instanceof \Traversable) {
$value = iterator_to_array($value);
}
});
$data = var_export($data, true);
$data = preg_replace('/array \(/', '[', $data);
$data = preg_replace('/\n {1,10}\[/', '[', $data);
$data = preg_replace('/ /', ' ', $data);
$data = preg_replace('/\),$/m', '],', $data);
$data = preg_replace('/\)$/', ']', $data);
$data = sprintf($template, $data);
file_put_contents($path.'/'.$locale.'.php', $data);
}
}

View File

@@ -0,0 +1,236 @@
<?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\Data\Bundle\Writer;
use Symfony\Component\Intl\Exception\UnexpectedTypeException;
/**
* Writes .txt resource bundles.
*
* The resulting files can be converted to binary .res files using a
* {@link \Symfony\Component\Intl\ResourceBundle\Compiler\BundleCompilerInterface}
* implementation.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @see http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt
*
* @internal
*/
class TextBundleWriter implements BundleWriterInterface
{
/**
* {@inheritdoc}
*/
public function write($path, $locale, $data, $fallback = true)
{
$file = fopen($path.'/'.$locale.'.txt', 'w');
$this->writeResourceBundle($file, $locale, $data, $fallback);
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
* @param bool $fallback Whether the resource bundle should be merged
* with the fallback locale
*
* @see http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt
*/
private function writeResourceBundle($file, $bundleName, $value, $fallback)
{
fwrite($file, $bundleName);
$this->writeTable($file, $value, 0, $fallback);
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 int $indentation The number of levels to indent
* @param bool $requireBraces Whether to require braces to be printedaround 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 ($value instanceof \Traversable) {
$value = iterator_to_array($value);
}
if (\is_array($value)) {
$intValues = \count($value) === \count(array_filter($value, 'is_int'));
$keys = array_keys($value);
// check that the keys are 0-indexed and ascending
$intKeys = $keys === range(0, \count($keys) - 1);
if ($intValues && $intKeys) {
$this->writeIntVector($file, $value, $indentation);
return;
}
if ($intKeys) {
$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 int $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 int $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 bool $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 int $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 iterable $value The value of the node
* @param int $indentation The number of levels to indent
* @param bool $fallback Whether the table should be merged
* with the fallback locale
*
* @throws UnexpectedTypeException when $value is not an array and not a
* \Traversable instance
*/
private function writeTable($file, $value, $indentation, $fallback = true)
{
if (!\is_array($value) && !$value instanceof \Traversable) {
throw new UnexpectedTypeException($value, 'array or \Traversable');
}
if (!$fallback) {
fwrite($file, ':table(nofallback)');
}
fwrite($file, "{\n");
foreach ($value as $key => $entry) {
fwrite($file, str_repeat(' ', $indentation + 1));
// escape colons, otherwise they are interpreted as resource types
if (false !== strpos($key, ':') || false !== strpos($key, ' ')) {
$key = '"'.$key.'"';
}
fwrite($file, $key);
$this->writeResource($file, $entry, $indentation + 1);
fwrite($file, "\n");
}
fprintf($file, '%s}', str_repeat(' ', $indentation));
}
}

View File

@@ -0,0 +1,129 @@
<?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\Data\Generator;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Intl\Data\Bundle\Compiler\BundleCompilerInterface;
use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReader;
use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface;
use Symfony\Component\Intl\Data\Bundle\Reader\IntlBundleReader;
use Symfony\Component\Intl\Data\Util\LocaleScanner;
/**
* The rule for compiling the currency bundle.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
abstract class AbstractDataGenerator
{
private $compiler;
private $dirName;
public function __construct(BundleCompilerInterface $compiler, $dirName)
{
$this->compiler = $compiler;
$this->dirName = (string) $dirName;
}
public function generateData(GeneratorConfig $config)
{
$filesystem = new Filesystem();
$localeScanner = new LocaleScanner();
$reader = new BundleEntryReader(new IntlBundleReader());
$writers = $config->getBundleWriters();
$tempDir = sys_get_temp_dir().'/icu-data-'.$this->dirName;
// Prepare filesystem directories
foreach ($writers as $targetDir => $writer) {
$filesystem->remove($targetDir.'/'.$this->dirName);
$filesystem->mkdir($targetDir.'/'.$this->dirName);
}
$filesystem->remove($tempDir);
$filesystem->mkdir($tempDir);
$locales = $this->scanLocales($localeScanner, $config->getSourceDir());
$this->compileTemporaryBundles($this->compiler, $config->getSourceDir(), $tempDir);
$this->preGenerate();
foreach ($locales as $locale) {
$localeData = $this->generateDataForLocale($reader, $tempDir, $locale);
if (null !== $localeData) {
foreach ($writers as $targetDir => $writer) {
$writer->write($targetDir.'/'.$this->dirName, $locale, $localeData);
}
}
}
$rootData = $this->generateDataForRoot($reader, $tempDir);
if (null !== $rootData) {
foreach ($writers as $targetDir => $writer) {
$writer->write($targetDir.'/'.$this->dirName, 'root', $rootData);
}
}
$metaData = $this->generateDataForMeta($reader, $tempDir);
if (null !== $metaData) {
foreach ($writers as $targetDir => $writer) {
$writer->write($targetDir.'/'.$this->dirName, 'meta', $metaData);
}
}
// Clean up
$filesystem->remove($tempDir);
}
/**
* @param string $sourceDir
*
* @return string[]
*/
abstract protected function scanLocales(LocaleScanner $scanner, $sourceDir);
/**
* @param string $sourceDir
* @param string $tempDir
*/
abstract protected function compileTemporaryBundles(BundleCompilerInterface $compiler, $sourceDir, $tempDir);
abstract protected function preGenerate();
/**
* @param string $tempDir
* @param string $displayLocale
*
* @return array|null
*/
abstract protected function generateDataForLocale(BundleEntryReaderInterface $reader, $tempDir, $displayLocale);
/**
* @param string $tempDir
*
* @return array|null
*/
abstract protected function generateDataForRoot(BundleEntryReaderInterface $reader, $tempDir);
/**
* @param string $tempDir
*
* @return array|null
*/
abstract protected function generateDataForMeta(BundleEntryReaderInterface $reader, $tempDir);
}

View File

@@ -0,0 +1,180 @@
<?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\Data\Generator;
use Symfony\Component\Intl\Data\Bundle\Compiler\BundleCompilerInterface;
use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface;
use Symfony\Component\Intl\Data\Util\ArrayAccessibleResourceBundle;
use Symfony\Component\Intl\Data\Util\LocaleScanner;
/**
* The rule for compiling the currency bundle.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class CurrencyDataGenerator extends AbstractDataGenerator
{
private static $denylist = [
'XBA' => true, // European Composite Unit
'XBB' => true, // European Monetary Unit
'XBC' => true, // European Unit of Account (XBC)
'XBD' => true, // European Unit of Account (XBD)
'XUA' => true, // ADB Unit of Account
'XAU' => true, // Gold
'XAG' => true, // Silver
'XPT' => true, // Platinum
'XPD' => true, // Palladium
'XSU' => true, // Sucre
'XDR' => true, // Special Drawing Rights
'XTS' => true, // Testing Currency Code
'XXX' => true, // Unknown Currency
];
/**
* Collects all available currency codes.
*
* @var string[]
*/
private $currencyCodes = [];
/**
* {@inheritdoc}
*/
protected function scanLocales(LocaleScanner $scanner, $sourceDir)
{
return $scanner->scanLocales($sourceDir.'/curr');
}
/**
* {@inheritdoc}
*/
protected function compileTemporaryBundles(BundleCompilerInterface $compiler, $sourceDir, $tempDir)
{
$compiler->compile($sourceDir.'/curr', $tempDir);
$compiler->compile($sourceDir.'/misc/currencyNumericCodes.txt', $tempDir);
}
/**
* {@inheritdoc}
*/
protected function preGenerate()
{
$this->currencyCodes = [];
}
/**
* {@inheritdoc}
*/
protected function generateDataForLocale(BundleEntryReaderInterface $reader, $tempDir, $displayLocale)
{
$localeBundle = $reader->read($tempDir, $displayLocale);
if (isset($localeBundle['Currencies']) && null !== $localeBundle['Currencies']) {
$data = [
'Names' => $this->generateSymbolNamePairs($localeBundle),
];
$this->currencyCodes = array_merge($this->currencyCodes, array_keys($data['Names']));
return $data;
}
return null;
}
/**
* {@inheritdoc}
*/
protected function generateDataForRoot(BundleEntryReaderInterface $reader, $tempDir)
{
$rootBundle = $reader->read($tempDir, 'root');
return [
'Names' => $this->generateSymbolNamePairs($rootBundle),
];
}
/**
* {@inheritdoc}
*/
protected function generateDataForMeta(BundleEntryReaderInterface $reader, $tempDir)
{
$supplementalDataBundle = $reader->read($tempDir, 'supplementalData');
$numericCodesBundle = $reader->read($tempDir, 'currencyNumericCodes');
$this->currencyCodes = array_unique($this->currencyCodes);
sort($this->currencyCodes);
$data = [
'Currencies' => $this->currencyCodes,
'Meta' => $this->generateCurrencyMeta($supplementalDataBundle),
'Alpha3ToNumeric' => $this->generateAlpha3ToNumericMapping($numericCodesBundle, $this->currencyCodes),
];
$data['NumericToAlpha3'] = $this->generateNumericToAlpha3Mapping($data['Alpha3ToNumeric']);
return $data;
}
/**
* @return array
*/
private function generateSymbolNamePairs(ArrayAccessibleResourceBundle $rootBundle)
{
$symbolNamePairs = iterator_to_array($rootBundle['Currencies']);
// Remove unwanted currencies
$symbolNamePairs = array_diff_key($symbolNamePairs, self::$denylist);
return $symbolNamePairs;
}
private function generateCurrencyMeta(ArrayAccessibleResourceBundle $supplementalDataBundle)
{
// The metadata is already de-duplicated. It contains one key "DEFAULT"
// which is used for currencies that don't have dedicated entries.
return iterator_to_array($supplementalDataBundle['CurrencyMeta']);
}
private function generateAlpha3ToNumericMapping(ArrayAccessibleResourceBundle $numericCodesBundle, array $currencyCodes)
{
$alpha3ToNumericMapping = iterator_to_array($numericCodesBundle['codeMap']);
asort($alpha3ToNumericMapping);
// Filter unknown currencies (e.g. "AYM")
$alpha3ToNumericMapping = array_intersect_key($alpha3ToNumericMapping, array_flip($currencyCodes));
return $alpha3ToNumericMapping;
}
private function generateNumericToAlpha3Mapping(array $alpha3ToNumericMapping)
{
$numericToAlpha3Mapping = [];
foreach ($alpha3ToNumericMapping as $alpha3 => $numeric) {
// Make sure that the mapping is stored as table and not as array
$numeric = (string) $numeric;
if (!isset($numericToAlpha3Mapping[$numeric])) {
$numericToAlpha3Mapping[$numeric] = [];
}
$numericToAlpha3Mapping[$numeric][] = $alpha3;
}
return $numericToAlpha3Mapping;
}
}

View File

@@ -0,0 +1,73 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\Data\Generator;
use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface;
use Symfony\Component\Intl\Locale;
/**
* @author Roland Franssen <franssen.roland@gmail.com>
*
* @internal
*/
trait FallbackTrait
{
private $fallbackCache = [];
private $generatingFallback = false;
/**
* @param string $tempDir
* @param string $displayLocale
*
* @return array|null
*
* @see AbstractDataGenerator::generateDataForLocale()
*/
abstract protected function generateDataForLocale(BundleEntryReaderInterface $reader, $tempDir, $displayLocale);
/**
* @param string $tempDir
*
* @return array|null
*
* @see AbstractDataGenerator::generateDataForRoot()
*/
abstract protected function generateDataForRoot(BundleEntryReaderInterface $reader, $tempDir);
/**
* @param string $tempDir
* @param string $displayLocale
*
* @return array
*/
private function generateFallbackData(BundleEntryReaderInterface $reader, $tempDir, $displayLocale)
{
if (null === $fallback = Locale::getFallback($displayLocale)) {
return [];
}
if (isset($this->fallbackCache[$fallback])) {
return $this->fallbackCache[$fallback];
}
$prevGeneratingFallback = $this->generatingFallback;
$this->generatingFallback = true;
try {
$data = 'root' === $fallback ? $this->generateDataForRoot($reader, $tempDir) : $this->generateDataForLocale($reader, $tempDir, $fallback);
} finally {
$this->generatingFallback = $prevGeneratingFallback;
}
return $this->fallbackCache[$fallback] = $data ?: [];
}
}

View File

@@ -0,0 +1,84 @@
<?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\Data\Generator;
use Symfony\Component\Intl\Data\Bundle\Writer\BundleWriterInterface;
/**
* Stores contextual information for resource bundle generation.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class GeneratorConfig
{
private $sourceDir;
private $icuVersion;
/**
* @var BundleWriterInterface[]
*/
private $bundleWriters = [];
/**
* @param string $sourceDir
* @param string $icuVersion
*/
public function __construct($sourceDir, $icuVersion)
{
$this->sourceDir = $sourceDir;
$this->icuVersion = $icuVersion;
}
/**
* Adds a writer to be used during the data conversion.
*
* @param string $targetDir The output directory
* @param BundleWriterInterface $writer The writer instance
*/
public function addBundleWriter($targetDir, BundleWriterInterface $writer)
{
$this->bundleWriters[$targetDir] = $writer;
}
/**
* Returns the writers indexed by their output directories.
*
* @return BundleWriterInterface[]
*/
public function getBundleWriters()
{
return $this->bundleWriters;
}
/**
* Returns the directory where the source versions of the resource bundles
* are stored.
*
* @return string An absolute path to a directory
*/
public function getSourceDir()
{
return $this->sourceDir;
}
/**
* Returns the ICU version of the bundles being converted.
*
* @return string The ICU version string
*/
public function getIcuVersion()
{
return $this->icuVersion;
}
}

View File

@@ -0,0 +1,198 @@
<?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\Data\Generator;
use Symfony\Component\Intl\Data\Bundle\Compiler\BundleCompilerInterface;
use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface;
use Symfony\Component\Intl\Data\Util\ArrayAccessibleResourceBundle;
use Symfony\Component\Intl\Data\Util\LocaleScanner;
use Symfony\Component\Intl\Exception\RuntimeException;
/**
* The rule for compiling the language bundle.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class LanguageDataGenerator extends AbstractDataGenerator
{
/**
* Source: https://iso639-3.sil.org/code_tables/639/data.
*/
private static $preferredAlpha2ToAlpha3Mapping = [
'ak' => 'aka',
'ar' => 'ara',
'ay' => 'aym',
'az' => 'aze',
'bo' => 'bod',
'cr' => 'cre',
'cs' => 'ces',
'cy' => 'cym',
'de' => 'deu',
'dz' => 'dzo',
'el' => 'ell',
'et' => 'est',
'eu' => 'eus',
'fa' => 'fas',
'ff' => 'ful',
'fr' => 'fra',
'gn' => 'grn',
'hy' => 'hye',
'hr' => 'hrv',
'ik' => 'ipk',
'is' => 'isl',
'iu' => 'iku',
'ka' => 'kat',
'kr' => 'kau',
'kg' => 'kon',
'kv' => 'kom',
'ku' => 'kur',
'lv' => 'lav',
'mg' => 'mlg',
'mi' => 'mri',
'mk' => 'mkd',
'mn' => 'mon',
'ms' => 'msa',
'my' => 'mya',
'nb' => 'nob',
'ne' => 'nep',
'nl' => 'nld',
'oj' => 'oji',
'om' => 'orm',
'or' => 'ori',
'ps' => 'pus',
'qu' => 'que',
'ro' => 'ron',
'sc' => 'srd',
'sk' => 'slk',
'sq' => 'sqi',
'sr' => 'srp',
'sw' => 'swa',
'uz' => 'uzb',
'yi' => 'yid',
'za' => 'zha',
'zh' => 'zho',
];
/**
* Collects all available language codes.
*
* @var string[]
*/
private $languageCodes = [];
/**
* {@inheritdoc}
*/
protected function scanLocales(LocaleScanner $scanner, $sourceDir)
{
return $scanner->scanLocales($sourceDir.'/lang');
}
/**
* {@inheritdoc}
*/
protected function compileTemporaryBundles(BundleCompilerInterface $compiler, $sourceDir, $tempDir)
{
$compiler->compile($sourceDir.'/lang', $tempDir);
$compiler->compile($sourceDir.'/misc/metadata.txt', $tempDir);
}
/**
* {@inheritdoc}
*/
protected function preGenerate()
{
$this->languageCodes = [];
}
/**
* {@inheritdoc}
*/
protected function generateDataForLocale(BundleEntryReaderInterface $reader, $tempDir, $displayLocale)
{
$localeBundle = $reader->read($tempDir, $displayLocale);
// isset() on \ResourceBundle returns true even if the value is null
if (isset($localeBundle['Languages']) && null !== $localeBundle['Languages']) {
$data = [
'Names' => iterator_to_array($localeBundle['Languages']),
];
$this->languageCodes = array_merge($this->languageCodes, array_keys($data['Names']));
return $data;
}
return null;
}
/**
* {@inheritdoc}
*/
protected function generateDataForRoot(BundleEntryReaderInterface $reader, $tempDir)
{
}
/**
* {@inheritdoc}
*/
protected function generateDataForMeta(BundleEntryReaderInterface $reader, $tempDir)
{
$metadataBundle = $reader->read($tempDir, 'metadata');
$this->languageCodes = array_unique($this->languageCodes);
sort($this->languageCodes);
return [
'Languages' => $this->languageCodes,
'Alpha2ToAlpha3' => $this->generateAlpha2ToAlpha3Mapping($metadataBundle),
];
}
private function generateAlpha2ToAlpha3Mapping(ArrayAccessibleResourceBundle $metadataBundle)
{
$aliases = iterator_to_array($metadataBundle['alias']['language']);
$alpha2ToAlpha3 = [];
foreach ($aliases as $alias => $data) {
$language = $data['replacement'];
if (2 === \strlen($language) && 3 === \strlen($alias) && 'overlong' === $data['reason']) {
if (isset(self::$preferredAlpha2ToAlpha3Mapping[$language])) {
// Validate to prevent typos
if (!isset($aliases[self::$preferredAlpha2ToAlpha3Mapping[$language]])) {
throw new RuntimeException('The statically set three-letter mapping '.self::$preferredAlpha2ToAlpha3Mapping[$language].' for the language code '.$language.' seems to be invalid. Typo?');
}
$alpha3 = self::$preferredAlpha2ToAlpha3Mapping[$language];
$alpha2 = $aliases[$alpha3]['replacement'];
if ($language !== $alpha2) {
throw new RuntimeException('The statically set three-letter mapping '.$alpha3.' for the language code '.$language.' seems to be an alias for '.$alpha2.'. Wrong mapping?');
}
$alpha2ToAlpha3[$language] = $alpha3;
} elseif (isset($alpha2ToAlpha3[$language])) {
throw new RuntimeException('Multiple three-letter mappings exist for the language code '.$language.'. Please add one of them to the property $preferredAlpha2ToAlpha3Mapping.');
} else {
$alpha2ToAlpha3[$language] = $alias;
}
}
}
asort($alpha2ToAlpha3);
return $alpha2ToAlpha3;
}
}

View File

@@ -0,0 +1,180 @@
<?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\Data\Generator;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Intl\Data\Bundle\Compiler\BundleCompilerInterface;
use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface;
use Symfony\Component\Intl\Data\Util\LocaleScanner;
use Symfony\Component\Intl\Exception\MissingResourceException;
/**
* The rule for compiling the locale bundle.
*
* @author Bernhard Schussek <bschussek@gmail.com>
* @author Roland Franssen <franssen.roland@gmail.com>
*
* @internal
*/
class LocaleDataGenerator extends AbstractDataGenerator
{
use FallbackTrait;
private $locales = [];
private $localeAliases = [];
/**
* {@inheritdoc}
*/
protected function scanLocales(LocaleScanner $scanner, $sourceDir)
{
$this->locales = $scanner->scanLocales($sourceDir.'/locales');
$this->localeAliases = $scanner->scanAliases($sourceDir.'/locales');
return $this->locales;
}
/**
* {@inheritdoc}
*/
protected function compileTemporaryBundles(BundleCompilerInterface $compiler, $sourceDir, $tempDir)
{
$filesystem = new Filesystem();
$filesystem->mkdir([
$tempDir.'/lang',
$tempDir.'/region',
]);
$compiler->compile($sourceDir.'/lang', $tempDir.'/lang');
$compiler->compile($sourceDir.'/region', $tempDir.'/region');
}
/**
* {@inheritdoc}
*/
protected function preGenerate()
{
}
/**
* {@inheritdoc}
*/
protected function generateDataForLocale(BundleEntryReaderInterface $reader, $tempDir, $displayLocale)
{
// Don't generate aliases, as they are resolved during runtime
// Unless an alias is needed as fallback for de-duplication purposes
if (isset($this->localeAliases[$displayLocale]) && !$this->generatingFallback) {
return null;
}
// Generate locale names for all locales that have translations in
// at least the language or the region bundle
$displayFormat = $reader->readEntry($tempDir.'/lang', $displayLocale, ['localeDisplayPattern']);
$pattern = $displayFormat['pattern'] ?? '{0} ({1})';
$separator = $displayFormat['separator'] ?? '{0}, {1}';
$localeNames = [];
foreach ($this->locales as $locale) {
// Ensure a normalized list of pure locales
if (\Locale::getAllVariants($locale)) {
continue;
}
try {
// Generate a locale name 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.
$localeNames[$locale] = $this->generateLocaleName($reader, $tempDir, $locale, $displayLocale, $pattern, $separator);
} catch (MissingResourceException $e) {
// Silently ignore incomplete locale names
// In this case one should configure at least one fallback locale that is complete (e.g. English) during
// runtime. Alternatively a translation for the missing resource can be proposed upstream.
}
}
$data = [
'Names' => $localeNames,
];
// Don't de-duplicate a fallback locale
// Ensures the display locale can be de-duplicated on itself
if ($this->generatingFallback) {
return $data;
}
// Process again to de-duplicate locale and its fallback locales
// Only keep the differences
$fallbackData = $this->generateFallbackData($reader, $tempDir, $displayLocale);
if (isset($fallbackData['Names'])) {
$data['Names'] = array_diff($data['Names'], $fallbackData['Names']);
}
if (!$data['Names']) {
return null;
}
return $data;
}
/**
* {@inheritdoc}
*/
protected function generateDataForRoot(BundleEntryReaderInterface $reader, $tempDir)
{
}
/**
* {@inheritdoc}
*/
protected function generateDataForMeta(BundleEntryReaderInterface $reader, $tempDir)
{
return [
'Locales' => $this->locales,
'Aliases' => $this->localeAliases,
];
}
/**
* @return string
*/
private function generateLocaleName(BundleEntryReaderInterface $reader, $tempDir, $locale, $displayLocale, $pattern, $separator)
{
// Apply generic notation using square brackets as described per http://cldr.unicode.org/translation/language-names
$name = str_replace(['(', ')'], ['[', ']'], $reader->readEntry($tempDir.'/lang', $displayLocale, ['Languages', \Locale::getPrimaryLanguage($locale)]));
$extras = [];
// Discover the name of the script part of the locale
// i.e. in zh_Hans_MO, "Hans" is the script
if ($script = \Locale::getScript($locale)) {
$extras[] = str_replace(['(', ')'], ['[', ']'], $reader->readEntry($tempDir.'/lang', $displayLocale, ['Scripts', $script]));
}
// Discover the name of the region part of the locale
// i.e. in de_AT, "AT" is the region
if ($region = \Locale::getRegion($locale)) {
if (!RegionDataGenerator::isValidCountryCode($region)) {
throw new MissingResourceException(sprintf('Skipping "%s" due an invalid country.', $locale));
}
$extras[] = str_replace(['(', ')'], ['[', ']'], $reader->readEntry($tempDir.'/region', $displayLocale, ['Countries', $region]));
}
if ($extras) {
$extra = array_shift($extras);
foreach ($extras as $part) {
$extra = str_replace(['{0}', '{1}'], [$extra, $part], $separator);
}
$name = str_replace(['{0}', '{1}'], [$name, $extra], $pattern);
}
return $name;
}
}

View File

@@ -0,0 +1,149 @@
<?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\Data\Generator;
use Symfony\Component\Intl\Data\Bundle\Compiler\BundleCompilerInterface;
use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface;
use Symfony\Component\Intl\Data\Util\ArrayAccessibleResourceBundle;
use Symfony\Component\Intl\Data\Util\LocaleScanner;
/**
* The rule for compiling the region bundle.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @see http://source.icu-project.org/repos/icu/icu4j/trunk/main/classes/core/src/com/ibm/icu/util/Region.java
*
* @internal
*/
class RegionDataGenerator extends AbstractDataGenerator
{
private static $denylist = [
// Look like countries, but are sub-continents
'QO' => true, // Outlying Oceania
'EU' => true, // European Union
'EZ' => true, // Eurozone
'UN' => true, // United Nations
// Uninhabited islands
'BV' => true, // Bouvet Island
'HM' => true, // Heard & McDonald Islands
'CP' => true, // Clipperton Island
// Misc
'ZZ' => true, // Unknown Region
];
/**
* Collects all available language codes.
*
* @var string[]
*/
private $regionCodes = [];
public static function isValidCountryCode($region)
{
if (isset(self::$denylist[$region])) {
return false;
}
// WORLD/CONTINENT/SUBCONTINENT/GROUPING
if (ctype_digit($region) || \is_int($region)) {
return false;
}
return true;
}
/**
* {@inheritdoc}
*/
protected function scanLocales(LocaleScanner $scanner, $sourceDir)
{
return $scanner->scanLocales($sourceDir.'/region');
}
/**
* {@inheritdoc}
*/
protected function compileTemporaryBundles(BundleCompilerInterface $compiler, $sourceDir, $tempDir)
{
$compiler->compile($sourceDir.'/region', $tempDir);
}
/**
* {@inheritdoc}
*/
protected function preGenerate()
{
$this->regionCodes = [];
}
/**
* {@inheritdoc}
*/
protected function generateDataForLocale(BundleEntryReaderInterface $reader, $tempDir, $displayLocale)
{
$localeBundle = $reader->read($tempDir, $displayLocale);
// isset() on \ResourceBundle returns true even if the value is null
if (isset($localeBundle['Countries']) && null !== $localeBundle['Countries']) {
$data = [
'Names' => $this->generateRegionNames($localeBundle),
];
$this->regionCodes = array_merge($this->regionCodes, array_keys($data['Names']));
return $data;
}
return null;
}
/**
* {@inheritdoc}
*/
protected function generateDataForRoot(BundleEntryReaderInterface $reader, $tempDir)
{
}
/**
* {@inheritdoc}
*/
protected function generateDataForMeta(BundleEntryReaderInterface $reader, $tempDir)
{
$this->regionCodes = array_unique($this->regionCodes);
sort($this->regionCodes);
return [
'Regions' => $this->regionCodes,
];
}
/**
* @return array
*/
protected function generateRegionNames(ArrayAccessibleResourceBundle $localeBundle)
{
$unfilteredRegionNames = iterator_to_array($localeBundle['Countries']);
$regionNames = [];
foreach ($unfilteredRegionNames as $region => $regionName) {
if (!self::isValidCountryCode($region)) {
continue;
}
$regionNames[$region] = $regionName;
}
return $regionNames;
}
}

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\Data\Generator;
use Symfony\Component\Intl\Data\Bundle\Compiler\BundleCompilerInterface;
use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface;
use Symfony\Component\Intl\Data\Util\LocaleScanner;
/**
* The rule for compiling the script bundle.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class ScriptDataGenerator extends AbstractDataGenerator
{
/**
* Collects all available language codes.
*
* @var string[]
*/
private $scriptCodes = [];
/**
* {@inheritdoc}
*/
protected function scanLocales(LocaleScanner $scanner, $sourceDir)
{
return $scanner->scanLocales($sourceDir.'/lang');
}
/**
* {@inheritdoc}
*/
protected function compileTemporaryBundles(BundleCompilerInterface $compiler, $sourceDir, $tempDir)
{
$compiler->compile($sourceDir.'/lang', $tempDir);
}
/**
* {@inheritdoc}
*/
protected function preGenerate()
{
$this->scriptCodes = [];
}
/**
* {@inheritdoc}
*/
protected function generateDataForLocale(BundleEntryReaderInterface $reader, $tempDir, $displayLocale)
{
$localeBundle = $reader->read($tempDir, $displayLocale);
// isset() on \ResourceBundle returns true even if the value is null
if (isset($localeBundle['Scripts']) && null !== $localeBundle['Scripts']) {
$data = [
'Names' => iterator_to_array($localeBundle['Scripts']),
];
$this->scriptCodes = array_merge($this->scriptCodes, array_keys($data['Names']));
return $data;
}
return null;
}
/**
* {@inheritdoc}
*/
protected function generateDataForRoot(BundleEntryReaderInterface $reader, $tempDir)
{
}
/**
* {@inheritdoc}
*/
protected function generateDataForMeta(BundleEntryReaderInterface $reader, $tempDir)
{
$this->scriptCodes = array_unique($this->scriptCodes);
sort($this->scriptCodes);
return [
'Scripts' => $this->scriptCodes,
];
}
}

View File

@@ -0,0 +1,141 @@
<?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\Data\Provider;
use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface;
use Symfony\Component\Intl\Exception\MissingResourceException;
/**
* Data provider for currency-related data.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class CurrencyDataProvider
{
const INDEX_SYMBOL = 0;
const INDEX_NAME = 1;
const INDEX_FRACTION_DIGITS = 0;
const INDEX_ROUNDING_INCREMENT = 1;
private $path;
private $reader;
/**
* Creates a data provider that reads currency-related data from a
* resource bundle.
*
* @param string $path The path to the resource bundle
* @param BundleEntryReaderInterface $reader The reader for reading the resource bundle
*/
public function __construct($path, BundleEntryReaderInterface $reader)
{
$this->path = $path;
$this->reader = $reader;
}
public function getCurrencies()
{
return $this->reader->readEntry($this->path, 'meta', ['Currencies']);
}
public function getSymbol($currency, $displayLocale = null)
{
if (null === $displayLocale) {
$displayLocale = \Locale::getDefault();
}
return $this->reader->readEntry($this->path, $displayLocale, ['Names', $currency, static::INDEX_SYMBOL]);
}
public function getName($currency, $displayLocale = null)
{
if (null === $displayLocale) {
$displayLocale = \Locale::getDefault();
}
return $this->reader->readEntry($this->path, $displayLocale, ['Names', $currency, static::INDEX_NAME]);
}
public function getNames($displayLocale = null)
{
if (null === $displayLocale) {
$displayLocale = \Locale::getDefault();
}
// ====================================================================
// For reference: It is NOT possible to return names indexed by
// numeric code here, because some numeric codes map to multiple
// 3-letter codes (e.g. 32 => "ARA", "ARP", "ARS")
// ====================================================================
$names = $this->reader->readEntry($this->path, $displayLocale, ['Names']);
if ($names instanceof \Traversable) {
$names = iterator_to_array($names);
}
$index = static::INDEX_NAME;
array_walk($names, function (&$value) use ($index) {
$value = $value[$index];
});
// Sorting by value cannot be done during bundle generation, because
// binary bundles are always sorted by keys
$collator = new \Collator($displayLocale);
$collator->asort($names);
return $names;
}
/**
* Data provider for {@link \Symfony\Component\Intl\Currency::getFractionDigits()}.
*/
public function getFractionDigits($currency)
{
try {
return $this->reader->readEntry($this->path, 'meta', ['Meta', $currency, static::INDEX_FRACTION_DIGITS]);
} catch (MissingResourceException $e) {
return $this->reader->readEntry($this->path, 'meta', ['Meta', 'DEFAULT', static::INDEX_FRACTION_DIGITS]);
}
}
/**
* Data provider for {@link \Symfony\Component\Intl\Currency::getRoundingIncrement()}.
*/
public function getRoundingIncrement($currency)
{
try {
return $this->reader->readEntry($this->path, 'meta', ['Meta', $currency, static::INDEX_ROUNDING_INCREMENT]);
} catch (MissingResourceException $e) {
return $this->reader->readEntry($this->path, 'meta', ['Meta', 'DEFAULT', static::INDEX_ROUNDING_INCREMENT]);
}
}
/**
* Data provider for {@link \Symfony\Component\Intl\Currency::getNumericCode()}.
*/
public function getNumericCode($currency)
{
return $this->reader->readEntry($this->path, 'meta', ['Alpha3ToNumeric', $currency]);
}
/**
* Data provider for {@link \Symfony\Component\Intl\Currency::forNumericCode()}.
*/
public function forNumericCode($numericCode)
{
return $this->reader->readEntry($this->path, 'meta', ['NumericToAlpha3', (string) $numericCode]);
}
}

View File

@@ -0,0 +1,81 @@
<?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\Data\Provider;
use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface;
/**
* Data provider for language-related ICU data.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class LanguageDataProvider
{
private $path;
private $reader;
/**
* Creates a data provider that reads locale-related data from .res files.
*
* @param string $path The path to the directory containing the .res files
* @param BundleEntryReaderInterface $reader The reader for reading the .res files
*/
public function __construct($path, BundleEntryReaderInterface $reader)
{
$this->path = $path;
$this->reader = $reader;
}
public function getLanguages()
{
return $this->reader->readEntry($this->path, 'meta', ['Languages']);
}
public function getAliases()
{
return $this->reader->readEntry($this->path, 'root', ['Aliases']);
}
public function getName($language, $displayLocale = null)
{
if (null === $displayLocale) {
$displayLocale = \Locale::getDefault();
}
return $this->reader->readEntry($this->path, $displayLocale, ['Names', $language]);
}
public function getNames($displayLocale = null)
{
if (null === $displayLocale) {
$displayLocale = \Locale::getDefault();
}
$languages = $this->reader->readEntry($this->path, $displayLocale, ['Names']);
if ($languages instanceof \Traversable) {
$languages = iterator_to_array($languages);
}
$collator = new \Collator($displayLocale);
$collator->asort($languages);
return $languages;
}
public function getAlpha3Code($language)
{
return $this->reader->readEntry($this->path, 'meta', ['Alpha2ToAlpha3', $language]);
}
}

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\Data\Provider;
use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface;
/**
* Data provider for locale-related ICU data.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class LocaleDataProvider
{
private $path;
private $reader;
/**
* Creates a data provider that reads locale-related data from .res files.
*
* @param string $path The path to the directory containing the .res files
* @param BundleEntryReaderInterface $reader The reader for reading the .res files
*/
public function __construct($path, BundleEntryReaderInterface $reader)
{
$this->path = $path;
$this->reader = $reader;
}
public function getLocales()
{
return $this->reader->readEntry($this->path, 'meta', ['Locales']);
}
public function getAliases()
{
$aliases = $this->reader->readEntry($this->path, 'meta', ['Aliases']);
if ($aliases instanceof \Traversable) {
$aliases = iterator_to_array($aliases);
}
return $aliases;
}
public function getName($locale, $displayLocale = null)
{
if (null === $displayLocale) {
$displayLocale = \Locale::getDefault();
}
return $this->reader->readEntry($this->path, $displayLocale, ['Names', $locale]);
}
public function getNames($displayLocale = null)
{
if (null === $displayLocale) {
$displayLocale = \Locale::getDefault();
}
$names = $this->reader->readEntry($this->path, $displayLocale, ['Names']);
if ($names instanceof \Traversable) {
$names = iterator_to_array($names);
}
$collator = new \Collator($displayLocale);
$collator->asort($names);
return $names;
}
}

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\Data\Provider;
use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface;
/**
* Data provider for region-related ICU data.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class RegionDataProvider
{
private $path;
private $reader;
/**
* Creates a data provider that reads locale-related data from .res files.
*
* @param string $path The path to the directory containing the .res files
* @param BundleEntryReaderInterface $reader The reader for reading the .res files
*/
public function __construct($path, BundleEntryReaderInterface $reader)
{
$this->path = $path;
$this->reader = $reader;
}
public function getRegions()
{
return $this->reader->readEntry($this->path, 'meta', ['Regions']);
}
public function getName($region, $displayLocale = null)
{
if (null === $displayLocale) {
$displayLocale = \Locale::getDefault();
}
return $this->reader->readEntry($this->path, $displayLocale, ['Names', $region]);
}
public function getNames($displayLocale = null)
{
if (null === $displayLocale) {
$displayLocale = \Locale::getDefault();
}
$names = $this->reader->readEntry($this->path, $displayLocale, ['Names']);
if ($names instanceof \Traversable) {
$names = iterator_to_array($names);
}
$collator = new \Collator($displayLocale);
$collator->asort($names);
return $names;
}
}

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\Data\Provider;
use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface;
/**
* Data provider for script-related ICU data.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class ScriptDataProvider
{
private $path;
private $reader;
/**
* Creates a data provider that reads locale-related data from .res files.
*
* @param string $path The path to the directory containing the .res files
* @param BundleEntryReaderInterface $reader The reader for reading the .res files
*/
public function __construct($path, BundleEntryReaderInterface $reader)
{
$this->path = $path;
$this->reader = $reader;
}
public function getScripts()
{
return $this->reader->readEntry($this->path, 'meta', ['Scripts']);
}
public function getName($script, $displayLocale = null)
{
if (null === $displayLocale) {
$displayLocale = \Locale::getDefault();
}
return $this->reader->readEntry($this->path, $displayLocale, ['Names', $script]);
}
public function getNames($displayLocale = null)
{
if (null === $displayLocale) {
$displayLocale = \Locale::getDefault();
}
$names = $this->reader->readEntry($this->path, $displayLocale, ['Names']);
if ($names instanceof \Traversable) {
$names = iterator_to_array($names);
}
$collator = new \Collator($displayLocale);
$collator->asort($names);
return $names;
}
}

View File

@@ -0,0 +1,81 @@
<?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\Data\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/64356.
* This class can be removed once that bug is fixed.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class ArrayAccessibleResourceBundle implements \ArrayAccess, \IteratorAggregate, \Countable
{
private $bundleImpl;
public function __construct(\ResourceBundle $bundleImpl)
{
$this->bundleImpl = $bundleImpl;
}
public function get($offset)
{
$value = $this->bundleImpl->get($offset);
return $value instanceof \ResourceBundle ? new static($value) : $value;
}
public function offsetExists($offset)
{
return null !== $this->bundleImpl->get($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,85 @@
<?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\Data\Util;
/**
* Scans a directory with data files for locales.
*
* The name of each file with the extension ".txt" is considered, if it "looks"
* like a locale:
*
* - the name must start with two letters;
* - the two letters may optionally be followed by an underscore and any
* sequence of other symbols.
*
* For example, "de" and "de_DE" are considered to be locales. "root" and "meta"
* are not.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class LocaleScanner
{
/**
* Returns all locales found in the given directory.
*
* @param string $sourceDir The directory with ICU files
*
* @return array An array of locales. The result also contains locales that
* are in fact just aliases for other locales. Use
* {@link scanAliases()} to determine which of the locales
* are aliases
*/
public function scanLocales($sourceDir)
{
$locales = glob($sourceDir.'/*.txt', \GLOB_NOSORT);
// Remove file extension and sort
array_walk($locales, function (&$locale) { $locale = basename($locale, '.txt'); });
// Remove non-locales
$locales = array_filter($locales, function ($locale) {
return preg_match('/^[a-z]{2}(_.+)?$/', $locale);
});
sort($locales);
return $locales;
}
/**
* Returns all locale aliases found in the given directory.
*
* @param string $sourceDir The directory with ICU files
*
* @return array An array with the locale aliases as keys and the aliased
* locales as values
*/
public function scanAliases($sourceDir)
{
$locales = $this->scanLocales($sourceDir);
$aliases = [];
// Delete locales that are no aliases
foreach ($locales as $locale) {
$content = file_get_contents($sourceDir.'/'.$locale.'.txt');
// Aliases contain the text "%%ALIAS" followed by the aliased locale
if (preg_match('/"%%ALIAS"\{"([^"]+)"\}/', $content, $matches)) {
$aliases[$locale] = $matches[1];
}
}
return $aliases;
}
}

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\Data\Util;
use Symfony\Component\Intl\Exception\OutOfBoundsException;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class RecursiveArrayAccess
{
public static function get($array, array $indices)
{
foreach ($indices as $index) {
// Use array_key_exists() for arrays, isset() otherwise
if (\is_array($array)) {
if (\array_key_exists($index, $array)) {
$array = $array[$index];
continue;
}
} elseif ($array instanceof \ArrayAccess) {
if (isset($array[$index])) {
$array = $array[$index];
continue;
}
}
throw new OutOfBoundsException(sprintf('The index "%s" does not exist.', $index));
}
return $array;
}
private function __construct()
{
}
}

View File

@@ -0,0 +1,87 @@
<?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\Data\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>
*
* @internal
*/
class RingBuffer implements \ArrayAccess
{
private $values = [];
private $indices = [];
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,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 AM/PM markers format.
*
* @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/
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 [
'marker' => $matched,
];
}
}

View File

@@ -0,0 +1,65 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\DateFormatter\DateFormat;
/**
* Parser and formatter for day of week format.
*
* @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/
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];
case 6:
return substr($dayOfWeek, 0, 2);
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]';
case 6:
return 'Mo|Tu|We|Th|Fr|Sa|Su';
default:
return 'Mon|Tue|Wed|Thu|Fri|Sat|Sun';
}
}
/**
* {@inheritdoc}
*/
public function extractDateOptions($matched, $length)
{
return [];
}
}

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 day of year format.
*
* @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/
class DayOfYearTransformer extends Transformer
{
/**
* {@inheritdoc}
*/
public function format(\DateTime $dateTime, $length)
{
$dayOfYear = (int) $dateTime->format('z') + 1;
return $this->padLeft($dayOfYear, $length);
}
/**
* {@inheritdoc}
*/
public function getReverseMatchingRegExp($length)
{
return '\d{'.$length.'}';
}
/**
* {@inheritdoc}
*/
public function extractDateOptions($matched, $length)
{
return [];
}
}

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 day format.
*
* @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/
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{1,'.$length.'}';
}
/**
* {@inheritdoc}
*/
public function extractDateOptions($matched, $length)
{
return [
'day' => (int) $matched,
];
}
}

View File

@@ -0,0 +1,351 @@
<?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\IntlGlobals;
/**
* Parser and formatter for date formats.
*
* @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/
class FullTransformer
{
private $quoteMatch = "'(?:[^']+|'')*'";
private $implementedChars = 'MLydQqhDEaHkKmsz';
private $notImplementedChars = 'GYuwWFgecSAZvVW';
private $regExp;
/**
* @var Transformer[]
*/
private $transformers;
private $pattern;
private $timezone;
/**
* @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 = [
'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)
{
$formatted = preg_replace_callback($this->regExp, function ($matches) use ($dateTime) {
return $this->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|null 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));
}
return null;
}
/**
* 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|false 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 = [];
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
IntlGlobals::setError(IntlGlobals::U_ZERO_ERROR);
return $this->calculateUnixTimestamp($dateTime, $options);
}
// behave like the intl extension
IntlGlobals::setError(IntlGlobals::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)
{
$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) {
$length = \strlen($matches[0]);
$transformerIndex = $matches[0][0];
$dateChars = $matches[0];
if ($this->isQuoteMatch($dateChars)) {
return $this->replaceQuoteMatch($dateChars);
}
$transformers = $this->getTransformers();
if (isset($transformers[$transformerIndex])) {
$transformer = $transformers[$transformerIndex];
$captureName = str_repeat($transformerIndex, $length);
return "(?P<$captureName>".$transformer->getReverseMatchingRegExp($length).')';
}
return null;
}, $escapedPattern);
return $reverseMatchingRegExp;
}
/**
* Check if the first char of a string is a single quote.
*
* @param string $quoteMatch The string to check
*
* @return bool 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.
*
* @return array
*/
protected function normalizeArray(array $data)
{
$ret = [];
foreach ($data as $key => $value) {
if (!\is_string($key)) {
continue;
}
$ret[$key[0]] = [
'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 bool|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) {
IntlGlobals::setError(IntlGlobals::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 > (int) $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.
*
* @return array
*/
private function getDefaultValueForOptions(array $options)
{
return [
'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,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 12 hour format (0-11).
*
* @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/
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 [
'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 12 hour format (1-12).
*
* @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/
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 [
'hour' => (int) $matched,
'hourInstance' => $this,
];
}
}

View File

@@ -0,0 +1,65 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\DateFormatter\DateFormat;
/**
* Parser and formatter for 24 hour format (0-23).
*
* @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/
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)
{
$marker = (string) $marker;
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 [
'hour' => (int) $matched,
'hourInstance' => $this,
];
}
}

View File

@@ -0,0 +1,66 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\DateFormatter\DateFormat;
/**
* Parser and formatter for 24 hour format (1-24).
*
* @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/
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 [
'hour' => (int) $matched,
'hourInstance' => $this,
];
}
}

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\DateFormatter\DateFormat;
/**
* Base class for hour transformers.
*
* @author Eriksen Costa <eriksen.costa@infranology.com.br>
*
* @internal
*/
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,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 minute format.
*
* @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/
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 [
'minute' => (int) $matched,
];
}
}

View File

@@ -0,0 +1,136 @@
<?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>
*
* @internal
*/
class MonthTransformer extends Transformer
{
protected static $months = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
];
/**
* Short months names (first 3 letters).
*/
protected static $shortMonths = [];
/**
* Flipped $months array, $name => $index.
*/
protected static $flippedMonths = [];
/**
* Flipped $shortMonths array, $name => $index.
*/
protected static $flippedShortMonths = [];
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 = [
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{1,'.$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 [
'month' => $matched,
];
}
}

View File

@@ -0,0 +1,66 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\DateFormatter\DateFormat;
/**
* Parser and formatter for quarter format.
*
* @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/
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 = [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 [];
}
}

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 the second format.
*
* @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/
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 [
'second' => (int) $matched,
];
}
}

View File

@@ -0,0 +1,116 @@
<?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>
*
* @internal
*/
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, ['Etc', 'UTC', 'GMT'])) {
throw new NotImplementedException('Time zone different than GMT or UTC is not supported as a formatting output.');
}
if ('Etc' === $timeZone) {
// i.e. Etc/GMT+1, Etc/UTC, Etc/Zulu
$timeZone = substr($dateTime->getTimezone()->getName(), 4);
}
// From ICU >= 59.1 GMT and UTC are no longer unified
if (\in_array($timeZone, ['UTC', 'UCT', 'Universal', 'Zulu'])) {
// offset is not supported with UTC
return $length > 3 ? 'Coordinated Universal Time' : 'UTC';
}
$offset = (int) $dateTime->format('O');
// From ICU >= 4.8, the zero offset is no more used, example: GMT instead of GMT+00:00
if (0 === $offset) {
return $length > 3 ? 'Greenwich Mean Time' : 'GMT';
}
if ($length > 3) {
return $dateTime->format('\G\M\TP');
}
return sprintf('GMT%s%d', ($offset >= 0 ? '+' : ''), $offset / 100);
}
/**
* {@inheritdoc}
*/
public function getReverseMatchingRegExp($length)
{
return 'GMT[+-]\d{2}:?\d{2}';
}
/**
* {@inheritdoc}
*/
public function extractDateOptions($matched, $length)
{
return [
'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 https://php.net/timezones.others
*
* @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'.(0 !== $hours ? $signal.$hours : '');
}
throw new \InvalidArgumentException(sprintf('The GMT time zone "%s" does not match with the supported formats GMT[+-]HH:MM or GMT[+-]HHMM.', $formattedTimeZone));
}
}

View File

@@ -0,0 +1,65 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Intl\DateFormatter\DateFormat;
/**
* Parser and formatter for date formats.
*
* @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/
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,52 @@
<?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>
*
* @internal
*/
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{1,4}';
}
/**
* {@inheritdoc}
*/
public function extractDateOptions($matched, $length)
{
return [
'year' => (int) $matched,
];
}
}

View File

@@ -0,0 +1,613 @@
<?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\DateFormatter\DateFormat\FullTransformer;
use Symfony\Component\Intl\Exception\MethodArgumentNotImplementedException;
use Symfony\Component\Intl\Exception\MethodArgumentValueNotImplementedException;
use Symfony\Component\Intl\Exception\MethodNotImplementedException;
use Symfony\Component\Intl\Globals\IntlGlobals;
use Symfony\Component\Intl\Locale\Locale;
/**
* Replacement for PHP's native {@link \IntlDateFormatter} class.
*
* The only methods currently supported in this class are:
*
* - {@link __construct}
* - {@link create}
* - {@link format}
* - {@link getCalendar}
* - {@link getDateType}
* - {@link getErrorCode}
* - {@link getErrorMessage}
* - {@link getLocale}
* - {@link getPattern}
* - {@link getTimeType}
* - {@link getTimeZoneId}
* - {@link isLenient}
* - {@link parse}
* - {@link setLenient}
* - {@link setPattern}
* - {@link setTimeZoneId}
* - {@link setTimeZone}
*
* @author Igor Wiedler <igor@wiedler.ch>
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class IntlDateFormatter
{
/**
* The error code from the last operation.
*
* @var int
*/
protected $errorCode = IntlGlobals::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.
*/
private $defaultDateFormats = [
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.
*/
private $defaultTimeFormats = [
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',
];
private $datetype;
private $timetype;
/**
* @var string
*/
private $pattern;
/**
* @var \DateTimeZone
*/
private $dateTimeZone;
/**
* @var bool
*/
private $uninitializedTimeZoneId = false;
/**
* @var string
*/
private $timeZoneId;
/**
* @param string|null $locale The locale code. The only currently supported locale is "en" (or null using the default locale, i.e. "en")
* @param int|null $datetype Type of date formatting, one of the format type constants
* @param int|null $timetype Type of time formatting, one of the format type constants
* @param \IntlTimeZone|\DateTimeZone|string|null $timezone Timezone identifier
* @param int $calendar Calendar to use for formatting or parsing. The only currently
* supported value is IntlDateFormatter::GREGORIAN (or null using the default calendar, i.e. "GREGORIAN")
* @param string|null $pattern Optional pattern to use when formatting
*
* @see https://php.net/intldateformatter.create
* @see http://userguide.icu-project.org/formatparse/datetime
*
* @throws MethodArgumentValueNotImplementedException When $locale different than "en" or null 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 && null !== $locale) {
throw new MethodArgumentValueNotImplementedException(__METHOD__, 'locale', $locale, 'Only the locale "en" is supported');
}
if (self::GREGORIAN !== $calendar && null !== $calendar) {
throw new MethodArgumentValueNotImplementedException(__METHOD__, 'calendar', $calendar, 'Only the GREGORIAN calendar is supported');
}
$this->datetype = null !== $datetype ? $datetype : self::FULL;
$this->timetype = null !== $timetype ? $timetype : self::FULL;
$this->setPattern($pattern);
$this->setTimeZone($timezone);
}
/**
* Static constructor.
*
* @param string|null $locale The locale code. The only currently supported locale is "en" (or null using the default locale, i.e. "en")
* @param int|null $datetype Type of date formatting, one of the format type constants
* @param int|null $timetype Type of time formatting, one of the format type constants
* @param \IntlTimeZone|\DateTimeZone|string|null $timezone Timezone identifier
* @param int $calendar Calendar to use for formatting or parsing; default is Gregorian
* One of the calendar constants
* @param string|null $pattern Optional pattern to use when formatting
*
* @return self
*
* @see https://php.net/intldateformatter.create
* @see http://userguide.icu-project.org/formatparse/datetime
*
* @throws MethodArgumentValueNotImplementedException When $locale different than "en" or null is passed
* @throws MethodArgumentValueNotImplementedException When $calendar different than GREGORIAN 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 int|\DateTimeInterface $timestamp The timestamp to format
*
* @return string|bool The formatted value or false if formatting failed
*
* @see https://php.net/intldateformatter.format
*
* @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 = 'Only integer Unix timestamps and DateTime objects are supported';
throw new MethodArgumentValueNotImplementedException(__METHOD__, 'timestamp', $timestamp, $message);
}
// behave like the intl extension
$argumentError = null;
if (!\is_int($timestamp) && !$timestamp instanceof \DateTimeInterface) {
$argumentError = sprintf('datefmt_format: string \'%s\' is not numeric, which would be required for it to be a valid date', $timestamp);
}
if (null !== $argumentError) {
IntlGlobals::setError(IntlGlobals::U_ILLEGAL_ARGUMENT_ERROR, $argumentError);
$this->errorCode = IntlGlobals::getErrorCode();
$this->errorMessage = IntlGlobals::getErrorMessage();
return false;
}
if ($timestamp instanceof \DateTimeInterface) {
$timestamp = $timestamp->getTimestamp();
}
$transformer = new FullTransformer($this->getPattern(), $this->getTimeZoneId());
$formatted = $transformer->format($this->createDateTime($timestamp));
// behave like the intl extension
IntlGlobals::setError(IntlGlobals::U_ZERO_ERROR);
$this->errorCode = IntlGlobals::getErrorCode();
$this->errorMessage = IntlGlobals::getErrorMessage();
return $formatted;
}
/**
* Not supported. Formats an object.
*
* @param object $object
* @param mixed $format
* @param string $locale
*
* @return string The formatted value
*
* @see https://php.net/intldateformatter.formatobject
*
* @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. Currently always returns
* IntlDateFormatter::GREGORIAN.
*
* @see https://php.net/intldateformatter.getcalendar
*/
public function getCalendar()
{
return self::GREGORIAN;
}
/**
* Not supported. Returns the formatter's calendar object.
*
* @return object The calendar's object being used by the formatter
*
* @see https://php.net/intldateformatter.getcalendarobject
*
* @throws MethodNotImplementedException
*/
public function getCalendarObject()
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Returns the formatter's datetype.
*
* @return int The current value of the formatter
*
* @see https://php.net/intldateformatter.getdatetype
*/
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 https://php.net/intldateformatter.geterrorcode
*/
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 https://php.net/intldateformatter.geterrormessage
*/
public function getErrorMessage()
{
return $this->errorMessage;
}
/**
* Returns the formatter's locale.
*
* @param int $type Not supported. The locale name type to return (Locale::VALID_LOCALE or Locale::ACTUAL_LOCALE)
*
* @return string The locale used to create the formatter. Currently always
* returns "en".
*
* @see https://php.net/intldateformatter.getlocale
*/
public function getLocale($type = Locale::ACTUAL_LOCALE)
{
return 'en';
}
/**
* Returns the formatter's pattern.
*
* @return string The pattern string used by the formatter
*
* @see https://php.net/intldateformatter.getpattern
*/
public function getPattern()
{
return $this->pattern;
}
/**
* Returns the formatter's time type.
*
* @return int The time type used by the formatter
*
* @see https://php.net/intldateformatter.gettimetype
*/
public function getTimeType()
{
return $this->timetype;
}
/**
* Returns the formatter's timezone identifier.
*
* @return string The timezone identifier used by the formatter
*
* @see https://php.net/intldateformatter.gettimezoneid
*/
public function getTimeZoneId()
{
if (!$this->uninitializedTimeZoneId) {
return $this->timeZoneId;
}
return date_default_timezone_get();
}
/**
* Not supported. Returns the formatter's timezone.
*
* @return mixed The timezone used by the formatter
*
* @see https://php.net/intldateformatter.gettimezone
*
* @throws MethodNotImplementedException
*/
public function getTimeZone()
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Returns whether the formatter is lenient.
*
* @return bool Currently always returns false
*
* @see https://php.net/intldateformatter.islenient
*
* @throws MethodNotImplementedException
*/
public function isLenient()
{
return false;
}
/**
* Not supported. 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 https://php.net/intldateformatter.localtime
*
* @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 Not supported. 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 int|false Parsed value as a timestamp
*
* @see https://php.net/intldateformatter.parse
*
* @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 = IntlGlobals::getErrorCode();
$this->errorMessage = IntlGlobals::getErrorMessage();
return $timestamp;
}
/**
* Not supported. Set the formatter's calendar.
*
* @param string $calendar The calendar to use. Default is IntlDateFormatter::GREGORIAN
*
* @return bool true on success or false on failure
*
* @see https://php.net/intldateformatter.setcalendar
*
* @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 bool $lenient Sets whether the parser is lenient or not. Currently
* only false (strict) is supported.
*
* @return bool true on success or false on failure
*
* @see https://php.net/intldateformatter.setlenient
*
* @throws MethodArgumentValueNotImplementedException When $lenient is true
*/
public function setLenient($lenient)
{
if ($lenient) {
throw new MethodArgumentValueNotImplementedException(__METHOD__, 'lenient', $lenient, 'Only the strict parser is supported');
}
return true;
}
/**
* Set the formatter's pattern.
*
* @param string|null $pattern A pattern string in conformance with the ICU IntlDateFormatter documentation
*
* @return bool true on success or false on failure
*
* @see https://php.net/intldateformatter.setpattern
* @see http://userguide.icu-project.org/formatparse/datetime
*/
public function setPattern($pattern)
{
if (null === $pattern) {
$pattern = $this->getDefaultPattern();
}
$this->pattern = $pattern;
return true;
}
/**
* Set the formatter's timezone identifier.
*
* @param string|null $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 bool true on success or false on failure
*
* @see https://php.net/intldateformatter.settimezoneid
*/
public function setTimeZoneId($timeZoneId)
{
if (null === $timeZoneId) {
$timeZoneId = date_default_timezone_get();
$this->uninitializedTimeZoneId = 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);
if ('GMT' !== $timeZoneId && $this->dateTimeZone->getName() !== $timeZoneId) {
$timeZone = $this->getTimeZoneId();
}
} catch (\Exception $e) {
$timeZoneId = $timeZone = $this->getTimeZoneId();
$this->dateTimeZone = new \DateTimeZone($timeZoneId);
}
$this->timeZoneId = $timeZone;
return true;
}
/**
* This method was added in PHP 5.5 as replacement for `setTimeZoneId()`.
*
* @param \IntlTimeZone|\DateTimeZone|string|null $timeZone
*
* @return bool true on success or false on failure
*
* @see https://php.net/intldateformatter.settimezone
*/
public function setTimeZone($timeZone)
{
if ($timeZone instanceof \IntlTimeZone) {
$timeZone = $timeZone->getID();
}
if ($timeZone instanceof \DateTimeZone) {
$timeZone = $timeZone->getName();
// DateTimeZone returns the GMT offset timezones without the leading GMT, while our parsing requires it.
if (!empty($timeZone) && ('+' === $timeZone[0] || '-' === $timeZone[0])) {
$timeZone = 'GMT'.$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 = [];
if (self::NONE !== $this->datetype) {
$patternParts[] = $this->defaultDateFormats[$this->datetype];
}
if (self::NONE !== $this->timetype) {
$patternParts[] = $this->defaultTimeFormats[$this->timetype];
}
return implode(', ', $patternParts);
}
}

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,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 MethodArgumentNotImplementedException extends NotImplementedException
{
/**
* @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,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\Exception;
/**
* @author Eriksen Costa <eriksen.costa@infranology.com.br>
*/
class MethodArgumentValueNotImplementedException extends NotImplementedException
{
/**
* @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,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\Exception;
/**
* @author Eriksen Costa <eriksen.costa@infranology.com.br>
*/
class MethodNotImplementedException extends NotImplementedException
{
/**
* @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,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;
/**
* Thrown when an invalid entry of a resource bundle was requested.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class MissingResourceException extends RuntimeException
{
}

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\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.';
/**
* @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,19 @@
<?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 Bernhard Schussek <bschussek@gmail.com>
*/
class ResourceBundleNotFoundException extends RuntimeException
{
}

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,25 @@
<?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;
/**
* Thrown when a method argument had an unexpected type.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class UnexpectedTypeException extends InvalidArgumentException
{
public function __construct($value, $expectedType)
{
parent::__construct(sprintf('Expected argument of type "%s", "%s" given', $expectedType, \is_object($value) ? \get_class($value) : \gettype($value)));
}
}

View File

@@ -0,0 +1,127 @@
<?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>
*
* @internal
*/
abstract class IntlGlobals
{
/**
* Indicates that no error occurred.
*/
const U_ZERO_ERROR = 0;
/**
* Indicates that an invalid argument was passed.
*/
const U_ILLEGAL_ARGUMENT_ERROR = 1;
/**
* Indicates that the parse() operation failed.
*/
const U_PARSE_ERROR = 9;
/**
* All known error codes.
*/
private static $errorCodes = [
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.
*/
private static $errorCode = self::U_ZERO_ERROR;
/**
* The error code of the last operation.
*/
private static $errorMessage = 'U_ZERO_ERROR';
/**
* Returns whether the error code indicates a failure.
*
* @param int $errorCode The error code returned by IntlGlobals::getErrorCode()
*
* @return bool
*/
public static function isFailure($errorCode)
{
return isset(self::$errorCodes[$errorCode])
&& $errorCode > self::U_ZERO_ERROR;
}
/**
* Returns the error code of the last operation.
*
* Returns IntlGlobals::U_ZERO_ERROR if no error occurred.
*
* @return int
*/
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 int $code The error code returned by IntlGlobals::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 int $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;
}
}

279
vendor/symfony/intl/Intl.php vendored Normal file
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.
*/
namespace Symfony\Component\Intl;
use Symfony\Component\Intl\Data\Bundle\Reader\BufferedBundleReader;
use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReader;
use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface;
use Symfony\Component\Intl\Data\Bundle\Reader\JsonBundleReader;
use Symfony\Component\Intl\Data\Provider\LocaleDataProvider;
use Symfony\Component\Intl\Data\Provider\ScriptDataProvider;
use Symfony\Component\Intl\ResourceBundle\CurrencyBundle;
use Symfony\Component\Intl\ResourceBundle\CurrencyBundleInterface;
use Symfony\Component\Intl\ResourceBundle\LanguageBundle;
use Symfony\Component\Intl\ResourceBundle\LanguageBundleInterface;
use Symfony\Component\Intl\ResourceBundle\LocaleBundle;
use Symfony\Component\Intl\ResourceBundle\LocaleBundleInterface;
use Symfony\Component\Intl\ResourceBundle\RegionBundle;
use Symfony\Component\Intl\ResourceBundle\RegionBundleInterface;
/**
* Gives access to internationalization data.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
final class Intl
{
/**
* 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 directory name of the currency data.
*/
const CURRENCY_DIR = 'currencies';
/**
* The directory name of the language data.
*/
const LANGUAGE_DIR = 'languages';
/**
* The directory name of the script data.
*/
const SCRIPT_DIR = 'scripts';
/**
* The directory name of the locale data.
*/
const LOCALE_DIR = 'locales';
/**
* The directory name of the region data.
*/
const REGION_DIR = 'regions';
/**
* @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|bool|null
*/
private static $icuVersion = false;
/**
* @var string
*/
private static $icuDataVersion = false;
/**
* @var BundleEntryReaderInterface
*/
private static $entryReader;
/**
* Returns whether the intl extension is installed.
*
* @return bool Returns true if the intl extension is installed, false otherwise
*/
public static function isExtensionLoaded()
{
return class_exists('\ResourceBundle');
}
/**
* Returns the bundle containing currency information.
*
* @return CurrencyBundleInterface The currency resource bundle
*/
public static function getCurrencyBundle()
{
if (null === self::$currencyBundle) {
self::$currencyBundle = new CurrencyBundle(
self::getDataDirectory().'/'.self::CURRENCY_DIR,
self::getEntryReader(),
self::getLocaleBundle()
);
}
return self::$currencyBundle;
}
/**
* Returns the bundle containing language information.
*
* @return LanguageBundleInterface The language resource bundle
*/
public static function getLanguageBundle()
{
if (null === self::$languageBundle) {
self::$languageBundle = new LanguageBundle(
self::getDataDirectory().'/'.self::LANGUAGE_DIR,
self::getEntryReader(),
self::getLocaleBundle(),
new ScriptDataProvider(
self::getDataDirectory().'/'.self::SCRIPT_DIR,
self::getEntryReader()
)
);
}
return self::$languageBundle;
}
/**
* Returns the bundle containing locale information.
*
* @return LocaleBundleInterface The locale resource bundle
*/
public static function getLocaleBundle()
{
if (null === self::$localeBundle) {
self::$localeBundle = new LocaleBundle(
self::getDataDirectory().'/'.self::LOCALE_DIR,
self::getEntryReader()
);
}
return self::$localeBundle;
}
/**
* Returns the bundle containing region information.
*
* @return RegionBundleInterface The region resource bundle
*/
public static function getRegionBundle()
{
if (null === self::$regionBundle) {
self::$regionBundle = new RegionBundle(
self::getDataDirectory().'/'.self::REGION_DIR,
self::getEntryReader(),
self::getLocaleBundle()
);
}
return self::$regionBundle;
}
/**
* Returns the version of the installed ICU library.
*
* @return string|null The ICU version or NULL if it could not be determined
*/
public static function getIcuVersion()
{
if (false === self::$icuVersion) {
if (!self::isExtensionLoaded()) {
self::$icuVersion = self::getIcuStubVersion();
} elseif (\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 = trim(file_get_contents(self::getDataDirectory().'/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 getIcuStubVersion()
{
return '67.1';
}
/**
* Returns the absolute path to the data directory.
*
* @return string The absolute path to the data directory
*/
public static function getDataDirectory()
{
return __DIR__.'/Resources/data';
}
/**
* Returns the cached bundle entry reader.
*
* @return BundleEntryReaderInterface The bundle entry reader
*/
private static function getEntryReader()
{
if (null === self::$entryReader) {
self::$entryReader = new BundleEntryReader(new BufferedBundleReader(
new JsonBundleReader(),
self::BUFFER_SIZE
));
$localeDataProvider = new LocaleDataProvider(
self::getDataDirectory().'/'.self::LOCALE_DIR,
self::$entryReader
);
self::$entryReader->setLocaleAliases($localeDataProvider->getAliases());
}
return self::$entryReader;
}
/**
* This class must not be instantiated.
*/
private function __construct()
{
}
}

19
vendor/symfony/intl/LICENSE vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2004-2020 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.

116
vendor/symfony/intl/Locale.php vendored Normal file
View File

@@ -0,0 +1,116 @@
<?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;
/**
* Provides access to locale-related data.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
final class Locale extends \Locale
{
/**
* @var string
*/
private static $defaultFallback = 'en';
/**
* Sets the default fallback locale.
*
* The default fallback locale is used as fallback for locales that have no
* fallback otherwise.
*
* @param string|null $locale The default fallback locale
*
* @see getFallback()
*/
public static function setDefaultFallback($locale)
{
self::$defaultFallback = $locale;
}
/**
* Returns the default fallback locale.
*
* @return string|null The default fallback locale
*
* @see setDefaultFallback()
* @see getFallback()
*/
public static function getDefaultFallback()
{
return self::$defaultFallback;
}
/**
* Returns the fallback locale for a given locale.
*
* For example, the fallback of "fr_FR" is "fr". The fallback of "fr" is
* the default fallback locale configured with {@link setDefaultFallback()}.
* The default fallback locale has no fallback.
*
* @param string $locale The ICU locale code to find the fallback for
*
* @return string|null The ICU locale code of the fallback locale, or null
* if no fallback exists
*/
public static function getFallback($locale)
{
if (\function_exists('locale_parse')) {
$localeSubTags = locale_parse($locale);
if (1 === \count($localeSubTags)) {
if ('root' !== self::$defaultFallback && self::$defaultFallback === $localeSubTags['language']) {
return 'root';
}
// Don't return default fallback for "root", "meta" or others
// Normal locales have two or three letters
if (\strlen($locale) < 4) {
return self::$defaultFallback;
}
return null;
}
array_pop($localeSubTags);
$fallback = locale_compose($localeSubTags);
return false !== $fallback ? $fallback : null;
}
if (false !== $pos = strrpos($locale, '_')) {
return substr($locale, 0, $pos);
}
if (false !== $pos = strrpos($locale, '-')) {
return substr($locale, 0, $pos);
}
if ('root' !== self::$defaultFallback && self::$defaultFallback === $locale) {
return 'root';
}
// Don't return default fallback for "root", "meta" or others
// Normal locales have two or three letters
return \strlen($locale) < 4 ? self::$defaultFallback : null;
}
/**
* This class must not be instantiated.
*/
private function __construct()
{
}
}

323
vendor/symfony/intl/Locale/Locale.php vendored Normal file
View File

@@ -0,0 +1,323 @@
<?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\Exception\MethodNotImplementedException;
/**
* Replacement for PHP's native {@link \Locale} class.
*
* The only method supported in this class is {@link getDefault}. This method
* will always return "en". All other methods will throw an exception when used.
*
* @author Eriksen Costa <eriksen.costa@infranology.com.br>
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class Locale
{
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';
/**
* Not supported. 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 https://php.net/locale.acceptfromhttp
*
* @throws MethodNotImplementedException
*/
public static function acceptFromHttp($header)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. 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 https://php.net/locale.composelocale
*
* @throws MethodNotImplementedException
*/
public static function composeLocale(array $subtags)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. 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 bool $canonicalize
*
* @return string The corresponding locale code
*
* @see https://php.net/locale.filtermatches
*
* @throws MethodNotImplementedException
*/
public static function filterMatches($langtag, $locale, $canonicalize = false)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Returns the variants for the input locale.
*
* @param string $locale The locale to extract the variants from
*
* @return array The locale variants
*
* @see https://php.net/locale.getallvariants
*
* @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 https://php.net/locale.getdefault
*/
public static function getDefault()
{
return 'en';
}
/**
* Not supported. 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 https://php.net/locale.getdisplaylanguage
*
* @throws MethodNotImplementedException
*/
public static function getDisplayLanguage($locale, $inLocale = null)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. 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 https://php.net/locale.getdisplayname
*
* @throws MethodNotImplementedException
*/
public static function getDisplayName($locale, $inLocale = null)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. 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 https://php.net/locale.getdisplayregion
*
* @throws MethodNotImplementedException
*/
public static function getDisplayRegion($locale, $inLocale = null)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. 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 https://php.net/locale.getdisplayscript
*
* @throws MethodNotImplementedException
*/
public static function getDisplayScript($locale, $inLocale = null)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. 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 https://php.net/locale.getdisplayvariant
*
* @throws MethodNotImplementedException
*/
public static function getDisplayVariant($locale, $inLocale = null)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. 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 https://php.net/locale.getkeywords
*
* @throws MethodNotImplementedException
*/
public static function getKeywords($locale)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. 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 https://php.net/locale.getprimarylanguage
*
* @throws MethodNotImplementedException
*/
public static function getPrimaryLanguage($locale)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. 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 https://php.net/locale.getregion
*
* @throws MethodNotImplementedException
*/
public static function getRegion($locale)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. 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 https://php.net/locale.getscript
*
* @throws MethodNotImplementedException
*/
public static function getScript($locale)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. 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 bool $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 https://php.net/locale.lookup
*
* @throws MethodNotImplementedException
*/
public static function lookup(array $langtag, $locale, $canonicalize = false, $default = null)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. 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 https://php.net/locale.parselocale
*
* @throws MethodNotImplementedException
*/
public static function parseLocale($locale)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Sets the default runtime locale.
*
* @param string $locale The locale code
*
* @return bool true on success or false on failure
*
* @see https://php.net/locale.setdefault
*
* @throws MethodNotImplementedException
*/
public static function setDefault($locale)
{
if ('en' !== $locale) {
throw new MethodNotImplementedException(__METHOD__);
}
return true;
}
}

View File

@@ -0,0 +1,905 @@
<?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\MethodArgumentNotImplementedException;
use Symfony\Component\Intl\Exception\MethodArgumentValueNotImplementedException;
use Symfony\Component\Intl\Exception\MethodNotImplementedException;
use Symfony\Component\Intl\Exception\NotImplementedException;
use Symfony\Component\Intl\Globals\IntlGlobals;
use Symfony\Component\Intl\Intl;
use Symfony\Component\Intl\Locale\Locale;
/**
* Replacement for PHP's native {@link \NumberFormatter} class.
*
* The only methods currently supported in this class are:
*
* - {@link __construct}
* - {@link create}
* - {@link formatCurrency}
* - {@link format}
* - {@link getAttribute}
* - {@link getErrorCode}
* - {@link getErrorMessage}
* - {@link getLocale}
* - {@link parse}
* - {@link setAttribute}
*
* @author Eriksen Costa <eriksen.costa@infranology.com.br>
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class NumberFormatter
{
/* 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 int
*/
protected $errorCode = IntlGlobals::U_ZERO_ERROR;
/**
* The error message from the last operation.
*
* @var string
*/
protected $errorMessage = 'U_ZERO_ERROR';
/**
* @var int
*/
private $style;
/**
* Default values for the en locale.
*/
private $attributes = [
self::FRACTION_DIGITS => 0,
self::GROUPING_USED => 1,
self::ROUNDING_MODE => self::ROUND_HALFEVEN,
];
/**
* Holds the initialized attributes code.
*/
private $initializedAttributes = [];
/**
* The supported styles to the constructor $styles argument.
*/
private static $supportedStyles = [
'CURRENCY' => self::CURRENCY,
'DECIMAL' => self::DECIMAL,
];
/**
* Supported attributes to the setAttribute() $attr argument.
*/
private static $supportedAttributes = [
'FRACTION_DIGITS' => self::FRACTION_DIGITS,
'GROUPING_USED' => self::GROUPING_USED,
'ROUNDING_MODE' => self::ROUNDING_MODE,
];
/**
* The available rounding modes for setAttribute() usage with
* NumberFormatter::ROUNDING_MODE. NumberFormatter::ROUND_DOWN
* and NumberFormatter::ROUND_UP does not have a PHP only equivalent.
*/
private static $roundingModes = [
'ROUND_HALFEVEN' => self::ROUND_HALFEVEN,
'ROUND_HALFDOWN' => self::ROUND_HALFDOWN,
'ROUND_HALFUP' => self::ROUND_HALFUP,
'ROUND_CEILING' => self::ROUND_CEILING,
'ROUND_FLOOR' => self::ROUND_FLOOR,
'ROUND_DOWN' => self::ROUND_DOWN,
'ROUND_UP' => self::ROUND_UP,
];
/**
* The mapping between NumberFormatter rounding modes to the available
* modes in PHP's round() function.
*
* @see https://php.net/round
*/
private static $phpRoundingMap = [
self::ROUND_HALFDOWN => \PHP_ROUND_HALF_DOWN,
self::ROUND_HALFEVEN => \PHP_ROUND_HALF_EVEN,
self::ROUND_HALFUP => \PHP_ROUND_HALF_UP,
];
/**
* The list of supported rounding modes which aren't available modes in
* PHP's round() function, but there's an equivalent. Keys are rounding
* modes, values does not matter.
*/
private static $customRoundingList = [
self::ROUND_CEILING => true,
self::ROUND_FLOOR => true,
self::ROUND_DOWN => true,
self::ROUND_UP => true,
];
/**
* The maximum value of the integer type in 32 bit platforms.
*/
private static $int32Max = 2147483647;
/**
* The maximum value of the integer type in 64 bit platforms.
*
* @var int|float
*/
private static $int64Max = 9223372036854775807;
private static $enSymbols = [
self::DECIMAL => ['.', ',', ';', '%', '0', '#', '-', '+', '¤', '¤¤', '.', 'E', '‰', '*', '∞', 'NaN', '@', ','],
self::CURRENCY => ['.', ',', ';', '%', '0', '#', '-', '+', '¤', '¤¤', '.', 'E', '‰', '*', '∞', 'NaN', '@', ','],
];
private static $enTextAttributes = [
self::DECIMAL => ['', '', '-', '', ' ', 'XXX', ''],
self::CURRENCY => ['¤', '', '-¤', '', ' ', 'XXX'],
];
/**
* @param string|null $locale The locale code. The only currently supported locale is "en" (or null using the default locale, i.e. "en")
* @param int $style Style of the formatting, one of the format style constants.
* The only supported styles are NumberFormatter::DECIMAL
* and NumberFormatter::CURRENCY.
* @param string $pattern Not supported. 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 https://php.net/numberformatter.create
* @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" or null 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 && null !== $locale) {
throw new MethodArgumentValueNotImplementedException(__METHOD__, 'locale', $locale, 'Only the locale "en" 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->style = null !== $style ? (int) $style : null;
}
/**
* Static constructor.
*
* @param string|null $locale The locale code. The only supported locale is "en" (or null using the default locale, i.e. "en")
* @param int $style Style of the formatting, one of the format style constants.
* The only currently supported styles are NumberFormatter::DECIMAL
* and NumberFormatter::CURRENCY.
* @param string $pattern Not supported. 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 self
*
* @see https://php.net/numberformatter.create
* @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" or null 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 https://php.net/numberformatter.formatcurrency
* @see https://en.wikipedia.org/wiki/ISO_4217#Active_codes
*/
public function formatCurrency($value, $currency)
{
if (self::DECIMAL === $this->style) {
return $this->format($value);
}
$symbol = Intl::getCurrencyBundle()->getCurrencySymbol($currency, 'en');
$fractionDigits = Intl::getCurrencyBundle()->getFractionDigits($currency);
$value = $this->roundCurrency($value, $currency);
$negative = false;
if (0 > $value) {
$negative = true;
$value *= -1;
}
$value = $this->formatNumber($value, $fractionDigits);
// There's a non-breaking space after the currency code (i.e. CRC 100), but not if the currency has a symbol (i.e. £100).
$ret = $symbol.(mb_strlen($symbol, 'UTF-8') > 2 ? "\xc2\xa0" : '').$value;
return $negative ? '-'.$ret : $ret;
}
/**
* Format a number.
*
* @param int|float $value The value to format
* @param int $type Type of the formatting, one of the format type constants.
* Only type NumberFormatter::TYPE_DEFAULT is currently supported.
*
* @return bool|string The formatted value or false on error
*
* @see https://php.net/numberformatter.format
*
* @throws NotImplementedException If the method is called with the class $style 'CURRENCY'
* @throws MethodArgumentValueNotImplementedException If the $type is different than TYPE_DEFAULT
*/
public function format($value, $type = self::TYPE_DEFAULT)
{
$type = (int) $type;
// The original NumberFormatter does not support this format type
if (self::TYPE_CURRENCY === $type) {
if (\PHP_VERSION_ID >= 80000) {
throw new \ValueError(sprintf('The format type must be a NumberFormatter::TYPE_* constant (%s given).', $type));
}
trigger_error(__METHOD__.'(): Unsupported format type '.$type, \E_USER_WARNING);
return false;
}
if (self::CURRENCY === $this->style) {
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 (self::TYPE_DEFAULT !== $type) {
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 int|false The attribute value on success or false on error
*
* @see https://php.net/numberformatter.getattribute
*/
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 https://php.net/numberformatter.geterrorcode
*/
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 https://php.net/numberformatter.geterrormessage
*/
public function getErrorMessage()
{
return $this->errorMessage;
}
/**
* Returns the formatter's locale.
*
* The parameter $type is currently ignored.
*
* @param int $type Not supported. The locale name type to return (Locale::VALID_LOCALE or Locale::ACTUAL_LOCALE)
*
* @return string The locale used to create the formatter. Currently always
* returns "en".
*
* @see https://php.net/numberformatter.getlocale
*/
public function getLocale($type = Locale::ACTUAL_LOCALE)
{
return 'en';
}
/**
* Not supported. Returns the formatter's pattern.
*
* @return string|false The pattern string used by the formatter or false on error
*
* @see https://php.net/numberformatter.getpattern
*
* @throws MethodNotImplementedException
*/
public function getPattern()
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Returns a formatter symbol value.
*
* @param int $attr A symbol specifier, one of the format symbol constants
*
* @return string|false The symbol value or false on error
*
* @see https://php.net/numberformatter.getsymbol
*/
public function getSymbol($attr)
{
return \array_key_exists($this->style, self::$enSymbols) && \array_key_exists($attr, self::$enSymbols[$this->style]) ? self::$enSymbols[$this->style][$attr] : false;
}
/**
* Not supported. Returns a formatter text attribute value.
*
* @param int $attr An attribute specifier, one of the text attribute constants
*
* @return string|false The attribute value or false on error
*
* @see https://php.net/numberformatter.gettextattribute
*/
public function getTextAttribute($attr)
{
return \array_key_exists($this->style, self::$enTextAttributes) && \array_key_exists($attr, self::$enTextAttributes[$this->style]) ? self::$enTextAttributes[$this->style][$attr] : false;
}
/**
* Not supported. 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 float|false The parsed numeric value or false on error
*
* @see https://php.net/numberformatter.parsecurrency
*
* @throws MethodNotImplementedException
*/
public function parseCurrency($value, &$currency, &$position = null)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Parse a number.
*
* @param string $value The value to parse
* @param int $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 int|float|false The parsed value or false on error
*
* @see https://php.net/numberformatter.parse
*/
public function parse($value, $type = self::TYPE_DOUBLE, &$position = 0)
{
$type = (int) $type;
if (self::TYPE_DEFAULT === $type || self::TYPE_CURRENCY === $type) {
if (\PHP_VERSION_ID >= 80000) {
throw new \ValueError(sprintf('The format type must be a NumberFormatter::TYPE_* constant (%d given).', $type));
}
trigger_error(__METHOD__.'(): Unsupported format type '.$type, \E_USER_WARNING);
return false;
}
// Any invalid number at the end of the string is removed.
// Only numbers and the fraction separator is expected in the string.
// If grouping is used, grouping separator also becomes a valid character.
$groupingMatch = $this->getAttribute(self::GROUPING_USED) ? '|(?P<grouping>\d++(,{1}\d+)++(\.\d*+)?)' : '';
if (preg_match("/^-?(?:\.\d++{$groupingMatch}|\d++(\.\d*+)?)/", $value, $matches)) {
$value = $matches[0];
$position = \strlen($value);
// value is not valid if grouping is used, but digits are not grouped in groups of three
if ($error = isset($matches['grouping']) && !preg_match('/^-?(?:\d{1,3}+)?(?:(?:,\d{3})++|\d*+)(?:\.\d*+)?$/', $value)) {
// the position on error is 0 for positive and 1 for negative numbers
$position = 0 === strpos($value, '-') ? 1 : 0;
}
} else {
$error = true;
$position = 0;
}
if ($error) {
IntlGlobals::setError(IntlGlobals::U_PARSE_ERROR, 'Number parsing failed');
$this->errorCode = IntlGlobals::getErrorCode();
$this->errorMessage = IntlGlobals::getErrorMessage();
return false;
}
$value = str_replace(',', '', $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.
* The only currently supported attributes are NumberFormatter::FRACTION_DIGITS,
* NumberFormatter::GROUPING_USED and NumberFormatter::ROUNDING_MODE.
* @param int $value The attribute value
*
* @return bool true on success or false on failure
*
* @see https://php.net/numberformatter.setattribute
*
* @throws MethodArgumentValueNotImplementedException When the $attr is not supported
* @throws MethodArgumentValueNotImplementedException When the $value is not supported
*/
public function setAttribute($attr, $value)
{
$attr = (int) $attr;
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);
if ($value < 0) {
// ignore negative values but do not raise an error
return true;
}
}
$this->attributes[$attr] = $value;
$this->initializedAttributes[$attr] = true;
return true;
}
/**
* Not supported. Set the formatter's pattern.
*
* @param string $pattern A pattern string in conformance with the ICU DecimalFormat documentation
*
* @return bool true on success or false on failure
*
* @see https://php.net/numberformatter.setpattern
* @see http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details
*
* @throws MethodNotImplementedException
*/
public function setPattern($pattern)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. 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 bool true on success or false on failure
*
* @see https://php.net/numberformatter.setsymbol
*
* @throws MethodNotImplementedException
*/
public function setSymbol($attr, $value)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Set a text attribute.
*
* @param int $attr An attribute specifier, one of the text attribute constants
* @param string $value The attribute value
*
* @return bool true on success or false on failure
*
* @see https://php.net/numberformatter.settextattribute
*
* @throws MethodNotImplementedException
*/
public function setTextAttribute($attr, $value)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Set the error to the default U_ZERO_ERROR.
*/
protected function resetError()
{
IntlGlobals::setError(IntlGlobals::U_ZERO_ERROR);
$this->errorCode = IntlGlobals::getErrorCode();
$this->errorMessage = IntlGlobals::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 float 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 int|float $value The value to round
* @param int $precision The number of decimal digits to round to
*
* @return int|float The rounded value
*/
private function round($value, $precision)
{
$precision = $this->getUninitializedPrecision($value, $precision);
$roundingModeAttribute = $this->getAttribute(self::ROUNDING_MODE);
if (isset(self::$phpRoundingMap[$roundingModeAttribute])) {
$value = round($value, $precision, self::$phpRoundingMap[$roundingModeAttribute]);
} elseif (isset(self::$customRoundingList[$roundingModeAttribute])) {
$roundingCoef = pow(10, $precision);
$value *= $roundingCoef;
$value = (float) (string) $value;
switch ($roundingModeAttribute) {
case self::ROUND_CEILING:
$value = ceil($value);
break;
case self::ROUND_FLOOR:
$value = floor($value);
break;
case self::ROUND_UP:
$value = $value > 0 ? ceil($value) : floor($value);
break;
case self::ROUND_DOWN:
$value = $value > 0 ? floor($value) : ceil($value);
break;
}
$value /= $roundingCoef;
}
return $value;
}
/**
* Formats a number.
*
* @param int|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->getUninitializedPrecision($value, $precision);
return number_format($value, $precision, '.', $this->getAttribute(self::GROUPING_USED) ? ',' : '');
}
/**
* Returns the precision value if the DECIMAL style is being used and the FRACTION_DIGITS attribute is uninitialized.
*
* @param int|float $value The value to get the precision from if the FRACTION_DIGITS attribute is uninitialized
* @param int $precision The precision value to returns if the FRACTION_DIGITS attribute is initialized
*
* @return int The precision value
*/
private function getUninitializedPrecision($value, $precision)
{
if (self::CURRENCY === $this->style) {
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 bool 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 int|float|false The converted value
*/
private function convertValueDataType($value, $type)
{
$type = (int) $type;
if (self::TYPE_DOUBLE === $type) {
$value = (float) $value;
} elseif (self::TYPE_INT32 === $type) {
$value = $this->getInt32Value($value);
} elseif (self::TYPE_INT64 === $type) {
$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|false The converted value
*/
private function getInt32Value($value)
{
if ($value > self::$int32Max || $value < -self::$int32Max - 1) {
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|false The converted value
*/
private function getInt64Value($value)
{
if ($value > self::$int64Max || $value < -self::$int64Max - 1) {
return false;
}
if (\PHP_INT_SIZE !== 8 && ($value > self::$int32Max || $value < -self::$int32Max - 1)) {
return (float) $value;
}
return (int) $value;
}
/**
* Check if the rounding mode is invalid.
*
* @param int $value The rounding mode value to check
*
* @return bool 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) (bool) (int) $value;
}
/**
* Returns the normalized value for the FRACTION_DIGITS attribute.
*
* @param mixed $value The value to be normalized
*
* @return int The normalized value for the attribute
*/
private function normalizeFractionDigitsValue($value)
{
return (int) $value;
}
}

21
vendor/symfony/intl/README.md vendored Normal file
View File

@@ -0,0 +1,21 @@
Intl Component
=============
A PHP replacement layer for the C intl extension that also provides access to
the localization data of the ICU library.
The replacement layer is limited to the locale "en". If you want to use other
locales, you should [install the intl PHP extension][0] instead.
Resources
---------
* [Documentation](https://symfony.com/doc/current/components/intl.html)
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
* [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls)
in the [main Symfony repository](https://github.com/symfony/symfony)
* [Docker images with intl support](https://hub.docker.com/r/jakzal/php-intl)
(for the Intl component development)
[0]: https://php.net/intl.setup

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;
use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface;
use Symfony\Component\Intl\Data\Provider\CurrencyDataProvider;
use Symfony\Component\Intl\Data\Provider\LocaleDataProvider;
use Symfony\Component\Intl\Exception\MissingResourceException;
/**
* Default implementation of {@link CurrencyBundleInterface}.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class CurrencyBundle extends CurrencyDataProvider implements CurrencyBundleInterface
{
private $localeProvider;
/**
* Creates a new currency bundle.
*
* @param string $path
*/
public function __construct($path, BundleEntryReaderInterface $reader, LocaleDataProvider $localeProvider)
{
parent::__construct($path, $reader);
$this->localeProvider = $localeProvider;
}
/**
* {@inheritdoc}
*/
public function getCurrencySymbol($currency, $displayLocale = null)
{
try {
return $this->getSymbol($currency, $displayLocale);
} catch (MissingResourceException $e) {
return null;
}
}
/**
* {@inheritdoc}
*/
public function getCurrencyName($currency, $displayLocale = null)
{
try {
return $this->getName($currency, $displayLocale);
} catch (MissingResourceException $e) {
return null;
}
}
/**
* {@inheritdoc}
*/
public function getCurrencyNames($displayLocale = null)
{
try {
return $this->getNames($displayLocale);
} catch (MissingResourceException $e) {
return [];
}
}
/**
* {@inheritdoc}
*/
public function getFractionDigits($currency)
{
try {
return parent::getFractionDigits($currency);
} catch (MissingResourceException $e) {
return null;
}
}
/**
* {@inheritdoc}
*/
public function getRoundingIncrement($currency)
{
try {
return parent::getRoundingIncrement($currency);
} catch (MissingResourceException $e) {
return null;
}
}
/**
* {@inheritdoc}
*/
public function getLocales()
{
try {
return $this->localeProvider->getLocales();
} catch (MissingResourceException $e) {
return [];
}
}
}

View File

@@ -0,0 +1,74 @@
<?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 $currency A currency code (e.g. "EUR")
* @param string $displayLocale Optional. The locale to return the result in
* Defaults to {@link \Locale::getDefault()}.
*
* @return string|null The currency symbol or NULL if not found
*/
public function getCurrencySymbol($currency, $displayLocale = null);
/**
* Returns the name of a currency.
*
* @param string $currency A currency code (e.g. "EUR")
* @param string $displayLocale Optional. The locale to return the name in
* Defaults to {@link \Locale::getDefault()}.
*
* @return string|null The name of the currency or NULL if not found
*/
public function getCurrencyName($currency, $displayLocale = null);
/**
* Returns the names of all known currencies.
*
* @param string $displayLocale Optional. The locale to return the names in
* Defaults to {@link \Locale::getDefault()}.
*
* @return string[] A list of currency names indexed by currency codes
*/
public function getCurrencyNames($displayLocale = null);
/**
* Returns the number of digits after the comma of a currency.
*
* @param string $currency A currency code (e.g. "EUR")
*
* @return int|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|int|null The rounding increment or NULL if not found
*/
public function getRoundingIncrement($currency);
}

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;
use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface;
use Symfony\Component\Intl\Data\Provider\LanguageDataProvider;
use Symfony\Component\Intl\Data\Provider\LocaleDataProvider;
use Symfony\Component\Intl\Data\Provider\ScriptDataProvider;
use Symfony\Component\Intl\Exception\MissingResourceException;
/**
* Default implementation of {@link LanguageBundleInterface}.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class LanguageBundle extends LanguageDataProvider implements LanguageBundleInterface
{
private $localeProvider;
private $scriptProvider;
/**
* Creates a new language bundle.
*
* @param string $path
*/
public function __construct($path, BundleEntryReaderInterface $reader, LocaleDataProvider $localeProvider, ScriptDataProvider $scriptProvider)
{
parent::__construct($path, $reader);
$this->localeProvider = $localeProvider;
$this->scriptProvider = $scriptProvider;
}
/**
* {@inheritdoc}
*/
public function getLanguageName($language, $region = null, $displayLocale = null)
{
// Some languages are translated together with their region,
// i.e. "en_GB" is translated as "British English"
if (null !== $region) {
try {
return $this->getName($language.'_'.$region, $displayLocale);
} catch (MissingResourceException $e) {
}
}
try {
return $this->getName($language, $displayLocale);
} catch (MissingResourceException $e) {
return null;
}
}
/**
* {@inheritdoc}
*/
public function getLanguageNames($displayLocale = null)
{
try {
return $this->getNames($displayLocale);
} catch (MissingResourceException $e) {
return [];
}
}
/**
* {@inheritdoc}
*/
public function getScriptName($script, $language = null, $displayLocale = null)
{
try {
return $this->scriptProvider->getName($script, $displayLocale);
} catch (MissingResourceException $e) {
return null;
}
}
/**
* {@inheritdoc}
*/
public function getScriptNames($displayLocale = null)
{
try {
return $this->scriptProvider->getNames($displayLocale);
} catch (MissingResourceException $e) {
return [];
}
}
/**
* {@inheritdoc}
*/
public function getLocales()
{
try {
return $this->localeProvider->getLocales();
} catch (MissingResourceException $e) {
return [];
}
}
}

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\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 $language A language code (e.g. "en")
* @param string|null $region Optional. A region code (e.g. "US")
* @param string $displayLocale Optional. The locale to return the name in
* Defaults to {@link \Locale::getDefault()}.
*
* @return string|null The name of the language or NULL if not found
*/
public function getLanguageName($language, $region = null, $displayLocale = null);
/**
* Returns the names of all known languages.
*
* @param string $displayLocale Optional. The locale to return the names in
* Defaults to {@link \Locale::getDefault()}.
*
* @return string[] A list of language names indexed by language codes
*/
public function getLanguageNames($displayLocale = null);
/**
* Returns the name of a script.
*
* @param string $script A script code (e.g. "Hans")
* @param string $language Optional. A language code (e.g. "zh")
* @param string $displayLocale Optional. The locale to return the name in
* Defaults to {@link \Locale::getDefault()}.
*
* @return string|null The name of the script or NULL if not found
*/
public function getScriptName($script, $language = null, $displayLocale = null);
/**
* Returns the names of all known scripts.
*
* @param string $displayLocale Optional. The locale to return the names in
* Defaults to {@link \Locale::getDefault()}.
*
* @return string[] A list of script names indexed by script codes
*/
public function getScriptNames($displayLocale = null);
}

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;
use Symfony\Component\Intl\Data\Provider\LocaleDataProvider;
use Symfony\Component\Intl\Exception\MissingResourceException;
/**
* Default implementation of {@link LocaleBundleInterface}.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class LocaleBundle extends LocaleDataProvider implements LocaleBundleInterface
{
/**
* {@inheritdoc}
*/
public function getLocales()
{
try {
return parent::getLocales();
} catch (MissingResourceException $e) {
return [];
}
}
/**
* {@inheritdoc}
*/
public function getLocaleName($locale, $displayLocale = null)
{
try {
return $this->getName($locale, $displayLocale);
} catch (MissingResourceException $e) {
return null;
}
}
/**
* {@inheritdoc}
*/
public function getLocaleNames($displayLocale = null)
{
try {
return $this->getNames($displayLocale);
} catch (MissingResourceException $e) {
return [];
}
}
}

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\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 of (e.g. "de_AT")
* @param string $displayLocale Optional. The locale to return the name in
* Defaults to {@link \Locale::getDefault()}.
*
* @return string|null The name of the locale or NULL if not found
*/
public function getLocaleName($locale, $displayLocale = null);
/**
* Returns the names of all known locales.
*
* @param string $displayLocale Optional. The locale to return the names in
* Defaults to {@link \Locale::getDefault()}.
*
* @return string[] A list of locale names indexed by locale codes
*/
public function getLocaleNames($displayLocale = null);
}

View File

@@ -0,0 +1,77 @@
<?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\Data\Bundle\Reader\BundleEntryReaderInterface;
use Symfony\Component\Intl\Data\Provider\LocaleDataProvider;
use Symfony\Component\Intl\Data\Provider\RegionDataProvider;
use Symfony\Component\Intl\Exception\MissingResourceException;
/**
* Default implementation of {@link RegionBundleInterface}.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class RegionBundle extends RegionDataProvider implements RegionBundleInterface
{
private $localeProvider;
/**
* Creates a new region bundle.
*
* @param string $path
*/
public function __construct($path, BundleEntryReaderInterface $reader, LocaleDataProvider $localeProvider)
{
parent::__construct($path, $reader);
$this->localeProvider = $localeProvider;
}
/**
* {@inheritdoc}
*/
public function getCountryName($country, $displayLocale = null)
{
try {
return $this->getName($country, $displayLocale);
} catch (MissingResourceException $e) {
return null;
}
}
/**
* {@inheritdoc}
*/
public function getCountryNames($displayLocale = null)
{
try {
return $this->getNames($displayLocale);
} catch (MissingResourceException $e) {
return [];
}
}
/**
* {@inheritdoc}
*/
public function getLocales()
{
try {
return $this->localeProvider->getLocales();
} catch (MissingResourceException $e) {
return [];
}
}
}

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\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 $country A country code (e.g. "US")
* @param string $displayLocale Optional. The locale to return the name in
* Defaults to {@link \Locale::getDefault()}.
*
* @return string|null The name of the country or NULL if not found
*/
public function getCountryName($country, $displayLocale = null);
/**
* Returns the names of all known countries.
*
* @param string $displayLocale Optional. The locale to return the names in
* Defaults to {@link \Locale::getDefault()}.
*
* @return string[] A list of country names indexed by country codes
*/
public function getCountryNames($displayLocale = null);
}

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,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.
*/
$autoload = __DIR__.'/../../vendor/autoload.php';
if (!file_exists($autoload)) {
bailout('You should run "composer install" in the component before running this script.');
}
require_once $autoload;

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.
*/
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 while running:\n ".getcwd().'$ '.$command."\nOutput:\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];
}
error_reporting(\E_ALL);
set_error_handler(function ($type, $msg, $file, $line) {
throw new \ErrorException($msg, 0, $type, $file, $line);
});
set_exception_handler(function (Throwable $exception) {
echo "\n";
$cause = $exception;
$root = true;
while (null !== $cause) {
if (!$root) {
echo "Caused by\n";
}
echo get_class($cause).': '.$cause->getMessage()."\n";
echo "\n";
echo $cause->getFile().':'.$cause->getLine()."\n";
echo $cause->getTraceAsString()."\n";
$cause = $cause->getPrevious();
$root = false;
}
});

View File

@@ -0,0 +1,13 @@
#!/usr/bin/env bash
[[ $1 == force ]] && docker pull jakzal/php-intl
[[ ! -d /tmp/symfony/icu ]] && mkdir -p /tmp/symfony/icu
docker run \
-it --rm --name symfony-intl \
-u $(id -u):$(id -g) \
-v /tmp/symfony/icu:/tmp \
-v $(pwd):/symfony \
-w /symfony \
jakzal/php-intl:latest \
php src/Symfony/Component/Intl/Resources/bin/update-data.php

View File

@@ -0,0 +1,226 @@
<?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\Intl\Data\Bundle\Compiler\GenrbCompiler;
use Symfony\Component\Intl\Data\Bundle\Writer\JsonBundleWriter;
use Symfony\Component\Intl\Data\Generator\CurrencyDataGenerator;
use Symfony\Component\Intl\Data\Generator\GeneratorConfig;
use Symfony\Component\Intl\Data\Generator\LanguageDataGenerator;
use Symfony\Component\Intl\Data\Generator\LocaleDataGenerator;
use Symfony\Component\Intl\Data\Generator\RegionDataGenerator;
use Symfony\Component\Intl\Data\Generator\ScriptDataGenerator;
use Symfony\Component\Intl\Intl;
use Symfony\Component\Intl\Locale;
use Symfony\Component\Intl\Util\GitRepository;
require_once __DIR__.'/common.php';
require_once __DIR__.'/autoload.php';
$argc = $_SERVER['argc'];
$argv = $_SERVER['argv'];
if ($argc > 3 || 2 === $argc && '-h' === $argv[1]) {
bailout(<<<'MESSAGE'
Usage: php update-data.php <path/to/icu/source> <path/to/icu/build>
Updates the ICU data for Symfony to the latest version of ICU.
If you downloaded the git repository before, you can pass the path to the
repository source in the first optional argument.
If you also built the repository before, you can pass the directory where that
build is stored in the second parameter. The build directory needs to contain
the subdirectories bin/ and lib/.
For running this script, the intl extension must be loaded and all vendors
must have been installed through composer:
composer install
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 ($argc >= 2) {
$repoDir = $argv[1];
$git = new GitRepository($repoDir);
echo "Using the existing git repository at {$repoDir}.\n";
} else {
echo "Starting git clone. This may take a while...\n";
$repoDir = sys_get_temp_dir().'/icu-data';
$git = GitRepository::download('https://github.com/unicode-org/icu.git', $repoDir);
echo "Git clone to {$repoDir} complete.\n";
}
$gitTag = $git->getLastTag(function ($tag) {
return preg_match('#^release-[0-9]{1,}-[0-9]{1}$#', $tag);
});
$shortIcuVersion = strip_minor_versions(preg_replace('#release-([0-9]{1,})-([0-9]{1,})#', '$1.$2', $gitTag));
echo "Checking out `{$gitTag}` for version `{$shortIcuVersion}`...\n";
$git->checkout('tags/'.$gitTag);
$filesystem = new Filesystem();
$sourceDir = $repoDir.'/icu4c/source';
if ($argc >= 3) {
$buildDir = $argv[2];
} else {
// Always build genrb so that we can determine the ICU version of the
// download by running genrb --version
echo "Building genrb.\n";
cd($sourceDir);
echo "Running configure...\n";
$buildDir = sys_get_temp_dir().'/icu-data/'.$shortIcuVersion.'/build';
$filesystem->remove($buildDir);
$filesystem->mkdir($buildDir);
run('./configure --prefix='.$buildDir.' 2>&1');
echo "Running make...\n";
// If the directory "lib" does not exist in the download, create it or we
// will run into problems when building libicuuc.so.
$filesystem->mkdir($sourceDir.'/lib');
// If the directory "bin" does not exist in the download, create it or we
// will run into problems when building genrb.
$filesystem->mkdir($sourceDir.'/bin');
echo '[1/6] libicudata.so...';
cd($sourceDir.'/stubdata');
run('make 2>&1 && make install 2>&1');
echo " ok.\n";
echo '[2/6] libicuuc.so...';
cd($sourceDir.'/common');
run('make 2>&1 && make install 2>&1');
echo " ok.\n";
echo '[3/6] libicui18n.so...';
cd($sourceDir.'/i18n');
run('make 2>&1 && make install 2>&1');
echo " ok.\n";
echo '[4/6] libicutu.so...';
cd($sourceDir.'/tools/toolutil');
run('make 2>&1 && make install 2>&1');
echo " ok.\n";
echo '[5/6] libicuio.so...';
cd($sourceDir.'/io');
run('make 2>&1 && make install 2>&1');
echo " ok.\n";
echo '[6/6] genrb...';
cd($sourceDir.'/tools/genrb');
run('make 2>&1 && make install 2>&1');
echo " ok.\n";
}
$genrb = $buildDir.'/bin/genrb';
$genrbEnv = 'LD_LIBRARY_PATH='.$buildDir.'/lib ';
echo "Using $genrb.\n";
$icuVersionInDownload = get_icu_version_from_genrb($genrbEnv.' '.$genrb);
echo "Preparing resource bundle compilation (version $icuVersionInDownload)...\n";
$compiler = new GenrbCompiler($genrb, $genrbEnv);
$config = new GeneratorConfig($sourceDir.'/data', $icuVersionInDownload);
$jsonDir = dirname(__DIR__).'/data';
$config->addBundleWriter($jsonDir, new JsonBundleWriter());
echo "Starting resource bundle compilation. This may take a while...\n";
// We don't want to use fallback to English during generation
Locale::setDefaultFallback('root');
echo "Generating language data...\n";
$generator = new LanguageDataGenerator($compiler, Intl::LANGUAGE_DIR);
$generator->generateData($config);
echo "Generating script data...\n";
$generator = new ScriptDataGenerator($compiler, Intl::SCRIPT_DIR);
$generator->generateData($config);
echo "Generating region data...\n";
$generator = new RegionDataGenerator($compiler, Intl::REGION_DIR);
$generator->generateData($config);
echo "Generating currency data...\n";
$generator = new CurrencyDataGenerator($compiler, Intl::CURRENCY_DIR);
$generator->generateData($config);
echo "Generating locale data...\n";
$generator = new LocaleDataGenerator($compiler, Intl::LOCALE_DIR);
$generator->generateData($config);
echo "Resource bundle compilation complete.\n";
$gitInfo = <<<GIT_INFO
Git information
===============
URL: {$git->getUrl()}
Revision: {$git->getLastCommitHash()}
Author: {$git->getLastAuthor()}
Date: {$git->getLastAuthoredDate()->format('c')}
GIT_INFO;
$gitInfoFile = $jsonDir.'/git-info.txt';
file_put_contents($gitInfoFile, $gitInfo);
echo "Wrote $gitInfoFile.\n";
$versionFile = $jsonDir.'/version.txt';
file_put_contents($versionFile, "$icuVersionInDownload\n");
echo "Wrote $versionFile.\n";
echo "Done.\n";

View File

@@ -0,0 +1,684 @@
{
"Names": {
"AED": [
"AED",
"Verenigde Arabiese Emirate-dirham"
],
"AFN": [
"AFN",
"Afgaanse afgani"
],
"ALL": [
"ALL",
"Albanese lek"
],
"AMD": [
"AMD",
"Armeense dram"
],
"ANG": [
"ANG",
"Nederlands-Antilliaanse gulde"
],
"AOA": [
"AOA",
"Angolese kwanza"
],
"ARS": [
"ARS",
"Argentynse peso"
],
"AUD": [
"A$",
"Australiese dollar"
],
"AWG": [
"AWG",
"Arubaanse floryn"
],
"AZN": [
"AZN",
"Azerbeidjaanse manat"
],
"BAM": [
"BAM",
"Bosnies-Herzegowiniese omskakelbare marka"
],
"BBD": [
"BBD",
"Barbados-dollar"
],
"BDT": [
"BDT",
"Bangladesjiese taka"
],
"BGN": [
"BGN",
"Bulgaarse lev"
],
"BHD": [
"BHD",
"Bahreinse dinar"
],
"BIF": [
"BIF",
"Burundiese frank"
],
"BMD": [
"BMD",
"Bermuda-dollar"
],
"BND": [
"BND",
"Broeneise dollar"
],
"BOB": [
"BOB",
"Boliviaanse boliviano"
],
"BRL": [
"R$",
"Brasilliaanse reaal"
],
"BSD": [
"BSD",
"Bahamiaanse dollar"
],
"BTN": [
"BTN",
"Bhoetanese ngoeltroem"
],
"BWP": [
"BWP",
"Botswana-pula"
],
"BYN": [
"BYN",
"Belarusiese roebel"
],
"BYR": [
"BYR",
"Belo-Russiese roebel (20002016)"
],
"BZD": [
"BZD",
"Beliziese dollar"
],
"CAD": [
"CAD",
"Kanadese dollar"
],
"CDF": [
"CDF",
"Kongolese frank"
],
"CHF": [
"CHF",
"Switserse frank"
],
"CLP": [
"CLP",
"Chileense peso"
],
"CNH": [
"CNH",
"Chinese joean (buiteland)"
],
"CNY": [
"CN¥",
"Chinese joean"
],
"COP": [
"COP",
"Colombiaanse peso"
],
"CRC": [
"CRC",
"Costa Ricaanse colón"
],
"CUC": [
"CUC",
"Kubaanse omskakelbare peso"
],
"CUP": [
"CUP",
"Kubaanse peso"
],
"CVE": [
"CVE",
"Kaap Verdiese escudo"
],
"CZK": [
"CZK",
"Tsjeggiese kroon"
],
"DJF": [
"DJF",
"Djiboeti-frank"
],
"DKK": [
"DKK",
"Deense kroon"
],
"DOP": [
"DOP",
"Dominikaanse peso"
],
"DZD": [
"DZD",
"Algeriese dinar"
],
"EGP": [
"EGP",
"Egiptiese pond"
],
"ERN": [
"ERN",
"Eritrese nakfa"
],
"ETB": [
"ETB",
"Etiopiese birr"
],
"EUR": [
"€",
"euro"
],
"FJD": [
"FJD",
"Fidjiaanse dollar"
],
"FKP": [
"FKP",
"Falkland-eilandse pond"
],
"GBP": [
"£",
"Britse pond"
],
"GEL": [
"GEL",
"Georgiese lari"
],
"GHC": [
"GHC",
"Ghanese cedi (19792007)"
],
"GHS": [
"GHS",
"Ghanese cedi"
],
"GIP": [
"GIP",
"Gibraltarese pond"
],
"GMD": [
"GMD",
"Gambiese dalasi"
],
"GNF": [
"GNF",
"Guinese frank"
],
"GNS": [
"GNS",
"Guinese syli"
],
"GTQ": [
"GTQ",
"Guatemalaanse quetzal"
],
"GYD": [
"GYD",
"Guyanese dollar"
],
"HKD": [
"HK$",
"Hongkongse dollar"
],
"HNL": [
"HNL",
"Hondurese lempira"
],
"HRK": [
"HRK",
"Kroatiese kuna"
],
"HTG": [
"HTG",
"Haïtiaanse gourde"
],
"HUF": [
"HUF",
"Hongaarse florint"
],
"IDR": [
"IDR",
"Indonesiese roepia"
],
"ILS": [
"₪",
"Israeliese nuwe sikkel"
],
"INR": [
"₹",
"Indiese roepee"
],
"IQD": [
"IQD",
"Irakse dinar"
],
"IRR": [
"IRR",
"Iranse rial"
],
"ISK": [
"ISK",
"Yslandse kroon"
],
"ITL": [
"ITL",
"Italiaanse lier"
],
"JMD": [
"JMD",
"Jamaikaanse dollar"
],
"JOD": [
"JOD",
"Jordaniese dinar"
],
"JPY": [
"JP¥",
"Japannese jen"
],
"KES": [
"KES",
"Keniaanse sjieling"
],
"KGS": [
"KGS",
"Kirgisiese som"
],
"KHR": [
"KHR",
"Kambodjaanse riel"
],
"KMF": [
"KMF",
"Comoraanse frank"
],
"KPW": [
"KPW",
"Noord-Koreaanse won"
],
"KRW": [
"₩",
"Suid-Koreaanse won"
],
"KWD": [
"KWD",
"Koeweitse dinar"
],
"KYD": [
"KYD",
"Cayman-eilandse dollar"
],
"KZT": [
"KZT",
"Kazakse tenge"
],
"LAK": [
"LAK",
"Laosiaanse kip"
],
"LBP": [
"LBP",
"Libanese pond"
],
"LKR": [
"LKR",
"Sri Lankaanse roepee"
],
"LRD": [
"LRD",
"Liberiese dollar"
],
"LSL": [
"LSL",
"Lesotho loti"
],
"LTL": [
"LTL",
"Litause litas"
],
"LVL": [
"LVL",
"Lettiese lats"
],
"LYD": [
"LYD",
"Libiese dinar"
],
"MAD": [
"MAD",
"Marokkaanse dirham"
],
"MDL": [
"MDL",
"Moldowiese leu"
],
"MGA": [
"MGA",
"Malgassiese ariary"
],
"MKD": [
"MKD",
"Macedoniese denar"
],
"MMK": [
"MMK",
"Mianmese kyat"
],
"MNT": [
"MNT",
"Mongoolse toegrik"
],
"MOP": [
"MOP",
"Macaose pataca"
],
"MRO": [
"MRO",
"Mauritaniese ouguiya (19732017)"
],
"MRU": [
"MRU",
"Mauritaniese ouguiya"
],
"MUR": [
"MUR",
"Mauritiaanse roepee"
],
"MVR": [
"MVR",
"Malediviese rufia"
],
"MWK": [
"MWK",
"Malawiese kwacha"
],
"MXN": [
"MXN",
"Meksikaanse peso"
],
"MYR": [
"MYR",
"Maleisiese ringgit"
],
"MZM": [
"MZM",
"Mosambiekse metical (19802006)"
],
"MZN": [
"MZN",
"Mosambiekse metical"
],
"NAD": [
"NAD",
"Namibiese dollar"
],
"NGN": [
"NGN",
"Nigeriese naira"
],
"NIO": [
"NIO",
"Nicaraguaanse córdoba"
],
"NOK": [
"NOK",
"Noorse kroon"
],
"NPR": [
"NPR",
"Nepalese roepee"
],
"NZD": [
"NZ$",
"Nieu-Seelandse dollar"
],
"OMR": [
"OMR",
"Omaanse rial"
],
"PAB": [
"PAB",
"Panamese balboa"
],
"PEN": [
"PEN",
"Peruaanse sol"
],
"PGK": [
"PGK",
"Papoea-Nieu-Guinese kina"
],
"PHP": [
"PHP",
"Filippynse peso"
],
"PKR": [
"PKR",
"Pakistanse roepee"
],
"PLN": [
"PLN",
"Poolse zloty"
],
"PYG": [
"PYG",
"Paraguaanse guarani"
],
"QAR": [
"QAR",
"Katarrese rial"
],
"RON": [
"RON",
"Roemeense leu"
],
"RSD": [
"RSD",
"Serwiese dinar"
],
"RUB": [
"RUB",
"Russiese roebel"
],
"RWF": [
"RWF",
"Rwandese frank"
],
"SAR": [
"SAR",
"Saoedi-Arabiese riyal"
],
"SBD": [
"SBD",
"Salomonseilandse dollar"
],
"SCR": [
"SCR",
"Seychellese roepee"
],
"SDG": [
"SDG",
"Soedannese pond"
],
"SDP": [
"SDP",
"Soedannese pond (19571998)"
],
"SEK": [
"SEK",
"Sweedse kroon"
],
"SGD": [
"SGD",
"Singapoer-dollar"
],
"SHP": [
"SHP",
"Sint Helena-pond"
],
"SLL": [
"SLL",
"Sierra Leoniese leone"
],
"SOS": [
"SOS",
"Somaliese sjieling"
],
"SRD": [
"SRD",
"Surinaamse dollar"
],
"SSP": [
"SSP",
"Suid-Soedanese pond"
],
"STD": [
"STD",
"São Tomé en Príncipe dobra (19772017)"
],
"STN": [
"STN",
"São Tomé en Príncipe-dobra"
],
"SYP": [
"SYP",
"Siriese pond"
],
"SZL": [
"SZL",
"Swazilandse lilangeni"
],
"THB": [
"฿",
"Thaise baht"
],
"TJS": [
"TJS",
"Tadjikse somoni"
],
"TMT": [
"TMT",
"Turkmeense manat"
],
"TND": [
"TND",
"Tunisiese dinar"
],
"TOP": [
"TOP",
"Tongaanse paanga"
],
"TRL": [
"TRL",
"Turkse lier (19222005)"
],
"TRY": [
"TRY",
"Turkse lira"
],
"TTD": [
"TTD",
"Trinidad en Tobago-dollar"
],
"TWD": [
"NT$",
"Nuwe Taiwanese dollar"
],
"TZS": [
"TZS",
"Tanzaniese sjieling"
],
"UAH": [
"UAH",
"Oekraïnse hriwna"
],
"UGX": [
"UGX",
"Ugandese sjieling"
],
"USD": [
"USD",
"VSA-dollar"
],
"UYU": [
"UYU",
"Uruguaanse peso"
],
"UZS": [
"UZS",
"Oezbekiese som"
],
"VEF": [
"VEF",
"Venezolaanse bolivar"
],
"VES": [
"VES",
"Venezolaanse bolívar"
],
"VND": [
"₫",
"Viëtnamese dong"
],
"VUV": [
"VUV",
"Vanuatuse vatu"
],
"WST": [
"WST",
"Samoaanse tala"
],
"XAF": [
"FCFA",
"Sentraal Afrikaanse CFA-frank"
],
"XCD": [
"EC$",
"Oos-Karibiese dollar"
],
"XOF": [
"CFA",
"Wes-Afrikaanse CFA-frank"
],
"XPF": [
"CFPF",
"CFP-frank"
],
"YER": [
"YER",
"Jemenitiese rial"
],
"ZAR": [
"R",
"Suid-Afrikaanse rand"
],
"ZMK": [
"ZMK",
"Zambiese kwacha (19682012)"
],
"ZMW": [
"ZMW",
"Zambiese kwacha"
],
"ZWD": [
"ZWD",
"Zimbabwiese dollar"
]
}
}

View File

@@ -0,0 +1,8 @@
{
"Names": {
"NAD": [
"$",
"Namibiese dollar"
]
}
}

View File

@@ -0,0 +1,232 @@
{
"Names": {
"AED": [
"AED",
"Ɛmirete Arab Nkabɔmu Deram"
],
"AOA": [
"AOA",
"Angola Kwanza"
],
"AUD": [
"A$",
"Ɔstrelia Dɔla"
],
"BHD": [
"BHD",
"Baren Dina"
],
"BIF": [
"BIF",
"Burundi Frank"
],
"BWP": [
"BWP",
"Botswana Pula"
],
"CAD": [
"CA$",
"Kanada Dɔla"
],
"CDF": [
"CDF",
"Kongo Frank"
],
"CNY": [
"CN¥",
"Yuan"
],
"CVE": [
"CVE",
"Ɛskudo"
],
"DJF": [
"DJF",
"Gyebuti Frank"
],
"DZD": [
"DZD",
"Ɔlgyeria Dina"
],
"EGP": [
"EGP",
"Egypt Pɔn"
],
"ERN": [
"ERN",
"Ɛretereya Nakfa"
],
"ETB": [
"ETB",
"Itiopia Bir"
],
"EUR": [
"€",
"Iro"
],
"GBP": [
"£",
"Breten Pɔn"
],
"GHC": [
"GHC",
"Ghana Sidi (19792007)"
],
"GHS": [
"GH₵",
"Ghana Sidi"
],
"GMD": [
"GMD",
"Gambia Dalasi"
],
"GNS": [
"GNS",
"Gini Frank"
],
"INR": [
"₹",
"India Rupi"
],
"JPY": [
"JP¥",
"Gyapan Yɛn"
],
"KES": [
"KES",
"Kenya Hyelen"
],
"KMF": [
"KMF",
"Komoro Frank"
],
"LRD": [
"LRD",
"Laeberia Dɔla"
],
"LSL": [
"LSL",
"Lesoto Loti"
],
"LYD": [
"LYD",
"Libya Dina"
],
"MAD": [
"MAD",
"Moroko Diram"
],
"MGA": [
"MGA",
"Madagasi Frank"
],
"MRO": [
"MRO",
"Mɔretenia Ouguiya (19732017)"
],
"MRU": [
"MRU",
"Mɔretenia Ouguiya"
],
"MUR": [
"MUR",
"Mɔrehyeɔs Rupi"
],
"MWK": [
"MWK",
"Malawi Kwacha"
],
"MZM": [
"MZM",
"Mozambik Metical"
],
"NAD": [
"NAD",
"Namibia Dɔla"
],
"NGN": [
"NGN",
"Naegyeria Naira"
],
"RWF": [
"RWF",
"Rewanda Frank"
],
"SAR": [
"SAR",
"Saudi Riyal"
],
"SCR": [
"SCR",
"Seyhyɛls Rupi"
],
"SDG": [
"SDG",
"Sudan Dina"
],
"SDP": [
"SDP",
"Sudan Pɔn"
],
"SHP": [
"SHP",
"St Helena Pɔn"
],
"SLL": [
"SLL",
"Leone"
],
"SOS": [
"SOS",
"Somailia Hyelen"
],
"STD": [
"STD",
"Sao Tome ne Principe Dobra (19772017)"
],
"STN": [
"STN",
"Sao Tome ne Principe Dobra"
],
"SZL": [
"SZL",
"Lilangeni"
],
"TND": [
"TND",
"Tunisia Dina"
],
"TZS": [
"TZS",
"Tanzania Hyelen"
],
"UGX": [
"UGX",
"Uganda Hyelen"
],
"USD": [
"US$",
"Amɛrika Dɔla"
],
"XAF": [
"FCFA",
"Sefa"
],
"ZAR": [
"ZAR",
"Afrika Anaafo Rand"
],
"ZMK": [
"ZMK",
"Zambia Kwacha (19682012)"
],
"ZMW": [
"ZMW",
"Zambia Kwacha"
],
"ZWD": [
"ZWD",
"Zimbabwe Dɔla"
]
}
}

View File

@@ -0,0 +1,668 @@
{
"Names": {
"AED": [
"AED",
"የተባበሩት የአረብ ኤምሬትስ ድርሀም"
],
"AFN": [
"AFN",
"የአፍጋን አፍጋኒ"
],
"ALL": [
"ALL",
"የአልባንያ ሌክ"
],
"AMD": [
"AMD",
"የአርመን ድራም"
],
"ANG": [
"ANG",
"ኔዘርላንድስ አንቲሊአን ጊልደር"
],
"AOA": [
"AOA",
"የአንጎላ ኩዋንዛ"
],
"ARS": [
"ARS",
"የአርጀንቲና ፔሶ"
],
"AUD": [
"AU$",
"የአውስትራሊያ ዶላር"
],
"AWG": [
"AWG",
"አሩባን ፍሎሪን"
],
"AZN": [
"AZN",
"የአዛርባጃን ማናት"
],
"BAM": [
"BAM",
"የቦስኒያ ሄርዞጎቪና የሚመነዘር ማርክ"
],
"BBD": [
"BBD",
"የባርቤዶስ ዶላር"
],
"BDT": [
"BDT",
"የባንግላዲሽ ታካ"
],
"BGN": [
"BGN",
"የቡልጋሪያ ሌቭ"
],
"BHD": [
"BHD",
"የባኽሬን ዲናር"
],
"BIF": [
"BIF",
"የብሩንዲ ፍራንክ"
],
"BMD": [
"BMD",
"የቤርሙዳ ዶላር"
],
"BND": [
"BND",
"የብሩኔ ዶላር"
],
"BOB": [
"BOB",
"የቦሊቪያ ቦሊቪያኖ"
],
"BRL": [
"R$",
"የብራዚል ሪል"
],
"BSD": [
"BSD",
"የባሃማስ ዶላር"
],
"BTN": [
"BTN",
"ብሁታኒዝ ንጉልትረም"
],
"BWP": [
"BWP",
"የቦትስዋና ፑላ"
],
"BYN": [
"BYN",
"የቤላሩስያ ሩብል"
],
"BYR": [
"BYR",
"የቤላሩስያ ሩብል (20002016)"
],
"BZD": [
"BZD",
"የቤሊዝ ዶላር"
],
"CAD": [
"CA$",
"የካናዳ ዶላር"
],
"CDF": [
"CDF",
"የኮንጐ ፍራንክ ኮንጐሌዝ"
],
"CHF": [
"CHF",
"የስዊስ ፍራንክ"
],
"CLP": [
"CLP",
"የቺሊ ፔሶ"
],
"CNH": [
"የቻይና ዩዋን",
"የቻይና ዩዋን (የውጭ ምንዛሪ)"
],
"CNY": [
"CN¥",
"የቻይና የን"
],
"COP": [
"COP",
"የኮሎምቢያ ፔሶ"
],
"CRC": [
"CRC",
"የኮስታሪካ ኮሎን"
],
"CUC": [
"CUC",
"የኩባ የሚመነዘር ፔሶ"
],
"CUP": [
"CUP",
"የኩባ ፔሶ"
],
"CVE": [
"CVE",
"የኬፕ ቫርዲ ኤስኩዶ"
],
"CZK": [
"CZK",
"ቼክ ሪፐብሊክ ኮሩና"
],
"DJF": [
"DJF",
"የጅቡቲ ፍራንክ"
],
"DKK": [
"DKK",
"የዴንማርክ ክሮን"
],
"DOP": [
"DOP",
"የዶሚኒክ ፔሶ"
],
"DZD": [
"DZD",
"የአልጄሪያ ዲናር"
],
"EGP": [
"EGP",
"የግብጽ ፓውንድ"
],
"ERN": [
"ERN",
"የኤርትራ ናቅፋ"
],
"ETB": [
"ብር",
"የኢትዮጵያ ብር"
],
"EUR": [
"€",
"ዩሮ"
],
"FJD": [
"FJD",
"የፊጂ ዶላር"
],
"FKP": [
"FKP",
"የፎክላንድ ደሴቶች ፓውንድ"
],
"GBP": [
"£",
"የእንግሊዝ ፓውንድ ስተርሊንግ"
],
"GEL": [
"GEL",
"የጆርጅያ ላሪ"
],
"GHC": [
"GHC",
"የጋና ሴዲ"
],
"GHS": [
"GHS",
"የጋና ሲዲ"
],
"GIP": [
"GIP",
"ጂብራልተር ፓውንድ"
],
"GMD": [
"GMD",
"የጋምቢያ ዳላሲ"
],
"GNF": [
"GNF",
"የጊኒ ፍራንክ"
],
"GTQ": [
"GTQ",
"ጓቲማላን ኩቲዛል"
],
"GYD": [
"GYD",
"የጉየና ዶላር"
],
"HKD": [
"HK$",
"የሆንግኮንግ ዶላር"
],
"HNL": [
"HNL",
"የሃንዱራ ሌምፓአይራ"
],
"HRK": [
"HRK",
"የክሮሽያ ኩና"
],
"HTG": [
"HTG",
"የሃያቲ ጓርዴ"
],
"HUF": [
"HUF",
"የሃንጋሪያን ፎሪንት"
],
"IDR": [
"IDR",
"የኢንዶኔዥያ ሩፒሃ"
],
"ILS": [
"₪",
"የእስራኤል አዲስ ሽቅል"
],
"INR": [
"₹",
"የሕንድ ሩፒ"
],
"IQD": [
"IQD",
"የኢራቅ ዲናር"
],
"IRR": [
"IRR",
"የኢራን ሪአል"
],
"ISK": [
"ISK",
"የአይስላንድ ክሮና"
],
"JMD": [
"JMD",
"የጃማይካ ዶላር"
],
"JOD": [
"JOD",
"የጆርዳን ዲናር"
],
"JPY": [
"JP¥",
"የጃፓን የን"
],
"KES": [
"KES",
"የኬኒያ ሺሊንግ"
],
"KGS": [
"KGS",
"የኪርጊስታን ሶም"
],
"KHR": [
"KHR",
"የካምቦዲያ ሬል"
],
"KMF": [
"KMF",
"የኮሞሮ ፍራንክ"
],
"KPW": [
"KPW",
"የሰሜን ኮሪያ ዎን"
],
"KRW": [
"₩",
"የደቡብ ኮሪያ ዎን"
],
"KWD": [
"KWD",
"የኩዌት ዲናር"
],
"KYD": [
"KYD",
"የካይማን ደሴቶች ዶላር"
],
"KZT": [
"KZT",
"የካዛኪስታን ተንጌ"
],
"LAK": [
"LAK",
"የላኦቲ ኪፕ"
],
"LBP": [
"LBP",
"የሊባኖስ ፓውንድ"
],
"LKR": [
"LKR",
"የሲሪላንካ ሩፒ"
],
"LRD": [
"LRD",
"የላይቤሪያ ዶላር"
],
"LSL": [
"LSL",
"የሌሶቶ ሎቲ"
],
"LTL": [
"LTL",
"ሊቱዌንያን ሊታስ"
],
"LVL": [
"LVL",
"የላቲቫ ላትስ"
],
"LYD": [
"LYD",
"የሊቢያ ዲናር"
],
"MAD": [
"MAD",
"የሞሮኮ ዲርሀም"
],
"MDL": [
"MDL",
"ሞልዶቫን ሊኡ"
],
"MGA": [
"MGA",
"የማደጋስካር ማላጋስይ አሪያርይ"
],
"MKD": [
"MKD",
"የሜቆድንያ ዲናር"
],
"MMK": [
"MMK",
"የማያናማር ክያት"
],
"MNT": [
"MNT",
"የሞንጎሊያን ቱግሪክ"
],
"MOP": [
"MOP",
"የማካኔዝ ፓታካ"
],
"MRO": [
"MRO",
"የሞሪቴኒያ ኦውጉያ (19732017)"
],
"MRU": [
"MRU",
"የሞሪቴኒያ ኦውጉያ"
],
"MUR": [
"MUR",
"የሞሪሸስ ሩፒ"
],
"MVR": [
"MVR",
"የማልዲቫ ሩፊያ"
],
"MWK": [
"MWK",
"የማላዊ ኩዋቻ"
],
"MXN": [
"MX$",
"የሜክሲኮ ፔሶ"
],
"MYR": [
"MYR",
"የማሌዥያ ሪንጊት"
],
"MZN": [
"MZN",
"የሞዛምቢክ ሜቲካል"
],
"NAD": [
"NAD",
"የናሚቢያ ዶላር"
],
"NGN": [
"NGN",
"የናይጄሪያ ናይራ"
],
"NIO": [
"NIO",
"የኒካራጓ ኮርዶባ"
],
"NOK": [
"NOK",
"የኖርዌይ ክሮን"
],
"NPR": [
"NPR",
"የኔፓል ሩፒ"
],
"NZD": [
"NZ$",
"የኒውዚላንድ ዶላር"
],
"OMR": [
"OMR",
"የኦማን ሪአል"
],
"PAB": [
"PAB",
"ፓናማኒአን ባልቦአ"
],
"PEN": [
"PEN",
"የፔሩቪያ ሶል"
],
"PGK": [
"PGK",
"የፓፕዋ ኒው ጊኒ ኪና"
],
"PHP": [
"PHP",
"የፊሊፒንስ ፔሶ"
],
"PKR": [
"PKR",
"የፓኪስታን ሩፒ"
],
"PLN": [
"PLN",
"የፖላንድ ዝሎቲ"
],
"PYG": [
"PYG",
"የፓራጓይ ጉአራኒ"
],
"QAR": [
"QAR",
"የኳታር ሪአል"
],
"RON": [
"RON",
"የሮማኒያ ለው"
],
"RSD": [
"RSD",
"የሰርቢያ ዲናር"
],
"RUB": [
"RUB",
"የሩስያ ሩብል"
],
"RWF": [
"RWF",
"የሩዋንዳ ፍራንክ"
],
"SAR": [
"SAR",
"የሳውዲ ሪያል"
],
"SBD": [
"SBD",
"የሰለሞን ደሴቶች ዶላር"
],
"SCR": [
"SCR",
"የሲሼል ሩፒ"
],
"SDG": [
"SDG",
"የሱዳን ፓውንድ"
],
"SDP": [
"SDP",
"የሱዳን ፓውንድ (19571998)"
],
"SEK": [
"SEK",
"የስዊድን ክሮና"
],
"SGD": [
"SGD",
"የሲንጋፖር ዶላር"
],
"SHP": [
"SHP",
"የሴይንት ሔሌና ፓውንድ"
],
"SLL": [
"SLL",
"የሴራሊዎን ሊዎን"
],
"SOS": [
"SOS",
"የሶማሌ ሺሊንግ"
],
"SRD": [
"SRD",
"የሰርናሜዝ ዶላር"
],
"SSP": [
"SSP",
"የደቡብ ሱዳን ፓውንድ"
],
"STD": [
"STD",
"የሳኦ ቶሜ እና ፕሪንሲፔ ዶብራ (19772017)"
],
"STN": [
"STN",
"የሳኦ ቶሜ እና ፕሪንሲፔ ዶብራ"
],
"SYP": [
"SYP",
"የሲሪያ ፓውንድ"
],
"SZL": [
"SZL",
"የስዋዚላንድ ሊላንገኒ"
],
"THB": [
"฿",
"የታይላንድ ባህት"
],
"TJS": [
"TJS",
"የታጂክስታን ሶሞኒ"
],
"TMT": [
"TMT",
"ቱርክሜኒስታኒ ማናት"
],
"TND": [
"TND",
"የቱኒዚያ ዲናር"
],
"TOP": [
"TOP",
"ቶንጋን ፓ’አንጋ"
],
"TRY": [
"TRY",
"የቱርክ ሊራ"
],
"TTD": [
"TTD",
"የትሪንዳድ እና ቶቤጎዶላር"
],
"TWD": [
"NT$",
"የአዲሷ ታይዋን ዶላር"
],
"TZS": [
"TZS",
"የታንዛኒያ ሺሊንግ"
],
"UAH": [
"UAH",
"የዩክሬን ሀሪይቭኒአ"
],
"UGX": [
"UGX",
"የዩጋንዳ ሺሊንግ"
],
"USD": [
"US$",
"የአሜሪካን ዶላር"
],
"UYU": [
"UYU",
"የኡራጓይ ፔሶ"
],
"UZS": [
"UZS",
"የኡዝፔኪስታን ሶም"
],
"VEF": [
"VEF",
"የቬንዝዌላ ቦሊቫር (20082018)"
],
"VES": [
"VES",
"የቬንዝዌላ-ቦሊቫር"
],
"VND": [
"₫",
"የቭየትናም ዶንግ"
],
"VUV": [
"VUV",
"የቫንዋንቱ ቫቱ"
],
"WST": [
"WST",
"ሳሞአን ታላ"
],
"XAF": [
"FCFA",
"የመካከለኛው አፍሪካ ሴፋ ፍራንክ"
],
"XCD": [
"EC$",
"የምዕራብ ካሪብያን ዶላር"
],
"XOF": [
"CFA",
"የምዕራብ አፍሪካ ሴፋ ፍራንክ"
],
"XPF": [
"CFPF",
"ሲ ኤፍ ፒ ፍራንክ"
],
"YER": [
"YER",
"የየመን ሪአል"
],
"ZAR": [
"ZAR",
"የደቡብ አፍሪካ ራንድ"
],
"ZMK": [
"ZMK",
"የዛምቢያ ክዋቻ (19682012)"
],
"ZMW": [
"ZMW",
"የዛምቢያ ክዋቻ"
],
"ZWD": [
"ZWD",
"የዚምቧቡዌ ዶላር"
]
}
}

View File

@@ -0,0 +1,996 @@
{
"Names": {
"ADP": [
"ADP",
"بيستا أندوري"
],
"AED": [
"د.إ.",
"درهم إماراتي"
],
"AFA": [
"AFA",
"أفغاني - 1927-2002"
],
"AFN": [
"AFN",
"أفغاني"
],
"ALL": [
"ALL",
"ليك ألباني"
],
"AMD": [
"AMD",
"درام أرميني"
],
"ANG": [
"ANG",
"غيلدر أنتيلي هولندي"
],
"AOA": [
"AOA",
"كوانزا أنغولي"
],
"AOK": [
"AOK",
"كوانزا أنجولي - 1977-1990"
],
"AON": [
"AON",
"كوانزا أنجولي جديدة - 1990-2000"
],
"AOR": [
"AOR",
"كوانزا أنجولي معدلة - 1995 - 1999"
],
"ARA": [
"ARA",
"استرال أرجنتيني"
],
"ARP": [
"ARP",
"بيزو أرجنتيني - 1983-1985"
],
"ARS": [
"ARS",
"بيزو أرجنتيني"
],
"ATS": [
"ATS",
"شلن نمساوي"
],
"AUD": [
"AU$",
"دولار أسترالي"
],
"AWG": [
"AWG",
"فلورن أروبي"
],
"AZM": [
"AZM",
"مانات أذريبجاني"
],
"AZN": [
"AZN",
"مانات أذربيجان"
],
"BAD": [
"BAD",
"دينار البوسنة والهرسك"
],
"BAM": [
"BAM",
"مارك البوسنة والهرسك قابل للتحويل"
],
"BBD": [
"BBD",
"دولار بربادوسي"
],
"BDT": [
"BDT",
"تاكا بنغلاديشي"
],
"BEC": [
"BEC",
"فرنك بلجيكي قابل للتحويل"
],
"BEF": [
"BEF",
"فرنك بلجيكي"
],
"BEL": [
"BEL",
"فرنك بلجيكي مالي"
],
"BGN": [
"BGN",
"ليف بلغاري"
],
"BHD": [
"د.ب.",
"دينار بحريني"
],
"BIF": [
"BIF",
"فرنك بروندي"
],
"BMD": [
"BMD",
"دولار برمودي"
],
"BND": [
"BND",
"دولار بروناي"
],
"BOB": [
"BOB",
"بوليفيانو بوليفي"
],
"BOP": [
"BOP",
"بيزو بوليفي"
],
"BOV": [
"BOV",
"مفدول بوليفي"
],
"BRB": [
"BRB",
"نوفو كروزايرو برازيلي - 1967-1986"
],
"BRC": [
"BRC",
"كروزادو برازيلي"
],
"BRE": [
"BRE",
"كروزايرو برازيلي - 1990-1993"
],
"BRL": [
"R$",
"ريال برازيلي"
],
"BSD": [
"BSD",
"دولار باهامي"
],
"BTN": [
"BTN",
"نولتوم بوتاني"
],
"BUK": [
"BUK",
"كيات بورمي"
],
"BWP": [
"BWP",
"بولا بتسواني"
],
"BYB": [
"BYB",
"روبل بيلاروسي جديد - 1994-1999"
],
"BYN": [
"BYN",
"روبل بيلاروسي"
],
"BYR": [
"BYR",
"روبل بيلاروسي (٢٠٠٠–٢٠١٦)"
],
"BZD": [
"BZD",
"دولار بليزي"
],
"CAD": [
"CA$",
"دولار كندي"
],
"CDF": [
"CDF",
"فرنك كونغولي"
],
"CHF": [
"CHF",
"فرنك سويسري"
],
"CLP": [
"CLP",
"بيزو تشيلي"
],
"CNH": [
"CNH",
"يوان صيني (في الخارج)"
],
"CNY": [
"CN¥",
"يوان صيني"
],
"COP": [
"COP",
"بيزو كولومبي"
],
"CRC": [
"CRC",
"كولن كوستاريكي"
],
"CSD": [
"CSD",
"دينار صربي قديم"
],
"CSK": [
"CSK",
"كرونة تشيكوسلوفاكيا"
],
"CUC": [
"CUC",
"بيزو كوبي قابل للتحويل"
],
"CUP": [
"CUP",
"بيزو كوبي"
],
"CVE": [
"CVE",
"اسكودو الرأس الأخضر"
],
"CYP": [
"CYP",
"جنيه قبرصي"
],
"CZK": [
"CZK",
"كرونة تشيكية"
],
"DDM": [
"DDM",
"أوستمارك ألماني شرقي"
],
"DEM": [
"DEM",
"مارك ألماني"
],
"DJF": [
"DJF",
"فرنك جيبوتي"
],
"DKK": [
"DKK",
"كرونة دنماركية"
],
"DOP": [
"DOP",
"بيزو الدومنيكان"
],
"DZD": [
"د.ج.",
"دينار جزائري"
],
"EEK": [
"EEK",
"كرونة استونية"
],
"EGP": [
"ج.م.",
"جنيه مصري"
],
"ERN": [
"ERN",
"ناكفا أريتري"
],
"ESP": [
"ESP",
"بيزيتا إسباني"
],
"ETB": [
"ETB",
"بير أثيوبي"
],
"EUR": [
"€",
"يورو"
],
"FIM": [
"FIM",
"ماركا فنلندي"
],
"FJD": [
"FJD",
"دولار فيجي"
],
"FKP": [
"FKP",
"جنيه جزر فوكلاند"
],
"FRF": [
"FRF",
"فرنك فرنسي"
],
"GBP": [
"UK£",
"جنيه إسترليني"
],
"GEL": [
"GEL",
"لارى جورجي"
],
"GHC": [
"GHC",
"سيدي غاني"
],
"GHS": [
"GHS",
"سيدي غانا"
],
"GIP": [
"GIP",
"جنيه جبل طارق"
],
"GMD": [
"GMD",
"دلاسي غامبي"
],
"GNF": [
"GNF",
"فرنك غينيا"
],
"GNS": [
"GNS",
"سيلي غينيا"
],
"GQE": [
"GQE",
"اكويل جونينا غينيا الاستوائيّة"
],
"GRD": [
"GRD",
"دراخما يوناني"
],
"GTQ": [
"GTQ",
"كوتزال غواتيمالا"
],
"GWE": [
"GWE",
"اسكود برتغالي غينيا"
],
"GWP": [
"GWP",
"بيزو غينيا بيساو"
],
"GYD": [
"GYD",
"دولار غيانا"
],
"HKD": [
"HK$",
"دولار هونغ كونغ"
],
"HNL": [
"HNL",
"ليمبيرا هنداروس"
],
"HRD": [
"HRD",
"دينار كرواتي"
],
"HRK": [
"HRK",
"كونا كرواتي"
],
"HTG": [
"HTG",
"جوردى هايتي"
],
"HUF": [
"HUF",
"فورينت هنغاري"
],
"IDR": [
"IDR",
"روبية إندونيسية"
],
"IEP": [
"IEP",
"جنيه إيرلندي"
],
"ILP": [
"ILP",
"جنيه إسرائيلي"
],
"ILS": [
"₪",
"شيكل إسرائيلي جديد"
],
"INR": [
"₹",
"روبية هندي"
],
"IQD": [
"د.ع.",
"دينار عراقي"
],
"IRR": [
"ر.إ.",
"ريال إيراني"
],
"ISK": [
"ISK",
"كرونة أيسلندية"
],
"ITL": [
"ITL",
"ليرة إيطالية"
],
"JMD": [
"JMD",
"دولار جامايكي"
],
"JOD": [
"د.أ.",
"دينار أردني"
],
"JPY": [
"JP¥",
"ين ياباني"
],
"KES": [
"KES",
"شلن كينيي"
],
"KGS": [
"KGS",
"سوم قيرغستاني"
],
"KHR": [
"KHR",
"رييال كمبودي"
],
"KMF": [
"KMF",
"فرنك جزر القمر"
],
"KPW": [
"KPW",
"وون كوريا الشمالية"
],
"KRW": [
"₩",
"وون كوريا الجنوبية"
],
"KWD": [
"د.ك.",
"دينار كويتي"
],
"KYD": [
"KYD",
"دولار جزر كيمن"
],
"KZT": [
"KZT",
"تينغ كازاخستاني"
],
"LAK": [
"LAK",
"كيب لاوسي"
],
"LBP": [
"ل.ل.",
"جنيه لبناني"
],
"LKR": [
"LKR",
"روبية سريلانكية"
],
"LRD": [
"LRD",
"دولار ليبيري"
],
"LSL": [
"LSL",
"لوتي ليسوتو"
],
"LTL": [
"LTL",
"ليتا ليتوانية"
],
"LTT": [
"LTT",
"تالوناس ليتواني"
],
"LUC": [
"LUC",
"فرنك لوكسمبرج قابل للتحويل"
],
"LUF": [
"LUF",
"فرنك لوكسمبرج"
],
"LUL": [
"LUL",
"فرنك لوكسمبرج المالي"
],
"LVL": [
"LVL",
"لاتس لاتفيا"
],
"LVR": [
"LVR",
"روبل لاتفيا"
],
"LYD": [
"د.ل.",
"دينار ليبي"
],
"MAD": [
"د.م.",
"درهم مغربي"
],
"MAF": [
"MAF",
"فرنك مغربي"
],
"MDL": [
"MDL",
"ليو مولدوفي"
],
"MGA": [
"MGA",
"أرياري مدغشقر"
],
"MGF": [
"MGF",
"فرنك مدغشقر"
],
"MKD": [
"MKD",
"دينار مقدوني"
],
"MLF": [
"MLF",
"فرنك مالي"
],
"MMK": [
"MMK",
"كيات ميانمار"
],
"MNT": [
"MNT",
"توغروغ منغولي"
],
"MOP": [
"MOP",
"باتاكا ماكاوي"
],
"MRO": [
"MRO",
"أوقية موريتانية - 1973-2017"
],
"MRU": [
"أ.م.",
"أوقية موريتانية"
],
"MTL": [
"MTL",
"ليرة مالطية"
],
"MTP": [
"MTP",
"جنيه مالطي"
],
"MUR": [
"MUR",
"روبية موريشيوسية"
],
"MVR": [
"MVR",
"روفيه جزر المالديف"
],
"MWK": [
"MWK",
"كواشا مالاوي"
],
"MXN": [
"MX$",
"بيزو مكسيكي"
],
"MXP": [
"MXP",
"بيزو فضي مكسيكي - 1861-1992"
],
"MYR": [
"MYR",
"رينغيت ماليزي"
],
"MZE": [
"MZE",
"اسكود موزمبيقي"
],
"MZN": [
"MZN",
"متكال موزمبيقي"
],
"NAD": [
"NAD",
"دولار ناميبي"
],
"NGN": [
"NGN",
"نايرا نيجيري"
],
"NIC": [
"NIC",
"كوردوبة نيكاراجوا"
],
"NIO": [
"NIO",
"قرطبة نيكاراغوا"
],
"NLG": [
"NLG",
"جلدر هولندي"
],
"NOK": [
"NOK",
"كرونة نرويجية"
],
"NPR": [
"NPR",
"روبية نيبالي"
],
"NZD": [
"NZ$",
"دولار نيوزيلندي"
],
"OMR": [
"ر.ع.",
"ريال عماني"
],
"PAB": [
"PAB",
"بالبوا بنمي"
],
"PEN": [
"PEN",
"سول بيروفي"
],
"PGK": [
"PGK",
"كينا بابوا غينيا الجديدة"
],
"PHP": [
"PHP",
"بيزو فلبيني"
],
"PKR": [
"PKR",
"روبية باكستاني"
],
"PLN": [
"PLN",
"زلوتي بولندي"
],
"PLZ": [
"PLZ",
"زلوتي بولندي - 1950-1995"
],
"PTE": [
"PTE",
"اسكود برتغالي"
],
"PYG": [
"PYG",
"غواراني باراغواي"
],
"QAR": [
"ر.ق.",
"ريال قطري"
],
"RHD": [
"RHD",
"دولار روديسي"
],
"ROL": [
"ROL",
"ليو روماني قديم"
],
"RON": [
"RON",
"ليو روماني"
],
"RSD": [
"RSD",
"دينار صربي"
],
"RUB": [
"RUB",
"روبل روسي"
],
"RUR": [
"RUR",
"روبل روسي - 1991-1998"
],
"RWF": [
"RWF",
"فرنك رواندي"
],
"SAR": [
"ر.س.",
"ريال سعودي"
],
"SBD": [
"SBD",
"دولار جزر سليمان"
],
"SCR": [
"SCR",
"روبية سيشيلية"
],
"SDD": [
"د.س.",
"دينار سوداني"
],
"SDG": [
"ج.س.",
"جنيه سوداني"
],
"SDP": [
"SDP",
"جنيه سوداني قديم"
],
"SEK": [
"SEK",
"كرونة سويدية"
],
"SGD": [
"SGD",
"دولار سنغافوري"
],
"SHP": [
"SHP",
"جنيه سانت هيلين"
],
"SIT": [
"SIT",
"تولار سلوفيني"
],
"SKK": [
"SKK",
"كرونة سلوفاكية"
],
"SLL": [
"SLL",
"ليون سيراليوني"
],
"SOS": [
"SOS",
"شلن صومالي"
],
"SRD": [
"SRD",
"دولار سورينامي"
],
"SRG": [
"SRG",
"جلدر سورينامي"
],
"SSP": [
"SSP",
"جنيه جنوب السودان"
],
"STD": [
"STD",
"دوبرا ساو تومي وبرينسيبي - 1977-2017"
],
"STN": [
"STN",
"دوبرا ساو تومي وبرينسيبي"
],
"SUR": [
"SUR",
"روبل سوفيتي"
],
"SVC": [
"SVC",
"كولون سلفادوري"
],
"SYP": [
"ل.س.",
"ليرة سورية"
],
"SZL": [
"SZL",
"ليلانجيني سوازيلندي"
],
"THB": [
"฿",
"باخت تايلاندي"
],
"TJR": [
"TJR",
"روبل طاجيكستاني"
],
"TJS": [
"TJS",
"سوموني طاجيكستاني"
],
"TMM": [
"TMM",
"مانات تركمنستاني"
],
"TMT": [
"TMT",
"مانات تركمانستان"
],
"TND": [
"د.ت.",
"دينار تونسي"
],
"TOP": [
"TOP",
"بانغا تونغا"
],
"TPE": [
"TPE",
"اسكود تيموري"
],
"TRL": [
"TRL",
"ليرة تركي"
],
"TRY": [
"TRY",
"ليرة تركية"
],
"TTD": [
"TTD",
"دولار ترينداد وتوباغو"
],
"TWD": [
"NT$",
"دولار تايواني"
],
"TZS": [
"TZS",
"شلن تنزاني"
],
"UAH": [
"UAH",
"هريفنيا أوكراني"
],
"UGS": [
"UGS",
"شلن أوغندي - 1966-1987"
],
"UGX": [
"UGX",
"شلن أوغندي"
],
"USD": [
"US$",
"دولار أمريكي"
],
"USN": [
"USN",
"دولار أمريكي (اليوم التالي)"
],
"USS": [
"USS",
"دولار أمريكي (نفس اليوم)"
],
"UYP": [
"UYP",
"بيزو أوروجواي - 1975-1993"
],
"UYU": [
"UYU",
"بيزو اوروغواي"
],
"UZS": [
"UZS",
"سوم أوزبكستاني"
],
"VEB": [
"VEB",
"بوليفار فنزويلي - 1871-2008"
],
"VEF": [
"VEF",
"بوليفار فنزويلي - 20082018"
],
"VES": [
"VES",
"بوليفار فنزويلي"
],
"VND": [
"₫",
"دونج فيتنامي"
],
"VUV": [
"VUV",
"فاتو فانواتو"
],
"WST": [
"WST",
"تالا ساموا"
],
"XAF": [
"FCFA",
"فرنك وسط أفريقي"
],
"XCD": [
"EC$",
"دولار شرق الكاريبي"
],
"XEU": [
"XEU",
"وحدة النقد الأوروبية"
],
"XFO": [
"XFO",
"فرنك فرنسي ذهبي"
],
"XFU": [
"XFU",
"(UIC)فرنك فرنسي"
],
"XOF": [
"CFA",
"فرنك غرب أفريقي"
],
"XPF": [
"CFPF",
"فرنك سي إف بي"
],
"YDD": [
"YDD",
"دينار يمني"
],
"YER": [
"ر.ي.",
"ريال يمني"
],
"YUD": [
"YUD",
"دينار يوغسلافي"
],
"YUN": [
"YUN",
"دينار يوغسلافي قابل للتحويل"
],
"ZAL": [
"ZAL",
"راند جنوب أفريقيا -مالي"
],
"ZAR": [
"ZAR",
"راند جنوب أفريقيا"
],
"ZMK": [
"ZMK",
"كواشا زامبي - 1968-2012"
],
"ZMW": [
"ZMW",
"كواشا زامبي"
],
"ZRN": [
"ZRN",
"زائير زائيري جديد"
],
"ZRZ": [
"ZRZ",
"زائير زائيري"
],
"ZWD": [
"ZWD",
"دولار زمبابوي"
],
"ZWL": [
"ZWL",
"دولار زمبابوي 2009"
]
}
}

View File

@@ -0,0 +1,8 @@
{
"Names": {
"DJF": [
"Fdj",
"فرنك جيبوتي"
]
}
}

View File

@@ -0,0 +1,8 @@
{
"Names": {
"ERN": [
"Nfk",
"ناكفا أريتري"
]
}
}

View File

@@ -0,0 +1,8 @@
{
"Names": {
"KMF": [
"CF",
"فرنك جزر القمر"
]
}
}

View File

@@ -0,0 +1,8 @@
{
"Names": {
"SDG": [
"SDG",
"جنيه سوداني"
]
}
}

View File

@@ -0,0 +1,8 @@
{
"Names": {
"SOS": [
"S",
"شلن صومالي"
]
}
}

View File

@@ -0,0 +1,12 @@
{
"Names": {
"GBP": [
"GB£",
"جنيه إسترليني"
],
"SSP": [
"£",
"جنيه جنوب السودان"
]
}
}

View File

@@ -0,0 +1,640 @@
{
"Names": {
"AED": [
"AED",
"সংযুক্ত আৰব আমিৰাত ডিৰহেম"
],
"AFN": [
"AFN",
"আফগান আফগানী"
],
"ALL": [
"ALL",
"আলবেনীয় লেক"
],
"AMD": [
"AMD",
"আৰ্মেনিয়ান ড্ৰাম"
],
"ANG": [
"ANG",
"নেডাৰলেণ্ডছ এণ্টিলিয়েন গিল্ডাৰ"
],
"AOA": [
"AOA",
"এংগোলান কোৱাঞ্জা"
],
"ARS": [
"ARS",
"আৰ্জেণ্টাইন পেছো"
],
"AUD": [
"A$",
"অষ্ট্ৰেলিয়ান ডলাৰ"
],
"AWG": [
"AWG",
"আৰুবান ফ্ল’ৰিন"
],
"AZN": [
"AZN",
"আজেৰবাইজানী মানাত"
],
"BAM": [
"BAM",
"ব’ছনিয়া আৰু হাৰ্জেগ’ভিনা কনভাৰ্টিব্‌ল মাৰ্ক"
],
"BBD": [
"BBD",
"বাৰ্বাডিয়ান ডলাৰ"
],
"BDT": [
"BDT",
"বাংলাদেশী টাকা"
],
"BGN": [
"BGN",
"বুলগেৰীয় লেভ"
],
"BHD": [
"BHD",
"বাহৰেইনী ডিনাৰ"
],
"BIF": [
"BIF",
"বুৰুণ্ডিয়ান ফ্ৰেংক"
],
"BMD": [
"BMD",
"বাৰ্মুডান ডলাৰ"
],
"BND": [
"BND",
"ব্ৰুনেই ডলাৰ"
],
"BOB": [
"BOB",
"বলিভিয়ান বলিভিয়ানো"
],
"BRL": [
"R$",
"ব্ৰাজিলিয়ান ৰিয়েল"
],
"BSD": [
"BSD",
"বাহামিয়ান ডলাৰ"
],
"BTN": [
"BTN",
"ভুটানী নংগলট্ৰাম"
],
"BWP": [
"BWP",
"ব’টচোৱানান পুলা"
],
"BYN": [
"BYN",
"বেলাৰুছীয় ৰুবেল"
],
"BZD": [
"BZD",
"বেলিজ ডলাৰ"
],
"CAD": [
"CA$",
"কানাডিয়ান ডলাৰ"
],
"CDF": [
"CDF",
"কংগো ফ্ৰেংক"
],
"CHF": [
"CHF",
"চুইছ ফ্ৰেংক"
],
"CLP": [
"CLP",
"চিলিয়ান পেছো"
],
"CNH": [
"CNH",
"চীনা ইউৱান (অফশ্ব’ৰ)"
],
"CNY": [
"CN¥",
"চীনা ইউৱান"
],
"COP": [
"COP",
"কলম্বিয়ান পেছো"
],
"CRC": [
"CRC",
"কোষ্টা ৰিকান কোলন"
],
"CUC": [
"CUC",
"কিউবান ৰূপান্তৰযোগ্য পেছো"
],
"CUP": [
"CUP",
"কিউবান পেছো"
],
"CVE": [
"CVE",
"কেপ ভাৰ্দে এছকুডো"
],
"CZK": [
"CZK",
"চেক কোৰুনা"
],
"DJF": [
"DJF",
"জিবুটি ফ্ৰেংক"
],
"DKK": [
"DKK",
"ডেনিচ ক্ৰোন"
],
"DOP": [
"DOP",
"ড’মিনিকান পেছো"
],
"DZD": [
"DZD",
"আলজেৰীয় ডিনাৰ"
],
"EGP": [
"EGP",
"ইজিপ্তৰ পাউণ্ড"
],
"ERN": [
"ERN",
"এৰিট্ৰিয়ন নাক্‌ফা"
],
"ETB": [
"ETB",
"ইথিঅ’পিয়ান বিৰ"
],
"EUR": [
"€",
"ইউৰো"
],
"FJD": [
"FJD",
"ফিজিয়ান ডলাৰ"
],
"FKP": [
"FKP",
"ফকলেণ্ড দ্বীপপুঞ্জৰ পাউণ্ড"
],
"GBP": [
"£",
"ব্ৰিটিছ পাউণ্ড"
],
"GEL": [
"GEL",
"জৰ্জিয়ান লাৰি"
],
"GHS": [
"GHS",
"ঘানাৰ চেডি"
],
"GIP": [
"GIP",
"জিব্ৰাল্টৰ পাউণ্ড"
],
"GMD": [
"GMD",
"গাম্বিয়া ডালাছি"
],
"GNF": [
"GNF",
"গিনি ফ্ৰেংক"
],
"GTQ": [
"GTQ",
"গুৱাটেমালা কুৱেৎজাল"
],
"GYD": [
"GYD",
"গায়ানিজ ডলাৰ"
],
"HKD": [
"HK$",
"হং কং ডলাৰ"
],
"HNL": [
"HNL",
"হোন্দুৰান লেম্পিৰা"
],
"HRK": [
"HRK",
"ক্ৰোৱেছিয়ান কুনা"
],
"HTG": [
"HTG",
"হাইটিয়ান গৌৰ্ড"
],
"HUF": [
"HUF",
"হাংগেৰীয়ান ফ’ৰিণ্ট"
],
"IDR": [
"IDR",
"ইণ্ডোনেচিয়ান ৰুপিয়াহ"
],
"ILS": [
"₪",
"ইজৰাইলী নিউ শ্বেকেল"
],
"INR": [
"₹",
"ভাৰতীয় ৰুপী"
],
"IQD": [
"IQD",
"ইৰাকী ডিনাৰ"
],
"IRR": [
"IRR",
"ইৰানীয়ান ৰিয়েল"
],
"ISK": [
"ISK",
"আইচলেণ্ডিক ক্ৰোনা"
],
"JMD": [
"JMD",
"জামাইকান ডলাৰ"
],
"JOD": [
"JOD",
"জৰ্ডানিয়ান ডিনাৰ"
],
"JPY": [
"JP¥",
"জাপানী য়েন"
],
"KES": [
"KES",
"কেনিয়ান শ্বিলিং"
],
"KGS": [
"KGS",
"কিৰ্গিস্তানী ছোম"
],
"KHR": [
"KHR",
"কেম্ব’ডিয়ান ৰিয়েল"
],
"KMF": [
"KMF",
"ক’মোৰিয়ান ফ্ৰেংক"
],
"KPW": [
"KPW",
"উত্তৰ কোৰিয়াৰ ওৱান"
],
"KRW": [
"₩",
"দক্ষিণ কোৰিয়াৰ ওৱান"
],
"KWD": [
"KWD",
"কুৱেইটি ডিনাৰ"
],
"KYD": [
"KYD",
"কেইমেন দ্বীপপুঞ্জৰ ডলাৰ"
],
"KZT": [
"KZT",
"কাজাখস্তানী তেঞ্জ"
],
"LAK": [
"LAK",
"লাওচিয়ান কিপ"
],
"LBP": [
"LBP",
"লেবানীজ পাউণ্ড"
],
"LKR": [
"LKR",
"শ্ৰীলংকান ৰুপী"
],
"LRD": [
"LRD",
"লাইবেৰিয়ান ডলাৰ"
],
"LYD": [
"LYD",
"লিবিয়ান ডিনাৰ"
],
"MAD": [
"MAD",
"মৰোক্কান ডিৰহাম"
],
"MDL": [
"MDL",
"মোলডোভান লেউ"
],
"MGA": [
"MGA",
"মালাগাছী এৰিয়াৰী"
],
"MKD": [
"MKD",
"মেচিডোনীয় ডেনাৰ"
],
"MMK": [
"MMK",
"ম্যানমাৰ কিয়াট"
],
"MNT": [
"MNT",
"মঙ্গোলিয়ান টুৰ্গিক"
],
"MOP": [
"MOP",
"মেকানীজ পাটাকা"
],
"MRO": [
"MRO",
"ম’ৰিটেনিয়ান ঔগুইয়া (19732017)"
],
"MRU": [
"MRU",
"ম’ৰিটেনিয়ান ঔগুইয়া"
],
"MUR": [
"MUR",
"মৰিচিয়ান ৰুপী"
],
"MVR": [
"MVR",
"মালডিভিয়ান ৰুফিয়া"
],
"MWK": [
"MWK",
"মালাউইয়ান কোৱাচা"
],
"MXN": [
"MX$",
"মেক্সিকান পেছো"
],
"MYR": [
"MYR",
"মালায়েচিয়ান ৰিংগিট"
],
"MZN": [
"MZN",
"মোজাম্বিকান মেটিকল"
],
"NAD": [
"NAD",
"নামিবিয়ান ডলাৰ"
],
"NGN": [
"NGN",
"নাইজেৰিয়ান নাইৰা"
],
"NIO": [
"NIO",
"নিকাৰাগুৱান কোৰ্ডোবা"
],
"NOK": [
"NOK",
"নৰৱেজিয়ান ক্ৰোন"
],
"NPR": [
"NPR",
"নেপালী ৰুপী"
],
"NZD": [
"NZ$",
"নিউজিলেণ্ড ডলাৰ"
],
"OMR": [
"OMR",
"ওমানি ৰিয়েল"
],
"PAB": [
"PAB",
"পানামেনিয়ান বাল্বোৱা"
],
"PEN": [
"PEN",
"পেৰুভিয়ান ছ’ল"
],
"PGK": [
"PGK",
"পাপুৱা নিউ গিনি কিনা"
],
"PHP": [
"PHP",
"ফিলিপিন পেইছ’"
],
"PKR": [
"PKR",
"পাকিস্তানী ৰুপী"
],
"PLN": [
"PLN",
"প’লিচ জোল্টী"
],
"PYG": [
"PYG",
"পাৰাগুয়ান গুৱাৰানি"
],
"QAR": [
"QAR",
"কাটাৰি ৰিয়েল"
],
"RON": [
"RON",
"ৰোমানীয় লেউ"
],
"RSD": [
"RSD",
"চাৰ্বিয়ান ডিনাৰ"
],
"RUB": [
"RUB",
"ৰাছিয়ান ৰুব্‌ল"
],
"RWF": [
"RWF",
"ৰোৱান্দান ফ্ৰেংক"
],
"SAR": [
"SAR",
"চৌডি ৰিয়েল"
],
"SBD": [
"SBD",
"চোলোমোন দ্বীপপুঞ্জৰ ডলাৰ"
],
"SCR": [
"SCR",
"ছেচেলৱা ৰুপী"
],
"SDG": [
"SDG",
"চুডানী পাউণ্ড"
],
"SEK": [
"SEK",
"চুইডিছ ক্ৰোনা"
],
"SGD": [
"SGD",
"ছিংগাপুৰ ডলাৰ"
],
"SHP": [
"SHP",
"ছেইণ্ট হেলেনা পাউণ্ড"
],
"SLL": [
"SLL",
"চিয়েৰা লিঅ’নৰ লিঅ’ন"
],
"SOS": [
"SOS",
"চোমালি শ্বিলিং"
],
"SRD": [
"SRD",
"ছুৰিনামী ডলাৰ"
],
"SSP": [
"SSP",
"দক্ষিণ চুডানীজ পাউণ্ড"
],
"STD": [
"STD",
"চাও টোমে আৰু প্ৰিনচিপে ডোব্‌ৰা (19772017)"
],
"STN": [
"STN",
"চাও টোমে আৰু প্ৰিনচিপে ডোব্‌ৰা"
],
"SYP": [
"SYP",
"চিৰিয়ান পাউণ্ড"
],
"SZL": [
"SZL",
"স্বাজি লিলেংগেনি"
],
"THB": [
"THB",
"থাই বাত"
],
"TJS": [
"TJS",
"তাজিকিস্তানী ছোমনি"
],
"TMT": [
"TMT",
"তুৰ্কমেনিস্তানী মানাত"
],
"TND": [
"TND",
"টুনিচিয়ান ডিনাৰ"
],
"TOP": [
"TOP",
"টংগান পাআংগা"
],
"TRY": [
"TRY",
"তুৰ্কীৰ লিৰা"
],
"TTD": [
"TTD",
"ট্ৰিনিডাড আৰু টোবাগো ডলাৰ"
],
"TWD": [
"NT$",
"নিউ টাইৱান ডলাৰ"
],
"TZS": [
"TZS",
"টানজানিয়ান শ্বিলিং"
],
"UAH": [
"UAH",
"ইউক্ৰেইনীয় হৃভনিয়া"
],
"UGX": [
"UGX",
"উগাণ্ডান শ্বিলিং"
],
"USD": [
"US$",
"ইউ. এছ. ডলাৰ"
],
"UYU": [
"UYU",
"উৰুগুৱেয়ান পেছো"
],
"UZS": [
"UZS",
"উজবেকিস্তানী ছোম"
],
"VEB": [
"VEB",
"ভেনিজুৱেলান বলিভাৰ (18712008)"
],
"VEF": [
"VEF",
"ভেনিজুৱেলান বলিভাৰ (20082018)"
],
"VES": [
"VES",
"ভেনিজুৱেলান বলিভাৰ"
],
"VND": [
"₫",
"ভিয়েটনামীজ ডং"
],
"VUV": [
"VUV",
"ভানাটুৰ ভাটু"
],
"WST": [
"WST",
"ছামোৱান টালা"
],
"XAF": [
"FCFA",
"মধ্য আফ্ৰিকান CFA ফ্ৰেংক"
],
"XCD": [
"EC$",
"ইষ্ট কেৰিবিয়ান ডলাৰ"
],
"XOF": [
"CFA",
"পশ্চিম আফ্ৰিকান CFA ফ্ৰেংক"
],
"XPF": [
"CFPF",
"CFP ফ্ৰেংক"
],
"YER": [
"YER",
"য়েমেনী ৰিয়েল"
],
"ZAR": [
"ZAR",
"দক্ষিণ আফ্ৰিকাৰ ৰাণ্ড"
],
"ZMW": [
"ZMW",
"জাম্বিয়ান কোৱাচা"
]
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
{
"Names": {
"AZN": [
"₼",
"AZN"
]
}
}

View File

@@ -0,0 +1,640 @@
{
"Names": {
"AED": [
"AED",
"дырхам ААЭ"
],
"AFN": [
"AFN",
"афганскі афгані"
],
"ALL": [
"ALL",
"албанскі лек"
],
"AMD": [
"AMD",
"армянскі драм"
],
"ANG": [
"ANG",
"нідэрландскі антыльскі гульдэн"
],
"AOA": [
"AOA",
"ангольская кванза"
],
"ARS": [
"ARS",
"аргенцінскае песа"
],
"AUD": [
"A$",
"аўстралійскі долар"
],
"AWG": [
"AWG",
"арубанскі фларын"
],
"AZN": [
"AZN",
"азербайджанскі манат"
],
"BAM": [
"BAM",
"канверсоўная марка Босніі і Герцагавіны"
],
"BBD": [
"BBD",
"барбадаскі долар"
],
"BDT": [
"BDT",
"бангладэшская така"
],
"BGN": [
"BGN",
"балгарскі леў"
],
"BHD": [
"BHD",
"бахрэйнскі дынар"
],
"BIF": [
"BIF",
"бурундзійскі франк"
],
"BMD": [
"BMD",
"бермудскі долар"
],
"BND": [
"BND",
"брунейскі долар"
],
"BOB": [
"BOB",
"балівіяна"
],
"BRL": [
"BRL",
"бразільскі рэал"
],
"BSD": [
"BSD",
"багамскі долар"
],
"BTN": [
"BTN",
"бутанскі нгултрум"
],
"BWP": [
"BWP",
"батсванская пула"
],
"BYN": [
"Br",
"беларускі рубель"
],
"BYR": [
"BYR",
"беларускі рубель (20002016)"
],
"BZD": [
"BZD",
"белізскі долар"
],
"CAD": [
"CAD",
"канадскі долар"
],
"CDF": [
"CDF",
"кангалезскі франк"
],
"CHF": [
"CHF",
"швейцарскі франк"
],
"CLP": [
"CLP",
"чылійскае песа"
],
"CNH": [
"CNH",
"афшорны кітайскі юань"
],
"CNY": [
"CN¥",
"кітайскі юань"
],
"COP": [
"COP",
"калумбійскае песа"
],
"CRC": [
"CRC",
"коста-рыканскі калон"
],
"CUC": [
"CUC",
"кубінскае канверсоўнае песа"
],
"CUP": [
"CUP",
"кубінскае песа"
],
"CVE": [
"CVE",
"эскуда Каба-Вердэ"
],
"CZK": [
"CZK",
"чэшская крона"
],
"DJF": [
"DJF",
"джыбуційскі франк"
],
"DKK": [
"DKK",
"дацкая крона"
],
"DOP": [
"DOP",
"дамініканскае песа"
],
"DZD": [
"DZD",
"алжырскі дынар"
],
"EGP": [
"EGP",
"егіпецкі фунт"
],
"ERN": [
"ERN",
"эрытрэйская накфа"
],
"ETB": [
"ETB",
"эфіопскі быр"
],
"EUR": [
"€",
"еўра"
],
"FJD": [
"FJD",
"фіджыйскі долар"
],
"FKP": [
"FKP",
"фунт Фалклендскіх астравоў"
],
"GBP": [
"£",
"брытанскі фунт стэрлінгаў"
],
"GEL": [
"GEL",
"грузінскі лары"
],
"GHS": [
"GHS",
"ганскі седзі"
],
"GIP": [
"GIP",
"гібралтарскі фунт"
],
"GMD": [
"GMD",
"гамбійскі даласі"
],
"GNF": [
"GNF",
"гвінейскі франк"
],
"GTQ": [
"GTQ",
"гватэмальскі кетсаль"
],
"GYD": [
"GYD",
"гаянскі долар"
],
"HKD": [
"HK$",
"ганконгскі долар"
],
"HNL": [
"HNL",
"гандураская лемпіра"
],
"HRK": [
"HRK",
"харвацкая куна"
],
"HTG": [
"HTG",
"гаіцянскі гурд"
],
"HUF": [
"HUF",
"венгерскі форынт"
],
"IDR": [
"IDR",
"інданезійская рупія"
],
"ILS": [
"₪",
"новы ізраільскі шэкель"
],
"INR": [
"₹",
"індыйская рупія"
],
"IQD": [
"IQD",
"іракскі дынар"
],
"IRR": [
"IRR",
"іранскі рыял"
],
"ISK": [
"ISK",
"ісландская крона"
],
"JMD": [
"JMD",
"ямайскі долар"
],
"JOD": [
"JOD",
"іарданскі дынар"
],
"JPY": [
"¥",
"японская іена"
],
"KES": [
"KES",
"кенійскі шылінг"
],
"KGS": [
"KGS",
"кіргізскі сом"
],
"KHR": [
"KHR",
"камбаджыйскі рыель"
],
"KMF": [
"KMF",
"каморскі франк"
],
"KPW": [
"KPW",
"паўночнакарэйская вона"
],
"KRW": [
"₩",
"паўднёвакарэйская вона"
],
"KWD": [
"KWD",
"кувейцкі дынар"
],
"KYD": [
"KYD",
"долар Кайманавых астравоў"
],
"KZT": [
"KZT",
"казахстанскі тэнге"
],
"LAK": [
"LAK",
"лаоскі кіп"
],
"LBP": [
"LBP",
"ліванскі фунт"
],
"LKR": [
"LKR",
"шры-ланкійская рупія"
],
"LRD": [
"LRD",
"ліберыйскі долар"
],
"LYD": [
"LYD",
"лівійскі дынар"
],
"MAD": [
"MAD",
"мараканскі дырхам"
],
"MDL": [
"MDL",
"малдаўскі лей"
],
"MGA": [
"MGA",
"малагасійскі арыяры"
],
"MKD": [
"MKD",
"македонскі дэнар"
],
"MMK": [
"MMK",
"м’янманскі к’ят"
],
"MNT": [
"MNT",
"мангольскі тугрык"
],
"MOP": [
"MOP",
"патака Макаа"
],
"MRO": [
"MRO",
"маўрытанская ўгія (19732017)"
],
"MRU": [
"MRU",
"маўрытанская угія"
],
"MUR": [
"MUR",
"маўрыкійская рупія"
],
"MVR": [
"MVR",
"мальдыўская руфія"
],
"MWK": [
"MWK",
"малавійская квача"
],
"MXN": [
"MX$",
"мексіканскае песа"
],
"MYR": [
"MYR",
"малайзійскі рынгіт"
],
"MZN": [
"MZN",
"мазамбікскі метыкал"
],
"NAD": [
"NAD",
"намібійскі долар"
],
"NGN": [
"NGN",
"нігерыйская наіра"
],
"NIO": [
"NIO",
"нікарагуанская кордаба"
],
"NOK": [
"NOK",
"нарвежская крона"
],
"NPR": [
"NPR",
"непальская рупія"
],
"NZD": [
"NZD",
"новазеландскі долар"
],
"OMR": [
"OMR",
"аманскі рыял"
],
"PAB": [
"PAB",
"панамскае бальбоа"
],
"PEN": [
"PEN",
"перуанскі соль"
],
"PGK": [
"PGK",
"кіна Папуа-Новай Гвінеі"
],
"PHP": [
"PHP",
"філіпінскае песа"
],
"PKR": [
"PKR",
"пакістанская рупія"
],
"PLN": [
"PLN",
"польскі злоты"
],
"PYG": [
"PYG",
"парагвайскі гуарані"
],
"QAR": [
"QAR",
"катарскі рыял"
],
"RON": [
"RON",
"румынскі лей"
],
"RSD": [
"RSD",
"сербскі дынар"
],
"RUB": [
"₽",
"расійскі рубель"
],
"RWF": [
"RWF",
"руандыйскі франк"
],
"SAR": [
"SAR",
"саудаўскі рыял"
],
"SBD": [
"SBD",
"долар Саламонавых астравоў"
],
"SCR": [
"SCR",
"сейшэльская рупія"
],
"SDG": [
"SDG",
"суданскі фунт"
],
"SEK": [
"SEK",
"шведская крона"
],
"SGD": [
"SGD",
"сінгапурскі долар"
],
"SHP": [
"SHP",
"фунт в-ва Святой Алены"
],
"SLL": [
"SLL",
"сьера-леонскі леонэ"
],
"SOS": [
"SOS",
"самалійскі шылінг"
],
"SRD": [
"SRD",
"сурынамскі долар"
],
"SSP": [
"SSP",
"паўднёвасуданскі фунт"
],
"STD": [
"STD",
"добра Сан-Тамэ і Прынсіпі (19772017)"
],
"STN": [
"STN",
"добра Сан-Тамэ і Прынсіпі"
],
"SYP": [
"SYP",
"сірыйскі фунт"
],
"SZL": [
"SZL",
"свазілендскі лілангені"
],
"THB": [
"THB",
"тайскі бат"
],
"TJS": [
"TJS",
"таджыкскі самані"
],
"TMT": [
"TMT",
"туркменскі манат"
],
"TND": [
"TND",
"туніскі дынар"
],
"TOP": [
"TOP",
"танганская паанга"
],
"TRY": [
"TRY",
"турэцкая ліра"
],
"TTD": [
"TTD",
"долар Трынідада і Табага"
],
"TWD": [
"NT$",
"новы тайваньскі долар"
],
"TZS": [
"TZS",
"танзанійскі шылінг"
],
"UAH": [
"UAH",
"украінская грыўня"
],
"UGX": [
"UGX",
"угандыйскі шылінг"
],
"USD": [
"$",
"долар ЗША"
],
"UYU": [
"UYU",
"уругвайскае песа"
],
"UZS": [
"UZS",
"узбекскі сум"
],
"VEF": [
"VEF",
"венесуальскі балівар (20082018)"
],
"VES": [
"VES",
"венесуэльскі балівар"
],
"VND": [
"₫",
"в’етнамскі донг"
],
"VUV": [
"VUV",
"вануацкі вату"
],
"WST": [
"WST",
"самаанская тала"
],
"XAF": [
"FCFA",
"цэнтральнаафрыканскі франк КФА"
],
"XCD": [
"EC$",
"усходнекарыбскі долар"
],
"XOF": [
"CFA",
"заходнеафрыканскі франк КФА"
],
"XPF": [
"CFPF",
"французскі ціхаакіянскі франк"
],
"YER": [
"YER",
"еменскі рыал"
],
"ZAR": [
"ZAR",
"паўднёваафрыканскі рэнд"
],
"ZMW": [
"ZMW",
"замбійская квача"
]
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,236 @@
{
"Names": {
"AED": [
"AED",
"arabu mara kafoli Diram"
],
"AOA": [
"AOA",
"angola Kwanza"
],
"AUD": [
"A$",
"ositirali Dolar"
],
"BHD": [
"BHD",
"bareyini Dinar"
],
"BIF": [
"BIF",
"burundi Fraŋ"
],
"BWP": [
"BWP",
"bɔtisiwana Pula"
],
"CAD": [
"CA$",
"kanada Dolar"
],
"CDF": [
"CDF",
"kongole Fraŋ"
],
"CHF": [
"CHF",
"suwisi Fraŋ"
],
"CNY": [
"CN¥",
"siniwa Yuwan"
],
"CVE": [
"CVE",
"capivɛrdi Esekudo"
],
"DJF": [
"DJF",
"jibuti Fraŋ"
],
"DZD": [
"DZD",
"alizeri Dinar"
],
"EGP": [
"EGP",
"eziputi Livri"
],
"ERN": [
"ERN",
"eritere Nafika"
],
"ETB": [
"ETB",
"etiopi Bir"
],
"EUR": [
"€",
"ero"
],
"GBP": [
"£",
"angilɛ Livri"
],
"GHC": [
"GHC",
"gana Sedi"
],
"GMD": [
"GMD",
"gambi Dalasi"
],
"GNS": [
"GNS",
"gine Fraŋ"
],
"INR": [
"₹",
"Ɛndu Rupi"
],
"JPY": [
"JP¥",
"zapɔne Yɛn"
],
"KES": [
"KES",
"keniya Siling"
],
"KMF": [
"KMF",
"komɔri Fraŋ"
],
"LRD": [
"LRD",
"liberiya Dolar"
],
"LSL": [
"LSL",
"lesoto Loti"
],
"LYD": [
"LYD",
"libi Dinar"
],
"MAD": [
"MAD",
"marɔku Diram"
],
"MGA": [
"MGA",
"madagasikari Fraŋ"
],
"MRO": [
"MRO",
"mɔritani Uguwiya (19732017)"
],
"MRU": [
"MRU",
"mɔritani Uguwiya"
],
"MUR": [
"MUR",
"morisi Rupi"
],
"MWK": [
"MWK",
"malawi Kwaca"
],
"MZM": [
"MZM",
"mozanbiki Metikali"
],
"NAD": [
"NAD",
"namibi Dolar"
],
"NGN": [
"NGN",
"nizeriya Nɛra"
],
"RWF": [
"RWF",
"ruwanda Fraŋ"
],
"SAR": [
"SAR",
"sawudiya Riyal"
],
"SCR": [
"SCR",
"sesɛli Rupi"
],
"SDG": [
"SDG",
"sudani Dinar"
],
"SDP": [
"SDP",
"sudani Livri"
],
"SHP": [
"SHP",
"Ɛlɛni-Senu Livri"
],
"SLL": [
"SLL",
"siyeralewɔni Lewɔni"
],
"SOS": [
"SOS",
"somali Siling"
],
"STD": [
"STD",
"sawotome Dobra (19772017)"
],
"STN": [
"STN",
"sawotome Dobra"
],
"SZL": [
"SZL",
"swazilandi Lilangeni"
],
"TND": [
"TND",
"tunizi Dinar"
],
"TZS": [
"TZS",
"tanzani Siling"
],
"UGX": [
"UGX",
"uganda Siling"
],
"USD": [
"US$",
"ameriki Dolar"
],
"XAF": [
"FCFA",
"sefa Fraŋ (BEAC)"
],
"XOF": [
"CFA",
"sefa Fraŋ (BCEAO)"
],
"ZAR": [
"ZAR",
"sudafriki Randi"
],
"ZMK": [
"ZMK",
"zambi Kwaca (19682012)"
],
"ZMW": [
"ZMW",
"zambi Kwaca"
],
"ZWD": [
"ZWD",
"zimbabuwe Dolar"
]
}
}

File diff suppressed because it is too large Load Diff

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