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 $fallbackChain = array_reverse( $fallbackChain );
1167 foreach ( $fallbackChain as $code ) {
1168 if ( isset( $newWords[$code] ) ) {
1169 $this->mMagicExtensions = $newWords[$code] + $this->mMagicExtensions;
1175 * Get special page names, as an associative array
1176 * case folded alias => real name
1178 function getSpecialPageAliases() {
1180 if ( !isset( $this->mExtendedSpecialPageAliases ) ) {
1181 $this->mExtendedSpecialPageAliases = $this->specialPageAliases;
1182 wfRunHooks( 'LangugeGetSpecialPageAliases',
1183 array( &$this->mExtendedSpecialPageAliases, $this->getCode() ) );
1185 return $this->mExtendedSpecialPageAliases;
1189 * Italic is unsuitable for some languages
1193 * @param string $text The text to be emphasized.
1196 function emphasize( $text ) {
1197 return "<em>$text</em>";
1201 * Normally we output all numbers in plain en_US style, that is
1202 * 293,291.235 for twohundredninetythreethousand-twohundredninetyone
1203 * point twohundredthirtyfive. However this is not sutable for all
1204 * languages, some such as Pakaran want ੨੯੩,੨੯੫.੨੩੫ and others such as
1205 * Icelandic just want to use commas instead of dots, and dots instead
1206 * of commas like "293.291,235".
1208 * An example of this function being called:
1210 * wfMsg( 'message', $wgLang->formatNum( $num ) )
1213 * See LanguageGu.php for the Gujarati implementation and
1214 * LanguageIs.php for the , => . and . => , implementation.
1216 * @todo check if it's viable to use localeconv() for the decimal
1219 * @param mixed $number the string to be formatted, should be an integer or
1220 * a floating point number.
1221 * @param bool $nocommafy Set to true for special numbers like dates
1224 function formatNum( $number, $nocommafy = false ) {
1225 global $wgTranslateNumerals;
1227 $number = $this->commafy($number);
1228 $s = $this->separatorTransformTable();
1229 if (!is_null($s)) { $number = strtr($number, $s); }
1232 if ($wgTranslateNumerals) {
1233 $s = $this->digitTransformTable();
1234 if (!is_null($s)) { $number = strtr($number, $s); }
1240 function parseFormattedNumber( $number ) {
1241 $s = $this->digitTransformTable();
1242 if (!is_null($s)) { $number = strtr($number, array_flip($s)); }
1244 $s = $this->separatorTransformTable();
1245 if (!is_null($s)) { $number = strtr($number, array_flip($s)); }
1247 $number = strtr( $number, array (',' => '') );
1252 * Adds commas to a given number
1257 function commafy($_) {
1258 return strrev((string)preg_replace('/(\d{3})(?=\d)(?!\d*\.)/','$1,',strrev($_)));
1261 function digitTransformTable() {
1263 return $this->digitTransformTable;
1266 function separatorTransformTable() {
1268 return $this->separatorTransformTable;
1273 * For the credit list in includes/Credits.php (action=credits)
1278 function listToText( $l ) {
1281 for ($i = $m; $i >= 0; $i--) {
1284 } else if ($i == $m - 1) {
1285 $s = $l[$i] . ' ' . $this->getMessageFromDB( 'and' ) . ' ' . $s;
1287 $s = $l[$i] . ', ' . $s;
1294 * Truncate a string to a specified length in bytes, appending an optional
1295 * string (e.g. for ellipses)
1297 * The database offers limited byte lengths for some columns in the database;
1298 * multi-byte character sets mean we need to ensure that only whole characters
1299 * are included, otherwise broken characters can be passed to the user
1301 * If $length is negative, the string will be truncated from the beginning
1303 * @param string $string String to truncate
1304 * @param int $length Maximum length (excluding ellipses)
1305 * @param string $ellipses String to append to the truncated text
1308 function truncate( $string, $length, $ellipsis = "" ) {
1309 if( $length == 0 ) {
1312 if ( strlen( $string ) <= abs( $length ) ) {
1316 $string = substr( $string, 0, $length );
1317 $char = ord( $string[strlen( $string ) - 1] );
1319 if ($char >= 0xc0) {
1320 # We got the first byte only of a multibyte char; remove it.
1321 $string = substr( $string, 0, -1 );
1322 } elseif( $char >= 0x80 &&
1323 preg_match( '/^(.*)(?:[\xe0-\xef][\x80-\xbf]|' .
1324 '[\xf0-\xf7][\x80-\xbf]{1,2})$/', $string, $m ) ) {
1325 # We chopped in the middle of a character; remove it
1328 return $string . $ellipsis;
1330 $string = substr( $string, $length );
1331 $char = ord( $string[0] );
1332 if( $char >= 0x80 && $char < 0xc0 ) {
1333 # We chopped in the middle of a character; remove the whole thing
1334 $string = preg_replace( '/^[\x80-\xbf]+/', '', $string );
1336 return $ellipsis . $string;
1341 * Grammatical transformations, needed for inflected languages
1342 * Invoked by putting {{grammar:case|word}} in a message
1344 * @param string $word
1345 * @param string $case
1348 function convertGrammar( $word, $case ) {
1349 global $wgGrammarForms;
1350 if ( isset($wgGrammarForms['en'][$case][$word]) ) {
1351 return $wgGrammarForms['en'][$case][$word];
1357 * Plural form transformations, needed for some languages.
1358 * For example, where are 3 form of plural in Russian and Polish,
1359 * depending on "count mod 10". See [[w:Plural]]
1360 * For English it is pretty simple.
1362 * Invoked by putting {{plural:count|wordform1|wordform2}}
1363 * or {{plural:count|wordform1|wordform2|wordform3}}
1365 * Example: {{plural:{{NUMBEROFARTICLES}}|article|articles}}
1367 * @param integer $count
1368 * @param string $wordform1
1369 * @param string $wordform2
1370 * @param string $wordform3 (optional)
1371 * @param string $wordform4 (optional)
1372 * @param string $wordform5 (optional)
1375 function convertPlural( $count, $w1, $w2, $w3, $w4, $w5) {
1376 return ( $count == '1' || $count == '-1' ) ? $w1 : $w2;
1380 * For translaing of expiry times
1381 * @param string The validated block time in English
1382 * @param $forContent, avoid html?
1383 * @return Somehow translated block time
1384 * @see LanguageFi.php for example implementation
1386 function translateBlockExpiry( $str, $forContent=false ) {
1388 $scBlockExpiryOptions = $this->getMessageFromDB( 'ipboptions' );
1390 if ( $scBlockExpiryOptions == '-') {
1394 foreach (explode(',', $scBlockExpiryOptions) as $option) {
1395 if ( strpos($option, ":") === false )
1397 list($show, $value) = explode(":", $option);
1398 if ( strcmp ( $str, $value) == 0 ) {
1400 return htmlspecialchars($str) . htmlspecialchars( trim( $show ) );
1402 return '<span title="' . htmlspecialchars($str). '">' . htmlspecialchars( trim( $show ) ) . '</span>';
1410 * languages like Chinese need to be segmented in order for the diff
1413 * @param string $text
1416 function segmentForDiff( $text ) {
1421 * and unsegment to show the result
1423 * @param string $text
1426 function unsegmentForDiff( $text ) {
1430 # convert text to different variants of a language.
1431 function convert( $text, $isTitle = false) {
1432 return $this->mConverter->convert($text, $isTitle);
1435 # Convert text from within Parser
1436 function parserConvert( $text, &$parser ) {
1437 return $this->mConverter->parserConvert( $text, $parser );
1440 # Check if this is a language with variants
1441 function hasVariants(){
1442 return sizeof($this->getVariants())>1;
1445 # Put custom tags (e.g. -{ }-) around math to prevent conversion
1446 function armourMath($text){
1447 return $this->mConverter->armourMath($text);
1452 * Perform output conversion on a string, and encode for safe HTML output.
1453 * @param string $text
1454 * @param bool $isTitle -- wtf?
1456 * @todo this should get integrated somewhere sane
1458 function convertHtml( $text, $isTitle = false ) {
1459 return htmlspecialchars( $this->convert( $text, $isTitle ) );
1462 function convertCategoryKey( $key ) {
1463 return $this->mConverter->convertCategoryKey( $key );
1467 * get the list of variants supported by this langauge
1468 * see sample implementation in LanguageZh.php
1470 * @return array an array of language codes
1472 function getVariants() {
1473 return $this->mConverter->getVariants();
1477 function getPreferredVariant( $fromUser = true ) {
1478 return $this->mConverter->getPreferredVariant( $fromUser );
1482 * if a language supports multiple variants, it is
1483 * possible that non-existing link in one variant
1484 * actually exists in another variant. this function
1485 * tries to find it. See e.g. LanguageZh.php
1487 * @param string $link the name of the link
1488 * @param mixed $nt the title object of the link
1489 * @return null the input parameters may be modified upon return
1491 function findVariantLink( &$link, &$nt ) {
1492 $this->mConverter->findVariantLink($link, $nt);
1496 * If a language supports multiple variants, converts text
1497 * into an array of all possible variants of the text:
1498 * 'variant' => text in that variant
1501 function convertLinkToAllVariants($text){
1502 return $this->mConverter->convertLinkToAllVariants($text);
1507 * returns language specific options used by User::getPageRenderHash()
1508 * for example, the preferred language variant
1513 function getExtraHashOptions() {
1514 return $this->mConverter->getExtraHashOptions();
1518 * for languages that support multiple variants, the title of an
1519 * article may be displayed differently in different variants. this
1520 * function returns the apporiate title defined in the body of the article.
1524 function getParsedTitle() {
1525 return $this->mConverter->getParsedTitle();
1529 * Enclose a string with the "no conversion" tag. This is used by
1530 * various functions in the Parser
1532 * @param string $text text to be tagged for no conversion
1533 * @return string the tagged text
1535 function markNoConversion( $text, $noParse=false ) {
1536 return $this->mConverter->markNoConversion( $text, $noParse );
1540 * A regular expression to match legal word-trailing characters
1541 * which should be merged onto a link of the form [[foo]]bar.
1546 function linkTrail() {
1548 return $this->linkTrail;
1551 function getLangObj() {
1556 * Get the RFC 3066 code for this language object
1558 function getCode() {
1559 return $this->mCode;
1562 function setCode( $code ) {
1563 $this->mCode = $code;
1566 static function getFileName( $prefix = 'Language', $code, $suffix = '.php' ) {
1567 return $prefix . str_replace( '-', '_', ucfirst( $code ) ) . $suffix;
1570 static function getMessagesFileName( $code ) {
1572 return self::getFileName( "$IP/languages/messages/Messages", $code, '.php' );
1575 static function getClassFileName( $code ) {
1577 return self::getFileName( "$IP/languages/classes/Language", $code, '.php' );
1580 static function getLocalisationArray( $code, $disableCache = false ) {
1581 self::loadLocalisation( $code, $disableCache );
1582 return self::$mLocalisationCache[$code];
1586 * Load localisation data for a given code into the static cache
1588 * @return array Dependencies, map of filenames to mtimes
1590 static function loadLocalisation( $code, $disableCache = false ) {
1591 static $recursionGuard = array();
1595 throw new MWException( "Invalid language code requested" );
1598 if ( !$disableCache ) {
1599 # Try the per-process cache
1600 if ( isset( self::$mLocalisationCache[$code] ) ) {
1601 return self::$mLocalisationCache[$code]['deps'];
1604 wfProfileIn( __METHOD__ );
1606 # Try the serialized directory
1607 $cache = wfGetPrecompiledData( self::getFileName( "Messages", $code, '.ser' ) );
1609 self::$mLocalisationCache[$code] = $cache;
1610 wfDebug( "Language::loadLocalisation(): got localisation for $code from precompiled data file\n" );
1611 wfProfileOut( __METHOD__ );
1612 return self::$mLocalisationCache[$code]['deps'];
1615 # Try the global cache
1616 $memcKey = wfMemcKey('localisation', $code );
1617 $cache = $wgMemc->get( $memcKey );
1619 # Check file modification times
1620 foreach ( $cache['deps'] as $file => $mtime ) {
1621 if ( !file_exists( $file ) || filemtime( $file ) > $mtime ) {
1625 if ( self::isLocalisationOutOfDate( $cache ) ) {
1626 $wgMemc->delete( $memcKey );
1628 wfDebug( "Language::loadLocalisation(): localisation cache for $code had expired due to update of $file\n" );
1630 self::$mLocalisationCache[$code] = $cache;
1631 wfDebug( "Language::loadLocalisation(): got localisation for $code from cache\n" );
1632 wfProfileOut( __METHOD__ );
1633 return $cache['deps'];
1637 wfProfileIn( __METHOD__ );
1640 # Default fallback, may be overridden when the messages file is included
1641 if ( $code != 'en' ) {
1647 # Load the primary localisation from the source file
1648 $filename = self::getMessagesFileName( $code );
1649 if ( !file_exists( $filename ) ) {
1650 wfDebug( "Language::loadLocalisation(): no localisation file for $code, using implicit fallback to en\n" );
1654 $deps = array( $filename => filemtime( $filename ) );
1655 require( $filename );
1656 $cache = compact( self::$mLocalisationKeys );
1657 wfDebug( "Language::loadLocalisation(): got localisation for $code from source\n" );
1660 if ( !empty( $fallback ) ) {
1661 # Load the fallback localisation, with a circular reference guard
1662 if ( isset( $recursionGuard[$code] ) ) {
1663 throw new MWException( "Error: Circular fallback reference in language code $code" );
1665 $recursionGuard[$code] = true;
1666 $newDeps = self::loadLocalisation( $fallback, $disableCache );
1667 unset( $recursionGuard[$code] );
1669 $secondary = self::$mLocalisationCache[$fallback];
1670 $deps = array_merge( $deps, $newDeps );
1672 # Merge the fallback localisation with the current localisation
1673 foreach ( self::$mLocalisationKeys as $key ) {
1674 if ( isset( $cache[$key] ) ) {
1675 if ( isset( $secondary[$key] ) ) {
1676 if ( in_array( $key, self::$mMergeableMapKeys ) ) {
1677 $cache[$key] = $cache[$key] + $secondary[$key];
1678 } elseif ( in_array( $key, self::$mMergeableListKeys ) ) {
1679 $cache[$key] = array_merge( $secondary[$key], $cache[$key] );
1680 } elseif ( in_array( $key, self::$mMergeableAliasListKeys ) ) {
1681 $cache[$key] = array_merge_recursive( $cache[$key], $secondary[$key] );
1685 $cache[$key] = $secondary[$key];
1689 # Merge bookstore lists if requested
1690 if ( !empty( $cache['bookstoreList']['inherit'] ) ) {
1691 $cache['bookstoreList'] = array_merge( $cache['bookstoreList'], $secondary['bookstoreList'] );
1693 if ( isset( $cache['bookstoreList']['inherit'] ) ) {
1694 unset( $cache['bookstoreList']['inherit'] );
1698 # Add dependencies to the cache entry
1699 $cache['deps'] = $deps;
1701 # Replace spaces with underscores in namespace names
1702 $cache['namespaceNames'] = str_replace( ' ', '_', $cache['namespaceNames'] );
1704 # Save to both caches
1705 self::$mLocalisationCache[$code] = $cache;
1706 if ( !$disableCache ) {
1707 $wgMemc->set( $memcKey, $cache );
1710 wfProfileOut( __METHOD__ );
1715 * Test if a given localisation cache is out of date with respect to the
1716 * source Messages files. This is done automatically for the global cache
1717 * in $wgMemc, but is only done on certain occasions for the serialized
1720 * @param $cache mixed Either a language code or a cache array
1722 static function isLocalisationOutOfDate( $cache ) {
1723 if ( !is_array( $cache ) ) {
1724 self::loadLocalisation( $cache );
1725 $cache = self::$mLocalisationCache[$cache];
1728 foreach ( $cache['deps'] as $file => $mtime ) {
1729 if ( !file_exists( $file ) || filemtime( $file ) > $mtime ) {
1738 * Get the fallback for a given language
1740 static function getFallbackFor( $code ) {
1741 self::loadLocalisation( $code );
1742 return self::$mLocalisationCache[$code]['fallback'];
1746 * Get all messages for a given language
1748 static function getMessagesFor( $code ) {
1749 self::loadLocalisation( $code );
1750 return self::$mLocalisationCache[$code]['messages'];
1754 * Get a message for a given language
1756 static function getMessageFor( $key, $code ) {
1757 self::loadLocalisation( $code );
1758 return isset( self::$mLocalisationCache[$code]['messages'][$key] ) ? self::$mLocalisationCache[$code]['messages'][$key] : null;
1762 * Load localisation data for this object
1765 if ( !$this->mLoaded ) {
1766 self::loadLocalisation( $this->getCode() );
1767 $cache =& self::$mLocalisationCache[$this->getCode()];
1768 foreach ( self::$mLocalisationKeys as $key ) {
1769 $this->$key = $cache[$key];
1771 $this->mLoaded = true;
1773 $this->fixUpSettings();
1778 * Do any necessary post-cache-load settings adjustment
1780 function fixUpSettings() {
1781 global $wgExtraNamespaces, $wgMetaNamespace, $wgMetaNamespaceTalk,
1782 $wgNamespaceAliases, $wgAmericanDates;
1783 wfProfileIn( __METHOD__ );
1784 if ( $wgExtraNamespaces ) {
1785 $this->namespaceNames = $wgExtraNamespaces + $this->namespaceNames;
1788 $this->namespaceNames[NS_PROJECT] = $wgMetaNamespace;
1789 if ( $wgMetaNamespaceTalk ) {
1790 $this->namespaceNames[NS_PROJECT_TALK] = $wgMetaNamespaceTalk;
1792 $talk = $this->namespaceNames[NS_PROJECT_TALK];
1793 $talk = str_replace( '$1', $wgMetaNamespace, $talk );
1795 # Allow grammar transformations
1796 # Allowing full message-style parsing would make simple requests
1797 # such as action=raw much more expensive than they need to be.
1798 # This will hopefully cover most cases.
1799 $talk = preg_replace_callback( '/{{grammar:(.*?)\|(.*?)}}/i',
1800 array( &$this, 'replaceGrammarInNamespace' ), $talk );
1801 $talk = str_replace( ' ', '_', $talk );
1802 $this->namespaceNames[NS_PROJECT_TALK] = $talk;
1805 # The above mixing may leave namespaces out of canonical order.
1806 # Re-order by namespace ID number...
1807 ksort( $this->namespaceNames );
1809 # Put namespace names and aliases into a hashtable.
1810 # If this is too slow, then we should arrange it so that it is done
1811 # before caching. The catch is that at pre-cache time, the above
1812 # class-specific fixup hasn't been done.
1813 $this->mNamespaceIds = array();
1814 foreach ( $this->namespaceNames as $index => $name ) {
1815 $this->mNamespaceIds[$this->lc($name)] = $index;
1817 if ( $this->namespaceAliases ) {
1818 foreach ( $this->namespaceAliases as $name => $index ) {
1819 $this->mNamespaceIds[$this->lc($name)] = $index;
1822 if ( $wgNamespaceAliases ) {
1823 foreach ( $wgNamespaceAliases as $name => $index ) {
1824 $this->mNamespaceIds[$this->lc($name)] = $index;
1828 if ( $this->defaultDateFormat == 'dmy or mdy' ) {
1829 $this->defaultDateFormat = $wgAmericanDates ? 'mdy' : 'dmy';
1831 wfProfileOut( __METHOD__ );
1834 function replaceGrammarInNamespace( $m ) {
1835 return $this->convertGrammar( trim( $m[2] ), trim( $m[1] ) );
1838 static function getCaseMaps() {
1839 static $wikiUpperChars, $wikiLowerChars;
1840 if ( isset( $wikiUpperChars ) ) {
1841 return array( $wikiUpperChars, $wikiLowerChars );
1844 wfProfileIn( __METHOD__ );
1845 $arr = wfGetPrecompiledData( 'Utf8Case.ser' );
1846 if ( $arr === false ) {
1847 throw new MWException(
1848 "Utf8Case.ser is missing, please run \"make\" in the serialized directory\n" );
1851 wfProfileOut( __METHOD__ );
1852 return array( $wikiUpperChars, $wikiLowerChars );
1855 function formatTimePeriod( $seconds ) {
1856 if ( $seconds < 10 ) {
1857 return $this->formatNum( sprintf( "%.1f", $seconds ) ) . wfMsg( 'seconds-abbrev' );
1858 } elseif ( $seconds < 60 ) {
1859 return $this->formatNum( round( $seconds ) ) . wfMsg( 'seconds-abbrev' );
1860 } elseif ( $seconds < 3600 ) {
1861 return $this->formatNum( floor( $seconds / 60 ) ) . wfMsg( 'minutes-abbrev' ) .
1862 $this->formatNum( round( fmod( $seconds, 60 ) ) ) . wfMsg( 'seconds-abbrev' );
1864 $hours = floor( $seconds / 3600 );
1865 $minutes = floor( ( $seconds - $hours * 3600 ) / 60 );
1866 $secondsPart = round( $seconds - $hours * 3600 - $minutes * 60 );
1867 return $this->formatNum( $hours ) . wfMsg( 'hours-abbrev' ) .
1868 $this->formatNum( $minutes ) . wfMsg( 'minutes-abbrev' ) .
1869 $this->formatNum( $secondsPart ) . wfMsg( 'seconds-abbrev' );
1873 function formatBitrate( $bps ) {
1874 $units = array( 'bps', 'kbps', 'Mbps', 'Gbps' );
1876 return $this->formatNum( $bps ) . $units[0];
1878 $unitIndex = floor( log10( $bps ) / 3 );
1879 $mantissa = $bps / pow( 1000, $unitIndex );
1880 if ( $mantissa < 10 ) {
1881 $mantissa = round( $mantissa, 1 );
1883 $mantissa = round( $mantissa );
1885 return $this->formatNum( $mantissa ) . $units[$unitIndex];
1889 * Format a size in bytes for output, using an appropriate
1890 * unit (B, KB, MB or GB) according to the magnitude in question
1892 * @param $size Size to format
1893 * @return string Plain text (not HTML)
1895 function formatSize( $size ) {
1896 // For small sizes no decimal places necessary
1898 if( $size > 1024 ) {
1899 $size = $size / 1024;
1900 if( $size > 1024 ) {
1901 $size = $size / 1024;
1902 // For MB and bigger two decimal places are smarter
1904 if( $size > 1024 ) {
1905 $size = $size / 1024;
1906 $msg = 'size-gigabytes';
1908 $msg = 'size-megabytes';
1911 $msg = 'size-kilobytes';
1914 $msg = 'size-bytes';
1916 $size = round( $size, $round );
1917 $text = $this->getMessageFromDB( $msg );
1918 return str_replace( '$1', $this->formatNum( $size ), $text );