6 if( !defined( 'MEDIAWIKI' ) ) {
7 echo "This file is part of MediaWiki, it is not a valid entry point.\n";
12 # In general you should not make customizations in these language files
13 # directly, but should use the MediaWiki: special namespace to customize
14 # user interface messages through the wiki.
15 # See http://meta.wikipedia.org/wiki/MediaWiki_namespace
17 # NOTE TO TRANSLATORS: Do not copy this whole file when making translations!
18 # A lot of common constants and a base class with inheritable methods are
19 # defined here, which should not be redefined. See the other LanguageXx.php
24 global $wgLanguageNames;
25 require_once( dirname(__FILE__
) . '/Names.php' ) ;
27 global $wgInputEncoding, $wgOutputEncoding;
30 * These are always UTF-8, they exist only for backwards compatibility
32 $wgInputEncoding = "UTF-8";
33 $wgOutputEncoding = "UTF-8";
35 if( function_exists( 'mb_strtoupper' ) ) {
36 mb_internal_encoding('UTF-8');
39 /* a fake language converter */
42 function FakeConverter($langobj) {$this->mLang
= $langobj;}
43 function convert($t, $i) {return $t;}
44 function parserConvert($t, $p) {return $t;}
45 function getVariants() { return array( $this->mLang
->getCode() ); }
46 function getPreferredVariant() {return $this->mLang
->getCode(); }
47 function findVariantLink(&$l, &$n) {}
48 function getExtraHashOptions() {return '';}
49 function getParsedTitle() {return '';}
50 function markNoConversion($text, $noParse=false) {return $text;}
51 function convertCategoryKey( $key ) {return $key; }
52 function convertLinkToAllVariants($text){ return array( $this->mLang
->getCode() => $text); }
53 function armourMath($text){ return $text; }
56 #--------------------------------------------------------------------------
57 # Internationalisation code
58 #--------------------------------------------------------------------------
61 var $mConverter, $mVariants, $mCode, $mLoaded = false;
62 var $mMagicExtensions = array(), $mMagicHookDone = false;
64 static public $mLocalisationKeys = array( 'fallback', 'namespaceNames',
65 'skinNames', 'mathNames',
66 'bookstoreList', 'magicWords', 'messages', 'rtl', 'digitTransformTable',
67 'separatorTransformTable', 'fallback8bitEncoding', 'linkPrefixExtension',
68 'defaultUserOptionOverrides', 'linkTrail', 'namespaceAliases',
69 'dateFormats', 'datePreferences', 'datePreferenceMigrationMap',
70 'defaultDateFormat', 'extraUserToggles', 'specialPageAliases' );
72 static public $mMergeableMapKeys = array( 'messages', 'namespaceNames', 'mathNames',
73 'dateFormats', 'defaultUserOptionOverrides', 'magicWords' );
75 static public $mMergeableListKeys = array( 'extraUserToggles' );
77 static public $mMergeableAliasListKeys = array( 'specialPageAliases' );
79 static public $mLocalisationCache = array();
81 static public $mWeekdayMsgs = array(
82 'sunday', 'monday', 'tuesday', 'wednesday', 'thursday',
86 static public $mWeekdayAbbrevMsgs = array(
87 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'
90 static public $mMonthMsgs = array(
91 'january', 'february', 'march', 'april', 'may_long', 'june',
92 'july', 'august', 'september', 'october', 'november',
95 static public $mMonthGenMsgs = array(
96 'january-gen', 'february-gen', 'march-gen', 'april-gen', 'may-gen', 'june-gen',
97 'july-gen', 'august-gen', 'september-gen', 'october-gen', 'november-gen',
100 static public $mMonthAbbrevMsgs = array(
101 'jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug',
102 'sep', 'oct', 'nov', 'dec'
106 * Create a language object for a given language code
108 static function factory( $code ) {
110 static $recursionLevel = 0;
112 if ( $code == 'en' ) {
115 $class = 'Language' . str_replace( '-', '_', ucfirst( $code ) );
116 // Preload base classes to work around APC/PHP5 bug
117 if ( file_exists( "$IP/languages/classes/$class.deps.php" ) ) {
118 include_once("$IP/languages/classes/$class.deps.php");
120 if ( file_exists( "$IP/languages/classes/$class.php" ) ) {
121 include_once("$IP/languages/classes/$class.php");
125 if ( $recursionLevel > 5 ) {
126 throw new MWException( "Language fallback loop detected when creating class $class\n" );
129 if( ! class_exists( $class ) ) {
130 $fallback = Language
::getFallbackFor( $code );
132 $lang = Language
::factory( $fallback );
134 $lang->setCode( $code );
142 function __construct() {
143 $this->mConverter
= new FakeConverter($this);
144 // Set the code to the name of the descendant
145 if ( get_class( $this ) == 'Language' ) {
148 $this->mCode
= str_replace( '_', '-', strtolower( substr( get_class( $this ), 8 ) ) );
153 * Hook which will be called if this is the content language.
154 * Descendants can use this to register hook functions or modify globals
156 function initContLang() {}
162 function getDefaultUserOptions() {
163 return User
::getDefaultOptions();
166 function getFallbackLanguageCode() {
168 return $this->fallback
;
172 * Exports $wgBookstoreListEn
175 function getBookstoreList() {
177 return $this->bookstoreList
;
183 function getNamespaces() {
185 return $this->namespaceNames
;
189 * A convenience function that returns the same thing as
190 * getNamespaces() except with the array values changed to ' '
191 * where it found '_', useful for producing output to be displayed
192 * e.g. in <select> forms.
196 function getFormattedNamespaces() {
197 $ns = $this->getNamespaces();
198 foreach($ns as $k => $v) {
199 $ns[$k] = strtr($v, '_', ' ');
205 * Get a namespace value by key
207 * $mw_ns = $wgContLang->getNsText( NS_MEDIAWIKI );
208 * echo $mw_ns; // prints 'MediaWiki'
211 * @param int $index the array key of the namespace to return
212 * @return mixed, string if the namespace value exists, otherwise false
214 function getNsText( $index ) {
215 $ns = $this->getNamespaces();
216 return isset( $ns[$index] ) ?
$ns[$index] : false;
220 * A convenience function that returns the same thing as
221 * getNsText() except with '_' changed to ' ', useful for
226 function getFormattedNsText( $index ) {
227 $ns = $this->getNsText( $index );
228 return strtr($ns, '_', ' ');
232 * Get a namespace key by value, case insensitive.
233 * Only matches namespace names for the current language, not the
234 * canonical ones defined in Namespace.php.
236 * @param string $text
237 * @return mixed An integer if $text is a valid value otherwise false
239 function getLocalNsIndex( $text ) {
241 $lctext = $this->lc($text);
242 return isset( $this->mNamespaceIds
[$lctext] ) ?
$this->mNamespaceIds
[$lctext] : false;
246 * Get a namespace key by value, case insensitive. Canonical namespace
247 * names override custom ones defined for the current language.
249 * @param string $text
250 * @return mixed An integer if $text is a valid value otherwise false
252 function getNsIndex( $text ) {
254 $lctext = $this->lc($text);
255 if( ( $ns = Namespace::getCanonicalIndex( $lctext ) ) !== null ) return $ns;
256 return isset( $this->mNamespaceIds
[$lctext] ) ?
$this->mNamespaceIds
[$lctext] : false;
260 * short names for language variants used for language conversion links.
262 * @param string $code
265 function getVariantname( $code ) {
266 return $this->getMessageFromDB( "variantname-$code" );
269 function specialPage( $name ) {
270 $aliases = $this->getSpecialPageAliases();
271 if ( isset( $aliases[$name][0] ) ) {
272 $name = $aliases[$name][0];
274 return $this->getNsText(NS_SPECIAL
) . ':' . $name;
277 function getQuickbarSettings() {
279 $this->getMessage( 'qbsettings-none' ),
280 $this->getMessage( 'qbsettings-fixedleft' ),
281 $this->getMessage( 'qbsettings-fixedright' ),
282 $this->getMessage( 'qbsettings-floatingleft' ),
283 $this->getMessage( 'qbsettings-floatingright' )
287 function getSkinNames() {
289 return $this->skinNames
;
292 function getMathNames() {
294 return $this->mathNames
;
297 function getDatePreferences() {
299 return $this->datePreferences
;
302 function getDateFormats() {
304 return $this->dateFormats
;
307 function getDefaultDateFormat() {
309 return $this->defaultDateFormat
;
312 function getDatePreferenceMigrationMap() {
314 return $this->datePreferenceMigrationMap
;
317 function getDefaultUserOptionOverrides() {
319 return $this->defaultUserOptionOverrides
;
322 function getExtraUserToggles() {
324 return $this->extraUserToggles
;
327 function getUserToggle( $tog ) {
328 return $this->getMessageFromDB( "tog-$tog" );
332 * Get language names, indexed by code.
333 * If $customisedOnly is true, only returns codes with a messages file
335 public static function getLanguageNames( $customisedOnly = false ) {
336 global $wgLanguageNames;
337 if ( !$customisedOnly ) {
338 return $wgLanguageNames;
343 $dir = opendir( "$IP/languages/messages" );
344 while( false !== ( $file = readdir( $dir ) ) ) {
346 if( preg_match( '/Messages([A-Z][a-z_]+)\.php$/', $file, $m ) ) {
347 $code = str_replace( '_', '-', strtolower( $m[1] ) );
348 if ( isset( $wgLanguageNames[$code] ) ) {
349 $names[$code] = $wgLanguageNames[$code];
358 * Ugly hack to get a message maybe from the MediaWiki namespace, if this
359 * language object is the content or user language.
361 function getMessageFromDB( $msg ) {
362 global $wgContLang, $wgLang;
363 if ( $wgContLang->getCode() == $this->getCode() ) {
365 return wfMsgForContent( $msg );
366 } elseif ( $wgLang->getCode() == $this->getCode() ) {
368 return wfMsg( $msg );
370 # Neither, get from localisation
371 return $this->getMessage( $msg );
375 function getLanguageName( $code ) {
376 global $wgLanguageNames;
377 if ( ! array_key_exists( $code, $wgLanguageNames ) ) {
380 return $wgLanguageNames[$code];
383 function getMonthName( $key ) {
384 return $this->getMessageFromDB( self
::$mMonthMsgs[$key-1] );
387 function getMonthNameGen( $key ) {
388 return $this->getMessageFromDB( self
::$mMonthGenMsgs[$key-1] );
391 function getMonthAbbreviation( $key ) {
392 return $this->getMessageFromDB( self
::$mMonthAbbrevMsgs[$key-1] );
395 function getWeekdayName( $key ) {
396 return $this->getMessageFromDB( self
::$mWeekdayMsgs[$key-1] );
399 function getWeekdayAbbreviation( $key ) {
400 return $this->getMessageFromDB( self
::$mWeekdayAbbrevMsgs[$key-1] );
404 * Used by date() and time() to adjust the time output.
406 * @param int $ts the time in date('YmdHis') format
407 * @param mixed $tz adjust the time by this amount (default false,
408 * mean we get user timecorrection setting)
411 function userAdjust( $ts, $tz = false ) {
412 global $wgUser, $wgLocalTZoffset;
415 $tz = $wgUser->getOption( 'timecorrection' );
418 # minutes and hours differences:
423 # Global offset in minutes.
424 if( isset($wgLocalTZoffset) ) {
425 if( $wgLocalTZoffset >= 0 ) {
426 $hrDiff = floor($wgLocalTZoffset / 60);
428 $hrDiff = ceil($wgLocalTZoffset / 60);
430 $minDiff = $wgLocalTZoffset %
60;
432 } elseif ( strpos( $tz, ':' ) !== false ) {
433 $tzArray = explode( ':', $tz );
434 $hrDiff = intval($tzArray[0]);
435 $minDiff = intval($hrDiff < 0 ?
-$tzArray[1] : $tzArray[1]);
437 $hrDiff = intval( $tz );
440 # No difference ? Return time unchanged
441 if ( 0 == $hrDiff && 0 == $minDiff ) { return $ts; }
443 wfSuppressWarnings(); // E_STRICT system time bitching
444 # Generate an adjusted date
446 (int)substr( $ts, 8, 2) ) +
$hrDiff, # Hours
447 (int)substr( $ts, 10, 2 ) +
$minDiff, # Minutes
448 (int)substr( $ts, 12, 2 ), # Seconds
449 (int)substr( $ts, 4, 2 ), # Month
450 (int)substr( $ts, 6, 2 ), # Day
451 (int)substr( $ts, 0, 4 ) ); #Year
453 $date = date( 'YmdHis', $t );
460 * This is a workalike of PHP's date() function, but with better
461 * internationalisation, a reduced set of format characters, and a better
464 * Supported format characters are dDjlNwzWFmMntLYyaAgGhHiscrU. See the
465 * PHP manual for definitions. There are a number of extensions, which
468 * xn Do not translate digits of the next numeric format character
469 * xN Toggle raw digit (xn) flag, stays set until explicitly unset
470 * xr Use roman numerals for the next numeric format character
472 * xg Genitive month name
474 * Characters enclosed in double quotes will be considered literal (with
475 * the quotes themselves removed). Unmatched quotes will be considered
476 * literal quotes. Example:
478 * "The month is" F => The month is January
481 * Backslash escaping is also supported.
483 * Input timestamp is assumed to be pre-normalized to the desired local
486 * @param string $format
487 * @param string $ts 14-character timestamp
491 function sprintfDate( $format, $ts ) {
497 for ( $p = 0; $p < strlen( $format ); $p++
) {
500 if ( $code == 'x' && $p < strlen( $format ) - 1 ) {
501 $code .= $format[++
$p];
512 $rawToggle = !$rawToggle;
518 $s .= $this->getMonthNameGen( substr( $ts, 4, 2 ) );
521 $num = substr( $ts, 6, 2 );
524 if ( !$unix ) $unix = wfTimestamp( TS_UNIX
, $ts );
525 $s .= $this->getWeekdayAbbreviation( gmdate( 'w', $unix ) +
1 );
528 $num = intval( substr( $ts, 6, 2 ) );
531 if ( !$unix ) $unix = wfTimestamp( TS_UNIX
, $ts );
532 $s .= $this->getWeekdayName( gmdate( 'w', $unix ) +
1 );
535 if ( !$unix ) $unix = wfTimestamp( TS_UNIX
, $ts );
536 $w = gmdate( 'w', $unix );
540 if ( !$unix ) $unix = wfTimestamp( TS_UNIX
, $ts );
541 $num = gmdate( 'w', $unix );
544 if ( !$unix ) $unix = wfTimestamp( TS_UNIX
, $ts );
545 $num = gmdate( 'z', $unix );
548 if ( !$unix ) $unix = wfTimestamp( TS_UNIX
, $ts );
549 $num = gmdate( 'W', $unix );
552 $s .= $this->getMonthName( substr( $ts, 4, 2 ) );
555 $num = substr( $ts, 4, 2 );
558 $s .= $this->getMonthAbbreviation( substr( $ts, 4, 2 ) );
561 $num = intval( substr( $ts, 4, 2 ) );
564 if ( !$unix ) $unix = wfTimestamp( TS_UNIX
, $ts );
565 $num = gmdate( 't', $unix );
568 if ( !$unix ) $unix = wfTimestamp( TS_UNIX
, $ts );
569 $num = gmdate( 'L', $unix );
572 $num = substr( $ts, 0, 4 );
575 $num = substr( $ts, 2, 2 );
578 $s .= intval( substr( $ts, 8, 2 ) ) < 12 ?
'am' : 'pm';
581 $s .= intval( substr( $ts, 8, 2 ) ) < 12 ?
'AM' : 'PM';
584 $h = substr( $ts, 8, 2 );
585 $num = $h %
12 ?
$h %
12 : 12;
588 $num = intval( substr( $ts, 8, 2 ) );
591 $h = substr( $ts, 8, 2 );
592 $num = sprintf( '%02d', $h %
12 ?
$h %
12 : 12 );
595 $num = substr( $ts, 8, 2 );
598 $num = substr( $ts, 10, 2 );
601 $num = substr( $ts, 12, 2 );
604 if ( !$unix ) $unix = wfTimestamp( TS_UNIX
, $ts );
605 $s .= gmdate( 'c', $unix );
608 if ( !$unix ) $unix = wfTimestamp( TS_UNIX
, $ts );
609 $s .= gmdate( 'r', $unix );
612 if ( !$unix ) $unix = wfTimestamp( TS_UNIX
, $ts );
617 if ( $p < strlen( $format ) - 1 ) {
625 if ( $p < strlen( $format ) - 1 ) {
626 $endQuote = strpos( $format, '"', $p +
1 );
627 if ( $endQuote === false ) {
628 # No terminating quote, assume literal "
631 $s .= substr( $format, $p +
1, $endQuote - $p - 1 );
635 # Quote at end of string, assume literal "
642 if ( $num !== false ) {
643 if ( $rawToggle ||
$raw ) {
646 } elseif ( $roman ) {
647 $s .= self
::romanNumeral( $num );
650 $s .= $this->formatNum( $num, true );
659 * Roman number formatting up to 3000
661 static function romanNumeral( $num ) {
662 static $table = array(
663 array( '', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X' ),
664 array( '', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC', 'C' ),
665 array( '', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM', 'M' ),
666 array( '', 'M', 'MM', 'MMM' )
669 $num = intval( $num );
670 if ( $num > 3000 ||
$num <= 0 ) {
675 for ( $pow10 = 1000, $i = 3; $i >= 0; $pow10 /= 10, $i-- ) {
676 if ( $num >= $pow10 ) {
677 $s .= $table[$i][floor($num / $pow10)];
679 $num = $num %
$pow10;
685 * This is meant to be used by time(), date(), and timeanddate() to get
686 * the date preference they're supposed to use, it should be used in
690 * function timeanddate([...], $format = true) {
691 * $datePreference = $this->dateFormat($format);
696 * @param mixed $usePrefs: if true, the user's preference is used
697 * if false, the site/language default is used
698 * if int/string, assumed to be a format.
701 function dateFormat( $usePrefs = true ) {
704 if( is_bool( $usePrefs ) ) {
706 $datePreference = $wgUser->getDatePreference();
708 $options = User
::getDefaultOptions();
709 $datePreference = (string)$options['date'];
712 $datePreference = (string)$usePrefs;
716 if( $datePreference == '' ) {
720 return $datePreference;
725 * @param mixed $ts the time format which needs to be turned into a
726 * date('YmdHis') format with wfTimestamp(TS_MW,$ts)
727 * @param bool $adj whether to adjust the time output according to the
728 * user configured offset ($timecorrection)
729 * @param mixed $format true to use user's date format preference
730 * @param string $timecorrection the time offset as returned by
731 * validateTimeZone() in Special:Preferences
734 function date( $ts, $adj = false, $format = true, $timecorrection = false ) {
737 $ts = $this->userAdjust( $ts, $timecorrection );
740 $pref = $this->dateFormat( $format );
741 if( $pref == 'default' ||
!isset( $this->dateFormats
["$pref date"] ) ) {
742 $pref = $this->defaultDateFormat
;
744 return $this->sprintfDate( $this->dateFormats
["$pref date"], $ts );
749 * @param mixed $ts the time format which needs to be turned into a
750 * date('YmdHis') format with wfTimestamp(TS_MW,$ts)
751 * @param bool $adj whether to adjust the time output according to the
752 * user configured offset ($timecorrection)
753 * @param mixed $format true to use user's date format preference
754 * @param string $timecorrection the time offset as returned by
755 * validateTimeZone() in Special:Preferences
758 function time( $ts, $adj = false, $format = true, $timecorrection = false ) {
761 $ts = $this->userAdjust( $ts, $timecorrection );
764 $pref = $this->dateFormat( $format );
765 if( $pref == 'default' ||
!isset( $this->dateFormats
["$pref time"] ) ) {
766 $pref = $this->defaultDateFormat
;
768 return $this->sprintfDate( $this->dateFormats
["$pref time"], $ts );
773 * @param mixed $ts the time format which needs to be turned into a
774 * date('YmdHis') format with wfTimestamp(TS_MW,$ts)
775 * @param bool $adj whether to adjust the time output according to the
776 * user configured offset ($timecorrection)
778 * @param mixed $format what format to return, if it's false output the
779 * default one (default true)
780 * @param string $timecorrection the time offset as returned by
781 * validateTimeZone() in Special:Preferences
784 function timeanddate( $ts, $adj = false, $format = true, $timecorrection = false) {
787 $ts = wfTimestamp( TS_MW
, $ts );
790 $ts = $this->userAdjust( $ts, $timecorrection );
793 $pref = $this->dateFormat( $format );
794 if( $pref == 'default' ||
!isset( $this->dateFormats
["$pref both"] ) ) {
795 $pref = $this->defaultDateFormat
;
798 return $this->sprintfDate( $this->dateFormats
["$pref both"], $ts );
801 function getMessage( $key ) {
803 return isset( $this->messages
[$key] ) ?
$this->messages
[$key] : null;
806 function getAllMessages() {
808 return $this->messages
;
811 function iconv( $in, $out, $string ) {
812 # For most languages, this is a wrapper for iconv
813 return iconv( $in, $out . '//IGNORE', $string );
816 // callback functions for uc(), lc(), ucwords(), ucwordbreaks()
817 function ucwordbreaksCallbackAscii($matches){
818 return $this->ucfirst($matches[1]);
821 function ucwordbreaksCallbackMB($matches){
822 return mb_strtoupper($matches[0]);
825 function ucCallback($matches){
826 list( $wikiUpperChars ) = self
::getCaseMaps();
827 return strtr( $matches[1], $wikiUpperChars );
830 function lcCallback($matches){
831 list( , $wikiLowerChars ) = self
::getCaseMaps();
832 return strtr( $matches[1], $wikiLowerChars );
835 function ucwordsCallbackMB($matches){
836 return mb_strtoupper($matches[0]);
839 function ucwordsCallbackWiki($matches){
840 list( $wikiUpperChars ) = self
::getCaseMaps();
841 return strtr( $matches[0], $wikiUpperChars );
844 function ucfirst( $str ) {
845 return self
::uc( $str, true );
848 function uc( $str, $first = false ) {
849 if ( function_exists( 'mb_strtoupper' ) ) {
851 if ( self
::isMultibyte( $str ) ) {
852 return mb_strtoupper( mb_substr( $str, 0, 1 ) ) . mb_substr( $str, 1 );
854 return ucfirst( $str );
857 return self
::isMultibyte( $str ) ?
mb_strtoupper( $str ) : strtoupper( $str );
860 if ( self
::isMultibyte( $str ) ) {
861 list( $wikiUpperChars ) = $this->getCaseMaps();
862 $x = $first ?
'^' : '';
863 return preg_replace_callback(
864 "/$x([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/",
865 array($this,"ucCallback"),
869 return $first ?
ucfirst( $str ) : strtoupper( $str );
874 function lcfirst( $str ) {
875 return self
::lc( $str, true );
878 function lc( $str, $first = false ) {
879 if ( function_exists( 'mb_strtolower' ) )
881 if ( self
::isMultibyte( $str ) )
882 return mb_strtolower( mb_substr( $str, 0, 1 ) ) . mb_substr( $str, 1 );
884 return strtolower( substr( $str, 0, 1 ) ) . substr( $str, 1 );
886 return self
::isMultibyte( $str ) ?
mb_strtolower( $str ) : strtolower( $str );
888 if ( self
::isMultibyte( $str ) ) {
889 list( , $wikiLowerChars ) = self
::getCaseMaps();
890 $x = $first ?
'^' : '';
891 return preg_replace_callback(
892 "/$x([A-Z]|[\\xc0-\\xff][\\x80-\\xbf]*)/",
893 array($this,"lcCallback"),
897 return $first ?
strtolower( substr( $str, 0, 1 ) ) . substr( $str, 1 ) : strtolower( $str );
900 function isMultibyte( $str ) {
901 return (bool)preg_match( '/[\x80-\xff]/', $str );
904 function ucwords($str) {
905 if ( self
::isMultibyte( $str ) ) {
906 $str = self
::lc($str);
908 // regexp to find first letter in each word (i.e. after each space)
909 $replaceRegexp = "/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)| ([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/";
911 // function to use to capitalize a single char
912 if ( function_exists( 'mb_strtoupper' ) )
913 return preg_replace_callback(
915 array($this,"ucwordsCallbackMB"),
919 return preg_replace_callback(
921 array($this,"ucwordsCallbackWiki"),
926 return ucwords( strtolower( $str ) );
929 # capitalize words at word breaks
930 function ucwordbreaks($str){
931 if (self
::isMultibyte( $str ) ) {
932 $str = self
::lc($str);
934 // since \b doesn't work for UTF-8, we explicitely define word break chars
935 $breaks= "[ \-\(\)\}\{\.,\?!]";
937 // find first letter after word break
938 $replaceRegexp = "/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)|$breaks([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/";
940 if ( function_exists( 'mb_strtoupper' ) )
941 return preg_replace_callback(
943 array($this,"ucwordbreaksCallbackMB"),
947 return preg_replace_callback(
949 array($this,"ucwordsCallbackWiki"),
954 return preg_replace_callback(
955 '/\b([\w\x80-\xff]+)\b/',
956 array($this,"ucwordbreaksCallbackAscii"),
961 * Return a case-folded representation of $s
963 * This is a representation such that caseFold($s1)==caseFold($s2) if $s1
964 * and $s2 are the same except for the case of their characters. It is not
965 * necessary for the value returned to make sense when displayed.
967 * Do *not* perform any other normalisation in this function. If a caller
968 * uses this function when it should be using a more general normalisation
969 * function, then fix the caller.
971 function caseFold( $s ) {
972 return $this->uc( $s );
975 function checkTitleEncoding( $s ) {
976 if( is_array( $s ) ) {
977 wfDebugDieBacktrace( 'Given array to checkTitleEncoding.' );
979 # Check for non-UTF-8 URLs
980 $ishigh = preg_match( '/[\x80-\xff]/', $s);
981 if(!$ishigh) return $s;
983 $isutf8 = preg_match( '/^([\x00-\x7f]|[\xc0-\xdf][\x80-\xbf]|' .
984 '[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3})+$/', $s );
985 if( $isutf8 ) return $s;
987 return $this->iconv( $this->fallback8bitEncoding(), "utf-8", $s );
990 function fallback8bitEncoding() {
992 return $this->fallback8bitEncoding
;
996 * Some languages have special punctuation to strip out
997 * or characters which need to be converted for MySQL's
998 * indexing to grok it correctly. Make such changes here.
1003 function stripForSearch( $string ) {
1005 if ( $wgDBtype != 'mysql' ) {
1009 # MySQL fulltext index doesn't grok utf-8, so we
1010 # need to fold cases and convert to hex
1012 wfProfileIn( __METHOD__
);
1013 if( function_exists( 'mb_strtolower' ) ) {
1014 $out = preg_replace(
1015 "/([\\xc0-\\xff][\\x80-\\xbf]*)/e",
1016 "'U8' . bin2hex( \"$1\" )",
1017 mb_strtolower( $string ) );
1019 list( , $wikiLowerChars ) = self
::getCaseMaps();
1020 $out = preg_replace(
1021 "/([\\xc0-\\xff][\\x80-\\xbf]*)/e",
1022 "'U8' . bin2hex( strtr( \"\$1\", \$wikiLowerChars ) )",
1025 wfProfileOut( __METHOD__
);
1029 function convertForSearchResult( $termsArray ) {
1030 # some languages, e.g. Chinese, need to do a conversion
1031 # in order for search results to be displayed correctly
1036 * Get the first character of a string.
1041 function firstChar( $s ) {
1043 preg_match( '/^([\x00-\x7f]|[\xc0-\xdf][\x80-\xbf]|' .
1044 '[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3})/', $s, $matches);
1046 return isset( $matches[1] ) ?
$matches[1] : "";
1049 function initEncoding() {
1050 # Some languages may have an alternate char encoding option
1051 # (Esperanto X-coding, Japanese furigana conversion, etc)
1052 # If this language is used as the primary content language,
1053 # an override to the defaults can be set here on startup.
1056 function recodeForEdit( $s ) {
1057 # For some languages we'll want to explicitly specify
1058 # which characters make it into the edit box raw
1059 # or are converted in some way or another.
1060 # Note that if wgOutputEncoding is different from
1061 # wgInputEncoding, this text will be further converted
1062 # to wgOutputEncoding.
1063 global $wgEditEncoding;
1064 if( $wgEditEncoding == '' or
1065 $wgEditEncoding == 'UTF-8' ) {
1068 return $this->iconv( 'UTF-8', $wgEditEncoding, $s );
1072 function recodeInput( $s ) {
1073 # Take the previous into account.
1074 global $wgEditEncoding;
1075 if($wgEditEncoding != "") {
1076 $enc = $wgEditEncoding;
1080 if( $enc == 'UTF-8' ) {
1083 return $this->iconv( $enc, 'UTF-8', $s );
1088 * For right-to-left language support
1098 * A hidden direction mark (LRM or RLM), depending on the language direction
1102 function getDirMark() {
1103 return $this->isRTL() ?
"\xE2\x80\x8F" : "\xE2\x80\x8E";
1107 * An arrow, depending on the language direction
1111 function getArrow() {
1112 return $this->isRTL() ?
'←' : '→';
1116 * To allow "foo[[bar]]" to extend the link over the whole word "foobar"
1120 function linkPrefixExtension() {
1122 return $this->linkPrefixExtension
;
1125 function &getMagicWords() {
1127 return $this->magicWords
;
1130 # Fill a MagicWord object with data from here
1131 function getMagic( &$mw ) {
1132 if ( !$this->mMagicHookDone
) {
1133 $this->mMagicHookDone
= true;
1134 wfRunHooks( 'LanguageGetMagic', array( &$this->mMagicExtensions
, $this->getCode() ) );
1136 if ( isset( $this->mMagicExtensions
[$mw->mId
] ) ) {
1137 $rawEntry = $this->mMagicExtensions
[$mw->mId
];
1139 $magicWords =& $this->getMagicWords();
1140 if ( isset( $magicWords[$mw->mId
] ) ) {
1141 $rawEntry = $magicWords[$mw->mId
];
1143 # Fall back to English if local list is incomplete
1144 $magicWords =& Language
::getMagicWords();
1145 $rawEntry = $magicWords[$mw->mId
];
1149 if( !is_array( $rawEntry ) ) {
1150 error_log( "\"$rawEntry\" is not a valid magic thingie for \"$mw->mId\"" );
1152 $mw->mCaseSensitive
= $rawEntry[0];
1153 $mw->mSynonyms
= array_slice( $rawEntry, 1 );
1157 * Add magic words to the extension array
1159 function addMagicWordsByLang( $newWords ) {
1160 $code = $this->getCode();
1161 $fallbackChain = array();
1162 while ( $code && !in_array( $code, $fallbackChain ) ) {
1163 $fallbackChain[] = $code;
1164 $code = self
::getFallbackFor( $code );
1166 if ( !in_array( 'en', $fallbackChain ) ) {
1167 $fallbackChain[] = 'en';
1169 $fallbackChain = array_reverse( $fallbackChain );
1170 foreach ( $fallbackChain as $code ) {
1171 if ( isset( $newWords[$code] ) ) {
1172 $this->mMagicExtensions
= $newWords[$code] +
$this->mMagicExtensions
;
1178 * Get special page names, as an associative array
1179 * case folded alias => real name
1181 function getSpecialPageAliases() {
1183 if ( !isset( $this->mExtendedSpecialPageAliases
) ) {
1184 $this->mExtendedSpecialPageAliases
= $this->specialPageAliases
;
1185 wfRunHooks( 'LangugeGetSpecialPageAliases',
1186 array( &$this->mExtendedSpecialPageAliases
, $this->getCode() ) );
1188 return $this->mExtendedSpecialPageAliases
;
1192 * Italic is unsuitable for some languages
1196 * @param string $text The text to be emphasized.
1199 function emphasize( $text ) {
1200 return "<em>$text</em>";
1204 * Normally we output all numbers in plain en_US style, that is
1205 * 293,291.235 for twohundredninetythreethousand-twohundredninetyone
1206 * point twohundredthirtyfive. However this is not sutable for all
1207 * languages, some such as Pakaran want ੨੯੩,੨੯੫.੨੩੫ and others such as
1208 * Icelandic just want to use commas instead of dots, and dots instead
1209 * of commas like "293.291,235".
1211 * An example of this function being called:
1213 * wfMsg( 'message', $wgLang->formatNum( $num ) )
1216 * See LanguageGu.php for the Gujarati implementation and
1217 * LanguageIs.php for the , => . and . => , implementation.
1219 * @todo check if it's viable to use localeconv() for the decimal
1222 * @param mixed $number the string to be formatted, should be an integer or
1223 * a floating point number.
1224 * @param bool $nocommafy Set to true for special numbers like dates
1227 function formatNum( $number, $nocommafy = false ) {
1228 global $wgTranslateNumerals;
1230 $number = $this->commafy($number);
1231 $s = $this->separatorTransformTable();
1232 if (!is_null($s)) { $number = strtr($number, $s); }
1235 if ($wgTranslateNumerals) {
1236 $s = $this->digitTransformTable();
1237 if (!is_null($s)) { $number = strtr($number, $s); }
1243 function parseFormattedNumber( $number ) {
1244 $s = $this->digitTransformTable();
1245 if (!is_null($s)) { $number = strtr($number, array_flip($s)); }
1247 $s = $this->separatorTransformTable();
1248 if (!is_null($s)) { $number = strtr($number, array_flip($s)); }
1250 $number = strtr( $number, array (',' => '') );
1255 * Adds commas to a given number
1260 function commafy($_) {
1261 return strrev((string)preg_replace('/(\d{3})(?=\d)(?!\d*\.)/','$1,',strrev($_)));
1264 function digitTransformTable() {
1266 return $this->digitTransformTable
;
1269 function separatorTransformTable() {
1271 return $this->separatorTransformTable
;
1276 * For the credit list in includes/Credits.php (action=credits)
1281 function listToText( $l ) {
1284 for ($i = $m; $i >= 0; $i--) {
1287 } else if ($i == $m - 1) {
1288 $s = $l[$i] . ' ' . $this->getMessageFromDB( 'and' ) . ' ' . $s;
1290 $s = $l[$i] . ', ' . $s;
1297 * Truncate a string to a specified length in bytes, appending an optional
1298 * string (e.g. for ellipses)
1300 * The database offers limited byte lengths for some columns in the database;
1301 * multi-byte character sets mean we need to ensure that only whole characters
1302 * are included, otherwise broken characters can be passed to the user
1304 * If $length is negative, the string will be truncated from the beginning
1306 * @param string $string String to truncate
1307 * @param int $length Maximum length (excluding ellipses)
1308 * @param string $ellipses String to append to the truncated text
1311 function truncate( $string, $length, $ellipsis = "" ) {
1312 if( $length == 0 ) {
1315 if ( strlen( $string ) <= abs( $length ) ) {
1319 $string = substr( $string, 0, $length );
1320 $char = ord( $string[strlen( $string ) - 1] );
1322 if ($char >= 0xc0) {
1323 # We got the first byte only of a multibyte char; remove it.
1324 $string = substr( $string, 0, -1 );
1325 } elseif( $char >= 0x80 &&
1326 preg_match( '/^(.*)(?:[\xe0-\xef][\x80-\xbf]|' .
1327 '[\xf0-\xf7][\x80-\xbf]{1,2})$/', $string, $m ) ) {
1328 # We chopped in the middle of a character; remove it
1331 return $string . $ellipsis;
1333 $string = substr( $string, $length );
1334 $char = ord( $string[0] );
1335 if( $char >= 0x80 && $char < 0xc0 ) {
1336 # We chopped in the middle of a character; remove the whole thing
1337 $string = preg_replace( '/^[\x80-\xbf]+/', '', $string );
1339 return $ellipsis . $string;
1344 * Grammatical transformations, needed for inflected languages
1345 * Invoked by putting {{grammar:case|word}} in a message
1347 * @param string $word
1348 * @param string $case
1351 function convertGrammar( $word, $case ) {
1352 global $wgGrammarForms;
1353 if ( isset($wgGrammarForms['en'][$case][$word]) ) {
1354 return $wgGrammarForms['en'][$case][$word];
1360 * Plural form transformations, needed for some languages.
1361 * For example, where are 3 form of plural in Russian and Polish,
1362 * depending on "count mod 10". See [[w:Plural]]
1363 * For English it is pretty simple.
1365 * Invoked by putting {{plural:count|wordform1|wordform2}}
1366 * or {{plural:count|wordform1|wordform2|wordform3}}
1368 * Example: {{plural:{{NUMBEROFARTICLES}}|article|articles}}
1370 * @param integer $count
1371 * @param string $wordform1
1372 * @param string $wordform2
1373 * @param string $wordform3 (optional)
1374 * @param string $wordform4 (optional)
1375 * @param string $wordform5 (optional)
1378 function convertPlural( $count, $w1, $w2, $w3, $w4, $w5) {
1379 return ( $count == '1' ||
$count == '-1' ) ?
$w1 : $w2;
1383 * For translaing of expiry times
1384 * @param string The validated block time in English
1385 * @return Somehow translated block time
1386 * @see LanguageFi.php for example implementation
1388 function translateBlockExpiry( $str ) {
1390 $scBlockExpiryOptions = $this->getMessageFromDB( 'ipboptions' );
1392 if ( $scBlockExpiryOptions == '-') {
1396 foreach (explode(',', $scBlockExpiryOptions) as $option) {
1397 if ( strpos($option, ":") === false )
1399 list($show, $value) = explode(":", $option);
1400 if ( strcmp ( $str, $value) == 0 ) {
1401 return htmlspecialchars( trim( $show ) );
1409 * languages like Chinese need to be segmented in order for the diff
1412 * @param string $text
1415 function segmentForDiff( $text ) {
1420 * and unsegment to show the result
1422 * @param string $text
1425 function unsegmentForDiff( $text ) {
1429 # convert text to different variants of a language.
1430 function convert( $text, $isTitle = false) {
1431 return $this->mConverter
->convert($text, $isTitle);
1434 # Convert text from within Parser
1435 function parserConvert( $text, &$parser ) {
1436 return $this->mConverter
->parserConvert( $text, $parser );
1439 # Check if this is a language with variants
1440 function hasVariants(){
1441 return sizeof($this->getVariants())>1;
1444 # Put custom tags (e.g. -{ }-) around math to prevent conversion
1445 function armourMath($text){
1446 return $this->mConverter
->armourMath($text);
1451 * Perform output conversion on a string, and encode for safe HTML output.
1452 * @param string $text
1453 * @param bool $isTitle -- wtf?
1455 * @todo this should get integrated somewhere sane
1457 function convertHtml( $text, $isTitle = false ) {
1458 return htmlspecialchars( $this->convert( $text, $isTitle ) );
1461 function convertCategoryKey( $key ) {
1462 return $this->mConverter
->convertCategoryKey( $key );
1466 * get the list of variants supported by this langauge
1467 * see sample implementation in LanguageZh.php
1469 * @return array an array of language codes
1471 function getVariants() {
1472 return $this->mConverter
->getVariants();
1476 function getPreferredVariant( $fromUser = true ) {
1477 return $this->mConverter
->getPreferredVariant( $fromUser );
1481 * if a language supports multiple variants, it is
1482 * possible that non-existing link in one variant
1483 * actually exists in another variant. this function
1484 * tries to find it. See e.g. LanguageZh.php
1486 * @param string $link the name of the link
1487 * @param mixed $nt the title object of the link
1488 * @return null the input parameters may be modified upon return
1490 function findVariantLink( &$link, &$nt ) {
1491 $this->mConverter
->findVariantLink($link, $nt);
1495 * If a language supports multiple variants, converts text
1496 * into an array of all possible variants of the text:
1497 * 'variant' => text in that variant
1500 function convertLinkToAllVariants($text){
1501 return $this->mConverter
->convertLinkToAllVariants($text);
1506 * returns language specific options used by User::getPageRenderHash()
1507 * for example, the preferred language variant
1512 function getExtraHashOptions() {
1513 return $this->mConverter
->getExtraHashOptions();
1517 * for languages that support multiple variants, the title of an
1518 * article may be displayed differently in different variants. this
1519 * function returns the apporiate title defined in the body of the article.
1523 function getParsedTitle() {
1524 return $this->mConverter
->getParsedTitle();
1528 * Enclose a string with the "no conversion" tag. This is used by
1529 * various functions in the Parser
1531 * @param string $text text to be tagged for no conversion
1532 * @return string the tagged text
1534 function markNoConversion( $text, $noParse=false ) {
1535 return $this->mConverter
->markNoConversion( $text, $noParse );
1539 * A regular expression to match legal word-trailing characters
1540 * which should be merged onto a link of the form [[foo]]bar.
1545 function linkTrail() {
1547 return $this->linkTrail
;
1550 function getLangObj() {
1555 * Get the RFC 3066 code for this language object
1557 function getCode() {
1558 return $this->mCode
;
1561 function setCode( $code ) {
1562 $this->mCode
= $code;
1565 static function getFileName( $prefix = 'Language', $code, $suffix = '.php' ) {
1566 return $prefix . str_replace( '-', '_', ucfirst( $code ) ) . $suffix;
1569 static function getMessagesFileName( $code ) {
1571 return self
::getFileName( "$IP/languages/messages/Messages", $code, '.php' );
1574 static function getClassFileName( $code ) {
1576 return self
::getFileName( "$IP/languages/classes/Language", $code, '.php' );
1579 static function getLocalisationArray( $code, $disableCache = false ) {
1580 self
::loadLocalisation( $code, $disableCache );
1581 return self
::$mLocalisationCache[$code];
1585 * Load localisation data for a given code into the static cache
1587 * @return array Dependencies, map of filenames to mtimes
1589 static function loadLocalisation( $code, $disableCache = false ) {
1590 static $recursionGuard = array();
1594 throw new MWException( "Invalid language code requested" );
1597 if ( !$disableCache ) {
1598 # Try the per-process cache
1599 if ( isset( self
::$mLocalisationCache[$code] ) ) {
1600 return self
::$mLocalisationCache[$code]['deps'];
1603 wfProfileIn( __METHOD__
);
1605 # Try the serialized directory
1606 $cache = wfGetPrecompiledData( self
::getFileName( "Messages", $code, '.ser' ) );
1608 self
::$mLocalisationCache[$code] = $cache;
1609 wfDebug( "Language::loadLocalisation(): got localisation for $code from precompiled data file\n" );
1610 wfProfileOut( __METHOD__
);
1611 return self
::$mLocalisationCache[$code]['deps'];
1614 # Try the global cache
1615 $memcKey = wfMemcKey('localisation', $code );
1616 $cache = $wgMemc->get( $memcKey );
1618 # Check file modification times
1619 foreach ( $cache['deps'] as $file => $mtime ) {
1620 if ( !file_exists( $file ) ||
filemtime( $file ) > $mtime ) {
1624 if ( self
::isLocalisationOutOfDate( $cache ) ) {
1625 $wgMemc->delete( $memcKey );
1627 wfDebug( "Language::loadLocalisation(): localisation cache for $code had expired due to update of $file\n" );
1629 self
::$mLocalisationCache[$code] = $cache;
1630 wfDebug( "Language::loadLocalisation(): got localisation for $code from cache\n" );
1631 wfProfileOut( __METHOD__
);
1632 return $cache['deps'];
1636 wfProfileIn( __METHOD__
);
1639 # Default fallback, may be overridden when the messages file is included
1640 if ( $code != 'en' ) {
1646 # Load the primary localisation from the source file
1647 $filename = self
::getMessagesFileName( $code );
1648 if ( !file_exists( $filename ) ) {
1649 wfDebug( "Language::loadLocalisation(): no localisation file for $code, using implicit fallback to en\n" );
1653 $deps = array( $filename => filemtime( $filename ) );
1654 require( $filename );
1655 $cache = compact( self
::$mLocalisationKeys );
1656 wfDebug( "Language::loadLocalisation(): got localisation for $code from source\n" );
1659 if ( !empty( $fallback ) ) {
1660 # Load the fallback localisation, with a circular reference guard
1661 if ( isset( $recursionGuard[$code] ) ) {
1662 throw new MWException( "Error: Circular fallback reference in language code $code" );
1664 $recursionGuard[$code] = true;
1665 $newDeps = self
::loadLocalisation( $fallback, $disableCache );
1666 unset( $recursionGuard[$code] );
1668 $secondary = self
::$mLocalisationCache[$fallback];
1669 $deps = array_merge( $deps, $newDeps );
1671 # Merge the fallback localisation with the current localisation
1672 foreach ( self
::$mLocalisationKeys as $key ) {
1673 if ( isset( $cache[$key] ) ) {
1674 if ( isset( $secondary[$key] ) ) {
1675 if ( in_array( $key, self
::$mMergeableMapKeys ) ) {
1676 $cache[$key] = $cache[$key] +
$secondary[$key];
1677 } elseif ( in_array( $key, self
::$mMergeableListKeys ) ) {
1678 $cache[$key] = array_merge( $secondary[$key], $cache[$key] );
1679 } elseif ( in_array( $key, self
::$mMergeableAliasListKeys ) ) {
1680 $cache[$key] = array_merge_recursive( $cache[$key], $secondary[$key] );
1684 $cache[$key] = $secondary[$key];
1688 # Merge bookstore lists if requested
1689 if ( !empty( $cache['bookstoreList']['inherit'] ) ) {
1690 $cache['bookstoreList'] = array_merge( $cache['bookstoreList'], $secondary['bookstoreList'] );
1692 if ( isset( $cache['bookstoreList']['inherit'] ) ) {
1693 unset( $cache['bookstoreList']['inherit'] );
1697 # Add dependencies to the cache entry
1698 $cache['deps'] = $deps;
1700 # Replace spaces with underscores in namespace names
1701 $cache['namespaceNames'] = str_replace( ' ', '_', $cache['namespaceNames'] );
1703 # Save to both caches
1704 self
::$mLocalisationCache[$code] = $cache;
1705 if ( !$disableCache ) {
1706 $wgMemc->set( $memcKey, $cache );
1709 wfProfileOut( __METHOD__
);
1714 * Test if a given localisation cache is out of date with respect to the
1715 * source Messages files. This is done automatically for the global cache
1716 * in $wgMemc, but is only done on certain occasions for the serialized
1719 * @param $cache mixed Either a language code or a cache array
1721 static function isLocalisationOutOfDate( $cache ) {
1722 if ( !is_array( $cache ) ) {
1723 self
::loadLocalisation( $cache );
1724 $cache = self
::$mLocalisationCache[$cache];
1727 foreach ( $cache['deps'] as $file => $mtime ) {
1728 if ( !file_exists( $file ) ||
filemtime( $file ) > $mtime ) {
1737 * Get the fallback for a given language
1739 static function getFallbackFor( $code ) {
1740 self
::loadLocalisation( $code );
1741 return self
::$mLocalisationCache[$code]['fallback'];
1745 * Get all messages for a given language
1747 static function getMessagesFor( $code ) {
1748 self
::loadLocalisation( $code );
1749 return self
::$mLocalisationCache[$code]['messages'];
1753 * Get a message for a given language
1755 static function getMessageFor( $key, $code ) {
1756 self
::loadLocalisation( $code );
1757 return isset( self
::$mLocalisationCache[$code]['messages'][$key] ) ? self
::$mLocalisationCache[$code]['messages'][$key] : null;
1761 * Load localisation data for this object
1764 if ( !$this->mLoaded
) {
1765 self
::loadLocalisation( $this->getCode() );
1766 $cache =& self
::$mLocalisationCache[$this->getCode()];
1767 foreach ( self
::$mLocalisationKeys as $key ) {
1768 $this->$key = $cache[$key];
1770 $this->mLoaded
= true;
1772 $this->fixUpSettings();
1777 * Do any necessary post-cache-load settings adjustment
1779 function fixUpSettings() {
1780 global $wgExtraNamespaces, $wgMetaNamespace, $wgMetaNamespaceTalk,
1781 $wgNamespaceAliases, $wgAmericanDates;
1782 wfProfileIn( __METHOD__
);
1783 if ( $wgExtraNamespaces ) {
1784 $this->namespaceNames
= $wgExtraNamespaces +
$this->namespaceNames
;
1787 $this->namespaceNames
[NS_PROJECT
] = $wgMetaNamespace;
1788 if ( $wgMetaNamespaceTalk ) {
1789 $this->namespaceNames
[NS_PROJECT_TALK
] = $wgMetaNamespaceTalk;
1791 $talk = $this->namespaceNames
[NS_PROJECT_TALK
];
1792 $talk = str_replace( '$1', $wgMetaNamespace, $talk );
1794 # Allow grammar transformations
1795 # Allowing full message-style parsing would make simple requests
1796 # such as action=raw much more expensive than they need to be.
1797 # This will hopefully cover most cases.
1798 $talk = preg_replace_callback( '/{{grammar:(.*?)\|(.*?)}}/i',
1799 array( &$this, 'replaceGrammarInNamespace' ), $talk );
1800 $talk = str_replace( ' ', '_', $talk );
1801 $this->namespaceNames
[NS_PROJECT_TALK
] = $talk;
1804 # The above mixing may leave namespaces out of canonical order.
1805 # Re-order by namespace ID number...
1806 ksort( $this->namespaceNames
);
1808 # Put namespace names and aliases into a hashtable.
1809 # If this is too slow, then we should arrange it so that it is done
1810 # before caching. The catch is that at pre-cache time, the above
1811 # class-specific fixup hasn't been done.
1812 $this->mNamespaceIds
= array();
1813 foreach ( $this->namespaceNames
as $index => $name ) {
1814 $this->mNamespaceIds
[$this->lc($name)] = $index;
1816 if ( $this->namespaceAliases
) {
1817 foreach ( $this->namespaceAliases
as $name => $index ) {
1818 $this->mNamespaceIds
[$this->lc($name)] = $index;
1821 if ( $wgNamespaceAliases ) {
1822 foreach ( $wgNamespaceAliases as $name => $index ) {
1823 $this->mNamespaceIds
[$this->lc($name)] = $index;
1827 if ( $this->defaultDateFormat
== 'dmy or mdy' ) {
1828 $this->defaultDateFormat
= $wgAmericanDates ?
'mdy' : 'dmy';
1830 wfProfileOut( __METHOD__
);
1833 function replaceGrammarInNamespace( $m ) {
1834 return $this->convertGrammar( trim( $m[2] ), trim( $m[1] ) );
1837 static function getCaseMaps() {
1838 static $wikiUpperChars, $wikiLowerChars;
1839 if ( isset( $wikiUpperChars ) ) {
1840 return array( $wikiUpperChars, $wikiLowerChars );
1843 wfProfileIn( __METHOD__
);
1844 $arr = wfGetPrecompiledData( 'Utf8Case.ser' );
1845 if ( $arr === false ) {
1846 throw new MWException(
1847 "Utf8Case.ser is missing, please run \"make\" in the serialized directory\n" );
1850 wfProfileOut( __METHOD__
);
1851 return array( $wikiUpperChars, $wikiLowerChars );
1854 function formatTimePeriod( $seconds ) {
1855 if ( $seconds < 10 ) {
1856 return $this->formatNum( sprintf( "%.1f", $seconds ) ) . wfMsg( 'seconds-abbrev' );
1857 } elseif ( $seconds < 60 ) {
1858 return $this->formatNum( round( $seconds ) ) . wfMsg( 'seconds-abbrev' );
1859 } elseif ( $seconds < 3600 ) {
1860 return $this->formatNum( floor( $seconds / 60 ) ) . wfMsg( 'minutes-abbrev' ) .
1861 $this->formatNum( round( fmod( $seconds, 60 ) ) ) . wfMsg( 'seconds-abbrev' );
1863 $hours = floor( $seconds / 3600 );
1864 $minutes = floor( ( $seconds - $hours * 3600 ) / 60 );
1865 $secondsPart = round( $seconds - $hours * 3600 - $minutes * 60 );
1866 return $this->formatNum( $hours ) . wfMsg( 'hours-abbrev' ) .
1867 $this->formatNum( $minutes ) . wfMsg( 'minutes-abbrev' ) .
1868 $this->formatNum( $secondsPart ) . wfMsg( 'seconds-abbrev' );
1872 function formatBitrate( $bps ) {
1873 $units = array( 'bps', 'kbps', 'Mbps', 'Gbps' );
1875 return $this->formatNum( $bps ) . $units[0];
1877 $unitIndex = floor( log10( $bps ) / 3 );
1878 $mantissa = $bps / pow( 1000, $unitIndex );
1879 if ( $mantissa < 10 ) {
1880 $mantissa = round( $mantissa, 1 );
1882 $mantissa = round( $mantissa );
1884 return $this->formatNum( $mantissa ) . $units[$unitIndex];
1888 * Format a size in bytes for output, using an appropriate
1889 * unit (B, KB, MB or GB) according to the magnitude in question
1891 * @param $size Size to format
1892 * @return string Plain text (not HTML)
1894 function formatSize( $size ) {
1895 // For small sizes no decimal places necessary
1897 if( $size > 1024 ) {
1898 $size = $size / 1024;
1899 if( $size > 1024 ) {
1900 $size = $size / 1024;
1901 // For MB and bigger two decimal places are smarter
1903 if( $size > 1024 ) {
1904 $size = $size / 1024;
1905 $msg = 'size-gigabytes';
1907 $msg = 'size-megabytes';
1910 $msg = 'size-kilobytes';
1913 $msg = 'size-bytes';
1915 $size = round( $size, $round );
1916 $text = $this->getMessageFromDB( $msg );
1917 return str_replace( '$1', $this->formatNum( $size ), $text );