MediaWiki  1.23.12
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  // If passed an invalid language code to use, fallback to en
887  if ( $inLanguage !== null && !Language::isValidCode( $inLanguage ) ) {
888  $inLanguage = 'en';
889  }
890 
891  $names = array();
892 
893  if ( $inLanguage ) {
894  # TODO: also include when $inLanguage is null, when this code is more efficient
895  wfRunHooks( 'LanguageGetTranslatedLanguageNames', array( &$names, $inLanguage ) );
896  }
897 
898  $mwNames = $wgExtraLanguageNames + $coreLanguageNames;
899  foreach ( $mwNames as $mwCode => $mwName ) {
900  # - Prefer own MediaWiki native name when not using the hook
901  # - For other names just add if not added through the hook
902  if ( $mwCode === $inLanguage || !isset( $names[$mwCode] ) ) {
903  $names[$mwCode] = $mwName;
904  }
905  }
906 
907  if ( $include === 'all' ) {
908  return $names;
909  }
910 
911  $returnMw = array();
912  $coreCodes = array_keys( $mwNames );
913  foreach ( $coreCodes as $coreCode ) {
914  $returnMw[$coreCode] = $names[$coreCode];
915  }
916 
917  if ( $include === 'mwfile' ) {
918  $namesMwFile = array();
919  # We do this using a foreach over the codes instead of a directory
920  # loop so that messages files in extensions will work correctly.
921  foreach ( $returnMw as $code => $value ) {
922  if ( is_readable( self::getMessagesFileName( $code ) )
923  || is_readable( self::getJsonMessagesFileName( $code ) )
924  ) {
925  $namesMwFile[$code] = $names[$code];
926  }
927  }
928 
929  return $namesMwFile;
930  }
931 
932  # 'mw' option; default if it's not one of the other two options (all/mwfile)
933  return $returnMw;
934  }
935 
943  public static function fetchLanguageName( $code, $inLanguage = null, $include = 'all' ) {
944  $code = strtolower( $code );
945  $array = self::fetchLanguageNames( $inLanguage, $include );
946  return !array_key_exists( $code, $array ) ? '' : $array[$code];
947  }
948 
955  function getMessageFromDB( $msg ) {
956  return wfMessage( $msg )->inLanguage( $this )->text();
957  }
958 
966  function getLanguageName( $code ) {
967  return self::fetchLanguageName( $code );
968  }
969 
974  function getMonthName( $key ) {
975  return $this->getMessageFromDB( self::$mMonthMsgs[$key - 1] );
976  }
977 
981  function getMonthNamesArray() {
982  $monthNames = array( '' );
983  for ( $i = 1; $i < 13; $i++ ) {
984  $monthNames[] = $this->getMonthName( $i );
985  }
986  return $monthNames;
987  }
988 
993  function getMonthNameGen( $key ) {
994  return $this->getMessageFromDB( self::$mMonthGenMsgs[$key - 1] );
995  }
996 
1001  function getMonthAbbreviation( $key ) {
1002  return $this->getMessageFromDB( self::$mMonthAbbrevMsgs[$key - 1] );
1003  }
1004 
1008  function getMonthAbbreviationsArray() {
1009  $monthNames = array( '' );
1010  for ( $i = 1; $i < 13; $i++ ) {
1011  $monthNames[] = $this->getMonthAbbreviation( $i );
1012  }
1013  return $monthNames;
1014  }
1015 
1020  function getWeekdayName( $key ) {
1021  return $this->getMessageFromDB( self::$mWeekdayMsgs[$key - 1] );
1022  }
1023 
1028  function getWeekdayAbbreviation( $key ) {
1029  return $this->getMessageFromDB( self::$mWeekdayAbbrevMsgs[$key - 1] );
1030  }
1031 
1036  function getIranianCalendarMonthName( $key ) {
1037  return $this->getMessageFromDB( self::$mIranianCalendarMonthMsgs[$key - 1] );
1038  }
1039 
1044  function getHebrewCalendarMonthName( $key ) {
1045  return $this->getMessageFromDB( self::$mHebrewCalendarMonthMsgs[$key - 1] );
1046  }
1047 
1052  function getHebrewCalendarMonthNameGen( $key ) {
1053  return $this->getMessageFromDB( self::$mHebrewCalendarMonthGenMsgs[$key - 1] );
1054  }
1055 
1060  function getHijriCalendarMonthName( $key ) {
1061  return $this->getMessageFromDB( self::$mHijriCalendarMonthMsgs[$key - 1] );
1062  }
1063 
1129  function sprintfDate( $format, $ts, DateTimeZone $zone = null ) {
1130  $s = '';
1131  $raw = false;
1132  $roman = false;
1133  $hebrewNum = false;
1134  $dateTimeObj = false;
1135  $rawToggle = false;
1136  $iranian = false;
1137  $hebrew = false;
1138  $hijri = false;
1139  $thai = false;
1140  $minguo = false;
1141  $tenno = false;
1142 
1143  if ( strlen( $ts ) !== 14 ) {
1144  throw new MWException( __METHOD__ . ": The timestamp $ts should have 14 characters" );
1145  }
1146 
1147  if ( !ctype_digit( $ts ) ) {
1148  throw new MWException( __METHOD__ . ": The timestamp $ts should be a number" );
1149  }
1150 
1151  for ( $p = 0; $p < strlen( $format ); $p++ ) {
1152  $num = false;
1153  $code = $format[$p];
1154  if ( $code == 'x' && $p < strlen( $format ) - 1 ) {
1155  $code .= $format[++$p];
1156  }
1157 
1158  if ( ( $code === 'xi' || $code == 'xj' || $code == 'xk' || $code == 'xm' || $code == 'xo' || $code == 'xt' ) && $p < strlen( $format ) - 1 ) {
1159  $code .= $format[++$p];
1160  }
1161 
1162  switch ( $code ) {
1163  case 'xx':
1164  $s .= 'x';
1165  break;
1166  case 'xn':
1167  $raw = true;
1168  break;
1169  case 'xN':
1170  $rawToggle = !$rawToggle;
1171  break;
1172  case 'xr':
1173  $roman = true;
1174  break;
1175  case 'xh':
1176  $hebrewNum = true;
1177  break;
1178  case 'xg':
1179  $s .= $this->getMonthNameGen( substr( $ts, 4, 2 ) );
1180  break;
1181  case 'xjx':
1182  if ( !$hebrew ) {
1183  $hebrew = self::tsToHebrew( $ts );
1184  }
1185  $s .= $this->getHebrewCalendarMonthNameGen( $hebrew[1] );
1186  break;
1187  case 'd':
1188  $num = substr( $ts, 6, 2 );
1189  break;
1190  case 'D':
1191  if ( !$dateTimeObj ) {
1192  $dateTimeObj = DateTime::createFromFormat(
1193  'YmdHis', $ts, $zone ?: new DateTimeZone( 'UTC' )
1194  );
1195  }
1196  $s .= $this->getWeekdayAbbreviation( $dateTimeObj->format( 'w' ) + 1 );
1197  break;
1198  case 'j':
1199  $num = intval( substr( $ts, 6, 2 ) );
1200  break;
1201  case 'xij':
1202  if ( !$iranian ) {
1203  $iranian = self::tsToIranian( $ts );
1204  }
1205  $num = $iranian[2];
1206  break;
1207  case 'xmj':
1208  if ( !$hijri ) {
1209  $hijri = self::tsToHijri( $ts );
1210  }
1211  $num = $hijri[2];
1212  break;
1213  case 'xjj':
1214  if ( !$hebrew ) {
1215  $hebrew = self::tsToHebrew( $ts );
1216  }
1217  $num = $hebrew[2];
1218  break;
1219  case 'l':
1220  if ( !$dateTimeObj ) {
1221  $dateTimeObj = DateTime::createFromFormat(
1222  'YmdHis', $ts, $zone ?: new DateTimeZone( 'UTC' )
1223  );
1224  }
1225  $s .= $this->getWeekdayName( $dateTimeObj->format( 'w' ) + 1 );
1226  break;
1227  case 'F':
1228  $s .= $this->getMonthName( substr( $ts, 4, 2 ) );
1229  break;
1230  case 'xiF':
1231  if ( !$iranian ) {
1232  $iranian = self::tsToIranian( $ts );
1233  }
1234  $s .= $this->getIranianCalendarMonthName( $iranian[1] );
1235  break;
1236  case 'xmF':
1237  if ( !$hijri ) {
1238  $hijri = self::tsToHijri( $ts );
1239  }
1240  $s .= $this->getHijriCalendarMonthName( $hijri[1] );
1241  break;
1242  case 'xjF':
1243  if ( !$hebrew ) {
1244  $hebrew = self::tsToHebrew( $ts );
1245  }
1246  $s .= $this->getHebrewCalendarMonthName( $hebrew[1] );
1247  break;
1248  case 'm':
1249  $num = substr( $ts, 4, 2 );
1250  break;
1251  case 'M':
1252  $s .= $this->getMonthAbbreviation( substr( $ts, 4, 2 ) );
1253  break;
1254  case 'n':
1255  $num = intval( substr( $ts, 4, 2 ) );
1256  break;
1257  case 'xin':
1258  if ( !$iranian ) {
1259  $iranian = self::tsToIranian( $ts );
1260  }
1261  $num = $iranian[1];
1262  break;
1263  case 'xmn':
1264  if ( !$hijri ) {
1265  $hijri = self::tsToHijri ( $ts );
1266  }
1267  $num = $hijri[1];
1268  break;
1269  case 'xjn':
1270  if ( !$hebrew ) {
1271  $hebrew = self::tsToHebrew( $ts );
1272  }
1273  $num = $hebrew[1];
1274  break;
1275  case 'xjt':
1276  if ( !$hebrew ) {
1277  $hebrew = self::tsToHebrew( $ts );
1278  }
1279  $num = $hebrew[3];
1280  break;
1281  case 'Y':
1282  $num = substr( $ts, 0, 4 );
1283  break;
1284  case 'xiY':
1285  if ( !$iranian ) {
1286  $iranian = self::tsToIranian( $ts );
1287  }
1288  $num = $iranian[0];
1289  break;
1290  case 'xmY':
1291  if ( !$hijri ) {
1292  $hijri = self::tsToHijri( $ts );
1293  }
1294  $num = $hijri[0];
1295  break;
1296  case 'xjY':
1297  if ( !$hebrew ) {
1298  $hebrew = self::tsToHebrew( $ts );
1299  }
1300  $num = $hebrew[0];
1301  break;
1302  case 'xkY':
1303  if ( !$thai ) {
1304  $thai = self::tsToYear( $ts, 'thai' );
1305  }
1306  $num = $thai[0];
1307  break;
1308  case 'xoY':
1309  if ( !$minguo ) {
1310  $minguo = self::tsToYear( $ts, 'minguo' );
1311  }
1312  $num = $minguo[0];
1313  break;
1314  case 'xtY':
1315  if ( !$tenno ) {
1316  $tenno = self::tsToYear( $ts, 'tenno' );
1317  }
1318  $num = $tenno[0];
1319  break;
1320  case 'y':
1321  $num = substr( $ts, 2, 2 );
1322  break;
1323  case 'xiy':
1324  if ( !$iranian ) {
1325  $iranian = self::tsToIranian( $ts );
1326  }
1327  $num = substr( $iranian[0], -2 );
1328  break;
1329  case 'a':
1330  $s .= intval( substr( $ts, 8, 2 ) ) < 12 ? 'am' : 'pm';
1331  break;
1332  case 'A':
1333  $s .= intval( substr( $ts, 8, 2 ) ) < 12 ? 'AM' : 'PM';
1334  break;
1335  case 'g':
1336  $h = substr( $ts, 8, 2 );
1337  $num = $h % 12 ? $h % 12 : 12;
1338  break;
1339  case 'G':
1340  $num = intval( substr( $ts, 8, 2 ) );
1341  break;
1342  case 'h':
1343  $h = substr( $ts, 8, 2 );
1344  $num = sprintf( '%02d', $h % 12 ? $h % 12 : 12 );
1345  break;
1346  case 'H':
1347  $num = substr( $ts, 8, 2 );
1348  break;
1349  case 'i':
1350  $num = substr( $ts, 10, 2 );
1351  break;
1352  case 's':
1353  $num = substr( $ts, 12, 2 );
1354  break;
1355  case 'c':
1356  case 'r':
1357  case 'e':
1358  case 'O':
1359  case 'P':
1360  case 'T':
1361  // Pass through string from $dateTimeObj->format()
1362  if ( !$dateTimeObj ) {
1363  $dateTimeObj = DateTime::createFromFormat(
1364  'YmdHis', $ts, $zone ?: new DateTimeZone( 'UTC' )
1365  );
1366  }
1367  $s .= $dateTimeObj->format( $code );
1368  break;
1369  case 'w':
1370  case 'N':
1371  case 'z':
1372  case 'W':
1373  case 't':
1374  case 'L':
1375  case 'o':
1376  case 'U':
1377  case 'I':
1378  case 'Z':
1379  // Pass through number from $dateTimeObj->format()
1380  if ( !$dateTimeObj ) {
1381  $dateTimeObj = DateTime::createFromFormat(
1382  'YmdHis', $ts, $zone ?: new DateTimeZone( 'UTC' )
1383  );
1384  }
1385  $num = $dateTimeObj->format( $code );
1386  break;
1387  case '\\':
1388  # Backslash escaping
1389  if ( $p < strlen( $format ) - 1 ) {
1390  $s .= $format[++$p];
1391  } else {
1392  $s .= '\\';
1393  }
1394  break;
1395  case '"':
1396  # Quoted literal
1397  if ( $p < strlen( $format ) - 1 ) {
1398  $endQuote = strpos( $format, '"', $p + 1 );
1399  if ( $endQuote === false ) {
1400  # No terminating quote, assume literal "
1401  $s .= '"';
1402  } else {
1403  $s .= substr( $format, $p + 1, $endQuote - $p - 1 );
1404  $p = $endQuote;
1405  }
1406  } else {
1407  # Quote at end of string, assume literal "
1408  $s .= '"';
1409  }
1410  break;
1411  default:
1412  $s .= $format[$p];
1413  }
1414  if ( $num !== false ) {
1415  if ( $rawToggle || $raw ) {
1416  $s .= $num;
1417  $raw = false;
1418  } elseif ( $roman ) {
1419  $s .= Language::romanNumeral( $num );
1420  $roman = false;
1421  } elseif ( $hebrewNum ) {
1422  $s .= self::hebrewNumeral( $num );
1423  $hebrewNum = false;
1424  } else {
1425  $s .= $this->formatNum( $num, true );
1426  }
1427  }
1428  }
1429  return $s;
1430  }
1432  private static $GREG_DAYS = array( 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );
1433  private static $IRANIAN_DAYS = array( 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29 );
1434 
1447  private static function tsToIranian( $ts ) {
1448  $gy = substr( $ts, 0, 4 ) -1600;
1449  $gm = substr( $ts, 4, 2 ) -1;
1450  $gd = substr( $ts, 6, 2 ) -1;
1451 
1452  # Days passed from the beginning (including leap years)
1453  $gDayNo = 365 * $gy
1454  + floor( ( $gy + 3 ) / 4 )
1455  - floor( ( $gy + 99 ) / 100 )
1456  + floor( ( $gy + 399 ) / 400 );
1457 
1458  // Add days of the past months of this year
1459  for ( $i = 0; $i < $gm; $i++ ) {
1460  $gDayNo += self::$GREG_DAYS[$i];
1461  }
1462 
1463  // Leap years
1464  if ( $gm > 1 && ( ( $gy % 4 === 0 && $gy % 100 !== 0 || ( $gy % 400 == 0 ) ) ) ) {
1465  $gDayNo++;
1466  }
1467 
1468  // Days passed in current month
1469  $gDayNo += (int)$gd;
1470 
1471  $jDayNo = $gDayNo - 79;
1472 
1473  $jNp = floor( $jDayNo / 12053 );
1474  $jDayNo %= 12053;
1475 
1476  $jy = 979 + 33 * $jNp + 4 * floor( $jDayNo / 1461 );
1477  $jDayNo %= 1461;
1478 
1479  if ( $jDayNo >= 366 ) {
1480  $jy += floor( ( $jDayNo - 1 ) / 365 );
1481  $jDayNo = floor( ( $jDayNo - 1 ) % 365 );
1482  }
1483 
1484  for ( $i = 0; $i < 11 && $jDayNo >= self::$IRANIAN_DAYS[$i]; $i++ ) {
1485  $jDayNo -= self::$IRANIAN_DAYS[$i];
1486  }
1487 
1488  $jm = $i + 1;
1489  $jd = $jDayNo + 1;
1490 
1491  return array( $jy, $jm, $jd );
1492  }
1493 
1505  private static function tsToHijri( $ts ) {
1506  $year = substr( $ts, 0, 4 );
1507  $month = substr( $ts, 4, 2 );
1508  $day = substr( $ts, 6, 2 );
1509 
1510  $zyr = $year;
1511  $zd = $day;
1512  $zm = $month;
1513  $zy = $zyr;
1514 
1515  if (
1516  ( $zy > 1582 ) || ( ( $zy == 1582 ) && ( $zm > 10 ) ) ||
1517  ( ( $zy == 1582 ) && ( $zm == 10 ) && ( $zd > 14 ) )
1518  ) {
1519  $zjd = (int)( ( 1461 * ( $zy + 4800 + (int)( ( $zm - 14 ) / 12 ) ) ) / 4 ) +
1520  (int)( ( 367 * ( $zm - 2 - 12 * ( (int)( ( $zm - 14 ) / 12 ) ) ) ) / 12 ) -
1521  (int)( ( 3 * (int)( ( ( $zy + 4900 + (int)( ( $zm - 14 ) / 12 ) ) / 100 ) ) ) / 4 ) +
1522  $zd - 32075;
1523  } else {
1524  $zjd = 367 * $zy - (int)( ( 7 * ( $zy + 5001 + (int)( ( $zm - 9 ) / 7 ) ) ) / 4 ) +
1525  (int)( ( 275 * $zm ) / 9 ) + $zd + 1729777;
1526  }
1527 
1528  $zl = $zjd -1948440 + 10632;
1529  $zn = (int)( ( $zl - 1 ) / 10631 );
1530  $zl = $zl - 10631 * $zn + 354;
1531  $zj = ( (int)( ( 10985 - $zl ) / 5316 ) ) * ( (int)( ( 50 * $zl ) / 17719 ) ) + ( (int)( $zl / 5670 ) ) * ( (int)( ( 43 * $zl ) / 15238 ) );
1532  $zl = $zl - ( (int)( ( 30 - $zj ) / 15 ) ) * ( (int)( ( 17719 * $zj ) / 50 ) ) - ( (int)( $zj / 16 ) ) * ( (int)( ( 15238 * $zj ) / 43 ) ) + 29;
1533  $zm = (int)( ( 24 * $zl ) / 709 );
1534  $zd = $zl - (int)( ( 709 * $zm ) / 24 );
1535  $zy = 30 * $zn + $zj - 30;
1536 
1537  return array( $zy, $zm, $zd );
1538  }
1539 
1555  private static function tsToHebrew( $ts ) {
1556  # Parse date
1557  $year = substr( $ts, 0, 4 );
1558  $month = substr( $ts, 4, 2 );
1559  $day = substr( $ts, 6, 2 );
1560 
1561  # Calculate Hebrew year
1562  $hebrewYear = $year + 3760;
1563 
1564  # Month number when September = 1, August = 12
1565  $month += 4;
1566  if ( $month > 12 ) {
1567  # Next year
1568  $month -= 12;
1569  $year++;
1570  $hebrewYear++;
1571  }
1572 
1573  # Calculate day of year from 1 September
1574  $dayOfYear = $day;
1575  for ( $i = 1; $i < $month; $i++ ) {
1576  if ( $i == 6 ) {
1577  # February
1578  $dayOfYear += 28;
1579  # Check if the year is leap
1580  if ( $year % 400 == 0 || ( $year % 4 == 0 && $year % 100 > 0 ) ) {
1581  $dayOfYear++;
1582  }
1583  } elseif ( $i == 8 || $i == 10 || $i == 1 || $i == 3 ) {
1584  $dayOfYear += 30;
1585  } else {
1586  $dayOfYear += 31;
1587  }
1588  }
1589 
1590  # Calculate the start of the Hebrew year
1591  $start = self::hebrewYearStart( $hebrewYear );
1592 
1593  # Calculate next year's start
1594  if ( $dayOfYear <= $start ) {
1595  # Day is before the start of the year - it is the previous year
1596  # Next year's start
1597  $nextStart = $start;
1598  # Previous year
1599  $year--;
1600  $hebrewYear--;
1601  # Add days since previous year's 1 September
1602  $dayOfYear += 365;
1603  if ( ( $year % 400 == 0 ) || ( $year % 100 != 0 && $year % 4 == 0 ) ) {
1604  # Leap year
1605  $dayOfYear++;
1606  }
1607  # Start of the new (previous) year
1608  $start = self::hebrewYearStart( $hebrewYear );
1609  } else {
1610  # Next year's start
1611  $nextStart = self::hebrewYearStart( $hebrewYear + 1 );
1612  }
1613 
1614  # Calculate Hebrew day of year
1615  $hebrewDayOfYear = $dayOfYear - $start;
1616 
1617  # Difference between year's days
1618  $diff = $nextStart - $start;
1619  # Add 12 (or 13 for leap years) days to ignore the difference between
1620  # Hebrew and Gregorian year (353 at least vs. 365/6) - now the
1621  # difference is only about the year type
1622  if ( ( $year % 400 == 0 ) || ( $year % 100 != 0 && $year % 4 == 0 ) ) {
1623  $diff += 13;
1624  } else {
1625  $diff += 12;
1626  }
1627 
1628  # Check the year pattern, and is leap year
1629  # 0 means an incomplete year, 1 means a regular year, 2 means a complete year
1630  # This is mod 30, to work on both leap years (which add 30 days of Adar I)
1631  # and non-leap years
1632  $yearPattern = $diff % 30;
1633  # Check if leap year
1634  $isLeap = $diff >= 30;
1635 
1636  # Calculate day in the month from number of day in the Hebrew year
1637  # Don't check Adar - if the day is not in Adar, we will stop before;
1638  # if it is in Adar, we will use it to check if it is Adar I or Adar II
1639  $hebrewDay = $hebrewDayOfYear;
1640  $hebrewMonth = 1;
1641  $days = 0;
1642  while ( $hebrewMonth <= 12 ) {
1643  # Calculate days in this month
1644  if ( $isLeap && $hebrewMonth == 6 ) {
1645  # Adar in a leap year
1646  if ( $isLeap ) {
1647  # Leap year - has Adar I, with 30 days, and Adar II, with 29 days
1648  $days = 30;
1649  if ( $hebrewDay <= $days ) {
1650  # Day in Adar I
1651  $hebrewMonth = 13;
1652  } else {
1653  # Subtract the days of Adar I
1654  $hebrewDay -= $days;
1655  # Try Adar II
1656  $days = 29;
1657  if ( $hebrewDay <= $days ) {
1658  # Day in Adar II
1659  $hebrewMonth = 14;
1660  }
1661  }
1662  }
1663  } elseif ( $hebrewMonth == 2 && $yearPattern == 2 ) {
1664  # Cheshvan in a complete year (otherwise as the rule below)
1665  $days = 30;
1666  } elseif ( $hebrewMonth == 3 && $yearPattern == 0 ) {
1667  # Kislev in an incomplete year (otherwise as the rule below)
1668  $days = 29;
1669  } else {
1670  # Odd months have 30 days, even have 29
1671  $days = 30 - ( $hebrewMonth - 1 ) % 2;
1672  }
1673  if ( $hebrewDay <= $days ) {
1674  # In the current month
1675  break;
1676  } else {
1677  # Subtract the days of the current month
1678  $hebrewDay -= $days;
1679  # Try in the next month
1680  $hebrewMonth++;
1681  }
1682  }
1683 
1684  return array( $hebrewYear, $hebrewMonth, $hebrewDay, $days );
1685  }
1686 
1696  private static function hebrewYearStart( $year ) {
1697  $a = intval( ( 12 * ( $year - 1 ) + 17 ) % 19 );
1698  $b = intval( ( $year - 1 ) % 4 );
1699  $m = 32.044093161144 + 1.5542417966212 * $a + $b / 4.0 - 0.0031777940220923 * ( $year - 1 );
1700  if ( $m < 0 ) {
1701  $m--;
1702  }
1703  $Mar = intval( $m );
1704  if ( $m < 0 ) {
1705  $m++;
1706  }
1707  $m -= $Mar;
1708 
1709  $c = intval( ( $Mar + 3 * ( $year - 1 ) + 5 * $b + 5 ) % 7 );
1710  if ( $c == 0 && $a > 11 && $m >= 0.89772376543210 ) {
1711  $Mar++;
1712  } elseif ( $c == 1 && $a > 6 && $m >= 0.63287037037037 ) {
1713  $Mar += 2;
1714  } elseif ( $c == 2 || $c == 4 || $c == 6 ) {
1715  $Mar++;
1716  }
1717 
1718  $Mar += intval( ( $year - 3761 ) / 100 ) - intval( ( $year - 3761 ) / 400 ) - 24;
1719  return $Mar;
1720  }
1721 
1734  private static function tsToYear( $ts, $cName ) {
1735  $gy = substr( $ts, 0, 4 );
1736  $gm = substr( $ts, 4, 2 );
1737  $gd = substr( $ts, 6, 2 );
1738 
1739  if ( !strcmp( $cName, 'thai' ) ) {
1740  # Thai solar dates
1741  # Add 543 years to the Gregorian calendar
1742  # Months and days are identical
1743  $gy_offset = $gy + 543;
1744  } elseif ( ( !strcmp( $cName, 'minguo' ) ) || !strcmp( $cName, 'juche' ) ) {
1745  # Minguo dates
1746  # Deduct 1911 years from the Gregorian calendar
1747  # Months and days are identical
1748  $gy_offset = $gy - 1911;
1749  } elseif ( !strcmp( $cName, 'tenno' ) ) {
1750  # Nengō dates up to Meiji period
1751  # Deduct years from the Gregorian calendar
1752  # depending on the nengo periods
1753  # Months and days are identical
1754  if ( ( $gy < 1912 ) || ( ( $gy == 1912 ) && ( $gm < 7 ) ) || ( ( $gy == 1912 ) && ( $gm == 7 ) && ( $gd < 31 ) ) ) {
1755  # Meiji period
1756  $gy_gannen = $gy - 1868 + 1;
1757  $gy_offset = $gy_gannen;
1758  if ( $gy_gannen == 1 ) {
1759  $gy_offset = '元';
1760  }
1761  $gy_offset = '明治' . $gy_offset;
1762  } elseif (
1763  ( ( $gy == 1912 ) && ( $gm == 7 ) && ( $gd == 31 ) ) ||
1764  ( ( $gy == 1912 ) && ( $gm >= 8 ) ) ||
1765  ( ( $gy > 1912 ) && ( $gy < 1926 ) ) ||
1766  ( ( $gy == 1926 ) && ( $gm < 12 ) ) ||
1767  ( ( $gy == 1926 ) && ( $gm == 12 ) && ( $gd < 26 ) )
1768  ) {
1769  # Taishō period
1770  $gy_gannen = $gy - 1912 + 1;
1771  $gy_offset = $gy_gannen;
1772  if ( $gy_gannen == 1 ) {
1773  $gy_offset = '元';
1774  }
1775  $gy_offset = '大正' . $gy_offset;
1776  } elseif (
1777  ( ( $gy == 1926 ) && ( $gm == 12 ) && ( $gd >= 26 ) ) ||
1778  ( ( $gy > 1926 ) && ( $gy < 1989 ) ) ||
1779  ( ( $gy == 1989 ) && ( $gm == 1 ) && ( $gd < 8 ) )
1780  ) {
1781  # Shōwa period
1782  $gy_gannen = $gy - 1926 + 1;
1783  $gy_offset = $gy_gannen;
1784  if ( $gy_gannen == 1 ) {
1785  $gy_offset = '元';
1786  }
1787  $gy_offset = '昭和' . $gy_offset;
1788  } else {
1789  # Heisei period
1790  $gy_gannen = $gy - 1989 + 1;
1791  $gy_offset = $gy_gannen;
1792  if ( $gy_gannen == 1 ) {
1793  $gy_offset = '元';
1794  }
1795  $gy_offset = '平成' . $gy_offset;
1796  }
1797  } else {
1798  $gy_offset = $gy;
1799  }
1800 
1801  return array( $gy_offset, $gm, $gd );
1802  }
1803 
1811  static function romanNumeral( $num ) {
1812  static $table = array(
1813  array( '', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X' ),
1814  array( '', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC', 'C' ),
1815  array( '', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM', 'M' ),
1816  array( '', 'M', 'MM', 'MMM', 'MMMM', 'MMMMM', 'MMMMMM', 'MMMMMMM', 'MMMMMMMM', 'MMMMMMMMM', 'MMMMMMMMMM' )
1817  );
1818 
1819  $num = intval( $num );
1820  if ( $num > 10000 || $num <= 0 ) {
1821  return $num;
1822  }
1823 
1824  $s = '';
1825  for ( $pow10 = 1000, $i = 3; $i >= 0; $pow10 /= 10, $i-- ) {
1826  if ( $num >= $pow10 ) {
1827  $s .= $table[$i][(int)floor( $num / $pow10 )];
1828  }
1829  $num = $num % $pow10;
1830  }
1831  return $s;
1832  }
1833 
1841  static function hebrewNumeral( $num ) {
1842  static $table = array(
1843  array( '', 'א', 'ב', 'ג', 'ד', 'ה', 'ו', 'ז', 'ח', 'ט', 'י' ),
1844  array( '', 'י', 'כ', 'ל', 'מ', 'נ', 'ס', 'ע', 'פ', 'צ', 'ק' ),
1845  array( '', 'ק', 'ר', 'ש', 'ת', 'תק', 'תר', 'תש', 'תת', 'תתק', 'תתר' ),
1846  array( '', 'א', 'ב', 'ג', 'ד', 'ה', 'ו', 'ז', 'ח', 'ט', 'י' )
1847  );
1848 
1849  $num = intval( $num );
1850  if ( $num > 9999 || $num <= 0 ) {
1851  return $num;
1852  }
1853 
1854  $s = '';
1855  for ( $pow10 = 1000, $i = 3; $i >= 0; $pow10 /= 10, $i-- ) {
1856  if ( $num >= $pow10 ) {
1857  if ( $num == 15 || $num == 16 ) {
1858  $s .= $table[0][9] . $table[0][$num - 9];
1859  $num = 0;
1860  } else {
1861  $s .= $table[$i][intval( ( $num / $pow10 ) )];
1862  if ( $pow10 == 1000 ) {
1863  $s .= "'";
1864  }
1865  }
1866  }
1867  $num = $num % $pow10;
1868  }
1869  if ( strlen( $s ) == 2 ) {
1870  $str = $s . "'";
1871  } else {
1872  $str = substr( $s, 0, strlen( $s ) - 2 ) . '"';
1873  $str .= substr( $s, strlen( $s ) - 2, 2 );
1874  }
1875  $start = substr( $str, 0, strlen( $str ) - 2 );
1876  $end = substr( $str, strlen( $str ) - 2 );
1877  switch ( $end ) {
1878  case 'כ':
1879  $str = $start . 'ך';
1880  break;
1881  case 'מ':
1882  $str = $start . 'ם';
1883  break;
1884  case 'נ':
1885  $str = $start . 'ן';
1886  break;
1887  case 'פ':
1888  $str = $start . 'ף';
1889  break;
1890  case 'צ':
1891  $str = $start . 'ץ';
1892  break;
1893  }
1894  return $str;
1895  }
1896 
1905  function userAdjust( $ts, $tz = false ) {
1906  global $wgUser, $wgLocalTZoffset;
1907 
1908  if ( $tz === false ) {
1909  $tz = $wgUser->getOption( 'timecorrection' );
1910  }
1911 
1912  $data = explode( '|', $tz, 3 );
1913 
1914  if ( $data[0] == 'ZoneInfo' ) {
1916  $userTZ = timezone_open( $data[2] );
1918  if ( $userTZ !== false ) {
1919  $date = date_create( $ts, timezone_open( 'UTC' ) );
1920  date_timezone_set( $date, $userTZ );
1921  $date = date_format( $date, 'YmdHis' );
1922  return $date;
1923  }
1924  # Unrecognized timezone, default to 'Offset' with the stored offset.
1925  $data[0] = 'Offset';
1926  }
1927 
1928  $minDiff = 0;
1929  if ( $data[0] == 'System' || $tz == '' ) {
1930  #  Global offset in minutes.
1931  if ( isset( $wgLocalTZoffset ) ) {
1932  $minDiff = $wgLocalTZoffset;
1933  }
1934  } elseif ( $data[0] == 'Offset' ) {
1935  $minDiff = intval( $data[1] );
1936  } else {
1937  $data = explode( ':', $tz );
1938  if ( count( $data ) == 2 ) {
1939  $data[0] = intval( $data[0] );
1940  $data[1] = intval( $data[1] );
1941  $minDiff = abs( $data[0] ) * 60 + $data[1];
1942  if ( $data[0] < 0 ) {
1943  $minDiff = -$minDiff;
1944  }
1945  } else {
1946  $minDiff = intval( $data[0] ) * 60;
1947  }
1948  }
1949 
1950  # No difference ? Return time unchanged
1951  if ( 0 == $minDiff ) {
1952  return $ts;
1953  }
1954 
1955  wfSuppressWarnings(); // E_STRICT system time bitching
1956  # Generate an adjusted date; take advantage of the fact that mktime
1957  # will normalize out-of-range values so we don't have to split $minDiff
1958  # into hours and minutes.
1959  $t = mktime( (
1960  (int)substr( $ts, 8, 2 ) ), # Hours
1961  (int)substr( $ts, 10, 2 ) + $minDiff, # Minutes
1962  (int)substr( $ts, 12, 2 ), # Seconds
1963  (int)substr( $ts, 4, 2 ), # Month
1964  (int)substr( $ts, 6, 2 ), # Day
1965  (int)substr( $ts, 0, 4 ) ); # Year
1966 
1967  $date = date( 'YmdHis', $t );
1969 
1970  return $date;
1971  }
1972 
1990  function dateFormat( $usePrefs = true ) {
1991  global $wgUser;
1992 
1993  if ( is_bool( $usePrefs ) ) {
1994  if ( $usePrefs ) {
1995  $datePreference = $wgUser->getDatePreference();
1996  } else {
1997  $datePreference = (string)User::getDefaultOption( 'date' );
1998  }
1999  } else {
2000  $datePreference = (string)$usePrefs;
2001  }
2002 
2003  // return int
2004  if ( $datePreference == '' ) {
2005  return 'default';
2006  }
2007 
2008  return $datePreference;
2009  }
2010 
2020  function getDateFormatString( $type, $pref ) {
2021  if ( !isset( $this->dateFormatStrings[$type][$pref] ) ) {
2022  if ( $pref == 'default' ) {
2023  $pref = $this->getDefaultDateFormat();
2024  $df = self::$dataCache->getSubitem( $this->mCode, 'dateFormats', "$pref $type" );
2025  } else {
2026  $df = self::$dataCache->getSubitem( $this->mCode, 'dateFormats', "$pref $type" );
2027 
2028  if ( $type === 'pretty' && $df === null ) {
2029  $df = $this->getDateFormatString( 'date', $pref );
2030  }
2031 
2032  if ( $df === null ) {
2033  $pref = $this->getDefaultDateFormat();
2034  $df = self::$dataCache->getSubitem( $this->mCode, 'dateFormats', "$pref $type" );
2035  }
2036  }
2037  $this->dateFormatStrings[$type][$pref] = $df;
2038  }
2039  return $this->dateFormatStrings[$type][$pref];
2040  }
2041 
2052  function date( $ts, $adj = false, $format = true, $timecorrection = false ) {
2053  $ts = wfTimestamp( TS_MW, $ts );
2054  if ( $adj ) {
2055  $ts = $this->userAdjust( $ts, $timecorrection );
2056  }
2057  $df = $this->getDateFormatString( 'date', $this->dateFormat( $format ) );
2058  return $this->sprintfDate( $df, $ts );
2059  }
2060 
2071  function time( $ts, $adj = false, $format = true, $timecorrection = false ) {
2072  $ts = wfTimestamp( TS_MW, $ts );
2073  if ( $adj ) {
2074  $ts = $this->userAdjust( $ts, $timecorrection );
2075  }
2076  $df = $this->getDateFormatString( 'time', $this->dateFormat( $format ) );
2077  return $this->sprintfDate( $df, $ts );
2078  }
2079 
2091  function timeanddate( $ts, $adj = false, $format = true, $timecorrection = false ) {
2092  $ts = wfTimestamp( TS_MW, $ts );
2093  if ( $adj ) {
2094  $ts = $this->userAdjust( $ts, $timecorrection );
2095  }
2096  $df = $this->getDateFormatString( 'both', $this->dateFormat( $format ) );
2097  return $this->sprintfDate( $df, $ts );
2098  }
2099 
2110  public function formatDuration( $seconds, array $chosenIntervals = array() ) {
2111  $intervals = $this->getDurationIntervals( $seconds, $chosenIntervals );
2112 
2113  $segments = array();
2114 
2115  foreach ( $intervals as $intervalName => $intervalValue ) {
2116  // Messages: duration-seconds, duration-minutes, duration-hours, duration-days, duration-weeks,
2117  // duration-years, duration-decades, duration-centuries, duration-millennia
2118  $message = wfMessage( 'duration-' . $intervalName )->numParams( $intervalValue );
2119  $segments[] = $message->inLanguage( $this )->escaped();
2120  }
2121 
2122  return $this->listToText( $segments );
2123  }
2124 
2136  public function getDurationIntervals( $seconds, array $chosenIntervals = array() ) {
2137  if ( empty( $chosenIntervals ) ) {
2138  $chosenIntervals = array( 'millennia', 'centuries', 'decades', 'years', 'days', 'hours', 'minutes', 'seconds' );
2139  }
2140 
2141  $intervals = array_intersect_key( self::$durationIntervals, array_flip( $chosenIntervals ) );
2142  $sortedNames = array_keys( $intervals );
2143  $smallestInterval = array_pop( $sortedNames );
2144 
2145  $segments = array();
2146 
2147  foreach ( $intervals as $name => $length ) {
2148  $value = floor( $seconds / $length );
2149 
2150  if ( $value > 0 || ( $name == $smallestInterval && empty( $segments ) ) ) {
2151  $seconds -= $value * $length;
2152  $segments[$name] = $value;
2153  }
2154  }
2155 
2156  return $segments;
2157  }
2158 
2178  private function internalUserTimeAndDate( $type, $ts, User $user, array $options ) {
2179  $ts = wfTimestamp( TS_MW, $ts );
2180  $options += array( 'timecorrection' => true, 'format' => true );
2181  if ( $options['timecorrection'] !== false ) {
2182  if ( $options['timecorrection'] === true ) {
2183  $offset = $user->getOption( 'timecorrection' );
2184  } else {
2185  $offset = $options['timecorrection'];
2186  }
2187  $ts = $this->userAdjust( $ts, $offset );
2188  }
2189  if ( $options['format'] === true ) {
2190  $format = $user->getDatePreference();
2191  } else {
2192  $format = $options['format'];
2193  }
2194  $df = $this->getDateFormatString( $type, $this->dateFormat( $format ) );
2195  return $this->sprintfDate( $df, $ts );
2196  }
2197 
2217  public function userDate( $ts, User $user, array $options = array() ) {
2218  return $this->internalUserTimeAndDate( 'date', $ts, $user, $options );
2219  }
2220 
2240  public function userTime( $ts, User $user, array $options = array() ) {
2241  return $this->internalUserTimeAndDate( 'time', $ts, $user, $options );
2242  }
2243 
2263  public function userTimeAndDate( $ts, User $user, array $options = array() ) {
2264  return $this->internalUserTimeAndDate( 'both', $ts, $user, $options );
2265  }
2266 
2282  public function getHumanTimestamp( MWTimestamp $ts, MWTimestamp $relativeTo, User $user ) {
2283  $diff = $ts->diff( $relativeTo );
2284  $diffDay = (bool)( (int)$ts->timestamp->format( 'w' ) - (int)$relativeTo->timestamp->format( 'w' ) );
2285  $days = $diff->days ?: (int)$diffDay;
2286  if ( $diff->invert || $days > 5 && $ts->timestamp->format( 'Y' ) !== $relativeTo->timestamp->format( 'Y' ) ) {
2287  // Timestamps are in different years: use full timestamp
2288  // Also do full timestamp for future dates
2292  $format = $this->getDateFormatString( 'both', $user->getDatePreference() ?: 'default' );
2293  $ts = $this->sprintfDate( $format, $ts->getTimestamp( TS_MW ) );
2294  } elseif ( $days > 5 ) {
2295  // Timestamps are in same year, but more than 5 days ago: show day and month only.
2296  $format = $this->getDateFormatString( 'pretty', $user->getDatePreference() ?: 'default' );
2297  $ts = $this->sprintfDate( $format, $ts->getTimestamp( TS_MW ) );
2298  } elseif ( $days > 1 ) {
2299  // Timestamp within the past week: show the day of the week and time
2300  $format = $this->getDateFormatString( 'time', $user->getDatePreference() ?: 'default' );
2301  $weekday = self::$mWeekdayMsgs[$ts->timestamp->format( 'w' )];
2302  // Messages:
2303  // sunday-at, monday-at, tuesday-at, wednesday-at, thursday-at, friday-at, saturday-at
2304  $ts = wfMessage( "$weekday-at" )
2305  ->inLanguage( $this )
2306  ->params( $this->sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
2307  ->text();
2308  } elseif ( $days == 1 ) {
2309  // Timestamp was yesterday: say 'yesterday' and the time.
2310  $format = $this->getDateFormatString( 'time', $user->getDatePreference() ?: 'default' );
2311  $ts = wfMessage( 'yesterday-at' )
2312  ->inLanguage( $this )
2313  ->params( $this->sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
2314  ->text();
2315  } elseif ( $diff->h > 1 || $diff->h == 1 && $diff->i > 30 ) {
2316  // Timestamp was today, but more than 90 minutes ago: say 'today' and the time.
2317  $format = $this->getDateFormatString( 'time', $user->getDatePreference() ?: 'default' );
2318  $ts = wfMessage( 'today-at' )
2319  ->inLanguage( $this )
2320  ->params( $this->sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
2321  ->text();
2322 
2323  // From here on in, the timestamp was soon enough ago so that we can simply say
2324  // XX units ago, e.g., "2 hours ago" or "5 minutes ago"
2325  } elseif ( $diff->h == 1 ) {
2326  // Less than 90 minutes, but more than an hour ago.
2327  $ts = wfMessage( 'hours-ago' )->inLanguage( $this )->numParams( 1 )->text();
2328  } elseif ( $diff->i >= 1 ) {
2329  // A few minutes ago.
2330  $ts = wfMessage( 'minutes-ago' )->inLanguage( $this )->numParams( $diff->i )->text();
2331  } elseif ( $diff->s >= 30 ) {
2332  // Less than a minute, but more than 30 sec ago.
2333  $ts = wfMessage( 'seconds-ago' )->inLanguage( $this )->numParams( $diff->s )->text();
2334  } else {
2335  // Less than 30 seconds ago.
2336  $ts = wfMessage( 'just-now' )->text();
2337  }
2338 
2339  return $ts;
2340  }
2341 
2346  function getMessage( $key ) {
2347  return self::$dataCache->getSubitem( $this->mCode, 'messages', $key );
2348  }
2349 
2353  function getAllMessages() {
2354  return self::$dataCache->getItem( $this->mCode, 'messages' );
2355  }
2356 
2363  function iconv( $in, $out, $string ) {
2364  # This is a wrapper for iconv in all languages except esperanto,
2365  # which does some nasty x-conversions beforehand
2366 
2367  # Even with //IGNORE iconv can whine about illegal characters in
2368  # *input* string. We just ignore those too.
2369  # REF: http://bugs.php.net/bug.php?id=37166
2370  # REF: https://bugzilla.wikimedia.org/show_bug.cgi?id=16885
2372  $text = iconv( $in, $out . '//IGNORE', $string );
2374  return $text;
2375  }
2376 
2377  // callback functions for uc(), lc(), ucwords(), ucwordbreaks()
2378 
2383  function ucwordbreaksCallbackAscii( $matches ) {
2384  return $this->ucfirst( $matches[1] );
2385  }
2386 
2391  function ucwordbreaksCallbackMB( $matches ) {
2392  return mb_strtoupper( $matches[0] );
2393  }
2394 
2399  function ucCallback( $matches ) {
2400  list( $wikiUpperChars ) = self::getCaseMaps();
2401  return strtr( $matches[1], $wikiUpperChars );
2402  }
2403 
2408  function lcCallback( $matches ) {
2409  list( , $wikiLowerChars ) = self::getCaseMaps();
2410  return strtr( $matches[1], $wikiLowerChars );
2411  }
2412 
2417  function ucwordsCallbackMB( $matches ) {
2418  return mb_strtoupper( $matches[0] );
2419  }
2420 
2425  function ucwordsCallbackWiki( $matches ) {
2426  list( $wikiUpperChars ) = self::getCaseMaps();
2427  return strtr( $matches[0], $wikiUpperChars );
2428  }
2429 
2437  function ucfirst( $str ) {
2438  $o = ord( $str );
2439  if ( $o < 96 ) { // if already uppercase...
2440  return $str;
2441  } elseif ( $o < 128 ) {
2442  return ucfirst( $str ); // use PHP's ucfirst()
2443  } else {
2444  // fall back to more complex logic in case of multibyte strings
2445  return $this->uc( $str, true );
2446  }
2447  }
2448 
2457  function uc( $str, $first = false ) {
2458  if ( function_exists( 'mb_strtoupper' ) ) {
2459  if ( $first ) {
2460  if ( $this->isMultibyte( $str ) ) {
2461  return mb_strtoupper( mb_substr( $str, 0, 1 ) ) . mb_substr( $str, 1 );
2462  } else {
2463  return ucfirst( $str );
2464  }
2465  } else {
2466  return $this->isMultibyte( $str ) ? mb_strtoupper( $str ) : strtoupper( $str );
2467  }
2468  } else {
2469  if ( $this->isMultibyte( $str ) ) {
2470  $x = $first ? '^' : '';
2471  return preg_replace_callback(
2472  "/$x([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/",
2473  array( $this, 'ucCallback' ),
2474  $str
2475  );
2476  } else {
2477  return $first ? ucfirst( $str ) : strtoupper( $str );
2478  }
2479  }
2480  }
2481 
2486  function lcfirst( $str ) {
2487  $o = ord( $str );
2488  if ( !$o ) {
2489  return strval( $str );
2490  } elseif ( $o >= 128 ) {
2491  return $this->lc( $str, true );
2492  } elseif ( $o > 96 ) {
2493  return $str;
2494  } else {
2495  $str[0] = strtolower( $str[0] );
2496  return $str;
2497  }
2498  }
2499 
2505  function lc( $str, $first = false ) {
2506  if ( function_exists( 'mb_strtolower' ) ) {
2507  if ( $first ) {
2508  if ( $this->isMultibyte( $str ) ) {
2509  return mb_strtolower( mb_substr( $str, 0, 1 ) ) . mb_substr( $str, 1 );
2510  } else {
2511  return strtolower( substr( $str, 0, 1 ) ) . substr( $str, 1 );
2512  }
2513  } else {
2514  return $this->isMultibyte( $str ) ? mb_strtolower( $str ) : strtolower( $str );
2515  }
2516  } else {
2517  if ( $this->isMultibyte( $str ) ) {
2518  $x = $first ? '^' : '';
2519  return preg_replace_callback(
2520  "/$x([A-Z]|[\\xc0-\\xff][\\x80-\\xbf]*)/",
2521  array( $this, 'lcCallback' ),
2522  $str
2523  );
2524  } else {
2525  return $first ? strtolower( substr( $str, 0, 1 ) ) . substr( $str, 1 ) : strtolower( $str );
2526  }
2527  }
2528  }
2529 
2534  function isMultibyte( $str ) {
2535  return (bool)preg_match( '/[\x80-\xff]/', $str );
2536  }
2537 
2542  function ucwords( $str ) {
2543  if ( $this->isMultibyte( $str ) ) {
2544  $str = $this->lc( $str );
2545 
2546  // regexp to find first letter in each word (i.e. after each space)
2547  $replaceRegexp = "/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)| ([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/";
2548 
2549  // function to use to capitalize a single char
2550  if ( function_exists( 'mb_strtoupper' ) ) {
2551  return preg_replace_callback(
2552  $replaceRegexp,
2553  array( $this, 'ucwordsCallbackMB' ),
2554  $str
2555  );
2556  } else {
2557  return preg_replace_callback(
2558  $replaceRegexp,
2559  array( $this, 'ucwordsCallbackWiki' ),
2560  $str
2561  );
2562  }
2563  } else {
2564  return ucwords( strtolower( $str ) );
2565  }
2566  }
2567 
2574  function ucwordbreaks( $str ) {
2575  if ( $this->isMultibyte( $str ) ) {
2576  $str = $this->lc( $str );
2577 
2578  // since \b doesn't work for UTF-8, we explicitely define word break chars
2579  $breaks = "[ \-\(\)\}\{\.,\?!]";
2580 
2581  // find first letter after word break
2582  $replaceRegexp = "/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)|$breaks([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/";
2583 
2584  if ( function_exists( 'mb_strtoupper' ) ) {
2585  return preg_replace_callback(
2586  $replaceRegexp,
2587  array( $this, 'ucwordbreaksCallbackMB' ),
2588  $str
2589  );
2590  } else {
2591  return preg_replace_callback(
2592  $replaceRegexp,
2593  array( $this, 'ucwordsCallbackWiki' ),
2594  $str
2595  );
2596  }
2597  } else {
2598  return preg_replace_callback(
2599  '/\b([\w\x80-\xff]+)\b/',
2600  array( $this, 'ucwordbreaksCallbackAscii' ),
2601  $str
2602  );
2603  }
2604  }
2605 
2621  function caseFold( $s ) {
2622  return $this->uc( $s );
2623  }
2624 
2629  function checkTitleEncoding( $s ) {
2630  if ( is_array( $s ) ) {
2631  throw new MWException( 'Given array to checkTitleEncoding.' );
2632  }
2633  if ( StringUtils::isUtf8( $s ) ) {
2634  return $s;
2635  }
2636 
2637  return $this->iconv( $this->fallback8bitEncoding(), 'utf-8', $s );
2638  }
2639 
2643  function fallback8bitEncoding() {
2644  return self::$dataCache->getItem( $this->mCode, 'fallback8bitEncoding' );
2645  }
2646 
2655  function hasWordBreaks() {
2656  return true;
2657  }
2658 
2666  function segmentByWord( $string ) {
2667  return $string;
2668  }
2669 
2677  function normalizeForSearch( $string ) {
2678  return self::convertDoubleWidth( $string );
2679  }
2680 
2689  protected static function convertDoubleWidth( $string ) {
2690  static $full = null;
2691  static $half = null;
2692 
2693  if ( $full === null ) {
2694  $fullWidth = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
2695  $halfWidth = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
2696  $full = str_split( $fullWidth, 3 );
2697  $half = str_split( $halfWidth );
2698  }
2699 
2700  $string = str_replace( $full, $half, $string );
2701  return $string;
2702  }
2703 
2709  protected static function insertSpace( $string, $pattern ) {
2710  $string = preg_replace( $pattern, " $1 ", $string );
2711  $string = preg_replace( '/ +/', ' ', $string );
2712  return $string;
2713  }
2714 
2719  function convertForSearchResult( $termsArray ) {
2720  # some languages, e.g. Chinese, need to do a conversion
2721  # in order for search results to be displayed correctly
2722  return $termsArray;
2723  }
2724 
2731  function firstChar( $s ) {
2732  $matches = array();
2733  preg_match(
2734  '/^([\x00-\x7f]|[\xc0-\xdf][\x80-\xbf]|' .
2735  '[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3})/',
2736  $s,
2737  $matches
2738  );
2739 
2740  if ( isset( $matches[1] ) ) {
2741  if ( strlen( $matches[1] ) != 3 ) {
2742  return $matches[1];
2743  }
2744 
2745  // Break down Hangul syllables to grab the first jamo
2746  $code = utf8ToCodepoint( $matches[1] );
2747  if ( $code < 0xac00 || 0xd7a4 <= $code ) {
2748  return $matches[1];
2749  } elseif ( $code < 0xb098 ) {
2750  return "\xe3\x84\xb1";
2751  } elseif ( $code < 0xb2e4 ) {
2752  return "\xe3\x84\xb4";
2753  } elseif ( $code < 0xb77c ) {
2754  return "\xe3\x84\xb7";
2755  } elseif ( $code < 0xb9c8 ) {
2756  return "\xe3\x84\xb9";
2757  } elseif ( $code < 0xbc14 ) {
2758  return "\xe3\x85\x81";
2759  } elseif ( $code < 0xc0ac ) {
2760  return "\xe3\x85\x82";
2761  } elseif ( $code < 0xc544 ) {
2762  return "\xe3\x85\x85";
2763  } elseif ( $code < 0xc790 ) {
2764  return "\xe3\x85\x87";
2765  } elseif ( $code < 0xcc28 ) {
2766  return "\xe3\x85\x88";
2767  } elseif ( $code < 0xce74 ) {
2768  return "\xe3\x85\x8a";
2769  } elseif ( $code < 0xd0c0 ) {
2770  return "\xe3\x85\x8b";
2771  } elseif ( $code < 0xd30c ) {
2772  return "\xe3\x85\x8c";
2773  } elseif ( $code < 0xd558 ) {
2774  return "\xe3\x85\x8d";
2775  } else {
2776  return "\xe3\x85\x8e";
2777  }
2778  } else {
2779  return '';
2780  }
2781  }
2782 
2783  function initEncoding() {
2784  # Some languages may have an alternate char encoding option
2785  # (Esperanto X-coding, Japanese furigana conversion, etc)
2786  # If this language is used as the primary content language,
2787  # an override to the defaults can be set here on startup.
2788  }
2789 
2794  function recodeForEdit( $s ) {
2795  # For some languages we'll want to explicitly specify
2796  # which characters make it into the edit box raw
2797  # or are converted in some way or another.
2798  global $wgEditEncoding;
2799  if ( $wgEditEncoding == '' || $wgEditEncoding == 'UTF-8' ) {
2800  return $s;
2801  } else {
2802  return $this->iconv( 'UTF-8', $wgEditEncoding, $s );
2803  }
2804  }
2805 
2810  function recodeInput( $s ) {
2811  # Take the previous into account.
2812  global $wgEditEncoding;
2813  if ( $wgEditEncoding != '' ) {
2814  $enc = $wgEditEncoding;
2815  } else {
2816  $enc = 'UTF-8';
2817  }
2818  if ( $enc == 'UTF-8' ) {
2819  return $s;
2820  } else {
2821  return $this->iconv( $enc, 'UTF-8', $s );
2822  }
2823  }
2824 
2836  function normalize( $s ) {
2837  global $wgAllUnicodeFixes;
2838  $s = UtfNormal::cleanUp( $s );
2839  if ( $wgAllUnicodeFixes ) {
2840  $s = $this->transformUsingPairFile( 'normalize-ar.ser', $s );
2841  $s = $this->transformUsingPairFile( 'normalize-ml.ser', $s );
2842  }
2843 
2844  return $s;
2845  }
2846 
2861  function transformUsingPairFile( $file, $string ) {
2862  if ( !isset( $this->transformData[$file] ) ) {
2863  $data = wfGetPrecompiledData( $file );
2864  if ( $data === false ) {
2865  throw new MWException( __METHOD__ . ": The transformation file $file is missing" );
2866  }
2867  $this->transformData[$file] = new ReplacementArray( $data );
2868  }
2869  return $this->transformData[$file]->replace( $string );
2870  }
2871 
2877  function isRTL() {
2878  return self::$dataCache->getItem( $this->mCode, 'rtl' );
2879  }
2880 
2885  function getDir() {
2886  return $this->isRTL() ? 'rtl' : 'ltr';
2887  }
2888 
2897  function alignStart() {
2898  return $this->isRTL() ? 'right' : 'left';
2899  }
2900 
2909  function alignEnd() {
2910  return $this->isRTL() ? 'left' : 'right';
2911  }
2912 
2924  function getDirMarkEntity( $opposite = false ) {
2925  if ( $opposite ) {
2926  return $this->isRTL() ? '&lrm;' : '&rlm;';
2927  }
2928  return $this->isRTL() ? '&rlm;' : '&lrm;';
2929  }
2930 
2941  function getDirMark( $opposite = false ) {
2942  $lrm = "\xE2\x80\x8E"; # LEFT-TO-RIGHT MARK, commonly abbreviated LRM
2943  $rlm = "\xE2\x80\x8F"; # RIGHT-TO-LEFT MARK, commonly abbreviated RLM
2944  if ( $opposite ) {
2945  return $this->isRTL() ? $lrm : $rlm;
2946  }
2947  return $this->isRTL() ? $rlm : $lrm;
2948  }
2949 
2953  function capitalizeAllNouns() {
2954  return self::$dataCache->getItem( $this->mCode, 'capitalizeAllNouns' );
2955  }
2956 
2963  function getArrow( $direction = 'forwards' ) {
2964  switch ( $direction ) {
2965  case 'forwards':
2966  return $this->isRTL() ? '←' : '→';
2967  case 'backwards':
2968  return $this->isRTL() ? '→' : '←';
2969  case 'left':
2970  return '←';
2971  case 'right':
2972  return '→';
2973  case 'up':
2974  return '↑';
2975  case 'down':
2976  return '↓';
2977  }
2978  }
2979 
2985  function linkPrefixExtension() {
2986  return self::$dataCache->getItem( $this->mCode, 'linkPrefixExtension' );
2987  }
2988 
2993  function getMagicWords() {
2994  return self::$dataCache->getItem( $this->mCode, 'magicWords' );
2995  }
2996 
3000  protected function doMagicHook() {
3001  if ( $this->mMagicHookDone ) {
3002  return;
3003  }
3004  $this->mMagicHookDone = true;
3005  wfProfileIn( 'LanguageGetMagic' );
3006  wfRunHooks( 'LanguageGetMagic', array( &$this->mMagicExtensions, $this->getCode() ) );
3007  wfProfileOut( 'LanguageGetMagic' );
3008  }
3009 
3015  function getMagic( $mw ) {
3016  // Saves a function call
3017  if ( ! $this->mMagicHookDone ) {
3018  $this->doMagicHook();
3019  }
3020 
3021  if ( isset( $this->mMagicExtensions[$mw->mId] ) ) {
3022  $rawEntry = $this->mMagicExtensions[$mw->mId];
3023  } else {
3024  $rawEntry = self::$dataCache->getSubitem(
3025  $this->mCode, 'magicWords', $mw->mId );
3026  }
3027 
3028  if ( !is_array( $rawEntry ) ) {
3029  error_log( "\"$rawEntry\" is not a valid magic word for \"$mw->mId\"" );
3030  } else {
3031  $mw->mCaseSensitive = $rawEntry[0];
3032  $mw->mSynonyms = array_slice( $rawEntry, 1 );
3033  }
3034  }
3035 
3041  function addMagicWordsByLang( $newWords ) {
3042  $fallbackChain = $this->getFallbackLanguages();
3043  $fallbackChain = array_reverse( $fallbackChain );
3044  foreach ( $fallbackChain as $code ) {
3045  if ( isset( $newWords[$code] ) ) {
3046  $this->mMagicExtensions = $newWords[$code] + $this->mMagicExtensions;
3047  }
3048  }
3049  }
3050 
3055  function getSpecialPageAliases() {
3056  // Cache aliases because it may be slow to load them
3057  if ( is_null( $this->mExtendedSpecialPageAliases ) ) {
3058  // Initialise array
3059  $this->mExtendedSpecialPageAliases =
3060  self::$dataCache->getItem( $this->mCode, 'specialPageAliases' );
3061  wfRunHooks( 'LanguageGetSpecialPageAliases',
3062  array( &$this->mExtendedSpecialPageAliases, $this->getCode() ) );
3063  }
3064 
3066  }
3067 
3074  function emphasize( $text ) {
3075  return "<em>$text</em>";
3076  }
3077 
3101  public function formatNum( $number, $nocommafy = false ) {
3102  global $wgTranslateNumerals;
3103  if ( !$nocommafy ) {
3104  $number = $this->commafy( $number );
3105  $s = $this->separatorTransformTable();
3106  if ( $s ) {
3107  $number = strtr( $number, $s );
3108  }
3109  }
3110 
3111  if ( $wgTranslateNumerals ) {
3112  $s = $this->digitTransformTable();
3113  if ( $s ) {
3114  $number = strtr( $number, $s );
3115  }
3116  }
3117 
3118  return $number;
3119  }
3120 
3129  public function formatNumNoSeparators( $number ) {
3130  return $this->formatNum( $number, true );
3131  }
3132 
3137  function parseFormattedNumber( $number ) {
3138  $s = $this->digitTransformTable();
3139  if ( $s ) {
3140  $number = strtr( $number, array_flip( $s ) );
3141  }
3142 
3143  $s = $this->separatorTransformTable();
3144  if ( $s ) {
3145  $number = strtr( $number, array_flip( $s ) );
3146  }
3147 
3148  $number = strtr( $number, array( ',' => '' ) );
3149  return $number;
3150  }
3151 
3158  function commafy( $number ) {
3160  if ( $number === null ) {
3161  return '';
3162  }
3163 
3164  if ( !$digitGroupingPattern || $digitGroupingPattern === "###,###,###" ) {
3165  // default grouping is at thousands, use the same for ###,###,### pattern too.
3166  return strrev( (string)preg_replace( '/(\d{3})(?=\d)(?!\d*\.)/', '$1,', strrev( $number ) ) );
3167  } else {
3168  // Ref: http://cldr.unicode.org/translation/number-patterns
3169  $sign = "";
3170  if ( intval( $number ) < 0 ) {
3171  // For negative numbers apply the algorithm like positive number and add sign.
3172  $sign = "-";
3173  $number = substr( $number, 1 );
3174  }
3175  $integerPart = array();
3176  $decimalPart = array();
3177  $numMatches = preg_match_all( "/(#+)/", $digitGroupingPattern, $matches );
3178  preg_match( "/\d+/", $number, $integerPart );
3179  preg_match( "/\.\d*/", $number, $decimalPart );
3180  $groupedNumber = ( count( $decimalPart ) > 0 ) ? $decimalPart[0] : "";
3181  if ( $groupedNumber === $number ) {
3182  // the string does not have any number part. Eg: .12345
3183  return $sign . $groupedNumber;
3184  }
3185  $start = $end = strlen( $integerPart[0] );
3186  while ( $start > 0 ) {
3187  $match = $matches[0][$numMatches - 1];
3188  $matchLen = strlen( $match );
3189  $start = $end - $matchLen;
3190  if ( $start < 0 ) {
3191  $start = 0;
3192  }
3193  $groupedNumber = substr( $number, $start, $end -$start ) . $groupedNumber;
3194  $end = $start;
3195  if ( $numMatches > 1 ) {
3196  // use the last pattern for the rest of the number
3197  $numMatches--;
3198  }
3199  if ( $start > 0 ) {
3200  $groupedNumber = "," . $groupedNumber;
3201  }
3202  }
3203  return $sign . $groupedNumber;
3204  }
3205  }
3206 
3210  function digitGroupingPattern() {
3211  return self::$dataCache->getItem( $this->mCode, 'digitGroupingPattern' );
3212  }
3213 
3217  function digitTransformTable() {
3218  return self::$dataCache->getItem( $this->mCode, 'digitTransformTable' );
3219  }
3220 
3224  function separatorTransformTable() {
3225  return self::$dataCache->getItem( $this->mCode, 'separatorTransformTable' );
3226  }
3227 
3237  function listToText( array $l ) {
3238  $m = count( $l ) - 1;
3239  if ( $m < 0 ) {
3240  return '';
3241  }
3242  if ( $m > 0 ) {
3243  $and = $this->getMessageFromDB( 'and' );
3244  $space = $this->getMessageFromDB( 'word-separator' );
3245  if ( $m > 1 ) {
3246  $comma = $this->getMessageFromDB( 'comma-separator' );
3247  }
3248  }
3249  $s = $l[$m];
3250  for ( $i = $m - 1; $i >= 0; $i-- ) {
3251  if ( $i == $m - 1 ) {
3252  $s = $l[$i] . $and . $space . $s;
3253  } else {
3254  $s = $l[$i] . $comma . $s;
3255  }
3256  }
3257  return $s;
3258  }
3259 
3266  function commaList( array $list ) {
3267  return implode(
3268  wfMessage( 'comma-separator' )->inLanguage( $this )->escaped(),
3269  $list
3270  );
3271  }
3272 
3279  function semicolonList( array $list ) {
3280  return implode(
3281  wfMessage( 'semicolon-separator' )->inLanguage( $this )->escaped(),
3282  $list
3283  );
3284  }
3285 
3291  function pipeList( array $list ) {
3292  return implode(
3293  wfMessage( 'pipe-separator' )->inLanguage( $this )->escaped(),
3294  $list
3295  );
3296  }
3297 
3315  function truncate( $string, $length, $ellipsis = '...', $adjustLength = true ) {
3316  # Use the localized ellipsis character
3317  if ( $ellipsis == '...' ) {
3318  $ellipsis = wfMessage( 'ellipsis' )->inLanguage( $this )->escaped();
3319  }
3320  # Check if there is no need to truncate
3321  if ( $length == 0 ) {
3322  return $ellipsis; // convention
3323  } elseif ( strlen( $string ) <= abs( $length ) ) {
3324  return $string; // no need to truncate
3325  }
3326  $stringOriginal = $string;
3327  # If ellipsis length is >= $length then we can't apply $adjustLength
3328  if ( $adjustLength && strlen( $ellipsis ) >= abs( $length ) ) {
3329  $string = $ellipsis; // this can be slightly unexpected
3330  # Otherwise, truncate and add ellipsis...
3331  } else {
3332  $eLength = $adjustLength ? strlen( $ellipsis ) : 0;
3333  if ( $length > 0 ) {
3334  $length -= $eLength;
3335  $string = substr( $string, 0, $length ); // xyz...
3336  $string = $this->removeBadCharLast( $string );
3337  $string = rtrim( $string );
3338  $string = $string . $ellipsis;
3339  } else {
3340  $length += $eLength;
3341  $string = substr( $string, $length ); // ...xyz
3342  $string = $this->removeBadCharFirst( $string );
3343  $string = ltrim( $string );
3344  $string = $ellipsis . $string;
3345  }
3346  }
3347  # Do not truncate if the ellipsis makes the string longer/equal (bug 22181).
3348  # This check is *not* redundant if $adjustLength, due to the single case where
3349  # LEN($ellipsis) > ABS($limit arg); $stringOriginal could be shorter than $string.
3350  if ( strlen( $string ) < strlen( $stringOriginal ) ) {
3351  return $string;
3352  } else {
3353  return $stringOriginal;
3354  }
3355  }
3356 
3364  protected function removeBadCharLast( $string ) {
3365  if ( $string != '' ) {
3366  $char = ord( $string[strlen( $string ) - 1] );
3367  $m = array();
3368  if ( $char >= 0xc0 ) {
3369  # We got the first byte only of a multibyte char; remove it.
3370  $string = substr( $string, 0, -1 );
3371  } elseif ( $char >= 0x80 &&
3372  preg_match( '/^(.*)(?:[\xe0-\xef][\x80-\xbf]|' .
3373  '[\xf0-\xf7][\x80-\xbf]{1,2})$/', $string, $m )
3374  ) {
3375  # We chopped in the middle of a character; remove it
3376  $string = $m[1];
3377  }
3378  }
3379  return $string;
3380  }
3381 
3389  protected function removeBadCharFirst( $string ) {
3390  if ( $string != '' ) {
3391  $char = ord( $string[0] );
3392  if ( $char >= 0x80 && $char < 0xc0 ) {
3393  # We chopped in the middle of a character; remove the whole thing
3394  $string = preg_replace( '/^[\x80-\xbf]+/', '', $string );
3395  }
3396  }
3397  return $string;
3398  }
3399 
3415  function truncateHtml( $text, $length, $ellipsis = '...' ) {
3416  # Use the localized ellipsis character
3417  if ( $ellipsis == '...' ) {
3418  $ellipsis = wfMessage( 'ellipsis' )->inLanguage( $this )->escaped();
3419  }
3420  # Check if there is clearly no need to truncate
3421  if ( $length <= 0 ) {
3422  return $ellipsis; // no text shown, nothing to format (convention)
3423  } elseif ( strlen( $text ) <= $length ) {
3424  return $text; // string short enough even *with* HTML (short-circuit)
3425  }
3426 
3427  $dispLen = 0; // innerHTML legth so far
3428  $testingEllipsis = false; // checking if ellipses will make string longer/equal?
3429  $tagType = 0; // 0-open, 1-close
3430  $bracketState = 0; // 1-tag start, 2-tag name, 0-neither
3431  $entityState = 0; // 0-not entity, 1-entity
3432  $tag = $ret = ''; // accumulated tag name, accumulated result string
3433  $openTags = array(); // open tag stack
3434  $maybeState = null; // possible truncation state
3435 
3436  $textLen = strlen( $text );
3437  $neLength = max( 0, $length - strlen( $ellipsis ) ); // non-ellipsis len if truncated
3438  for ( $pos = 0; true; ++$pos ) {
3439  # Consider truncation once the display length has reached the maximim.
3440  # We check if $dispLen > 0 to grab tags for the $neLength = 0 case.
3441  # Check that we're not in the middle of a bracket/entity...
3442  if ( $dispLen && $dispLen >= $neLength && $bracketState == 0 && !$entityState ) {
3443  if ( !$testingEllipsis ) {
3444  $testingEllipsis = true;
3445  # Save where we are; we will truncate here unless there turn out to
3446  # be so few remaining characters that truncation is not necessary.
3447  if ( !$maybeState ) { // already saved? ($neLength = 0 case)
3448  $maybeState = array( $ret, $openTags ); // save state
3449  }
3450  } elseif ( $dispLen > $length && $dispLen > strlen( $ellipsis ) ) {
3451  # String in fact does need truncation, the truncation point was OK.
3452  list( $ret, $openTags ) = $maybeState; // reload state
3453  $ret = $this->removeBadCharLast( $ret ); // multi-byte char fix
3454  $ret .= $ellipsis; // add ellipsis
3455  break;
3456  }
3457  }
3458  if ( $pos >= $textLen ) {
3459  break; // extra iteration just for above checks
3460  }
3461 
3462  # Read the next char...
3463  $ch = $text[$pos];
3464  $lastCh = $pos ? $text[$pos - 1] : '';
3465  $ret .= $ch; // add to result string
3466  if ( $ch == '<' ) {
3467  $this->truncate_endBracket( $tag, $tagType, $lastCh, $openTags ); // for bad HTML
3468  $entityState = 0; // for bad HTML
3469  $bracketState = 1; // tag started (checking for backslash)
3470  } elseif ( $ch == '>' ) {
3471  $this->truncate_endBracket( $tag, $tagType, $lastCh, $openTags );
3472  $entityState = 0; // for bad HTML
3473  $bracketState = 0; // out of brackets
3474  } elseif ( $bracketState == 1 ) {
3475  if ( $ch == '/' ) {
3476  $tagType = 1; // close tag (e.g. "</span>")
3477  } else {
3478  $tagType = 0; // open tag (e.g. "<span>")
3479  $tag .= $ch;
3480  }
3481  $bracketState = 2; // building tag name
3482  } elseif ( $bracketState == 2 ) {
3483  if ( $ch != ' ' ) {
3484  $tag .= $ch;
3485  } else {
3486  // Name found (e.g. "<a href=..."), add on tag attributes...
3487  $pos += $this->truncate_skip( $ret, $text, "<>", $pos + 1 );
3488  }
3489  } elseif ( $bracketState == 0 ) {
3490  if ( $entityState ) {
3491  if ( $ch == ';' ) {
3492  $entityState = 0;
3493  $dispLen++; // entity is one displayed char
3494  }
3495  } else {
3496  if ( $neLength == 0 && !$maybeState ) {
3497  // Save state without $ch. We want to *hit* the first
3498  // display char (to get tags) but not *use* it if truncating.
3499  $maybeState = array( substr( $ret, 0, -1 ), $openTags );
3500  }
3501  if ( $ch == '&' ) {
3502  $entityState = 1; // entity found, (e.g. "&#160;")
3503  } else {
3504  $dispLen++; // this char is displayed
3505  // Add the next $max display text chars after this in one swoop...
3506  $max = ( $testingEllipsis ? $length : $neLength ) - $dispLen;
3507  $skipped = $this->truncate_skip( $ret, $text, "<>&", $pos + 1, $max );
3508  $dispLen += $skipped;
3509  $pos += $skipped;
3510  }
3511  }
3512  }
3513  }
3514  // Close the last tag if left unclosed by bad HTML
3515  $this->truncate_endBracket( $tag, $text[$textLen - 1], $tagType, $openTags );
3516  while ( count( $openTags ) > 0 ) {
3517  $ret .= '</' . array_pop( $openTags ) . '>'; // close open tags
3518  }
3519  return $ret;
3520  }
3521 
3533  private function truncate_skip( &$ret, $text, $search, $start, $len = null ) {
3534  if ( $len === null ) {
3535  $len = -1; // -1 means "no limit" for strcspn
3536  } elseif ( $len < 0 ) {
3537  $len = 0; // sanity
3538  }
3539  $skipCount = 0;
3540  if ( $start < strlen( $text ) ) {
3541  $skipCount = strcspn( $text, $search, $start, $len );
3542  $ret .= substr( $text, $start, $skipCount );
3543  }
3544  return $skipCount;
3545  }
3546 
3556  private function truncate_endBracket( &$tag, $tagType, $lastCh, &$openTags ) {
3557  $tag = ltrim( $tag );
3558  if ( $tag != '' ) {
3559  if ( $tagType == 0 && $lastCh != '/' ) {
3560  $openTags[] = $tag; // tag opened (didn't close itself)
3561  } elseif ( $tagType == 1 ) {
3562  if ( $openTags && $tag == $openTags[count( $openTags ) - 1] ) {
3563  array_pop( $openTags ); // tag closed
3564  }
3565  }
3566  $tag = '';
3567  }
3568  }
3569 
3578  function convertGrammar( $word, $case ) {
3579  global $wgGrammarForms;
3580  if ( isset( $wgGrammarForms[$this->getCode()][$case][$word] ) ) {
3581  return $wgGrammarForms[$this->getCode()][$case][$word];
3582  }
3583  return $word;
3584  }
3590  function getGrammarForms() {
3591  global $wgGrammarForms;
3592  if ( isset( $wgGrammarForms[$this->getCode()] ) && is_array( $wgGrammarForms[$this->getCode()] ) ) {
3593  return $wgGrammarForms[$this->getCode()];
3594  }
3595  return array();
3596  }
3616  function gender( $gender, $forms ) {
3617  if ( !count( $forms ) ) {
3618  return '';
3619  }
3620  $forms = $this->preConvertPlural( $forms, 2 );
3621  if ( $gender === 'male' ) {
3622  return $forms[0];
3623  }
3624  if ( $gender === 'female' ) {
3625  return $forms[1];
3626  }
3627  return isset( $forms[2] ) ? $forms[2] : $forms[0];
3628  }
3629 
3645  function convertPlural( $count, $forms ) {
3646  // Handle explicit n=pluralform cases
3647  $forms = $this->handleExplicitPluralForms( $count, $forms );
3648  if ( is_string( $forms ) ) {
3649  return $forms;
3650  }
3651  if ( !count( $forms ) ) {
3652  return '';
3653  }
3654 
3655  $pluralForm = $this->getPluralRuleIndexNumber( $count );
3656  $pluralForm = min( $pluralForm, count( $forms ) - 1 );
3657  return $forms[$pluralForm];
3658  }
3659 
3675  protected function handleExplicitPluralForms( $count, array $forms ) {
3676  foreach ( $forms as $index => $form ) {
3677  if ( preg_match( '/\d+=/i', $form ) ) {
3678  $pos = strpos( $form, '=' );
3679  if ( substr( $form, 0, $pos ) === (string) $count ) {
3680  return substr( $form, $pos + 1 );
3681  }
3682  unset( $forms[$index] );
3683  }
3684  }
3685  return array_values( $forms );
3686  }
3687 
3696  protected function preConvertPlural( /* Array */ $forms, $count ) {
3697  while ( count( $forms ) < $count ) {
3698  $forms[] = $forms[count( $forms ) - 1];
3699  }
3700  return $forms;
3701  }
3702 
3714  function translateBlockExpiry( $str ) {
3715  $duration = SpecialBlock::getSuggestedDurations( $this );
3716  foreach ( $duration as $show => $value ) {
3717  if ( strcmp( $str, $value ) == 0 ) {
3718  return htmlspecialchars( trim( $show ) );
3719  }
3720  }
3721 
3722  // Since usually only infinite or indefinite is only on list, so try
3723  // equivalents if still here.
3724  $indefs = array( 'infinite', 'infinity', 'indefinite' );
3725  if ( in_array( $str, $indefs ) ) {
3726  foreach ( $indefs as $val ) {
3727  $show = array_search( $val, $duration, true );
3728  if ( $show !== false ) {
3729  return htmlspecialchars( trim( $show ) );
3730  }
3731  }
3732  }
3733 
3734  // If all else fails, return a standard duration or timestamp description.
3735  $time = strtotime( $str, 0 );
3736  if ( $time === false ) { // Unknown format. Return it as-is in case.
3737  return $str;
3738  } elseif ( $time !== strtotime( $str, 1 ) ) { // It's a relative timestamp.
3739  // $time is relative to 0 so it's a duration length.
3740  return $this->formatDuration( $time );
3741  } else { // It's an absolute timestamp.
3742  if ( $time === 0 ) {
3743  // wfTimestamp() handles 0 as current time instead of epoch.
3744  return $this->timeanddate( '19700101000000' );
3745  } else {
3746  return $this->timeanddate( $time );
3747  }
3748  }
3749  }
3750 
3758  public function segmentForDiff( $text ) {
3759  return $text;
3760  }
3761 
3768  public function unsegmentForDiff( $text ) {
3769  return $text;
3770  }
3771 
3778  public function getConverter() {
3779  return $this->mConverter;
3780  }
3781 
3788  public function autoConvertToAllVariants( $text ) {
3789  return $this->mConverter->autoConvertToAllVariants( $text );
3790  }
3791 
3798  public function convert( $text ) {
3799  return $this->mConverter->convert( $text );
3800  }
3801 
3808  public function convertTitle( $title ) {
3809  return $this->mConverter->convertTitle( $title );
3810  }
3811 
3818  public function convertNamespace( $ns ) {
3819  return $this->mConverter->convertNamespace( $ns );
3820  }
3821 
3827  public function hasVariants() {
3828  return count( $this->getVariants() ) > 1;
3829  }
3830 
3838  public function hasVariant( $variant ) {
3839  return (bool)$this->mConverter->validateVariant( $variant );
3840  }
3841 
3849  public function armourMath( $text ) {
3850  return $this->mConverter->armourMath( $text );
3851  }
3852 
3860  public function convertHtml( $text, $isTitle = false ) {
3861  return htmlspecialchars( $this->convert( $text, $isTitle ) );
3862  }
3863 
3868  public function convertCategoryKey( $key ) {
3869  return $this->mConverter->convertCategoryKey( $key );
3870  }
3871 
3878  public function getVariants() {
3879  return $this->mConverter->getVariants();
3880  }
3881 
3885  public function getPreferredVariant() {
3886  return $this->mConverter->getPreferredVariant();
3887  }
3888 
3892  public function getDefaultVariant() {
3893  return $this->mConverter->getDefaultVariant();
3894  }
3895 
3899  public function getURLVariant() {
3900  return $this->mConverter->getURLVariant();
3901  }
3902 
3915  public function findVariantLink( &$link, &$nt, $ignoreOtherCond = false ) {
3916  $this->mConverter->findVariantLink( $link, $nt, $ignoreOtherCond );
3917  }
3918 
3925  function getExtraHashOptions() {
3926  return $this->mConverter->getExtraHashOptions();
3927  }
3928 
3936  public function getParsedTitle() {
3937  return $this->mConverter->getParsedTitle();
3938  }
3939 
3952  public function markNoConversion( $text, $noParse = false ) {
3953  // Excluding protocal-relative URLs may avoid many false positives.
3954  if ( $noParse || preg_match( '/^(?:' . wfUrlProtocolsWithoutProtRel() . ')/', $text ) ) {
3955  return $this->mConverter->markNoConversion( $text );
3956  } else {
3957  return $text;
3958  }
3959  }
3960 
3967  public function linkTrail() {
3968  return self::$dataCache->getItem( $this->mCode, 'linkTrail' );
3969  }
3970 
3977  public function linkPrefixCharset() {
3978  return self::$dataCache->getItem( $this->mCode, 'linkPrefixCharset' );
3979  }
3980 
3984  function getLangObj() {
3985  return $this;
3986  }
3987 
3995  public function getParentLanguage() {
3996  if ( $this->mParentLanguage !== false ) {
3997  return $this->mParentLanguage;
3998  }
3999 
4000  $pieces = explode( '-', $this->getCode() );
4001  $code = $pieces[0];
4002  if ( !in_array( $code, LanguageConverter::$languagesWithVariants ) ) {
4003  $this->mParentLanguage = null;
4004  return null;
4005  }
4006  $lang = Language::factory( $code );
4007  if ( !$lang->hasVariant( $this->getCode() ) ) {
4008  $this->mParentLanguage = null;
4009  return null;
4010  }
4011 
4012  $this->mParentLanguage = $lang;
4013  return $lang;
4014  }
4015 
4024  public function getCode() {
4025  return $this->mCode;
4026  }
4027 
4038  public function getHtmlCode() {
4039  if ( is_null( $this->mHtmlCode ) ) {
4040  $this->mHtmlCode = wfBCP47( $this->getCode() );
4041  }
4042  return $this->mHtmlCode;
4043  }
4044 
4048  public function setCode( $code ) {
4049  $this->mCode = $code;
4050  // Ensure we don't leave incorrect cached data lying around
4051  $this->mHtmlCode = null;
4052  $this->mParentLanguage = false;
4053  }
4054 
4063  public static function getFileName( $prefix = 'Language', $code, $suffix = '.php' ) {
4064  if ( !self::isValidBuiltInCode( $code ) ) {
4065  throw new MWException( "Invalid language code \"$code\"" );
4066  }
4067 
4068  return $prefix . str_replace( '-', '_', ucfirst( $code ) ) . $suffix;
4069  }
4070 
4078  public static function getCodeFromFileName( $filename, $prefix = 'Language', $suffix = '.php' ) {
4079  $m = null;
4080  preg_match( '/' . preg_quote( $prefix, '/' ) . '([A-Z][a-z_]+)' .
4081  preg_quote( $suffix, '/' ) . '/', $filename, $m );
4082  if ( !count( $m ) ) {
4083  return false;
4084  }
4085  return str_replace( '_', '-', strtolower( $m[1] ) );
4086  }
4087 
4092  public static function getMessagesFileName( $code ) {
4093  global $IP;
4094  $file = self::getFileName( "$IP/languages/messages/Messages", $code, '.php' );
4095  wfRunHooks( 'Language::getMessagesFileName', array( $code, &$file ) );
4096  return $file;
4097  }
4098 
4104  public static function getJsonMessagesFileName( $code ) {
4105  global $IP;
4106 
4107  if ( !self::isValidBuiltInCode( $code ) ) {
4108  throw new MWException( "Invalid language code \"$code\"" );
4109  }
4110 
4111  return "$IP/languages/i18n/$code.json" ;
4112  }
4113 
4118  public static function getClassFileName( $code ) {
4119  global $IP;
4120  return self::getFileName( "$IP/languages/classes/Language", $code, '.php' );
4121  }
4122 
4130  public static function getFallbackFor( $code ) {
4131  if ( $code === 'en' || !Language::isValidBuiltInCode( $code ) ) {
4132  return false;
4133  } else {
4134  $fallbacks = self::getFallbacksFor( $code );
4135  $first = array_shift( $fallbacks );
4136  return $first;
4137  }
4138  }
4139 
4147  public static function getFallbacksFor( $code ) {
4148  if ( $code === 'en' || !Language::isValidBuiltInCode( $code ) ) {
4149  return array();
4150  } else {
4151  $v = self::getLocalisationCache()->getItem( $code, 'fallback' );
4152  $v = array_map( 'trim', explode( ',', $v ) );
4153  if ( $v[count( $v ) - 1] !== 'en' ) {
4154  $v[] = 'en';
4155  }
4156  return $v;
4157  }
4158  }
4159 
4168  public static function getFallbacksIncludingSiteLanguage( $code ) {
4169  global $wgLanguageCode;
4170 
4171  // Usually, we will only store a tiny number of fallback chains, so we
4172  // keep them in static memory.
4173  $cacheKey = "{$code}-{$wgLanguageCode}";
4174 
4175  if ( !array_key_exists( $cacheKey, self::$fallbackLanguageCache ) ) {
4176  $fallbacks = self::getFallbacksFor( $code );
4177 
4178  // Append the site's fallback chain, including the site language itself
4179  $siteFallbacks = self::getFallbacksFor( $wgLanguageCode );
4180  array_unshift( $siteFallbacks, $wgLanguageCode );
4181 
4182  // Eliminate any languages already included in the chain
4183  $siteFallbacks = array_diff( $siteFallbacks, $fallbacks );
4184 
4185  self::$fallbackLanguageCache[$cacheKey] = array( $fallbacks, $siteFallbacks );
4186  }
4187  return self::$fallbackLanguageCache[$cacheKey];
4188  }
4189 
4199  public static function getMessagesFor( $code ) {
4200  return self::getLocalisationCache()->getItem( $code, 'messages' );
4201  }
4202 
4211  public static function getMessageFor( $key, $code ) {
4212  return self::getLocalisationCache()->getSubitem( $code, 'messages', $key );
4213  }
4214 
4223  public static function getMessageKeysFor( $code ) {
4224  return self::getLocalisationCache()->getSubItemList( $code, 'messages' );
4225  }
4226 
4231  function fixVariableInNamespace( $talk ) {
4232  if ( strpos( $talk, '$1' ) === false ) {
4233  return $talk;
4234  }
4235 
4236  global $wgMetaNamespace;
4237  $talk = str_replace( '$1', $wgMetaNamespace, $talk );
4238 
4239  # Allow grammar transformations
4240  # Allowing full message-style parsing would make simple requests
4241  # such as action=raw much more expensive than they need to be.
4242  # This will hopefully cover most cases.
4243  $talk = preg_replace_callback( '/{{grammar:(.*?)\|(.*?)}}/i',
4244  array( &$this, 'replaceGrammarInNamespace' ), $talk );
4245  return str_replace( ' ', '_', $talk );
4246  }
4247 
4252  function replaceGrammarInNamespace( $m ) {
4253  return $this->convertGrammar( trim( $m[2] ), trim( $m[1] ) );
4254  }
4255 
4260  static function getCaseMaps() {
4261  static $wikiUpperChars, $wikiLowerChars;
4262  if ( isset( $wikiUpperChars ) ) {
4263  return array( $wikiUpperChars, $wikiLowerChars );
4264  }
4265 
4266  wfProfileIn( __METHOD__ );
4267  $arr = wfGetPrecompiledData( 'Utf8Case.ser' );
4268  if ( $arr === false ) {
4269  throw new MWException(
4270  "Utf8Case.ser is missing, please run \"make\" in the serialized directory\n" );
4271  }
4272  $wikiUpperChars = $arr['wikiUpperChars'];
4273  $wikiLowerChars = $arr['wikiLowerChars'];
4274  wfProfileOut( __METHOD__ );
4275  return array( $wikiUpperChars, $wikiLowerChars );
4276  }
4277 
4289  public function formatExpiry( $expiry, $format = true ) {
4290  static $infinity;
4291  if ( $infinity === null ) {
4292  $infinity = wfGetDB( DB_SLAVE )->getInfinity();
4293  }
4294 
4295  if ( $expiry == '' || $expiry == $infinity ) {
4296  return $format === true
4297  ? $this->getMessageFromDB( 'infiniteblock' )
4298  : $infinity;
4299  } else {
4300  return $format === true
4301  ? $this->timeanddate( $expiry, /* User preference timezone */ true )
4302  : wfTimestamp( $format, $expiry );
4303  }
4304  }
4305 
4316  function formatTimePeriod( $seconds, $format = array() ) {
4317  if ( !is_array( $format ) ) {
4318  $format = array( 'avoid' => $format ); // For backwards compatibility
4319  }
4320  if ( !isset( $format['avoid'] ) ) {
4321  $format['avoid'] = false;
4322  }
4323  if ( !isset( $format['noabbrevs' ] ) ) {
4324  $format['noabbrevs'] = false;
4325  }
4326  $secondsMsg = wfMessage(
4327  $format['noabbrevs'] ? 'seconds' : 'seconds-abbrev' )->inLanguage( $this );
4328  $minutesMsg = wfMessage(
4329  $format['noabbrevs'] ? 'minutes' : 'minutes-abbrev' )->inLanguage( $this );
4330  $hoursMsg = wfMessage(
4331  $format['noabbrevs'] ? 'hours' : 'hours-abbrev' )->inLanguage( $this );
4332  $daysMsg = wfMessage(
4333  $format['noabbrevs'] ? 'days' : 'days-abbrev' )->inLanguage( $this );
4334 
4335  if ( round( $seconds * 10 ) < 100 ) {
4336  $s = $this->formatNum( sprintf( "%.1f", round( $seconds * 10 ) / 10 ) );
4337  $s = $secondsMsg->params( $s )->text();
4338  } elseif ( round( $seconds ) < 60 ) {
4339  $s = $this->formatNum( round( $seconds ) );
4340  $s = $secondsMsg->params( $s )->text();
4341  } elseif ( round( $seconds ) < 3600 ) {
4342  $minutes = floor( $seconds / 60 );
4343  $secondsPart = round( fmod( $seconds, 60 ) );
4344  if ( $secondsPart == 60 ) {
4345  $secondsPart = 0;
4346  $minutes++;
4347  }
4348  $s = $minutesMsg->params( $this->formatNum( $minutes ) )->text();
4349  $s .= ' ';
4350  $s .= $secondsMsg->params( $this->formatNum( $secondsPart ) )->text();
4351  } elseif ( round( $seconds ) <= 2 * 86400 ) {
4352  $hours = floor( $seconds / 3600 );
4353  $minutes = floor( ( $seconds - $hours * 3600 ) / 60 );
4354  $secondsPart = round( $seconds - $hours * 3600 - $minutes * 60 );
4355  if ( $secondsPart == 60 ) {
4356  $secondsPart = 0;
4357  $minutes++;
4358  }
4359  if ( $minutes == 60 ) {
4360  $minutes = 0;
4361  $hours++;
4362  }
4363  $s = $hoursMsg->params( $this->formatNum( $hours ) )->text();
4364  $s .= ' ';
4365  $s .= $minutesMsg->params( $this->formatNum( $minutes ) )->text();
4366  if ( !in_array( $format['avoid'], array( 'avoidseconds', 'avoidminutes' ) ) ) {
4367  $s .= ' ' . $secondsMsg->params( $this->formatNum( $secondsPart ) )->text();
4368  }
4369  } else {
4370  $days = floor( $seconds / 86400 );
4371  if ( $format['avoid'] === 'avoidminutes' ) {
4372  $hours = round( ( $seconds - $days * 86400 ) / 3600 );
4373  if ( $hours == 24 ) {
4374  $hours = 0;
4375  $days++;
4376  }
4377  $s = $daysMsg->params( $this->formatNum( $days ) )->text();
4378  $s .= ' ';
4379  $s .= $hoursMsg->params( $this->formatNum( $hours ) )->text();
4380  } elseif ( $format['avoid'] === 'avoidseconds' ) {
4381  $hours = floor( ( $seconds - $days * 86400 ) / 3600 );
4382  $minutes = round( ( $seconds - $days * 86400 - $hours * 3600 ) / 60 );
4383  if ( $minutes == 60 ) {
4384  $minutes = 0;
4385  $hours++;
4386  }
4387  if ( $hours == 24 ) {
4388  $hours = 0;
4389  $days++;
4390  }
4391  $s = $daysMsg->params( $this->formatNum( $days ) )->text();
4392  $s .= ' ';
4393  $s .= $hoursMsg->params( $this->formatNum( $hours ) )->text();
4394  $s .= ' ';
4395  $s .= $minutesMsg->params( $this->formatNum( $minutes ) )->text();
4396  } else {
4397  $s = $daysMsg->params( $this->formatNum( $days ) )->text();
4398  $s .= ' ';
4399  $s .= $this->formatTimePeriod( $seconds - $days * 86400, $format );
4400  }
4401  }
4402  return $s;
4403  }
4404 
4415  function formatBitrate( $bps ) {
4416  return $this->formatComputingNumbers( $bps, 1000, "bitrate-$1bits" );
4417  }
4418 
4425  function formatComputingNumbers( $size, $boundary, $messageKey ) {
4426  if ( $size <= 0 ) {
4427  return str_replace( '$1', $this->formatNum( $size ),
4428  $this->getMessageFromDB( str_replace( '$1', '', $messageKey ) )
4429  );
4430  }
4431  $sizes = array( '', 'kilo', 'mega', 'giga', 'tera', 'peta', 'exa', 'zeta', 'yotta' );
4432  $index = 0;
4433 
4434  $maxIndex = count( $sizes ) - 1;
4435  while ( $size >= $boundary && $index < $maxIndex ) {
4436  $index++;
4437  $size /= $boundary;
4438  }
4439 
4440  // For small sizes no decimal places necessary
4441  $round = 0;
4442  if ( $index > 1 ) {
4443  // For MB and bigger two decimal places are smarter
4444  $round = 2;
4445  }
4446  $msg = str_replace( '$1', $sizes[$index], $messageKey );
4447 
4448  $size = round( $size, $round );
4449  $text = $this->getMessageFromDB( $msg );
4450  return str_replace( '$1', $this->formatNum( $size ), $text );
4451  }
4452 
4463  function formatSize( $size ) {
4464  return $this->formatComputingNumbers( $size, 1024, "size-$1bytes" );
4465  }
4466 
4476  function specialList( $page, $details, $oppositedm = true ) {
4477  $dirmark = ( $oppositedm ? $this->getDirMark( true ) : '' ) .
4478  $this->getDirMark();
4479  $details = $details ? $dirmark . $this->getMessageFromDB( 'word-separator' ) .
4480  wfMessage( 'parentheses' )->rawParams( $details )->inLanguage( $this )->escaped() : '';
4481  return $page . $details;
4482  }
4483 
4494  public function viewPrevNext( Title $title, $offset, $limit, array $query = array(), $atend = false ) {
4495  // @todo FIXME: Why on earth this needs one message for the text and another one for tooltip?
4496 
4497  # Make 'previous' link
4498  $prev = wfMessage( 'prevn' )->inLanguage( $this )->title( $title )->numParams( $limit )->text();
4499  if ( $offset > 0 ) {
4500  $plink = $this->numLink( $title, max( $offset - $limit, 0 ), $limit,
4501  $query, $prev, 'prevn-title', 'mw-prevlink' );
4502  } else {
4503  $plink = htmlspecialchars( $prev );
4504  }
4505 
4506  # Make 'next' link
4507  $next = wfMessage( 'nextn' )->inLanguage( $this )->title( $title )->numParams( $limit )->text();
4508  if ( $atend ) {
4509  $nlink = htmlspecialchars( $next );
4510  } else {
4511  $nlink = $this->numLink( $title, $offset + $limit, $limit,
4512  $query, $next, 'nextn-title', 'mw-nextlink' );
4513  }
4514 
4515  # Make links to set number of items per page
4516  $numLinks = array();
4517  foreach ( array( 20, 50, 100, 250, 500 ) as $num ) {
4518  $numLinks[] = $this->numLink( $title, $offset, $num,
4519  $query, $this->formatNum( $num ), 'shown-title', 'mw-numlink' );
4520  }
4521 
4522  return wfMessage( 'viewprevnext' )->inLanguage( $this )->title( $title
4523  )->rawParams( $plink, $nlink, $this->pipeList( $numLinks ) )->escaped();
4524  }
4525 
4538  private function numLink( Title $title, $offset, $limit, array $query, $link, $tooltipMsg, $class ) {
4539  $query = array( 'limit' => $limit, 'offset' => $offset ) + $query;
4540  $tooltip = wfMessage( $tooltipMsg )->inLanguage( $this )->title( $title )->numParams( $limit )->text();
4541  return Html::element( 'a', array( 'href' => $title->getLocalURL( $query ),
4542  'title' => $tooltip, 'class' => $class ), $link );
4543  }
4544 
4550  public function getConvRuleTitle() {
4551  return $this->mConverter->getConvRuleTitle();
4552  }
4553 
4559  public function getCompiledPluralRules() {
4560  $pluralRules = self::$dataCache->getItem( strtolower( $this->mCode ), 'compiledPluralRules' );
4561  $fallbacks = Language::getFallbacksFor( $this->mCode );
4562  if ( !$pluralRules ) {
4563  foreach ( $fallbacks as $fallbackCode ) {
4564  $pluralRules = self::$dataCache->getItem( strtolower( $fallbackCode ), 'compiledPluralRules' );
4565  if ( $pluralRules ) {
4566  break;
4567  }
4568  }
4569  }
4570  return $pluralRules;
4571  }
4572 
4578  public function getPluralRules() {
4579  $pluralRules = self::$dataCache->getItem( strtolower( $this->mCode ), 'pluralRules' );
4580  $fallbacks = Language::getFallbacksFor( $this->mCode );
4581  if ( !$pluralRules ) {
4582  foreach ( $fallbacks as $fallbackCode ) {
4583  $pluralRules = self::$dataCache->getItem( strtolower( $fallbackCode ), 'pluralRules' );
4584  if ( $pluralRules ) {
4585  break;
4586  }
4587  }
4588  }
4589  return $pluralRules;
4590  }
4591 
4597  public function getPluralRuleTypes() {
4598  $pluralRuleTypes = self::$dataCache->getItem( strtolower( $this->mCode ), 'pluralRuleTypes' );
4599  $fallbacks = Language::getFallbacksFor( $this->mCode );
4600  if ( !$pluralRuleTypes ) {
4601  foreach ( $fallbacks as $fallbackCode ) {
4602  $pluralRuleTypes = self::$dataCache->getItem( strtolower( $fallbackCode ), 'pluralRuleTypes' );
4603  if ( $pluralRuleTypes ) {
4604  break;
4605  }
4606  }
4607  }
4608  return $pluralRuleTypes;
4609  }
4610 
4615  public function getPluralRuleIndexNumber( $number ) {
4616  $pluralRules = $this->getCompiledPluralRules();
4617  $form = CLDRPluralRuleEvaluator::evaluateCompiled( $number, $pluralRules );
4618  return $form;
4619  }
4620 
4628  public function getPluralRuleType( $number ) {
4629  $index = $this->getPluralRuleIndexNumber( $number );
4630  $pluralRuleTypes = $this->getPluralRuleTypes();
4631  if ( isset( $pluralRuleTypes[$index] ) ) {
4632  return $pluralRuleTypes[$index];
4633  } else {
4634  return 'other';
4635  }
4636  }
4637 }
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:2176
Language\getFallbacksFor
static getFallbacksFor( $code)
Get the ordered list of fallback languages.
Definition: Language.php:4145
Language\getCodeFromFileName
static getCodeFromFileName( $filename, $prefix='Language', $suffix='.php')
Get the language code from a file name.
Definition: Language.php:4076
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:3923
Language\lc
lc( $str, $first=false)
Definition: Language.php:2503
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:3135
Language\getURLVariant
getURLVariant()
Definition: Language.php:3897
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:572
Language\replaceGrammarInNamespace
replaceGrammarInNamespace( $m)
Definition: Language.php:4250
Language\linkPrefixExtension
linkPrefixExtension()
To allow "foo[[bar]]" to extend the link over the whole word "foobar".
Definition: Language.php:2983
wfBCP47
wfBCP47( $code)
Get the normalised IETF language tag See unit test for examples.
Definition: GlobalFunctions.php:3977
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:3554
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:3264
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:3277
Language\$IRANIAN_DAYS
static $IRANIAN_DAYS
Definition: Language.php:1431
Language\$mIranianCalendarMonthMsgs
static $mIranianCalendarMonthMsgs
Definition: Language.php:125
Language\getIranianCalendarMonthName
getIranianCalendarMonthName( $key)
Definition: Language.php:1034
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:4461
Language\formatTimePeriod
formatTimePeriod( $seconds, $format=array())
Definition: Language.php:4314
Language\separatorTransformTable
separatorTransformTable()
Definition: Language.php:3222
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:3776
Language\hasVariants
hasVariants()
Check if this is a language with variants.
Definition: Language.php:3825
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:3694
Language\segmentByWord
segmentByWord( $string)
Some languages such as Chinese require word segmentation, Specify such segmentation when overridden i...
Definition: Language.php:2664
Language\iconv
iconv( $in, $out, $string)
Definition: Language.php:2361
Language\getMessageFromDB
getMessageFromDB( $msg)
Get a message from the MediaWiki namespace.
Definition: Language.php:953
Language\$GREG_DAYS
static $GREG_DAYS
Definition: Language.php:1430
wfGetDB
& wfGetDB( $db, $groups=array(), $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:3706
Language\convert
convert( $text)
convert text to different variants of a language.
Definition: Language.php:3796
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:3313
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:2134
Language\convertGrammar
convertGrammar( $word, $case)
Grammatical transformations, needed for inflected languages Invoked by putting {{grammar:case|word}} ...
Definition: Language.php:3576
$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:2578
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:2530
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:1127
$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:2434
Language\timeanddate
timeanddate( $ts, $adj=false, $format=true, $timecorrection=false)
Definition: Language.php:2089
Language\getHebrewCalendarMonthNameGen
getHebrewCalendarMonthNameGen( $key)
Definition: Language.php:1050
Language\$mMagicHookDone
$mMagicHookDone
Definition: Language.php:81
$time
see documentation in includes Linker php for Linker::makeImageLink & $time
Definition: hooks.txt:1358
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:3235
Language\getMonthNamesArray
getMonthNamesArray()
Definition: Language.php:979
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:3883
Language\getMonthAbbreviation
getMonthAbbreviation( $key)
Definition: Language.php:999
Language\ucwordbreaksCallbackAscii
ucwordbreaksCallbackAscii( $matches)
Definition: Language.php:2381
$digitGroupingPattern
$digitGroupingPattern
Definition: MessagesAs.php:168
Language\recodeForEdit
recodeForEdit( $s)
Definition: Language.php:2792
Language\markNoConversion
markNoConversion( $text, $noParse=false)
Prepare external link text for conversion.
Definition: Language.php:3950
User\getDatePreference
getDatePreference()
Get the user's preferred date format.
Definition: User.php:2729
Language\getDefaultVariant
getDefaultVariant()
Definition: Language.php:3890
Language\fallback8bitEncoding
fallback8bitEncoding()
Definition: Language.php:2641
Language\getFileName
static getFileName( $prefix='Language', $code, $suffix='.php')
Get the name of a file for a certain language code.
Definition: Language.php:4061
Language\formatBitrate
formatBitrate( $bps)
Format a bitrate for output, using an appropriate unit (bps, kbps, Mbps, Gbps, Tbps,...
Definition: Language.php:4413
Language\convertNamespace
convertNamespace( $ns)
Convert a namespace index to a string in the preferred variant.
Definition: Language.php:3816
Language\getSpecialPageAliases
getSpecialPageAliases()
Get special page names, as an associative array case folded alias => real name.
Definition: Language.php:3053
Language\getMessagesFileName
static getMessagesFileName( $code)
Definition: Language.php:4090
Language\getPluralRuleTypes
getPluralRuleTypes()
Get the plural rule types for the language.
Definition: Language.php:4595
Language\capitalizeAllNouns
capitalizeAllNouns()
Definition: Language.php:2951
$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:2154
Language\specialList
specialList( $page, $details, $oppositedm=true)
Make a list item, used by various special pages.
Definition: Language.php:4474
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:964
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:2215
Language\checkTitleEncoding
checkTitleEncoding( $s)
Definition: Language.php:2627
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:2484
Language\initEncoding
initEncoding()
Definition: Language.php:2781
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:3072
Language\getFallbackLanguages
getFallbackLanguages()
Definition: Language.php:492
Language\caseFold
caseFold( $s)
Return a case-folded representation of $s.
Definition: Language.php:2619
Language\segmentForDiff
segmentForDiff( $text)
languages like Chinese need to be segmented in order for the diff to be of any use
Definition: Language.php:3756
FakeConverter\convert
convert( $t)
Definition: Language.php:49
Language\translateBlockExpiry
translateBlockExpiry( $str)
Definition: Language.php:3712
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:1026
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:3934
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:2261
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:1174
Language\tsToIranian
static tsToIranian( $ts)
Algorithm by Roozbeh Pournader and Mohammad Toossi to convert Gregorian dates to Iranian dates.
Definition: Language.php:1445
Language\unsegmentForDiff
unsegmentForDiff( $text)
and unsegment to show the result
Definition: Language.php:3766
wfRestoreWarnings
wfRestoreWarnings()
Restore error level to previous value.
Definition: GlobalFunctions.php:2464
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:3413
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:2834
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:3289
Language\doMagicHook
doMagicHook()
Run the LanguageGetMagic hook once.
Definition: Language.php:2998
Language\convertForSearchResult
convertForSearchResult( $termsArray)
Definition: Language.php:2717
wfUrlProtocolsWithoutProtRel
wfUrlProtocolsWithoutProtRel()
Like wfUrlProtocols(), but excludes '//' from the protocol list.
Definition: GlobalFunctions.php:787
Language\getMessageFor
static getMessageFor( $key, $code)
Get a message for a given language.
Definition: Language.php:4209
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:4036
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:3039
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:972
Language\$mMonthMsgs
static $mMonthMsgs
Definition: Language.php:110
Language\armourMath
armourMath( $text)
Put custom tags (e.g.
Definition: Language.php:3847
Language\getAllMessages
getAllMessages()
Definition: Language.php:2351
wfRunHooks
wfRunHooks( $event, array $args=array(), $deprecatedVersion=null)
Call hook functions defined in $wgHooks.
Definition: GlobalFunctions.php:4058
Language\getMagic
getMagic( $mw)
Fill a MagicWord object with data from here.
Definition: Language.php:3013
Language\isRTL
isRTL()
For right-to-left language support.
Definition: Language.php:2875
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:2238
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:4613
Language\getMonthNameGen
getMonthNameGen( $key)
Definition: Language.php:991
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:2687
cssjanus.LEFT
string LEFT
Definition: cssjanus.py:48
Language\getCompiledPluralRules
getCompiledPluralRules()
Get the compiled plural rules for the language.
Definition: Language.php:4557
Language\convertCategoryKey
convertCategoryKey( $key)
Definition: Language.php:3866
Language\digitGroupingPattern
digitGroupingPattern()
Definition: Language.php:3208
Language\ucwords
ucwords( $str)
Definition: Language.php:2540
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:4576
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:2961
Language\uc
uc( $str, $first=false)
Convert a string to uppercase.
Definition: Language.php:2455
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:3127
Language\getClassFileName
static getClassFileName( $code)
Definition: Language.php:4116
FakeConverter\getVariants
getVariants()
Definition: Language.php:53
Language\lcCallback
lcCallback( $matches)
Definition: Language.php:2406
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:4492
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:2478
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:3975
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:3913
$matches
if(!defined( 'MEDIAWIKI')) if(!isset( $wgVersion)) $matches
Definition: NoLocalSettings.php:33
Language\fixVariableInNamespace
fixVariableInNamespace( $talk)
Definition: Language.php:4229
$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:2572
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:3982
Language\getCode
getCode()
Get the RFC 3066 code for this language object.
Definition: Language.php:4022
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:4626
$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:1988
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:2415
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:1839
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:4197
Language\ucwordbreaksCallbackMB
ucwordbreaksCallbackMB( $matches)
Definition: Language.php:2389
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:3673
Language\recodeInput
recodeInput( $s)
Definition: Language.php:2808
Language\fetchLanguageName
static fetchLanguageName( $code, $inLanguage=null, $include='all')
Definition: Language.php:941
$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:2939
Language\normalizeForSearch
normalizeForSearch( $string)
Some languages have special punctuation need to be normalized.
Definition: Language.php:2675
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:4287
FakeConverter
a fake language converter
Definition: Language.php:42
Language\ucCallback
ucCallback( $matches)
Definition: Language.php:2397
Language\userAdjust
userAdjust( $ts, $tz=false)
Used by date() and time() to adjust the time output.
Definition: Language.php:1903
Language\formatComputingNumbers
formatComputingNumbers( $size, $boundary, $messageKey)
Definition: Language.php:4423
$file
if(PHP_SAPI !='cli') $file
Definition: UtfNormalTest2.php:30
Language\ucwordsCallbackWiki
ucwordsCallbackWiki( $matches)
Definition: Language.php:2423
$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:3858
Language\getFallbackFor
static getFallbackFor( $code)
Get the first fallback for a given language.
Definition: Language.php:4128
Language\__construct
__construct()
Definition: Language.php:452
Language\getMonthAbbreviationsArray
getMonthAbbreviationsArray()
Definition: Language.php:1006
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:3993
Language\$dateFormatStrings
$dateFormatStrings
Definition: Language.php:84
Language\hasWordBreaks
hasWordBreaks()
Most writing systems use whitespace to break up words.
Definition: Language.php:2653
Language\getDir
getDir()
Return the correct HTML 'dir' attribute value for this language.
Definition: Language.php:2883
Language\date
date( $ts, $adj=false, $format=true, $timecorrection=false)
Definition: Language.php:2050
Language\alignEnd
alignEnd()
Return 'right' or 'left' as appropriate alignment for line-end for this language's text direction.
Definition: Language.php:2907
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:3531
Language\setCode
setCode( $code)
Definition: Language.php:4046
$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:3876
Language\convertPlural
convertPlural( $count, $forms)
Plural form transformations, needed for some languages.
Definition: Language.php:3643
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:3608
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:4166
$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:2280
Language\getGrammarForms
getGrammarForms()
Get the grammar forms for the content language.
Definition: Language.php:3588
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:3387
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:1058
Language\getMessage
getMessage( $key)
Definition: Language.php:2344
Language\getMessageKeysFor
static getMessageKeysFor( $code)
Get all message keys for a given language.
Definition: Language.php:4221
cssjanus.RIGHT
string RIGHT
Definition: cssjanus.py:49
Language\getJsonMessagesFileName
static getJsonMessagesFileName( $code)
Definition: Language.php:4102
Language\firstChar
firstChar( $s)
Get the first character of a string.
Definition: Language.php:2729
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:2108
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:2859
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:3362
Language\romanNumeral
static romanNumeral( $num)
Roman number formatting up to 10000.
Definition: Language.php:1809
FakeConverter\getPreferredVariant
getPreferredVariant()
Definition: Language.php:55
Language\getConvRuleTitle
getConvRuleTitle()
Get the conversion rule title, if any.
Definition: Language.php:4548
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:4258
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:1503
$t
$t
Definition: testCompression.php:65
Language\time
time( $ts, $adj=false, $format=true, $timecorrection=false)
Definition: Language.php:2069
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:2707
Language\isMultibyte
isMultibyte( $str)
Definition: Language.php:2532
Language\alignStart
alignStart()
Return 'left' or 'right' as appropriate alignment for line-start for this language's text direction.
Definition: Language.php:2895
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:3215
Language\tsToYear
static tsToYear( $ts, $cName)
Algorithm to convert Gregorian dates to Thai solar dates, Minguo dates or Minguo dates.
Definition: Language.php:1732
Language\getDirMarkEntity
getDirMarkEntity( $opposite=false)
A hidden direction mark (LRM or RLM), depending on the language direction.
Definition: Language.php:2922
Language\hebrewYearStart
static hebrewYearStart( $year)
This calculates the Hebrew year start, as days since 1 September.
Definition: Language.php:1694
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:2018
Language\getMagicWords
getMagicWords()
Get all magic words from cache.
Definition: Language.php:2991
Language\hasVariant
hasVariant( $variant)
Check if the language has the specific variant.
Definition: Language.php:3836
Language\numLink
numLink(Title $title, $offset, $limit, array $query, $link, $tooltipMsg, $class)
Helper function for viewPrevNext() that generates links.
Definition: Language.php:4536
Language\__destruct
__destruct()
Reduce memory usage.
Definition: Language.php:466
Language\commafy
commafy( $number)
Adds commas to a given number.
Definition: Language.php:3156
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:1553
Language\autoConvertToAllVariants
autoConvertToAllVariants( $text)
convert text to all supported variants
Definition: Language.php:3786
Language\linkTrail
linkTrail()
A regular expression to match legal word-trailing characters which should be merged onto a link of th...
Definition: Language.php:3965
Language\ucfirst
ucfirst( $str)
Make a string's first character uppercase.
Definition: Language.php:2435
Language\$mMagicExtensions
$mMagicExtensions
Definition: Language.php:81
FakeConverter\convertTitle
convertTitle( $t)
Definition: Language.php:51
Language\getWeekdayName
getWeekdayName( $key)
Definition: Language.php:1018
Language\gender
gender( $gender, $forms)
Provides an alternative text depending on specified gender.
Definition: Language.php:3614
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:1042
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:3099
$type
$type
Definition: testCompression.php:46
Language\convertTitle
convertTitle( $title)
Convert a Title object to a string in the preferred variant.
Definition: Language.php:3806