X-Git-Url: http://git.cyclocoop.org/?a=blobdiff_plain;f=languages%2FLanguage.php;h=463ea19062c43a0c291e8ddd024a52c2ce4cef7b;hb=ddcf8cc660c810f1601b61c90aa33cb36ca60cad;hp=7d4041a8f66caea6da209b7687e3ee0e926c919b;hpb=6e2acfd778db237e2d36f654ffeae55ae2b32922;p=lhc%2Fweb%2Fwiklou.git diff --git a/languages/Language.php b/languages/Language.php index 7d4041a8f6..463ea19062 100644 --- a/languages/Language.php +++ b/languages/Language.php @@ -29,10 +29,15 @@ if ( function_exists( 'mb_strtoupper' ) ) { * @ingroup Language */ class FakeConverter { + + /** + * @var Language + */ var $mLang; function __construct( $langobj ) { $this->mLang = $langobj; } 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 getVariants() { return array( $this->mLang->getCode() ); } function getPreferredVariant() { return $this->mLang->getCode(); } @@ -63,10 +68,11 @@ class Language { var $mMagicExtensions = array(), $mMagicHookDone = false; private $mHtmlCode = null; - var $mNamespaceIds, $namespaceNames, $namespaceAliases; var $dateFormatStrings = array(); var $mExtendedSpecialPageAliases; + protected $namespaceNames, $mNamespaceIds, $namespaceAliases; + /** * ReplacementArray object caches */ @@ -152,6 +158,7 @@ class Language { /** * Create a language object for a given language code * @param $code String + * @throws MWException * @return Language */ protected static function newFromCode( $code ) { @@ -329,7 +336,7 @@ class Language { /** * @return array */ - function getNamespaces() { + public function getNamespaces() { if ( is_null( $this->namespaceNames ) ) { global $wgMetaNamespace, $wgMetaNamespaceTalk, $wgExtraNamespaces; @@ -363,6 +370,14 @@ class Language { return $this->namespaceNames; } + /** + * Arbitrarily set all of the namespace names at once. Mainly used for testing + * @param $namespaces Array of namespaces (id => name) + */ + public function setNamespaces( array $namespaces ) { + $this->namespaceNames = $namespaces; + } + /** * A convenience function that returns the same thing as * getNamespaces() except with the array values changed to ' ' @@ -541,11 +556,10 @@ class Language { */ function getVariantname( $code, $usemsg = true ) { $msg = "variantname-$code"; - list( $rootCode ) = explode( '-', $code ); if ( $usemsg && wfMessage( $msg )->exists() ) { return $this->getMessageFromDB( $msg ); } - $name = self::getLanguageName( $code ); + $name = self::fetchLanguageName( $code ); if ( $name ) { return $name; # if it's defined as a language name, show that } else { @@ -638,36 +652,17 @@ class Language { } /** - * Get language names, indexed by code. + * Get native language names, indexed by code. + * Only those defined in MediaWiki, no other data like CLDR. * If $customisedOnly is true, only returns codes with a messages file * * @param $customisedOnly bool * * @return array + * @deprecated in 1.20, use fetchLanguageNames() */ public static function getLanguageNames( $customisedOnly = false ) { - global $wgExtraLanguageNames; - static $coreLanguageNames; - - if ( $coreLanguageNames === null ) { - include( MWInit::compiledPath( 'languages/Names.php' ) ); - } - - $allNames = $wgExtraLanguageNames + $coreLanguageNames; - if ( !$customisedOnly ) { - return $allNames; - } - - global $IP; - $names = array(); - // We do this using a foreach over the codes instead of a directory - // loop so that messages files in extensions will work correctly. - foreach ( $allNames as $code => $value ) { - if ( is_readable( self::getMessagesFileName( $code ) ) ) { - $names[$code] = $allNames[$code]; - } - } - return $names; + return self::fetchLanguageNames( null, $customisedOnly ? 'mwfile' : 'mw' ); } /** @@ -677,16 +672,84 @@ class Language { * @param $code String Language code. * @return Array language code => language name * @since 1.18.0 + * @deprecated in 1.20, use fetchLanguageNames() */ public static function getTranslatedLanguageNames( $code ) { + return self::fetchLanguageNames( $code, 'all' ); + } + + /** + * Get an array of language names, indexed by code. + * @param $inLanguage null|string: Code of language in which to return the names + * Use null for autonyms (native names) + * @param $include string: + * 'all' all available languages + * 'mw' only if the language is defined in MediaWiki or wgExtraLanguageNames + * 'mwfile' only if the language is in 'mw' *and* has a message file + * @return array|bool: language code => language name, false if $include is wrong + * @since 1.20 + */ + public static function fetchLanguageNames( $inLanguage = null, $include = 'mw' ) { + global $wgExtraLanguageNames; + static $coreLanguageNames; + + if ( $coreLanguageNames === null ) { + include( MWInit::compiledPath( 'languages/Names.php' ) ); + } + $names = array(); - wfRunHooks( 'LanguageGetTranslatedLanguageNames', array( &$names, $code ) ); - foreach ( self::getLanguageNames() as $code => $name ) { - if ( !isset( $names[$code] ) ) $names[$code] = $name; + if( $inLanguage ) { + # TODO: also include when $inLanguage is null, when this code is more efficient + wfRunHooks( 'LanguageGetTranslatedLanguageNames', array( &$names, $inLanguage ) ); + } + + $mwNames = $wgExtraLanguageNames + $coreLanguageNames; + foreach ( $mwNames as $mwCode => $mwName ) { + # - Prefer own MediaWiki native name when not using the hook + # TODO: prefer it always to make it consistent, but casing is different in CLDR + # - For other names just add if not added through the hook + if ( ( $mwCode === $inLanguage && !$inLanguage ) || !isset( $names[$mwCode] ) ) { + $names[$mwCode] = $mwName; + } + } + + if ( $include === 'all' ) { + return $names; + } + + $returnMw = array(); + $coreCodes = array_keys( $mwNames ); + foreach( $coreCodes as $coreCode ) { + $returnMw[$coreCode] = $names[$coreCode]; + } + + if( $include === 'mw' ) { + return $returnMw; + } elseif( $include === 'mwfile' ) { + $namesMwFile = array(); + # 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 ) ) ) { + $namesMwFile[$code] = $names[$code]; + } + } + return $namesMwFile; } + return false; + } - return $names; + /** + * @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 + * @since 1.20 + */ + public static function fetchLanguageName( $code, $inLanguage = null, $include = 'all' ) { + $array = self::fetchLanguageNames( $inLanguage, $include ); + return !array_key_exists( $code, $array ) ? '' : $array[$code]; } /** @@ -700,15 +763,14 @@ class Language { } /** + * Get the native language name of $code. + * Only if defined in MediaWiki, no other data like CLDR. * @param $code string * @return string + * @deprecated in 1.20, use fetchLanguageName() */ function getLanguageName( $code ) { - $names = self::getLanguageNames(); - if ( !array_key_exists( $code, $names ) ) { - return ''; - } - return $names[$code]; + return self::fetchLanguageName( $code ); } /** @@ -824,6 +886,7 @@ class Language { * xij j (day number) in Iranian calendar * xiF F (month name) in Iranian calendar * xin n (month number) in Iranian calendar + * xiy y (two digit year) in Iranian calendar * xiY Y (full year) in Iranian calendar * * xjj j (day number) in Hebrew calendar @@ -1042,7 +1105,7 @@ class Language { if ( !$unix ) { $unix = wfTimestamp( TS_UNIX, $ts ); } - $num = date( 'o', $unix ); + $num = gmdate( 'o', $unix ); break; case 'Y': $num = substr( $ts, 0, 4 ); @@ -1086,6 +1149,12 @@ class Language { case 'y': $num = substr( $ts, 2, 2 ); break; + case 'xiy': + if ( !$iranian ) { + $iranian = self::tsToIranian( $ts ); + } + $num = substr( $iranian[0], -2 ); + break; case 'a': $s .= intval( substr( $ts, 8, 2 ) ) < 12 ? 'am' : 'pm'; break; @@ -1242,7 +1311,7 @@ class Language { * * Based on a PHP-Nuke block by Sharjeel which is released under GNU/GPL license * - * @link http://phpnuke.org/modules.php?name=News&file=article&sid=8234&mode=thread&order=0&thold=0 + * @see http://phpnuke.org/modules.php?name=News&file=article&sid=8234&mode=thread&order=0&thold=0 * * @param $ts string * @@ -1651,7 +1720,7 @@ class Language { * get user timecorrection setting) * @return int */ - function userAdjust( $ts, $tz = false ) { + function userAdjust( $ts, $tz = false ) { global $wgUser, $wgLocalTZoffset; if ( $tz === false ) { @@ -1855,6 +1924,7 @@ class Language { * - true: use user's preference * - false: use default preference * - string: format to use + * @since 1.19 * @return String */ private function internalUserTimeAndDate( $type, $ts, User $user, array $options ) { @@ -1893,6 +1963,7 @@ class Language { * - true: use user's preference * - false: use default preference * - string: format to use + * @since 1.19 * @return String */ public function userDate( $ts, User $user, array $options = array() ) { @@ -1915,6 +1986,7 @@ class Language { * - true: use user's preference * - false: use default preference * - string: format to use + * @since 1.19 * @return String */ public function userTime( $ts, User $user, array $options = array() ) { @@ -1937,6 +2009,7 @@ class Language { * - true: use user's preference * - false: use default preference * - string: format to use + * @since 1.19 * @return String */ public function userTimeAndDate( $ts, User $user, array $options = array() ) { @@ -2467,6 +2540,7 @@ class Language { * @param $file string * @param $string string * + * @throws MWException * @return string */ function transformUsingPairFile( $file, $string ) { @@ -2522,16 +2596,35 @@ class Language { } /** - * A hidden direction mark (LRM or RLM), depending on the language direction + * A hidden direction mark (LRM or RLM), depending on the language direction. + * Unlike getDirMark(), this function returns the character as an HTML entity. + * This function should be used when the output is guaranteed to be HTML, + * 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 + * @return string + */ + function getDirMarkEntity( $opposite = false ) { + if ( $opposite ) { return $this->isRTL() ? '‎' : '‏'; } + return $this->isRTL() ? '‏' : '‎'; + } + + /** + * A hidden direction mark (LRM or RLM), depending on the language direction. + * This function produces them as invisible Unicode characters and + * the output may be hard to read and debug, so it should only be used + * 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 * @return string */ function getDirMark( $opposite = false ) { - $rtl = "\xE2\x80\x8F"; - $ltr = "\xE2\x80\x8E"; - if ( $opposite ) { return $this->isRTL() ? $ltr : $rtl; } - return $this->isRTL() ? $rtl : $ltr; + $lrm = "\xE2\x80\x8E"; # LEFT-TO-RIGHT MARK, commonly abbreviated LRM + $rlm = "\xE2\x80\x8F"; # RIGHT-TO-LEFT MARK, commonly abbreviated RLM + if ( $opposite ) { return $this->isRTL() ? $lrm : $rlm; } + return $this->isRTL() ? $rlm : $lrm; } /** @@ -2716,6 +2809,9 @@ class Language { */ function commafy( $_ ) { $digitGroupingPattern = $this->digitGroupingPattern(); + if ( $_ === null ) { + return ''; + } if ( !$digitGroupingPattern || $digitGroupingPattern === "###,###,###" ) { // default grouping is at thousands, use the same for ###,###,### pattern too. @@ -2726,7 +2822,7 @@ class Language { if ( intval( $_ ) < 0 ) { // For negative numbers apply the algorithm like positive number and add sign. $sign = "-"; - $_ = substr( $_,1 ); + $_ = substr( $_, 1 ); } $numberpart = array(); $decimalpart = array(); @@ -3105,7 +3201,7 @@ class Language { * (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 char|string Character before the '>' that ended this tag + * @param $lastCh string Character before the '>' that ended this tag * @param &$openTags array Open tag stack (not accounting for $tag) */ private function truncate_endBracket( &$tag, $tagType, $lastCh, &$openTags ) { @@ -3137,14 +3233,31 @@ class Language { } return $word; } - + /** + * Get the grammar forms for the content language + * @return array of grammar forms + * @since 1.20 + */ + function getGrammarForms() { + global $wgGrammarForms; + 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}}. * username is optional, in which case the gender of current user is used, * but only in (some) interface messages; otherwise default gender is used. - * If second or third parameter are not specified, masculine is used. - * These details may be overriden per language. + * + * If no forms are given, an empty string is returned. If only one form is + * 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. * * @param $gender string * @param $forms array @@ -3212,7 +3325,7 @@ class Language { * match up with it. * * @param $str String: the validated block duration in English - * @return Somehow translated block duration + * @return string Somehow translated block duration * @see LanguageFi.php for example implementation */ function translateBlockExpiry( $str ) { @@ -3261,6 +3374,8 @@ class Language { /** * Return the LanguageConverter used in the Language + * + * @since 1.19 * @return LanguageConverter */ public function getConverter() { @@ -3308,6 +3423,8 @@ class Language { /** * Check if the language has the specific variant + * + * @since 1.19 * @param $variant string * @return bool */ @@ -3484,7 +3601,7 @@ class Language { public function setCode( $code ) { $this->mCode = $code; // Ensure we don't leave an incorrect html code lying around - unset( $this->mHtmlCode ); + $this->mHtmlCode = null; } /** @@ -3492,6 +3609,7 @@ class Language { * @param $prefix string Prepend this to the filename * @param $code string Language code * @param $suffix string Append this to the filename + * @throws MWException * @return string $prefix . $mangledCode . $suffix */ public static function getFileName( $prefix = 'Language', $code, $suffix = '.php' ) { @@ -3547,7 +3665,7 @@ class Language { * * @param $code string * - * @return false|string + * @return bool|string */ public static function getFallbackFor( $code ) { if ( $code === 'en' || !Language::isValidBuiltInCode( $code ) ) { @@ -3670,6 +3788,8 @@ class Language { /** * Decode an expiry (block, protection, etc) which has come from the DB * + * @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 * to return the expiry in a given timestamp @@ -3793,57 +3913,67 @@ 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 + * + * This use base 1000. For base 1024 use formatSize(), for another base + * see formatComputingNumbers() + * * @param $bps int * @return string */ function formatBitrate( $bps ) { - $units = array( 'bps', 'kbps', 'Mbps', 'Gbps' ); - if ( $bps <= 0 ) { - return $this->formatNum( $bps ) . $units[0]; - } - $unitIndex = (int)floor( log10( $bps ) / 3 ); - $mantissa = $bps / pow( 1000, $unitIndex ); - if ( $mantissa < 10 ) { - $mantissa = round( $mantissa, 1 ); - } else { - $mantissa = round( $mantissa ); - } - return $this->formatNum( $mantissa ) . $units[$unitIndex]; + return $this->formatComputingNumbers( $bps, 1000, "bitrate-$1bits" ); } /** - * Format a size in bytes for output, using an appropriate - * unit (B, KB, MB or GB) according to the magnitude in question - * - * @param $size int Size to format - * @return string Plain text (not HTML) + * @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 + * @return string */ - function formatSize( $size ) { + function formatComputingNumbers( $size, $boundary, $messageKey ) { + if ( $size <= 0 ) { + return str_replace( '$1', $this->formatNum( $size ), + $this->getMessageFromDB( str_replace( '$1', '', $messageKey ) ) + ); + } + $sizes = array( '', 'kilo', 'mega', 'giga', 'tera', 'peta', 'exa', 'zeta', 'yotta' ); + $index = 0; + + $maxIndex = count( $sizes ) - 1; + while ( $size >= $boundary && $index < $maxIndex ) { + $index++; + $size /= $boundary; + } + // For small sizes no decimal places necessary $round = 0; - if ( $size > 1024 ) { - $size = $size / 1024; - if ( $size > 1024 ) { - $size = $size / 1024; - // For MB and bigger two decimal places are smarter - $round = 2; - if ( $size > 1024 ) { - $size = $size / 1024; - $msg = 'size-gigabytes'; - } else { - $msg = 'size-megabytes'; - } - } else { - $msg = 'size-kilobytes'; - } - } else { - $msg = 'size-bytes'; + if ( $index > 1 ) { + // For MB and bigger two decimal places are smarter + $round = 2; } + $msg = str_replace( '$1', $sizes[$index], $messageKey ); + $size = round( $size, $round ); $text = $this->getMessageFromDB( $msg ); return str_replace( '$1', $this->formatNum( $size ), $text ); } + /** + * Format a size in bytes for output, using an appropriate + * unit (B, KB, MB, GB, TB, PB, EB, ZB or YB) according to the magnitude in question + * + * This method use base 1024. For base 1000 use formatBitrate(), for + * another base see formatComputingNumbers() + * + * @param $size int Size to format + * @return string Plain text (not HTML) + */ + function formatSize( $size ) { + return $this->formatComputingNumbers( $size, 1024, "size-$1bytes" ); + } + /** * Make a list item, used by various special pages * @@ -3876,7 +4006,7 @@ class Language { # Make 'previous' link $prev = wfMessage( 'prevn' )->inLanguage( $this )->title( $title )->numParams( $limit )->text(); - if( $offset > 0 ) { + if ( $offset > 0 ) { $plink = $this->numLink( $title, max( $offset - $limit, 0 ), $limit, $query, $prev, 'prevn-title', 'mw-prevlink' ); } else { @@ -3885,7 +4015,7 @@ class Language { # Make 'next' link $next = wfMessage( 'nextn' )->inLanguage( $this )->title( $title )->numParams( $limit )->text(); - if( $atend ) { + if ( $atend ) { $nlink = htmlspecialchars( $next ); } else { $nlink = $this->numLink( $title, $offset + $limit, $limit, @@ -3894,7 +4024,7 @@ class Language { # Make links to set number of items per page $numLinks = array(); - foreach( array( 20, 50, 100, 250, 500 ) as $num ) { + foreach ( array( 20, 50, 100, 250, 500 ) as $num ) { $numLinks[] = $this->numLink( $title, $offset, $num, $query, $this->formatNum( $num ), 'shown-title', 'mw-numlink' ); }