58        $this->
id = 
$info[
'id'];
 
   59        $this->name = 
$info[
'name'];
 
   60        $this->supersededBy = isset(
$info[
'supersededBy']) ? 
$info[
'supersededBy'] : 
null;
 
   61        $this->script = isset(
$info[
'script']) ? 
$info[
'script'] : 
null;
 
   62        $this->territory = isset(
$info[
'territory']) ? 
$info[
'territory'] : 
null;
 
   63        $this->baseLanguage = isset(
$info[
'baseLanguage']) ? 
$info[
'baseLanguage'] : 
null;
 
   65        $this->categories = array();
 
   66        foreach (
$info[
'categories'] as $cldrCategoryId => $cldrFormulaAndExamples) {
 
   67            $category = 
new Category($cldrCategoryId, $cldrFormulaAndExamples);
 
   68            foreach ($this->categories as 
$c) {
 
   69                if ($category->id === 
$c->id) {
 
   70                    throw new Exception(
"The category '{$category->id}' is specified more than once");
 
   73            $this->categories[] = $category;
 
   75        if (empty($this->categories)) {
 
   76            throw new Exception(
"The language '{$info['id']}' does not have any plural category");
 
   79        usort($this->categories, 
function (
Category $category1, 
Category $category2) {
 
   84            throw new Exception(
"The language '{$info['id']}' does not have the '".
CldrData::OTHER_CATEGORY.
"' plural category");
 
  128        $alwaysTrueCategory = 
null;
 
  129        foreach ($this->categories as $category) {
 
  130            if ($category->formula === 
true) {
 
  131                if (!isset($category->examples)) {
 
  132                    throw new Exception(
"The category '{$category->id}' should always occur, but it does not have examples (so for CLDR it will never occur for integers!)");
 
  134                $alwaysTrueCategory = $category;
 
  138        if (isset($alwaysTrueCategory)) {
 
  139            foreach ($this->categories as $category) {
 
  140                if (($category !== $alwaysTrueCategory) && isset($category->examples)) {
 
  141                    throw new Exception(
"The category '{$category->id}' should never occur, but it has some examples (so for CLDR it will occur!)");
 
  145            $alwaysTrueCategory->formula = 
null;
 
  146            $this->categories = array($alwaysTrueCategory);
 
  157        foreach ($this->categories as $category) {
 
  158            if ($category->formula === 
false) {
 
  159                if (isset($category->examples)) {
 
  160                    throw new Exception(
"The category '{$category->id}' should never occur, but it has examples (so for CLDR it may occur!)");
 
  163                $filtered[] = $category;
 
  166        $this->categories = $filtered;
 
  176        $allCategoriesIds = array();
 
  177        $goodCategories = array();
 
  178        $badCategories = array();
 
  179        $badCategoriesIds = array();
 
  180        foreach ($this->categories as $category) {
 
  181            $allCategoriesIds[] = $category->id;
 
  182            if (isset($category->examples)) {
 
  183                $goodCategories[] = $category;
 
  185                $badCategories[] = $category;
 
  186                $badCategoriesIds[] = $category->id;
 
  189        if (empty($badCategories)) {
 
  192        $removeCategoriesWithoutExamples = 
false;
 
  193        switch (implode(
',', $badCategoriesIds).
'@'.implode(
',', $allCategoriesIds)) {
 
  196                    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))':
 
  212                        $removeCategoriesWithoutExamples = 
true;
 
  214                    case '(n == 1) ? 0 : ((n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14)) ? 1 : ((n != 1 && (n % 10 == 0 || n % 10 == 1) || n % 10 >= 5 && n % 10 <= 9 || n % 100 >= 12 && n % 100 <= 14) ? 2 : 3))':
 
  230                        $removeCategoriesWithoutExamples = 
true;
 
  234        if (!$removeCategoriesWithoutExamples) {
 
  235            throw new Exception(
"Unhandled case of plural categories without examples '".implode(
', ', $badCategoriesIds).
"' out of '".implode(
', ', $allCategoriesIds).
"'");
 
  239            $lastGood = $goodCategories[count($goodCategories) - 1];
 
  241            $lastGood->formula = 
null;
 
  243        $this->categories = $goodCategories;
 
  251        $numCategories = count($this->categories);
 
  252        switch ($numCategories) {
 
  259                $formula = strval($numCategories - 1);
 
  260                for (
$i = $numCategories - 2; 
$i >= 0; 
$i--) {
 
  262                    if (!preg_match(
'/^\([^()]+\)$/', 
$f)) {
 
  282        if (preg_match(
'/^n( % \d+)? == \d+(\.\.\d+|,\d+)*?$/', 
$formula)) {
 
  283            return str_replace(
' == ', 
' != ', 
$formula);
 
  285        if (preg_match(
'/^n( % \d+)? != \d+(\.\.\d+|,\d+)*?$/', 
$formula)) {
 
  286            return str_replace(
' != ', 
' == ', 
$formula);
 
  288        if (preg_match(
'/^\(?n == \d+ \|\| n == \d+\)?$/', 
$formula)) {
 
  289            return trim(str_replace(array(
' == ', 
' || '), array(
' != ', 
' && '), 
$formula), 
'()');
 
  292        if (preg_match(
'/^(n(?: % \d+)?) == (\d+) && (n(?: % \d+)?) != (\d+)$/', 
$formula, 
$m)) {
 
  293            return "{$m[1]} != {$m[2]} || {$m[3]} == {$m[4]}";
 
  296            case '(n == 1 || n == 2 || n == 3) || n % 10 != 4 && n % 10 != 6 && n % 10 != 9':
 
  297                return 'n != 1 && n != 2 && n != 3 && (n % 10 == 4 || n % 10 == 6 || n % 10 == 9)';
 
  298            case '(n == 0 || n == 1) || n >= 11 && n <= 99':
 
  299                return 'n >= 2 && (n < 11 || n > 99)';
 
  301        throw new Exception(
"Unable to reverse the formula '$formula'");
 
  311            'n != 0 && n != 1'              =>  
'n > 1' ,
 
  312            '(n == 0 || n == 1) && n != 0'  =>  
'n == 1',
 
  324        if (is_string($value) && ($value !== 
'')) {
 
  326            $transliterated = strtr($value, array(
 
  327                'À' => 
'A', 
'Á' => 
'A', 
'Â' => 
'A', 
'Ã' => 
'A', 
'Ä' => 
'A',
 
  328                'È' => 
'E', 
'É' => 
'E', 
'Ê' => 
'E', 
'Ë' => 
'E',
 
  329                'Ì' => 
'I', 
'Í' => 
'I', 
'Î' => 
'I', 
'Ï' => 
'I',
 
  331                'Ò' => 
'O', 
'Ó' => 
'O', 
'Ô' => 
'O', 
'Õ' => 
'O', 
'Ö' => 
'O',
 
  332                'Ù' => 
'U', 
'Ú' => 
'U', 
'Û' => 
'U', 
'Ü' => 
'U',
 
  333                'Ÿ' => 
'Y', 
'Ý' => 
'Y',
 
  334                'à' => 
'a', 
'á' => 
'a', 
'â' => 
'a', 
'ã' => 
'a', 
'ä' => 
'a',
 
  335                'è' => 
'e', 
'é' => 
'e', 
'ê' => 
'e', 
'ë' => 
'e',
 
  336                'ì' => 
'i', 
'í' => 
'i', 
'î' => 
'i', 
'ï' => 
'i',
 
  337                'ñ' => 
'n', 
'ò' => 
'o', 
'ó' => 
'o', 
'ô' => 
'o', 
'õ' => 
'o', 
'ö' => 
'o',
 
  338                'ù' => 
'u', 
'ú' => 
'u', 
'û' => 
'u', 
'ü' => 
'u',
 
  339                'ý' => 
'y', 
'ÿ' => 
'y',
 
  341            $transliterated = @iconv(
'UTF-8', 
'US-ASCII//IGNORE//TRANSLIT', $transliterated);
 
  342            if (($transliterated === 
false) || ($transliterated === 
'')) {
 
  343                throw new Exception(
"Unable to transliterate '$value'");
 
  345            $value = $transliterated;
 
  354        $clone = clone $this;
 
  357        $clone->categories = array();
 
  358        foreach ($this->categories as $category) {
 
  359            $categoryClone = clone $category;
 
  361            $clone->categories[] = $categoryClone;
 
An exception for terminatinating execution or to throw for unit testing.
A helper class that handles a single category rules (eg 'zero', 'one', ...) and its formula and examp...
static getLanguageNames()
Returns a dictionary containing the language names.
static getLanguageInfo($id)
Retrieve the name of a language, as well as if a language code is deprecated in favor of another lang...
Main class to convert the plural rules of a language from CLDR to gettext.
__construct($info)
Initialize the instance and parse the language code.
checkAllCategoriesWithExamples()
Let's look for categories that don't have examples.
static getAll()
Return a list of all languages available.
static reverseFormula($formula)
Reverse a formula.
static reduceFormula($formula)
Reduce some excessively complex formulas.
getUSAsciiClone()
Returns a clone of this instance with all the strings to US-ASCII.
buildFormula()
Build the formula starting from the currently defined categories.
static getById($id)
Return a Language instance given the language id.
checkAlwaysTrueCategories()
Let's look for categories that will always occur.
static asciifier(&$value)
Take one variable and, if it's a string, we transliterate it to US-ASCII.
checkAlwaysFalseCategories()
Let's look for categories that will never occur.