License updated to GPLv3

🧲 New features
Custom user role permissions
Employee edit form updated
Employee daily task list
Attendance and employee distribution charts on dashboard
Improvements to company structure and company assets module
Improved tables for displaying data in several modules
Faster data loading (specially for employee module)
Initials based profile pictures
Re-designed login page
Re-designed user profile page
Improvements to filtering
New REST endpoints for employee qualifications

🐛 Bug fixes
Fixed, issue with managers being able to create performance reviews for employees who are not their direct reports
Fixed, issues related to using full profile image instead of using smaller version of profile image
Changing third gender to other
Improvements and fixes for internal frontend data caching
This commit is contained in:
Thilina Pituwala
2020-10-31 19:02:37 +01:00
parent 86b8345505
commit b1df0037db
29343 changed files with 867614 additions and 2191082 deletions

View File

@@ -1,17 +1,17 @@
Contributing to Gettext
=======================
Looking to contribute something to this library? Here's how you can help.
## Bugs
A bug is a demonstrable problem that is caused by the code in the repository. Good bug reports are extremely helpful thank you!
Please try to be as detailed as possible in your report. Include specific information about the environment version of PHP, version of gettext, etc, and steps required to reproduce the issue.
## Pull Requests
Good pull requests patches, improvements, new features are a fantastic help. New extractors or generator are welcome. Before create a pull request, please follow these instructions:
* The code must be PSR-2 compliant
* Write some tests
Contributing to Gettext
=======================
Looking to contribute something to this library? Here's how you can help.
## Bugs
A bug is a demonstrable problem that is caused by the code in the repository. Good bug reports are extremely helpful thank you!
Please try to be as detailed as possible in your report. Include specific information about the environment version of PHP, version of gettext, etc, and steps required to reproduce the issue.
## Pull Requests
Good pull requests patches, improvements, new features are a fantastic help. New extractors or generator are welcome. Before create a pull request, please follow these instructions:
* The code must be PSR-2 compliant
* Write some tests

View File

@@ -1,50 +1,46 @@
UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE
Unicode Data Files include all data files under the directories
http://www.unicode.org/Public/, http://www.unicode.org/reports/, and
http://www.unicode.org/cldr/data/. Unicode Data Files do not include PDF
online code charts under the directory http://www.unicode.org/Public/.
Software includes any source code published in the Unicode Standard or under
the directories http://www.unicode.org/Public/,
http://www.unicode.org/reports/, and http://www.unicode.org/cldr/data/.
See Terms of Use for definitions of Unicode Inc.'s
Data Files and Software.
NOTICE TO USER: Carefully read the following legal agreement. BY
DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S DATA FILES
("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), YOU UNEQUIVOCALLY ACCEPT, AND
AGREE TO BE BOUND BY, ALL OF THE TERMS AND CONDITIONS OF THIS AGREEMENT. IF
YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA
FILES OR SOFTWARE.
NOTICE TO USER: Carefully read the following legal agreement.
BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S
DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"),
YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE
TERMS AND CONDITIONS OF THIS AGREEMENT.
IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE
THE DATA FILES OR SOFTWARE.
COPYRIGHT AND PERMISSION NOTICE
COPYRIGHT AND PERMISSION NOTICE
Copyright © 1991-2014 Unicode, Inc. All rights reserved. Distributed under
the Terms of Use in http://www.unicode.org/copyright.html.
Copyright © 1991-2019 Unicode, Inc. All rights reserved.
Distributed under the Terms of Use in https://www.unicode.org/copyright.html.
Permission is hereby granted, free of charge, to any person obtaining a
copy of the Unicode data files and any associated documentation (the "Data
Files") or Unicode software and any associated documentation (the "Software")
to deal in the Data Files or Software without restriction, including without
limitation the rights to use, copy, modify, merge, publish, distribute, and/or
sell copies of the Data Files or Software, and to permit persons to whom the
Data Files or Software are furnished to do so, provided that (a) the above
copyright notice(s) and this permission notice appear with all copies of the
Data Files or Software, (b) both the above copyright notice(s) and this
permission notice appear in associated documentation, and (c) there is clear
notice in each modified Data File or in the Software as well as in the
documentation associated with the Data File(s) or Software that the data or
software has been modified.
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Unicode data files and any associated documentation
(the "Data Files") or Unicode software and any associated documentation
(the "Software") to deal in the Data Files or Software
without restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, and/or sell copies of
the Data Files or Software, and to permit persons to whom the Data Files
or Software are furnished to do so, provided that either
(a) this copyright and permission notice appear with all copies
of the Data Files or Software, or
(b) this copyright and permission notice appear in associated
Documentation.
THE DATA FILES AND SOFTWARE ARE 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 OF THIRD
PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN
THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE
DATA FILES OR SOFTWARE.
THE DATA FILES AND SOFTWARE ARE 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 OF THIRD PARTY RIGHTS.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THE DATA FILES OR SOFTWARE.
Except as contained in this notice, the name of a copyright holder shall
not be used in advertising or otherwise to promote the sale, use or other
dealings in these Data Files or Software without prior written authorization
of the copyright holder.
Except as contained in this notice, the name of a copyright holder
shall not be used in advertising or otherwise to promote the sale,
use or other dealings in these Data Files or Software without prior
written authorization of the copyright holder.

View File

@@ -1,4 +1,287 @@
#!/usr/bin/env php
<?php
include 'export-plural-rules.php';
use Gettext\Languages\Exporter\Exporter;
use Gettext\Languages\Language;
// Let's start by imposing that we don't accept any error or warning.
// This is a really life-saving approach.
error_reporting(E_ALL);
set_error_handler(function ($errno, $errstr, $errfile, $errline) {
fprintf(STDERR, "${errstr}\nFile: ${errfile}\nLine: ${errline}\nCode: ${errno}\n");
die(5);
});
require_once dirname(__DIR__) . '/src/autoloader.php';
// Parse the command line options
Enviro::initialize();
try {
if (isset(Enviro::$languages)) {
$languages = array();
foreach (Enviro::$languages as $languageId) {
$language = Language::getById($languageId);
if (!isset($language)) {
throw new Exception("Unable to find the language with id '${languageId}'");
}
$languages[] = $language;
}
} else {
$languages = Language::getAll();
}
if (Enviro::$reduce) {
$languages = Enviro::reduce($languages);
}
if (Enviro::$noExtraParenthesis) {
$languages = array_map(
function (Language $language) {
$language->formula = $language->buildFormula(true);
return $language;
},
$languages
);
}
if (isset(Enviro::$outputFilename)) {
echo call_user_func(array(Exporter::getExporterClassName(Enviro::$outputFormat), 'toFile'), $languages, Enviro::$outputFilename, array('us-ascii' => Enviro::$outputUSAscii));
} else {
echo call_user_func(array(Exporter::getExporterClassName(Enviro::$outputFormat), 'toString'), $languages, array('us-ascii' => Enviro::$outputUSAscii));
}
} catch (Exception $x) {
fprintf(STDERR, $x->getMessage() . "\n");
fprintf(STDERR, "Trace:\n");
fprintf(STDERR, $x->getTraceAsString() . "\n");
die(4);
}
die(0);
/**
* Helper class to handle command line options.
*/
class Enviro
{
/**
* Shall the output contain only US-ASCII characters?
*
* @var bool
*/
public static $outputUSAscii;
/**
* The output format.
*
* @var string
*/
public static $outputFormat;
/**
* Output file name.
*
* @var string
*/
public static $outputFilename;
/**
* List of wanted language IDs; it not set: all languages will be returned.
*
* @var array|null
*/
public static $languages;
/**
* Reduce the language list to the minimum common denominator.
*
* @var bool
*/
public static $reduce;
/**
* Omit extra parenthesis in plural rule formulas.
*
* @var bool
*/
public static $noExtraParenthesis;
/**
* Parse the command line options.
*/
public static function initialize()
{
global $argv;
self::$outputUSAscii = false;
self::$outputFormat = null;
self::$outputFilename = null;
self::$languages = null;
self::$reduce = null;
self::$noExtraParenthesis = false;
$exporters = Exporter::getExporters();
if (isset($argv) && is_array($argv)) {
foreach ($argv as $argi => $arg) {
if ($argi === 0) {
continue;
}
if (is_string($arg)) {
$argLC = trim(strtolower($arg));
switch ($argLC) {
case '-h':
case '--help':
self::showSyntax();
die(0);
case '--us-ascii':
self::$outputUSAscii = true;
break;
case '--reduce=yes':
self::$reduce = true;
break;
case '--reduce=no':
self::$reduce = false;
break;
case '--parenthesis=yes':
self::$noExtraParenthesis = false;
break;
case '--parenthesis=no':
self::$noExtraParenthesis = true;
break;
default:
if (preg_match('/^--output=.+$/', $argLC)) {
if (isset(self::$outputFilename)) {
fprintf(STDERR, "The output file name has been specified more than once!\n");
self::showSyntax();
die(3);
}
list(, self::$outputFilename) = explode('=', $arg, 2);
self::$outputFilename = trim(self::$outputFilename);
} elseif (preg_match('/^--languages?=.+$/', $argLC)) {
list(, $s) = explode('=', $arg, 2);
$list = explode(',', $s);
if (is_array(self::$languages)) {
self::$languages = array_merge(self::$languages, $list);
} else {
self::$languages = $list;
}
} elseif (isset($exporters[$argLC])) {
if (isset(self::$outputFormat)) {
fprintf(STDERR, "The output format has been specified more than once!\n");
self::showSyntax();
die(3);
}
self::$outputFormat = $argLC;
} else {
fprintf(STDERR, "Unknown option: ${arg}\n");
self::showSyntax();
die(2);
}
break;
}
}
}
}
if (!isset(self::$outputFormat)) {
self::showSyntax();
die(1);
}
if (isset(self::$languages)) {
self::$languages = array_values(array_unique(self::$languages));
}
if (!isset(self::$reduce)) {
self::$reduce = isset(self::$languages) ? false : true;
}
}
/**
* Write out the syntax.
*/
public static function showSyntax()
{
$basename = basename(__FILE__);
$exporters = array_keys(Exporter::getExporters(true));
$exporterList = implode('|', $exporters);
fprintf(STDERR, <<<EOT
Syntax:
${basename} [-h|--help] [--us-ascii] [--languages=<LanguageId>[,<LanguageId>,...]] [--reduce=yes|no] [--parenthesis=yes|no] [--output=<file name>] <${exporterList}>
Where:
--help
show this help message.
--us-ascii
if specified, the output will contain only US-ASCII characters.
--languages(or --language)
export only the specified language codes.
Separate languages with commas; you can also use this argument
more than once; it's case insensitive and accepts both '_' and
'-' as locale chunks separator (eg we accept 'it_IT' as well as
'it-it').
--reduce
if set to yes the output won't contain languages with the same
base language and rules.
For instance nl_BE ('Flemish') will be omitted because it's the
same as nl ('Dutch').
Defaults to 'no' if --languages is specified, to 'yes' otherwise.
--parenthesis
if set to no, extra parenthesis will be omitted in generated
plural rules formulas.
Those extra parenthesis are needed to create a PHP-compatible
formula.
Defaults to 'yes'
--output
if specified, the output will be saved to <file name>. If not
specified we'll output to standard output.
Output formats
EOT
);
$len = max(array_map('strlen', $exporters));
foreach ($exporters as $exporter) {
fprintf(STDERR, ' ' . str_pad($exporter, $len) . ': ' . Exporter::getExporterDescription($exporter) . "\n");
}
fprintf(STDERR, "\n");
}
/**
* Reduce a language list to the minimum common denominator.
*
* @param Language[] $languages
*
* @return Language[]
*/
public static function reduce($languages)
{
for ($numChunks = 3; $numChunks >= 2; $numChunks--) {
$filtered = array();
foreach ($languages as $language) {
$chunks = explode('_', $language->id);
$compatibleFound = false;
if (count($chunks) === $numChunks) {
$categoriesHash = serialize($language->categories);
$otherIds = array();
$otherIds[] = $chunks[0];
for ($k = 2; $k < $numChunks; $k++) {
$otherIds[] = $chunks[0] . '_' . $chunks[$numChunks - 1];
}
foreach ($languages as $check) {
foreach ($otherIds as $otherId) {
if (($check->id === $otherId) && ($check->formula === $language->formula) && (serialize($check->categories) === $categoriesHash)) {
$compatibleFound = true;
break;
}
}
if ($compatibleFound === true) {
break;
}
}
}
if (!$compatibleFound) {
$filtered[] = $language;
}
}
$languages = $filtered;
}
return $languages;
}
}

View File

@@ -1,8 +1 @@
@echo off
if "%PHPBIN%" == "" set PHPBIN=@php_bin@
if not exist "%PHPBIN%" set PHPBIN=%PHP_PEAR_PHP_BIN%
if not exist "%PHPBIN%" set PHPBIN=php
set MY_BIN_FOLDER=@bin_dir@
if not exist "%MY_BIN_FOLDER%" set MY_BIN_FOLDER=%~dp0
"%PHPBIN%" "%MY_BIN_FOLDER%\export-plural-rules" %*
@php "%~dpn0" %*

View File

@@ -1,234 +0,0 @@
<?php
use Gettext\Languages\Exporter\Exporter;
use Gettext\Languages\Language;
// Let's start by imposing that we don't accept any error or warning.
// This is a really life-saving approach.
error_reporting(E_ALL);
set_error_handler(function ($errno, $errstr, $errfile, $errline) {
Enviro::echoErr("$errstr\nFile: $errfile\nLine: $errline\nCode: $errno\n");
die(5);
});
require_once dirname(__DIR__).'/src/autoloader.php';
// Parse the command line options
Enviro::initialize();
try {
if (isset(Enviro::$languages)) {
$languages = array();
foreach (Enviro::$languages as $languageId) {
$language = Language::getById($languageId);
if (!isset($language)) {
throw new Exception("Unable to find the language with id '$languageId'");
}
$languages[] = $language;
}
} else {
$languages = Language::getAll();
}
if (Enviro::$reduce) {
$languages = Enviro::reduce($languages);
}
if (isset(Enviro::$outputFilename)) {
echo call_user_func(array(Exporter::getExporterClassName(Enviro::$outputFormat), 'toFile'), $languages, Enviro::$outputFilename, array('us-ascii' => Enviro::$outputUSAscii));
} else {
echo call_user_func(array(Exporter::getExporterClassName(Enviro::$outputFormat), 'toString'), $languages, array('us-ascii' => Enviro::$outputUSAscii));
}
} catch (Exception $x) {
Enviro::echoErr($x->getMessage()."\n");
Enviro::echoErr("Trace:\n");
Enviro::echoErr($x->getTraceAsString()."\n");
die(4);
}
die(0);
/**
* Helper class to handle command line options.
*/
class Enviro
{
/**
* Shall the output contain only US-ASCII characters?
* @var bool
*/
public static $outputUSAscii;
/**
* The output format.
* @var string
*/
public static $outputFormat;
/**
* Output file name.
* @var string
*/
public static $outputFilename;
/**
* List of wanted language IDs; it not set: all languages will be returned.
* @var array|null
*/
public static $languages;
/**
* Reduce the language list to the minimum common denominator.
* @var bool
*/
public static $reduce;
/**
* Parse the command line options.
*/
public static function initialize()
{
global $argv;
self::$outputUSAscii = false;
self::$outputFormat = null;
self::$outputFilename = null;
self::$languages = null;
self::$reduce = null;
$exporters = Exporter::getExporters();
if (isset($argv) && is_array($argv)) {
foreach ($argv as $argi => $arg) {
if ($argi === 0) {
continue;
}
if (is_string($arg)) {
$argLC = trim(strtolower($arg));
switch ($argLC) {
case '--us-ascii':
self::$outputUSAscii = true;
break;
case '--reduce=yes':
self::$reduce = true;
break;
case '--reduce=no':
self::$reduce = false;
break;
default:
if (preg_match('/^--output=.+$/', $argLC)) {
if (isset(self::$outputFilename)) {
self::echoErr("The output file name has been specified more than once!\n");
self::showSyntax();
die(3);
}
list(, self::$outputFilename) = explode('=', $arg, 2);
self::$outputFilename = trim(self::$outputFilename);
} elseif (preg_match('/^--languages?=.+$/', $argLC)) {
list(, $s) = explode('=', $arg, 2);
$list = explode(',', $s);
if (is_array(self::$languages)) {
self::$languages = array_merge(self::$languages, $list);
} else {
self::$languages = $list;
}
} elseif (isset($exporters[$argLC])) {
if (isset(self::$outputFormat)) {
self::echoErr("The output format has been specified more than once!\n");
self::showSyntax();
die(3);
}
self::$outputFormat = $argLC;
} else {
self::echoErr("Unknown option: $arg\n");
self::showSyntax();
die(2);
}
break;
}
}
}
}
if (!isset(self::$outputFormat)) {
self::showSyntax();
die(1);
}
if (isset(self::$languages)) {
self::$languages = array_values(array_unique(self::$languages));
}
if (!isset(self::$reduce)) {
self::$reduce = isset(self::$languages) ? false : true;
}
}
/**
* Write out the syntax.
*/
public static function showSyntax()
{
$exporters = array_keys(Exporter::getExporters(true));
self::echoErr("Syntax: php ".basename(__FILE__)." [--us-ascii] [--languages=<LanguageId>[,<LanguageId>,...]] [--reduce=yes|no] [--output=<file name>] <".implode('|', $exporters).">\n");
self::echoErr("Where:\n");
self::echoErr("--us-ascii : if specified, the output will contain only US-ASCII characters.\n");
self::echoErr("--languages: (or --language) export only the specified language codes.\n");
self::echoErr(" Separate languages with commas; you can also use this argument\n");
self::echoErr(" more than once; it's case insensitive and accepts both '_' and\n");
self::echoErr(" '-' as locale chunks separator (eg we accept 'it_IT' as well as\n");
self::echoErr(" 'it-it').\n");
self::echoErr("--reduce : if set to yes the output won't contain languages with the same\n");
self::echoErr(" base language and rules.\n For instance nl_BE ('Flemish') will be\n");
self::echoErr(" omitted because it's the same as nl ('Dutch').\n");
self::echoErr(" Defaults to 'no' --languages is specified, to 'yes' otherwise.\n");
self::echoErr("--output : if specified, the output will be saved to <file name>. If not\n");
self::echoErr(" specified we'll output to standard output.\n");
self::echoErr("Output formats\n");
$len = max(array_map('strlen', $exporters));
foreach ($exporters as $exporter) {
self::echoErr(str_pad($exporter, $len).": ".Exporter::getExporterDescription($exporter)."\n");
}
}
/**
* Print a string to stderr.
* @param string $str The string to be printed out.
*/
public static function echoErr($str)
{
$hStdErr = @fopen('php://stderr', 'a');
if ($hStdErr === false) {
echo $str;
} else {
fwrite($hStdErr, $str);
fclose($hStdErr);
}
}
/**
* Reduce a language list to the minimum common denominator.
* @param Language[] $languages
* @return Language[]
*/
public static function reduce($languages)
{
for ($numChunks = 3; $numChunks >= 2; $numChunks--) {
$filtered = array();
foreach ($languages as $language) {
$chunks = explode('_', $language->id);
$compatibleFound = false;
if (count($chunks) === $numChunks) {
$categoriesHash = serialize($language->categories);
$otherIds = array();
$otherIds[] = $chunks[0];
for ($k = 2; $k < $numChunks; $k++) {
$otherIds[] = $chunks[0].'_'.$chunks[$numChunks - 1];
}
foreach ($languages as $check) {
foreach ($otherIds as $otherId) {
if (($check->id === $otherId) && ($check->formula === $language->formula) && (serialize($check->categories) === $categoriesHash)) {
$compatibleFound = true;
break;
}
}
if ($compatibleFound === true) {
break;
}
}
}
if (!$compatibleFound) {
$filtered[] = $language;
}
}
$languages = $filtered;
}
return $languages;
}
}

View File

@@ -1,46 +1,54 @@
{
"name" : "gettext/languages",
"description" : "gettext languages with plural rules",
"keywords" : [
"localization",
"l10n",
"internationalization",
"i18n",
"translations",
"translate",
"php",
"unicode",
"cldr",
"language",
"languages",
"plural",
"plurals",
"plural rules"
],
"homepage" : "https://github.com/mlocati/cldr-to-gettext-plural-rules",
"license" : "MIT",
"authors" : [{
"name" : "Michele Locati",
"email" : "mlocati@gmail.com",
"role" : "Developer"
}
],
"autoload" : {
"psr-4" : {
"Gettext\\Languages\\" : "src/"
}
},
"require" : {
"php" : ">=5.3"
},
"require-dev": {
"phpunit/phpunit": "^4"
},
"scripts": {
"test": "phpunit"
},
"bin" : [
"bin/export-plural-rules",
"bin/export-plural-rules.php"
]
"name": "gettext/languages",
"description": "gettext languages with plural rules",
"keywords": [
"localization",
"l10n",
"internationalization",
"i18n",
"translations",
"translate",
"php",
"unicode",
"cldr",
"language",
"languages",
"plural",
"plurals",
"plural rules"
],
"homepage": "https://github.com/php-gettext/Languages",
"license": "MIT",
"authors": [
{
"name": "Michele Locati",
"email": "mlocati@gmail.com",
"role": "Developer"
}
],
"autoload": {
"psr-4": {
"Gettext\\Languages\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Gettext\\Languages\\Test\\": "tests/test/"
}
},
"require": {
"php": ">=5.3"
},
"require-dev": {
"phpunit/phpunit": "^4.8 || ^5.7 || ^6.5 || ^7.5 || ^8.4",
"friendsofphp/php-cs-fixer": "^2.16.0"
},
"scripts": {
"test": "phpunit",
"cs-test": "php-cs-fixer fix --config=.php_cs.dist --path-mode=intersection --verbose --dry-run --diff",
"cs-fix": "php-cs-fixer fix --config=.php_cs.dist --path-mode=intersection"
},
"bin": [
"bin/export-plural-rules"
]
}

View File

@@ -1,4 +1,5 @@
<?php
namespace Gettext\Languages;
use Exception;
@@ -10,38 +11,46 @@ class Category
{
/**
* The category identifier (eg 'zero', 'one', ..., 'other').
*
* @var string
*/
public $id;
/**
* The gettext formula that identifies this category (null if and only if the category is 'other').
*
* @var string|null
*/
public $formula;
/**
* The CLDR representation of some exemplar numeric ranges that satisfy this category.
*
* @var string|null
*/
public $examples;
/**
* Initialize the instance and parse the formula.
* @param string $cldrCategoryId The CLDR category identifier (eg 'pluralRule-count-one').
* @param string $cldrFormulaAndExamples The CLDR formula and examples (eg 'i = 1 and v = 0 @integer 1').
* @throws Exception
*
* @param string $cldrCategoryId the CLDR category identifier (eg 'pluralRule-count-one')
* @param string $cldrFormulaAndExamples the CLDR formula and examples (eg 'i = 1 and v = 0 @integer 1')
*
* @throws \Exception
*/
public function __construct($cldrCategoryId, $cldrFormulaAndExamples)
{
$matches = array();
if (!preg_match('/^pluralRule-count-(.+)$/', $cldrCategoryId, $matches)) {
throw new Exception("Invalid CLDR category: '$cldrCategoryId'");
throw new Exception("Invalid CLDR category: '${cldrCategoryId}'");
}
if (!in_array($matches[1], CldrData::$categories)) {
throw new Exception("Invalid CLDR category: '$cldrCategoryId'");
throw new Exception("Invalid CLDR category: '${cldrCategoryId}'");
}
$this->id = $matches[1];
$cldrFormulaAndExamplesNormalized = trim(preg_replace('/\s+/', ' ', $cldrFormulaAndExamples));
if (!preg_match('/^([^@]*)(?:@integer([^@]+))?(?:@decimal(?:[^@]+))?$/', $cldrFormulaAndExamplesNormalized, $matches)) {
throw new Exception("Invalid CLDR category rule: $cldrFormulaAndExamples");
throw new Exception("Invalid CLDR category rule: ${cldrFormulaAndExamples}");
}
$cldrFormula = trim($matches[1]);
$s = isset($matches[2]) ? trim($matches[2]) : '';
@@ -49,7 +58,7 @@ class Category
switch ($this->id) {
case CldrData::OTHER_CATEGORY:
if ($cldrFormula !== '') {
throw new Exception("The '".CldrData::OTHER_CATEGORY."' category should not have any formula, but it has '$cldrFormula'");
throw new Exception("The '" . CldrData::OTHER_CATEGORY . "' category should not have any formula, but it has '${cldrFormula}'");
}
$this->formula = null;
break;
@@ -61,19 +70,26 @@ class Category
break;
}
}
/**
* Return a list of numbers corresponding to the $examples value.
* @throws Exception Throws an Exception if we weren't able to expand the examples.
*
* @throws \Exception throws an Exception if we weren't able to expand the examples
*
* @return int[]
*/
public function getExampleIntegers()
{
return self::expandExamples($this->examples);
}
/**
* Expand a list of examples as defined by CLDR.
*
* @param string $examples A string like '1, 2, 5...7, …'.
* @throws Exception Throws an Exception if we weren't able to expand $examples.
*
* @throws \Exception throws an Exception if we weren't able to expand $examples
*
* @return int[]
*/
public static function expandExamples($examples)
@@ -81,14 +97,14 @@ class Category
$result = array();
$m = null;
if (substr($examples, -strlen(', …')) === ', …') {
$examples = substr($examples, 0, strlen($examples) -strlen(', …'));
$examples = substr($examples, 0, strlen($examples) - strlen(', …'));
}
foreach (explode(',', str_replace(' ', '', $examples)) as $range) {
if (preg_match('/^\d+$/', $range)) {
$result[] = intval($range);
$result[] = (int) $range;
} elseif (preg_match('/^(\d+)~(\d+)$/', $range, $m)) {
$from = intval($m[1]);
$to = intval($m[2]);
$from = (int) $m[1];
$to = (int) $m[2];
$delta = $to - $from;
$step = (int) max(1, $delta / 100);
for ($i = $from; $i < $to; $i += $step) {
@@ -96,11 +112,11 @@ class Category
}
$result[] = $to;
} else {
throw new Exception("Unhandled test range '$range' in '$examples'");
throw new Exception("Unhandled test range '${range}' in '${examples}'");
}
}
if (empty($result)) {
throw new Exception("No test numbers from '$examples'");
throw new Exception("No test numbers from '${examples}'");
}
return $result;

View File

@@ -1,4 +1,5 @@
<?php
namespace Gettext\Languages;
use Exception;
@@ -10,166 +11,73 @@ class CldrData
{
/**
* Super-special plural category: this should always be present for any language.
*
* @var string
*/
const OTHER_CATEGORY = 'other';
/**
* The list of the plural categories, sorted from 'zero' to 'other'.
*
* @var string[]
*/
public static $categories = array('zero', 'one', 'two', 'few', 'many', self::OTHER_CATEGORY);
/**
* The loaded CLDR data
* The loaded CLDR data.
*
* @var array
*/
private static $data;
/**
* Returns the loaded CLDR data.
* @param string $key Can be 'languages', 'territories', 'plurals', 'supersededLanguages', 'scripts', 'standAloneScripts'
* @var array
*/
private static function getData($key)
{
if (!isset(self::$data)) {
$fixKeys = function ($list, &$standAlone = null) {
$result = array();
$standAlone = array();
$match = null;
foreach ($list as $key => $value) {
$variant = '';
if (preg_match('/^(.+)-alt-(short|variant|stand-alone|long)$/', $key, $match)) {
$key = $match[1];
$variant = $match[2];
}
$key = str_replace('-', '_', $key);
switch ($key) {
case 'root': // Language: Root
case 'und': // Language: Unknown Language
case 'zxx': // Language: No linguistic content
case 'ZZ': // Territory: Unknown Region
case 'Zinh': // Script: Inherited
case 'Zmth': // Script: Mathematical Notation
case 'Zsym': // Script: Symbols
case 'Zxxx': // Script: Unwritten
case 'Zyyy': // Script: Common
case 'Zzzz': // Script: Unknown Script
break;
default:
switch ($variant) {
case 'stand-alone':
$standAlone[$key] = $value;
break;
case '':
$result[$key] = $value;
break;
}
break;
}
}
private static $plurals;
return $result;
};
$data = array();
$json = json_decode(file_get_contents(__DIR__.'/cldr-data/main/en-US/languages.json'), true);
$data['languages'] = $fixKeys($json['main']['en-US']['localeDisplayNames']['languages']);
$json = json_decode(file_get_contents(__DIR__.'/cldr-data/main/en-US/territories.json'), true);
$data['territories'] = $fixKeys($json['main']['en-US']['localeDisplayNames']['territories']);
$json = json_decode(file_get_contents(__DIR__.'/cldr-data/supplemental/plurals.json'), true);
$data['plurals'] = $fixKeys($json['supplemental']['plurals-type-cardinal']);
$json = json_decode(file_get_contents(__DIR__.'/cldr-data/main/en-US/scripts.json'), true);
$data['scripts'] = $fixKeys($json['main']['en-US']['localeDisplayNames']['scripts'], $data['standAloneScripts']);
$data['standAloneScripts'] = array_merge($data['scripts'], $data['standAloneScripts']);
$data['scripts'] = array_merge($data['standAloneScripts'], $data['scripts']);
$data['supersededLanguages'] = array();
// Remove the languages for which we don't have plurals
$m = null;
foreach (array_keys(array_diff_key($data['languages'], $data['plurals'])) as $missingPlural) {
if (preg_match('/^([a-z]{2,3})_/', $missingPlural, $m)) {
if (!isset($data['plurals'][$m[1]])) {
unset($data['languages'][$missingPlural]);
}
} else {
unset($data['languages'][$missingPlural]);
}
}
// Fix the languages for which we have plurals
$formerCodes = array(
'in' => 'id', // former Indonesian
'iw' => 'he', // former Hebrew
'ji' => 'yi', // former Yiddish
'jw' => 'jv', // former Javanese
'mo' => 'ro_MD', // former Moldavian
);
$knownMissingLanguages = array(
'bh' => 'Bihari',
'guw' => 'Gun',
'nah' => 'Nahuatl',
'smi' => 'Sami',
);
foreach (array_keys(array_diff_key($data['plurals'], $data['languages'])) as $missingLanguage) {
if (isset($formerCodes[$missingLanguage]) && isset($data['languages'][$formerCodes[$missingLanguage]])) {
$data['languages'][$missingLanguage] = $data['languages'][$formerCodes[$missingLanguage]];
$data['supersededLanguages'][$missingLanguage] = $formerCodes[$missingLanguage];
} else {
if (isset($knownMissingLanguages[$missingLanguage])) {
$data['languages'][$missingLanguage] = $knownMissingLanguages[$missingLanguage];
} else {
throw new Exception("We have the plural rule for the language '$missingLanguage' but we don't have its language name");
}
}
}
ksort($data['languages'], SORT_STRING);
ksort($data['territories'], SORT_STRING);
ksort($data['plurals'], SORT_STRING);
ksort($data['scripts'], SORT_STRING);
ksort($data['standAloneScripts'], SORT_STRING);
ksort($data['supersededLanguages'], SORT_STRING);
self::$data = $data;
}
if (!@isset(self::$data[$key])) {
throw new Exception("Invalid CLDR data key: '$key'");
}
return self::$data[$key];
}
/**
* Returns a dictionary containing the language names.
* The keys are the language identifiers.
* The values are the language names in US English.
*
* @return string[]
*/
public static function getLanguageNames()
{
return self::getData('languages');
}
/**
* Return a dictionary containing the territory names (in US English).
* The keys are the territory identifiers.
* The values are the territory names in US English.
*
* @return string[]
*/
public static function getTerritoryNames()
{
return self::getData('territories');
}
/**
* Return a dictionary containing the script names (in US English).
* The keys are the script identifiers.
* The values are the script names in US English.
* @param bool $standAlone Set to true to retrieve the stand-alone script names, false otherwise.
*
* @param bool $standAlone set to true to retrieve the stand-alone script names, false otherwise
*
* @return string[]
*/
public static function getScriptNames($standAlone)
{
return self::getData($standAlone ? 'standAloneScripts' : 'scripts');
}
/**
* @var array
*/
private static $plurals;
/**
* A dictionary containing the plural rules.
* The keys are the language identifiers.
* The values are arrays whose keys are the CLDR category names and the values are the CLDR category definition.
*
* @example The English key-value pair is somethink like this:
* <code><pre>
* "en": {
@@ -177,23 +85,29 @@ class CldrData
* "pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
* }
* </pre></code>
* @var array
*
* @return array
*/
public static function getPlurals()
{
return self::getData('plurals');
}
/**
* Return a list of superseded language codes.
* @return array Keys are the former language codes, values are the new language/locale codes.
*
* @return array keys are the former language codes, values are the new language/locale codes
*/
public static function getSupersededLanguages()
{
return self::getData('supersededLanguages');
}
/**
* Retrieve the name of a language, as well as if a language code is deprecated in favor of another language code.
* @param string $id The language identifier.
*
* @param string $id the language identifier
*
* @return array|null Returns an array with the keys 'id' (normalized), 'name', 'supersededBy' (optional), 'territory' (optional), 'script' (optional), 'baseLanguage' (optional), 'categories'. If $id is not valid returns null.
*/
public static function getLanguageInfo($id)
@@ -206,10 +120,10 @@ class CldrData
$territoryId = (isset($matches[3]) && ($matches[3] !== '')) ? strtoupper($matches[3]) : null;
$normalizedId = $languageId;
if (isset($scriptId)) {
$normalizedId .= '_'.$scriptId;
$normalizedId .= '_' . $scriptId;
}
if (isset($territoryId)) {
$normalizedId .= '_'.$territoryId;
$normalizedId .= '_' . $territoryId;
}
// Structure precedence: see Likely Subtags - http://www.unicode.org/reports/tr35/tr35-31/tr35.html#Likely_Subtags
$variants = array();
@@ -255,10 +169,10 @@ class CldrData
if (isset($languageNames[$variant])) {
$languageName = $languageNames[$variant];
if (isset($scriptName) && (!in_array($variant, $variantsWithScript))) {
$languageName = $scriptName.' '.$languageName;
$languageName = $scriptName . ' ' . $languageName;
}
if (isset($territoryName) && (!in_array($variant, $variantsWithTerritory))) {
$languageName .= ' ('.$territoryNames[$territoryId].')';
$languageName .= ' (' . $territoryNames[$territoryId] . ')';
}
break;
}
@@ -313,4 +227,115 @@ class CldrData
return $result;
}
/**
* Returns the loaded CLDR data.
*
* @param string $key Can be 'languages', 'territories', 'plurals', 'supersededLanguages', 'scripts', 'standAloneScripts'
*
* @return array
*/
private static function getData($key)
{
if (!isset(self::$data)) {
$fixKeys = function ($list, &$standAlone = null) {
$result = array();
$standAlone = array();
$match = null;
foreach ($list as $key => $value) {
$variant = '';
if (preg_match('/^(.+)-alt-(short|variant|stand-alone|long|menu)$/', $key, $match)) {
$key = $match[1];
$variant = $match[2];
}
$key = str_replace('-', '_', $key);
switch ($key) {
case 'root': // Language: Root
case 'und': // Language: Unknown Language
case 'zxx': // Language: No linguistic content
case 'ZZ': // Territory: Unknown Region
case 'Zinh': // Script: Inherited
case 'Zmth': // Script: Mathematical Notation
case 'Zsym': // Script: Symbols
case 'Zxxx': // Script: Unwritten
case 'Zyyy': // Script: Common
case 'Zzzz': // Script: Unknown Script
break;
default:
switch ($variant) {
case 'stand-alone':
$standAlone[$key] = $value;
break;
case '':
$result[$key] = $value;
break;
}
break;
}
}
return $result;
};
$data = array();
$json = json_decode(file_get_contents(__DIR__ . '/cldr-data/main/en-US/languages.json'), true);
$data['languages'] = $fixKeys($json['main']['en-US']['localeDisplayNames']['languages']);
$json = json_decode(file_get_contents(__DIR__ . '/cldr-data/main/en-US/territories.json'), true);
$data['territories'] = $fixKeys($json['main']['en-US']['localeDisplayNames']['territories']);
$json = json_decode(file_get_contents(__DIR__ . '/cldr-data/supplemental/plurals.json'), true);
$data['plurals'] = $fixKeys($json['supplemental']['plurals-type-cardinal']);
$json = json_decode(file_get_contents(__DIR__ . '/cldr-data/main/en-US/scripts.json'), true);
$data['scripts'] = $fixKeys($json['main']['en-US']['localeDisplayNames']['scripts'], $data['standAloneScripts']);
$data['standAloneScripts'] = array_merge($data['scripts'], $data['standAloneScripts']);
$data['scripts'] = array_merge($data['standAloneScripts'], $data['scripts']);
$data['supersededLanguages'] = array();
// Remove the languages for which we don't have plurals
$m = null;
foreach (array_keys(array_diff_key($data['languages'], $data['plurals'])) as $missingPlural) {
if (preg_match('/^([a-z]{2,3})_/', $missingPlural, $m)) {
if (!isset($data['plurals'][$m[1]])) {
unset($data['languages'][$missingPlural]);
}
} else {
unset($data['languages'][$missingPlural]);
}
}
// Fix the languages for which we have plurals
$formerCodes = array(
'in' => 'id', // former Indonesian
'iw' => 'he', // former Hebrew
'ji' => 'yi', // former Yiddish
'jw' => 'jv', // former Javanese
'mo' => 'ro_MD', // former Moldavian
);
$knownMissingLanguages = array(
'guw' => 'Gun',
'nah' => 'Nahuatl',
'smi' => 'Sami',
);
foreach (array_keys(array_diff_key($data['plurals'], $data['languages'])) as $missingLanguage) {
if (isset($formerCodes[$missingLanguage]) && isset($data['languages'][$formerCodes[$missingLanguage]])) {
$data['languages'][$missingLanguage] = $data['languages'][$formerCodes[$missingLanguage]];
$data['supersededLanguages'][$missingLanguage] = $formerCodes[$missingLanguage];
} else {
if (isset($knownMissingLanguages[$missingLanguage])) {
$data['languages'][$missingLanguage] = $knownMissingLanguages[$missingLanguage];
} else {
throw new Exception("We have the plural rule for the language '${missingLanguage}' but we don't have its language name");
}
}
}
ksort($data['languages'], SORT_STRING);
ksort($data['territories'], SORT_STRING);
ksort($data['plurals'], SORT_STRING);
ksort($data['scripts'], SORT_STRING);
ksort($data['standAloneScripts'], SORT_STRING);
ksort($data['supersededLanguages'], SORT_STRING);
self::$data = $data;
}
if (!isset(self::$data[$key])) {
throw new Exception("Invalid CLDR data key: '${key}'");
}
return self::$data[$key];
}
}

View File

@@ -1,10 +1,33 @@
<?php
namespace Gettext\Languages\Exporter;
class Docs extends Html
{
/**
* @see Exporter::toStringDo
* {@inheritdoc}
*
* @see \Gettext\Languages\Exporter\Exporter::isForPublicUse()
*/
public static function isForPublicUse()
{
return false;
}
/**
* {@inheritdoc}
*
* @see \Gettext\Languages\Exporter\Exporter::getDescription()
*/
public static function getDescription()
{
return 'Build the page https://php-gettext.github.io/Languages/';
}
/**
* {@inheritdoc}
*
* @see \Gettext\Languages\Exporter\Exporter::toStringDo()
*/
protected static function toStringDo($languages)
{
@@ -18,11 +41,11 @@ class Docs extends Html
<meta name="author" content="Michele Locati">
<title>gettext plural rules - built from CLDR</title>
<meta name="description" content="List of all language rules for gettext .po files automatically generated from the Unicode CLDR data" />
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<a href="https://github.com/mlocati/cldr-to-gettext-plural-rules" class="hidden-xs"><img style="position: fixed; top: 0; right: 0; border: 0; z-index: 2000" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub"></a>
<a href="https://github.com/php-gettext/Languages" class="hidden-xs"><img style="position: fixed; top: 0; right: 0; border: 0; z-index: 2000" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub"></a>
<div class="container-fluid">
EOT;
@@ -30,7 +53,7 @@ EOT;
$result .= <<<EOT
</div>
<script src="http://code.jquery.com/jquery-1.11.2.min.js"></script>
<script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
<script src="script.js"></script>
</body>
</html>
@@ -38,18 +61,4 @@ EOT;
return $result;
}
/**
* @see Exporter::isForPublicUse
*/
public static function isForPublicUse()
{
return false;
}
/**
* @see Exporter::getDescription
*/
public static function getDescription()
{
return 'Build the page http://mlocati.github.io/cldr-to-gettext-plural-rules';
}
}

View File

@@ -1,8 +1,8 @@
<?php
namespace Gettext\Languages\Exporter;
use Exception;
use Gettext\Languages\Language;
/**
* Base class for all the exporters.
@@ -13,9 +13,12 @@ abstract class Exporter
* @var array
*/
private static $exporters;
/**
* Return the list of all the available exporters. Keys are the exporter handles, values are the exporter class names.
* @param bool $onlyForPublicUse If true, internal exporters will be omitted.
*
* @param bool $onlyForPublicUse if true, internal exporters will be omitted
*
* @return string[]
*/
final public static function getExporters($onlyForPublicUse = false)
@@ -35,7 +38,7 @@ abstract class Exporter
if ($onlyForPublicUse) {
$result = array();
foreach (self::$exporters as $handle => $class) {
if (call_user_func(self::getExporterClassName($handle).'::isForPublicUse') === true) {
if (call_user_func(self::getExporterClassName($handle) . '::isForPublicUse') === true) {
$result[$handle] = $class;
}
}
@@ -45,42 +48,43 @@ abstract class Exporter
return $result;
}
/**
* Return the description of a specific exporter.
* @param string $exporterHandle The handle of the exporter.
* @throws Exception Throws an Exception if $exporterHandle is not valid.
*
* @param string $exporterHandle the handle of the exporter
*
* @throws \Exception throws an Exception if $exporterHandle is not valid
*
* @return string
*/
final public static function getExporterDescription($exporterHandle)
{
$exporters = self::getExporters();
if (!isset($exporters[$exporterHandle])) {
throw new Exception("Invalid exporter handle: '$exporterHandle'");
throw new Exception("Invalid exporter handle: '${exporterHandle}'");
}
return call_user_func(self::getExporterClassName($exporterHandle).'::getDescription');
return call_user_func(self::getExporterClassName($exporterHandle) . '::getDescription');
}
/**
* Returns the fully qualified class name of a exporter given its handle.
* @param string $exporterHandle The exporter class handle.
*
* @param string $exporterHandle the exporter class handle
*
* @return string
*/
final public static function getExporterClassName($exporterHandle)
{
return __NAMESPACE__.'\\'.ucfirst(strtolower($exporterHandle));
return __NAMESPACE__ . '\\' . ucfirst(strtolower($exporterHandle));
}
/**
* Convert a list of Language instances to string.
* @param Language[] $languages The Language instances to convert.
* @return string
*/
protected static function toStringDo($languages)
{
throw new Exception(get_called_class().' does not implement the method '.__FUNCTION__);
}
/**
* Convert a list of Language instances to string.
* @param Language[] $languages The Language instances to convert.
*
* @param \Gettext\Languages\Language[] $languages the Language instances to convert
*
* @return string
*/
final public static function toString($languages, $options = null)
@@ -97,32 +101,51 @@ abstract class Exporter
return static::toStringDo($languages);
}
/**
* Save the Language instances to a file.
* @param Language[] $languages The Language instances to convert.
* @throws Exception
*
* @param \Gettext\Languages\Language[] $languages the Language instances to convert
*
* @throws \Exception
*/
final public static function toFile($languages, $filename, $options = null)
{
$data = self::toString($languages, $options);
if (@file_put_contents($filename, $data) === false) {
throw new Exception("Error writing data to '$filename'");
throw new Exception("Error writing data to '${filename}'");
}
}
/**
* Is this exporter for public use?
*
* @return bool
*/
public static function isForPublicUse()
{
return true;
}
/**
* Return a short description of the exporter.
*
* @return string
*/
public static function getDescription()
{
throw new Exception(get_called_class().' does not implement the method '.__FUNCTION__);
throw new Exception(get_called_class() . ' does not implement the method ' . __FUNCTION__);
}
/**
* Convert a list of Language instances to string.
*
* @param \Gettext\Languages\Language[] $languages the Language instances to convert
*
* @return string
*/
protected static function toStringDo($languages)
{
throw new Exception(get_called_class() . ' does not implement the method ' . __FUNCTION__);
}
}

View File

@@ -1,61 +1,69 @@
<?php
namespace Gettext\Languages\Exporter;
class Html extends Exporter
{
/**
* @see Exporter::toStringDo
*/
protected static function toStringDo($languages)
{
return self::buildTable($languages, false);
}
protected static function h($str)
{
return htmlspecialchars($str, ENT_COMPAT, 'UTF-8');
}
protected static function buildTable($languages, $forDocs)
{
$prefix = $forDocs ? ' ' : '';
$lines = array();
$lines[] = $prefix.'<table'.($forDocs ? ' class="table table-bordered table-condensed table-striped"' : '').'>';
$lines[] = $prefix.' <thead>';
$lines[] = $prefix.' <tr>';
$lines[] = $prefix.' <th>Language code</th>';
$lines[] = $prefix.' <th>Language name</th>';
$lines[] = $prefix.' <th># plurals</th>';
$lines[] = $prefix.' <th>Formula</th>';
$lines[] = $prefix.' <th>Plurals</th>';
$lines[] = $prefix.' </tr>';
$lines[] = $prefix.' </thead>';
$lines[] = $prefix.' <tbody>';
foreach ($languages as $lc) {
$lines[] = $prefix.' <tr>';
$lines[] = $prefix.' <td>'.$lc->id.'</td>';
$name = self::h($lc->name);
if (isset($lc->supersededBy)) {
$name .= '<br /><small><span>Superseded by</span> '.$lc->supersededBy.'</small>';
}
$lines[] = $prefix.' <td>'.$name.'</td>';
$lines[] = $prefix.' <td>'.count($lc->categories).'</td>';
$lines[] = $prefix.' <td>'.self::h($lc->formula).'</td>';
$cases = array();
foreach ($lc->categories as $c) {
$cases[] = '<li><span>'.$c->id.'</span><code>'.self::h($c->examples).'</code></li>';
}
$lines[] = $prefix.' <td><ol'.($forDocs ? ' class="cases"' : '').' start="0">'.implode('', $cases).'</ol></td>';
$lines[] = $prefix.' </tr>';
}
$lines[] = $prefix.' </tbody>';
$lines[] = $prefix.'</table>';
return implode("\n", $lines);
}
/**
* @see Exporter::getDescription
* {@inheritdoc}
*
* @see \Gettext\Languages\Exporter\Exporter::getDescription()
*/
public static function getDescription()
{
return 'Build a HTML table';
}
/**
* {@inheritdoc}
*
* @see \Gettext\Languages\Exporter\Exporter::toStringDo()
*/
protected static function toStringDo($languages)
{
return self::buildTable($languages, false);
}
protected static function h($str)
{
return htmlspecialchars($str, ENT_COMPAT, 'UTF-8');
}
protected static function buildTable($languages, $forDocs)
{
$prefix = $forDocs ? ' ' : '';
$lines = array();
$lines[] = $prefix . '<table' . ($forDocs ? ' class="table table-bordered table-condensed table-striped"' : '') . '>';
$lines[] = $prefix . ' <thead>';
$lines[] = $prefix . ' <tr>';
$lines[] = $prefix . ' <th>Language code</th>';
$lines[] = $prefix . ' <th>Language name</th>';
$lines[] = $prefix . ' <th># plurals</th>';
$lines[] = $prefix . ' <th>Formula</th>';
$lines[] = $prefix . ' <th>Plurals</th>';
$lines[] = $prefix . ' </tr>';
$lines[] = $prefix . ' </thead>';
$lines[] = $prefix . ' <tbody>';
foreach ($languages as $lc) {
$lines[] = $prefix . ' <tr>';
$lines[] = $prefix . ' <td>' . $lc->id . '</td>';
$name = self::h($lc->name);
if (isset($lc->supersededBy)) {
$name .= '<br /><small><span>Superseded by</span> ' . $lc->supersededBy . '</small>';
}
$lines[] = $prefix . ' <td>' . $name . '</td>';
$lines[] = $prefix . ' <td>' . count($lc->categories) . '</td>';
$lines[] = $prefix . ' <td>' . self::h($lc->formula) . '</td>';
$cases = array();
foreach ($lc->categories as $c) {
$cases[] = '<li><span>' . $c->id . '</span><code>' . self::h($c->examples) . '</code></li>';
}
$lines[] = $prefix . ' <td><ol' . ($forDocs ? ' class="cases"' : '') . ' start="0">' . implode('', $cases) . '</ol></td>';
$lines[] = $prefix . ' </tr>';
}
$lines[] = $prefix . ' </tbody>';
$lines[] = $prefix . '</table>';
return implode("\n", $lines);
}
}

View File

@@ -1,10 +1,22 @@
<?php
namespace Gettext\Languages\Exporter;
class Json extends Exporter
{
/**
* {@inheritdoc}
*
* @see \Gettext\Languages\Exporter\Exporter::getDescription()
*/
public static function getDescription()
{
return 'Build a compressed JSON-encoded file';
}
/**
* Return the options for json_encode.
*
* @return int
*/
protected static function getEncodeOptions()
@@ -19,8 +31,11 @@ class Json extends Exporter
return $result;
}
/**
* @see Exporter::toStringDo
* {@inheritdoc}
*
* @see \Gettext\Languages\Exporter\Exporter::toStringDo()
*/
protected static function toStringDo($languages)
{
@@ -53,11 +68,4 @@ class Json extends Exporter
return json_encode($list, static::getEncodeOptions());
}
/**
* @see Exporter::getDescription
*/
public static function getDescription()
{
return 'Build a compressed JSON-encoded file';
}
}

View File

@@ -1,10 +1,23 @@
<?php
namespace Gettext\Languages\Exporter;
class Php extends Exporter
{
/**
* @see Exporter::toStringDo
* {@inheritdoc}
*
* @see \Gettext\Languages\Exporter\Exporter::getDescription()
*/
public static function getDescription()
{
return 'Build a PHP array';
}
/**
* {@inheritdoc}
*
* @see \Gettext\Languages\Exporter\Exporter::toStringDo()
*/
protected static function toStringDo($languages)
{
@@ -12,30 +25,30 @@ class Php extends Exporter
$lines[] = '<?php';
$lines[] = 'return array(';
foreach ($languages as $lc) {
$lines[] = ' \''.$lc->id.'\' => array(';
$lines[] = ' \'name\' => \''.addslashes($lc->name).'\',';
$lines[] = ' \'' . $lc->id . '\' => array(';
$lines[] = ' \'name\' => \'' . addslashes($lc->name) . '\',';
if (isset($lc->supersededBy)) {
$lines[] = ' \'supersededBy\' => \''.$lc->supersededBy.'\',';
$lines[] = ' \'supersededBy\' => \'' . $lc->supersededBy . '\',';
}
if (isset($lc->script)) {
$lines[] = ' \'script\' => \''.addslashes($lc->script).'\',';
$lines[] = ' \'script\' => \'' . addslashes($lc->script) . '\',';
}
if (isset($lc->territory)) {
$lines[] = ' \'territory\' => \''.addslashes($lc->territory).'\',';
$lines[] = ' \'territory\' => \'' . addslashes($lc->territory) . '\',';
}
if (isset($lc->baseLanguage)) {
$lines[] = ' \'baseLanguage\' => \''.addslashes($lc->baseLanguage).'\',';
$lines[] = ' \'baseLanguage\' => \'' . addslashes($lc->baseLanguage) . '\',';
}
$lines[] = ' \'formula\' => \''.$lc->formula.'\',';
$lines[] = ' \'plurals\' => '.count($lc->categories).',';
$lines[] = ' \'formula\' => \'' . $lc->formula . '\',';
$lines[] = ' \'plurals\' => ' . count($lc->categories) . ',';
$catNames = array();
foreach ($lc->categories as $c) {
$catNames[] = "'{$c->id}'";
}
$lines[] = ' \'cases\' => array('.implode(', ', $catNames).'),';
$lines[] = ' \'cases\' => array(' . implode(', ', $catNames) . '),';
$lines[] = ' \'examples\' => array(';
foreach ($lc->categories as $c) {
$lines[] = ' \''.$c->id.'\' => \''.$c->examples.'\',';
$lines[] = ' \'' . $c->id . '\' => \'' . $c->examples . '\',';
}
$lines[] = ' ),';
$lines[] = ' ),';
@@ -45,11 +58,4 @@ class Php extends Exporter
return implode("\n", $lines);
}
/**
* @see Exporter::getDescription
*/
public static function getDescription()
{
return 'Build a PHP array';
}
}

View File

@@ -1,4 +1,5 @@
<?php
namespace Gettext\Languages\Exporter;
use Exception;
@@ -6,26 +7,31 @@ use Exception;
class Po extends Exporter
{
/**
* @see Exporter::toStringDo
*/
protected static function toStringDo($languages)
{
if (count($languages) !== 1) {
throw new Exception('The '.get_called_class().' exporter can only export one language');
}
$language = $languages[0];
$lines = array();
$lines[] = '"Language: '.$language->id.'\n"';
$lines[] = '"Plural-Forms: nplurals='.count($language->categories).'; plural='.$language->formula.'\n"';
$lines[] = '';
return implode("\n", $lines);
}
/**
* @see Exporter::getDescription
* {@inheritdoc}
*
* @see \Gettext\Languages\Exporter\Exporter::getDescription()
*/
public static function getDescription()
{
return 'Build a string to be used for gettext .po files';
}
/**
* {@inheritdoc}
*
* @see \Gettext\Languages\Exporter\Exporter::toStringDo()
*/
protected static function toStringDo($languages)
{
if (count($languages) !== 1) {
throw new Exception('The ' . get_called_class() . ' exporter can only export one language');
}
$language = $languages[0];
$lines = array();
$lines[] = '"Language: ' . $language->id . '\n"';
$lines[] = '"Plural-Forms: nplurals=' . count($language->categories) . '; plural=' . $language->formula . '\n"';
$lines[] = '';
return implode("\n", $lines);
}
}

View File

@@ -1,4 +1,5 @@
<?php
namespace Gettext\Languages\Exporter;
use Exception;
@@ -6,20 +7,26 @@ use Exception;
class Prettyjson extends Json
{
/**
* @see Json::getEncodeOptions
* {@inheritdoc}
*
* @see \Gettext\Languages\Exporter\Exporter::getDescription()
*/
public static function getDescription()
{
return 'Build an uncompressed JSON-encoded file (PHP 5.4 or later is needed)';
}
/**
* {@inheritdoc}
*
* @see \Gettext\Languages\Exporter\Json::getEncodeOptions()
*/
protected static function getEncodeOptions()
{
if (!(defined('\JSON_PRETTY_PRINT') && defined('\JSON_UNESCAPED_SLASHES') && defined('\JSON_UNESCAPED_UNICODE'))) {
throw new Exception('PHP 5.4 or later is required to export uncompressed JSON');
}
return \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE;
}
/**
* @see Exporter::getDescription
*/
public static function getDescription()
{
return 'Build an uncompressed JSON-encoded file (PHP 5.4 or later is needed)';
}
}

View File

@@ -1,10 +1,23 @@
<?php
namespace Gettext\Languages\Exporter;
class Xml extends Exporter
{
/**
* @see Exporter::toStringDo
* {@inheritdoc}
*
* @see \Gettext\Languages\Exporter\Exporter::getDescription()
*/
public static function getDescription()
{
return 'Build an XML file - schema available at http://mlocati.github.io/cldr-to-gettext-plural-rules/GettextLanguages.xsd';
}
/**
* {@inheritdoc}
*
* @see \Gettext\Languages\Exporter\Exporter::toStringDo()
*/
protected static function toStringDo($languages)
{
@@ -44,11 +57,4 @@ class Xml extends Exporter
return $xml->saveXML();
}
/**
* @see Exporter::getDescription
*/
public static function getDescription()
{
return 'Build an XML file - schema available at http://mlocati.github.io/cldr-to-gettext-plural-rules/GettextLanguages.xsd';
}
}

View File

@@ -1,4 +1,5 @@
<?php
namespace Gettext\Languages;
use Exception;
@@ -10,14 +11,17 @@ class FormulaConverter
{
/**
* Converts a formula from the CLDR representation to the gettext representation.
* @param string $cldrFormula The CLDR formula to convert.
* @throws Exception
* @return bool|string Returns true if the gettext will always evaluate to true, false if gettext will always evaluate to false, return the gettext formula otherwise.
*
* @param string $cldrFormula the CLDR formula to convert
*
* @throws \Exception
*
* @return bool|string returns true if the gettext will always evaluate to true, false if gettext will always evaluate to false, return the gettext formula otherwise
*/
public static function convertFormula($cldrFormula)
{
if (strpbrk($cldrFormula, '()') !== false) {
throw new Exception("Unable to convert the formula '$cldrFormula': parenthesis handling not implemented");
throw new Exception("Unable to convert the formula '${cldrFormula}': parenthesis handling not implemented");
}
$orSeparatedChunks = array();
foreach (explode(' or ', $cldrFormula) as $cldrFormulaChunk) {
@@ -29,7 +33,8 @@ class FormulaConverter
// One atom joined by 'and' always evaluates to false => the whole 'and' group is always false
$gettextFormulaChunk = false;
break;
} elseif ($gettextAtom !== true) {
}
if ($gettextAtom !== true) {
$andSeparatedChunks[] = $gettextAtom;
}
}
@@ -50,22 +55,27 @@ class FormulaConverter
if ($gettextFormulaChunk === true) {
// One part of the formula joined with the others by 'or' always evaluates to true => the whole formula always evaluates to true
return true;
} elseif ($gettextFormulaChunk !== false) {
}
if ($gettextFormulaChunk !== false) {
$orSeparatedChunks[] = $gettextFormulaChunk;
}
}
if (empty($orSeparatedChunks)) {
// All the parts joined by 'or' always evaluate to false => the whole formula always evaluates to false
return false;
} else {
return implode(' || ', $orSeparatedChunks);
}
return implode(' || ', $orSeparatedChunks);
}
/**
* Converts an atomic part of the CLDR formula to its gettext representation.
* @param string $cldrAtom The CLDR formula atom to convert.
* @throws Exception
* @return bool|string Returns true if the gettext will always evaluate to true, false if gettext will always evaluate to false, return the gettext formula otherwise.
*
* @param string $cldrAtom the CLDR formula atom to convert
*
* @throws \Exception
*
* @return bool|string returns true if the gettext will always evaluate to true, false if gettext will always evaluate to false, return the gettext formula otherwise
*/
private static function convertAtom($cldrAtom)
{
@@ -80,23 +90,27 @@ class FormulaConverter
return self::expandAtom($gettextAtom);
}
if (preg_match('/^(?:v|w)(?: % 10+)? == (\d+)(?:\.\.\d+)?$/', $gettextAtom, $m)) { // For gettext: v == 0, w == 0
return (intval($m[1]) === 0) ? true : false;
return (int) $m[1] === 0 ? true : false;
}
if (preg_match('/^(?:v|w)(?: % 10+)? != (\d+)(?:\.\.\d+)?$/', $gettextAtom, $m)) { // For gettext: v == 0, w == 0
return (intval($m[1]) === 0) ? false : true;
return (int) $m[1] === 0 ? false : true;
}
if (preg_match('/^(?:f|t)(?: % 10+)? == (\d+)(?:\.\.\d+)?$/', $gettextAtom, $m)) { // f == empty, t == empty
return (intval($m[1]) === 0) ? true : false;
return (int) $m[1] === 0 ? true : false;
}
if (preg_match('/^(?:f|t)(?: % 10+)? != (\d+)(?:\.\.\d+)?$/', $gettextAtom, $m)) { // f == empty, t == empty
return (intval($m[1]) === 0) ? false : true;
return (int) $m[1] === 0 ? false : true;
}
throw new Exception("Unable to convert the formula chunk '$cldrAtom' from CLDR to gettext");
throw new Exception("Unable to convert the formula chunk '${cldrAtom}' from CLDR to gettext");
}
/**
* Expands an atom containing a range (for instance: 'n == 1,3..5').
*
* @param string $atom
* @throws Exception
*
* @throws \Exception
*
* @return string
*/
private static function expandAtom($atom)
@@ -109,37 +123,37 @@ class FormulaConverter
foreach (explode(',', $m[3]) as $range) {
$chunk = null;
if ((!isset($chunk)) && preg_match('/^\d+$/', $range)) {
$chunk = "$what $op $range";
$chunk = "${what} ${op} ${range}";
}
if ((!isset($chunk)) && preg_match('/^(\d+)\.\.(\d+)$/', $range, $m)) {
$from = intval($m[1]);
$to = intval($m[2]);
$from = (int) $m[1];
$to = (int) $m[2];
if (($to - $from) === 1) {
switch ($op) {
case '==':
$chunk = "($what == $from || $what == $to)";
$chunk = "(${what} == ${from} || ${what} == ${to})";
break;
case '!=':
$chunk = "$what != $from && $what == $to";
$chunk = "${what} != ${from} && ${what} == ${to}";
break;
}
} else {
switch ($op) {
case '==':
$chunk = "$what >= $from && $what <= $to";
$chunk = "${what} >= ${from} && ${what} <= ${to}";
break;
case '!=':
if ($what === 'n' && $from <= 0) {
$chunk = "$what > $to";
$chunk = "${what} > ${to}";
} else {
$chunk = "($what < $from || $what > $to)";
$chunk = "(${what} < ${from} || ${what} > ${to})";
}
break;
}
}
}
if (!isset($chunk)) {
throw new Exception("Unhandled range '$range' in '$atom'");
throw new Exception("Unhandled range '${range}' in '${atom}'");
}
$chunks[] = $chunk;
}
@@ -148,11 +162,11 @@ class FormulaConverter
}
switch ($op) {
case '==':
return '('.implode(' || ', $chunks).')';break;
return '(' . implode(' || ', $chunks) . ')'; break;
case '!=':
return implode(' && ', $chunks);
}
}
throw new Exception("Unable to expand '$atom'");
throw new Exception("Unable to expand '${atom}'");
}
}

View File

@@ -1,4 +1,5 @@
<?php
namespace Gettext\Languages;
use Exception;
@@ -10,48 +11,66 @@ class Language
{
/**
* The language ID.
*
* @var string
*/
public $id;
/**
* The language name.
*
* @var string
*/
public $name;
/**
* If this language is deprecated: the gettext code of the new language.
* @var null|string
*
* @var string|null
*/
public $supersededBy;
/**
* The script name.
*
* @var string|null
*/
public $script;
/**
* The territory name.
*
* @var string|null
*/
public $territory;
/**
* The name of the base language
* The name of the base language.
*
* @var string|null
*/
public $baseLanguage;
/**
* The list of categories.
* @var Category[]
*
* @var \Gettext\Languages\Category[]
*/
public $categories;
/**
* The gettext formula to decide which category should be applied.
*
* @var string
*/
public $formula;
/**
* Initialize the instance and parse the language code.
*
* @param array $info The result of CldrData::getLanguageInfo()
* @throws Exception Throws an Exception if $fullId is not valid.
*
* @throws \Exception throws an Exception if $fullId is not valid
*/
private function __construct($info)
{
@@ -81,47 +100,108 @@ class Language
});
// The 'other' category should always be there
if ($this->categories[count($this->categories) - 1]->id !== CldrData::OTHER_CATEGORY) {
throw new Exception("The language '{$info['id']}' does not have the '".CldrData::OTHER_CATEGORY."' plural category");
throw new Exception("The language '{$info['id']}' does not have the '" . CldrData::OTHER_CATEGORY . "' plural category");
}
$this->checkAlwaysTrueCategories();
$this->checkAlwaysFalseCategories();
$this->checkAllCategoriesWithExamples();
$this->formula = $this->buildFormula();
}
/**
* Return a list of all languages available.
* @throws Exception
* @return Language[]
*
* @throws \Exception
*
* @return \Gettext\Languages\Language[]
*/
public static function getAll()
{
$result = array();
foreach (array_keys(CldrData::getLanguageNames()) as $cldrLanguageId) {
$result[] = new Language(CldrData::getLanguageInfo($cldrLanguageId));
$result[] = new self(CldrData::getLanguageInfo($cldrLanguageId));
}
return $result;
}
/**
* Return a Language instance given the language id
* Return a Language instance given the language id.
*
* @param string $id
* @return Language|null
*
* @return \Gettext\Languages\Language|null
*/
public static function getById($id)
{
$result = null;
$info = CldrData::getLanguageInfo($id);
if (isset($info)) {
$result = new Language($info);
$result = new self($info);
}
return $result;
}
/**
* Returns a clone of this instance with all the strings to US-ASCII.
*
* @return \Gettext\Languages\Language
*/
public function getUSAsciiClone()
{
$clone = clone $this;
self::asciifier($clone->name);
self::asciifier($clone->formula);
$clone->categories = array();
foreach ($this->categories as $category) {
$categoryClone = clone $category;
self::asciifier($categoryClone->examples);
$clone->categories[] = $categoryClone;
}
return $clone;
}
/**
* Build the formula starting from the currently defined categories.
*
* @param bool $withoutParenthesis TRUE to build a formula in standard gettext format, FALSE (default) to build a PHP-compatible formula
*
* @return string
*/
public function buildFormula($withoutParenthesis = false)
{
$numCategories = count($this->categories);
switch ($numCategories) {
case 1:
// Just one category
return '0';
case 2:
return self::reduceFormula(self::reverseFormula($this->categories[0]->formula));
default:
$formula = (string) ($numCategories - 1);
for ($i = $numCategories - 2; $i >= 0; $i--) {
$f = self::reduceFormula($this->categories[$i]->formula);
if (!$withoutParenthesis && !preg_match('/^\([^()]+\)$/', $f)) {
$f = "(${f})";
}
$formula = "${f} ? ${i} : ${formula}";
if (!$withoutParenthesis && $i > 0) {
$formula = "(${formula})";
}
}
return $formula;
}
}
/**
* Let's look for categories that will always occur.
* This because with decimals (CLDR) we may have more cases, with integers (gettext) we have just one case.
* If we found that (single) category we reduce the categories to that one only.
*
* @throws \Exception
*/
private function checkAlwaysTrueCategories()
{
@@ -146,10 +226,13 @@ class Language
$this->categories = array($alwaysTrueCategory);
}
}
/**
* Let's look for categories that will never occur.
* This because with decimals (CLDR) we may have more cases, with integers (gettext) we have some less cases.
* If we found those categories we strip them out.
*
* @throws \Exception
*/
private function checkAlwaysFalseCategories()
{
@@ -165,11 +248,13 @@ class Language
}
$this->categories = $filtered;
}
/**
* Let's look for categories that don't have examples.
* This because with decimals (CLDR) we may have more cases, with integers (gettext) we have some less cases.
* If we found those categories, we check that they never occur and we strip them out.
* @throws Exception
*
* @throws \Exception
*/
private function checkAllCategoriesWithExamples()
{
@@ -190,8 +275,8 @@ class Language
return;
}
$removeCategoriesWithoutExamples = false;
switch (implode(',', $badCategoriesIds).'@'.implode(',', $allCategoriesIds)) {
case CldrData::OTHER_CATEGORY.'@one,few,many,'.CldrData::OTHER_CATEGORY:
switch (implode(',', $badCategoriesIds) . '@' . implode(',', $allCategoriesIds)) {
case CldrData::OTHER_CATEGORY . '@one,few,many,' . CldrData::OTHER_CATEGORY:
switch ($this->buildFormula()) {
case '(n % 10 == 1 && n % 100 != 11) ? 0 : ((n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14)) ? 1 : ((n % 10 == 0 || n % 10 >= 5 && n % 10 <= 9 || n % 100 >= 11 && n % 100 <= 14) ? 2 : 3))':
// Numbers ending with 0 => case 2 ('many')
@@ -232,7 +317,7 @@ class Language
}
}
if (!$removeCategoriesWithoutExamples) {
throw new Exception("Unhandled case of plural categories without examples '".implode(', ', $badCategoriesIds)."' out of '".implode(', ', $allCategoriesIds)."'");
throw new Exception("Unhandled case of plural categories without examples '" . implode(', ', $badCategoriesIds) . "' out of '" . implode(', ', $allCategoriesIds) . "'");
}
if ($badCategories[count($badCategories) - 1]->id === CldrData::OTHER_CATEGORY) {
// We're removing the 'other' cagory: let's change the last good category to 'other'
@@ -242,39 +327,14 @@ class Language
}
$this->categories = $goodCategories;
}
/**
* Build the formula starting from the currently defined categories.
* @return string
*/
private function buildFormula()
{
$numCategories = count($this->categories);
switch ($numCategories) {
case 1:
// Just one category
return '0';
case 2:
return self::reduceFormula(self::reverseFormula($this->categories[0]->formula));
default:
$formula = strval($numCategories - 1);
for ($i = $numCategories - 2; $i >= 0; $i--) {
$f = self::reduceFormula($this->categories[$i]->formula);
if (!preg_match('/^\([^()]+\)$/', $f)) {
$f = "($f)";
}
$formula = "$f ? $i : $formula";
if ($i > 0) {
$formula = "($formula)";
}
}
return $formula;
}
}
/**
* Reverse a formula.
*
* @param string $formula
* @throws Exception
*
* @throws \Exception
*
* @return string
*/
private static function reverseFormula($formula)
@@ -298,30 +358,36 @@ class Language
case '(n == 0 || n == 1) || n >= 11 && n <= 99':
return 'n >= 2 && (n < 11 || n > 99)';
}
throw new Exception("Unable to reverse the formula '$formula'");
throw new Exception("Unable to reverse the formula '${formula}'");
}
/**
* Reduce some excessively complex formulas.
*
* @param string $formula
*
* @return string
*/
private static function reduceFormula($formula)
{
$map = array(
'n != 0 && n != 1' => 'n > 1' ,
'(n == 0 || n == 1) && n != 0' => 'n == 1',
'n != 0 && n != 1' => 'n > 1',
'(n == 0 || n == 1) && n != 0' => 'n == 1',
);
return isset($map[$formula]) ? $map[$formula] : $formula;
}
/**
* Take one variable and, if it's a string, we transliterate it to US-ASCII.
* @param mixed $value The variable to work on.
* @throws Exception
*
* @param mixed $value the variable to work on
*
* @throws \Exception
*/
private static function asciifier(&$value)
{
if (is_string($value) && ($value !== '')) {
if (is_string($value) && $value !== '') {
// Avoid converting from 'Ÿ' to '"Y', let's prefer 'Y'
$transliterated = strtr($value, array(
'À' => 'A', 'Á' => 'A', 'Â' => 'A', 'Ã' => 'A', 'Ä' => 'A',
@@ -339,28 +405,10 @@ class Language
'ý' => 'y', 'ÿ' => 'y',
));
$transliterated = @iconv('UTF-8', 'US-ASCII//IGNORE//TRANSLIT', $transliterated);
if (($transliterated === false) || ($transliterated === '')) {
throw new Exception("Unable to transliterate '$value'");
if ($transliterated === false || $transliterated === '') {
throw new Exception("Unable to transliterate '${value}'");
}
$value = $transliterated;
}
}
/**
* Returns a clone of this instance with all the strings to US-ASCII.
* @return Language
*/
public function getUSAsciiClone()
{
$clone = clone $this;
self::asciifier($clone->name);
self::asciifier($clone->formula);
$clone->categories = array();
foreach ($this->categories as $category) {
$categoryClone = clone $category;
self::asciifier($categoryClone->examples);
$clone->categories[] = $categoryClone;
}
return $clone;
}
}

View File

@@ -1,10 +1,11 @@
<?php
spl_autoload_register(
function ($class) {
if (strpos($class, 'Gettext\\Languages\\') !== 0) {
return;
}
$file = __DIR__.str_replace('\\', DIRECTORY_SEPARATOR, substr($class, strlen('Gettext\\Languages'))).'.php';
$file = __DIR__ . str_replace('\\', DIRECTORY_SEPARATOR, substr($class, strlen('Gettext\\Languages'))) . '.php';
if (is_file($file)) {
require_once $file;
}

View File

@@ -3,8 +3,8 @@
"en-US": {
"identity": {
"version": {
"_number": "$Revision: 13133 $",
"_cldrVersion": "31"
"_number": "$Revision$",
"_cldrVersion": "36"
},
"language": "en",
"territory": "US"
@@ -99,6 +99,7 @@
"car": "Carib",
"cay": "Cayuga",
"cch": "Atsam",
"ccp": "Chakma",
"ce": "Chechen",
"ceb": "Cebuano",
"cgg": "Chiga",
@@ -112,6 +113,7 @@
"chp": "Chipewyan",
"chr": "Cherokee",
"chy": "Cheyenne",
"cic": "Chickasaw",
"ckb": "Central Kurdish",
"co": "Corsican",
"cop": "Coptic",
@@ -334,6 +336,7 @@
"ln": "Lingala",
"lo": "Lao",
"lol": "Mongo",
"lou": "Louisiana Creole",
"loz": "Lozi",
"lrc": "Northern Luri",
"lt": "Lithuanian",
@@ -382,7 +385,7 @@
"mt": "Maltese",
"mua": "Mundang",
"mul": "Multiple languages",
"mus": "Creek",
"mus": "Muscogee",
"mwl": "Mirandese",
"mwr": "Marwari",
"mwv": "Mentawai",
@@ -525,6 +528,7 @@
"sog": "Sogdien",
"sq": "Albanian",
"sr": "Serbian",
"sr-ME": "Montenegrin",
"srn": "Sranan Tongo",
"srr": "Serer",
"ss": "Swati",
@@ -621,6 +625,7 @@
"yo": "Yoruba",
"yrl": "Nheengatu",
"yue": "Cantonese",
"yue-alt-menu": "Chinese, Cantonese",
"za": "Zhuang",
"zap": "Zapotec",
"zbl": "Blissymbols",
@@ -629,8 +634,11 @@
"zgh": "Standard Moroccan Tamazight",
"zh": "Chinese",
"zh-alt-long": "Mandarin Chinese",
"zh-alt-menu": "Chinese, Mandarin",
"zh-Hans": "Simplified Chinese",
"zh-Hans-alt-long": "Simplified Mandarin Chinese",
"zh-Hant": "Traditional Chinese",
"zh-Hant-alt-long": "Traditional Mandarin Chinese",
"zu": "Zulu",
"zun": "Zuni",
"zxx": "No linguistic content",

View File

@@ -3,8 +3,8 @@
"en-US": {
"identity": {
"version": {
"_number": "$Revision: 13133 $",
"_cldrVersion": "31"
"_number": "$Revision$",
"_cldrVersion": "36"
},
"language": "en",
"territory": "US"
@@ -44,16 +44,20 @@
"Cyrl": "Cyrillic",
"Cyrs": "Old Church Slavonic Cyrillic",
"Deva": "Devanagari",
"Dogr": "Dogra",
"Dsrt": "Deseret",
"Dupl": "Duployan shorthand",
"Egyd": "Egyptian demotic",
"Egyh": "Egyptian hieratic",
"Egyp": "Egyptian hieroglyphs",
"Elba": "Elbasan",
"Elym": "Elymaic",
"Ethi": "Ethiopic",
"Geok": "Georgian Khutsuri",
"Geor": "Georgian",
"Glag": "Glagolitic",
"Gong": "Gunjala Gondi",
"Gonm": "Masaram Gondi",
"Goth": "Gothic",
"Gran": "Grantha",
"Grek": "Greek",
@@ -72,6 +76,7 @@
"Hira": "Hiragana",
"Hluw": "Anatolian Hieroglyphs",
"Hmng": "Pahawh Hmong",
"Hmnp": "Nyiakeng Puachue Hmong",
"Hrkt": "Japanese syllabaries",
"Hung": "Old Hungarian",
"Inds": "Indus",
@@ -103,10 +108,12 @@
"Lyci": "Lycian",
"Lydi": "Lydian",
"Mahj": "Mahajani",
"Maka": "Makasar",
"Mand": "Mandaean",
"Mani": "Manichaean",
"Marc": "Marchen",
"Maya": "Mayan hieroglyphs",
"Medf": "Medefaidrin",
"Mend": "Mende",
"Merc": "Meroitic Cursive",
"Mero": "Meroitic",
@@ -118,6 +125,7 @@
"Mtei": "Meitei Mayek",
"Mult": "Multani",
"Mymr": "Myanmar",
"Nand": "Nandinagari",
"Narb": "Old North Arabian",
"Nbat": "Nabataean",
"Newa": "Newa",
@@ -140,7 +148,9 @@
"Phnx": "Phoenician",
"Plrd": "Pollard Phonetic",
"Prti": "Inscriptional Parthian",
"Qaag": "Zawgyi",
"Rjng": "Rejang",
"Rohg": "Hanifi Rohingya",
"Roro": "Rongorongo",
"Runr": "Runic",
"Samr": "Samaritan",
@@ -153,7 +163,10 @@
"Sidd": "Siddham",
"Sind": "Khudawadi",
"Sinh": "Sinhala",
"Sogd": "Sogdian",
"Sogo": "Old Sogdian",
"Sora": "Sora Sompeng",
"Soyo": "Soyombo",
"Sund": "Sundanese",
"Sylo": "Syloti Nagri",
"Syrc": "Syriac",
@@ -179,11 +192,13 @@
"Vaii": "Vai",
"Visp": "Visible Speech",
"Wara": "Varang Kshiti",
"Wcho": "Wancho",
"Wole": "Woleai",
"Xpeo": "Old Persian",
"Xsux": "Sumero-Akkadian Cuneiform",
"Xsux-alt-short": "S-A Cuneiform",
"Yiii": "Yi",
"Zanb": "Zanabazar Square",
"Zinh": "Inherited",
"Zmth": "Mathematical Notation",
"Zsye": "Emoji",

View File

@@ -3,8 +3,8 @@
"en-US": {
"identity": {
"version": {
"_number": "$Revision: 13133 $",
"_cldrVersion": "31"
"_number": "$Revision$",
"_cldrVersion": "36"
},
"language": "en",
"territory": "US"
@@ -40,6 +40,7 @@
"151": "Eastern Europe",
"154": "Northern Europe",
"155": "Western Europe",
"202": "Sub-Saharan Africa",
"419": "Latin America",
"AC": "Ascension Island",
"AD": "Andorra",
@@ -199,14 +200,14 @@
"MF": "St. Martin",
"MG": "Madagascar",
"MH": "Marshall Islands",
"MK": "Macedonia",
"MK-alt-variant": "Macedonia (FYROM)",
"MK": "North Macedonia",
"MK-alt-variant": "MK",
"ML": "Mali",
"MM": "Myanmar (Burma)",
"MM-alt-short": "Myanmar",
"MN": "Mongolia",
"MO": "Macau SAR China",
"MO-alt-short": "Macau",
"MO": "Macao SAR China",
"MO-alt-short": "Macao",
"MP": "Northern Mariana Islands",
"MQ": "Martinique",
"MR": "Mauritania",
@@ -273,7 +274,8 @@
"SV": "El Salvador",
"SX": "Sint Maarten",
"SY": "Syria",
"SZ": "Swaziland",
"SZ": "Eswatini",
"SZ-alt-variant": "Swaziland",
"TA": "Tristan da Cunha",
"TC": "Turks & Caicos Islands",
"TD": "Chad",
@@ -310,6 +312,8 @@
"VU": "Vanuatu",
"WF": "Wallis & Futuna",
"WS": "Samoa",
"XA": "Pseudo-Accents",
"XB": "Pseudo-Bidi",
"XK": "Kosovo",
"YE": "Yemen",
"YT": "Mayotte",

View File

@@ -1,9 +1,9 @@
{
"supplemental": {
"version": {
"_number": "$Revision: 13253 $",
"_unicodeVersion": "9.0.0",
"_cldrVersion": "31"
"_number": "$Revision$",
"_unicodeVersion": "12.1.0",
"_cldrVersion": "36"
},
"plurals-type-cardinal": {
"af": {
@@ -18,6 +18,10 @@
"pluralRule-count-one": "i = 0 or n = 1 @integer 0, 1 @decimal 0.0~1.0, 0.00~0.04",
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 1.1~2.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
},
"an": {
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
},
"ar": {
"pluralRule-count-zero": "n = 0 @integer 0 @decimal 0.0, 0.00, 0.000, 0.0000",
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
@@ -68,7 +72,7 @@
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
},
"bh": {
"bho": {
"pluralRule-count-one": "n = 0..1 @integer 0, 1 @decimal 0.0, 1.0, 0.00, 1.00, 0.000, 1.000, 0.0000, 1.0000",
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
},
@@ -106,6 +110,10 @@
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
},
"ceb": {
"pluralRule-count-one": "v = 0 and i = 1,2,3 or v = 0 and i % 10 != 4,6,9 or v != 0 and f % 10 != 4,6,9 @integer 0~3, 5, 7, 8, 10~13, 15, 17, 18, 20, 21, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.3, 0.5, 0.7, 0.8, 1.0~1.3, 1.5, 1.7, 1.8, 2.0, 2.1, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …",
"pluralRule-count-other": " @integer 4, 6, 9, 14, 16, 19, 24, 26, 104, 1004, … @decimal 0.4, 0.6, 0.9, 1.4, 1.6, 1.9, 2.4, 2.6, 10.4, 100.4, 1000.4, …"
},
"cgg": {
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
@@ -286,6 +294,10 @@
"pluralRule-count-one": "i = 0,1 @integer 0, 1 @decimal 0.0~1.5",
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
},
"ia": {
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
},
"id": {
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
},
@@ -298,6 +310,10 @@
"in": {
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
},
"io": {
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
},
"is": {
"pluralRule-count-one": "t = 0 and i % 10 = 1 and i % 100 != 11 or t != 0 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 0.1~1.6, 10.1, 100.1, 1000.1, …",
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
@@ -403,9 +419,12 @@
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
},
"kw": {
"pluralRule-count-zero": "n = 0 @integer 0 @decimal 0.0, 0.00, 0.000, 0.0000",
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
"pluralRule-count-two": "n = 2 @integer 2 @decimal 2.0, 2.00, 2.000, 2.0000",
"pluralRule-count-other": " @integer 0, 3~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
"pluralRule-count-two": "n % 100 = 2,22,42,62,82 or n % 1000 = 0 and n % 100000 = 1000..20000,40000,60000,80000 or n != 0 and n % 1000000 = 100000 @integer 2, 22, 42, 62, 82, 102, 122, 142, 1000, 10000, 100000, … @decimal 2.0, 22.0, 42.0, 62.0, 82.0, 102.0, 122.0, 142.0, 1000.0, 10000.0, 100000.0, …",
"pluralRule-count-few": "n % 100 = 3,23,43,63,83 @integer 3, 23, 43, 63, 83, 103, 123, 143, 1003, … @decimal 3.0, 23.0, 43.0, 63.0, 83.0, 103.0, 123.0, 143.0, 1003.0, …",
"pluralRule-count-many": "n != 1 and n % 100 = 1,21,41,61,81 @integer 21, 41, 61, 81, 101, 121, 141, 161, 1001, … @decimal 21.0, 41.0, 61.0, 81.0, 101.0, 121.0, 141.0, 161.0, 1001.0, …",
"pluralRule-count-other": " @integer 4~19, 100, 1004, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.0, 100.0, 1000.1, 1000000.0, …"
},
"ky": {
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
@@ -458,8 +477,8 @@
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
},
"mk": {
"pluralRule-count-one": "v = 0 and i % 10 = 1 or f % 10 = 1 @integer 1, 11, 21, 31, 41, 51, 61, 71, 101, 1001, … @decimal 0.1, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 10.1, 100.1, 1000.1, …",
"pluralRule-count-other": " @integer 0, 2~10, 12~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 0.2~1.0, 1.2~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
"pluralRule-count-one": "v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 0.1, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 10.1, 100.1, 1000.1, …",
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 0.2~1.0, 1.2~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
},
"ml": {
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
@@ -471,12 +490,12 @@
},
"mo": {
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
"pluralRule-count-few": "v != 0 or n = 0 or n != 1 and n % 100 = 1..19 @integer 0, 2~16, 101, 1001, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …",
"pluralRule-count-few": "v != 0 or n = 0 or n % 100 = 2..19 @integer 0, 2~16, 102, 1002, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …",
"pluralRule-count-other": " @integer 20~35, 100, 1000, 10000, 100000, 1000000, …"
},
"mr": {
"pluralRule-count-one": "i = 0 or n = 1 @integer 0, 1 @decimal 0.0~1.0, 0.00~0.04",
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 1.1~2.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
},
"ms": {
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
@@ -558,6 +577,9 @@
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
},
"osa": {
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
},
"pa": {
"pluralRule-count-one": "n = 0..1 @integer 0, 1 @decimal 0.0, 1.0, 0.00, 1.00, 0.000, 1.000, 0.0000, 1.0000",
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
@@ -585,13 +607,17 @@
"pluralRule-count-one": "i = 0..1 @integer 0, 1 @decimal 0.0~1.5",
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
},
"pt-PT": {
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
},
"rm": {
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
},
"ro": {
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
"pluralRule-count-few": "v != 0 or n = 0 or n != 1 and n % 100 = 1..19 @integer 0, 2~16, 101, 1001, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …",
"pluralRule-count-few": "v != 0 or n = 0 or n % 100 = 2..19 @integer 0, 2~16, 102, 1002, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …",
"pluralRule-count-other": " @integer 20~35, 100, 1000, 10000, 100000, 1000000, …"
},
"rof": {
@@ -618,6 +644,18 @@
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
},
"sc": {
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
},
"scn": {
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
},
"sd": {
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
},
"sdh": {
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
@@ -717,6 +755,9 @@
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
},
"su": {
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
},
"sv": {
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"