MediaWiki  1.29.1
LanguageConverter.php
Go to the documentation of this file.
1 <?php
22 
32 class LanguageConverter {
38  static public $languagesWithVariants = [
39  'gan',
40  'iu',
41  'kk',
42  'ku',
43  'shi',
44  'sr',
45  'tg',
46  'uz',
47  'zh',
48  ];
49 
50  public $mMainLanguageCode;
51 
55  public $mVariants;
56  public $mVariantFallbacks;
57  public $mVariantNames;
58  public $mTablesLoaded = false;
59  public $mTables;
60  // 'bidirectional' 'unidirectional' 'disable' for each variant
61  public $mManualLevel;
62 
66  public $mCacheKey;
67 
68  public $mLangObj;
69  public $mFlags;
70  public $mDescCodeSep = ':', $mDescVarSep = ';';
71  public $mUcfirst = false;
72  public $mConvRuleTitle = false;
73  public $mURLVariant;
74  public $mUserVariant;
75  public $mHeaderVariant;
76  public $mMaxDepth = 10;
77  public $mVarSeparatorPattern;
78 
79  const CACHE_VERSION_KEY = 'VERSION 7';
80 
89  public function __construct( $langobj, $maincode, $variants = [],
90  $variantfallbacks = [], $flags = [],
91  $manualLevel = [] ) {
92  global $wgDisabledVariants;
93  $this->mLangObj = $langobj;
94  $this->mMainLanguageCode = $maincode;
95  $this->mVariants = array_diff( $variants, $wgDisabledVariants );
96  $this->mVariantFallbacks = $variantfallbacks;
97  $this->mVariantNames = Language::fetchLanguageNames();
98  $this->mCacheKey = wfMemcKey( 'conversiontables', $maincode );
99  $defaultflags = [
100  // 'S' show converted text
101  // '+' add rules for alltext
102  // 'E' the gave flags is error
103  // these flags above are reserved for program
104  'A' => 'A', // add rule for convert code (all text convert)
105  'T' => 'T', // title convert
106  'R' => 'R', // raw content
107  'D' => 'D', // convert description (subclass implement)
108  '-' => '-', // remove convert (not implement)
109  'H' => 'H', // add rule for convert code (but no display in placed code)
110  'N' => 'N', // current variant name
111  ];
112  $this->mFlags = array_merge( $defaultflags, $flags );
113  foreach ( $this->mVariants as $v ) {
114  if ( array_key_exists( $v, $manualLevel ) ) {
115  $this->mManualLevel[$v] = $manualLevel[$v];
116  } else {
117  $this->mManualLevel[$v] = 'bidirectional';
118  }
119  $this->mFlags[$v] = $v;
120  }
121  }
122 
129  public function getVariants() {
130  return $this->mVariants;
131  }
132 
144  public function getVariantFallbacks( $variant ) {
145  if ( isset( $this->mVariantFallbacks[$variant] ) ) {
146  return $this->mVariantFallbacks[$variant];
147  }
148  return $this->mMainLanguageCode;
149  }
150 
155  public function getConvRuleTitle() {
156  return $this->mConvRuleTitle;
157  }
158 
163  public function getPreferredVariant() {
164  global $wgDefaultLanguageVariant, $wgUser;
165 
166  $req = $this->getURLVariant();
167 
168  if ( $wgUser->isSafeToLoad() && $wgUser->isLoggedIn() && !$req ) {
169  $req = $this->getUserVariant();
170  } elseif ( !$req ) {
171  $req = $this->getHeaderVariant();
172  }
173 
174  if ( $wgDefaultLanguageVariant && !$req ) {
175  $req = $this->validateVariant( $wgDefaultLanguageVariant );
176  }
177 
178  // This function, unlike the other get*Variant functions, is
179  // not memoized (i.e. there return value is not cached) since
180  // new information might appear during processing after this
181  // is first called.
182  if ( $this->validateVariant( $req ) ) {
183  return $req;
184  }
185  return $this->mMainLanguageCode;
186  }
187 
193  public function getDefaultVariant() {
194  global $wgDefaultLanguageVariant;
195 
196  $req = $this->getURLVariant();
197 
198  if ( !$req ) {
199  $req = $this->getHeaderVariant();
200  }
201 
202  if ( $wgDefaultLanguageVariant && !$req ) {
203  $req = $this->validateVariant( $wgDefaultLanguageVariant );
204  }
205 
206  if ( $req ) {
207  return $req;
208  }
209  return $this->mMainLanguageCode;
210  }
211 
217  public function validateVariant( $variant = null ) {
218  if ( $variant !== null && in_array( $variant, $this->mVariants ) ) {
219  return $variant;
220  }
221  return null;
222  }
223 
229  public function getURLVariant() {
231 
232  if ( $this->mURLVariant ) {
233  return $this->mURLVariant;
234  }
235 
236  // see if the preference is set in the request
237  $ret = $wgRequest->getText( 'variant' );
238 
239  if ( !$ret ) {
240  $ret = $wgRequest->getVal( 'uselang' );
241  }
242 
243  $this->mURLVariant = $this->validateVariant( $ret );
244  return $this->mURLVariant;
245  }
246 
252  protected function getUserVariant() {
254 
255  // memoizing this function wreaks havoc on parserTest.php
256  /*
257  if ( $this->mUserVariant ) {
258  return $this->mUserVariant;
259  }
260  */
261 
262  // Get language variant preference from logged in users
263  // Don't call this on stub objects because that causes infinite
264  // recursion during initialisation
265  if ( !$wgUser->isSafeToLoad() ) {
266  return false;
267  }
268  if ( $wgUser->isLoggedIn() ) {
269  if ( $this->mMainLanguageCode == $wgContLang->getCode() ) {
270  $ret = $wgUser->getOption( 'variant' );
271  } else {
272  $ret = $wgUser->getOption( 'variant-' . $this->mMainLanguageCode );
273  }
274  } else {
275  // figure out user lang without constructing wgLang to avoid
276  // infinite recursion
277  $ret = $wgUser->getOption( 'language' );
278  }
279 
280  $this->mUserVariant = $this->validateVariant( $ret );
281  return $this->mUserVariant;
282  }
283 
289  protected function getHeaderVariant() {
291 
292  if ( $this->mHeaderVariant ) {
293  return $this->mHeaderVariant;
294  }
295 
296  // see if some supported language variant is set in the
297  // HTTP header.
298  $languages = array_keys( $wgRequest->getAcceptLang() );
299  if ( empty( $languages ) ) {
300  return null;
301  }
302 
303  $fallbackLanguages = [];
304  foreach ( $languages as $language ) {
305  $this->mHeaderVariant = $this->validateVariant( $language );
306  if ( $this->mHeaderVariant ) {
307  break;
308  }
309 
310  // To see if there are fallbacks of current language.
311  // We record these fallback variants, and process
312  // them later.
313  $fallbacks = $this->getVariantFallbacks( $language );
314  if ( is_string( $fallbacks ) && $fallbacks !== $this->mMainLanguageCode ) {
315  $fallbackLanguages[] = $fallbacks;
316  } elseif ( is_array( $fallbacks ) ) {
317  $fallbackLanguages =
318  array_merge( $fallbackLanguages, $fallbacks );
319  }
320  }
321 
322  if ( !$this->mHeaderVariant ) {
323  // process fallback languages now
324  $fallback_languages = array_unique( $fallbackLanguages );
325  foreach ( $fallback_languages as $language ) {
326  $this->mHeaderVariant = $this->validateVariant( $language );
327  if ( $this->mHeaderVariant ) {
328  break;
329  }
330  }
331  }
332 
333  return $this->mHeaderVariant;
334  }
335 
346  public function autoConvert( $text, $toVariant = false ) {
347 
348  $this->loadTables();
349 
350  if ( !$toVariant ) {
351  $toVariant = $this->getPreferredVariant();
352  if ( !$toVariant ) {
353  return $text;
354  }
355  }
356 
357  if ( $this->guessVariant( $text, $toVariant ) ) {
358  return $text;
359  }
360 
361  /* we convert everything except:
362  * 1. HTML markups (anything between < and >)
363  * 2. HTML entities
364  * 3. placeholders created by the parser
365  */
366  $marker = '|' . Parser::MARKER_PREFIX . '[\-a-zA-Z0-9]+';
367 
368  // this one is needed when the text is inside an HTML markup
369  $htmlfix = '|<[^>]+$|^[^<>]*>';
370 
371  // disable convert to variants between <code> tags
372  $codefix = '<code>.+?<\/code>|';
373  // disable conversion of <script> tags
374  $scriptfix = '<script.*?>.*?<\/script>|';
375  // disable conversion of <pre> tags
376  $prefix = '<pre.*?>.*?<\/pre>|';
377 
378  $reg = '/' . $codefix . $scriptfix . $prefix .
379  '<[^>]+>|&[a-zA-Z#][a-z0-9]+;' . $marker . $htmlfix . '/s';
380  $startPos = 0;
381  $sourceBlob = '';
382  $literalBlob = '';
383 
384  // Guard against delimiter nulls in the input
385  // (should never happen: see T159174)
386  $text = str_replace( "\000", '', $text );
387 
388  $markupMatches = null;
389  $elementMatches = null;
390  while ( $startPos < strlen( $text ) ) {
391  if ( preg_match( $reg, $text, $markupMatches, PREG_OFFSET_CAPTURE, $startPos ) ) {
392  $elementPos = $markupMatches[0][1];
393  $element = $markupMatches[0][0];
394  } else {
395  $elementPos = strlen( $text );
396  $element = '';
397  }
398 
399  // Queue the part before the markup for translation in a batch
400  $sourceBlob .= substr( $text, $startPos, $elementPos - $startPos ) . "\000";
401 
402  // Advance to the next position
403  $startPos = $elementPos + strlen( $element );
404 
405  // Translate any alt or title attributes inside the matched element
406  if ( $element !== ''
407  && preg_match( '/^(<[^>\s]*)\s([^>]*)(.*)$/', $element, $elementMatches )
408  ) {
409  $attrs = Sanitizer::decodeTagAttributes( $elementMatches[2] );
410  $changed = false;
411  foreach ( [ 'title', 'alt' ] as $attrName ) {
412  if ( !isset( $attrs[$attrName] ) ) {
413  continue;
414  }
415  $attr = $attrs[$attrName];
416  // Don't convert URLs
417  if ( !strpos( $attr, '://' ) ) {
418  $attr = $this->recursiveConvertTopLevel( $attr, $toVariant );
419  }
420 
421  if ( $attr !== $attrs[$attrName] ) {
422  $attrs[$attrName] = $attr;
423  $changed = true;
424  }
425  }
426  if ( $changed ) {
427  $element = $elementMatches[1] . Html::expandAttributes( $attrs ) .
428  $elementMatches[3];
429  }
430  }
431  $literalBlob .= $element . "\000";
432  }
433 
434  // Do the main translation batch
435  $translatedBlob = $this->translate( $sourceBlob, $toVariant );
436 
437  // Put the output back together
438  $translatedIter = StringUtils::explode( "\000", $translatedBlob );
439  $literalIter = StringUtils::explode( "\000", $literalBlob );
440  $output = '';
441  while ( $translatedIter->valid() && $literalIter->valid() ) {
442  $output .= $translatedIter->current();
443  $output .= $literalIter->current();
444  $translatedIter->next();
445  $literalIter->next();
446  }
447 
448  return $output;
449  }
450 
460  public function translate( $text, $variant ) {
461  // If $text is empty or only includes spaces, do nothing
462  // Otherwise translate it
463  if ( trim( $text ) ) {
464  $this->loadTables();
465  $text = $this->mTables[$variant]->replace( $text );
466  }
467  return $text;
468  }
469 
476  public function autoConvertToAllVariants( $text ) {
477  $this->loadTables();
478 
479  $ret = [];
480  foreach ( $this->mVariants as $variant ) {
481  $ret[$variant] = $this->translate( $text, $variant );
482  }
483 
484  return $ret;
485  }
486 
492  protected function applyManualConv( $convRule ) {
493  // Use syntax -{T|zh-cn:TitleCN; zh-tw:TitleTw}- to custom
494  // title conversion.
495  // T26072: $mConvRuleTitle was overwritten by other manual
496  // rule(s) not for title, this breaks the title conversion.
497  $newConvRuleTitle = $convRule->getTitle();
498  if ( $newConvRuleTitle ) {
499  // So I add an empty check for getTitle()
500  $this->mConvRuleTitle = $newConvRuleTitle;
501  }
502 
503  // merge/remove manual conversion rules to/from global table
504  $convTable = $convRule->getConvTable();
505  $action = $convRule->getRulesAction();
506  foreach ( $convTable as $variant => $pair ) {
507  if ( !$this->validateVariant( $variant ) ) {
508  continue;
509  }
510 
511  if ( $action == 'add' ) {
512  // More efficient than array_merge(), about 2.5 times.
513  foreach ( $pair as $from => $to ) {
514  $this->mTables[$variant]->setPair( $from, $to );
515  }
516  } elseif ( $action == 'remove' ) {
517  $this->mTables[$variant]->removeArray( $pair );
518  }
519  }
520  }
521 
529  public function convertTitle( $title ) {
530  $variant = $this->getPreferredVariant();
531  $index = $title->getNamespace();
532  if ( $index !== NS_MAIN ) {
533  $text = $this->convertNamespace( $index, $variant ) . ':';
534  } else {
535  $text = '';
536  }
537  $text .= $this->translate( $title->getText(), $variant );
538  return $text;
539  }
540 
548  public function convertNamespace( $index, $variant = null ) {
549  if ( $index === NS_MAIN ) {
550  return '';
551  }
552 
553  if ( $variant === null ) {
554  $variant = $this->getPreferredVariant();
555  }
556 
557  $cache = MediaWikiServices::getInstance()->getLocalServerObjectCache();
558  $key = $cache->makeKey( 'languageconverter', 'namespace-text', $index, $variant );
559  $nsVariantText = $cache->get( $key );
560  if ( $nsVariantText !== false ) {
561  return $nsVariantText;
562  }
563 
564  // First check if a message gives a converted name in the target variant.
565  $nsConvMsg = wfMessage( 'conversion-ns' . $index )->inLanguage( $variant );
566  if ( $nsConvMsg->exists() ) {
567  $nsVariantText = $nsConvMsg->plain();
568  }
569 
570  // Then check if a message gives a converted name in content language
571  // which needs extra translation to the target variant.
572  if ( $nsVariantText === false ) {
573  $nsConvMsg = wfMessage( 'conversion-ns' . $index )->inContentLanguage();
574  if ( $nsConvMsg->exists() ) {
575  $nsVariantText = $this->translate( $nsConvMsg->plain(), $variant );
576  }
577  }
578 
579  if ( $nsVariantText === false ) {
580  // No message exists, retrieve it from the target variant's namespace names.
581  $langObj = $this->mLangObj->factory( $variant );
582  $nsVariantText = $langObj->getFormattedNsText( $index );
583  }
584 
585  $cache->set( $key, $nsVariantText, 60 );
586 
587  return $nsVariantText;
588  }
589 
604  public function convert( $text ) {
605  $variant = $this->getPreferredVariant();
606  return $this->convertTo( $text, $variant );
607  }
608 
616  public function convertTo( $text, $variant ) {
617  global $wgDisableLangConversion;
618  if ( $wgDisableLangConversion ) {
619  return $text;
620  }
621  // Reset converter state for a new converter run.
622  $this->mConvRuleTitle = false;
623  return $this->recursiveConvertTopLevel( $text, $variant );
624  }
625 
635  protected function recursiveConvertTopLevel( $text, $variant, $depth = 0 ) {
636  $startPos = 0;
637  $out = '';
638  $length = strlen( $text );
639  $shouldConvert = !$this->guessVariant( $text, $variant );
640 
641  while ( $startPos < $length ) {
642  $pos = strpos( $text, '-{', $startPos );
643 
644  if ( $pos === false ) {
645  // No more markup, append final segment
646  $fragment = substr( $text, $startPos );
647  $out .= $shouldConvert ? $this->autoConvert( $fragment, $variant ) : $fragment;
648  return $out;
649  }
650 
651  // Markup found
652  // Append initial segment
653  $fragment = substr( $text, $startPos, $pos - $startPos );
654  $out .= $shouldConvert ? $this->autoConvert( $fragment, $variant ) : $fragment;
655 
656  // Advance position
657  $startPos = $pos;
658 
659  // Do recursive conversion
660  $out .= $this->recursiveConvertRule( $text, $variant, $startPos, $depth + 1 );
661  }
662 
663  return $out;
664  }
665 
677  protected function recursiveConvertRule( $text, $variant, &$startPos, $depth = 0 ) {
678  // Quick sanity check (no function calls)
679  if ( $text[$startPos] !== '-' || $text[$startPos + 1] !== '{' ) {
680  throw new MWException( __METHOD__ . ': invalid input string' );
681  }
682 
683  $startPos += 2;
684  $inner = '';
685  $warningDone = false;
686  $length = strlen( $text );
687 
688  while ( $startPos < $length ) {
689  $m = false;
690  preg_match( '/-\{|\}-/', $text, $m, PREG_OFFSET_CAPTURE, $startPos );
691  if ( !$m ) {
692  // Unclosed rule
693  break;
694  }
695 
696  $token = $m[0][0];
697  $pos = $m[0][1];
698 
699  // Markup found
700  // Append initial segment
701  $inner .= substr( $text, $startPos, $pos - $startPos );
702 
703  // Advance position
704  $startPos = $pos;
705 
706  switch ( $token ) {
707  case '-{':
708  // Check max depth
709  if ( $depth >= $this->mMaxDepth ) {
710  $inner .= '-{';
711  if ( !$warningDone ) {
712  $inner .= '<span class="error">' .
713  wfMessage( 'language-converter-depth-warning' )
714  ->numParams( $this->mMaxDepth )->inContentLanguage()->text() .
715  '</span>';
716  $warningDone = true;
717  }
718  $startPos += 2;
719  continue;
720  }
721  // Recursively parse another rule
722  $inner .= $this->recursiveConvertRule( $text, $variant, $startPos, $depth + 1 );
723  break;
724  case '}-':
725  // Apply the rule
726  $startPos += 2;
727  $rule = new ConverterRule( $inner, $this );
728  $rule->parse( $variant );
729  $this->applyManualConv( $rule );
730  return $rule->getDisplay();
731  default:
732  throw new MWException( __METHOD__ . ': invalid regex match' );
733  }
734  }
735 
736  // Unclosed rule
737  if ( $startPos < $length ) {
738  $inner .= substr( $text, $startPos );
739  }
740  $startPos = $length;
741  return '-{' . $this->autoConvert( $inner, $variant );
742  }
743 
755  public function findVariantLink( &$link, &$nt, $ignoreOtherCond = false ) {
756  # If the article has already existed, there is no need to
757  # check it again, otherwise it may cause a fault.
758  if ( is_object( $nt ) && $nt->exists() ) {
759  return;
760  }
761 
762  global $wgDisableLangConversion, $wgDisableTitleConversion, $wgRequest;
763  $isredir = $wgRequest->getText( 'redirect', 'yes' );
764  $action = $wgRequest->getText( 'action' );
765  if ( $action == 'edit' && $wgRequest->getBool( 'redlink' ) ) {
766  $action = 'view';
767  }
768  $linkconvert = $wgRequest->getText( 'linkconvert', 'yes' );
769  $disableLinkConversion = $wgDisableLangConversion
770  || $wgDisableTitleConversion;
771  $linkBatch = new LinkBatch();
772 
773  $ns = NS_MAIN;
774 
775  if ( $disableLinkConversion ||
776  ( !$ignoreOtherCond &&
777  ( $isredir == 'no'
778  || $action == 'edit'
779  || $action == 'submit'
780  || $linkconvert == 'no' ) ) ) {
781  return;
782  }
783 
784  if ( is_object( $nt ) ) {
785  $ns = $nt->getNamespace();
786  }
787 
788  $variants = $this->autoConvertToAllVariants( $link );
789  if ( !$variants ) { // give up
790  return;
791  }
792 
793  $titles = [];
794 
795  foreach ( $variants as $v ) {
796  if ( $v != $link ) {
797  $varnt = Title::newFromText( $v, $ns );
798  if ( !is_null( $varnt ) ) {
799  $linkBatch->addObj( $varnt );
800  $titles[] = $varnt;
801  }
802  }
803  }
804 
805  // fetch all variants in single query
806  $linkBatch->execute();
807 
808  foreach ( $titles as $varnt ) {
809  if ( $varnt->getArticleID() > 0 ) {
810  $nt = $varnt;
811  $link = $varnt->getText();
812  break;
813  }
814  }
815  }
816 
822  public function getExtraHashOptions() {
823  $variant = $this->getPreferredVariant();
824 
825  return '!' . $variant;
826  }
827 
838  public function guessVariant( $text, $variant ) {
839  return false;
840  }
841 
849  function loadDefaultTables() {
850  $class = static::class;
851  throw new MWException( "Must implement loadDefaultTables() method in class $class" );
852  }
853 
859  function loadTables( $fromCache = true ) {
861 
862  if ( $this->mTablesLoaded ) {
863  return;
864  }
865 
866  $this->mTablesLoaded = true;
867  $this->mTables = false;
869  if ( $fromCache ) {
870  $this->mTables = $cache->get( $this->mCacheKey );
871  }
872  if ( !$this->mTables || !array_key_exists( self::CACHE_VERSION_KEY, $this->mTables ) ) {
873  // not in cache, or we need a fresh reload.
874  // We will first load the default tables
875  // then update them using things in MediaWiki:Conversiontable/*
876  $this->loadDefaultTables();
877  foreach ( $this->mVariants as $var ) {
878  $cached = $this->parseCachedTable( $var );
879  $this->mTables[$var]->mergeArray( $cached );
880  }
881 
882  $this->postLoadTables();
883  $this->mTables[self::CACHE_VERSION_KEY] = true;
884 
885  $cache->set( $this->mCacheKey, $this->mTables, 43200 );
886  }
887  }
888 
892  function postLoadTables() {
893  }
894 
900  function reloadTables() {
901  if ( $this->mTables ) {
902  unset( $this->mTables );
903  }
904 
905  $this->mTablesLoaded = false;
906  $this->loadTables( false );
907  }
908 
928  function parseCachedTable( $code, $subpage = '', $recursive = true ) {
929  static $parsed = [];
930 
931  $key = 'Conversiontable/' . $code;
932  if ( $subpage ) {
933  $key .= '/' . $subpage;
934  }
935  if ( array_key_exists( $key, $parsed ) ) {
936  return [];
937  }
938 
939  $parsed[$key] = true;
940 
941  if ( $subpage === '' ) {
942  $txt = MessageCache::singleton()->getMsgFromNamespace( $key, $code );
943  } else {
944  $txt = false;
946  if ( $title && $title->exists() ) {
947  $revision = Revision::newFromTitle( $title );
948  if ( $revision ) {
949  if ( $revision->getContentModel() == CONTENT_MODEL_WIKITEXT ) {
950  $txt = $revision->getContent( Revision::RAW )->getNativeData();
951  }
952 
953  // @todo in the future, use a specialized content model, perhaps based on json!
954  }
955  }
956  }
957 
958  # Nothing to parse if there's no text
959  if ( $txt === false || $txt === null || $txt === '' ) {
960  return [];
961  }
962 
963  // get all subpage links of the form
964  // [[MediaWiki:Conversiontable/zh-xx/...|...]]
965  $linkhead = $this->mLangObj->getNsText( NS_MEDIAWIKI ) .
966  ':Conversiontable';
967  $subs = StringUtils::explode( '[[', $txt );
968  $sublinks = [];
969  foreach ( $subs as $sub ) {
970  $link = explode( ']]', $sub, 2 );
971  if ( count( $link ) != 2 ) {
972  continue;
973  }
974  $b = explode( '|', $link[0], 2 );
975  $b = explode( '/', trim( $b[0] ), 3 );
976  if ( count( $b ) == 3 ) {
977  $sublink = $b[2];
978  } else {
979  $sublink = '';
980  }
981 
982  if ( $b[0] == $linkhead && $b[1] == $code ) {
983  $sublinks[] = $sublink;
984  }
985  }
986 
987  // parse the mappings in this page
988  $blocks = StringUtils::explode( '-{', $txt );
989  $ret = [];
990  $first = true;
991  foreach ( $blocks as $block ) {
992  if ( $first ) {
993  // Skip the part before the first -{
994  $first = false;
995  continue;
996  }
997  $mappings = explode( '}-', $block, 2 )[0];
998  $stripped = str_replace( [ "'", '"', '*', '#' ], '', $mappings );
999  $table = StringUtils::explode( ';', $stripped );
1000  foreach ( $table as $t ) {
1001  $m = explode( '=>', $t, 3 );
1002  if ( count( $m ) != 2 ) {
1003  continue;
1004  }
1005  // trim any trailling comments starting with '//'
1006  $tt = explode( '//', $m[1], 2 );
1007  $ret[trim( $m[0] )] = trim( $tt[0] );
1008  }
1009  }
1010 
1011  // recursively parse the subpages
1012  if ( $recursive ) {
1013  foreach ( $sublinks as $link ) {
1014  $s = $this->parseCachedTable( $code, $link, $recursive );
1015  $ret = $s + $ret;
1016  }
1017  }
1018 
1019  if ( $this->mUcfirst ) {
1020  foreach ( $ret as $k => $v ) {
1021  $ret[$this->mLangObj->ucfirst( $k )] = $this->mLangObj->ucfirst( $v );
1022  }
1023  }
1024  return $ret;
1025  }
1026 
1035  public function markNoConversion( $text, $noParse = false ) {
1036  # don't mark if already marked
1037  if ( strpos( $text, '-{' ) || strpos( $text, '}-' ) ) {
1038  return $text;
1039  }
1040 
1041  $ret = "-{R|$text}-";
1042  return $ret;
1043  }
1044 
1053  function convertCategoryKey( $key ) {
1054  return $key;
1055  }
1056 
1063  public function updateConversionTable( Title $titleobj ) {
1064  if ( $titleobj->getNamespace() == NS_MEDIAWIKI ) {
1065  $title = $titleobj->getDBkey();
1066  $t = explode( '/', $title, 3 );
1067  $c = count( $t );
1068  if ( $c > 1 && $t[0] == 'Conversiontable' ) {
1069  if ( $this->validateVariant( $t[1] ) ) {
1070  $this->reloadTables();
1071  }
1072  }
1073  }
1074  }
1075 
1080  function getVarSeparatorPattern() {
1081  if ( is_null( $this->mVarSeparatorPattern ) ) {
1082  // varsep_pattern for preg_split:
1083  // text should be splited by ";" only if a valid variant
1084  // name exist after the markup, for example:
1085  // -{zh-hans:<span style="font-size:120%;">xxx</span>;zh-hant:\
1086  // <span style="font-size:120%;">yyy</span>;}-
1087  // we should split it as:
1088  // [
1089  // [0] => 'zh-hans:<span style="font-size:120%;">xxx</span>'
1090  // [1] => 'zh-hant:<span style="font-size:120%;">yyy</span>'
1091  // [2] => ''
1092  // ]
1093  $pat = '/;\s*(?=';
1094  foreach ( $this->mVariants as $variant ) {
1095  // zh-hans:xxx;zh-hant:yyy
1096  $pat .= $variant . '\s*:|';
1097  // xxx=>zh-hans:yyy; xxx=>zh-hant:zzz
1098  $pat .= '[^;]*?=>\s*' . $variant . '\s*:|';
1099  }
1100  $pat .= '\s*$)/';
1101  $this->mVarSeparatorPattern = $pat;
1102  }
1103  return $this->mVarSeparatorPattern;
1104  }
1105 }
ConverterRule
Parser for rules of language conversion , parse rules in -{ }- tag.
Definition: ConverterRule.php:27
$wgUser
$wgUser
Definition: Setup.php:781
Title\newFromText
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:265
LinkBatch
Class representing a list of titles The execute() method checks them all for existence and adds them ...
Definition: LinkBatch.php:34
captcha-old.count
count
Definition: captcha-old.py:225
$languages
switch( $options['output']) $languages
Definition: transstat.php:76
use
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
Definition: MIT-LICENSE.txt:10
$req
this hook is for auditing only $req
Definition: hooks.txt:990
Html\expandAttributes
static expandAttributes(array $attribs)
Given an associative array of element attributes, generate a string to stick after the element name i...
Definition: Html.php:474
$s
$s
Definition: mergeMessageFileList.php:188
CONTENT_MODEL_WIKITEXT
const CONTENT_MODEL_WIKITEXT
Definition: Defines.php:233
php
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
NS_MAIN
const NS_MAIN
Definition: Defines.php:62
Revision\newFromTitle
static newFromTitle(LinkTarget $linkTarget, $id=0, $flags=0)
Load either the current, or a specified, revision that's attached to a given link target.
Definition: Revision.php:134
Title\getDBkey
getDBkey()
Get the main part with underscores.
Definition: Title.php:901
MWException
MediaWiki exception.
Definition: MWException.php:26
wfMemcKey
wfMemcKey()
Make a cache key for the local wiki.
Definition: GlobalFunctions.php:2961
$title
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:934
$titles
linkcache txt The LinkCache class maintains a list of article titles and the information about whether or not the article exists in the database This is used to mark up links when displaying a page If the same link appears more than once on any page then it only has to be looked up once In most cases link lookups are done in batches with the LinkBatch class or the equivalent in so the link cache is mostly useful for short snippets of parsed and for links in the navigation areas of the skin The link cache was formerly used to track links used in a document for the purposes of updating the link tables This application is now deprecated To create a you can use the following $titles
Definition: linkcache.txt:17
Language\fetchLanguageNames
static fetchLanguageNames( $inLanguage=null, $include='mw')
Get an array of language names, indexed by code.
Definition: Language.php:803
Title\getNamespace
getNamespace()
Get the namespace index, i.e.
Definition: Title.php:924
$wgLanguageConverterCacheType
$wgLanguageConverterCacheType
The cache type for storing language conversion tables, which are used when parsing certain text and i...
Definition: DefaultSettings.php:2257
StringUtils\explode
static explode( $separator, $subject)
Workalike for explode() with limited memory usage.
Definition: StringUtils.php:335
ObjectCache\getInstance
static getInstance( $id)
Get a cached instance of the specified type of cache object.
Definition: ObjectCache.php:92
$output
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context the output can only depend on parameters provided to this hook not on global state indicating whether full HTML should be generated If generation of HTML may be but other information should still be present in the ParserOutput object & $output
Definition: hooks.txt:1049
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
MessageCache\singleton
static singleton()
Get the signleton instance of this class.
Definition: MessageCache.php:113
Title\makeTitleSafe
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:538
$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:1956
Revision\RAW
const RAW
Definition: Revision.php:100
Title
Represents a title within MediaWiki.
Definition: Title.php:39
$cache
$cache
Definition: mcc.php:33
Makefile.translate
def translate(text, conv_table)
Definition: Makefile.py:235
$code
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output modifiable & $code
Definition: hooks.txt:783
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
$link
usually copyright or history_copyright This message must be in HTML not wikitext & $link
Definition: hooks.txt:2929
wfMessage
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation 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
NS_MEDIAWIKI
const NS_MEDIAWIKI
Definition: Defines.php:70
class
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:52
$t
$t
Definition: testCompression.php:67
$wgRequest
if(! $wgDBerrorLogTZ) $wgRequest
Definition: Setup.php:639
MediaWikiServices
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency MediaWikiServices
Definition: injection.txt:23
$flags
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition: hooks.txt:2749
array
the array() calling protocol came about after MediaWiki 1.4rc1.
$wgContLang
this class mediates it Skin Encapsulates a look and feel for the wiki All of the functions that render HTML and make choices about how to render it are here and are called from various other places when and is meant to be subclassed with other skins that may override some of its functions The User object contains a reference to a and so rather than having a global skin object we just rely on the global User and get the skin with $wgUser and also has some character encoding functions and other locale stuff The current user interface language is instantiated as and the content language as $wgContLang
Definition: design.txt:56
$out
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output $out
Definition: hooks.txt:783