Merge "resourceloader: Optimize module registry sent in the startup module"
[lhc/web/wiklou.git] / languages / Language.php
index 320688a..95bd31e 100644 (file)
@@ -34,46 +34,11 @@ if ( function_exists( 'mb_strtoupper' ) ) {
        mb_internal_encoding( 'UTF-8' );
 }
 
-/**
- * a fake language converter
- *
- * @ingroup Language
- */
-class FakeConverter {
-       /**
-        * @var Language
-        */
-       public $mLang;
-       function __construct( $langobj ) { $this->mLang = $langobj; }
-       function autoConvert( $text, $variant = false ) { return $text; }
-       function autoConvertToAllVariants( $text ) { return array( $this->mLang->getCode() => $text ); }
-       function convert( $t ) { return $t; }
-       function convertTo( $text, $variant ) { return $text; }
-       function convertTitle( $t ) { return $t->getPrefixedText(); }
-       function convertNamespace( $ns ) { return $this->mLang->getFormattedNsText( $ns ); }
-       function getVariants() { return array( $this->mLang->getCode() ); }
-       function getVariantFallbacks( $variant ) { return $this->mLang->getCode(); }
-       function getPreferredVariant() { return $this->mLang->getCode(); }
-       function getDefaultVariant() { return $this->mLang->getCode(); }
-       function getURLVariant() { return ''; }
-       function getConvRuleTitle() { return false; }
-       function findVariantLink( &$l, &$n, $ignoreOtherCond = false ) { }
-       function getExtraHashOptions() { return ''; }
-       function getParsedTitle() { return ''; }
-       function markNoConversion( $text, $noParse = false ) { return $text; }
-       function convertCategoryKey( $key ) { return $key; }
-       /** @deprecated since 1.22 is no longer used */
-       function armourMath( $text ) { return $text; }
-       function validateVariant( $variant = null ) { return $variant === $this->mLang->getCode() ? $variant : null; }
-       function translate( $text, $variant ) { return $text; }
-}
-
 /**
  * Internationalisation code
  * @ingroup Language
  */
 class Language {
-
        /**
         * @var LanguageConverter
         */
@@ -180,7 +145,7 @@ class Language {
 
        /**
         * Get a cached or new language object for a given language code
-        * @param $code String
+        * @param string $code
         * @return Language
         */
        static function factory( $code ) {
@@ -205,7 +170,7 @@ class Language {
 
        /**
         * Create a language object for a given language code
-        * @param $code String
+        * @param string $code
         * @throws MWException
         * @return Language
         */
@@ -261,7 +226,10 @@ class Language {
         * @since 1.21
         */
        public static function isSupportedLanguage( $code ) {
-               return $code === strtolower( $code ) && is_readable( self::getMessagesFileName( $code ) );
+               return self::isValidBuiltInCode( $code )
+                       && ( is_readable( self::getMessagesFileName( $code ) )
+                               || is_readable( self::getJsonMessagesFileName( $code ) )
+               );
        }
 
        /**
@@ -273,8 +241,8 @@ class Language {
         * Based on regexes by Mark Davis of the Unicode Consortium:
         * http://unicode.org/repos/cldr/trunk/tools/java/org/unicode/cldr/util/data/langtagRegex.txt
         *
-        * @param $code string
-        * @param $lenient boolean Whether to allow '_' as separator. The default is only '-'.
+        * @param string $code
+        * @param bool $lenient Whether to allow '_' as separator. The default is only '-'.
         *
         * @return bool
         * @since 1.21
@@ -329,7 +297,7 @@ class Language {
         * not it exists. This includes codes which are used solely for
         * customisation via the MediaWiki namespace.
         *
-        * @param $code string
+        * @param string $code
         *
         * @return bool
         */
@@ -351,9 +319,9 @@ class Language {
 
        /**
         * Returns true if a language code is of a valid form for the purposes of
-        * internal customisation of MediaWiki, via Messages*.php.
+        * internal customisation of MediaWiki, via Messages*.php or *.json.
         *
-        * @param $code string
+        * @param string $code
         *
         * @throws MWException
         * @since 1.18
@@ -377,7 +345,7 @@ class Language {
        /**
         * Returns true if a language code is an IETF tag known to MediaWiki.
         *
-        * @param $code string
+        * @param string $code
         *
         * @since 1.21
         * @return bool
@@ -406,8 +374,8 @@ class Language {
        }
 
        /**
-        * @param $code
-        * @return String Name of the language class
+        * @param string $code
+        * @return string Name of the language class
         */
        public static function classFromCode( $code ) {
                if ( $code == 'en' ) {
@@ -420,7 +388,7 @@ class Language {
        /**
         * Includes language class files
         *
-        * @param $class string Name of the language class
+        * @param string $class Name of the language class
         */
        public static function preloadLanguageClass( $class ) {
                global $IP;
@@ -472,15 +440,17 @@ class Language {
         * Hook which will be called if this is the content language.
         * Descendants can use this to register hook functions or modify globals
         */
-       function initContLang() { }
+       function initContLang() {
+       }
 
        /**
         * Same as getFallbacksFor for current language.
         * @return array|bool
-        * @deprecated in 1.19
+        * @deprecated since 1.19
         */
        function getFallbackLanguageCode() {
                wfDeprecated( __METHOD__, '1.19' );
+
                return self::getFallbackFor( $this->mCode );
        }
 
@@ -537,12 +507,13 @@ class Language {
 
                        wfRunHooks( 'LanguageGetNamespaces', array( &$this->namespaceNames ) );
                }
+
                return $this->namespaceNames;
        }
 
        /**
         * Arbitrarily set all of the namespace names at once. Mainly used for testing
-        * @param $namespaces Array of namespaces (id => name)
+        * @param array $namespaces Array of namespaces (id => name)
         */
        public function setNamespaces( array $namespaces ) {
                $this->namespaceNames = $namespaces;
@@ -581,11 +552,12 @@ class Language {
         * echo $mw_ns; // prints 'MediaWiki'
         * </code>
         *
-        * @param $index Int: the array key of the namespace to return
-        * @return mixed, string if the namespace value exists, otherwise false
+        * @param int $index The array key of the namespace to return
+        * @return string|bool String if the namespace value exists, otherwise false
         */
        function getNsText( $index ) {
                $ns = $this->getNamespaces();
+
                return isset( $ns[$index] ) ? $ns[$index] : false;
        }
 
@@ -604,26 +576,30 @@ class Language {
         */
        function getFormattedNsText( $index ) {
                $ns = $this->getNsText( $index );
+
                return strtr( $ns, '_', ' ' );
        }
 
        /**
         * Returns gender-dependent namespace alias if available.
-        * @param $index Int: namespace index
-        * @param $gender String: gender key (male, female... )
-        * @return String
+        * See https://www.mediawiki.org/wiki/Manual:$wgExtraGenderNamespaces
+        * @param int $index Namespace index
+        * @param string $gender Gender key (male, female... )
+        * @return string
         * @since 1.18
         */
        function getGenderNsText( $index, $gender ) {
                global $wgExtraGenderNamespaces;
 
-               $ns = $wgExtraGenderNamespaces + self::$dataCache->getItem( $this->mCode, 'namespaceGenderAliases' );
+               $ns = $wgExtraGenderNamespaces +
+                       self::$dataCache->getItem( $this->mCode, 'namespaceGenderAliases' );
+
                return isset( $ns[$index][$gender] ) ? $ns[$index][$gender] : $this->getNsText( $index );
        }
 
        /**
-        * Whether this language makes distinguishes genders for example in
-        * namespaces.
+        * Whether this language uses gender-dependent namespace aliases.
+        * See https://www.mediawiki.org/wiki/Manual:$wgExtraGenderNamespaces
         * @return bool
         * @since 1.18
         */
@@ -648,8 +624,8 @@ class Language {
         * Only matches namespace names for the current language, not the
         * canonical ones defined in Namespace.php.
         *
-        * @param $text String
-        * @return mixed An integer if $text is a valid value otherwise false
+        * @param string $text
+        * @return int|bool An integer if $text is a valid value otherwise false
         */
        function getLocalNsIndex( $text ) {
                $lctext = $this->lc( $text );
@@ -676,7 +652,8 @@ class Language {
                        }
 
                        global $wgExtraGenderNamespaces;
-                       $genders = $wgExtraGenderNamespaces + (array)self::$dataCache->getItem( $this->mCode, 'namespaceGenderAliases' );
+                       $genders = $wgExtraGenderNamespaces +
+                               (array)self::$dataCache->getItem( $this->mCode, 'namespaceGenderAliases' );
                        foreach ( $genders as $index => $forms ) {
                                foreach ( $forms as $alias ) {
                                        $aliases[$alias] = $index;
@@ -696,6 +673,7 @@ class Language {
 
                        $this->namespaceAliases = $aliases + $convertedNames;
                }
+
                return $this->namespaceAliases;
        }
 
@@ -729,8 +707,8 @@ class Language {
         * Get a namespace key by value, case insensitive.  Canonical namespace
         * names override custom ones defined for the current language.
         *
-        * @param $text String
-        * @return mixed An integer if $text is a valid value otherwise false
+        * @param string $text
+        * @return int|bool An integer if $text is a valid value otherwise false
         */
        function getNsIndex( $text ) {
                $lctext = $this->lc( $text );
@@ -745,8 +723,8 @@ class Language {
        /**
         * short names for language variants used for language conversion links.
         *
-        * @param $code String
-        * @param $usemsg bool Use the "variantname-xyz" message if it exists
+        * @param string $code
+        * @param bool $usemsg Use the "variantname-xyz" message if it exists
         * @return string
         */
        function getVariantname( $code, $usemsg = true ) {
@@ -764,7 +742,7 @@ class Language {
        }
 
        /**
-        * @param $name string
+        * @param string $name
         * @return string
         */
        function specialPage( $name ) {
@@ -810,7 +788,7 @@ class Language {
        }
 
        /**
-        * @param  $image
+        * @param string $image
         * @return array|null
         */
        function getImageFile( $image ) {
@@ -825,7 +803,7 @@ class Language {
        }
 
        /**
-        * @param  $tog
+        * @param string $tog
         * @return string
         */
        function getUserToggle( $tog ) {
@@ -837,10 +815,10 @@ class Language {
         * Only those defined in MediaWiki, no other data like CLDR.
         * If $customisedOnly is true, only returns codes with a messages file
         *
-        * @param $customisedOnly bool
+        * @param bool $customisedOnly
         *
         * @return array
-        * @deprecated in 1.20, use fetchLanguageNames()
+        * @deprecated since 1.20, use fetchLanguageNames()
         */
        public static function getLanguageNames( $customisedOnly = false ) {
                return self::fetchLanguageNames( null, $customisedOnly ? 'mwfile' : 'mw' );
@@ -850,10 +828,10 @@ class Language {
         * Get translated language names. This is done on best effort and
         * by default this is exactly the same as Language::getLanguageNames.
         * The CLDR extension provides translated names.
-        * @param $code String Language code.
-        * @return Array language code => language name
+        * @param string $code Language code.
+        * @return array Language code => language name
         * @since 1.18.0
-        * @deprecated in 1.20, use fetchLanguageNames()
+        * @deprecated since 1.20, use fetchLanguageNames()
         */
        public static function getTranslatedLanguageNames( $code ) {
                return self::fetchLanguageNames( $code, 'all' );
@@ -861,13 +839,13 @@ class Language {
 
        /**
         * Get an array of language names, indexed by code.
-        * @param $inLanguage null|string: Code of language in which to return the names
+        * @param null|string $inLanguage Code of language in which to return the names
         *              Use null for autonyms (native names)
-        * @param $include string:
+        * @param string $include One of:
         *              'all' all available languages
         *              'mw' only if the language is defined in MediaWiki or wgExtraLanguageNames (default)
         *              'mwfile' only if the language is in 'mw' *and* has a message file
-        * @return array: language code => language name
+        * @return array Language code => language name
         * @since 1.20
         */
        public static function fetchLanguageNames( $inLanguage = null, $include = 'mw' ) {
@@ -910,21 +888,25 @@ class Language {
                        # We do this using a foreach over the codes instead of a directory
                        # loop so that messages files in extensions will work correctly.
                        foreach ( $returnMw as $code => $value ) {
-                               if ( is_readable( self::getMessagesFileName( $code ) ) ) {
+                               if ( is_readable( self::getMessagesFileName( $code ) )
+                                       || is_readable( self::getJsonMessagesFileName( $code ) )
+                               ) {
                                        $namesMwFile[$code] = $names[$code];
                                }
                        }
+
                        return $namesMwFile;
                }
+
                # 'mw' option; default if it's not one of the other two options (all/mwfile)
                return $returnMw;
        }
 
        /**
-        * @param $code string: The code of the language for which to get the name
-        * @param $inLanguage null|string: Code of language in which to return the name (null for autonyms)
-        * @param $include string: 'all', 'mw' or 'mwfile'; see fetchLanguageNames()
-        * @return string: Language name or empty
+        * @param string $code The code of the language for which to get the name
+        * @param null|string $inLanguage Code of language in which to return the name (null for autonyms)
+        * @param string $include 'all', 'mw' or 'mwfile'; see fetchLanguageNames()
+        * @return string Language name or empty
         * @since 1.20
         */
        public static function fetchLanguageName( $code, $inLanguage = null, $include = 'all' ) {
@@ -936,7 +918,7 @@ class Language {
        /**
         * Get a message from the MediaWiki namespace.
         *
-        * @param $msg String: message name
+        * @param string $msg Message name
         * @return string
         */
        function getMessageFromDB( $msg ) {
@@ -946,16 +928,16 @@ class Language {
        /**
         * Get the native language name of $code.
         * Only if defined in MediaWiki, no other data like CLDR.
-        * @param $code string
+        * @param string $code
         * @return string
-        * @deprecated in 1.20, use fetchLanguageName()
+        * @deprecated since 1.20, use fetchLanguageName()
         */
        function getLanguageName( $code ) {
                return self::fetchLanguageName( $code );
        }
 
        /**
-        * @param $key string
+        * @param string $key
         * @return string
         */
        function getMonthName( $key ) {
@@ -974,7 +956,7 @@ class Language {
        }
 
        /**
-        * @param $key string
+        * @param string $key
         * @return string
         */
        function getMonthNameGen( $key ) {
@@ -982,7 +964,7 @@ class Language {
        }
 
        /**
-        * @param $key string
+        * @param string $key
         * @return string
         */
        function getMonthAbbreviation( $key ) {
@@ -1001,7 +983,7 @@ class Language {
        }
 
        /**
-        * @param $key string
+        * @param string $key
         * @return string
         */
        function getWeekdayName( $key ) {
@@ -1009,7 +991,7 @@ class Language {
        }
 
        /**
-        * @param $key string
+        * @param string $key
         * @return string
         */
        function getWeekdayAbbreviation( $key ) {
@@ -1017,7 +999,7 @@ class Language {
        }
 
        /**
-        * @param $key string
+        * @param string $key
         * @return string
         */
        function getIranianCalendarMonthName( $key ) {
@@ -1025,7 +1007,7 @@ class Language {
        }
 
        /**
-        * @param $key string
+        * @param string $key
         * @return string
         */
        function getHebrewCalendarMonthName( $key ) {
@@ -1033,7 +1015,7 @@ class Language {
        }
 
        /**
-        * @param $key string
+        * @param string $key
         * @return string
         */
        function getHebrewCalendarMonthNameGen( $key ) {
@@ -1041,7 +1023,7 @@ class Language {
        }
 
        /**
-        * @param $key string
+        * @param string $key
         * @return string
         */
        function getHijriCalendarMonthName( $key ) {
@@ -1103,11 +1085,11 @@ class Language {
         * time zone, if any. Note that the format characters crUeIOPTZ will assume
         * $ts is UTC if $zone is not given.
         *
-        * @param $format String
-        * @param $ts String: 14-character timestamp
+        * @param string $format
+        * @param string $ts 14-character timestamp
         *      YYYYMMDDHHMMSS
         *      01234567890123
-        * @param $zone DateTimeZone: Timezone of $ts
+        * @param DateTimeZone $zone Timezone of $ts
         * @todo handling of "o" format character for Iranian, Hebrew, Hijri & Thai?
         *
         * @throws MWException
@@ -1135,14 +1117,21 @@ class Language {
                        throw new MWException( __METHOD__ . ": The timestamp $ts should be a number" );
                }
 
-               for ( $p = 0; $p < strlen( $format ); $p++ ) {
+               $formatLength = strlen( $format );
+               for ( $p = 0; $p < $formatLength; $p++ ) {
                        $num = false;
                        $code = $format[$p];
-                       if ( $code == 'x' && $p < strlen( $format ) - 1 ) {
+                       if ( $code == 'x' && $p < $formatLength - 1 ) {
                                $code .= $format[++$p];
                        }
 
-                       if ( ( $code === 'xi' || $code == 'xj' || $code == 'xk' || $code == 'xm' || $code == 'xo' || $code == 'xt' ) && $p < strlen( $format ) - 1 ) {
+                       if ( ( $code === 'xi'
+                                       || $code === 'xj'
+                                       || $code === 'xk'
+                                       || $code === 'xm'
+                                       || $code === 'xo'
+                                       || $code === 'xt' )
+                               && $p < $formatLength - 1 ) {
                                $code .= $format[++$p];
                        }
 
@@ -1373,7 +1362,7 @@ class Language {
                                        break;
                                case '\\':
                                        # Backslash escaping
-                                       if ( $p < strlen( $format ) - 1 ) {
+                                       if ( $p < $formatLength - 1 ) {
                                                $s .= $format[++$p];
                                        } else {
                                                $s .= '\\';
@@ -1381,7 +1370,7 @@ class Language {
                                        break;
                                case '"':
                                        # Quoted literal
-                                       if ( $p < strlen( $format ) - 1 ) {
+                                       if ( $p < $formatLength - 1 ) {
                                                $endQuote = strpos( $format, '"', $p + 1 );
                                                if ( $endQuote === false ) {
                                                        # No terminating quote, assume literal "
@@ -1413,6 +1402,7 @@ class Language {
                                }
                        }
                }
+
                return $s;
        }
 
@@ -1427,7 +1417,7 @@ class Language {
         *
         * Link: http://www.farsiweb.info/jalali/jalali.c
         *
-        * @param $ts string
+        * @param string $ts
         *
         * @return string
         */
@@ -1485,7 +1475,7 @@ class Language {
         *
         * @see http://phpnuke.org/modules.php?name=News&file=article&sid=8234&mode=thread&order=0&thold=0
         *
-        * @param $ts string
+        * @param string $ts
         *
         * @return string
         */
@@ -1515,8 +1505,10 @@ class Language {
                $zl = $zjd -1948440 + 10632;
                $zn = (int)( ( $zl - 1 ) / 10631 );
                $zl = $zl - 10631 * $zn + 354;
-               $zj = ( (int)( ( 10985 - $zl ) / 5316 ) ) * ( (int)( ( 50 * $zl ) / 17719 ) ) + ( (int)( $zl / 5670 ) ) * ( (int)( ( 43 * $zl ) / 15238 ) );
-               $zl = $zl - ( (int)( ( 30 - $zj ) / 15 ) ) * ( (int)( ( 17719 * $zj ) / 50 ) ) - ( (int)( $zj / 16 ) ) * ( (int)( ( 15238 * $zj ) / 43 ) ) + 29;
+               $zj = ( (int)( ( 10985 - $zl ) / 5316 ) ) * ( (int)( ( 50 * $zl ) / 17719 ) ) +
+                       ( (int)( $zl / 5670 ) ) * ( (int)( ( 43 * $zl ) / 15238 ) );
+               $zl = $zl - ( (int)( ( 30 - $zj ) / 15 ) ) * ( (int)( ( 17719 * $zj ) / 50 ) ) -
+                       ( (int)( $zj / 16 ) ) * ( (int)( ( 15238 * $zj ) / 43 ) ) + 29;
                $zm = (int)( ( 24 * $zl ) / 709 );
                $zd = $zl - (int)( ( 709 * $zm ) / 24 );
                $zy = 30 * $zn + $zj - 30;
@@ -1535,7 +1527,7 @@ class Language {
         * The months are counted from Tishrei = 1. In a leap year, Adar I is 13
         * and Adar II is 14. In a non-leap year, Adar is 6.
         *
-        * @param $ts string
+        * @param string $ts
         *
         * @return string
         */
@@ -1676,7 +1668,7 @@ class Language {
         * Based on Carl Friedrich Gauss algorithm for finding Easter date.
         * Used for Hebrew date.
         *
-        * @param $year int
+        * @param int $year
         *
         * @return string
         */
@@ -1714,9 +1706,9 @@ class Language {
         *       http://en.wikipedia.org/wiki/Minguo_calendar
         *       http://en.wikipedia.org/wiki/Japanese_era_name
         *
-        * @param $ts String: 14-character timestamp
-        * @param $cName String: calender name
-        * @return Array: converted year, month, day
+        * @param string $ts 14-character timestamp
+        * @param string $cName Calender name
+        * @return array Converted year, month, day
         */
        private static function tsToYear( $ts, $cName ) {
                $gy = substr( $ts, 0, 4 );
@@ -1738,7 +1730,10 @@ class Language {
                        # Deduct years from the Gregorian calendar
                        # depending on the nengo periods
                        # Months and days are identical
-                       if ( ( $gy < 1912 ) || ( ( $gy == 1912 ) && ( $gm < 7 ) ) || ( ( $gy == 1912 ) && ( $gm == 7 ) && ( $gd < 31 ) ) ) {
+                       if ( ( $gy < 1912 )
+                               || ( ( $gy == 1912 ) && ( $gm < 7 ) )
+                               || ( ( $gy == 1912 ) && ( $gm == 7 ) && ( $gd < 31 ) )
+                       ) {
                                # Meiji period
                                $gy_gannen = $gy - 1868 + 1;
                                $gy_offset = $gy_gannen;
@@ -1791,7 +1786,7 @@ class Language {
        /**
         * Roman number formatting up to 10000
         *
-        * @param $num int
+        * @param int $num
         *
         * @return string
         */
@@ -1800,7 +1795,8 @@ class Language {
                        array( '', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X' ),
                        array( '', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC', 'C' ),
                        array( '', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM', 'M' ),
-                       array( '', 'M', 'MM', 'MMM', 'MMMM', 'MMMMM', 'MMMMMM', 'MMMMMMM', 'MMMMMMMM', 'MMMMMMMMM', 'MMMMMMMMMM' )
+                       array( '', 'M', 'MM', 'MMM', 'MMMM', 'MMMMM', 'MMMMMM', 'MMMMMMM',
+                               'MMMMMMMM', 'MMMMMMMMM', 'MMMMMMMMMM' )
                );
 
                $num = intval( $num );
@@ -1821,7 +1817,7 @@ class Language {
        /**
         * Hebrew Gematria number formatting up to 9999
         *
-        * @param $num int
+        * @param int $num
         *
         * @return string
         */
@@ -1884,9 +1880,9 @@ class Language {
        /**
         * Used by date() and time() to adjust the time output.
         *
-        * @param $ts Int the time in date('YmdHis') format
-        * @param $tz Mixed: adjust the time by this amount (default false, mean we
-        *            get user timecorrection setting)
+        * @param int $ts The time in date('YmdHis') format
+        * @param mixed $tz Adjust the time by this amount (default false, mean we
+        *   get user timecorrection setting)
         * @return int
         */
        function userAdjust( $ts, $tz = false ) {
@@ -1969,9 +1965,9 @@ class Language {
         * }
         *</code>
         *
-        * @param $usePrefs Mixed: if true, the user's preference is used
-        *                         if false, the site/language default is used
-        *                         if int/string, assumed to be a format.
+        * @param int|string|bool $usePrefs If true, the user's preference is used
+        *   if false, the site/language default is used
+        *   if int/string, assumed to be a format.
         * @return string
         */
        function dateFormat( $usePrefs = true ) {
@@ -1997,8 +1993,8 @@ class Language {
 
        /**
         * Get a format string for a given type and preference
-        * @param $type string May be date, time or both
-        * @param $pref string The format name as it appears in Messages*.php
+        * @param string $type May be date, time or both
+        * @param string $pref The format name as it appears in Messages*.php
         *
         * @since 1.22 New type 'pretty' that provides a more readable timestamp format
         *
@@ -2027,13 +2023,13 @@ class Language {
        }
 
        /**
-        * @param $ts Mixed: the time format which needs to be turned into a
-        *            date('YmdHis') format with wfTimestamp(TS_MW,$ts)
-        * @param $adj Bool: whether to adjust the time output according to the
-        *             user configured offset ($timecorrection)
-        * @param $format Mixed: true to use user's date format preference
-        * @param $timecorrection String|bool the time offset as returned by
-        *                        validateTimeZone() in Special:Preferences
+        * @param mixed $ts The time format which needs to be turned into a
+        *   date('YmdHis') format with wfTimestamp(TS_MW,$ts)
+        * @param bool $adj Whether to adjust the time output according to the
+        *   user configured offset ($timecorrection)
+        * @param mixed $format True to use user's date format preference
+        * @param string|bool $timecorrection The time offset as returned by
+        *   validateTimeZone() in Special:Preferences
         * @return string
         */
        function date( $ts, $adj = false, $format = true, $timecorrection = false ) {
@@ -2046,13 +2042,13 @@ class Language {
        }
 
        /**
-        * @param $ts Mixed: the time format which needs to be turned into a
-        *            date('YmdHis') format with wfTimestamp(TS_MW,$ts)
-        * @param $adj Bool: whether to adjust the time output according to the
-        *             user configured offset ($timecorrection)
-        * @param $format Mixed: true to use user's date format preference
-        * @param $timecorrection String|bool the time offset as returned by
-        *                        validateTimeZone() in Special:Preferences
+        * @param mixed $ts The time format which needs to be turned into a
+        *   date('YmdHis') format with wfTimestamp(TS_MW,$ts)
+        * @param bool $adj Whether to adjust the time output according to the
+        *   user configured offset ($timecorrection)
+        * @param mixed $format True to use user's date format preference
+        * @param string|bool $timecorrection The time offset as returned by
+        *   validateTimeZone() in Special:Preferences
         * @return string
         */
        function time( $ts, $adj = false, $format = true, $timecorrection = false ) {
@@ -2065,14 +2061,14 @@ class Language {
        }
 
        /**
-        * @param $ts Mixed: the time format which needs to be turned into a
-        *            date('YmdHis') format with wfTimestamp(TS_MW,$ts)
-        * @param $adj Bool: whether to adjust the time output according to the
-        *             user configured offset ($timecorrection)
-        * @param $format Mixed: what format to return, if it's false output the
-        *                default one (default true)
-        * @param $timecorrection String|bool the time offset as returned by
-        *                        validateTimeZone() in Special:Preferences
+        * @param mixed $ts The time format which needs to be turned into a
+        *   date('YmdHis') format with wfTimestamp(TS_MW,$ts)
+        * @param bool $adj Whether to adjust the time output according to the
+        *   user configured offset ($timecorrection)
+        * @param mixed $format What format to return, if it's false output the
+        *   default one (default true)
+        * @param string|bool $timecorrection The time offset as returned by
+        *   validateTimeZone() in Special:Preferences
         * @return string
         */
        function timeanddate( $ts, $adj = false, $format = true, $timecorrection = false ) {
@@ -2089,7 +2085,7 @@ class Language {
         *
         * @since 1.20
         *
-        * @param integer $seconds The amount of seconds.
+        * @param int $seconds The amount of seconds.
         * @param array $chosenIntervals The intervals to enable.
         *
         * @return string
@@ -2115,14 +2111,23 @@ class Language {
         *
         * @since 1.20
         *
-        * @param integer $seconds The amount of seconds.
+        * @param int $seconds The amount of seconds.
         * @param array $chosenIntervals The intervals to enable.
         *
         * @return array
         */
        public function getDurationIntervals( $seconds, array $chosenIntervals = array() ) {
                if ( empty( $chosenIntervals ) ) {
-                       $chosenIntervals = array( 'millennia', 'centuries', 'decades', 'years', 'days', 'hours', 'minutes', 'seconds' );
+                       $chosenIntervals = array(
+                               'millennia',
+                               'centuries',
+                               'decades',
+                               'years',
+                               'days',
+                               'hours',
+                               'minutes',
+                               'seconds'
+                       );
                }
 
                $intervals = array_intersect_key( self::$durationIntervals, array_flip( $chosenIntervals ) );
@@ -2146,21 +2151,21 @@ class Language {
        /**
         * Internal helper function for userDate(), userTime() and userTimeAndDate()
         *
-        * @param $type String: can be 'date', 'time' or 'both'
-        * @param $ts Mixed: the time format which needs to be turned into a
-        *            date('YmdHis') format with wfTimestamp(TS_MW,$ts)
-        * @param $user User object used to get preferences for timezone and format
-        * @param $options Array, can contain the following keys:
-        *        - 'timecorrection': time correction, can have the following values:
-        *             - true: use user's preference
-        *             - false: don't use time correction
-        *             - integer: value of time correction in minutes
-        *        - 'format': format to use, can have the following values:
-        *             - true: use user's preference
-        *             - false: use default preference
-        *             - string: format to use
+        * @param string $type Can be 'date', 'time' or 'both'
+        * @param mixed $ts The time format which needs to be turned into a
+        *   date('YmdHis') format with wfTimestamp(TS_MW,$ts)
+        * @param User $user User object used to get preferences for timezone and format
+        * @param array $options Array, can contain the following keys:
+        *   - 'timecorrection': time correction, can have the following values:
+        *     - true: use user's preference
+        *     - false: don't use time correction
+        *     - int: value of time correction in minutes
+        *   - 'format': format to use, can have the following values:
+        *     - true: use user's preference
+        *     - false: use default preference
+        *     - string: format to use
         * @since 1.19
-        * @return String
+        * @return string
         */
        private function internalUserTimeAndDate( $type, $ts, User $user, array $options ) {
                $ts = wfTimestamp( TS_MW, $ts );
@@ -2186,20 +2191,20 @@ class Language {
         * Get the formatted date for the given timestamp and formatted for
         * the given user.
         *
-        * @param $ts Mixed: the time format which needs to be turned into a
-        *            date('YmdHis') format with wfTimestamp(TS_MW,$ts)
-        * @param $user User object used to get preferences for timezone and format
-        * @param $options Array, can contain the following keys:
-        *        - 'timecorrection': time correction, can have the following values:
-        *             - true: use user's preference
-        *             - false: don't use time correction
-        *             - integer: value of time correction in minutes
-        *        - 'format': format to use, can have the following values:
-        *             - true: use user's preference
-        *             - false: use default preference
-        *             - string: format to use
+        * @param mixed $ts Mixed: the time format which needs to be turned into a
+        *   date('YmdHis') format with wfTimestamp(TS_MW,$ts)
+        * @param User $user User object used to get preferences for timezone and format
+        * @param array $options Array, can contain the following keys:
+        *   - 'timecorrection': time correction, can have the following values:
+        *     - true: use user's preference
+        *     - false: don't use time correction
+        *     - int: value of time correction in minutes
+        *   - 'format': format to use, can have the following values:
+        *     - true: use user's preference
+        *     - false: use default preference
+        *     - string: format to use
         * @since 1.19
-        * @return String
+        * @return string
         */
        public function userDate( $ts, User $user, array $options = array() ) {
                return $this->internalUserTimeAndDate( 'date', $ts, $user, $options );
@@ -2209,20 +2214,20 @@ class Language {
         * Get the formatted time for the given timestamp and formatted for
         * the given user.
         *
-        * @param $ts Mixed: the time format which needs to be turned into a
-        *            date('YmdHis') format with wfTimestamp(TS_MW,$ts)
-        * @param $user User object used to get preferences for timezone and format
-        * @param $options Array, can contain the following keys:
-        *        - 'timecorrection': time correction, can have the following values:
-        *             - true: use user's preference
-        *             - false: don't use time correction
-        *             - integer: value of time correction in minutes
-        *        - 'format': format to use, can have the following values:
-        *             - true: use user's preference
-        *             - false: use default preference
-        *             - string: format to use
+        * @param mixed $ts The time format which needs to be turned into a
+        *   date('YmdHis') format with wfTimestamp(TS_MW,$ts)
+        * @param User $user User object used to get preferences for timezone and format
+        * @param array $options Array, can contain the following keys:
+        *   - 'timecorrection': time correction, can have the following values:
+        *     - true: use user's preference
+        *     - false: don't use time correction
+        *     - int: value of time correction in minutes
+        *   - 'format': format to use, can have the following values:
+        *     - true: use user's preference
+        *     - false: use default preference
+        *     - string: format to use
         * @since 1.19
-        * @return String
+        * @return string
         */
        public function userTime( $ts, User $user, array $options = array() ) {
                return $this->internalUserTimeAndDate( 'time', $ts, $user, $options );
@@ -2232,20 +2237,20 @@ class Language {
         * Get the formatted date and time for the given timestamp and formatted for
         * the given user.
         *
-        * @param $ts Mixed: the time format which needs to be turned into a
-        *            date('YmdHis') format with wfTimestamp(TS_MW,$ts)
-        * @param $user User object used to get preferences for timezone and format
-        * @param $options Array, can contain the following keys:
-        *        - 'timecorrection': time correction, can have the following values:
-        *             - true: use user's preference
-        *             - false: don't use time correction
-        *             - integer: value of time correction in minutes
-        *        - 'format': format to use, can have the following values:
-        *             - true: use user's preference
-        *             - false: use default preference
-        *             - string: format to use
+        * @param mixed $ts the time format which needs to be turned into a
+        *   date('YmdHis') format with wfTimestamp(TS_MW,$ts)
+        * @param User $user User object used to get preferences for timezone and format
+        * @param array $options Array, can contain the following keys:
+        *   - 'timecorrection': time correction, can have the following values:
+        *     - true: use user's preference
+        *     - false: don't use time correction
+        *     - int: value of time correction in minutes
+        *   - 'format': format to use, can have the following values:
+        *     - true: use user's preference
+        *     - false: use default preference
+        *     - string: format to use
         * @since 1.19
-        * @return String
+        * @return string
         */
        public function userTimeAndDate( $ts, User $user, array $options = array() ) {
                return $this->internalUserTimeAndDate( 'both', $ts, $user, $options );
@@ -2268,9 +2273,12 @@ class Language {
         */
        public function getHumanTimestamp( MWTimestamp $ts, MWTimestamp $relativeTo, User $user ) {
                $diff = $ts->diff( $relativeTo );
-               $diffDay = (bool)( (int)$ts->timestamp->format( 'w' ) - (int)$relativeTo->timestamp->format( 'w' ) );
+               $diffDay = (bool)( (int)$ts->timestamp->format( 'w' ) -
+                       (int)$relativeTo->timestamp->format( 'w' ) );
                $days = $diff->days ?: (int)$diffDay;
-               if ( $diff->invert || $days > 5 && $ts->timestamp->format( 'Y' ) !== $relativeTo->timestamp->format( 'Y' ) ) {
+               if ( $diff->invert || $days > 5
+                       && $ts->timestamp->format( 'Y' ) !== $relativeTo->timestamp->format( 'Y' )
+               ) {
                        // Timestamps are in different years: use full timestamp
                        // Also do full timestamp for future dates
                        /**
@@ -2327,7 +2335,7 @@ class Language {
        }
 
        /**
-        * @param $key string
+        * @param string $key
         * @return array|null
         */
        function getMessage( $key ) {
@@ -2342,9 +2350,9 @@ class Language {
        }
 
        /**
-        * @param $in
-        * @param $out
-        * @param $string
+        * @param string $in
+        * @param string $out
+        * @param string $string
         * @return string
         */
        function iconv( $in, $out, $string ) {
@@ -2364,7 +2372,7 @@ class Language {
        // callback functions for uc(), lc(), ucwords(), ucwordbreaks()
 
        /**
-        * @param $matches array
+        * @param array $matches
         * @return mixed|string
         */
        function ucwordbreaksCallbackAscii( $matches ) {
@@ -2372,7 +2380,7 @@ class Language {
        }
 
        /**
-        * @param $matches array
+        * @param array $matches
         * @return string
         */
        function ucwordbreaksCallbackMB( $matches ) {
@@ -2380,7 +2388,7 @@ class Language {
        }
 
        /**
-        * @param $matches array
+        * @param array $matches
         * @return string
         */
        function ucCallback( $matches ) {
@@ -2389,7 +2397,7 @@ class Language {
        }
 
        /**
-        * @param $matches array
+        * @param array $matches
         * @return string
         */
        function lcCallback( $matches ) {
@@ -2398,7 +2406,7 @@ class Language {
        }
 
        /**
-        * @param $matches array
+        * @param array $matches
         * @return string
         */
        function ucwordsCallbackMB( $matches ) {
@@ -2406,7 +2414,7 @@ class Language {
        }
 
        /**
-        * @param $matches array
+        * @param array $matches
         * @return string
         */
        function ucwordsCallbackWiki( $matches ) {
@@ -2417,7 +2425,7 @@ class Language {
        /**
         * Make a string's first character uppercase
         *
-        * @param $str string
+        * @param string $str
         *
         * @return string
         */
@@ -2436,8 +2444,8 @@ class Language {
        /**
         * Convert a string to uppercase
         *
-        * @param $str string
-        * @param $first bool
+        * @param string $str
+        * @param bool $first
         *
         * @return string
         */
@@ -2467,7 +2475,7 @@ class Language {
        }
 
        /**
-        * @param $str string
+        * @param string $str
         * @return mixed|string
         */
        function lcfirst( $str ) {
@@ -2485,8 +2493,8 @@ class Language {
        }
 
        /**
-        * @param $str string
-        * @param $first bool
+        * @param string $str
+        * @param bool $first
         * @return mixed|string
         */
        function lc( $str, $first = false ) {
@@ -2515,7 +2523,7 @@ class Language {
        }
 
        /**
-        * @param $str string
+        * @param string $str
         * @return bool
         */
        function isMultibyte( $str ) {
@@ -2523,7 +2531,7 @@ class Language {
        }
 
        /**
-        * @param $str string
+        * @param string $str
         * @return mixed|string
         */
        function ucwords( $str ) {
@@ -2555,7 +2563,7 @@ class Language {
        /**
         * capitalize words at word breaks
         *
-        * @param $str string
+        * @param string $str
         * @return mixed
         */
        function ucwordbreaks( $str ) {
@@ -2566,7 +2574,8 @@ class Language {
                        $breaks = "[ \-\(\)\}\{\.,\?!]";
 
                        // find first letter after word break
-                       $replaceRegexp = "/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)|$breaks([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/";
+                       $replaceRegexp = "/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)|" .
+                               "$breaks([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/";
 
                        if ( function_exists( 'mb_strtoupper' ) ) {
                                return preg_replace_callback(
@@ -2601,7 +2610,7 @@ class Language {
         * uses this function when it should be using a more general normalisation
         * function, then fix the caller.
         *
-        * @param $s string
+        * @param string $s
         *
         * @return string
         */
@@ -2610,7 +2619,7 @@ class Language {
        }
 
        /**
-        * @param $s string
+        * @param string $s
         * @return string
         */
        function checkTitleEncoding( $s ) {
@@ -2647,8 +2656,8 @@ class Language {
         * Some languages such as Chinese require word segmentation,
         * Specify such segmentation when overridden in derived class.
         *
-        * @param $string String
-        * @return String
+        * @param string $string
+        * @return string
         */
        function segmentByWord( $string ) {
                return $string;
@@ -2658,8 +2667,8 @@ class Language {
         * Some languages have special punctuation need to be normalized.
         * Make such changes here.
         *
-        * @param $string String
-        * @return String
+        * @param string $string
+        * @return string
         */
        function normalizeForSearch( $string ) {
                return self::convertDoubleWidth( $string );
@@ -2669,7 +2678,7 @@ class Language {
         * convert double-width roman characters to single-width.
         * range: ff00-ff5f ~= 0020-007f
         *
-        * @param $string string
+        * @param string $string
         *
         * @return string
         */
@@ -2689,8 +2698,8 @@ class Language {
        }
 
        /**
-        * @param $string string
-        * @param $pattern string
+        * @param string $string
+        * @param string $pattern
         * @return string
         */
        protected static function insertSpace( $string, $pattern ) {
@@ -2700,7 +2709,7 @@ class Language {
        }
 
        /**
-        * @param $termsArray array
+        * @param array $termsArray
         * @return array
         */
        function convertForSearchResult( $termsArray ) {
@@ -2712,7 +2721,7 @@ class Language {
        /**
         * Get the first character of a string.
         *
-        * @param $s string
+        * @param string $s
         * @return string
         */
        function firstChar( $s ) {
@@ -2775,7 +2784,7 @@ class Language {
        }
 
        /**
-        * @param $s string
+        * @param string $s
         * @return string
         */
        function recodeForEdit( $s ) {
@@ -2791,7 +2800,7 @@ class Language {
        }
 
        /**
-        * @param $s string
+        * @param string $s
         * @return string
         */
        function recodeInput( $s ) {
@@ -2816,7 +2825,7 @@ class Language {
         *
         * This is language-specific for performance reasons only.
         *
-        * @param $s string
+        * @param string $s
         *
         * @return string
         */
@@ -2839,8 +2848,8 @@ class Language {
         * The data is cached in process memory. This will go faster if you have the
         * FastStringSearch extension.
         *
-        * @param $file string
-        * @param $string string
+        * @param string $file
+        * @param string $string
         *
         * @throws MWException
         * @return string
@@ -2867,7 +2876,7 @@ class Language {
 
        /**
         * Return the correct HTML 'dir' attribute value for this language.
-        * @return String
+        * @return string
         */
        function getDir() {
                return $this->isRTL() ? 'rtl' : 'ltr';
@@ -2879,7 +2888,7 @@ class Language {
         *
         * Should be equivalent to CSS3 'start' text-align value....
         *
-        * @return String
+        * @return string
         */
        function alignStart() {
                return $this->isRTL() ? 'right' : 'left';
@@ -2891,7 +2900,7 @@ class Language {
         *
         * Should be equivalent to CSS3 'end' text-align value....
         *
-        * @return String
+        * @return string
         */
        function alignEnd() {
                return $this->isRTL() ? 'left' : 'right';
@@ -2904,7 +2913,7 @@ class Language {
         * because it makes the output HTML source code more readable. When
         * the output is plain text or can be escaped, getDirMark() should be used.
         *
-        * @param $opposite Boolean Get the direction mark opposite to your language
+        * @param bool $opposite Get the direction mark opposite to your language
         * @return string
         * @since 1.20
         */
@@ -2922,7 +2931,7 @@ class Language {
         * when the output is plain text or can be escaped. When the output is
         * HTML, use getDirMarkEntity() instead.
         *
-        * @param $opposite Boolean Get the direction mark opposite to your language
+        * @param bool $opposite Get the direction mark opposite to your language
         * @return string
         */
        function getDirMark( $opposite = false ) {
@@ -2944,7 +2953,8 @@ class Language {
        /**
         * An arrow, depending on the language direction.
         *
-        * @param $direction String: the direction of the arrow: forwards (default), backwards, left, right, up, down.
+        * @param string $direction The direction of the arrow: forwards (default),
+        *   backwards, left, right, up, down.
         * @return string
         */
        function getArrow( $direction = 'forwards' ) {
@@ -2997,7 +3007,7 @@ class Language {
        /**
         * Fill a MagicWord object with data from here
         *
-        * @param $mw
+        * @param MagicWord $mw
         */
        function getMagic( $mw ) {
                // Saves a function call
@@ -3023,7 +3033,7 @@ class Language {
        /**
         * Add magic words to the extension array
         *
-        * @param $newWords array
+        * @param array $newWords
         */
        function addMagicWordsByLang( $newWords ) {
                $fallbackChain = $this->getFallbackLanguages();
@@ -3055,7 +3065,7 @@ class Language {
        /**
         * Italic is unsuitable for some languages
         *
-        * @param $text String: the text to be emphasized.
+        * @param string $text The text to be emphasized.
         * @return string
         */
        function emphasize( $text ) {
@@ -3075,15 +3085,13 @@ class Language {
         * wfMessage( 'message' )->numParams( $num )->text()
         * </code>
         *
-        * See LanguageGu.php for the Gujarati implementation and
-        * $separatorTransformTable on MessageIs.php for
+        * See $separatorTransformTable on MessageIs.php for
         * the , => . and . => , implementation.
         *
-        * @todo check if it's viable to use localeconv() for the decimal
-        *       separator thing.
-        * @param $number Mixed: the string to be formatted, should be an integer
-        *        or a floating point number.
-        * @param $nocommafy Bool: set to true for special numbers like dates
+        * @todo check if it's viable to use localeconv() for the decimal separator thing.
+        * @param int|float $number The string to be formatted, should be an integer
+        *   or a floating point number.
+        * @param bool $nocommafy Set to true for special numbers like dates
         * @return string
         */
        public function formatNum( $number, $nocommafy = false ) {
@@ -3109,7 +3117,7 @@ class Language {
        /**
         * Front-end for non-commafied formatNum
         *
-        * @param mixed $number the string to be formatted, should be an integer
+        * @param int|float $number The string to be formatted, should be an integer
         *        or a floating point number.
         * @since 1.21
         * @return string
@@ -3119,17 +3127,21 @@ class Language {
        }
 
        /**
-        * @param $number string
+        * @param string $number
         * @return string
         */
-       function parseFormattedNumber( $number ) {
+       public function parseFormattedNumber( $number ) {
                $s = $this->digitTransformTable();
                if ( $s ) {
+                       // eliminate empty array values such as ''. (bug 64347)
+                       $s = array_filter( $s );
                        $number = strtr( $number, array_flip( $s ) );
                }
 
                $s = $this->separatorTransformTable();
                if ( $s ) {
+                       // eliminate empty array values such as ''. (bug 64347)
+                       $s = array_filter( $s );
                        $number = strtr( $number, array_flip( $s ) );
                }
 
@@ -3140,7 +3152,7 @@ class Language {
        /**
         * Adds commas to a given number
         * @since 1.19
-        * @param $number mixed
+        * @param mixed $number
         * @return string
         */
        function commafy( $number ) {
@@ -3193,7 +3205,7 @@ class Language {
        }
 
        /**
-        * @return String
+        * @return string
         */
        function digitGroupingPattern() {
                return self::$dataCache->getItem( $this->mCode, 'digitGroupingPattern' );
@@ -3219,7 +3231,7 @@ class Language {
         * The last two strings are chained with an "and".
         * NOTE: This function will only work with standard numeric array keys (0, 1, 2…)
         *
-        * @param $l Array
+        * @param string[] $l
         * @return string
         */
        function listToText( array $l ) {
@@ -3248,7 +3260,7 @@ class Language {
        /**
         * Take a list of strings and build a locale-friendly comma-separated
         * list, using the local comma-separator message.
-        * @param $list array of strings to put in a comma list
+        * @param string[] $list Array of strings to put in a comma list
         * @return string
         */
        function commaList( array $list ) {
@@ -3261,7 +3273,7 @@ class Language {
        /**
         * Take a list of strings and build a locale-friendly semicolon-separated
         * list, using the local semicolon-separator message.
-        * @param $list array of strings to put in a semicolon list
+        * @param string[] $list Array of strings to put in a semicolon list
         * @return string
         */
        function semicolonList( array $list ) {
@@ -3273,7 +3285,7 @@ class Language {
 
        /**
         * Same as commaList, but separate it with the pipe instead.
-        * @param $list array of strings to put in a pipe list
+        * @param string[] $list Array of strings to put in a pipe list
         * @return string
         */
        function pipeList( array $list ) {
@@ -3293,10 +3305,10 @@ class Language {
         *
         * If $length is negative, the string will be truncated from the beginning
         *
-        * @param $string String to truncate
-        * @param $length Int: maximum length (including ellipses)
-        * @param $ellipsis String to append to the truncated text
-        * @param $adjustLength Boolean: Subtract length of ellipsis from $length.
+        * @param string $string String to truncate
+        * @param int $length Maximum length (including ellipses)
+        * @param string $ellipsis String to append to the truncated text
+        * @param bool $adjustLength Subtract length of ellipsis from $length.
         *      $adjustLength was introduced in 1.18, before that behaved as if false.
         * @return string
         */
@@ -3346,7 +3358,7 @@ class Language {
         * Remove bytes that represent an incomplete Unicode character
         * at the end of string (e.g. bytes of the char are missing)
         *
-        * @param $string String
+        * @param string $string
         * @return string
         */
        protected function removeBadCharLast( $string ) {
@@ -3371,7 +3383,7 @@ class Language {
         * Remove bytes that represent an incomplete Unicode character
         * at the start of string (e.g. bytes of the char are missing)
         *
-        * @param $string String
+        * @param string $string
         * @return string
         */
        protected function removeBadCharFirst( $string ) {
@@ -3511,11 +3523,11 @@ class Language {
         * truncateHtml() helper function
         * like strcspn() but adds the skipped chars to $ret
         *
-        * @param $ret
-        * @param $text
-        * @param $search
-        * @param $start
-        * @param $len
+        * @param string $ret
+        * @param string $text
+        * @param string $search
+        * @param int $start
+        * @param null|int $len
         * @return int
         */
        private function truncate_skip( &$ret, $text, $search, $start, $len = null ) {
@@ -3536,10 +3548,10 @@ class Language {
         * truncateHtml() helper function
         * (a) push or pop $tag from $openTags as needed
         * (b) clear $tag value
-        * @param &$tag string Current HTML tag name we are looking at
-        * @param $tagType int (0-open tag, 1-close tag)
-        * @param $lastCh string Character before the '>' that ended this tag
-        * @param &$openTags array Open tag stack (not accounting for $tag)
+        * @param string &$tag Current HTML tag name we are looking at
+        * @param int $tagType (0-open tag, 1-close tag)
+        * @param string $lastCh Character before the '>' that ended this tag
+        * @param array &$openTags Open tag stack (not accounting for $tag)
         */
        private function truncate_endBracket( &$tag, $tagType, $lastCh, &$openTags ) {
                $tag = ltrim( $tag );
@@ -3559,8 +3571,8 @@ class Language {
         * Grammatical transformations, needed for inflected languages
         * Invoked by putting {{grammar:case|word}} in a message
         *
-        * @param $word string
-        * @param $case string
+        * @param string $word
+        * @param string $case
         * @return string
         */
        function convertGrammar( $word, $case ) {
@@ -3568,23 +3580,27 @@ class Language {
                if ( isset( $wgGrammarForms[$this->getCode()][$case][$word] ) ) {
                        return $wgGrammarForms[$this->getCode()][$case][$word];
                }
+
                return $word;
        }
        /**
         * Get the grammar forms for the content language
-        * @return array of grammar forms
+        * @return array Array of grammar forms
         * @since 1.20
         */
        function getGrammarForms() {
                global $wgGrammarForms;
-               if ( isset( $wgGrammarForms[$this->getCode()] ) && is_array( $wgGrammarForms[$this->getCode()] ) ) {
+               if ( isset( $wgGrammarForms[$this->getCode()] )
+                       && is_array( $wgGrammarForms[$this->getCode()] )
+               ) {
                        return $wgGrammarForms[$this->getCode()];
                }
+
                return array();
        }
        /**
         * Provides an alternative text depending on specified gender.
-        * Usage {{gender:username|masculine|feminine|neutral}}.
+        * Usage {{gender:username|masculine|feminine|unknown}}.
         * username is optional, in which case the gender of current user is used,
         * but only in (some) interface messages; otherwise default gender is used.
         *
@@ -3592,12 +3608,12 @@ class Language {
         * given, it will be returned unconditionally. These details are implied by
         * the caller and cannot be overridden in subclasses.
         *
-        * If more than one form is given, the default is to use the neutral one
-        * if it is specified, and to use the masculine one otherwise. These
-        * details can be overridden in subclasses.
+        * If three forms are given, the default is to use the third (unknown) form.
+        * If fewer than three forms are given, the default is to use the first (masculine) form.
+        * These details can be overridden in subclasses.
         *
-        * @param $gender string
-        * @param $forms array
+        * @param string $gender
+        * @param array $forms
         *
         * @return string
         */
@@ -3626,8 +3642,8 @@ class Language {
         *
         * Example: {{plural:{{NUMBEROFARTICLES}}|article|articles}}
         *
-        * @param $count Integer: non-localized number
-        * @param $forms Array: different plural forms
+        * @param int $count Non-localized number
+        * @param array $forms Different plural forms
         * @return string Correct form of plural for $count in this language
         */
        function convertPlural( $count, $forms ) {
@@ -3677,8 +3693,8 @@ class Language {
         * Checks that convertPlural was given an array and pads it to requested
         * amount of forms by copying the last one.
         *
-        * @param $count Integer: How many forms should there be at least
-        * @param $forms Array of forms given to convertPlural
+        * @param int $count How many forms should there be at least
+        * @param array $forms Array of forms given to convertPlural
         * @return array Padded array of forms or an exception if not an array
         */
        protected function preConvertPlural( /* Array */ $forms, $count ) {
@@ -3695,7 +3711,7 @@ class Language {
         * on old expiry lengths recorded in log entries. You'd need to provide the start date to
         * match up with it.
         *
-        * @param $str String: the validated block duration in English
+        * @param string $str The validated block duration in English
         * @return string Somehow translated block duration
         * @see LanguageFi.php for example implementation
         */
@@ -3740,8 +3756,8 @@ class Language {
         * languages like Chinese need to be segmented in order for the diff
         * to be of any use
         *
-        * @param $text String
-        * @return String
+        * @param string $text
+        * @return string
         */
        public function segmentForDiff( $text ) {
                return $text;
@@ -3750,8 +3766,8 @@ class Language {
        /**
         * and unsegment to show the result
         *
-        * @param $text String
-        * @return String
+        * @param string $text
+        * @return string
         */
        public function unsegmentForDiff( $text ) {
                return $text;
@@ -3770,7 +3786,7 @@ class Language {
        /**
         * convert text to all supported variants
         *
-        * @param $text string
+        * @param string $text
         * @return array
         */
        public function autoConvertToAllVariants( $text ) {
@@ -3780,7 +3796,7 @@ class Language {
        /**
         * convert text to different variants of a language.
         *
-        * @param $text string
+        * @param string $text
         * @return string
         */
        public function convert( $text ) {
@@ -3790,7 +3806,7 @@ class Language {
        /**
         * Convert a Title object to a string in the preferred variant
         *
-        * @param $title Title
+        * @param Title $title
         * @return string
         */
        public function convertTitle( $title ) {
@@ -3800,7 +3816,7 @@ class Language {
        /**
         * Convert a namespace index to a string in the preferred variant
         *
-        * @param $ns int
+        * @param int $ns
         * @return string
         */
        public function convertNamespace( $ns ) {
@@ -3820,7 +3836,7 @@ class Language {
         * Check if the language has the specific variant
         *
         * @since 1.19
-        * @param $variant string
+        * @param string $variant
         * @return bool
         */
        public function hasVariant( $variant ) {
@@ -3830,7 +3846,7 @@ class Language {
        /**
         * Put custom tags (e.g. -{ }-) around math to prevent conversion
         *
-        * @param $text string
+        * @param string $text
         * @return string
         * @deprecated since 1.22 is no longer used
         */
@@ -3840,8 +3856,8 @@ class Language {
 
        /**
         * Perform output conversion on a string, and encode for safe HTML output.
-        * @param $text String text to be converted
-        * @param $isTitle Bool whether this conversion is for the article title
+        * @param string $text Text to be converted
+        * @param bool $isTitle Whether this conversion is for the article title
         * @return string
         * @todo this should get integrated somewhere sane
         */
@@ -3850,7 +3866,7 @@ class Language {
        }
 
        /**
-        * @param $key string
+        * @param string $key
         * @return string
         */
        public function convertCategoryKey( $key ) {
@@ -3894,10 +3910,10 @@ class Language {
         * actually exists in another variant. this function
         * tries to find it. See e.g. LanguageZh.php
         *
-        * @param $link String: the name of the link
-        * @param $nt Mixed: the title object of the link
-        * @param $ignoreOtherCond Boolean: to disable other conditions when
-        *      we need to transclude a template or update a category's link
+        * @param string $link The name of the link
+        * @param Title $nt The title object of the link
+        * @param bool $ignoreOtherCond To disable other conditions when
+        *   we need to transclude a template or update a category's link
         * @return null the input parameters may be modified upon return
         */
        public function findVariantLink( &$link, &$nt, $ignoreOtherCond = false ) {
@@ -3933,9 +3949,9 @@ class Language {
         * This function is called "markNoConversion" for historical
         * reasons.
         *
-        * @param $text String: text to be used for external link
-        * @param $noParse bool: wrap it without confirming it's a real URL first
-        * @return string the tagged text
+        * @param string $text Text to be used for external link
+        * @param bool $noParse Wrap it without confirming it's a real URL first
+        * @return string The tagged text
         */
        public function markNoConversion( $text, $noParse = false ) {
                // Excluding protocal-relative URLs may avoid many false positives.
@@ -4031,7 +4047,7 @@ class Language {
        }
 
        /**
-        * @param $code string
+        * @param string $code
         */
        public function setCode( $code ) {
                $this->mCode = $code;
@@ -4042,17 +4058,14 @@ class Language {
 
        /**
         * Get the name of a file for a certain language code
-        * @param $prefix string Prepend this to the filename
-        * @param $code string Language code
-        * @param $suffix string Append this to the filename
+        * @param string $prefix Prepend this to the filename
+        * @param string $code Language code
+        * @param string $suffix Append this to the filename
         * @throws MWException
         * @return string $prefix . $mangledCode . $suffix
         */
        public static function getFileName( $prefix = 'Language', $code, $suffix = '.php' ) {
-               // Protect against path traversal
-               if ( !Language::isValidCode( $code )
-                       || strcspn( $code, ":/\\\000" ) !== strlen( $code )
-               ) {
+               if ( !self::isValidBuiltInCode( $code ) ) {
                        throw new MWException( "Invalid language code \"$code\"" );
                }
 
@@ -4061,9 +4074,9 @@ class Language {
 
        /**
         * Get the language code from a file name. Inverse of getFileName()
-        * @param $filename string $prefix . $languageCode . $suffix
-        * @param $prefix string Prefix before the language code
-        * @param $suffix string Suffix after the language code
+        * @param string $filename $prefix . $languageCode . $suffix
+        * @param string $prefix Prefix before the language code
+        * @param string $suffix Suffix after the language code
         * @return string Language code, or false if $prefix or $suffix isn't found
         */
        public static function getCodeFromFileName( $filename, $prefix = 'Language', $suffix = '.php' ) {
@@ -4077,7 +4090,7 @@ class Language {
        }
 
        /**
-        * @param $code string
+        * @param string $code
         * @return string
         */
        public static function getMessagesFileName( $code ) {
@@ -4088,7 +4101,22 @@ class Language {
        }
 
        /**
-        * @param $code string
+        * @param string $code
+        * @return string
+        * @since 1.23
+        */
+       public static function getJsonMessagesFileName( $code ) {
+               global $IP;
+
+               if ( !self::isValidBuiltInCode( $code ) ) {
+                       throw new MWException( "Invalid language code \"$code\"" );
+               }
+
+               return "$IP/languages/i18n/$code.json";
+       }
+
+       /**
+        * @param string $code
         * @return string
         */
        public static function getClassFileName( $code ) {
@@ -4099,7 +4127,7 @@ class Language {
        /**
         * Get the first fallback for a given language.
         *
-        * @param $code string
+        * @param string $code
         *
         * @return bool|string
         */
@@ -4117,7 +4145,7 @@ class Language {
         * Get the ordered list of fallback languages.
         *
         * @since 1.19
-        * @param $code string Language code
+        * @param string $code Language code
         * @return array
         */
        public static function getFallbacksFor( $code ) {
@@ -4168,7 +4196,7 @@ class Language {
         * WARNING: this may take a long time. If you just need all message *keys*
         * but need the *contents* of only a few messages, consider using getMessageKeysFor().
         *
-        * @param $code string
+        * @param string $code
         *
         * @return array
         */
@@ -4179,8 +4207,8 @@ class Language {
        /**
         * Get a message for a given language
         *
-        * @param $key string
-        * @param $code string
+        * @param string $key
+        * @param string $code
         *
         * @return string
         */
@@ -4193,7 +4221,7 @@ class Language {
         * array_keys( Language::getMessagesFor( $code ) )
         *
         * @since 1.19
-        * @param $code string Language code
+        * @param string $code Language code
         * @return array of message keys (strings)
         */
        public static function getMessageKeysFor( $code ) {
@@ -4201,7 +4229,7 @@ class Language {
        }
 
        /**
-        * @param $talk
+        * @param string $talk
         * @return mixed
         */
        function fixVariableInNamespace( $talk ) {
@@ -4222,7 +4250,7 @@ class Language {
        }
 
        /**
-        * @param $m string
+        * @param string $m
         * @return string
         */
        function replaceGrammarInNamespace( $m ) {
@@ -4256,10 +4284,10 @@ class Language {
         *
         * @todo FIXME: why are we returnings DBMS-dependent strings???
         *
-        * @param $expiry String: Database expiry String
-        * @param $format Bool|Int true to process using language functions, or TS_ constant
+        * @param string $expiry Database expiry String
+        * @param bool|int $format True to process using language functions, or TS_ constant
         *     to return the expiry in a given timestamp
-        * @return String
+        * @return string
         * @since 1.18
         */
        public function formatExpiry( $expiry, $format = true ) {
@@ -4281,12 +4309,14 @@ class Language {
 
        /**
         * @todo Document
-        * @param $seconds int|float
-        * @param $format Array Optional
-        *              If $format['avoid'] == 'avoidseconds' - don't mention seconds if $seconds >= 1 hour
-        *              If $format['avoid'] == 'avoidminutes' - don't mention seconds/minutes if $seconds > 48 hours
-        *              If $format['noabbrevs'] is true - use 'seconds' and friends instead of 'seconds-abbrev' and friends
-        *              For backwards compatibility, $format may also be one of the strings 'avoidseconds' or 'avoidminutes'
+        * @param int|float $seconds
+        * @param array $format Optional
+        *   If $format['avoid'] === 'avoidseconds': don't mention seconds if $seconds >= 1 hour.
+        *   If $format['avoid'] === 'avoidminutes': don't mention seconds/minutes if $seconds > 48 hours.
+        *   If $format['noabbrevs'] is true: use 'seconds' and friends instead of 'seconds-abbrev'
+        *     and friends.
+        *   For backwards compatibility, $format may also be one of the strings 'avoidseconds'
+        *     or 'avoidminutes'.
         * @return string
         */
        function formatTimePeriod( $seconds, $format = array() ) {
@@ -4380,12 +4410,13 @@ class Language {
 
        /**
         * Format a bitrate for output, using an appropriate
-        * unit (bps, kbps, Mbps, Gbps, Tbps, Pbps, Ebps, Zbps or Ybps) according to the magnitude in question
+        * unit (bps, kbps, Mbps, Gbps, Tbps, Pbps, Ebps, Zbps or Ybps) according to
+        *   the magnitude in question.
         *
         * This use base 1000. For base 1024 use formatSize(), for another base
-        * see formatComputingNumbers()
+        * see formatComputingNumbers().
         *
-        * @param $bps int
+        * @param int $bps
         * @return string
         */
        function formatBitrate( $bps ) {
@@ -4393,9 +4424,9 @@ class Language {
        }
 
        /**
-        * @param $size int Size of the unit
-        * @param $boundary int Size boundary (1000, or 1024 in most cases)
-        * @param $messageKey string Message key to be uesd
+        * @param int $size Size of the unit
+        * @param int $boundary Size boundary (1000, or 1024 in most cases)
+        * @param string $messageKey Message key to be uesd
         * @return string
         */
        function formatComputingNumbers( $size, $boundary, $messageKey ) {
@@ -4433,7 +4464,7 @@ class Language {
         * This method use base 1024. For base 1000 use formatBitrate(), for
         * another base see formatComputingNumbers()
         *
-        * @param $size int Size to format
+        * @param int $size Size to format
         * @return string Plain text (not HTML)
         */
        function formatSize( $size ) {
@@ -4443,11 +4474,11 @@ class Language {
        /**
         * Make a list item, used by various special pages
         *
-        * @param $page String Page link
-        * @param $details String Text between brackets
-        * @param $oppositedm Boolean Add the direction mark opposite to your
-        *                    language, to display text properly
-        * @return String
+        * @param string $page Page link
+        * @param string $details Text between brackets
+        * @param bool $oppositedm Add the direction mark opposite to your
+        *   language, to display text properly
+        * @return string
         */
        function specialList( $page, $details, $oppositedm = true ) {
                $dirmark = ( $oppositedm ? $this->getDirMark( true ) : '' ) .
@@ -4460,14 +4491,16 @@ class Language {
        /**
         * Generate (prev x| next x) (20|50|100...) type links for paging
         *
-        * @param $title Title object to link
-        * @param $offset Integer offset parameter
-        * @param $limit Integer limit parameter
-        * @param $query array|String optional URL query parameter string
-        * @param $atend Bool optional param for specified if this is the last page
-        * @return String
+        * @param Title $title Title object to link
+        * @param int $offset
+        * @param int $limit
+        * @param array|string $query Optional URL query parameter string
+        * @param bool $atend Optional param for specified if this is the last page
+        * @return string
         */
-       public function viewPrevNext( Title $title, $offset, $limit, array $query = array(), $atend = false ) {
+       public function viewPrevNext( Title $title, $offset, $limit,
+               array $query = array(), $atend = false
+       ) {
                // @todo FIXME: Why on earth this needs one message for the text and another one for tooltip?
 
                # Make 'previous' link
@@ -4502,18 +4535,22 @@ class Language {
        /**
         * Helper function for viewPrevNext() that generates links
         *
-        * @param $title Title object to link
-        * @param $offset Integer offset parameter
-        * @param $limit Integer limit parameter
-        * @param $query Array extra query parameters
-        * @param $link String text to use for the link; will be escaped
-        * @param $tooltipMsg String name of the message to use as tooltip
-        * @param $class String value of the "class" attribute of the link
-        * @return String HTML fragment
-        */
-       private function numLink( Title $title, $offset, $limit, array $query, $link, $tooltipMsg, $class ) {
+        * @param Title $title Title object to link
+        * @param int $offset
+        * @param int $limit
+        * @param array $query Extra query parameters
+        * @param string $link Text to use for the link; will be escaped
+        * @param string $tooltipMsg Name of the message to use as tooltip
+        * @param string $class Value of the "class" attribute of the link
+        * @return string HTML fragment
+        */
+       private function numLink( Title $title, $offset, $limit, array $query, $link,
+               $tooltipMsg, $class
+       ) {
                $query = array( 'limit' => $limit, 'offset' => $offset ) + $query;
-               $tooltip = wfMessage( $tooltipMsg )->inLanguage( $this )->title( $title )->numParams( $limit )->text();
+               $tooltip = wfMessage( $tooltipMsg )->inLanguage( $this )->title( $title )
+                       ->numParams( $limit )->text();
+
                return Html::element( 'a', array( 'href' => $title->getLocalURL( $query ),
                        'title' => $tooltip, 'class' => $class ), $link );
        }