MediaWiki  1.23.2
Language.php
Go to the documentation of this file.
1 <?php
28 if ( !defined( 'MEDIAWIKI' ) ) {
29  echo "This file is part of MediaWiki, it is not a valid entry point.\n";
30  exit( 1 );
31 }
32 
33 if ( function_exists( 'mb_strtoupper' ) ) {
34  mb_internal_encoding( 'UTF-8' );
35 }
36 
46  public $mLang;
47  function __construct( $langobj ) { $this->mLang = $langobj; }
48  function autoConvert( $text, $variant = false ) { return $text; }
49  function autoConvertToAllVariants( $text ) { return array( $this->mLang->getCode() => $text ); }
50  function convert( $t ) { return $t; }
51  function convertTo( $text, $variant ) { return $text; }
52  function convertTitle( $t ) { return $t->getPrefixedText(); }
53  function convertNamespace( $ns ) { return $this->mLang->getFormattedNsText( $ns ); }
54  function getVariants() { return array( $this->mLang->getCode() ); }
55  function getVariantFallbacks( $variant ) { return $this->mLang->getCode(); }
56  function getPreferredVariant() { return $this->mLang->getCode(); }
57  function getDefaultVariant() { return $this->mLang->getCode(); }
58  function getURLVariant() { return ''; }
59  function getConvRuleTitle() { return false; }
60  function findVariantLink( &$l, &$n, $ignoreOtherCond = false ) { }
61  function getExtraHashOptions() { return ''; }
62  function getParsedTitle() { return ''; }
63  function markNoConversion( $text, $noParse = false ) { return $text; }
64  function convertCategoryKey( $key ) { return $key; }
66  function armourMath( $text ) { return $text; }
67  function validateVariant( $variant = null ) { return $variant === $this->mLang->getCode() ? $variant : null; }
68  function translate( $text, $variant ) { return $text; }
69 }
70 
75 class Language {
76 
80  public $mConverter;
81 
82  public $mVariants, $mCode, $mLoaded = false;
83  public $mMagicExtensions = array(), $mMagicHookDone = false;
84  private $mHtmlCode = null, $mParentLanguage = false;
85 
86  public $dateFormatStrings = array();
88 
90 
94  public $transformData = array();
95 
99  static public $dataCache;
100 
101  static public $mLangObjCache = array();
102 
103  static public $mWeekdayMsgs = array(
104  'sunday', 'monday', 'tuesday', 'wednesday', 'thursday',
105  'friday', 'saturday'
106  );
107 
108  static public $mWeekdayAbbrevMsgs = array(
109  'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'
110  );
111 
112  static public $mMonthMsgs = array(
113  'january', 'february', 'march', 'april', 'may_long', 'june',
114  'july', 'august', 'september', 'october', 'november',
115  'december'
116  );
117  static public $mMonthGenMsgs = array(
118  'january-gen', 'february-gen', 'march-gen', 'april-gen', 'may-gen', 'june-gen',
119  'july-gen', 'august-gen', 'september-gen', 'october-gen', 'november-gen',
120  'december-gen'
121  );
122  static public $mMonthAbbrevMsgs = array(
123  'jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug',
124  'sep', 'oct', 'nov', 'dec'
125  );
126 
127  static public $mIranianCalendarMonthMsgs = array(
128  'iranian-calendar-m1', 'iranian-calendar-m2', 'iranian-calendar-m3',
129  'iranian-calendar-m4', 'iranian-calendar-m5', 'iranian-calendar-m6',
130  'iranian-calendar-m7', 'iranian-calendar-m8', 'iranian-calendar-m9',
131  'iranian-calendar-m10', 'iranian-calendar-m11', 'iranian-calendar-m12'
132  );
133 
134  static public $mHebrewCalendarMonthMsgs = array(
135  'hebrew-calendar-m1', 'hebrew-calendar-m2', 'hebrew-calendar-m3',
136  'hebrew-calendar-m4', 'hebrew-calendar-m5', 'hebrew-calendar-m6',
137  'hebrew-calendar-m7', 'hebrew-calendar-m8', 'hebrew-calendar-m9',
138  'hebrew-calendar-m10', 'hebrew-calendar-m11', 'hebrew-calendar-m12',
139  'hebrew-calendar-m6a', 'hebrew-calendar-m6b'
140  );
141 
142  static public $mHebrewCalendarMonthGenMsgs = array(
143  'hebrew-calendar-m1-gen', 'hebrew-calendar-m2-gen', 'hebrew-calendar-m3-gen',
144  'hebrew-calendar-m4-gen', 'hebrew-calendar-m5-gen', 'hebrew-calendar-m6-gen',
145  'hebrew-calendar-m7-gen', 'hebrew-calendar-m8-gen', 'hebrew-calendar-m9-gen',
146  'hebrew-calendar-m10-gen', 'hebrew-calendar-m11-gen', 'hebrew-calendar-m12-gen',
147  'hebrew-calendar-m6a-gen', 'hebrew-calendar-m6b-gen'
148  );
149 
150  static public $mHijriCalendarMonthMsgs = array(
151  'hijri-calendar-m1', 'hijri-calendar-m2', 'hijri-calendar-m3',
152  'hijri-calendar-m4', 'hijri-calendar-m5', 'hijri-calendar-m6',
153  'hijri-calendar-m7', 'hijri-calendar-m8', 'hijri-calendar-m9',
154  'hijri-calendar-m10', 'hijri-calendar-m11', 'hijri-calendar-m12'
155  );
156 
161  static public $durationIntervals = array(
162  'millennia' => 31556952000,
163  'centuries' => 3155695200,
164  'decades' => 315569520,
165  'years' => 31556952, // 86400 * ( 365 + ( 24 * 3 + 25 ) / 400 )
166  'weeks' => 604800,
167  'days' => 86400,
168  'hours' => 3600,
169  'minutes' => 60,
170  'seconds' => 1,
171  );
172 
179  static private $fallbackLanguageCache = array();
180 
186  static function factory( $code ) {
187  global $wgDummyLanguageCodes, $wgLangObjCacheSize;
188 
189  if ( isset( $wgDummyLanguageCodes[$code] ) ) {
190  $code = $wgDummyLanguageCodes[$code];
191  }
192 
193  // get the language object to process
194  $langObj = isset( self::$mLangObjCache[$code] )
195  ? self::$mLangObjCache[$code]
196  : self::newFromCode( $code );
197 
198  // merge the language object in to get it up front in the cache
199  self::$mLangObjCache = array_merge( array( $code => $langObj ), self::$mLangObjCache );
200  // get rid of the oldest ones in case we have an overflow
201  self::$mLangObjCache = array_slice( self::$mLangObjCache, 0, $wgLangObjCacheSize, true );
202 
203  return $langObj;
204  }
205 
212  protected static function newFromCode( $code ) {
213  // Protect against path traversal below
214  if ( !Language::isValidCode( $code )
215  || strcspn( $code, ":/\\\000" ) !== strlen( $code )
216  ) {
217  throw new MWException( "Invalid language code \"$code\"" );
218  }
219 
220  if ( !Language::isValidBuiltInCode( $code ) ) {
221  // It's not possible to customise this code with class files, so
222  // just return a Language object. This is to support uselang= hacks.
223  $lang = new Language;
224  $lang->setCode( $code );
225  return $lang;
226  }
227 
228  // Check if there is a language class for the code
229  $class = self::classFromCode( $code );
230  self::preloadLanguageClass( $class );
231  if ( class_exists( $class ) ) {
232  $lang = new $class;
233  return $lang;
234  }
235 
236  // Keep trying the fallback list until we find an existing class
237  $fallbacks = Language::getFallbacksFor( $code );
238  foreach ( $fallbacks as $fallbackCode ) {
239  if ( !Language::isValidBuiltInCode( $fallbackCode ) ) {
240  throw new MWException( "Invalid fallback '$fallbackCode' in fallback sequence for '$code'" );
241  }
242 
243  $class = self::classFromCode( $fallbackCode );
244  self::preloadLanguageClass( $class );
245  if ( class_exists( $class ) ) {
246  $lang = Language::newFromCode( $fallbackCode );
247  $lang->setCode( $code );
248  return $lang;
249  }
250  }
251 
252  throw new MWException( "Invalid fallback sequence for language '$code'" );
253  }
254 
263  public static function isSupportedLanguage( $code ) {
264  return self::isValidBuiltInCode( $code )
265  && ( is_readable( self::getMessagesFileName( $code ) )
266  || is_readable( self::getJsonMessagesFileName( $code ) )
267  );
268  }
269 
285  public static function isWellFormedLanguageTag( $code, $lenient = false ) {
286  $alpha = '[a-z]';
287  $digit = '[0-9]';
288  $alphanum = '[a-z0-9]';
289  $x = 'x'; # private use singleton
290  $singleton = '[a-wy-z]'; # other singleton
291  $s = $lenient ? '[-_]' : '-';
292 
293  $language = "$alpha{2,8}|$alpha{2,3}$s$alpha{3}";
294  $script = "$alpha{4}"; # ISO 15924
295  $region = "(?:$alpha{2}|$digit{3})"; # ISO 3166-1 alpha-2 or UN M.49
296  $variant = "(?:$alphanum{5,8}|$digit$alphanum{3})";
297  $extension = "$singleton(?:$s$alphanum{2,8})+";
298  $privateUse = "$x(?:$s$alphanum{1,8})+";
299 
300  # Define certain grandfathered codes, since otherwise the regex is pretty useless.
301  # Since these are limited, this is safe even later changes to the registry --
302  # the only oddity is that it might change the type of the tag, and thus
303  # the results from the capturing groups.
304  # http://www.iana.org/assignments/language-subtag-registry
305 
306  $grandfathered = "en{$s}GB{$s}oed"
307  . "|i{$s}(?:ami|bnn|default|enochian|hak|klingon|lux|mingo|navajo|pwn|tao|tay|tsu)"
308  . "|no{$s}(?:bok|nyn)"
309  . "|sgn{$s}(?:BE{$s}(?:fr|nl)|CH{$s}de)"
310  . "|zh{$s}min{$s}nan";
311 
312  $variantList = "$variant(?:$s$variant)*";
313  $extensionList = "$extension(?:$s$extension)*";
314 
315  $langtag = "(?:($language)"
316  . "(?:$s$script)?"
317  . "(?:$s$region)?"
318  . "(?:$s$variantList)?"
319  . "(?:$s$extensionList)?"
320  . "(?:$s$privateUse)?)";
321 
322  # The final breakdown, with capturing groups for each of these components
323  # The variants, extensions, grandfathered, and private-use may have interior '-'
324 
325  $root = "^(?:$langtag|$privateUse|$grandfathered)$";
326 
327  return (bool)preg_match( "/$root/", strtolower( $code ) );
328  }
329 
339  public static function isValidCode( $code ) {
340  static $cache = array();
341  if ( isset( $cache[$code] ) ) {
342  return $cache[$code];
343  }
344  // People think language codes are html safe, so enforce it.
345  // Ideally we should only allow a-zA-Z0-9-
346  // but, .+ and other chars are often used for {{int:}} hacks
347  // see bugs 37564, 37587, 36938
348  $cache[$code] =
349  strcspn( $code, ":/\\\000&<>'\"" ) === strlen( $code )
350  && !preg_match( Title::getTitleInvalidRegex(), $code );
351 
352  return $cache[$code];
353  }
354 
365  public static function isValidBuiltInCode( $code ) {
366 
367  if ( !is_string( $code ) ) {
368  if ( is_object( $code ) ) {
369  $addmsg = " of class " . get_class( $code );
370  } else {
371  $addmsg = '';
372  }
373  $type = gettype( $code );
374  throw new MWException( __METHOD__ . " must be passed a string, $type given$addmsg" );
375  }
376 
377  return (bool)preg_match( '/^[a-z0-9-]{2,}$/i', $code );
378  }
379 
388  public static function isKnownLanguageTag( $tag ) {
389  static $coreLanguageNames;
390 
391  // Quick escape for invalid input to avoid exceptions down the line
392  // when code tries to process tags which are not valid at all.
393  if ( !self::isValidBuiltInCode( $tag ) ) {
394  return false;
395  }
396 
397  if ( $coreLanguageNames === null ) {
398  global $IP;
399  include "$IP/languages/Names.php";
400  }
401 
402  if ( isset( $coreLanguageNames[$tag] )
403  || self::fetchLanguageName( $tag, $tag ) !== ''
404  ) {
405  return true;
406  }
407 
408  return false;
409  }
410 
415  public static function classFromCode( $code ) {
416  if ( $code == 'en' ) {
417  return 'Language';
418  } else {
419  return 'Language' . str_replace( '-', '_', ucfirst( $code ) );
420  }
421  }
422 
428  public static function preloadLanguageClass( $class ) {
429  global $IP;
430 
431  if ( $class === 'Language' ) {
432  return;
433  }
434 
435  if ( file_exists( "$IP/languages/classes/$class.php" ) ) {
436  include_once "$IP/languages/classes/$class.php";
437  }
438  }
439 
445  public static function getLocalisationCache() {
446  if ( is_null( self::$dataCache ) ) {
447  global $wgLocalisationCacheConf;
448  $class = $wgLocalisationCacheConf['class'];
449  self::$dataCache = new $class( $wgLocalisationCacheConf );
450  }
451  return self::$dataCache;
452  }
453 
454  function __construct() {
455  $this->mConverter = new FakeConverter( $this );
456  // Set the code to the name of the descendant
457  if ( get_class( $this ) == 'Language' ) {
458  $this->mCode = 'en';
459  } else {
460  $this->mCode = str_replace( '_', '-', strtolower( substr( get_class( $this ), 8 ) ) );
461  }
463  }
464 
468  function __destruct() {
469  foreach ( $this as $name => $value ) {
470  unset( $this->$name );
471  }
472  }
473 
478  function initContLang() { }
479 
485  function getFallbackLanguageCode() {
486  wfDeprecated( __METHOD__, '1.19' );
487  return self::getFallbackFor( $this->mCode );
488  }
489 
494  function getFallbackLanguages() {
495  return self::getFallbacksFor( $this->mCode );
496  }
497 
502  function getBookstoreList() {
503  return self::$dataCache->getItem( $this->mCode, 'bookstoreList' );
504  }
505 
512  public function getNamespaces() {
513  if ( is_null( $this->namespaceNames ) ) {
514  global $wgMetaNamespace, $wgMetaNamespaceTalk, $wgExtraNamespaces;
515 
516  $this->namespaceNames = self::$dataCache->getItem( $this->mCode, 'namespaceNames' );
517  $validNamespaces = MWNamespace::getCanonicalNamespaces();
518 
519  $this->namespaceNames = $wgExtraNamespaces + $this->namespaceNames + $validNamespaces;
520 
521  $this->namespaceNames[NS_PROJECT] = $wgMetaNamespace;
522  if ( $wgMetaNamespaceTalk ) {
523  $this->namespaceNames[NS_PROJECT_TALK] = $wgMetaNamespaceTalk;
524  } else {
525  $talk = $this->namespaceNames[NS_PROJECT_TALK];
526  $this->namespaceNames[NS_PROJECT_TALK] =
527  $this->fixVariableInNamespace( $talk );
528  }
529 
530  # Sometimes a language will be localised but not actually exist on this wiki.
531  foreach ( $this->namespaceNames as $key => $text ) {
532  if ( !isset( $validNamespaces[$key] ) ) {
533  unset( $this->namespaceNames[$key] );
534  }
535  }
536 
537  # The above mixing may leave namespaces out of canonical order.
538  # Re-order by namespace ID number...
539  ksort( $this->namespaceNames );
540 
541  wfRunHooks( 'LanguageGetNamespaces', array( &$this->namespaceNames ) );
542  }
543  return $this->namespaceNames;
544  }
545 
550  public function setNamespaces( array $namespaces ) {
551  $this->namespaceNames = $namespaces;
552  $this->mNamespaceIds = null;
553  }
554 
558  public function resetNamespaces() {
559  $this->namespaceNames = null;
560  $this->mNamespaceIds = null;
561  $this->namespaceAliases = null;
562  }
563 
572  function getFormattedNamespaces() {
573  $ns = $this->getNamespaces();
574  foreach ( $ns as $k => $v ) {
575  $ns[$k] = strtr( $v, '_', ' ' );
576  }
577  return $ns;
578  }
579 
590  function getNsText( $index ) {
591  $ns = $this->getNamespaces();
592  return isset( $ns[$index] ) ? $ns[$index] : false;
593  }
594 
608  function getFormattedNsText( $index ) {
609  $ns = $this->getNsText( $index );
610  return strtr( $ns, '_', ' ' );
611  }
612 
621  function getGenderNsText( $index, $gender ) {
622  global $wgExtraGenderNamespaces;
623 
624  $ns = $wgExtraGenderNamespaces + self::$dataCache->getItem( $this->mCode, 'namespaceGenderAliases' );
625  return isset( $ns[$index][$gender] ) ? $ns[$index][$gender] : $this->getNsText( $index );
626  }
627 
634  function needsGenderDistinction() {
635  global $wgExtraGenderNamespaces, $wgExtraNamespaces;
636  if ( count( $wgExtraGenderNamespaces ) > 0 ) {
637  // $wgExtraGenderNamespaces overrides everything
638  return true;
639  } elseif ( isset( $wgExtraNamespaces[NS_USER] ) && isset( $wgExtraNamespaces[NS_USER_TALK] ) ) {
641  // $wgExtraNamespaces overrides any gender aliases specified in i18n files
642  return false;
643  } else {
644  // Check what is in i18n files
645  $aliases = self::$dataCache->getItem( $this->mCode, 'namespaceGenderAliases' );
646  return count( $aliases ) > 0;
647  }
648  }
649 
658  function getLocalNsIndex( $text ) {
659  $lctext = $this->lc( $text );
660  $ids = $this->getNamespaceIds();
661  return isset( $ids[$lctext] ) ? $ids[$lctext] : false;
662  }
663 
667  function getNamespaceAliases() {
668  if ( is_null( $this->namespaceAliases ) ) {
669  $aliases = self::$dataCache->getItem( $this->mCode, 'namespaceAliases' );
670  if ( !$aliases ) {
671  $aliases = array();
672  } else {
673  foreach ( $aliases as $name => $index ) {
674  if ( $index === NS_PROJECT_TALK ) {
675  unset( $aliases[$name] );
676  $name = $this->fixVariableInNamespace( $name );
677  $aliases[$name] = $index;
678  }
679  }
680  }
681 
682  global $wgExtraGenderNamespaces;
683  $genders = $wgExtraGenderNamespaces + (array)self::$dataCache->getItem( $this->mCode, 'namespaceGenderAliases' );
684  foreach ( $genders as $index => $forms ) {
685  foreach ( $forms as $alias ) {
686  $aliases[$alias] = $index;
687  }
688  }
689 
690  # Also add converted namespace names as aliases, to avoid confusion.
691  $convertedNames = array();
692  foreach ( $this->getVariants() as $variant ) {
693  if ( $variant === $this->mCode ) {
694  continue;
695  }
696  foreach ( $this->getNamespaces() as $ns => $_ ) {
697  $convertedNames[$this->getConverter()->convertNamespace( $ns, $variant )] = $ns;
698  }
699  }
700 
701  $this->namespaceAliases = $aliases + $convertedNames;
702  }
704  }
705 
709  function getNamespaceIds() {
710  if ( is_null( $this->mNamespaceIds ) ) {
712  # Put namespace names and aliases into a hashtable.
713  # If this is too slow, then we should arrange it so that it is done
714  # before caching. The catch is that at pre-cache time, the above
715  # class-specific fixup hasn't been done.
716  $this->mNamespaceIds = array();
717  foreach ( $this->getNamespaces() as $index => $name ) {
718  $this->mNamespaceIds[$this->lc( $name )] = $index;
719  }
720  foreach ( $this->getNamespaceAliases() as $name => $index ) {
721  $this->mNamespaceIds[$this->lc( $name )] = $index;
722  }
723  if ( $wgNamespaceAliases ) {
724  foreach ( $wgNamespaceAliases as $name => $index ) {
725  $this->mNamespaceIds[$this->lc( $name )] = $index;
726  }
727  }
728  }
729  return $this->mNamespaceIds;
730  }
731 
739  function getNsIndex( $text ) {
740  $lctext = $this->lc( $text );
741  $ns = MWNamespace::getCanonicalIndex( $lctext );
742  if ( $ns !== null ) {
743  return $ns;
744  }
745  $ids = $this->getNamespaceIds();
746  return isset( $ids[$lctext] ) ? $ids[$lctext] : false;
747  }
748 
756  function getVariantname( $code, $usemsg = true ) {
757  $msg = "variantname-$code";
758  if ( $usemsg && wfMessage( $msg )->exists() ) {
759  return $this->getMessageFromDB( $msg );
760  }
761  $name = self::fetchLanguageName( $code );
762  if ( $name ) {
763  return $name; # if it's defined as a language name, show that
764  } else {
765  # otherwise, output the language code
766  return $code;
767  }
768  }
769 
774  function specialPage( $name ) {
775  $aliases = $this->getSpecialPageAliases();
776  if ( isset( $aliases[$name][0] ) ) {
777  $name = $aliases[$name][0];
778  }
779  return $this->getNsText( NS_SPECIAL ) . ':' . $name;
780  }
781 
785  function getDatePreferences() {
786  return self::$dataCache->getItem( $this->mCode, 'datePreferences' );
787  }
788 
792  function getDateFormats() {
793  return self::$dataCache->getItem( $this->mCode, 'dateFormats' );
794  }
795 
799  function getDefaultDateFormat() {
800  $df = self::$dataCache->getItem( $this->mCode, 'defaultDateFormat' );
801  if ( $df === 'dmy or mdy' ) {
802  global $wgAmericanDates;
803  return $wgAmericanDates ? 'mdy' : 'dmy';
804  } else {
805  return $df;
806  }
807  }
808 
812  function getDatePreferenceMigrationMap() {
813  return self::$dataCache->getItem( $this->mCode, 'datePreferenceMigrationMap' );
814  }
815 
820  function getImageFile( $image ) {
821  return self::$dataCache->getSubitem( $this->mCode, 'imageFiles', $image );
822  }
823 
827  function getExtraUserToggles() {
828  return (array)self::$dataCache->getItem( $this->mCode, 'extraUserToggles' );
829  }
830 
835  function getUserToggle( $tog ) {
836  return $this->getMessageFromDB( "tog-$tog" );
837  }
838 
849  public static function getLanguageNames( $customisedOnly = false ) {
850  return self::fetchLanguageNames( null, $customisedOnly ? 'mwfile' : 'mw' );
851  }
852 
862  public static function getTranslatedLanguageNames( $code ) {
863  return self::fetchLanguageNames( $code, 'all' );
864  }
865 
877  public static function fetchLanguageNames( $inLanguage = null, $include = 'mw' ) {
878  global $wgExtraLanguageNames;
879  static $coreLanguageNames;
880 
881  if ( $coreLanguageNames === null ) {
882  global $IP;
883  include "$IP/languages/Names.php";
884  }
885 
886  $names = array();
887 
888  if ( $inLanguage ) {
889  # TODO: also include when $inLanguage is null, when this code is more efficient
890  wfRunHooks( 'LanguageGetTranslatedLanguageNames', array( &$names, $inLanguage ) );
891  }
892 
893  $mwNames = $wgExtraLanguageNames + $coreLanguageNames;
894  foreach ( $mwNames as $mwCode => $mwName ) {
895  # - Prefer own MediaWiki native name when not using the hook
896  # - For other names just add if not added through the hook
897  if ( $mwCode === $inLanguage || !isset( $names[$mwCode] ) ) {
898  $names[$mwCode] = $mwName;
899  }
900  }
901 
902  if ( $include === 'all' ) {
903  return $names;
904  }
905 
906  $returnMw = array();
907  $coreCodes = array_keys( $mwNames );
908  foreach ( $coreCodes as $coreCode ) {
909  $returnMw[$coreCode] = $names[$coreCode];
910  }
911 
912  if ( $include === 'mwfile' ) {
913  $namesMwFile = array();
914  # We do this using a foreach over the codes instead of a directory
915  # loop so that messages files in extensions will work correctly.
916  foreach ( $returnMw as $code => $value ) {
917  if ( is_readable( self::getMessagesFileName( $code ) )
918  || is_readable( self::getJsonMessagesFileName( $code ) )
919  ) {
920  $namesMwFile[$code] = $names[$code];
921  }
922  }
923 
924  return $namesMwFile;
925  }
926 
927  # 'mw' option; default if it's not one of the other two options (all/mwfile)
928  return $returnMw;
929  }
930 
938  public static function fetchLanguageName( $code, $inLanguage = null, $include = 'all' ) {
939  $code = strtolower( $code );
940  $array = self::fetchLanguageNames( $inLanguage, $include );
941  return !array_key_exists( $code, $array ) ? '' : $array[$code];
942  }
943 
950  function getMessageFromDB( $msg ) {
951  return wfMessage( $msg )->inLanguage( $this )->text();
952  }
953 
961  function getLanguageName( $code ) {
962  return self::fetchLanguageName( $code );
963  }
964 
969  function getMonthName( $key ) {
970  return $this->getMessageFromDB( self::$mMonthMsgs[$key - 1] );
971  }
972 
976  function getMonthNamesArray() {
977  $monthNames = array( '' );
978  for ( $i = 1; $i < 13; $i++ ) {
979  $monthNames[] = $this->getMonthName( $i );
980  }
981  return $monthNames;
982  }
983 
988  function getMonthNameGen( $key ) {
989  return $this->getMessageFromDB( self::$mMonthGenMsgs[$key - 1] );
990  }
991 
996  function getMonthAbbreviation( $key ) {
997  return $this->getMessageFromDB( self::$mMonthAbbrevMsgs[$key - 1] );
998  }
999 
1003  function getMonthAbbreviationsArray() {
1004  $monthNames = array( '' );
1005  for ( $i = 1; $i < 13; $i++ ) {
1006  $monthNames[] = $this->getMonthAbbreviation( $i );
1007  }
1008  return $monthNames;
1009  }
1010 
1015  function getWeekdayName( $key ) {
1016  return $this->getMessageFromDB( self::$mWeekdayMsgs[$key - 1] );
1017  }
1018 
1023  function getWeekdayAbbreviation( $key ) {
1024  return $this->getMessageFromDB( self::$mWeekdayAbbrevMsgs[$key - 1] );
1025  }
1026 
1031  function getIranianCalendarMonthName( $key ) {
1032  return $this->getMessageFromDB( self::$mIranianCalendarMonthMsgs[$key - 1] );
1033  }
1034 
1039  function getHebrewCalendarMonthName( $key ) {
1040  return $this->getMessageFromDB( self::$mHebrewCalendarMonthMsgs[$key - 1] );
1041  }
1042 
1047  function getHebrewCalendarMonthNameGen( $key ) {
1048  return $this->getMessageFromDB( self::$mHebrewCalendarMonthGenMsgs[$key - 1] );
1049  }
1050 
1055  function getHijriCalendarMonthName( $key ) {
1056  return $this->getMessageFromDB( self::$mHijriCalendarMonthMsgs[$key - 1] );
1057  }
1058 
1124  function sprintfDate( $format, $ts, DateTimeZone $zone = null ) {
1125  $s = '';
1126  $raw = false;
1127  $roman = false;
1128  $hebrewNum = false;
1129  $dateTimeObj = false;
1130  $rawToggle = false;
1131  $iranian = false;
1132  $hebrew = false;
1133  $hijri = false;
1134  $thai = false;
1135  $minguo = false;
1136  $tenno = false;
1137 
1138  if ( strlen( $ts ) !== 14 ) {
1139  throw new MWException( __METHOD__ . ": The timestamp $ts should have 14 characters" );
1140  }
1141 
1142  if ( !ctype_digit( $ts ) ) {
1143  throw new MWException( __METHOD__ . ": The timestamp $ts should be a number" );
1144  }
1145 
1146  for ( $p = 0; $p < strlen( $format ); $p++ ) {
1147  $num = false;
1148  $code = $format[$p];
1149  if ( $code == 'x' && $p < strlen( $format ) - 1 ) {
1150  $code .= $format[++$p];
1151  }
1152 
1153  if ( ( $code === 'xi' || $code == 'xj' || $code == 'xk' || $code == 'xm' || $code == 'xo' || $code == 'xt' ) && $p < strlen( $format ) - 1 ) {
1154  $code .= $format[++$p];
1155  }
1156 
1157  switch ( $code ) {
1158  case 'xx':
1159  $s .= 'x';
1160  break;
1161  case 'xn':
1162  $raw = true;
1163  break;
1164  case 'xN':
1165  $rawToggle = !$rawToggle;
1166  break;
1167  case 'xr':
1168  $roman = true;
1169  break;
1170  case 'xh':
1171  $hebrewNum = true;
1172  break;
1173  case 'xg':
1174  $s .= $this->getMonthNameGen( substr( $ts, 4, 2 ) );
1175  break;
1176  case 'xjx':
1177  if ( !$hebrew ) {
1178  $hebrew = self::tsToHebrew( $ts );
1179  }
1180  $s .= $this->getHebrewCalendarMonthNameGen( $hebrew[1] );
1181  break;
1182  case 'd':
1183  $num = substr( $ts, 6, 2 );
1184  break;
1185  case 'D':
1186  if ( !$dateTimeObj ) {
1187  $dateTimeObj = DateTime::createFromFormat(
1188  'YmdHis', $ts, $zone ?: new DateTimeZone( 'UTC' )
1189  );
1190  }
1191  $s .= $this->getWeekdayAbbreviation( $dateTimeObj->format( 'w' ) + 1 );
1192  break;
1193  case 'j':
1194  $num = intval( substr( $ts, 6, 2 ) );
1195  break;
1196  case 'xij':
1197  if ( !$iranian ) {
1198  $iranian = self::tsToIranian( $ts );
1199  }
1200  $num = $iranian[2];
1201  break;
1202  case 'xmj':
1203  if ( !$hijri ) {
1204  $hijri = self::tsToHijri( $ts );
1205  }
1206  $num = $hijri[2];
1207  break;
1208  case 'xjj':
1209  if ( !$hebrew ) {
1210  $hebrew = self::tsToHebrew( $ts );
1211  }
1212  $num = $hebrew[2];
1213  break;
1214  case 'l':
1215  if ( !$dateTimeObj ) {
1216  $dateTimeObj = DateTime::createFromFormat(
1217  'YmdHis', $ts, $zone ?: new DateTimeZone( 'UTC' )
1218  );
1219  }
1220  $s .= $this->getWeekdayName( $dateTimeObj->format( 'w' ) + 1 );
1221  break;
1222  case 'F':
1223  $s .= $this->getMonthName( substr( $ts, 4, 2 ) );
1224  break;
1225  case 'xiF':
1226  if ( !$iranian ) {
1227  $iranian = self::tsToIranian( $ts );
1228  }
1229  $s .= $this->getIranianCalendarMonthName( $iranian[1] );
1230  break;
1231  case 'xmF':
1232  if ( !$hijri ) {
1233  $hijri = self::tsToHijri( $ts );
1234  }
1235  $s .= $this->getHijriCalendarMonthName( $hijri[1] );
1236  break;
1237  case 'xjF':
1238  if ( !$hebrew ) {
1239  $hebrew = self::tsToHebrew( $ts );
1240  }
1241  $s .= $this->getHebrewCalendarMonthName( $hebrew[1] );
1242  break;
1243  case 'm':
1244  $num = substr( $ts, 4, 2 );
1245  break;
1246  case 'M':
1247  $s .= $this->getMonthAbbreviation( substr( $ts, 4, 2 ) );
1248  break;
1249  case 'n':
1250  $num = intval( substr( $ts, 4, 2 ) );
1251  break;
1252  case 'xin':
1253  if ( !$iranian ) {
1254  $iranian = self::tsToIranian( $ts );
1255  }
1256  $num = $iranian[1];
1257  break;
1258  case 'xmn':
1259  if ( !$hijri ) {
1260  $hijri = self::tsToHijri ( $ts );
1261  }
1262  $num = $hijri[1];
1263  break;
1264  case 'xjn':
1265  if ( !$hebrew ) {
1266  $hebrew = self::tsToHebrew( $ts );
1267  }
1268  $num = $hebrew[1];
1269  break;
1270  case 'xjt':
1271  if ( !$hebrew ) {
1272  $hebrew = self::tsToHebrew( $ts );
1273  }
1274  $num = $hebrew[3];
1275  break;
1276  case 'Y':
1277  $num = substr( $ts, 0, 4 );
1278  break;
1279  case 'xiY':
1280  if ( !$iranian ) {
1281  $iranian = self::tsToIranian( $ts );
1282  }
1283  $num = $iranian[0];
1284  break;
1285  case 'xmY':
1286  if ( !$hijri ) {
1287  $hijri = self::tsToHijri( $ts );
1288  }
1289  $num = $hijri[0];
1290  break;
1291  case 'xjY':
1292  if ( !$hebrew ) {
1293  $hebrew = self::tsToHebrew( $ts );
1294  }
1295  $num = $hebrew[0];
1296  break;
1297  case 'xkY':
1298  if ( !$thai ) {
1299  $thai = self::tsToYear( $ts, 'thai' );
1300  }
1301  $num = $thai[0];
1302  break;
1303  case 'xoY':
1304  if ( !$minguo ) {
1305  $minguo = self::tsToYear( $ts, 'minguo' );
1306  }
1307  $num = $minguo[0];
1308  break;
1309  case 'xtY':
1310  if ( !$tenno ) {
1311  $tenno = self::tsToYear( $ts, 'tenno' );
1312  }
1313  $num = $tenno[0];
1314  break;
1315  case 'y':
1316  $num = substr( $ts, 2, 2 );
1317  break;
1318  case 'xiy':
1319  if ( !$iranian ) {
1320  $iranian = self::tsToIranian( $ts );
1321  }
1322  $num = substr( $iranian[0], -2 );
1323  break;
1324  case 'a':
1325  $s .= intval( substr( $ts, 8, 2 ) ) < 12 ? 'am' : 'pm';
1326  break;
1327  case 'A':
1328  $s .= intval( substr( $ts, 8, 2 ) ) < 12 ? 'AM' : 'PM';
1329  break;
1330  case 'g':
1331  $h = substr( $ts, 8, 2 );
1332  $num = $h % 12 ? $h % 12 : 12;
1333  break;
1334  case 'G':
1335  $num = intval( substr( $ts, 8, 2 ) );
1336  break;
1337  case 'h':
1338  $h = substr( $ts, 8, 2 );
1339  $num = sprintf( '%02d', $h % 12 ? $h % 12 : 12 );
1340  break;
1341  case 'H':
1342  $num = substr( $ts, 8, 2 );
1343  break;
1344  case 'i':
1345  $num = substr( $ts, 10, 2 );
1346  break;
1347  case 's':
1348  $num = substr( $ts, 12, 2 );
1349  break;
1350  case 'c':
1351  case 'r':
1352  case 'e':
1353  case 'O':
1354  case 'P':
1355  case 'T':
1356  // Pass through string from $dateTimeObj->format()
1357  if ( !$dateTimeObj ) {
1358  $dateTimeObj = DateTime::createFromFormat(
1359  'YmdHis', $ts, $zone ?: new DateTimeZone( 'UTC' )
1360  );
1361  }
1362  $s .= $dateTimeObj->format( $code );
1363  break;
1364  case 'w':
1365  case 'N':
1366  case 'z':
1367  case 'W':
1368  case 't':
1369  case 'L':
1370  case 'o':
1371  case 'U':
1372  case 'I':
1373  case 'Z':
1374  // Pass through number from $dateTimeObj->format()
1375  if ( !$dateTimeObj ) {
1376  $dateTimeObj = DateTime::createFromFormat(
1377  'YmdHis', $ts, $zone ?: new DateTimeZone( 'UTC' )
1378  );
1379  }
1380  $num = $dateTimeObj->format( $code );
1381  break;
1382  case '\\':
1383  # Backslash escaping
1384  if ( $p < strlen( $format ) - 1 ) {
1385  $s .= $format[++$p];
1386  } else {
1387  $s .= '\\';
1388  }
1389  break;
1390  case '"':
1391  # Quoted literal
1392  if ( $p < strlen( $format ) - 1 ) {
1393  $endQuote = strpos( $format, '"', $p + 1 );
1394  if ( $endQuote === false ) {
1395  # No terminating quote, assume literal "
1396  $s .= '"';
1397  } else {
1398  $s .= substr( $format, $p + 1, $endQuote - $p - 1 );
1399  $p = $endQuote;
1400  }
1401  } else {
1402  # Quote at end of string, assume literal "
1403  $s .= '"';
1404  }
1405  break;
1406  default:
1407  $s .= $format[$p];
1408  }
1409  if ( $num !== false ) {
1410  if ( $rawToggle || $raw ) {
1411  $s .= $num;
1412  $raw = false;
1413  } elseif ( $roman ) {
1414  $s .= Language::romanNumeral( $num );
1415  $roman = false;
1416  } elseif ( $hebrewNum ) {
1417  $s .= self::hebrewNumeral( $num );
1418  $hebrewNum = false;
1419  } else {
1420  $s .= $this->formatNum( $num, true );
1421  }
1422  }
1423  }
1424  return $s;
1425  }
1427  private static $GREG_DAYS = array( 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );
1428  private static $IRANIAN_DAYS = array( 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29 );
1429 
1442  private static function tsToIranian( $ts ) {
1443  $gy = substr( $ts, 0, 4 ) -1600;
1444  $gm = substr( $ts, 4, 2 ) -1;
1445  $gd = substr( $ts, 6, 2 ) -1;
1446 
1447  # Days passed from the beginning (including leap years)
1448  $gDayNo = 365 * $gy
1449  + floor( ( $gy + 3 ) / 4 )
1450  - floor( ( $gy + 99 ) / 100 )
1451  + floor( ( $gy + 399 ) / 400 );
1452 
1453  // Add days of the past months of this year
1454  for ( $i = 0; $i < $gm; $i++ ) {
1455  $gDayNo += self::$GREG_DAYS[$i];
1456  }
1457 
1458  // Leap years
1459  if ( $gm > 1 && ( ( $gy % 4 === 0 && $gy % 100 !== 0 || ( $gy % 400 == 0 ) ) ) ) {
1460  $gDayNo++;
1461  }
1462 
1463  // Days passed in current month
1464  $gDayNo += (int)$gd;
1465 
1466  $jDayNo = $gDayNo - 79;
1467 
1468  $jNp = floor( $jDayNo / 12053 );
1469  $jDayNo %= 12053;
1470 
1471  $jy = 979 + 33 * $jNp + 4 * floor( $jDayNo / 1461 );
1472  $jDayNo %= 1461;
1473 
1474  if ( $jDayNo >= 366 ) {
1475  $jy += floor( ( $jDayNo - 1 ) / 365 );
1476  $jDayNo = floor( ( $jDayNo - 1 ) % 365 );
1477  }
1478 
1479  for ( $i = 0; $i < 11 && $jDayNo >= self::$IRANIAN_DAYS[$i]; $i++ ) {
1480  $jDayNo -= self::$IRANIAN_DAYS[$i];
1481  }
1482 
1483  $jm = $i + 1;
1484  $jd = $jDayNo + 1;
1485 
1486  return array( $jy, $jm, $jd );
1487  }
1488 
1500  private static function tsToHijri( $ts ) {
1501  $year = substr( $ts, 0, 4 );
1502  $month = substr( $ts, 4, 2 );
1503  $day = substr( $ts, 6, 2 );
1504 
1505  $zyr = $year;
1506  $zd = $day;
1507  $zm = $month;
1508  $zy = $zyr;
1509 
1510  if (
1511  ( $zy > 1582 ) || ( ( $zy == 1582 ) && ( $zm > 10 ) ) ||
1512  ( ( $zy == 1582 ) && ( $zm == 10 ) && ( $zd > 14 ) )
1513  ) {
1514  $zjd = (int)( ( 1461 * ( $zy + 4800 + (int)( ( $zm - 14 ) / 12 ) ) ) / 4 ) +
1515  (int)( ( 367 * ( $zm - 2 - 12 * ( (int)( ( $zm - 14 ) / 12 ) ) ) ) / 12 ) -
1516  (int)( ( 3 * (int)( ( ( $zy + 4900 + (int)( ( $zm - 14 ) / 12 ) ) / 100 ) ) ) / 4 ) +
1517  $zd - 32075;
1518  } else {
1519  $zjd = 367 * $zy - (int)( ( 7 * ( $zy + 5001 + (int)( ( $zm - 9 ) / 7 ) ) ) / 4 ) +
1520  (int)( ( 275 * $zm ) / 9 ) + $zd + 1729777;
1521  }
1522 
1523  $zl = $zjd -1948440 + 10632;
1524  $zn = (int)( ( $zl - 1 ) / 10631 );
1525  $zl = $zl - 10631 * $zn + 354;
1526  $zj = ( (int)( ( 10985 - $zl ) / 5316 ) ) * ( (int)( ( 50 * $zl ) / 17719 ) ) + ( (int)( $zl / 5670 ) ) * ( (int)( ( 43 * $zl ) / 15238 ) );
1527  $zl = $zl - ( (int)( ( 30 - $zj ) / 15 ) ) * ( (int)( ( 17719 * $zj ) / 50 ) ) - ( (int)( $zj / 16 ) ) * ( (int)( ( 15238 * $zj ) / 43 ) ) + 29;
1528  $zm = (int)( ( 24 * $zl ) / 709 );
1529  $zd = $zl - (int)( ( 709 * $zm ) / 24 );
1530  $zy = 30 * $zn + $zj - 30;
1531 
1532  return array( $zy, $zm, $zd );
1533  }
1534 
1550  private static function tsToHebrew( $ts ) {
1551  # Parse date
1552  $year = substr( $ts, 0, 4 );
1553  $month = substr( $ts, 4, 2 );
1554  $day = substr( $ts, 6, 2 );
1555 
1556  # Calculate Hebrew year
1557  $hebrewYear = $year + 3760;
1558 
1559  # Month number when September = 1, August = 12
1560  $month += 4;
1561  if ( $month > 12 ) {
1562  # Next year
1563  $month -= 12;
1564  $year++;
1565  $hebrewYear++;
1566  }
1567 
1568  # Calculate day of year from 1 September
1569  $dayOfYear = $day;
1570  for ( $i = 1; $i < $month; $i++ ) {
1571  if ( $i == 6 ) {
1572  # February
1573  $dayOfYear += 28;
1574  # Check if the year is leap
1575  if ( $year % 400 == 0 || ( $year % 4 == 0 && $year % 100 > 0 ) ) {
1576  $dayOfYear++;
1577  }
1578  } elseif ( $i == 8 || $i == 10 || $i == 1 || $i == 3 ) {
1579  $dayOfYear += 30;
1580  } else {
1581  $dayOfYear += 31;
1582  }
1583  }
1584 
1585  # Calculate the start of the Hebrew year
1586  $start = self::hebrewYearStart( $hebrewYear );
1587 
1588  # Calculate next year's start
1589  if ( $dayOfYear <= $start ) {
1590  # Day is before the start of the year - it is the previous year
1591  # Next year's start
1592  $nextStart = $start;
1593  # Previous year
1594  $year--;
1595  $hebrewYear--;
1596  # Add days since previous year's 1 September
1597  $dayOfYear += 365;
1598  if ( ( $year % 400 == 0 ) || ( $year % 100 != 0 && $year % 4 == 0 ) ) {
1599  # Leap year
1600  $dayOfYear++;
1601  }
1602  # Start of the new (previous) year
1603  $start = self::hebrewYearStart( $hebrewYear );
1604  } else {
1605  # Next year's start
1606  $nextStart = self::hebrewYearStart( $hebrewYear + 1 );
1607  }
1608 
1609  # Calculate Hebrew day of year
1610  $hebrewDayOfYear = $dayOfYear - $start;
1611 
1612  # Difference between year's days
1613  $diff = $nextStart - $start;
1614  # Add 12 (or 13 for leap years) days to ignore the difference between
1615  # Hebrew and Gregorian year (353 at least vs. 365/6) - now the
1616  # difference is only about the year type
1617  if ( ( $year % 400 == 0 ) || ( $year % 100 != 0 && $year % 4 == 0 ) ) {
1618  $diff += 13;
1619  } else {
1620  $diff += 12;
1621  }
1622 
1623  # Check the year pattern, and is leap year
1624  # 0 means an incomplete year, 1 means a regular year, 2 means a complete year
1625  # This is mod 30, to work on both leap years (which add 30 days of Adar I)
1626  # and non-leap years
1627  $yearPattern = $diff % 30;
1628  # Check if leap year
1629  $isLeap = $diff >= 30;
1630 
1631  # Calculate day in the month from number of day in the Hebrew year
1632  # Don't check Adar - if the day is not in Adar, we will stop before;
1633  # if it is in Adar, we will use it to check if it is Adar I or Adar II
1634  $hebrewDay = $hebrewDayOfYear;
1635  $hebrewMonth = 1;
1636  $days = 0;
1637  while ( $hebrewMonth <= 12 ) {
1638  # Calculate days in this month
1639  if ( $isLeap && $hebrewMonth == 6 ) {
1640  # Adar in a leap year
1641  if ( $isLeap ) {
1642  # Leap year - has Adar I, with 30 days, and Adar II, with 29 days
1643  $days = 30;
1644  if ( $hebrewDay <= $days ) {
1645  # Day in Adar I
1646  $hebrewMonth = 13;
1647  } else {
1648  # Subtract the days of Adar I
1649  $hebrewDay -= $days;
1650  # Try Adar II
1651  $days = 29;
1652  if ( $hebrewDay <= $days ) {
1653  # Day in Adar II
1654  $hebrewMonth = 14;
1655  }
1656  }
1657  }
1658  } elseif ( $hebrewMonth == 2 && $yearPattern == 2 ) {
1659  # Cheshvan in a complete year (otherwise as the rule below)
1660  $days = 30;
1661  } elseif ( $hebrewMonth == 3 && $yearPattern == 0 ) {
1662  # Kislev in an incomplete year (otherwise as the rule below)
1663  $days = 29;
1664  } else {
1665  # Odd months have 30 days, even have 29
1666  $days = 30 - ( $hebrewMonth - 1 ) % 2;
1667  }
1668  if ( $hebrewDay <= $days ) {
1669  # In the current month
1670  break;
1671  } else {
1672  # Subtract the days of the current month
1673  $hebrewDay -= $days;
1674  # Try in the next month
1675  $hebrewMonth++;
1676  }
1677  }
1678 
1679  return array( $hebrewYear, $hebrewMonth, $hebrewDay, $days );
1680  }
1681 
1691  private static function hebrewYearStart( $year ) {
1692  $a = intval( ( 12 * ( $year - 1 ) + 17 ) % 19 );
1693  $b = intval( ( $year - 1 ) % 4 );
1694  $m = 32.044093161144 + 1.5542417966212 * $a + $b / 4.0 - 0.0031777940220923 * ( $year - 1 );
1695  if ( $m < 0 ) {
1696  $m--;
1697  }
1698  $Mar = intval( $m );
1699  if ( $m < 0 ) {
1700  $m++;
1701  }
1702  $m -= $Mar;
1703 
1704  $c = intval( ( $Mar + 3 * ( $year - 1 ) + 5 * $b + 5 ) % 7 );
1705  if ( $c == 0 && $a > 11 && $m >= 0.89772376543210 ) {
1706  $Mar++;
1707  } elseif ( $c == 1 && $a > 6 && $m >= 0.63287037037037 ) {
1708  $Mar += 2;
1709  } elseif ( $c == 2 || $c == 4 || $c == 6 ) {
1710  $Mar++;
1711  }
1712 
1713  $Mar += intval( ( $year - 3761 ) / 100 ) - intval( ( $year - 3761 ) / 400 ) - 24;
1714  return $Mar;
1715  }
1716 
1729  private static function tsToYear( $ts, $cName ) {
1730  $gy = substr( $ts, 0, 4 );
1731  $gm = substr( $ts, 4, 2 );
1732  $gd = substr( $ts, 6, 2 );
1733 
1734  if ( !strcmp( $cName, 'thai' ) ) {
1735  # Thai solar dates
1736  # Add 543 years to the Gregorian calendar
1737  # Months and days are identical
1738  $gy_offset = $gy + 543;
1739  } elseif ( ( !strcmp( $cName, 'minguo' ) ) || !strcmp( $cName, 'juche' ) ) {
1740  # Minguo dates
1741  # Deduct 1911 years from the Gregorian calendar
1742  # Months and days are identical
1743  $gy_offset = $gy - 1911;
1744  } elseif ( !strcmp( $cName, 'tenno' ) ) {
1745  # Nengō dates up to Meiji period
1746  # Deduct years from the Gregorian calendar
1747  # depending on the nengo periods
1748  # Months and days are identical
1749  if ( ( $gy < 1912 ) || ( ( $gy == 1912 ) && ( $gm < 7 ) ) || ( ( $gy == 1912 ) && ( $gm == 7 ) && ( $gd < 31 ) ) ) {
1750  # Meiji period
1751  $gy_gannen = $gy - 1868 + 1;
1752  $gy_offset = $gy_gannen;
1753  if ( $gy_gannen == 1 ) {
1754  $gy_offset = '元';
1755  }
1756  $gy_offset = '明治' . $gy_offset;
1757  } elseif (
1758  ( ( $gy == 1912 ) && ( $gm == 7 ) && ( $gd == 31 ) ) ||
1759  ( ( $gy == 1912 ) && ( $gm >= 8 ) ) ||
1760  ( ( $gy > 1912 ) && ( $gy < 1926 ) ) ||
1761  ( ( $gy == 1926 ) && ( $gm < 12 ) ) ||
1762  ( ( $gy == 1926 ) && ( $gm == 12 ) && ( $gd < 26 ) )
1763  ) {
1764  # Taishō period
1765  $gy_gannen = $gy - 1912 + 1;
1766  $gy_offset = $gy_gannen;
1767  if ( $gy_gannen == 1 ) {
1768  $gy_offset = '元';
1769  }
1770  $gy_offset = '大正' . $gy_offset;
1771  } elseif (
1772  ( ( $gy == 1926 ) && ( $gm == 12 ) && ( $gd >= 26 ) ) ||
1773  ( ( $gy > 1926 ) && ( $gy < 1989 ) ) ||
1774  ( ( $gy == 1989 ) && ( $gm == 1 ) && ( $gd < 8 ) )
1775  ) {
1776  # Shōwa period
1777  $gy_gannen = $gy - 1926 + 1;
1778  $gy_offset = $gy_gannen;
1779  if ( $gy_gannen == 1 ) {
1780  $gy_offset = '元';
1781  }
1782  $gy_offset = '昭和' . $gy_offset;
1783  } else {
1784  # Heisei period
1785  $gy_gannen = $gy - 1989 + 1;
1786  $gy_offset = $gy_gannen;
1787  if ( $gy_gannen == 1 ) {
1788  $gy_offset = '元';
1789  }
1790  $gy_offset = '平成' . $gy_offset;
1791  }
1792  } else {
1793  $gy_offset = $gy;
1794  }
1795 
1796  return array( $gy_offset, $gm, $gd );
1797  }
1798 
1806  static function romanNumeral( $num ) {
1807  static $table = array(
1808  array( '', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X' ),
1809  array( '', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC', 'C' ),
1810  array( '', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM', 'M' ),
1811  array( '', 'M', 'MM', 'MMM', 'MMMM', 'MMMMM', 'MMMMMM', 'MMMMMMM', 'MMMMMMMM', 'MMMMMMMMM', 'MMMMMMMMMM' )
1812  );
1813 
1814  $num = intval( $num );
1815  if ( $num > 10000 || $num <= 0 ) {
1816  return $num;
1817  }
1818 
1819  $s = '';
1820  for ( $pow10 = 1000, $i = 3; $i >= 0; $pow10 /= 10, $i-- ) {
1821  if ( $num >= $pow10 ) {
1822  $s .= $table[$i][(int)floor( $num / $pow10 )];
1823  }
1824  $num = $num % $pow10;
1825  }
1826  return $s;
1827  }
1828 
1836  static function hebrewNumeral( $num ) {
1837  static $table = array(
1838  array( '', 'א', 'ב', 'ג', 'ד', 'ה', 'ו', 'ז', 'ח', 'ט', 'י' ),
1839  array( '', 'י', 'כ', 'ל', 'מ', 'נ', 'ס', 'ע', 'פ', 'צ', 'ק' ),
1840  array( '', 'ק', 'ר', 'ש', 'ת', 'תק', 'תר', 'תש', 'תת', 'תתק', 'תתר' ),
1841  array( '', 'א', 'ב', 'ג', 'ד', 'ה', 'ו', 'ז', 'ח', 'ט', 'י' )
1842  );
1843 
1844  $num = intval( $num );
1845  if ( $num > 9999 || $num <= 0 ) {
1846  return $num;
1847  }
1848 
1849  $s = '';
1850  for ( $pow10 = 1000, $i = 3; $i >= 0; $pow10 /= 10, $i-- ) {
1851  if ( $num >= $pow10 ) {
1852  if ( $num == 15 || $num == 16 ) {
1853  $s .= $table[0][9] . $table[0][$num - 9];
1854  $num = 0;
1855  } else {
1856  $s .= $table[$i][intval( ( $num / $pow10 ) )];
1857  if ( $pow10 == 1000 ) {
1858  $s .= "'";
1859  }
1860  }
1861  }
1862  $num = $num % $pow10;
1863  }
1864  if ( strlen( $s ) == 2 ) {
1865  $str = $s . "'";
1866  } else {
1867  $str = substr( $s, 0, strlen( $s ) - 2 ) . '"';
1868  $str .= substr( $s, strlen( $s ) - 2, 2 );
1869  }
1870  $start = substr( $str, 0, strlen( $str ) - 2 );
1871  $end = substr( $str, strlen( $str ) - 2 );
1872  switch ( $end ) {
1873  case 'כ':
1874  $str = $start . 'ך';
1875  break;
1876  case 'מ':
1877  $str = $start . 'ם';
1878  break;
1879  case 'נ':
1880  $str = $start . 'ן';
1881  break;
1882  case 'פ':
1883  $str = $start . 'ף';
1884  break;
1885  case 'צ':
1886  $str = $start . 'ץ';
1887  break;
1888  }
1889  return $str;
1890  }
1891 
1900  function userAdjust( $ts, $tz = false ) {
1901  global $wgUser, $wgLocalTZoffset;
1902 
1903  if ( $tz === false ) {
1904  $tz = $wgUser->getOption( 'timecorrection' );
1905  }
1906 
1907  $data = explode( '|', $tz, 3 );
1908 
1909  if ( $data[0] == 'ZoneInfo' ) {
1911  $userTZ = timezone_open( $data[2] );
1913  if ( $userTZ !== false ) {
1914  $date = date_create( $ts, timezone_open( 'UTC' ) );
1915  date_timezone_set( $date, $userTZ );
1916  $date = date_format( $date, 'YmdHis' );
1917  return $date;
1918  }
1919  # Unrecognized timezone, default to 'Offset' with the stored offset.
1920  $data[0] = 'Offset';
1921  }
1922 
1923  $minDiff = 0;
1924  if ( $data[0] == 'System' || $tz == '' ) {
1925  #  Global offset in minutes.
1926  if ( isset( $wgLocalTZoffset ) ) {
1927  $minDiff = $wgLocalTZoffset;
1928  }
1929  } elseif ( $data[0] == 'Offset' ) {
1930  $minDiff = intval( $data[1] );
1931  } else {
1932  $data = explode( ':', $tz );
1933  if ( count( $data ) == 2 ) {
1934  $data[0] = intval( $data[0] );
1935  $data[1] = intval( $data[1] );
1936  $minDiff = abs( $data[0] ) * 60 + $data[1];
1937  if ( $data[0] < 0 ) {
1938  $minDiff = -$minDiff;
1939  }
1940  } else {
1941  $minDiff = intval( $data[0] ) * 60;
1942  }
1943  }
1944 
1945  # No difference ? Return time unchanged
1946  if ( 0 == $minDiff ) {
1947  return $ts;
1948  }
1949 
1950  wfSuppressWarnings(); // E_STRICT system time bitching
1951  # Generate an adjusted date; take advantage of the fact that mktime
1952  # will normalize out-of-range values so we don't have to split $minDiff
1953  # into hours and minutes.
1954  $t = mktime( (
1955  (int)substr( $ts, 8, 2 ) ), # Hours
1956  (int)substr( $ts, 10, 2 ) + $minDiff, # Minutes
1957  (int)substr( $ts, 12, 2 ), # Seconds
1958  (int)substr( $ts, 4, 2 ), # Month
1959  (int)substr( $ts, 6, 2 ), # Day
1960  (int)substr( $ts, 0, 4 ) ); # Year
1961 
1962  $date = date( 'YmdHis', $t );
1964 
1965  return $date;
1966  }
1967 
1985  function dateFormat( $usePrefs = true ) {
1986  global $wgUser;
1987 
1988  if ( is_bool( $usePrefs ) ) {
1989  if ( $usePrefs ) {
1990  $datePreference = $wgUser->getDatePreference();
1991  } else {
1992  $datePreference = (string)User::getDefaultOption( 'date' );
1993  }
1994  } else {
1995  $datePreference = (string)$usePrefs;
1996  }
1997 
1998  // return int
1999  if ( $datePreference == '' ) {
2000  return 'default';
2001  }
2002 
2003  return $datePreference;
2004  }
2005 
2015  function getDateFormatString( $type, $pref ) {
2016  if ( !isset( $this->dateFormatStrings[$type][$pref] ) ) {
2017  if ( $pref == 'default' ) {
2018  $pref = $this->getDefaultDateFormat();
2019  $df = self::$dataCache->getSubitem( $this->mCode, 'dateFormats', "$pref $type" );
2020  } else {
2021  $df = self::$dataCache->getSubitem( $this->mCode, 'dateFormats', "$pref $type" );
2022 
2023  if ( $type === 'pretty' && $df === null ) {
2024  $df = $this->getDateFormatString( 'date', $pref );
2025  }
2026 
2027  if ( $df === null ) {
2028  $pref = $this->getDefaultDateFormat();
2029  $df = self::$dataCache->getSubitem( $this->mCode, 'dateFormats', "$pref $type" );
2030  }
2031  }
2032  $this->dateFormatStrings[$type][$pref] = $df;
2033  }
2034  return $this->dateFormatStrings[$type][$pref];
2035  }
2036 
2047  function date( $ts, $adj = false, $format = true, $timecorrection = false ) {
2048  $ts = wfTimestamp( TS_MW, $ts );
2049  if ( $adj ) {
2050  $ts = $this->userAdjust( $ts, $timecorrection );
2051  }
2052  $df = $this->getDateFormatString( 'date', $this->dateFormat( $format ) );
2053  return $this->sprintfDate( $df, $ts );
2054  }
2055 
2066  function time( $ts, $adj = false, $format = true, $timecorrection = false ) {
2067  $ts = wfTimestamp( TS_MW, $ts );
2068  if ( $adj ) {
2069  $ts = $this->userAdjust( $ts, $timecorrection );
2070  }
2071  $df = $this->getDateFormatString( 'time', $this->dateFormat( $format ) );
2072  return $this->sprintfDate( $df, $ts );
2073  }
2074 
2086  function timeanddate( $ts, $adj = false, $format = true, $timecorrection = false ) {
2087  $ts = wfTimestamp( TS_MW, $ts );
2088  if ( $adj ) {
2089  $ts = $this->userAdjust( $ts, $timecorrection );
2090  }
2091  $df = $this->getDateFormatString( 'both', $this->dateFormat( $format ) );
2092  return $this->sprintfDate( $df, $ts );
2093  }
2094 
2105  public function formatDuration( $seconds, array $chosenIntervals = array() ) {
2106  $intervals = $this->getDurationIntervals( $seconds, $chosenIntervals );
2107 
2108  $segments = array();
2109 
2110  foreach ( $intervals as $intervalName => $intervalValue ) {
2111  // Messages: duration-seconds, duration-minutes, duration-hours, duration-days, duration-weeks,
2112  // duration-years, duration-decades, duration-centuries, duration-millennia
2113  $message = wfMessage( 'duration-' . $intervalName )->numParams( $intervalValue );
2114  $segments[] = $message->inLanguage( $this )->escaped();
2115  }
2116 
2117  return $this->listToText( $segments );
2118  }
2119 
2131  public function getDurationIntervals( $seconds, array $chosenIntervals = array() ) {
2132  if ( empty( $chosenIntervals ) ) {
2133  $chosenIntervals = array( 'millennia', 'centuries', 'decades', 'years', 'days', 'hours', 'minutes', 'seconds' );
2134  }
2135 
2136  $intervals = array_intersect_key( self::$durationIntervals, array_flip( $chosenIntervals ) );
2137  $sortedNames = array_keys( $intervals );
2138  $smallestInterval = array_pop( $sortedNames );
2139 
2140  $segments = array();
2141 
2142  foreach ( $intervals as $name => $length ) {
2143  $value = floor( $seconds / $length );
2144 
2145  if ( $value > 0 || ( $name == $smallestInterval && empty( $segments ) ) ) {
2146  $seconds -= $value * $length;
2147  $segments[$name] = $value;
2148  }
2149  }
2150 
2151  return $segments;
2152  }
2153 
2173  private function internalUserTimeAndDate( $type, $ts, User $user, array $options ) {
2174  $ts = wfTimestamp( TS_MW, $ts );
2175  $options += array( 'timecorrection' => true, 'format' => true );
2176  if ( $options['timecorrection'] !== false ) {
2177  if ( $options['timecorrection'] === true ) {
2178  $offset = $user->getOption( 'timecorrection' );
2179  } else {
2180  $offset = $options['timecorrection'];
2181  }
2182  $ts = $this->userAdjust( $ts, $offset );
2183  }
2184  if ( $options['format'] === true ) {
2185  $format = $user->getDatePreference();
2186  } else {
2187  $format = $options['format'];
2188  }
2189  $df = $this->getDateFormatString( $type, $this->dateFormat( $format ) );
2190  return $this->sprintfDate( $df, $ts );
2191  }
2192 
2212  public function userDate( $ts, User $user, array $options = array() ) {
2213  return $this->internalUserTimeAndDate( 'date', $ts, $user, $options );
2214  }
2215 
2235  public function userTime( $ts, User $user, array $options = array() ) {
2236  return $this->internalUserTimeAndDate( 'time', $ts, $user, $options );
2237  }
2238 
2258  public function userTimeAndDate( $ts, User $user, array $options = array() ) {
2259  return $this->internalUserTimeAndDate( 'both', $ts, $user, $options );
2260  }
2261 
2277  public function getHumanTimestamp( MWTimestamp $ts, MWTimestamp $relativeTo, User $user ) {
2278  $diff = $ts->diff( $relativeTo );
2279  $diffDay = (bool)( (int)$ts->timestamp->format( 'w' ) - (int)$relativeTo->timestamp->format( 'w' ) );
2280  $days = $diff->days ?: (int)$diffDay;
2281  if ( $diff->invert || $days > 5 && $ts->timestamp->format( 'Y' ) !== $relativeTo->timestamp->format( 'Y' ) ) {
2282  // Timestamps are in different years: use full timestamp
2283  // Also do full timestamp for future dates
2287  $format = $this->getDateFormatString( 'both', $user->getDatePreference() ?: 'default' );
2288  $ts = $this->sprintfDate( $format, $ts->getTimestamp( TS_MW ) );
2289  } elseif ( $days > 5 ) {
2290  // Timestamps are in same year, but more than 5 days ago: show day and month only.
2291  $format = $this->getDateFormatString( 'pretty', $user->getDatePreference() ?: 'default' );
2292  $ts = $this->sprintfDate( $format, $ts->getTimestamp( TS_MW ) );
2293  } elseif ( $days > 1 ) {
2294  // Timestamp within the past week: show the day of the week and time
2295  $format = $this->getDateFormatString( 'time', $user->getDatePreference() ?: 'default' );
2296  $weekday = self::$mWeekdayMsgs[$ts->timestamp->format( 'w' )];
2297  // Messages:
2298  // sunday-at, monday-at, tuesday-at, wednesday-at, thursday-at, friday-at, saturday-at
2299  $ts = wfMessage( "$weekday-at" )
2300  ->inLanguage( $this )
2301  ->params( $this->sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
2302  ->text();
2303  } elseif ( $days == 1 ) {
2304  // Timestamp was yesterday: say 'yesterday' and the time.
2305  $format = $this->getDateFormatString( 'time', $user->getDatePreference() ?: 'default' );
2306  $ts = wfMessage( 'yesterday-at' )
2307  ->inLanguage( $this )
2308  ->params( $this->sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
2309  ->text();
2310  } elseif ( $diff->h > 1 || $diff->h == 1 && $diff->i > 30 ) {
2311  // Timestamp was today, but more than 90 minutes ago: say 'today' and the time.
2312  $format = $this->getDateFormatString( 'time', $user->getDatePreference() ?: 'default' );
2313  $ts = wfMessage( 'today-at' )
2314  ->inLanguage( $this )
2315  ->params( $this->sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
2316  ->text();
2317 
2318  // From here on in, the timestamp was soon enough ago so that we can simply say
2319  // XX units ago, e.g., "2 hours ago" or "5 minutes ago"
2320  } elseif ( $diff->h == 1 ) {
2321  // Less than 90 minutes, but more than an hour ago.
2322  $ts = wfMessage( 'hours-ago' )->inLanguage( $this )->numParams( 1 )->text();
2323  } elseif ( $diff->i >= 1 ) {
2324  // A few minutes ago.
2325  $ts = wfMessage( 'minutes-ago' )->inLanguage( $this )->numParams( $diff->i )->text();
2326  } elseif ( $diff->s >= 30 ) {
2327  // Less than a minute, but more than 30 sec ago.
2328  $ts = wfMessage( 'seconds-ago' )->inLanguage( $this )->numParams( $diff->s )->text();
2329  } else {
2330  // Less than 30 seconds ago.
2331  $ts = wfMessage( 'just-now' )->text();
2332  }
2333 
2334  return $ts;
2335  }
2336 
2341  function getMessage( $key ) {
2342  return self::$dataCache->getSubitem( $this->mCode, 'messages', $key );
2343  }
2344 
2348  function getAllMessages() {
2349  return self::$dataCache->getItem( $this->mCode, 'messages' );
2350  }
2351 
2358  function iconv( $in, $out, $string ) {
2359  # This is a wrapper for iconv in all languages except esperanto,
2360  # which does some nasty x-conversions beforehand
2361 
2362  # Even with //IGNORE iconv can whine about illegal characters in
2363  # *input* string. We just ignore those too.
2364  # REF: http://bugs.php.net/bug.php?id=37166
2365  # REF: https://bugzilla.wikimedia.org/show_bug.cgi?id=16885
2367  $text = iconv( $in, $out . '//IGNORE', $string );
2369  return $text;
2370  }
2371 
2372  // callback functions for uc(), lc(), ucwords(), ucwordbreaks()
2373 
2378  function ucwordbreaksCallbackAscii( $matches ) {
2379  return $this->ucfirst( $matches[1] );
2380  }
2381 
2386  function ucwordbreaksCallbackMB( $matches ) {
2387  return mb_strtoupper( $matches[0] );
2388  }
2389 
2394  function ucCallback( $matches ) {
2395  list( $wikiUpperChars ) = self::getCaseMaps();
2396  return strtr( $matches[1], $wikiUpperChars );
2397  }
2398 
2403  function lcCallback( $matches ) {
2404  list( , $wikiLowerChars ) = self::getCaseMaps();
2405  return strtr( $matches[1], $wikiLowerChars );
2406  }
2407 
2412  function ucwordsCallbackMB( $matches ) {
2413  return mb_strtoupper( $matches[0] );
2414  }
2415 
2420  function ucwordsCallbackWiki( $matches ) {
2421  list( $wikiUpperChars ) = self::getCaseMaps();
2422  return strtr( $matches[0], $wikiUpperChars );
2423  }
2424 
2432  function ucfirst( $str ) {
2433  $o = ord( $str );
2434  if ( $o < 96 ) { // if already uppercase...
2435  return $str;
2436  } elseif ( $o < 128 ) {
2437  return ucfirst( $str ); // use PHP's ucfirst()
2438  } else {
2439  // fall back to more complex logic in case of multibyte strings
2440  return $this->uc( $str, true );
2441  }
2442  }
2443 
2452  function uc( $str, $first = false ) {
2453  if ( function_exists( 'mb_strtoupper' ) ) {
2454  if ( $first ) {
2455  if ( $this->isMultibyte( $str ) ) {
2456  return mb_strtoupper( mb_substr( $str, 0, 1 ) ) . mb_substr( $str, 1 );
2457  } else {
2458  return ucfirst( $str );
2459  }
2460  } else {
2461  return $this->isMultibyte( $str ) ? mb_strtoupper( $str ) : strtoupper( $str );
2462  }
2463  } else {
2464  if ( $this->isMultibyte( $str ) ) {
2465  $x = $first ? '^' : '';
2466  return preg_replace_callback(
2467  "/$x([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/",
2468  array( $this, 'ucCallback' ),
2469  $str
2470  );
2471  } else {
2472  return $first ? ucfirst( $str ) : strtoupper( $str );
2473  }
2474  }
2475  }
2476 
2481  function lcfirst( $str ) {
2482  $o = ord( $str );
2483  if ( !$o ) {
2484  return strval( $str );
2485  } elseif ( $o >= 128 ) {
2486  return $this->lc( $str, true );
2487  } elseif ( $o > 96 ) {
2488  return $str;
2489  } else {
2490  $str[0] = strtolower( $str[0] );
2491  return $str;
2492  }
2493  }
2494 
2500  function lc( $str, $first = false ) {
2501  if ( function_exists( 'mb_strtolower' ) ) {
2502  if ( $first ) {
2503  if ( $this->isMultibyte( $str ) ) {
2504  return mb_strtolower( mb_substr( $str, 0, 1 ) ) . mb_substr( $str, 1 );
2505  } else {
2506  return strtolower( substr( $str, 0, 1 ) ) . substr( $str, 1 );
2507  }
2508  } else {
2509  return $this->isMultibyte( $str ) ? mb_strtolower( $str ) : strtolower( $str );
2510  }
2511  } else {
2512  if ( $this->isMultibyte( $str ) ) {
2513  $x = $first ? '^' : '';
2514  return preg_replace_callback(
2515  "/$x([A-Z]|[\\xc0-\\xff][\\x80-\\xbf]*)/",
2516  array( $this, 'lcCallback' ),
2517  $str
2518  );
2519  } else {
2520  return $first ? strtolower( substr( $str, 0, 1 ) ) . substr( $str, 1 ) : strtolower( $str );
2521  }
2522  }
2523  }
2524 
2529  function isMultibyte( $str ) {
2530  return (bool)preg_match( '/[\x80-\xff]/', $str );
2531  }
2532 
2537  function ucwords( $str ) {
2538  if ( $this->isMultibyte( $str ) ) {
2539  $str = $this->lc( $str );
2540 
2541  // regexp to find first letter in each word (i.e. after each space)
2542  $replaceRegexp = "/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)| ([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/";
2543 
2544  // function to use to capitalize a single char
2545  if ( function_exists( 'mb_strtoupper' ) ) {
2546  return preg_replace_callback(
2547  $replaceRegexp,
2548  array( $this, 'ucwordsCallbackMB' ),
2549  $str
2550  );
2551  } else {
2552  return preg_replace_callback(
2553  $replaceRegexp,
2554  array( $this, 'ucwordsCallbackWiki' ),
2555  $str
2556  );
2557  }
2558  } else {
2559  return ucwords( strtolower( $str ) );
2560  }
2561  }
2562 
2569  function ucwordbreaks( $str ) {
2570  if ( $this->isMultibyte( $str ) ) {
2571  $str = $this->lc( $str );
2572 
2573  // since \b doesn't work for UTF-8, we explicitely define word break chars
2574  $breaks = "[ \-\(\)\}\{\.,\?!]";
2575 
2576  // find first letter after word break
2577  $replaceRegexp = "/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)|$breaks([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/";
2578 
2579  if ( function_exists( 'mb_strtoupper' ) ) {
2580  return preg_replace_callback(
2581  $replaceRegexp,
2582  array( $this, 'ucwordbreaksCallbackMB' ),
2583  $str
2584  );
2585  } else {
2586  return preg_replace_callback(
2587  $replaceRegexp,
2588  array( $this, 'ucwordsCallbackWiki' ),
2589  $str
2590  );
2591  }
2592  } else {
2593  return preg_replace_callback(
2594  '/\b([\w\x80-\xff]+)\b/',
2595  array( $this, 'ucwordbreaksCallbackAscii' ),
2596  $str
2597  );
2598  }
2599  }
2600 
2616  function caseFold( $s ) {
2617  return $this->uc( $s );
2618  }
2619 
2624  function checkTitleEncoding( $s ) {
2625  if ( is_array( $s ) ) {
2626  throw new MWException( 'Given array to checkTitleEncoding.' );
2627  }
2628  if ( StringUtils::isUtf8( $s ) ) {
2629  return $s;
2630  }
2631 
2632  return $this->iconv( $this->fallback8bitEncoding(), 'utf-8', $s );
2633  }
2634 
2638  function fallback8bitEncoding() {
2639  return self::$dataCache->getItem( $this->mCode, 'fallback8bitEncoding' );
2640  }
2641 
2650  function hasWordBreaks() {
2651  return true;
2652  }
2653 
2661  function segmentByWord( $string ) {
2662  return $string;
2663  }
2664 
2672  function normalizeForSearch( $string ) {
2673  return self::convertDoubleWidth( $string );
2674  }
2675 
2684  protected static function convertDoubleWidth( $string ) {
2685  static $full = null;
2686  static $half = null;
2687 
2688  if ( $full === null ) {
2689  $fullWidth = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
2690  $halfWidth = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
2691  $full = str_split( $fullWidth, 3 );
2692  $half = str_split( $halfWidth );
2693  }
2694 
2695  $string = str_replace( $full, $half, $string );
2696  return $string;
2697  }
2698 
2704  protected static function insertSpace( $string, $pattern ) {
2705  $string = preg_replace( $pattern, " $1 ", $string );
2706  $string = preg_replace( '/ +/', ' ', $string );
2707  return $string;
2708  }
2709 
2714  function convertForSearchResult( $termsArray ) {
2715  # some languages, e.g. Chinese, need to do a conversion
2716  # in order for search results to be displayed correctly
2717  return $termsArray;
2718  }
2719 
2726  function firstChar( $s ) {
2727  $matches = array();
2728  preg_match(
2729  '/^([\x00-\x7f]|[\xc0-\xdf][\x80-\xbf]|' .
2730  '[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3})/',
2731  $s,
2732  $matches
2733  );
2734 
2735  if ( isset( $matches[1] ) ) {
2736  if ( strlen( $matches[1] ) != 3 ) {
2737  return $matches[1];
2738  }
2739 
2740  // Break down Hangul syllables to grab the first jamo
2741  $code = utf8ToCodepoint( $matches[1] );
2742  if ( $code < 0xac00 || 0xd7a4 <= $code ) {
2743  return $matches[1];
2744  } elseif ( $code < 0xb098 ) {
2745  return "\xe3\x84\xb1";
2746  } elseif ( $code < 0xb2e4 ) {
2747  return "\xe3\x84\xb4";
2748  } elseif ( $code < 0xb77c ) {
2749  return "\xe3\x84\xb7";
2750  } elseif ( $code < 0xb9c8 ) {
2751  return "\xe3\x84\xb9";
2752  } elseif ( $code < 0xbc14 ) {
2753  return "\xe3\x85\x81";
2754  } elseif ( $code < 0xc0ac ) {
2755  return "\xe3\x85\x82";
2756  } elseif ( $code < 0xc544 ) {
2757  return "\xe3\x85\x85";
2758  } elseif ( $code < 0xc790 ) {
2759  return "\xe3\x85\x87";
2760  } elseif ( $code < 0xcc28 ) {
2761  return "\xe3\x85\x88";
2762  } elseif ( $code < 0xce74 ) {
2763  return "\xe3\x85\x8a";
2764  } elseif ( $code < 0xd0c0 ) {
2765  return "\xe3\x85\x8b";
2766  } elseif ( $code < 0xd30c ) {
2767  return "\xe3\x85\x8c";
2768  } elseif ( $code < 0xd558 ) {
2769  return "\xe3\x85\x8d";
2770  } else {
2771  return "\xe3\x85\x8e";
2772  }
2773  } else {
2774  return '';
2775  }
2776  }
2777 
2778  function initEncoding() {
2779  # Some languages may have an alternate char encoding option
2780  # (Esperanto X-coding, Japanese furigana conversion, etc)
2781  # If this language is used as the primary content language,
2782  # an override to the defaults can be set here on startup.
2783  }
2784 
2789  function recodeForEdit( $s ) {
2790  # For some languages we'll want to explicitly specify
2791  # which characters make it into the edit box raw
2792  # or are converted in some way or another.
2793  global $wgEditEncoding;
2794  if ( $wgEditEncoding == '' || $wgEditEncoding == 'UTF-8' ) {
2795  return $s;
2796  } else {
2797  return $this->iconv( 'UTF-8', $wgEditEncoding, $s );
2798  }
2799  }
2800 
2805  function recodeInput( $s ) {
2806  # Take the previous into account.
2807  global $wgEditEncoding;
2808  if ( $wgEditEncoding != '' ) {
2809  $enc = $wgEditEncoding;
2810  } else {
2811  $enc = 'UTF-8';
2812  }
2813  if ( $enc == 'UTF-8' ) {
2814  return $s;
2815  } else {
2816  return $this->iconv( $enc, 'UTF-8', $s );
2817  }
2818  }
2819 
2831  function normalize( $s ) {
2832  global $wgAllUnicodeFixes;
2833  $s = UtfNormal::cleanUp( $s );
2834  if ( $wgAllUnicodeFixes ) {
2835  $s = $this->transformUsingPairFile( 'normalize-ar.ser', $s );
2836  $s = $this->transformUsingPairFile( 'normalize-ml.ser', $s );
2837  }
2838 
2839  return $s;
2840  }
2841 
2856  function transformUsingPairFile( $file, $string ) {
2857  if ( !isset( $this->transformData[$file] ) ) {
2858  $data = wfGetPrecompiledData( $file );
2859  if ( $data === false ) {
2860  throw new MWException( __METHOD__ . ": The transformation file $file is missing" );
2861  }
2862  $this->transformData[$file] = new ReplacementArray( $data );
2863  }
2864  return $this->transformData[$file]->replace( $string );
2865  }
2866 
2872  function isRTL() {
2873  return self::$dataCache->getItem( $this->mCode, 'rtl' );
2874  }
2875 
2880  function getDir() {
2881  return $this->isRTL() ? 'rtl' : 'ltr';
2882  }
2883 
2892  function alignStart() {
2893  return $this->isRTL() ? 'right' : 'left';
2894  }
2895 
2904  function alignEnd() {
2905  return $this->isRTL() ? 'left' : 'right';
2906  }
2907 
2919  function getDirMarkEntity( $opposite = false ) {
2920  if ( $opposite ) {
2921  return $this->isRTL() ? '&lrm;' : '&rlm;';
2922  }
2923  return $this->isRTL() ? '&rlm;' : '&lrm;';
2924  }
2925 
2936  function getDirMark( $opposite = false ) {
2937  $lrm = "\xE2\x80\x8E"; # LEFT-TO-RIGHT MARK, commonly abbreviated LRM
2938  $rlm = "\xE2\x80\x8F"; # RIGHT-TO-LEFT MARK, commonly abbreviated RLM
2939  if ( $opposite ) {
2940  return $this->isRTL() ? $lrm : $rlm;
2941  }
2942  return $this->isRTL() ? $rlm : $lrm;
2943  }
2944 
2948  function capitalizeAllNouns() {
2949  return self::$dataCache->getItem( $this->mCode, 'capitalizeAllNouns' );
2950  }
2951 
2958  function getArrow( $direction = 'forwards' ) {
2959  switch ( $direction ) {
2960  case 'forwards':
2961  return $this->isRTL() ? '←' : '→';
2962  case 'backwards':
2963  return $this->isRTL() ? '→' : '←';
2964  case 'left':
2965  return '←';
2966  case 'right':
2967  return '→';
2968  case 'up':
2969  return '↑';
2970  case 'down':
2971  return '↓';
2972  }
2973  }
2974 
2980  function linkPrefixExtension() {
2981  return self::$dataCache->getItem( $this->mCode, 'linkPrefixExtension' );
2982  }
2983 
2988  function getMagicWords() {
2989  return self::$dataCache->getItem( $this->mCode, 'magicWords' );
2990  }
2991 
2995  protected function doMagicHook() {
2996  if ( $this->mMagicHookDone ) {
2997  return;
2998  }
2999  $this->mMagicHookDone = true;
3000  wfProfileIn( 'LanguageGetMagic' );
3001  wfRunHooks( 'LanguageGetMagic', array( &$this->mMagicExtensions, $this->getCode() ) );
3002  wfProfileOut( 'LanguageGetMagic' );
3003  }
3004 
3010  function getMagic( $mw ) {
3011  // Saves a function call
3012  if ( ! $this->mMagicHookDone ) {
3013  $this->doMagicHook();
3014  }
3015 
3016  if ( isset( $this->mMagicExtensions[$mw->mId] ) ) {
3017  $rawEntry = $this->mMagicExtensions[$mw->mId];
3018  } else {
3019  $rawEntry = self::$dataCache->getSubitem(
3020  $this->mCode, 'magicWords', $mw->mId );
3021  }
3022 
3023  if ( !is_array( $rawEntry ) ) {
3024  error_log( "\"$rawEntry\" is not a valid magic word for \"$mw->mId\"" );
3025  } else {
3026  $mw->mCaseSensitive = $rawEntry[0];
3027  $mw->mSynonyms = array_slice( $rawEntry, 1 );
3028  }
3029  }
3030 
3036  function addMagicWordsByLang( $newWords ) {
3037  $fallbackChain = $this->getFallbackLanguages();
3038  $fallbackChain = array_reverse( $fallbackChain );
3039  foreach ( $fallbackChain as $code ) {
3040  if ( isset( $newWords[$code] ) ) {
3041  $this->mMagicExtensions = $newWords[$code] + $this->mMagicExtensions;
3042  }
3043  }
3044  }
3045 
3050  function getSpecialPageAliases() {
3051  // Cache aliases because it may be slow to load them
3052  if ( is_null( $this->mExtendedSpecialPageAliases ) ) {
3053  // Initialise array
3054  $this->mExtendedSpecialPageAliases =
3055  self::$dataCache->getItem( $this->mCode, 'specialPageAliases' );
3056  wfRunHooks( 'LanguageGetSpecialPageAliases',
3057  array( &$this->mExtendedSpecialPageAliases, $this->getCode() ) );
3058  }
3059 
3061  }
3062 
3069  function emphasize( $text ) {
3070  return "<em>$text</em>";
3071  }
3072 
3096  public function formatNum( $number, $nocommafy = false ) {
3097  global $wgTranslateNumerals;
3098  if ( !$nocommafy ) {
3099  $number = $this->commafy( $number );
3100  $s = $this->separatorTransformTable();
3101  if ( $s ) {
3102  $number = strtr( $number, $s );
3103  }
3104  }
3105 
3106  if ( $wgTranslateNumerals ) {
3107  $s = $this->digitTransformTable();
3108  if ( $s ) {
3109  $number = strtr( $number, $s );
3110  }
3111  }
3112 
3113  return $number;
3114  }
3115 
3124  public function formatNumNoSeparators( $number ) {
3125  return $this->formatNum( $number, true );
3126  }
3127 
3132  function parseFormattedNumber( $number ) {
3133  $s = $this->digitTransformTable();
3134  if ( $s ) {
3135  $number = strtr( $number, array_flip( $s ) );
3136  }
3137 
3138  $s = $this->separatorTransformTable();
3139  if ( $s ) {
3140  $number = strtr( $number, array_flip( $s ) );
3141  }
3142 
3143  $number = strtr( $number, array( ',' => '' ) );
3144  return $number;
3145  }
3146 
3153  function commafy( $number ) {
3155  if ( $number === null ) {
3156  return '';
3157  }
3158 
3159  if ( !$digitGroupingPattern || $digitGroupingPattern === "###,###,###" ) {
3160  // default grouping is at thousands, use the same for ###,###,### pattern too.
3161  return strrev( (string)preg_replace( '/(\d{3})(?=\d)(?!\d*\.)/', '$1,', strrev( $number ) ) );
3162  } else {
3163  // Ref: http://cldr.unicode.org/translation/number-patterns
3164  $sign = "";
3165  if ( intval( $number ) < 0 ) {
3166  // For negative numbers apply the algorithm like positive number and add sign.
3167  $sign = "-";
3168  $number = substr( $number, 1 );
3169  }
3170  $integerPart = array();
3171  $decimalPart = array();
3172  $numMatches = preg_match_all( "/(#+)/", $digitGroupingPattern, $matches );
3173  preg_match( "/\d+/", $number, $integerPart );
3174  preg_match( "/\.\d*/", $number, $decimalPart );
3175  $groupedNumber = ( count( $decimalPart ) > 0 ) ? $decimalPart[0] : "";
3176  if ( $groupedNumber === $number ) {
3177  // the string does not have any number part. Eg: .12345
3178  return $sign . $groupedNumber;
3179  }
3180  $start = $end = strlen( $integerPart[0] );
3181  while ( $start > 0 ) {
3182  $match = $matches[0][$numMatches - 1];
3183  $matchLen = strlen( $match );
3184  $start = $end - $matchLen;
3185  if ( $start < 0 ) {
3186  $start = 0;
3187  }
3188  $groupedNumber = substr( $number, $start, $end -$start ) . $groupedNumber;
3189  $end = $start;
3190  if ( $numMatches > 1 ) {
3191  // use the last pattern for the rest of the number
3192  $numMatches--;
3193  }
3194  if ( $start > 0 ) {
3195  $groupedNumber = "," . $groupedNumber;
3196  }
3197  }
3198  return $sign . $groupedNumber;
3199  }
3200  }
3201 
3205  function digitGroupingPattern() {
3206  return self::$dataCache->getItem( $this->mCode, 'digitGroupingPattern' );
3207  }
3208 
3212  function digitTransformTable() {
3213  return self::$dataCache->getItem( $this->mCode, 'digitTransformTable' );
3214  }
3215 
3219  function separatorTransformTable() {
3220  return self::$dataCache->getItem( $this->mCode, 'separatorTransformTable' );
3221  }
3222 
3232  function listToText( array $l ) {
3233  $m = count( $l ) - 1;
3234  if ( $m < 0 ) {
3235  return '';
3236  }
3237  if ( $m > 0 ) {
3238  $and = $this->getMessageFromDB( 'and' );
3239  $space = $this->getMessageFromDB( 'word-separator' );
3240  if ( $m > 1 ) {
3241  $comma = $this->getMessageFromDB( 'comma-separator' );
3242  }
3243  }
3244  $s = $l[$m];
3245  for ( $i = $m - 1; $i >= 0; $i-- ) {
3246  if ( $i == $m - 1 ) {
3247  $s = $l[$i] . $and . $space . $s;
3248  } else {
3249  $s = $l[$i] . $comma . $s;
3250  }
3251  }
3252  return $s;
3253  }
3254 
3261  function commaList( array $list ) {
3262  return implode(
3263  wfMessage( 'comma-separator' )->inLanguage( $this )->escaped(),
3264  $list
3265  );
3266  }
3267 
3274  function semicolonList( array $list ) {
3275  return implode(
3276  wfMessage( 'semicolon-separator' )->inLanguage( $this )->escaped(),
3277  $list
3278  );
3279  }
3280 
3286  function pipeList( array $list ) {
3287  return implode(
3288  wfMessage( 'pipe-separator' )->inLanguage( $this )->escaped(),
3289  $list
3290  );
3291  }
3292 
3310  function truncate( $string, $length, $ellipsis = '...', $adjustLength = true ) {
3311  # Use the localized ellipsis character
3312  if ( $ellipsis == '...' ) {
3313  $ellipsis = wfMessage( 'ellipsis' )->inLanguage( $this )->escaped();
3314  }
3315  # Check if there is no need to truncate
3316  if ( $length == 0 ) {
3317  return $ellipsis; // convention
3318  } elseif ( strlen( $string ) <= abs( $length ) ) {
3319  return $string; // no need to truncate
3320  }
3321  $stringOriginal = $string;
3322  # If ellipsis length is >= $length then we can't apply $adjustLength
3323  if ( $adjustLength && strlen( $ellipsis ) >= abs( $length ) ) {
3324  $string = $ellipsis; // this can be slightly unexpected
3325  # Otherwise, truncate and add ellipsis...
3326  } else {
3327  $eLength = $adjustLength ? strlen( $ellipsis ) : 0;
3328  if ( $length > 0 ) {
3329  $length -= $eLength;
3330  $string = substr( $string, 0, $length ); // xyz...
3331  $string = $this->removeBadCharLast( $string );
3332  $string = rtrim( $string );
3333  $string = $string . $ellipsis;
3334  } else {
3335  $length += $eLength;
3336  $string = substr( $string, $length ); // ...xyz
3337  $string = $this->removeBadCharFirst( $string );
3338  $string = ltrim( $string );
3339  $string = $ellipsis . $string;
3340  }
3341  }
3342  # Do not truncate if the ellipsis makes the string longer/equal (bug 22181).
3343  # This check is *not* redundant if $adjustLength, due to the single case where
3344  # LEN($ellipsis) > ABS($limit arg); $stringOriginal could be shorter than $string.
3345  if ( strlen( $string ) < strlen( $stringOriginal ) ) {
3346  return $string;
3347  } else {
3348  return $stringOriginal;
3349  }
3350  }
3351 
3359  protected function removeBadCharLast( $string ) {
3360  if ( $string != '' ) {
3361  $char = ord( $string[strlen( $string ) - 1] );
3362  $m = array();
3363  if ( $char >= 0xc0 ) {
3364  # We got the first byte only of a multibyte char; remove it.
3365  $string = substr( $string, 0, -1 );
3366  } elseif ( $char >= 0x80 &&
3367  preg_match( '/^(.*)(?:[\xe0-\xef][\x80-\xbf]|' .
3368  '[\xf0-\xf7][\x80-\xbf]{1,2})$/', $string, $m )
3369  ) {
3370  # We chopped in the middle of a character; remove it
3371  $string = $m[1];
3372  }
3373  }
3374  return $string;
3375  }
3376 
3384  protected function removeBadCharFirst( $string ) {
3385  if ( $string != '' ) {
3386  $char = ord( $string[0] );
3387  if ( $char >= 0x80 && $char < 0xc0 ) {
3388  # We chopped in the middle of a character; remove the whole thing
3389  $string = preg_replace( '/^[\x80-\xbf]+/', '', $string );
3390  }
3391  }
3392  return $string;
3393  }
3394 
3410  function truncateHtml( $text, $length, $ellipsis = '...' ) {
3411  # Use the localized ellipsis character
3412  if ( $ellipsis == '...' ) {
3413  $ellipsis = wfMessage( 'ellipsis' )->inLanguage( $this )->escaped();
3414  }
3415  # Check if there is clearly no need to truncate
3416  if ( $length <= 0 ) {
3417  return $ellipsis; // no text shown, nothing to format (convention)
3418  } elseif ( strlen( $text ) <= $length ) {
3419  return $text; // string short enough even *with* HTML (short-circuit)
3420  }
3421 
3422  $dispLen = 0; // innerHTML legth so far
3423  $testingEllipsis = false; // checking if ellipses will make string longer/equal?
3424  $tagType = 0; // 0-open, 1-close
3425  $bracketState = 0; // 1-tag start, 2-tag name, 0-neither
3426  $entityState = 0; // 0-not entity, 1-entity
3427  $tag = $ret = ''; // accumulated tag name, accumulated result string
3428  $openTags = array(); // open tag stack
3429  $maybeState = null; // possible truncation state
3430 
3431  $textLen = strlen( $text );
3432  $neLength = max( 0, $length - strlen( $ellipsis ) ); // non-ellipsis len if truncated
3433  for ( $pos = 0; true; ++$pos ) {
3434  # Consider truncation once the display length has reached the maximim.
3435  # We check if $dispLen > 0 to grab tags for the $neLength = 0 case.
3436  # Check that we're not in the middle of a bracket/entity...
3437  if ( $dispLen && $dispLen >= $neLength && $bracketState == 0 && !$entityState ) {
3438  if ( !$testingEllipsis ) {
3439  $testingEllipsis = true;
3440  # Save where we are; we will truncate here unless there turn out to
3441  # be so few remaining characters that truncation is not necessary.
3442  if ( !$maybeState ) { // already saved? ($neLength = 0 case)
3443  $maybeState = array( $ret, $openTags ); // save state
3444  }
3445  } elseif ( $dispLen > $length && $dispLen > strlen( $ellipsis ) ) {
3446  # String in fact does need truncation, the truncation point was OK.
3447  list( $ret, $openTags ) = $maybeState; // reload state
3448  $ret = $this->removeBadCharLast( $ret ); // multi-byte char fix
3449  $ret .= $ellipsis; // add ellipsis
3450  break;
3451  }
3452  }
3453  if ( $pos >= $textLen ) {
3454  break; // extra iteration just for above checks
3455  }
3456 
3457  # Read the next char...
3458  $ch = $text[$pos];
3459  $lastCh = $pos ? $text[$pos - 1] : '';
3460  $ret .= $ch; // add to result string
3461  if ( $ch == '<' ) {
3462  $this->truncate_endBracket( $tag, $tagType, $lastCh, $openTags ); // for bad HTML
3463  $entityState = 0; // for bad HTML
3464  $bracketState = 1; // tag started (checking for backslash)
3465  } elseif ( $ch == '>' ) {
3466  $this->truncate_endBracket( $tag, $tagType, $lastCh, $openTags );
3467  $entityState = 0; // for bad HTML
3468  $bracketState = 0; // out of brackets
3469  } elseif ( $bracketState == 1 ) {
3470  if ( $ch == '/' ) {
3471  $tagType = 1; // close tag (e.g. "</span>")
3472  } else {
3473  $tagType = 0; // open tag (e.g. "<span>")
3474  $tag .= $ch;
3475  }
3476  $bracketState = 2; // building tag name
3477  } elseif ( $bracketState == 2 ) {
3478  if ( $ch != ' ' ) {
3479  $tag .= $ch;
3480  } else {
3481  // Name found (e.g. "<a href=..."), add on tag attributes...
3482  $pos += $this->truncate_skip( $ret, $text, "<>", $pos + 1 );
3483  }
3484  } elseif ( $bracketState == 0 ) {
3485  if ( $entityState ) {
3486  if ( $ch == ';' ) {
3487  $entityState = 0;
3488  $dispLen++; // entity is one displayed char
3489  }
3490  } else {
3491  if ( $neLength == 0 && !$maybeState ) {
3492  // Save state without $ch. We want to *hit* the first
3493  // display char (to get tags) but not *use* it if truncating.
3494  $maybeState = array( substr( $ret, 0, -1 ), $openTags );
3495  }
3496  if ( $ch == '&' ) {
3497  $entityState = 1; // entity found, (e.g. "&#160;")
3498  } else {
3499  $dispLen++; // this char is displayed
3500  // Add the next $max display text chars after this in one swoop...
3501  $max = ( $testingEllipsis ? $length : $neLength ) - $dispLen;
3502  $skipped = $this->truncate_skip( $ret, $text, "<>&", $pos + 1, $max );
3503  $dispLen += $skipped;
3504  $pos += $skipped;
3505  }
3506  }
3507  }
3508  }
3509  // Close the last tag if left unclosed by bad HTML
3510  $this->truncate_endBracket( $tag, $text[$textLen - 1], $tagType, $openTags );
3511  while ( count( $openTags ) > 0 ) {
3512  $ret .= '</' . array_pop( $openTags ) . '>'; // close open tags
3513  }
3514  return $ret;
3515  }
3516 
3528  private function truncate_skip( &$ret, $text, $search, $start, $len = null ) {
3529  if ( $len === null ) {
3530  $len = -1; // -1 means "no limit" for strcspn
3531  } elseif ( $len < 0 ) {
3532  $len = 0; // sanity
3533  }
3534  $skipCount = 0;
3535  if ( $start < strlen( $text ) ) {
3536  $skipCount = strcspn( $text, $search, $start, $len );
3537  $ret .= substr( $text, $start, $skipCount );
3538  }
3539  return $skipCount;
3540  }
3541 
3551  private function truncate_endBracket( &$tag, $tagType, $lastCh, &$openTags ) {
3552  $tag = ltrim( $tag );
3553  if ( $tag != '' ) {
3554  if ( $tagType == 0 && $lastCh != '/' ) {
3555  $openTags[] = $tag; // tag opened (didn't close itself)
3556  } elseif ( $tagType == 1 ) {
3557  if ( $openTags && $tag == $openTags[count( $openTags ) - 1] ) {
3558  array_pop( $openTags ); // tag closed
3559  }
3560  }
3561  $tag = '';
3562  }
3563  }
3564 
3573  function convertGrammar( $word, $case ) {
3574  global $wgGrammarForms;
3575  if ( isset( $wgGrammarForms[$this->getCode()][$case][$word] ) ) {
3576  return $wgGrammarForms[$this->getCode()][$case][$word];
3577  }
3578  return $word;
3579  }
3585  function getGrammarForms() {
3586  global $wgGrammarForms;
3587  if ( isset( $wgGrammarForms[$this->getCode()] ) && is_array( $wgGrammarForms[$this->getCode()] ) ) {
3588  return $wgGrammarForms[$this->getCode()];
3589  }
3590  return array();
3591  }
3611  function gender( $gender, $forms ) {
3612  if ( !count( $forms ) ) {
3613  return '';
3614  }
3615  $forms = $this->preConvertPlural( $forms, 2 );
3616  if ( $gender === 'male' ) {
3617  return $forms[0];
3618  }
3619  if ( $gender === 'female' ) {
3620  return $forms[1];
3621  }
3622  return isset( $forms[2] ) ? $forms[2] : $forms[0];
3623  }
3624 
3640  function convertPlural( $count, $forms ) {
3641  // Handle explicit n=pluralform cases
3642  $forms = $this->handleExplicitPluralForms( $count, $forms );
3643  if ( is_string( $forms ) ) {
3644  return $forms;
3645  }
3646  if ( !count( $forms ) ) {
3647  return '';
3648  }
3649 
3650  $pluralForm = $this->getPluralRuleIndexNumber( $count );
3651  $pluralForm = min( $pluralForm, count( $forms ) - 1 );
3652  return $forms[$pluralForm];
3653  }
3654 
3670  protected function handleExplicitPluralForms( $count, array $forms ) {
3671  foreach ( $forms as $index => $form ) {
3672  if ( preg_match( '/\d+=/i', $form ) ) {
3673  $pos = strpos( $form, '=' );
3674  if ( substr( $form, 0, $pos ) === (string) $count ) {
3675  return substr( $form, $pos + 1 );
3676  }
3677  unset( $forms[$index] );
3678  }
3679  }
3680  return array_values( $forms );
3681  }
3682 
3691  protected function preConvertPlural( /* Array */ $forms, $count ) {
3692  while ( count( $forms ) < $count ) {
3693  $forms[] = $forms[count( $forms ) - 1];
3694  }
3695  return $forms;
3696  }
3697 
3709  function translateBlockExpiry( $str ) {
3710  $duration = SpecialBlock::getSuggestedDurations( $this );
3711  foreach ( $duration as $show => $value ) {
3712  if ( strcmp( $str, $value ) == 0 ) {
3713  return htmlspecialchars( trim( $show ) );
3714  }
3715  }
3716 
3717  // Since usually only infinite or indefinite is only on list, so try
3718  // equivalents if still here.
3719  $indefs = array( 'infinite', 'infinity', 'indefinite' );
3720  if ( in_array( $str, $indefs ) ) {
3721  foreach ( $indefs as $val ) {
3722  $show = array_search( $val, $duration, true );
3723  if ( $show !== false ) {
3724  return htmlspecialchars( trim( $show ) );
3725  }
3726  }
3727  }
3728 
3729  // If all else fails, return a standard duration or timestamp description.
3730  $time = strtotime( $str, 0 );
3731  if ( $time === false ) { // Unknown format. Return it as-is in case.
3732  return $str;
3733  } elseif ( $time !== strtotime( $str, 1 ) ) { // It's a relative timestamp.
3734  // $time is relative to 0 so it's a duration length.
3735  return $this->formatDuration( $time );
3736  } else { // It's an absolute timestamp.
3737  if ( $time === 0 ) {
3738  // wfTimestamp() handles 0 as current time instead of epoch.
3739  return $this->timeanddate( '19700101000000' );
3740  } else {
3741  return $this->timeanddate( $time );
3742  }
3743  }
3744  }
3745 
3753  public function segmentForDiff( $text ) {
3754  return $text;
3755  }
3756 
3763  public function unsegmentForDiff( $text ) {
3764  return $text;
3765  }
3766 
3773  public function getConverter() {
3774  return $this->mConverter;
3775  }
3776 
3783  public function autoConvertToAllVariants( $text ) {
3784  return $this->mConverter->autoConvertToAllVariants( $text );
3785  }
3786 
3793  public function convert( $text ) {
3794  return $this->mConverter->convert( $text );
3795  }
3796 
3803  public function convertTitle( $title ) {
3804  return $this->mConverter->convertTitle( $title );
3805  }
3806 
3813  public function convertNamespace( $ns ) {
3814  return $this->mConverter->convertNamespace( $ns );
3815  }
3816 
3822  public function hasVariants() {
3823  return count( $this->getVariants() ) > 1;
3824  }
3825 
3833  public function hasVariant( $variant ) {
3834  return (bool)$this->mConverter->validateVariant( $variant );
3835  }
3836 
3844  public function armourMath( $text ) {
3845  return $this->mConverter->armourMath( $text );
3846  }
3847 
3855  public function convertHtml( $text, $isTitle = false ) {
3856  return htmlspecialchars( $this->convert( $text, $isTitle ) );
3857  }
3858 
3863  public function convertCategoryKey( $key ) {
3864  return $this->mConverter->convertCategoryKey( $key );
3865  }
3866 
3873  public function getVariants() {
3874  return $this->mConverter->getVariants();
3875  }
3876 
3880  public function getPreferredVariant() {
3881  return $this->mConverter->getPreferredVariant();
3882  }
3883 
3887  public function getDefaultVariant() {
3888  return $this->mConverter->getDefaultVariant();
3889  }
3890 
3894  public function getURLVariant() {
3895  return $this->mConverter->getURLVariant();
3896  }
3897 
3910  public function findVariantLink( &$link, &$nt, $ignoreOtherCond = false ) {
3911  $this->mConverter->findVariantLink( $link, $nt, $ignoreOtherCond );
3912  }
3913 
3920  function getExtraHashOptions() {
3921  return $this->mConverter->getExtraHashOptions();
3922  }
3923 
3931  public function getParsedTitle() {
3932  return $this->mConverter->getParsedTitle();
3933  }
3934 
3947  public function markNoConversion( $text, $noParse = false ) {
3948  // Excluding protocal-relative URLs may avoid many false positives.
3949  if ( $noParse || preg_match( '/^(?:' . wfUrlProtocolsWithoutProtRel() . ')/', $text ) ) {
3950  return $this->mConverter->markNoConversion( $text );
3951  } else {
3952  return $text;
3953  }
3954  }
3955 
3962  public function linkTrail() {
3963  return self::$dataCache->getItem( $this->mCode, 'linkTrail' );
3964  }
3965 
3972  public function linkPrefixCharset() {
3973  return self::$dataCache->getItem( $this->mCode, 'linkPrefixCharset' );
3974  }
3975 
3979  function getLangObj() {
3980  return $this;
3981  }
3982 
3990  public function getParentLanguage() {
3991  if ( $this->mParentLanguage !== false ) {
3992  return $this->mParentLanguage;
3993  }
3994 
3995  $pieces = explode( '-', $this->getCode() );
3996  $code = $pieces[0];
3997  if ( !in_array( $code, LanguageConverter::$languagesWithVariants ) ) {
3998  $this->mParentLanguage = null;
3999  return null;
4000  }
4001  $lang = Language::factory( $code );
4002  if ( !$lang->hasVariant( $this->getCode() ) ) {
4003  $this->mParentLanguage = null;
4004  return null;
4005  }
4006 
4007  $this->mParentLanguage = $lang;
4008  return $lang;
4009  }
4010 
4019  public function getCode() {
4020  return $this->mCode;
4021  }
4022 
4033  public function getHtmlCode() {
4034  if ( is_null( $this->mHtmlCode ) ) {
4035  $this->mHtmlCode = wfBCP47( $this->getCode() );
4036  }
4037  return $this->mHtmlCode;
4038  }
4039 
4043  public function setCode( $code ) {
4044  $this->mCode = $code;
4045  // Ensure we don't leave incorrect cached data lying around
4046  $this->mHtmlCode = null;
4047  $this->mParentLanguage = false;
4048  }
4049 
4058  public static function getFileName( $prefix = 'Language', $code, $suffix = '.php' ) {
4059  if ( !self::isValidBuiltInCode( $code ) ) {
4060  throw new MWException( "Invalid language code \"$code\"" );
4061  }
4062 
4063  return $prefix . str_replace( '-', '_', ucfirst( $code ) ) . $suffix;
4064  }
4065 
4073  public static function getCodeFromFileName( $filename, $prefix = 'Language', $suffix = '.php' ) {
4074  $m = null;
4075  preg_match( '/' . preg_quote( $prefix, '/' ) . '([A-Z][a-z_]+)' .
4076  preg_quote( $suffix, '/' ) . '/', $filename, $m );
4077  if ( !count( $m ) ) {
4078  return false;
4079  }
4080  return str_replace( '_', '-', strtolower( $m[1] ) );
4081  }
4082 
4087  public static function getMessagesFileName( $code ) {
4088  global $IP;
4089  $file = self::getFileName( "$IP/languages/messages/Messages", $code, '.php' );
4090  wfRunHooks( 'Language::getMessagesFileName', array( $code, &$file ) );
4091  return $file;
4092  }
4093 
4099  public static function getJsonMessagesFileName( $code ) {
4100  global $IP;
4101 
4102  if ( !self::isValidBuiltInCode( $code ) ) {
4103  throw new MWException( "Invalid language code \"$code\"" );
4104  }
4105 
4106  return "$IP/languages/i18n/$code.json" ;
4107  }
4108 
4113  public static function getClassFileName( $code ) {
4114  global $IP;
4115  return self::getFileName( "$IP/languages/classes/Language", $code, '.php' );
4116  }
4117 
4125  public static function getFallbackFor( $code ) {
4126  if ( $code === 'en' || !Language::isValidBuiltInCode( $code ) ) {
4127  return false;
4128  } else {
4129  $fallbacks = self::getFallbacksFor( $code );
4130  $first = array_shift( $fallbacks );
4131  return $first;
4132  }
4133  }
4134 
4142  public static function getFallbacksFor( $code ) {
4143  if ( $code === 'en' || !Language::isValidBuiltInCode( $code ) ) {
4144  return array();
4145  } else {
4146  $v = self::getLocalisationCache()->getItem( $code, 'fallback' );
4147  $v = array_map( 'trim', explode( ',', $v ) );
4148  if ( $v[count( $v ) - 1] !== 'en' ) {
4149  $v[] = 'en';
4150  }
4151  return $v;
4152  }
4153  }
4154 
4163  public static function getFallbacksIncludingSiteLanguage( $code ) {
4164  global $wgLanguageCode;
4165 
4166  // Usually, we will only store a tiny number of fallback chains, so we
4167  // keep them in static memory.
4168  $cacheKey = "{$code}-{$wgLanguageCode}";
4169 
4170  if ( !array_key_exists( $cacheKey, self::$fallbackLanguageCache ) ) {
4171  $fallbacks = self::getFallbacksFor( $code );
4172 
4173  // Append the site's fallback chain, including the site language itself
4174  $siteFallbacks = self::getFallbacksFor( $wgLanguageCode );
4175  array_unshift( $siteFallbacks, $wgLanguageCode );
4176 
4177  // Eliminate any languages already included in the chain
4178  $siteFallbacks = array_diff( $siteFallbacks, $fallbacks );
4179 
4180  self::$fallbackLanguageCache[$cacheKey] = array( $fallbacks, $siteFallbacks );
4181  }
4182  return self::$fallbackLanguageCache[$cacheKey];
4183  }
4184 
4194  public static function getMessagesFor( $code ) {
4195  return self::getLocalisationCache()->getItem( $code, 'messages' );
4196  }
4197 
4206  public static function getMessageFor( $key, $code ) {
4207  return self::getLocalisationCache()->getSubitem( $code, 'messages', $key );
4208  }
4209 
4218  public static function getMessageKeysFor( $code ) {
4219  return self::getLocalisationCache()->getSubItemList( $code, 'messages' );
4220  }
4221 
4226  function fixVariableInNamespace( $talk ) {
4227  if ( strpos( $talk, '$1' ) === false ) {
4228  return $talk;
4229  }
4230 
4231  global $wgMetaNamespace;
4232  $talk = str_replace( '$1', $wgMetaNamespace, $talk );
4233 
4234  # Allow grammar transformations
4235  # Allowing full message-style parsing would make simple requests
4236  # such as action=raw much more expensive than they need to be.
4237  # This will hopefully cover most cases.
4238  $talk = preg_replace_callback( '/{{grammar:(.*?)\|(.*?)}}/i',
4239  array( &$this, 'replaceGrammarInNamespace' ), $talk );
4240  return str_replace( ' ', '_', $talk );
4241  }
4242 
4247  function replaceGrammarInNamespace( $m ) {
4248  return $this->convertGrammar( trim( $m[2] ), trim( $m[1] ) );
4249  }
4250 
4255  static function getCaseMaps() {
4256  static $wikiUpperChars, $wikiLowerChars;
4257  if ( isset( $wikiUpperChars ) ) {
4258  return array( $wikiUpperChars, $wikiLowerChars );
4259  }
4260 
4261  wfProfileIn( __METHOD__ );
4262  $arr = wfGetPrecompiledData( 'Utf8Case.ser' );
4263  if ( $arr === false ) {
4264  throw new MWException(
4265  "Utf8Case.ser is missing, please run \"make\" in the serialized directory\n" );
4266  }
4267  $wikiUpperChars = $arr['wikiUpperChars'];
4268  $wikiLowerChars = $arr['wikiLowerChars'];
4269  wfProfileOut( __METHOD__ );
4270  return array( $wikiUpperChars, $wikiLowerChars );
4271  }
4272 
4284  public function formatExpiry( $expiry, $format = true ) {
4285  static $infinity;
4286  if ( $infinity === null ) {
4287  $infinity = wfGetDB( DB_SLAVE )->getInfinity();
4288  }
4289 
4290  if ( $expiry == '' || $expiry == $infinity ) {
4291  return $format === true
4292  ? $this->getMessageFromDB( 'infiniteblock' )
4293  : $infinity;
4294  } else {
4295  return $format === true
4296  ? $this->timeanddate( $expiry, /* User preference timezone */ true )
4297  : wfTimestamp( $format, $expiry );
4298  }
4299  }
4300 
4311  function formatTimePeriod( $seconds, $format = array() ) {
4312  if ( !is_array( $format ) ) {
4313  $format = array( 'avoid' => $format ); // For backwards compatibility
4314  }
4315  if ( !isset( $format['avoid'] ) ) {
4316  $format['avoid'] = false;
4317  }
4318  if ( !isset( $format['noabbrevs' ] ) ) {
4319  $format['noabbrevs'] = false;
4320  }
4321  $secondsMsg = wfMessage(
4322  $format['noabbrevs'] ? 'seconds' : 'seconds-abbrev' )->inLanguage( $this );
4323  $minutesMsg = wfMessage(
4324  $format['noabbrevs'] ? 'minutes' : 'minutes-abbrev' )->inLanguage( $this );
4325  $hoursMsg = wfMessage(
4326  $format['noabbrevs'] ? 'hours' : 'hours-abbrev' )->inLanguage( $this );
4327  $daysMsg = wfMessage(
4328  $format['noabbrevs'] ? 'days' : 'days-abbrev' )->inLanguage( $this );
4329 
4330  if ( round( $seconds * 10 ) < 100 ) {
4331  $s = $this->formatNum( sprintf( "%.1f", round( $seconds * 10 ) / 10 ) );
4332  $s = $secondsMsg->params( $s )->text();
4333  } elseif ( round( $seconds ) < 60 ) {
4334  $s = $this->formatNum( round( $seconds ) );
4335  $s = $secondsMsg->params( $s )->text();
4336  } elseif ( round( $seconds ) < 3600 ) {
4337  $minutes = floor( $seconds / 60 );
4338  $secondsPart = round( fmod( $seconds, 60 ) );
4339  if ( $secondsPart == 60 ) {
4340  $secondsPart = 0;
4341  $minutes++;
4342  }
4343  $s = $minutesMsg->params( $this->formatNum( $minutes ) )->text();
4344  $s .= ' ';
4345  $s .= $secondsMsg->params( $this->formatNum( $secondsPart ) )->text();
4346  } elseif ( round( $seconds ) <= 2 * 86400 ) {
4347  $hours = floor( $seconds / 3600 );
4348  $minutes = floor( ( $seconds - $hours * 3600 ) / 60 );
4349  $secondsPart = round( $seconds - $hours * 3600 - $minutes * 60 );
4350  if ( $secondsPart == 60 ) {
4351  $secondsPart = 0;
4352  $minutes++;
4353  }
4354  if ( $minutes == 60 ) {
4355  $minutes = 0;
4356  $hours++;
4357  }
4358  $s = $hoursMsg->params( $this->formatNum( $hours ) )->text();
4359  $s .= ' ';
4360  $s .= $minutesMsg->params( $this->formatNum( $minutes ) )->text();
4361  if ( !in_array( $format['avoid'], array( 'avoidseconds', 'avoidminutes' ) ) ) {
4362  $s .= ' ' . $secondsMsg->params( $this->formatNum( $secondsPart ) )->text();
4363  }
4364  } else {
4365  $days = floor( $seconds / 86400 );
4366  if ( $format['avoid'] === 'avoidminutes' ) {
4367  $hours = round( ( $seconds - $days * 86400 ) / 3600 );
4368  if ( $hours == 24 ) {
4369  $hours = 0;
4370  $days++;
4371  }
4372  $s = $daysMsg->params( $this->formatNum( $days ) )->text();
4373  $s .= ' ';
4374  $s .= $hoursMsg->params( $this->formatNum( $hours ) )->text();
4375  } elseif ( $format['avoid'] === 'avoidseconds' ) {
4376  $hours = floor( ( $seconds - $days * 86400 ) / 3600 );
4377  $minutes = round( ( $seconds - $days * 86400 - $hours * 3600 ) / 60 );
4378  if ( $minutes == 60 ) {
4379  $minutes = 0;
4380  $hours++;
4381  }
4382  if ( $hours == 24 ) {
4383  $hours = 0;
4384  $days++;
4385  }
4386  $s = $daysMsg->params( $this->formatNum( $days ) )->text();
4387  $s .= ' ';
4388  $s .= $hoursMsg->params( $this->formatNum( $hours ) )->text();
4389  $s .= ' ';
4390  $s .= $minutesMsg->params( $this->formatNum( $minutes ) )->text();
4391  } else {
4392  $s = $daysMsg->params( $this->formatNum( $days ) )->text();
4393  $s .= ' ';
4394  $s .= $this->formatTimePeriod( $seconds - $days * 86400, $format );
4395  }
4396  }
4397  return $s;
4398  }
4399 
4410  function formatBitrate( $bps ) {
4411  return $this->formatComputingNumbers( $bps, 1000, "bitrate-$1bits" );
4412  }
4413 
4420  function formatComputingNumbers( $size, $boundary, $messageKey ) {
4421  if ( $size <= 0 ) {
4422  return str_replace( '$1', $this->formatNum( $size ),
4423  $this->getMessageFromDB( str_replace( '$1', '', $messageKey ) )
4424  );
4425  }
4426  $sizes = array( '', 'kilo', 'mega', 'giga', 'tera', 'peta', 'exa', 'zeta', 'yotta' );
4427  $index = 0;
4428 
4429  $maxIndex = count( $sizes ) - 1;
4430  while ( $size >= $boundary && $index < $maxIndex ) {
4431  $index++;
4432  $size /= $boundary;
4433  }
4434 
4435  // For small sizes no decimal places necessary
4436  $round = 0;
4437  if ( $index > 1 ) {
4438  // For MB and bigger two decimal places are smarter
4439  $round = 2;
4440  }
4441  $msg = str_replace( '$1', $sizes[$index], $messageKey );
4442 
4443  $size = round( $size, $round );
4444  $text = $this->getMessageFromDB( $msg );
4445  return str_replace( '$1', $this->formatNum( $size ), $text );
4446  }
4447 
4458  function formatSize( $size ) {
4459  return $this->formatComputingNumbers( $size, 1024, "size-$1bytes" );
4460  }
4461 
4471  function specialList( $page, $details, $oppositedm = true ) {
4472  $dirmark = ( $oppositedm ? $this->getDirMark( true ) : '' ) .
4473  $this->getDirMark();
4474  $details = $details ? $dirmark . $this->getMessageFromDB( 'word-separator' ) .
4475  wfMessage( 'parentheses' )->rawParams( $details )->inLanguage( $this )->escaped() : '';
4476  return $page . $details;
4477  }
4478 
4489  public function viewPrevNext( Title $title, $offset, $limit, array $query = array(), $atend = false ) {
4490  // @todo FIXME: Why on earth this needs one message for the text and another one for tooltip?
4491 
4492  # Make 'previous' link
4493  $prev = wfMessage( 'prevn' )->inLanguage( $this )->title( $title )->numParams( $limit )->text();
4494  if ( $offset > 0 ) {
4495  $plink = $this->numLink( $title, max( $offset - $limit, 0 ), $limit,
4496  $query, $prev, 'prevn-title', 'mw-prevlink' );
4497  } else {
4498  $plink = htmlspecialchars( $prev );
4499  }
4500 
4501  # Make 'next' link
4502  $next = wfMessage( 'nextn' )->inLanguage( $this )->title( $title )->numParams( $limit )->text();
4503  if ( $atend ) {
4504  $nlink = htmlspecialchars( $next );
4505  } else {
4506  $nlink = $this->numLink( $title, $offset + $limit, $limit,
4507  $query, $next, 'nextn-title', 'mw-nextlink' );
4508  }
4509 
4510  # Make links to set number of items per page
4511  $numLinks = array();
4512  foreach ( array( 20, 50, 100, 250, 500 ) as $num ) {
4513  $numLinks[] = $this->numLink( $title, $offset, $num,
4514  $query, $this->formatNum( $num ), 'shown-title', 'mw-numlink' );
4515  }
4516 
4517  return wfMessage( 'viewprevnext' )->inLanguage( $this )->title( $title
4518  )->rawParams( $plink, $nlink, $this->pipeList( $numLinks ) )->escaped();
4519  }
4520 
4533  private function numLink( Title $title, $offset, $limit, array $query, $link, $tooltipMsg, $class ) {
4534  $query = array( 'limit' => $limit, 'offset' => $offset ) + $query;
4535  $tooltip = wfMessage( $tooltipMsg )->inLanguage( $this )->title( $title )->numParams( $limit )->text();
4536  return Html::element( 'a', array( 'href' => $title->getLocalURL( $query ),
4537  'title' => $tooltip, 'class' => $class ), $link );
4538  }
4539 
4545  public function getConvRuleTitle() {
4546  return $this->mConverter->getConvRuleTitle();
4547  }
4548 
4554  public function getCompiledPluralRules() {
4555  $pluralRules = self::$dataCache->getItem( strtolower( $this->mCode ), 'compiledPluralRules' );
4556  $fallbacks = Language::getFallbacksFor( $this->mCode );
4557  if ( !$pluralRules ) {
4558  foreach ( $fallbacks as $fallbackCode ) {
4559  $pluralRules = self::$dataCache->getItem( strtolower( $fallbackCode ), 'compiledPluralRules' );
4560  if ( $pluralRules ) {
4561  break;
4562  }
4563  }
4564  }
4565  return $pluralRules;
4566  }
4567 
4573  public function getPluralRules() {
4574  $pluralRules = self::$dataCache->getItem( strtolower( $this->mCode ), 'pluralRules' );
4575  $fallbacks = Language::getFallbacksFor( $this->mCode );
4576  if ( !$pluralRules ) {
4577  foreach ( $fallbacks as $fallbackCode ) {
4578  $pluralRules = self::$dataCache->getItem( strtolower( $fallbackCode ), 'pluralRules' );
4579  if ( $pluralRules ) {
4580  break;
4581  }
4582  }
4583  }
4584  return $pluralRules;
4585  }
4586 
4592  public function getPluralRuleTypes() {
4593  $pluralRuleTypes = self::$dataCache->getItem( strtolower( $this->mCode ), 'pluralRuleTypes' );
4594  $fallbacks = Language::getFallbacksFor( $this->mCode );
4595  if ( !$pluralRuleTypes ) {
4596  foreach ( $fallbacks as $fallbackCode ) {
4597  $pluralRuleTypes = self::$dataCache->getItem( strtolower( $fallbackCode ), 'pluralRuleTypes' );
4598  if ( $pluralRuleTypes ) {
4599  break;
4600  }
4601  }
4602  }
4603  return $pluralRuleTypes;
4604  }
4605 
4610  public function getPluralRuleIndexNumber( $number ) {
4611  $pluralRules = $this->getCompiledPluralRules();
4612  $form = CLDRPluralRuleEvaluator::evaluateCompiled( $number, $pluralRules );
4613  return $form;
4614  }
4615 
4623  public function getPluralRuleType( $number ) {
4624  $index = $this->getPluralRuleIndexNumber( $number );
4625  $pluralRuleTypes = $this->getPluralRuleTypes();
4626  if ( isset( $pluralRuleTypes[$index] ) ) {
4627  return $pluralRuleTypes[$index];
4628  } else {
4629  return 'other';
4630  }
4631  }
4632 }
User\getDefaultOption
static getDefaultOption( $opt)
Get a given default option value.
Definition: User.php:1383
Language\$mCode
$mCode
Definition: Language.php:80
Language\internalUserTimeAndDate
internalUserTimeAndDate( $type, $ts, User $user, array $options)
Internal helper function for userDate(), userTime() and userTimeAndDate()
Definition: Language.php:2171
Language\getFallbacksFor
static getFallbacksFor( $code)
Get the ordered list of fallback languages.
Definition: Language.php:4140
Language\getCodeFromFileName
static getCodeFromFileName( $filename, $prefix='Language', $suffix='.php')
Get the language code from a file name.
Definition: Language.php:4071
Language\classFromCode
static classFromCode( $code)
Definition: Language.php:413
FakeConverter\convertCategoryKey
convertCategoryKey( $key)
Definition: Language.php:63
Language\getExtraHashOptions
getExtraHashOptions()
returns language specific options used by User::getPageRenderHash() for example, the preferred langua...
Definition: Language.php:3918
Language\lc
lc( $str, $first=false)
Definition: Language.php:2498
MWTimestamp
Library for creating and parsing MW-style timestamps.
Definition: MWTimestamp.php:31
FakeConverter\translate
translate( $text, $variant)
Definition: Language.php:67
Language\parseFormattedNumber
parseFormattedNumber( $number)
Definition: Language.php:3130
Language\getURLVariant
getURLVariant()
Definition: Language.php:3892
Language\getVariantname
getVariantname( $code, $usemsg=true)
short names for language variants used for language conversion links.
Definition: Language.php:754
$wgUser
$wgUser
Definition: Setup.php:552
Language\replaceGrammarInNamespace
replaceGrammarInNamespace( $m)
Definition: Language.php:4245
$time
see documentation in includes Linker php for Linker::makeImageLink & $time
Definition: hooks.txt:1358
Language\linkPrefixExtension
linkPrefixExtension()
To allow "foo[[bar]]" to extend the link over the whole word "foobar".
Definition: Language.php:2978
wfBCP47
wfBCP47( $code)
Get the normalised IETF language tag See unit test for examples.
Definition: GlobalFunctions.php:3920
of
globals txt Globals are evil The original MediaWiki code relied on globals for processing context far too often MediaWiki development since then has been a story of slowly moving context out of global variables and into objects Storing processing context in object member variables allows those objects to be reused in a much more flexible way Consider the elegance of
Definition: globals.txt:10
Language\truncate_endBracket
truncate_endBracket(&$tag, $tagType, $lastCh, &$openTags)
truncateHtml() helper function (a) push or pop $tag from $openTags as needed (b) clear $tag value
Definition: Language.php:3549
Language\preloadLanguageClass
static preloadLanguageClass( $class)
Includes language class files.
Definition: Language.php:426
Language\commaList
commaList(array $list)
Take a list of strings and build a locale-friendly comma-separated list, using the local comma-separa...
Definition: Language.php:3259
php
skin txt MediaWiki includes four core it has been set as the default in MediaWiki since the replacing Monobook it had been been the default skin since before being replaced by Vector largely rewritten in while keeping its appearance Several legacy skins were removed in the as the burden of supporting them became too heavy to bear Those in etc for skin dependent CSS etc for skin dependent JavaScript These can also be customised on a per user by etc This feature has led to a wide variety of user styles becoming that gallery is a good place to ending in php
Definition: skin.txt:62
Language\semicolonList
semicolonList(array $list)
Take a list of strings and build a locale-friendly semicolon-separated list, using the local semicolo...
Definition: Language.php:3272
Language\$IRANIAN_DAYS
static $IRANIAN_DAYS
Definition: Language.php:1426
Language\$mIranianCalendarMonthMsgs
static $mIranianCalendarMonthMsgs
Definition: Language.php:125
Language\getIranianCalendarMonthName
getIranianCalendarMonthName( $key)
Definition: Language.php:1029
Language\$mMonthAbbrevMsgs
static $mMonthAbbrevMsgs
Definition: Language.php:120
Language\formatSize
formatSize( $size)
Format a size in bytes for output, using an appropriate unit (B, KB, MB, GB, TB, PB,...
Definition: Language.php:4456
Language\formatTimePeriod
formatTimePeriod( $seconds, $format=array())
Definition: Language.php:4309
Language\separatorTransformTable
separatorTransformTable()
Definition: Language.php:3217
Language\$dataCache
static $dataCache
Definition: Language.php:97
FakeConverter\__construct
__construct( $langobj)
Definition: Language.php:46
Language\resetNamespaces
resetNamespaces()
Resets all of the namespace caches.
Definition: Language.php:556
Language\getConverter
getConverter()
Return the LanguageConverter used in the Language.
Definition: Language.php:3771
Language\hasVariants
hasVariants()
Check if this is a language with variants.
Definition: Language.php:3820
Language\$mWeekdayAbbrevMsgs
static $mWeekdayAbbrevMsgs
Definition: Language.php:106
Language\preConvertPlural
preConvertPlural($forms, $count)
Checks that convertPlural was given an array and pads it to requested amount of forms by copying the ...
Definition: Language.php:3689
Language\segmentByWord
segmentByWord( $string)
Some languages such as Chinese require word segmentation, Specify such segmentation when overridden i...
Definition: Language.php:2659
Language\iconv
iconv( $in, $out, $string)
Definition: Language.php:2356
Language\getMessageFromDB
getMessageFromDB( $msg)
Get a message from the MediaWiki namespace.
Definition: Language.php:948
Language\$GREG_DAYS
static $GREG_DAYS
Definition: Language.php:1425
wfGetDB
& wfGetDB( $db, $groups=array(), $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:3650
Language\convert
convert( $text)
convert text to different variants of a language.
Definition: Language.php:3791
Language\truncate
truncate( $string, $length, $ellipsis='...', $adjustLength=true)
Truncate a string to a specified length in bytes, appending an optional string (e....
Definition: Language.php:3308
Language\getDurationIntervals
getDurationIntervals( $seconds, array $chosenIntervals=array())
Takes a number of seconds and returns an array with a set of corresponding intervals.
Definition: Language.php:2129
Language\convertGrammar
convertGrammar( $word, $case)
Grammatical transformations, needed for inflected languages Invoked by putting {{grammar:case|word}} ...
Definition: Language.php:3571
$form
usually copyright or history_copyright This message must be in HTML not wikitext $subpages will be ignored and the rest of subPageSubtitle() will run. 'SkinTemplateBuildNavUrlsNav_urlsAfterPermalink' whether MediaWiki currently thinks this is a CSS JS page Hooks may change this value to override the return value of Title::isCssOrJsPage(). 'TitleIsAlwaysKnown' whether MediaWiki currently thinks this page is known isMovable() always returns false. $title whether MediaWiki currently thinks this page is movable Hooks may change this value to override the return value of Title::isMovable(). 'TitleIsWikitextPage' whether MediaWiki currently thinks this is a wikitext page Hooks may change this value to override the return value of Title::isWikitextPage() 'TitleMove' use UploadVerification and UploadVerifyFile instead $form
Definition: hooks.txt:2573
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:2483
UtfNormal\cleanUp
static cleanUp( $string)
The ultimate convenience function! Clean up invalid UTF-8 sequences, and convert to normal form C,...
Definition: UtfNormal.php:79
wfProfileIn
wfProfileIn( $functionname)
Begin profiling of a function.
Definition: Profiler.php:33
Language\sprintfDate
sprintfDate( $format, $ts, DateTimeZone $zone=null)
This is a workalike of PHP's date() function, but with better internationalisation,...
Definition: Language.php:1122
$n
$n
Definition: RandomTest.php:76
Language\$mMonthGenMsgs
static $mMonthGenMsgs
Definition: Language.php:115
$ret
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses & $ret
Definition: hooks.txt:1530
wfSuppressWarnings
wfSuppressWarnings( $end=false)
Reference-counted warning suppression.
Definition: GlobalFunctions.php:2387
Language\timeanddate
timeanddate( $ts, $adj=false, $format=true, $timecorrection=false)
Definition: Language.php:2084
Language\getHebrewCalendarMonthNameGen
getHebrewCalendarMonthNameGen( $key)
Definition: Language.php:1045
Language\$mMagicHookDone
$mMagicHookDone
Definition: Language.php:81
FakeConverter\armourMath
armourMath( $text)
Definition: Language.php:65
Language\$mConverter
LanguageConverter $mConverter
Definition: Language.php:78
$limit
if( $sleep) $limit
Definition: importImages.php:99
Language\listToText
listToText(array $l)
Take a list of strings and build a locale-friendly comma-separated list, using the local comma-separa...
Definition: Language.php:3230
Language\getMonthNamesArray
getMonthNamesArray()
Definition: Language.php:974
Language\$namespaceNames
$namespaceNames
Definition: Language.php:87
$s
$s
Definition: mergeMessageFileList.php:156
Language\$mHijriCalendarMonthMsgs
static $mHijriCalendarMonthMsgs
Definition: Language.php:148
Language\getPreferredVariant
getPreferredVariant()
Definition: Language.php:3878
Language\getMonthAbbreviation
getMonthAbbreviation( $key)
Definition: Language.php:994
Language\ucwordbreaksCallbackAscii
ucwordbreaksCallbackAscii( $matches)
Definition: Language.php:2376
$digitGroupingPattern
$digitGroupingPattern
Definition: MessagesAs.php:168
Language\recodeForEdit
recodeForEdit( $s)
Definition: Language.php:2787
Language\markNoConversion
markNoConversion( $text, $noParse=false)
Prepare external link text for conversion.
Definition: Language.php:3945
User\getDatePreference
getDatePreference()
Get the user's preferred date format.
Definition: User.php:2729
Language\getDefaultVariant
getDefaultVariant()
Definition: Language.php:3885
Language\fallback8bitEncoding
fallback8bitEncoding()
Definition: Language.php:2636
Language\getFileName
static getFileName( $prefix='Language', $code, $suffix='.php')
Get the name of a file for a certain language code.
Definition: Language.php:4056
Language\formatBitrate
formatBitrate( $bps)
Format a bitrate for output, using an appropriate unit (bps, kbps, Mbps, Gbps, Tbps,...
Definition: Language.php:4408
Language\convertNamespace
convertNamespace( $ns)
Convert a namespace index to a string in the preferred variant.
Definition: Language.php:3811
Language\getSpecialPageAliases
getSpecialPageAliases()
Get special page names, as an associative array case folded alias => real name.
Definition: Language.php:3048
Language\getMessagesFileName
static getMessagesFileName( $code)
Definition: Language.php:4085
Language\getPluralRuleTypes
getPluralRuleTypes()
Get the plural rule types for the language.
Definition: Language.php:4590
Language\capitalizeAllNouns
capitalizeAllNouns()
Definition: Language.php:2946
$link
set to $title object and return false for a match for latest after cache objects are set use the ContentHandler facility to handle CSS and JavaScript for highlighting & $link
Definition: hooks.txt:2149
Language\specialList
specialList( $page, $details, $oppositedm=true)
Make a list item, used by various special pages.
Definition: Language.php:4469
FakeConverter\autoConvertToAllVariants
autoConvertToAllVariants( $text)
Definition: Language.php:48
$in
$in
Definition: Utf8Test.php:42
Language\getLanguageName
getLanguageName( $code)
Get the native language name of $code.
Definition: Language.php:959
FakeConverter\markNoConversion
markNoConversion( $text, $noParse=false)
Definition: Language.php:62
Language\$mHebrewCalendarMonthGenMsgs
static $mHebrewCalendarMonthGenMsgs
Definition: Language.php:140
Language\userDate
userDate( $ts, User $user, array $options=array())
Get the formatted date for the given timestamp and formatted for the given user.
Definition: Language.php:2210
Language\checkTitleEncoding
checkTitleEncoding( $s)
Definition: Language.php:2622
Language\$mParentLanguage
$mParentLanguage
Definition: Language.php:82
Language\isWellFormedLanguageTag
static isWellFormedLanguageTag( $code, $lenient=false)
Returns true if a language code string is a well-formed language tag according to RFC 5646.
Definition: Language.php:283
Language\getFormattedNamespaces
getFormattedNamespaces()
A convenience function that returns the same thing as getNamespaces() except with the array values ch...
Definition: Language.php:570
MWTimestamp\format
format( $format)
Format the timestamp in a given format.
Definition: MWTimestamp.php:362
Language\lcfirst
lcfirst( $str)
Definition: Language.php:2479
Language\initEncoding
initEncoding()
Definition: Language.php:2776
Language\isKnownLanguageTag
static isKnownLanguageTag( $tag)
Returns true if a language code is an IETF tag known to MediaWiki.
Definition: Language.php:386
Language\emphasize
emphasize( $text)
Italic is unsuitable for some languages.
Definition: Language.php:3067
Language\getFallbackLanguages
getFallbackLanguages()
Definition: Language.php:492
Language\caseFold
caseFold( $s)
Return a case-folded representation of $s.
Definition: Language.php:2614
Language\segmentForDiff
segmentForDiff( $text)
languages like Chinese need to be segmented in order for the diff to be of any use
Definition: Language.php:3751
FakeConverter\convert
convert( $t)
Definition: Language.php:49
Language\translateBlockExpiry
translateBlockExpiry( $str)
Definition: Language.php:3707
MWNamespace\getCanonicalIndex
static getCanonicalIndex( $name)
Returns the index for a given canonical name, or NULL The input must be converted to lower case first...
Definition: Namespace.php:253
Language\getWeekdayAbbreviation
getWeekdayAbbreviation( $key)
Definition: Language.php:1021
Language\needsGenderDistinction
needsGenderDistinction()
Whether this language uses gender-dependent namespace aliases.
Definition: Language.php:632
Language\getParsedTitle
getParsedTitle()
For languages that support multiple variants, the title of an article may be displayed differently in...
Definition: Language.php:3929
Language\userTimeAndDate
userTimeAndDate( $ts, User $user, array $options=array())
Get the formatted date and time for the given timestamp and formatted for the given user.
Definition: Language.php:2256
FakeConverter\getConvRuleTitle
getConvRuleTitle()
Definition: Language.php:58
Language\getLocalisationCache
static getLocalisationCache()
Get the LocalisationCache instance.
Definition: Language.php:443
MWException
MediaWiki exception.
Definition: MWException.php:26
$out
$out
Definition: UtfNormalGenerate.php:167
NS_PROJECT
const NS_PROJECT
Definition: Defines.php:83
Language\$transformData
$transformData
ReplacementArray object caches.
Definition: Language.php:92
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
Definition: GlobalFunctions.php:1127
Language\tsToIranian
static tsToIranian( $ts)
Algorithm by Roozbeh Pournader and Mohammad Toossi to convert Gregorian dates to Iranian dates.
Definition: Language.php:1440
Language\unsegmentForDiff
unsegmentForDiff( $text)
and unsegment to show the result
Definition: Language.php:3761
wfRestoreWarnings
wfRestoreWarnings()
Restore error level to previous value.
Definition: GlobalFunctions.php:2417
Language\fetchLanguageNames
static fetchLanguageNames( $inLanguage=null, $include='mw')
Get an array of language names, indexed by code.
Definition: Language.php:875
Language\truncateHtml
truncateHtml( $text, $length, $ellipsis='...')
Truncate a string of valid HTML to a specified length in bytes, appending an optional string (e....
Definition: Language.php:3408
FakeConverter\validateVariant
validateVariant( $variant=null)
Definition: Language.php:66
Language\normalize
normalize( $s)
Convert a UTF-8 string to normal form C.
Definition: Language.php:2829
Language\getBookstoreList
getBookstoreList()
Exports $wgBookstoreListEn.
Definition: Language.php:500
Html\element
static element( $element, $attribs=array(), $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:148
Language\$mHebrewCalendarMonthMsgs
static $mHebrewCalendarMonthMsgs
Definition: Language.php:132
Language\pipeList
pipeList(array $list)
Same as commaList, but separate it with the pipe instead.
Definition: Language.php:3284
Language\doMagicHook
doMagicHook()
Run the LanguageGetMagic hook once.
Definition: Language.php:2993
Language\convertForSearchResult
convertForSearchResult( $termsArray)
Definition: Language.php:2712
wfUrlProtocolsWithoutProtRel
wfUrlProtocolsWithoutProtRel()
Like wfUrlProtocols(), but excludes '//' from the protocol list.
Definition: GlobalFunctions.php:740
Language\getMessageFor
static getMessageFor( $key, $code)
Get a message for a given language.
Definition: Language.php:4204
FakeConverter\getExtraHashOptions
getExtraHashOptions()
Definition: Language.php:60
Language\getGenderNsText
getGenderNsText( $index, $gender)
Returns gender-dependent namespace alias if available.
Definition: Language.php:619
FakeConverter\convertTo
convertTo( $text, $variant)
Definition: Language.php:50
Language\getHtmlCode
getHtmlCode()
Get the code in Bcp47 format which we can use inside of html lang="" tags.
Definition: Language.php:4031
Language\getNamespaceAliases
getNamespaceAliases()
Definition: Language.php:665
wfProfileOut
wfProfileOut( $functionname='missing')
Stop profiling of a function.
Definition: Profiler.php:46
Language\getLocalNsIndex
getLocalNsIndex( $text)
Get a namespace key by value, case insensitive.
Definition: Language.php:656
Language\addMagicWordsByLang
addMagicWordsByLang( $newWords)
Add magic words to the extension array.
Definition: Language.php:3034
wfMessage
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned and may include noclasses after processing after in associative array form externallinks including delete and has completed for all link tables default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock() - offset Set to overwrite offset parameter in $wgRequest set to '' to unset offset - wrap String Wrap the message in html(usually something like "&lt
Language\getMonthName
getMonthName( $key)
Definition: Language.php:967
Language\$mMonthMsgs
static $mMonthMsgs
Definition: Language.php:110
Language\armourMath
armourMath( $text)
Put custom tags (e.g.
Definition: Language.php:3842
Language\getAllMessages
getAllMessages()
Definition: Language.php:2346
wfRunHooks
wfRunHooks( $event, array $args=array(), $deprecatedVersion=null)
Call hook functions defined in $wgHooks.
Definition: GlobalFunctions.php:4001
Language\getMagic
getMagic( $mw)
Fill a MagicWord object with data from here.
Definition: Language.php:3008
Language\isRTL
isRTL()
For right-to-left language support.
Definition: Language.php:2870
FakeConverter\getVariantFallbacks
getVariantFallbacks( $variant)
Definition: Language.php:54
array
the array() calling protocol came about after MediaWiki 1.4rc1.
List of Api Query prop modules.
Language\userTime
userTime( $ts, User $user, array $options=array())
Get the formatted time for the given timestamp and formatted for the given user.
Definition: Language.php:2233
Language\getNsIndex
getNsIndex( $text)
Get a namespace key by value, case insensitive.
Definition: Language.php:737
Language\getNamespaceIds
getNamespaceIds()
Definition: Language.php:707
Language\getPluralRuleIndexNumber
getPluralRuleIndexNumber( $number)
Find the index number of the plural rule appropriate for the given number.
Definition: Language.php:4608
Language\getMonthNameGen
getMonthNameGen( $key)
Definition: Language.php:986
Language\getFallbackLanguageCode
getFallbackLanguageCode()
Same as getFallbacksFor for current language.
Definition: Language.php:483
MWTimestamp\getTimestamp
getTimestamp( $style=TS_UNIX)
Get the timestamp represented by this object in a certain form.
Definition: MWTimestamp.php:149
Language\$durationIntervals
static $durationIntervals
Definition: Language.php:159
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
Language\convertDoubleWidth
static convertDoubleWidth( $string)
convert double-width roman characters to single-width.
Definition: Language.php:2682
cssjanus.LEFT
string LEFT
Definition: cssjanus.py:48
Language\getCompiledPluralRules
getCompiledPluralRules()
Get the compiled plural rules for the language.
Definition: Language.php:4552
Language\convertCategoryKey
convertCategoryKey( $key)
Definition: Language.php:3861
Language\digitGroupingPattern
digitGroupingPattern()
Definition: Language.php:3203
Language\ucwords
ucwords( $str)
Definition: Language.php:2535
Language\initContLang
initContLang()
Hook which will be called if this is the content language.
Definition: Language.php:476
Language\getPluralRules
getPluralRules()
Get the plural rules for the language.
Definition: Language.php:4571
list
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition: deferred.txt:11
Language\getArrow
getArrow( $direction='forwards')
An arrow, depending on the language direction.
Definition: Language.php:2956
Language\uc
uc( $str, $first=false)
Convert a string to uppercase.
Definition: Language.php:2450
Language\$mVariants
$mVariants
Definition: Language.php:80
$options
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition: hooks.txt:1530
Language\formatNumNoSeparators
formatNumNoSeparators( $number)
Front-end for non-commafied formatNum.
Definition: Language.php:3122
Language\getClassFileName
static getClassFileName( $code)
Definition: Language.php:4111
FakeConverter\getVariants
getVariants()
Definition: Language.php:53
Language\lcCallback
lcCallback( $matches)
Definition: Language.php:2401
Language\viewPrevNext
viewPrevNext(Title $title, $offset, $limit, array $query=array(), $atend=false)
Generate (prev x| next x) (20|50|100...) type links for paging.
Definition: Language.php:4487
Language\$mHtmlCode
$mHtmlCode
Definition: Language.php:82
FakeConverter\$mLang
Language $mLang
Definition: Language.php:45
TS_MW
const TS_MW
MediaWiki concatenated string timestamp (YYYYMMDDHHMMSS)
Definition: GlobalFunctions.php:2431
Language\$mLoaded
$mLoaded
Definition: Language.php:80
Language\isValidBuiltInCode
static isValidBuiltInCode( $code)
Returns true if a language code is of a valid form for the purposes of internal customisation of Medi...
Definition: Language.php:363
$title
presenting them properly to the user as errors is done by the caller $title
Definition: hooks.txt:1324
Language\setNamespaces
setNamespaces(array $namespaces)
Arbitrarily set all of the namespace names at once.
Definition: Language.php:548
Language\linkPrefixCharset
linkPrefixCharset()
A regular expression character set to match legal word-prefixing characters which should be merged on...
Definition: Language.php:3970
NS_USER_TALK
const NS_USER_TALK
Definition: Defines.php:82
ReplacementArray
Replacement array for FSS with fallback to strtr() Supports lazy initialisation of FSS resource.
Definition: StringUtils.php:411
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:336
Language\findVariantLink
findVariantLink(&$link, &$nt, $ignoreOtherCond=false)
If a language supports multiple variants, it is possible that non-existing link in one variant actual...
Definition: Language.php:3908
$matches
if(!defined( 'MEDIAWIKI')) if(!isset( $wgVersion)) $matches
Definition: NoLocalSettings.php:33
Language\fixVariableInNamespace
fixVariableInNamespace( $talk)
Definition: Language.php:4224
$size
$size
Definition: RandomTest.php:75
$value
$value
Definition: styleTest.css.php:45
Language\ucwordbreaks
ucwordbreaks( $str)
capitalize words at word breaks
Definition: Language.php:2567
Language\getNsText
getNsText( $index)
Get a namespace value by key $mw_ns = $wgContLang->getNsText( NS_MEDIAWIKI ); echo $mw_ns; // prints...
Definition: Language.php:588
Language\getLangObj
getLangObj()
Definition: Language.php:3977
Language\getCode
getCode()
Get the RFC 3066 code for this language object.
Definition: Language.php:4017
Language\getPluralRuleType
getPluralRuleType( $number)
Find the plural rule type appropriate for the given number For example, if the language is set to Ara...
Definition: Language.php:4621
$wgNamespaceAliases
$wgNamespaceAliases['Image']
The canonical names of namespaces 6 and 7 are, as of v1.14, "File" and "File_talk".
Definition: Setup.php:142
Language\dateFormat
dateFormat( $usePrefs=true)
This is meant to be used by time(), date(), and timeanddate() to get the date preference they're supp...
Definition: Language.php:1983
options
We ve cleaned up the code here by removing clumps of infrequently used code and moving them off somewhere else It s much easier for someone working with this code to see what s _really_ going and make changes or fix bugs In we can take all the code that deals with the little used title reversing options(say) and put it in one place. Instead of having little title-reversing if-blocks spread all over the codebase in showAnArticle
Language\newFromCode
static newFromCode( $code)
Create a language object for a given language code.
Definition: Language.php:210
FakeConverter\getURLVariant
getURLVariant()
Definition: Language.php:57
Language\ucwordsCallbackMB
ucwordsCallbackMB( $matches)
Definition: Language.php:2410
SpecialBlock\getSuggestedDurations
static getSuggestedDurations( $lang=null)
Get an array of suggested block durations from MediaWiki:Ipboptions.
Definition: SpecialBlock.php:801
FakeConverter\autoConvert
autoConvert( $text, $variant=false)
Definition: Language.php:47
Language\hebrewNumeral
static hebrewNumeral( $num)
Hebrew Gematria number formatting up to 9999.
Definition: Language.php:1834
Language\getFormattedNsText
getFormattedNsText( $index)
A convenience function that returns the same thing as getNsText() except with '_' changed to ' ',...
Definition: Language.php:606
Language\getMessagesFor
static getMessagesFor( $code)
Get all messages for a given language WARNING: this may take a long time.
Definition: Language.php:4192
Language\ucwordbreaksCallbackMB
ucwordbreaksCallbackMB( $matches)
Definition: Language.php:2384
NS_PROJECT_TALK
const NS_PROJECT_TALK
Definition: Defines.php:84
Language\handleExplicitPluralForms
handleExplicitPluralForms( $count, array $forms)
Handles explicit plural forms for Language::convertPlural()
Definition: Language.php:3668
Language\recodeInput
recodeInput( $s)
Definition: Language.php:2803
Language\fetchLanguageName
static fetchLanguageName( $code, $inLanguage=null, $include='all')
Definition: Language.php:936
$user
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a account $user
Definition: hooks.txt:237
Language\getDirMark
getDirMark( $opposite=false)
A hidden direction mark (LRM or RLM), depending on the language direction.
Definition: Language.php:2934
Language\normalizeForSearch
normalizeForSearch( $string)
Some languages have special punctuation need to be normalized.
Definition: Language.php:2670
Language\isValidCode
static isValidCode( $code)
Returns true if a language code string is of a valid form, whether or not it exists.
Definition: Language.php:337
Language\formatExpiry
formatExpiry( $expiry, $format=true)
Decode an expiry (block, protection, etc) which has come from the DB.
Definition: Language.php:4282
FakeConverter
a fake language converter
Definition: Language.php:42
Language\ucCallback
ucCallback( $matches)
Definition: Language.php:2392
Language\userAdjust
userAdjust( $ts, $tz=false)
Used by date() and time() to adjust the time output.
Definition: Language.php:1898
Language\formatComputingNumbers
formatComputingNumbers( $size, $boundary, $messageKey)
Definition: Language.php:4418
$file
if(PHP_SAPI !='cli') $file
Definition: UtfNormalTest2.php:30
Language\ucwordsCallbackWiki
ucwordsCallbackWiki( $matches)
Definition: Language.php:2418
$count
$count
Definition: UtfNormalTest2.php:96
Language\convertHtml
convertHtml( $text, $isTitle=false)
Perform output conversion on a string, and encode for safe HTML output.
Definition: Language.php:3853
Language\getFallbackFor
static getFallbackFor( $code)
Get the first fallback for a given language.
Definition: Language.php:4123
Language\__construct
__construct()
Definition: Language.php:452
Language\getMonthAbbreviationsArray
getMonthAbbreviationsArray()
Definition: Language.php:1001
it
=Architecture==Two class hierarchies are used to provide the functionality associated with the different content models:*Content interface(and AbstractContent base class) define functionality that acts on the concrete content of a page, and *ContentHandler base class provides functionality specific to a content model, but not acting on concrete content. The most important function of ContentHandler is to act as a factory for the appropriate implementation of Content. These Content objects are to be used by MediaWiki everywhere, instead of passing page content around as text. All manipulation and analysis of page content must be done via the appropriate methods of the Content object. For each content model, a subclass of ContentHandler has to be registered with $wgContentHandlers. The ContentHandler object for a given content model can be obtained using ContentHandler::getForModelID($id). Also Title, WikiPage and Revision now have getContentHandler() methods for convenience. ContentHandler objects are singletons that provide functionality specific to the content type, but not directly acting on the content of some page. ContentHandler::makeEmptyContent() and ContentHandler::unserializeContent() can be used to create a Content object of the appropriate type. However, it is recommended to instead use WikiPage::getContent() resp. Revision::getContent() to get a page 's content as a Content object. These two methods should be the ONLY way in which page content is accessed. Another important function of ContentHandler objects is to define custom action handlers for a content model, see ContentHandler::getActionOverrides(). This is similar to what WikiPage::getActionOverrides() was already doing.==Serialization==With the ContentHandler facility, page content no longer has to be text based. Objects implementing the Content interface are used to represent and handle the content internally. For storage and data exchange, each content model supports at least one serialization format via ContentHandler::serializeContent($content). The list of supported formats for a given content model can be accessed using ContentHandler::getSupportedFormats(). Content serialization formats are identified using MIME type like strings. The following formats are built in:*text/x-wiki - wikitext *text/javascript - for js pages *text/css - for css pages *text/plain - for future use, e.g. with plain text messages. *text/html - for future use, e.g. with plain html messages. *application/vnd.php.serialized - for future use with the api and for extensions *application/json - for future use with the api, and for use by extensions *application/xml - for future use with the api, and for use by extensions In PHP, use the corresponding CONTENT_FORMAT_XXX constant. Note that when using the API to access page content, especially action=edit, action=parse and action=query &prop=revisions, the model and format of the content should always be handled explicitly. Without that information, interpretation of the provided content is not reliable. The same applies to XML dumps generated via maintenance/dumpBackup.php or Special:Export. Also note that the API will provide encapsulated, serialized content - so if the API was called with format=json, and contentformat is also json(or rather, application/json), the page content is represented as a string containing an escaped json structure. Extensions that use JSON to serialize some types of page content may provide specialized API modules that allow access to that content in a more natural form.==Compatibility==The ContentHandler facility is introduced in a way that should allow all existing code to keep functioning at least for pages that contain wikitext or other text based content. However, a number of functions and hooks have been deprecated in favor of new versions that are aware of the page 's content model, and will now generate warnings when used. Most importantly, the following functions have been deprecated:*Revisions::getText() and Revisions::getRawText() is deprecated in favor Revisions::getContent() *WikiPage::getText() is deprecated in favor WikiPage::getContent() Also, the old Article::getContent()(which returns text) is superceded by Article::getContentObject(). However, both methods should be avoided since they do not provide clean access to the page 's actual content. For instance, they may return a system message for non-existing pages. Use WikiPage::getContent() instead. Code that relies on a textual representation of the page content should eventually be rewritten. However, ContentHandler::getContentText() provides a stop-gap that can be used to get text for a page. Its behavior is controlled by $wgContentHandlerTextFallback it
Definition: contenthandler.txt:107
Language\getParentLanguage
getParentLanguage()
Get the "parent" language which has a converter to convert a "compatible" language (in another varian...
Definition: Language.php:3988
Language\$dateFormatStrings
$dateFormatStrings
Definition: Language.php:84
Language\hasWordBreaks
hasWordBreaks()
Most writing systems use whitespace to break up words.
Definition: Language.php:2648
Language\getDir
getDir()
Return the correct HTML 'dir' attribute value for this language.
Definition: Language.php:2878
Language\date
date( $ts, $adj=false, $format=true, $timecorrection=false)
Definition: Language.php:2045
Language\alignEnd
alignEnd()
Return 'right' or 'left' as appropriate alignment for line-end for this language's text direction.
Definition: Language.php:2902
DB_SLAVE
const DB_SLAVE
Definition: Defines.php:55
Title
Represents a title within MediaWiki.
Definition: Title.php:35
Language\truncate_skip
truncate_skip(&$ret, $text, $search, $start, $len=null)
truncateHtml() helper function like strcspn() but adds the skipped chars to $ret
Definition: Language.php:3526
Language\setCode
setCode( $code)
Definition: Language.php:4041
$namespaces
namespace and then decline to actually register it & $namespaces
Definition: hooks.txt:815
Language\getDefaultDateFormat
getDefaultDateFormat()
Definition: Language.php:797
Language\getVariants
getVariants()
Get the list of variants supported by this language see sample implementation in LanguageZh....
Definition: Language.php:3871
Language\convertPlural
convertPlural( $count, $forms)
Plural form transformations, needed for some languages.
Definition: Language.php:3638
Language\$fallbackLanguageCache
static $fallbackLanguageCache
Definition: Language.php:177
$cache
$cache
Definition: mcc.php:32
FakeConverter\findVariantLink
findVariantLink(&$l, &$n, $ignoreOtherCond=false)
Definition: Language.php:59
Language\$mLangObjCache
static $mLangObjCache
Definition: Language.php:99
wfGetPrecompiledData
wfGetPrecompiledData( $name)
Get an object from the precompiled serialized directory.
Definition: GlobalFunctions.php:3552
Language\getFallbacksIncludingSiteLanguage
static getFallbacksIncludingSiteLanguage( $code)
Get the ordered list of fallback languages, ending with the fallback language chain for the site lang...
Definition: Language.php:4161
$coreLanguageNames
$coreLanguageNames
These determine things like interwikis, language selectors, and so on.
Definition: Names.php:40
Language\getHumanTimestamp
getHumanTimestamp(MWTimestamp $ts, MWTimestamp $relativeTo, User $user)
Convert an MWTimestamp into a pretty human-readable timestamp using the given user preferences and re...
Definition: Language.php:2275
Language\getGrammarForms
getGrammarForms()
Get the grammar forms for the content language.
Definition: Language.php:3583
FakeConverter\getParsedTitle
getParsedTitle()
Definition: Language.php:61
Language\removeBadCharFirst
removeBadCharFirst( $string)
Remove bytes that represent an incomplete Unicode character at the start of string (e....
Definition: Language.php:3382
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
MWNamespace\getCanonicalNamespaces
static getCanonicalNamespaces( $rebuild=false)
Returns array of all defined namespaces with their canonical (English) names.
Definition: Namespace.php:218
Language\getHijriCalendarMonthName
getHijriCalendarMonthName( $key)
Definition: Language.php:1053
Language\getMessage
getMessage( $key)
Definition: Language.php:2339
Language\getMessageKeysFor
static getMessageKeysFor( $code)
Get all message keys for a given language.
Definition: Language.php:4216
cssjanus.RIGHT
string RIGHT
Definition: cssjanus.py:49
Language\getJsonMessagesFileName
static getJsonMessagesFileName( $code)
Definition: Language.php:4097
Language\firstChar
firstChar( $s)
Get the first character of a string.
Definition: Language.php:2724
NS_USER
const NS_USER
Definition: Defines.php:81
Language\formatDuration
formatDuration( $seconds, array $chosenIntervals=array())
Takes a number of seconds and turns it into a text using values such as hours and minutes.
Definition: Language.php:2103
Language\getNamespaces
getNamespaces()
Returns an array of localised namespaces indexed by their numbers.
Definition: Language.php:510
Language\transformUsingPairFile
transformUsingPairFile( $file, $string)
Transform a string using serialized data stored in the given file (which must be in the serialized su...
Definition: Language.php:2854
utf8ToCodepoint
utf8ToCodepoint( $char)
Determine the Unicode codepoint of a single-character UTF-8 sequence.
Definition: UtfNormalUtil.php:94
Language\removeBadCharLast
removeBadCharLast( $string)
Remove bytes that represent an incomplete Unicode character at the end of string (e....
Definition: Language.php:3357
Language\romanNumeral
static romanNumeral( $num)
Roman number formatting up to 10000.
Definition: Language.php:1804
FakeConverter\getPreferredVariant
getPreferredVariant()
Definition: Language.php:55
Language\getConvRuleTitle
getConvRuleTitle()
Get the conversion rule title, if any.
Definition: Language.php:4543
StringUtils\isUtf8
static isUtf8( $value, $disableMbstring=false)
Test whether a string is valid UTF-8.
Definition: StringUtils.php:51
Language\factory
static factory( $code)
Get a cached or new language object for a given language code.
Definition: Language.php:184
Language\$namespaceAliases
$namespaceAliases
Definition: Language.php:87
Language\getCaseMaps
static getCaseMaps()
Definition: Language.php:4253
Title\getTitleInvalidRegex
static getTitleInvalidRegex()
Returns a simple regex that will match on characters and sequences invalid in titles.
Definition: Title.php:543
Language\tsToHijri
static tsToHijri( $ts)
Converting Gregorian dates to Hijri dates.
Definition: Language.php:1498
$t
$t
Definition: testCompression.php:65
Language\time
time( $ts, $adj=false, $format=true, $timecorrection=false)
Definition: Language.php:2064
MWTimestamp\diff
diff(MWTimestamp $relativeTo)
Calculate the difference between two MWTimestamp objects.
Definition: MWTimestamp.php:326
Language\insertSpace
static insertSpace( $string, $pattern)
Definition: Language.php:2702
Language\isMultibyte
isMultibyte( $str)
Definition: Language.php:2527
Language\alignStart
alignStart()
Return 'left' or 'right' as appropriate alignment for line-start for this language's text direction.
Definition: Language.php:2890
Language\isSupportedLanguage
static isSupportedLanguage( $code)
Checks whether any localisation is available for that language tag in MediaWiki (MessagesXx....
Definition: Language.php:261
Language\$mWeekdayMsgs
static $mWeekdayMsgs
Definition: Language.php:101
$query
return true to allow those checks to and false if checking is done use this to change the tables headers temp or archived zone change it to an object instance and return false override the list derivative used the name of the old file when set the default code will be skipped add a value to it if you want to add a cookie that have to vary cache options can modify $query
Definition: hooks.txt:1105
FakeConverter\convertNamespace
convertNamespace( $ns)
Definition: Language.php:52
Language\digitTransformTable
digitTransformTable()
Definition: Language.php:3210
Language\tsToYear
static tsToYear( $ts, $cName)
Algorithm to convert Gregorian dates to Thai solar dates, Minguo dates or Minguo dates.
Definition: Language.php:1727
Language\getDirMarkEntity
getDirMarkEntity( $opposite=false)
A hidden direction mark (LRM or RLM), depending on the language direction.
Definition: Language.php:2917
Language\hebrewYearStart
static hebrewYearStart( $year)
This calculates the Hebrew year start, as days since 1 September.
Definition: Language.php:1689
Language\$mNamespaceIds
$mNamespaceIds
Definition: Language.php:87
$IP
$IP
Definition: WebStart.php:88
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:59
Language\getDateFormatString
getDateFormatString( $type, $pref)
Get a format string for a given type and preference.
Definition: Language.php:2013
Language\getMagicWords
getMagicWords()
Get all magic words from cache.
Definition: Language.php:2986
Language\hasVariant
hasVariant( $variant)
Check if the language has the specific variant.
Definition: Language.php:3831
Language\numLink
numLink(Title $title, $offset, $limit, array $query, $link, $tooltipMsg, $class)
Helper function for viewPrevNext() that generates links.
Definition: Language.php:4531
Language\__destruct
__destruct()
Reduce memory usage.
Definition: Language.php:466
Language\commafy
commafy( $number)
Adds commas to a given number.
Definition: Language.php:3151
FakeConverter\getDefaultVariant
getDefaultVariant()
Definition: Language.php:56
Language
Internationalisation code.
Definition: Language.php:74
Language\$mExtendedSpecialPageAliases
$mExtendedSpecialPageAliases
Definition: Language.php:85
Language\tsToHebrew
static tsToHebrew( $ts)
Converting Gregorian dates to Hebrew dates.
Definition: Language.php:1548
Language\autoConvertToAllVariants
autoConvertToAllVariants( $text)
convert text to all supported variants
Definition: Language.php:3781
Language\linkTrail
linkTrail()
A regular expression to match legal word-trailing characters which should be merged onto a link of th...
Definition: Language.php:3960
Language\ucfirst
ucfirst( $str)
Make a string's first character uppercase.
Definition: Language.php:2430
Language\$mMagicExtensions
$mMagicExtensions
Definition: Language.php:81
FakeConverter\convertTitle
convertTitle( $t)
Definition: Language.php:51
Language\getWeekdayName
getWeekdayName( $key)
Definition: Language.php:1013
Language\gender
gender( $gender, $forms)
Provides an alternative text depending on specified gender.
Definition: Language.php:3609
CLDRPluralRuleEvaluator\evaluateCompiled
static evaluateCompiled( $number, array $rules)
Evaluate a compiled set of rules returned by compile().
Definition: CLDRPluralRuleEvaluator.php:73
Language\getHebrewCalendarMonthName
getHebrewCalendarMonthName( $key)
Definition: Language.php:1037
Language\formatNum
formatNum( $number, $nocommafy=false)
Normally we output all numbers in plain en_US style, that is 293,291.235 for twohundredninetythreetho...
Definition: Language.php:3094
$type
$type
Definition: testCompression.php:46
Language\convertTitle
convertTitle( $title)
Convert a Title object to a string in the preferred variant.
Definition: Language.php:3801