Seditio Source
Root |
./othercms/dotclear-2.22/inc/libs/clearbricks/common/lib.l10n.php
<?php
/**
 * @class l10n
 * @brief Localization tools
 *
 * Localization utilities
 *
 * @package Clearbricks
 * @subpackage Common
 *
 * @copyright Olivier Meunier & Association Dotclear
 * @copyright GPL-2.0-only
 */

/* @cond ONCE */
if (!function_exists('__')) {
   
/** @endcond */

    /**
     * Translated string
     *
     * @see l10n::trans()
     *
     * @param      string   $singular Singular form of the string
     * @param      string   $plural Plural form of the string (optionnal)
     * @param      integer  $count Context number for plural form (optionnal)
     * @return     string   translated string
     */
   
function __(string $singular, ?string $plural = null, ?int $count = null): string
   
{
        return
l10n::trans($singular, $plural, $count);
    }

   
/* @cond ONCE */
}
/** @endcond */
class l10n
{
   
/// @name Languages properties
    //@{
   
protected static $languages_definitions      = [];
    protected static
$languages_name             = null;
    protected static
$languages_textdirection    = null;
    protected static
$languages_pluralsnumber    = null;
    protected static
$languages_pluralexpression = null;
   
//@}

    /// @name Current language properties
    //@{
   
protected static $language_code             = null;
    protected static
$language_name             = null;
    protected static
$language_textdirection    = null;
    protected static
$language_pluralsnumber    = null;
    protected static
$language_pluralexpression = null;
    protected static
$language_pluralfunction   = null;
   
//@}

    /** @deprecated */
   
public static $text_direction;

   
/** @deprecated */
   
protected static $langs = [];

   
/**
     * L10N initialization
     *
     * Create global arrays for L10N stuff. Should be called before any work
     * with other methods. For plural-forms, __l10n values can now be array.
     *
     * @param string $code Language code to work with
     */
   
public static function init($code = 'en')
    {
       
$GLOBALS['__l10n'] = $GLOBALS['__l10n_files'] = [];

       
self::lang($code);
    }

   
/**
     * Set a language to work on or return current working language code
     *
     * This set up language properties to manage plurals form.
     * Change of language code not reset global array of L10N stuff.
     *
     * @param string $code Language code
     * @return string Current language code
     */
   
public static function lang(?string $code = null): string
   
{
        if (
$code !== null && self::$language_code != $code && self::isCode($code)) {
           
self::$language_code             = $code;
           
self::$language_name             = self::getLanguageName($code);
           
self::$language_textdirection    = self::getLanguageTextDirection($code);
           
self::$language_pluralsnumber    = self::getLanguagePluralsNumber($code);
           
self::$language_pluralexpression = self::getLanguagePluralExpression($code);

           
self::$language_pluralfunction = self::createPluralFunction(
               
self::$language_pluralsnumber,
               
self::$language_pluralexpression
           
);

           
// Backwards compatibility
           
self::$text_direction = self::$language_textdirection;
        }

        return
self::$language_code;
    }

   
/**
     * Translate a string
     *
     * Returns a translated string of $singular
     * or $plural according to a number if it is set.
     * If translation is not found, returns the string.
     *
     * @param string $singular Singular form of the string
     * @param string $plural Plural form of the string (optionnal)
     * @param integer $count Context number for plural form (optionnal)
     * @return string Translated string
     */
   
public static function trans(string $singular, ?string $plural = null, ?int $count = null): string
   
{
       
// If no string to translate, return no string
       
if ($singular == '') {
            return
'';

       
// If no l10n translation loaded or exists
       
} elseif ((!array_key_exists('__l10n', $GLOBALS) || empty($GLOBALS['__l10n'])
            || !
array_key_exists($singular, $GLOBALS['__l10n'])) && is_null($count)) {
            return
$singular;

       
// If no $plural form or if current language has no plural form return $singular translation
       
} elseif ($plural === null || $count === null || self::$language_pluralsnumber == 1) {
           
$t = !empty($GLOBALS['__l10n'][$singular]) ? $GLOBALS['__l10n'][$singular] : $singular;

            return
is_array($t) ? $t[0] : $t;

           
// Else return translation according to $count
       
}
       
$i = self::index($count);

       
// If it is a plural and translation exists in "singular" form
       
if ($i > 0 && !empty($GLOBALS['__l10n'][$plural])) {
           
$t = $GLOBALS['__l10n'][$plural];

            return
is_array($t) ? $t[0] : $t;

       
// If it is plural and index exists in plurals translations
       
} elseif (!empty($GLOBALS['__l10n'][$singular])
                &&
is_array($GLOBALS['__l10n'][$singular])
                &&
array_key_exists($i, $GLOBALS['__l10n'][$singular])
                && !empty(
$GLOBALS['__l10n'][$singular][$i])) {
            return
$GLOBALS['__l10n'][$singular][$i];

           
// Else return input string according to "en" plural form
       
}

        return
$i > 0 ? $plural : $singular;
    }

   
/**
     * Retrieve plural index from input number
     *
     * @param integer $count Number to take account
     * @return integer Index of plural form
     */
   
public static function index(int $count): int
   
{
        return
call_user_func(self::$language_pluralfunction, $count);
    }

   
/**
     * Add a file
     *
     * Adds a l10n file in translation strings. $file should be given without
     * extension. This method will look for $file.lang.php and $file.po (in this
     * order) and retrieve the first one found.
     * We not care about language (and plurals forms) of the file.
     *
     * @param string    $file        Filename (without extension)
     * @return boolean True on success
     */
   
public static function set(string $file): bool
   
{
       
$lang_file = $file . '.lang';
       
$po_file   = $file . '.po';
       
$php_file  = $file . '.lang.php';

        if (
file_exists($php_file)) {
            require
$php_file;
        } elseif ((
$tmp = self::getPoFile($po_file)) !== false) {
           
$GLOBALS['__l10n_files'][] = $po_file;
           
$GLOBALS['__l10n']         = $tmp + $GLOBALS['__l10n']; // "+" erase numeric keys unlike array_merge
       
} elseif (($tmp = self::getLangFile($lang_file)) !== false) {
           
$GLOBALS['__l10n_files'][] = $lang_file;
           
$GLOBALS['__l10n']         = $tmp + $GLOBALS['__l10n']; // "+" erase numeric keys unlike array_merge
       
} else {
            return
false;
        }

        return
true;
    }

   
/**
     * L10N file
     *
     * Returns a file path for a file, a directory and a language.
     * If $dir/$lang/$file is not found, it will check if $dir/en/$file
     * exists and returns the result. Returns false if no file were found.
     *
     * @param string    $dir        Directory
     * @param string    $file    File
     * @param string    $lang    Language
     * @return string|false        File path or false
     */
   
public static function getFilePath(string $dir, string $file, string $lang)
    {
       
$f = $dir . '/' . $lang . '/' . $file;
        if (!
file_exists($f)) {
           
$f = $dir . '/en/' . $file;
        }

        return
file_exists($f) ? $f : false;
    }

   
/** @deprecated */
   
public static function getLangFile(string $file)
    {
        if (!
file_exists($file)) {
            return
false;
        }

       
$fp = @fopen($file, 'r');
        if (
$fp === false) {
            return
false;
        }

       
$res = [];
        while (
$l = fgets($fp)) {
           
$l = trim($l);
           
# Comment
           
if (substr($l, 0, 1) == '#') {
                continue;
            }

           
# Original text
           
if (substr($l, 0, 1) == ';' && ($t = fgets($fp)) !== false && trim($t) != '') {
               
$res[substr($l, 1)] = trim($t);
            }
        }
       
fclose($fp);

        return
$res;
    }

   
/// @name Gettext PO methods
    //@{
    /**
     * Load gettext file
     *
     * Returns an array of strings found in a given gettext (.po) file
     *
     * @param string    $file        Filename
     * @return array|false
     */
   
public static function getPoFile(string $file)
    {
        if ((
$m = self::parsePoFile($file)) === false) {
            return
false;
        }

        if (empty(
$m[1])) {
            return [];
        }

       
// Keep singular id and translations, remove headers and comments
       
$r = [];
        foreach (
$m[1] as $v) {
            if (isset(
$v['msgid']) && isset($v['msgstr'])) {
               
$r[$v['msgid']] = $v['msgstr'];
            }
        }

        return
$r;
    }

   
/**
     * Generates a PHP file from a po file
     *
     * Return a boolean depending on success or failure
     *
     * @param      string $file File
     * @param      string $license_block optional license block to add at the beginning
     * @return     boolean true on success
     */
   
public static function generatePhpFileFromPo(string $file, string $license_block = ''): bool
   
{
       
$po_file  = $file . '.po';
       
$php_file = $file . '.lang.php';

       
$strings  = self::getPoFile($po_file);
       
$fcontent = "<?php\n" .
           
$license_block .
           
"#\n#\n#\n" .
           
"#        DOT NOT MODIFY THIS FILE !\n\n\n\n\n";

        foreach (
$strings as $vo => $tr) {
           
$vo = str_replace("'", "\\'", $vo);
            if (
is_array($tr)) {
                foreach (
$tr as $i => $t) {
                   
$t = str_replace("'", "\\'", $t);
                   
$fcontent .= '$GLOBALS[\'__l10n\'][\'' . $vo . '\'][' . $i . '] = \'' . $t . '\';' . "\n";
                }
            } else {
               
$tr = str_replace("'", "\\'", $tr);
               
$fcontent .= '$GLOBALS[\'__l10n\'][\'' . $vo . '\'] = \'' . $tr . '\';' . "\n";
            }
        }

        if ((
$fp = fopen($php_file, 'w')) !== false) {
           
fwrite($fp, $fcontent, strlen($fcontent));
           
fclose($fp);

            return
true;
        }

        return
false;
    }

   
/**
     * Parse Po File
     *
     * Return an array of po headers and translations from a po file
     *
     * @param string $file File path
     * @return array|false Parsed file
     */
   
public static function parsePoFile(string $file)
    {
       
// stop if file not exists
       
if (!file_exists($file)) {
            return
false;
        }

       
// read file per line in array (without ending new line)
       
if (false === ($lines = file($file, FILE_IGNORE_NEW_LINES))) {
            return
false;
        }

       
// prepare variables
       
$headers = [
           
'Project-Id-Version'        => '',
           
'Report-Msgid-Bugs-To'      => '',
           
'POT-Creation-Date'         => '',
           
'PO-Revision-Date'          => '',
           
'Last-Translator'           => '',
           
'Language-Team'             => '',
           
'Content-Type'              => '',
           
'Content-Transfer-Encoding' => '',
           
'Plural-Forms'              => '',
           
// there are more headers but these ones are default
       
];
       
$headers_searched = $headers_found = false;
       
$h_line           = $h_val           = $h_key           = '';
       
$entries          = $entry          = $desc          = [];
       
$i                = 0;

       
// read through lines
       
for ($i = 0; $i < count($lines); $i++) {

           
// some people like mirovinben add white space at the end of line
           
$line = trim((string) $lines[$i]);

           
// jump to next line on blank one or empty comment (#)
           
if (strlen($line) < 2) {
                continue;
            }

           
// headers
           
if (!$headers_searched && preg_match('/^msgid\s+""$/', trim((string) $line))) {

               
// headers start wih empty msgid and msgstr follow be multine
               
if (!preg_match('/^msgstr\s+""$/', trim((string) $lines[$i + 1]))
                    || !
preg_match('/^"(.*)"$/', trim((string) $lines[$i + 2]))) {
                   
$headers_searched = true;
                } else {
                   
$l = $i + 2;
                    while (
false !== ($def = self::cleanPoLine('multi', $lines[$l++]))) {
                       
$h_line = self::cleanPoString($def[1]);

                       
// an header has key:val
                       
if (false === ($h_index = strpos($h_line, ':'))) {

                           
// multiline value
                           
if (!empty($h_key) && !empty($headers[$h_key])) {
                               
$headers[$h_key] = trim((string) $headers[$h_key] . $h_line);

                                continue;

                               
// your .po file is so bad
                           
}
                           
$headers_searched = true;

                            break;
                        }

                       
// extract key and value
                       
$h_key = substr($h_line, 0, $h_index);
                       
$h_val = substr($h_line, $h_index + 1);

                       
// unknow header
                       
if (!isset($headers[$h_key])) {
                           
//continue;
                       
}

                       
// ok it's an header, add it
                       
$headers[$h_key] = trim($h_val);
                       
$headers_found   = true;
                    }

                   
// headers found so stop search and clean previous comments
                   
if ($headers_found) {
                       
$headers_searched = true;
                       
$entry            = $desc            = [];
                       
$i                = $l - 1;

                        continue;
                    }
                }
            }

           
// comments
           
if (false !== ($def = self::cleanPoLine('comment', $line))) {
               
$str = self::cleanPoString($def[2]);

                switch (
$def[1]) {

                   
// translator comments
                   
case ' ':
                        if (!isset(
$desc['translator-comments'])) {
                           
$desc['translator-comments'] = $str;
                        } else {
                           
$desc['translator-comments'] .= "\n" . $str;
                        }

                        break;

                   
// extracted comments
                   
case '.':
                        if (!isset(
$desc['extracted-comments'])) {
                           
$desc['extracted-comments'] = $str;
                        } else {
                           
$desc['extracted-comments'] .= "\n" . $str;
                        }

                        break;

                   
// reference
                   
case ':':
                        if (!isset(
$desc['references'])) {
                           
$desc['references'] = [];
                        }
                       
$desc['references'][] = $str;

                        break;

                   
// flag
                   
case ',':
                        if (!isset(
$desc['flags'])) {
                           
$desc['flags'] = [];
                        }
                       
$desc['flags'][] = $str;

                        break;

                   
// previous msgid, msgctxt
                   
case '|':
                       
// msgid
                       
if (strpos($def[2], 'msgid') === 0) {
                           
$desc['previous-msgid'] = $str;
                       
// msgcxt
                       
} else {
                           
$desc['previous-msgctxt'] = $str;
                        }

                        break;
                }
            }

           
// msgid
           
elseif (false !== ($def = self::cleanPoLine('msgid', $line))) {

               
// add last translation and start new one
               
if ((isset($entry['msgid']) || isset($entry['msgid_plural'])) && isset($entry['msgstr'])) {

                   
// save last translation and start new one
                   
$entries[] = $entry;
                   
$entry     = [];

                   
// add comments to new translation
                   
if (!empty($desc)) {
                       
$entry = array_merge($entry, $desc);
                       
$desc  = [];
                    }

                   
// stop searching headers
                   
$headers_searched = true;
                }

               
$str = self::cleanPoString($def[2]);

               
// msgid_plural
               
if (!empty($def[1])) {
                   
$entry['msgid_plural'] = $str;
                } else {
                   
$entry['msgid'] = $str;
                }
            }

           
// msgstr
           
elseif (false !== ($def = self::cleanPoLine('msgstr', $line))) {
               
$str = self::cleanPoString($def[2]);

               
// plural forms
               
if (!empty($def[1])) {
                    if (!isset(
$entry['msgstr'])) {
                       
$entry['msgstr'] = [];
                    }
                   
$entry['msgstr'][] = $str;
                } else {
                   
$entry['msgstr'] = $str;
                }
            }

           
// multiline
           
elseif (false !== ($def = self::cleanPoLine('multi', $line))) {
               
$str = self::cleanPoString($def[1]);

               
// msgid
               
if (!isset($entry['msgstr'])) {
                   
//msgid plural
                   
if (isset($entry['msgid_plural'])) {
                        if (!
is_array($entry['msgid_plural'])) {
                           
$entry['msgid_plural'] .= $str;
                        } else {
                           
$entry['msgid_plural'][count($entry['msgid_plural']) - 1] .= $str;
                        }
                    } else {
                        if (!
is_array($entry['msgid'])) {
                           
$entry['msgid'] .= $str;
                        } else {
                           
$entry['msgid'][count($entry['msgid']) - 1] .= $str;
                        }
                    }

                   
// msgstr
               
} else {
                    if (!
is_array($entry['msgstr'])) {
                       
$entry['msgstr'] .= $str;
                    } else {
                       
$entry['msgstr'][count($entry['msgstr']) - 1] .= $str;
                    }
                }
            }
        }

       
// Add last translation
       
if (!empty($entry)) {
            if (!empty(
$desc)) {
               
$entry = array_merge($entry, $desc);
            }
           
$entries[] = $entry;
        }

        return [
$headers, $entries];
    }

   
/* @ignore */
   
protected static function cleanPoLine($type, $_)
    {
       
$patterns = [
           
'msgid'   => 'msgid(_plural|)\s+"(.*)"',
           
'msgstr'  => 'msgstr(\[.*?\]|)\s+"(.*)"',
           
'multi'   => '"(.*)"',
           
'comment' => '#\s*(\s|\.|:|\,|\|)\s*(.*)',
        ];

        if (
array_key_exists($type, $patterns)
            &&
preg_match('/^' . $patterns[$type] . '$/i', trim((string) $_), $m)) {
            return
$m;
        }

        return
false;
    }

   
/* @ignore */
   
protected static function cleanPoString($_): string
   
{
        return
stripslashes(str_replace(['\n', '\r\n'], "\n", $_));
    }

   
/**
     * Extract nplurals and plural from po expression
     *
     * @param string $expression Plural form as of gettext Plural-form param
     * @return array Number of plurals and cleaned plural expression
     */
   
public static function parsePluralExpression(string $expression): array
    {
        return
preg_match('/^\s*nplurals\s*=\s*(\d+)\s*;\s+plural\s*=\s*(.+)$/', $expression, $m) ?
        [(int)
$m[1], trim(self::cleanPluralExpression($m[2]))] :
        [
self::$language_pluralsnumber, self::$language_pluralexpression];
    }

   
/**
     * Create function to find plural msgstr index from gettext expression
     *
     * @param integer $nplurals Plurals number
     * @param string $expression Plural expression
     * @return callable Function to extract right plural index
     */
   
public static function createPluralFunction(int $nplurals, string $expression)
    {
        return function (
$n) use ($nplurals, $expression) {
           
$i = eval('return (integer) (' . str_replace('n', (string) $n, $expression) . ');');

            return (
$i < $nplurals) ? $i : $nplurals - 1;
        };
    }

   
/* @ignore */
   
protected static function cleanPluralExpression(string $_): string
   
{
       
$_ .= ';';
       
$r = '';
       
$l = 0;

        for (
$i = 0; $i < strlen($_); ++$i) {
            switch (
$_[$i]) {
                case
'?':
                   
$r .= ' ? (';
                   
$l++;

                    break;

                case
':':
                   
$r .= ') : (';

                    break;

                case
';':
                   
$r .= str_repeat(')', $l) . ';';
                   
$l = 0;

                    break;

                default:
                   
$r .= $_[$i];
            }
        }

        return
rtrim($r, ';');
    }
   
//@}

    /// @name Languages definitions methods
    //@{
    /**
     * Check if a language code exists
     *
     * @param string $code Language code
     * @return boolean True if code exists
     */
   
public static function isCode(string $code): bool
   
{
        return
array_key_exists($code, self::getLanguagesName());
    }

   
/**
     * Get a language code according to a language name
     *
     * @param string $code Language name
     * @return string Language code
     */
   
public static function getCode(string $code): string
   
{
       
$_ = self::getLanguagesName();

        return ((
$index = array_search($code, $_)) !== false) ? $index : self::$language_code;
    }

   
/**
     * ISO Codes
     *
     * @param boolean    $flip            Flip resulting array
     * @param boolean    $name_with_code    Prefix (code) to names
     * @return array
     */
   
public static function getISOcodes(bool $flip = false, bool $name_with_code = false): array
    {
       
$langs = self::getLanguagesName();
        if (
$name_with_code) {
            foreach (
$langs as $k => &$v) {
               
$v = $k . ' - ' . $v;
            }
        }

        if (
$flip) {
            return
array_flip($langs);
        }

        return
$langs;
    }

   
/**
     * Get a language name according to a lang code
     *
     * @param string $code Language code
     * @return string Language name
     */
   
public static function getLanguageName(string $code): string
   
{
       
$_ = self::getLanguagesName();

        return
array_key_exists($code, $_) ? $_[$code] : self::$language_name;
    }

   
/**
     * Get languages names
     *
     * @return array List of languages names by languages codes
     */
   
public static function getLanguagesName(): array
    {
        if (empty(
self::$languages_name)) {
           
self::$languages_name = self::getLanguagesDefinitions(3);

           
// Backwards compatibility
           
self::$langs = self::$languages_name;
        }

        return
self::$languages_name;
    }

   
/**
     * Get a text direction according to a language code
     *
     * @param string $code Language code
     * @return string Text direction (rtl or ltr)
     */
   
public static function getLanguageTextDirection(string $code): string
   
{
       
$_ = self::getLanguagesTextDirection();

        return
array_key_exists($code, $_) ? $_[$code] : self::$language_textdirection;
    }

   
/**
     * Get languages text directions
     *
     * @return array List of text directions by languages codes
     */
   
public static function getLanguagesTextDirection(): array
    {
        if (empty(
self::$languages_textdirection)) {
           
self::$languages_textdirection = self::getLanguagesDefinitions(4);
        }

        return
self::$languages_textdirection;
    }

   
/**
     * Text direction
     *
     * @deprecated
     * @see l10n::getLanguageTextDirection()
     *
     * @param string    $lang    Language code
     * @return string ltr or rtl
     */
   
public static function getTextDirection(string $lang): string
   
{
        return
self::getLanguageTextDirection($lang);
    }

   
/**
     * Get a number of plurals according to a language code
     *
     * @param string $code Language code
     * @return integer Number of plurals
     */
   
public static function getLanguagePluralsNumber(string $code): int
   
{
       
$_ = self::getLanguagesPluralsNumber();

        return !empty(
$_[$code]) ? $_[$code] : self::$language_pluralsnumber;
    }

   
/**
     * Get languages numbers of plurals
     *
     * @return array List of numbers of plurals by languages codes
     */
   
public static function getLanguagesPluralsNumber(): array
    {
        if (empty(
self::$languages_pluralsnumber)) {
           
self::$languages_pluralsnumber = self::getLanguagesDefinitions(5);
        }

        return
self::$languages_pluralsnumber;
    }

   
/**
     * Get a plural expression according to a language code
     *
     * @param string $code Language code
     * @return string Plural expression
     */
   
public static function getLanguagePluralExpression(string $code): string
   
{
       
$_ = self::getLanguagesPluralExpression();

        return !empty(
$_[$code]) ? $_[$code] : self::$language_pluralexpression;
    }

   
/**
     * Get languages plural expressions
     *
     * @return array List of plural expressions by languages codes
     */
   
public static function getLanguagesPluralExpression(): array
    {
        if (empty(
self::$languages_pluralexpression)) {
           
self::$languages_pluralexpression = self::getLanguagesDefinitions(6);
        }

        return
self::$languages_pluralexpression;
    }

   
/**
     * Get languages definitions of a given type
     *
     * The list follows ISO 639.1 norm with additionnal IETF codes as pt-br
     *
     * Countries codes and names from:
     * - http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
     * - http://www.gnu.org/software/gettext/manual/gettext.html#Language-Codes
     * - http://www.loc.gov/standards/iso639-2/php/English_list.php
     *
     * Text direction from:
     * - http://translate.sourceforge.net/wiki/l10n/displaysettings
     * - http://meta.wikimedia.org/wiki/Template:List_of_language_names_ordered_by_code
     *
     * Plural-forms taken from:
     * - http://translate.sourceforge.net/wiki/l10n/pluralforms
     *
     * $languages_definitions types look like this:
     * 0 = code ISO 639.1 (2 digit) + IETF add
     * 1 = code ISO 639.2 (english 3 digit)
     * 2 = English name
     * 3 = natal name
     * 4 = text direction (ltr or rtl)
     * 5 = number of plurals (1 means no plural form)
     * 6 = plural expression (as of gettext .po plural form)
     *
     * null values represent missing values
     *
     * @param integer $type Type of definition
     * @param string $default Default value if definition is empty
     * @return array List of requested definition by languages codes
     */
   
protected static function getLanguagesDefinitions(int $type, string $default = ''): array
    {
        if (
$type < 0 || $type > 6) {
            return [];
        }

        if (empty(
self::$languages_definitions)) {
           
self::$languages_definitions = [
                [
'aa', 'aar', 'Afar', 'Afaraf', 'ltr', null, null],
                [
'ab', 'abk', 'Abkhazian', 'Аҧсуа', 'ltr', null, null],
                [
'ae', 'ave', 'Avestan', 'Avesta', 'ltr', null, null],
                [
'af', 'afr', 'Afrikaans', 'Afrikaans', 'ltr', 2, 'n != 1'],
                [
'ak', 'aka', 'Akan', 'Akan', 'ltr', 2, 'n > 1)'],
                [
'am', 'amh', 'Amharic', 'አማርኛ', 'ltr', 2, 'n > 1'],
                [
'an', 'arg', 'Aragonese', 'Aragonés', 'ltr', 2, 'n != 1'],
                [
'ar', 'ara', 'Arabic', '‫العربية', 'rtl', 6, 'n==0 ? 0 : (n==1 ? 1 : (n==2 ? 2 : (n%100>=3 && n%100<=10 ? 3 : (n%100>=11 ? 4 : 5))))'],
                [
'as', 'asm', 'Assamese', 'অসমীয়া', 'ltr', null, null],
                [
'av', 'ava', 'Avaric', 'авар мацӀ', 'ltr', null, null],
                [
'ay', 'aym', 'Aymara', 'Aymar aru', 'ltr', 1, '0'],
                [
'az', 'aze', 'Azerbaijani', 'Azərbaycan dili', 'ltr', 2, 'n != 1'],

                [
'ba', 'bak', 'Bashkir', 'башҡорт теле', 'ltr', null, null],
                [
'be', 'bel', 'Belarusian', 'Беларуская', 'ltr', 3, 'n%10==1 && n%100!=11 ? 0 : (n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)'],
                [
'bg', 'bul', 'Bulgarian', 'български език', 'ltr', 2, 'n != 1'],
                [
'bh', 'bih', 'Bihari languages', 'भोजपुरी', 'ltr', null, null],
                [
'bi', 'bis', 'Bislama', 'Bislama', 'ltr', null, null],
                [
'bm', 'bam', 'Bambara', 'Bamanankan', 'ltr', null, null],
                [
'bn', 'ben', 'Bengali', 'বাংলা', 'ltr', 2, 'n != 1'],
                [
'bo', 'tib', 'Tibetan', 'བོད་ཡིག', 'ltr', 1, '0'],
                [
'br', 'bre', 'Breton', 'Brezhoneg', 'ltr', 2, 'n > 1'],
                [
'bs', 'bos', 'Bosnian', 'Bosanski jezik', 'ltr', 3, 'n%10==1 && n%100!=11 ? 0 : (n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)'],

                [
'ca', 'cat', 'Catalan', 'Català', 'ltr', 2, 'n != 1'],
                [
'ce', 'che', 'Chechen', 'нохчийн мотт', 'ltr', null, null],
                [
'ch', 'cha', 'Chamorro', 'Chamoru', 'ltr', 3, 'n==1 ? 0 : ((n>=2 && n<=4) ? 1 : 2)'],
                [
'co', 'cos', 'Corsican', 'Corsu', 'ltr', null, null],
                [
'cr', 'cre', 'Cree', 'ᓀᐦᐃᔭᐍᐏᐣ', 'ltr', null, null],
                [
'cs', 'cze', 'Czech', 'Česky', 'ltr', null, null],
                [
'cu', 'chu', 'Church Slavonic', 'ѩзыкъ Словѣньскъ', 'ltr', null, null],
                [
'cv', 'chv', 'Chuvash', 'чӑваш чӗлхи', 'ltr', null, null],
                [
'cy', 'wel', 'Welsh', 'Cymraeg', 'ltr', 4, 'n==1 ? 0 : ((n==2) ? 1 : ((n != 8 && n != 11) ? 2 : 3))'],

                [
'da', 'dan', 'Danish', 'Dansk', 'ltr', 2, 'n != 1'],
                [
'de', 'ger', 'German', 'Deutsch', 'ltr', 2, 'n != 1'],
                [
'dv', 'div', 'Maldivian', 'ދިވެހި', 'rtl', null, null],
                [
'dz', 'dzo', 'Dzongkha', 'རྫོང་ཁ', 'ltr', 1, '0'],

                [
'ee', 'ewe', 'Ewe', 'Ɛʋɛgbɛ', 'ltr', null, null],
                [
'el', 'gre', 'Greek', 'Ελληνικά', 'ltr', 2, 'n != 1'],
                [
'en', 'eng', 'English', 'English', 'ltr', 2, 'n != 1'],
                [
'eo', 'epo', 'Esperanto', 'Esperanto', 'ltr', 2, 'n != 1'],
                [
'es', 'spa', 'Spanish', 'Español', 'ltr', 2, 'n != 1'],
                [
'es-ar', null, 'Argentinean Spanish', 'Argentinean Spanish', 'ltr', 2, 'n != 1'],
                [
'et', 'est', 'Estonian', 'Eesti keel', 'ltr', 2, 'n != 1'],
                [
'eu', 'baq', 'Basque', 'Euskara', 'ltr', 2, 'n != 1'],

                [
'fa', 'per', 'Persian', '‫فارسی', 'rtl', 1, '0'],
                [
'ff', 'ful', 'Fulah', 'Fulfulde', 'ltr', 2, 'n != 1'],
                [
'fi', 'fin', 'Finnish', 'Suomen kieli', 'ltr', 2, 'n != 1'],
                [
'fj', 'fij', 'Fijian', 'Vosa Vakaviti', 'ltr', null, null],
                [
'fo', 'fao', 'Faroese', 'Føroyskt', 'ltr', 2, 'n != 1'],
                [
'fr', 'fre', 'French', 'Français', 'ltr', 2, 'n > 1'],
                [
'fy', 'fry', 'Western Frisian', 'Frysk', 'ltr', 2, 'n != 1'],

                [
'ga', 'gle', 'Irish', 'Gaeilge', 'ltr', 5, 'n==1 ? 0 : (n==2 ? 1 : (n<7 ? 2 : (n<11 ? 3 : 4)))'],
                [
'gd', 'gla', 'Gaelic', 'Gàidhlig', 'ltr', 4, '(n==1 || n==11) ? 0 : ((n==2 || n==12) ? 1 : ((n > 2 && n < 20) ? 2 : 3))'],
                [
'gl', 'glg', 'Galician', 'Galego', 'ltr', 2, 'n != 1'],
                [
'gn', 'grn', 'Guarani', "Avañe'ẽ", 'ltr', null, null],
                [
'gu', 'guj', 'Gujarati', 'ગુજરાતી', 'ltr', 2, 'n != 1'],
                [
'gv', 'glv', 'Manx', 'Ghaelg', 'ltr', null, null],

                [
'ha', 'hau', 'Hausa', '‫هَوُسَ', 'rtl', 2, 'n != 1'],
                [
'he', 'heb', 'Hebrew', '‫עברית', 'rtl', 2, 'n != 1'],
                [
'hi', 'hin', 'Hindi', 'हिन्दी', 'ltr', 2, 'n != 1'],
                [
'ho', 'hmo', 'Hiri Motu', 'Hiri Motu', 'ltr', null, null],
                [
'hr', 'hrv', 'Croatian', 'Hrvatski', 'ltr', 3, 'n%10==1 && n%100!=11 ? 0 : (n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)'],
                [
'ht', 'hat', 'Haitian', 'Kreyòl ayisyen', 'ltr', null, null],
                [
'hu', 'hun', 'Hungarian', 'Magyar', 'ltr', 2, 'n != 1'],
                [
'hy', 'arm', 'Armenian', 'Հայերեն', 'ltr', 2, 'n != 1'],
                [
'hz', 'her', 'Herero', 'Otjiherero', 'ltr', null, null],

                [
'ia', 'ina', 'Interlingua', 'Interlingua', 'ltr', 2, 'n != 1'],
                [
'id', 'ind', 'Indonesian', 'Bahasa Indonesia', 'ltr', 1, '0'],
                [
'ie', 'ile', 'Interlingue', 'Interlingue', 'ltr', null, null],
                [
'ig', 'ibo', 'Igbo', 'Igbo', 'ltr', null, null],
                [
'ii', 'iii', 'Sichuan Yi', 'ꆇꉙ', 'ltr', null, null],
                [
'ik', 'ipk', 'Inupiaq', 'Iñupiaq', 'ltr', null, null],
                [
'io', 'ido', 'Ido', 'Ido', 'ltr', null, null],
                [
'is', 'ice', 'Icelandic', 'Íslenska', 'ltr', 2, '(n%10!=1 || n%100==11) ? 1 : 0'],
                [
'it', 'ita', 'Italian', 'Italiano', 'ltr', 2, 'n != 1'],
                [
'iu', 'iku', 'Inuktitut', 'ᐃᓄᒃᑎᑐᑦ', 'ltr', null, null],

                [
'ja', 'jpn', 'Japanese', '日本語', 'ltr', 1, '0'],
                [
'jv', 'jav', 'Javanese', 'Basa Jawa', 'ltr', 2, 'n != 0'],

                [
'ka', 'geo', 'Georgian', 'ქართული', 'ltr', 1, '0'],
                [
'kg', 'kon', 'Kongo', 'KiKongo', 'ltr', null, null],
                [
'ki', 'kik', 'Kikuyu', 'Gĩkũyũ', 'ltr', null, null],
                [
'kj', 'kua', 'Kuanyama', 'Kuanyama', 'ltr', null, null],
                [
'kk', 'kaz', 'Kazakh', 'Қазақ тілі', 'ltr', 1, '0'],
                [
'kl', 'kal', 'Greenlandic', 'Kalaallisut', 'ltr', null, null],
                [
'km', 'khm', 'Central Khmer', 'ភាសាខ្មែរ', 'ltr', 1, '0'],
                [
'kn', 'kan', 'Kannada', 'ಕನ್ನಡ', 'ltr', 2, 'n != 1'],
                [
'ko', 'kor', 'Korean', '한국어', 'ltr', 1, '0'],
                [
'kr', 'kau', 'Kanuri', 'Kanuri', 'ltr', null, null],
                [
'ks', 'kas', 'Kashmiri', 'कश्मीरी', 'rtl', null, null],
                [
'ku', 'kur', 'Kurdish', 'Kurdî', 'ltr', 2, 'n!= 1'],
                [
'kv', 'kom', 'Komi', 'коми кыв', 'ltr', null, null],
                [
'kw', 'cor', 'Cornish', 'Kernewek', 'ltr', 4, 'n==1 ? 0 : ((n==2) ? 1 : ((n == 3) ? 2 : 3))'],
                [
'ky', 'kir', 'Kirghiz', 'кыргыз тили', 'ltr', 1, '0'],

                [
'la', 'lat', 'Latin', 'Latine', 'ltr', null, null],
                [
'lb', 'ltz', 'Luxembourgish', 'Lëtzebuergesch', 'ltr', 2, 'n != 1'],
                [
'lg', 'lug', 'Ganda', 'Luganda', 'ltr', null, null],
                [
'li', 'lim', 'Limburgan', 'Limburgs', 'ltr', null, null],
                [
'ln', 'lin', 'Lingala', 'Lingála', 'ltr', 2, 'n>1'],
                [
'lo', 'lao', 'Lao', 'ພາສາລາວ', 'ltr', 1, '0'],
                [
'lt', 'lit', 'Lithuanian', 'Lietuvių kalba', 'ltr', 3, 'n%10==1 && n%100!=11 ? 0 : (n%10>=2 && (n%100<10 or n%100>=20) ? 1 : 2)'],
                [
'lu', 'lub', 'Luba-Katanga', 'Luba-Katanga', 'ltr', null, null],
                [
'lv', 'lav', 'Latvian', 'Latviešu valoda', 'ltr', 3, 'n%10==1 && n%100!=11 ? 0 : (n != 0 ? 1 : 2)'],

                [
'mg', 'mlg', 'Malagasy', 'Malagasy fiteny', 'ltr', 2, 'n > 1'],
                [
'mh', 'mah', 'Marshallese', 'Kajin M̧ajeļ', 'ltr', null, null],
                [
'mi', 'mao', 'Maori', 'Te reo Māori', 'ltr', 2, 'n > 1'],
                [
'mk', 'mac', 'Macedonian', 'македонски јазик', 'ltr', 2, 'n==1 || n%10==1 ? 0 : 1'],
                [
'ml', 'mal', 'Malayalam', 'മലയാളം', 'ltr', 2, 'n != 1'],
                [
'mn', 'mon', 'Mongolian', 'Монгол', 'ltr', 2, 'n != 1'],
                [
'mo', null, 'Moldavian', 'Limba moldovenească', 'ltr', 3, 'n==1 ? 0 : ((n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2)'], //cf: ro
               
['mr', 'mar', 'Marathi', 'मराठी', 'ltr', 2, 'n != 1'],
                [
'ms', 'may', 'Malay', 'Bahasa Melayu', 'ltr', 1, '0'],
                [
'mt', 'mlt', 'Maltese', 'Malti', 'ltr', 4, 'n==1 ? 0 : (n==0 || ( n%100>1 && n%100<11) ? 1 : ((n%100>10 && n%100<20 ) ? 2 : 3))'],
                [
'my', 'bur', 'Burmese', 'ဗမာစာ', 'ltr', 1, '0'],

                [
'na', 'nau', 'Nauru', 'Ekakairũ Naoero', 'ltr', null, null],
                [
'nb', 'nob', 'Norwegian Bokmål', 'Norsk bokmål', 'ltr', 2, 'n != 1'],
                [
'nd', 'nde', 'North Ndebele', 'isiNdebele', 'ltr', null, null],
                [
'ne', 'nep', 'Nepali', 'नेपाली', 'ltr', 2, 'n != 1'],
                [
'ng', 'ndo', 'Ndonga', 'Owambo', 'ltr', null, null],
                [
'nl', 'dut', 'Flemish', 'Nederlands', 'ltr', 2, 'n != 1'],
                [
'nl-be', null, 'Flemish', 'Nederlands (Belgium)', 'ltr', 2, 'n != 1'],
                [
'nn', 'nno', 'Norwegian Nynorsk', 'Norsk nynorsk', 'ltr', 2, 'n != 1'],
                [
'no', 'nor', 'Norwegian', 'Norsk', 'ltr', 2, 'n != 1'],
                [
'nr', 'nbl', 'South Ndebele', 'Ndébélé', 'ltr', null, null],
                [
'nv', 'nav', 'Navajo', 'Diné bizaad', 'ltr', null, null],
                [
'ny', 'nya', 'Chichewa', 'ChiCheŵa', 'ltr', null, null],

                [
'oc', 'oci', 'Occitan', 'Occitan', 'ltr', 2, 'n > 1'],
                [
'oj', 'oji', 'Ojibwa', 'ᐊᓂᔑᓈᐯᒧᐎᓐ', 'ltr', null, null],
                [
'om', 'orm', 'Oromo', 'Afaan Oromoo', 'ltr', null, null],
                [
'or', 'ori', 'Oriya', 'ଓଡ଼ିଆ', 'ltr', 2, 'n != 1'],
                [
'os', 'oss', 'Ossetian', 'Ирон æвзаг', 'ltr', null, null],

                [
'pa', 'pan', 'Panjabi', 'ਪੰਜਾਬੀ', 'ltr', 2, 'n != 1'],
                [
'pi', 'pli', 'Pali', 'पाऴि', 'ltr', null, null],
                [
'pl', 'pol', 'Polish', 'Polski', 'ltr', 3, 'n==1 ? 0 : (n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)'],
                [
'ps', 'pus', 'Pushto', '‫پښتو', 'rtl', 2, 'n != 1'],
                [
'pt', 'por', 'Portuguese', 'Português', 'ltr', 2, 'n != 1'],
                [
'pt-br', null, 'Brazilian Portuguese', 'Português do Brasil', 'ltr', 2, 'n > 1'],

                [
'qu', 'que', 'Quechua', 'Runa Simi', 'ltr', null, null],

                [
'rm', 'roh', 'Romansh', 'Rumantsch grischun', 'ltr', 2, 'n != 1'],
                [
'rn', 'run', 'Rundi', 'kiRundi', 'ltr', null, null],
                [
'ro', 'rum', 'Romanian', 'Română', 'ltr', 3, 'n==1 ? 0 : ((n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2)'],
                [
'ru', 'rus', 'Russian', 'Русский', 'ltr', 3, 'n%10==1 && n%100!=11 ? 0 : (n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)'],
                [
'rw', 'kin', 'Kinyarwanda', 'IKinyarwanda', 'ltr', 2, 'n != 1'],

                [
'sa', 'san', 'Sanskrit', 'संस्कृतम्', 'ltr', null, null],
                [
'sc', 'srd', 'Sardinian', 'sardu', 'ltr', null, null],
                [
'sd', 'snd', 'Sindhi', 'सिन्धी', 'ltr', 2, 'n != 1'],
                [
'se', 'sme', 'Northern Sami', 'Davvisámegiella', 'ltr', null, null],
                [
'sg', 'sag', 'Sango', 'Yângâ tî sängö', 'ltr', null, null],
                [
'sh', null, null, 'SrpskoHrvatski', 'ltr', null, null], //!
               
['si', 'sin', 'Sinhalese', 'සිංහල', 'ltr', 2, 'n != 1'],
                [
'sk', 'slo', 'Slovak', 'Slovenčina', 'ltr', 3, '(n==1) ? 0 : ((n>=2 && n<=4) ? 1 : 2)'],
                [
'sl', 'slv', 'Slovenian', 'Slovenščina', 'ltr', 4, 'n%100==1 ? 1 : (n%100==2 ? 2 : (n%100==3 || n%100==4 ? 3 : 0))'],
                [
'sm', 'smo', 'Samoan', "Gagana fa'a Samoa", 'ltr', null, null],
                [
'sn', 'sna', 'Shona', 'chiShona', 'ltr', null, null],
                [
'so', 'som', 'Somali', 'Soomaaliga', 'ltr', 2, 'n != 1'],
                [
'sq', 'alb', 'Albanian', 'Shqip', 'ltr', 2, 'n != 1'],
                [
'sr', 'srp', 'Serbian', 'српски језик', 'ltr', 3, 'n%10==1 && n%100!=11 ? 0 : (n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)'],
                [
'ss', 'ssw', 'Swati', 'SiSwati', 'ltr', null, null],
                [
'st', 'sot', 'Southern Sotho', 'seSotho', 'ltr', null, null],
                [
'su', 'sun', 'Sundanese', 'Basa Sunda', 'ltr', 1, '0'],
                [
'sv', 'swe', 'Swedish', 'Svenska', 'ltr', 2, 'n != 1'],
                [
'sw', 'swa', 'Swahili', 'Kiswahili', 'ltr', 2, 'n != 1'],

                [
'ta', 'tam', 'Tamil', 'தமிழ்', 'ltr', 2, 'n != 1'],
                [
'te', 'tel', 'Telugu', 'తెలుగు', 'ltr', 2, 'n != 1'],
                [
'tg', 'tgk', 'Tajik', 'тоҷикӣ', 'ltr', 2, 'n > 1'],
                [
'th', 'tha', 'Thai', 'ไทย', 'ltr', 1, '0'],
                [
'ti', 'tir', 'Tigrinya', 'ትግርኛ', 'ltr', 2, 'n > 1'],
                [
'tk', 'tuk', 'Turkmen', 'Türkmen', 'ltr', 2, 'n != 1'],
                [
'tl', 'tlg', 'Tagalog', 'Tagalog', 'ltr', null, null],
                [
'tn', 'tsn', 'Tswana', 'seTswana', 'ltr', null, null],
                [
'to', 'ton', 'Tonga', 'faka Tonga', 'ltr', null, null],
                [
'tr', 'tur', 'Turkish', 'Türkçe', 'ltr', 2, 'n > 1'],
                [
'ts', 'tso', 'Tsonga', 'xiTsonga', 'ltr', null, null],
                [
'tt', 'tat', 'Tatar', 'татарча', 'ltr', 1, '0'],
                [
'tw', 'twi', 'Twi', 'Twi', 'ltr', null, null],
                [
'ty', 'tah', 'Tahitian', 'Reo Mā`ohi', 'ltr', null, null],

                [
'ug', 'uig', 'Uighur', 'Uyƣurqə', 'ltr', 1, '0'],
                [
'uk', 'ukr', 'Ukrainian', 'Українська', 'ltr', 3, 'n%10==1 && n%100!=11 ? 0 : (n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)'],
                [
'ur', 'urd', 'Urdu', '‫اردو', 'rtl', 2, 'n != 1'],
                [
'uz', 'uzb', 'Uzbek', "O'zbek", 'ltr', 2, 'n > 1'],

                [
've', 'ven', 'Venda', 'tshiVenḓa', 'ltr', null, null],
                [
'vi', 'vie', 'Vietnamese', 'Tiếng Việt', 'ltr', 1, '0'],
                [
'vo', 'vol', 'Volapük', 'Volapük', 'ltr', null, null],

                [
'wa', 'wln', 'Walloon', 'Walon', 'ltr', 2, 'n > 1'],
                [
'wo', 'wol', 'Wolof', 'Wollof', 'ltr', 1, '0'],

                [
'xh', 'xho', 'Xhosa', 'isiXhosa', 'ltr', null, null],

                [
'yi', 'yid', 'Yiddish', '‫ייִדיש', 'rtl', null, null],
                [
'yo', 'yor', 'Yoruba', 'Yorùbá', 'ltr', 2, 'n != 1'],

                [
'za', 'zha', 'Chuang', 'Saɯ cueŋƅ', 'ltr', null, null],
                [
'zh-cn', 'zhi', 'Chinese', '中文', 'ltr', 1, '0'],
                [
'zh-hk', null, 'Honk Kong Chinese', '中文 (香港)', 'ltr', 1, '0'],
                [
'zh-tw', null, 'Taiwan Chinese', '中文 (臺灣)', 'ltr', 1, '0'],
                [
'zu', 'zul', 'Zulu', 'isiZulu', 'ltr', null, null],
            ];
        }

       
$r = [];
        foreach (
self::$languages_definitions as $_) {
           
$r[$_[0]] = empty($_[$type]) ? $default : $_[$type];
        }

        return
$r;
    }
   
//@}
}