104 $variantfallbacks = [],
122 $this->mLangObj = $langobj;
123 $this->mMainLanguageCode = $maincode;
125 $this->mVariantFallbacks = $variantfallbacks;
126 $this->mVariantNames = MediaWikiServices::getInstance()
127 ->getLanguageNameUtils()
128 ->getLanguageNames();
142 $this->mFlags = array_merge( $defaultflags, $flags );
143 foreach ( $this->mVariants as $v ) {
144 if ( array_key_exists( $v, $manualLevel ) ) {
145 $this->mManualLevel[$v] = $manualLevel[$v];
147 $this->mManualLevel[$v] =
'bidirectional';
149 $this->mFlags[$v] = $v;
160 return $this->mVariants;
175 return $this->mVariantFallbacks[$variant] ?? $this->mMainLanguageCode;
183 return $this->mConvRuleTitle;
195 Hooks::runner()->onGetLangPreferredVariant( $req );
200 if ( $wgUser && $wgUser->isSafeToLoad() && $wgUser->isLoggedIn() && !$req ) {
219 return $this->mMainLanguageCode;
243 return $this->mMainLanguageCode;
256 if ( $variant ===
null ) {
261 $variant = LanguageCode::replaceDeprecatedCodes( strtolower( $variant ) );
262 if ( in_array( $variant, $this->mVariants ) ) {
269 foreach ( $this->mVariants as $v ) {
271 if ( strtolower( LanguageCode::bcp47( $v ) ) === $variant ) {
286 if ( $this->mURLVariant ) {
287 return $this->mURLVariant;
298 return $this->mURLVariant;
317 $this->mMainLanguageCode ==
318 MediaWikiServices::getInstance()->getContentLanguage()->getCode()
322 $ret = $user->
getOption(
'variant-' . $this->mMainLanguageCode );
331 return $this->mUserVariant;
342 if ( $this->mHeaderVariant ) {
343 return $this->mHeaderVariant;
348 $languages = array_keys(
$wgRequest->getAcceptLang() );
349 if ( empty( $languages ) ) {
353 $fallbackLanguages = [];
354 foreach ( $languages as $language ) {
356 if ( $this->mHeaderVariant ) {
364 if ( is_string( $fallbacks ) && $fallbacks !== $this->mMainLanguageCode ) {
365 $fallbackLanguages[] = $fallbacks;
366 } elseif ( is_array( $fallbacks ) ) {
368 array_merge( $fallbackLanguages, $fallbacks );
372 if ( !$this->mHeaderVariant ) {
374 $fallback_languages = array_unique( $fallbackLanguages );
375 foreach ( $fallback_languages as $language ) {
377 if ( $this->mHeaderVariant ) {
383 return $this->mHeaderVariant;
417 if ( $reg ===
null ) {
418 $marker =
'|' . Parser::MARKER_PREFIX .
'[^\x7f]++\x7f';
421 $htmlfix =
'|<[^>\004]++(?=\004$)|^[^<>]*+>';
428 $codefix =
'<code>[^<]*+(?:(?:(?!<\/code>).)[^<]*+)*+<\/code>|';
430 $scriptfix =
'<script[^>]*+>[^<]*+(?:(?:(?!<\/script>).)[^<]*+)*+<\/script>|';
432 $prefix =
'<pre[^>]*+>[^<]*+(?:(?:(?!<\/pre>).)[^<]*+)*+<\/pre>|';
435 $htmlFullTag =
'<(?:[^>=]*+(?>[^>=]*+=\s*+(?:"[^"]*"|\'[^\']*\'|[^\'">\s]*+))*+[^>=]*+>|.*+)|';
437 $reg =
'/' . $codefix . $scriptfix . $prefix . $htmlFullTag .
438 '&[a-zA-Z#][a-z0-9]++;' . $marker . $htmlfix .
'|\004$/s';
446 $text = str_replace(
"\000",
'', $text );
447 $text = str_replace(
"\004",
'', $text );
449 $markupMatches =
null;
450 $elementMatches =
null;
454 $textWithMarker = $text .
"\004";
455 while ( $startPos < strlen( $text ) ) {
456 if ( preg_match( $reg, $textWithMarker, $markupMatches, PREG_OFFSET_CAPTURE, $startPos ) ) {
457 $elementPos = $markupMatches[0][1];
458 $element = $markupMatches[0][0];
459 if ( $element ===
"\004" ) {
461 $elementPos = strlen( $text );
463 } elseif ( substr( $element, -1 ) ===
"\004" ) {
469 $element = substr( $element, 0, -1 );
476 $log = LoggerFactory::getInstance(
'languageconverter' );
477 $log->error(
"Hit pcre.backtrack_limit in " . __METHOD__
478 .
". Disabling language conversion for this page.",
480 "method" => __METHOD__,
481 "variant" => $toVariant,
482 "startOfText" => substr( $text, 0, 500 )
488 $sourceBlob .= substr( $text, $startPos, $elementPos - $startPos ) .
"\000";
491 $startPos = $elementPos + strlen( $element );
495 && preg_match(
'/^(<[^>\s]*+)\s([^>]*+)(.*+)$/', $element, $elementMatches )
502 $attrs = Sanitizer::decodeTagAttributes( $elementMatches[2] );
504 $close = substr( $elementMatches[2], -1 ) ===
'/' ?
' /' :
'';
506 foreach ( [
'title',
'alt' ] as $attrName ) {
507 if ( !isset( $attrs[$attrName] ) ) {
510 $attr = $attrs[$attrName];
512 if ( !strpos( $attr,
'://' ) ) {
516 if ( $attr !== $attrs[$attrName] ) {
517 $attrs[$attrName] = $attr;
522 $element = $elementMatches[1] . Html::expandAttributes( $attrs ) .
523 $close . $elementMatches[3];
526 $literalBlob .= $element .
"\000";
530 $translatedBlob = $this->
translate( $sourceBlob, $toVariant );
536 while ( $translatedIter->valid() && $literalIter->valid() ) {
537 $output .= $translatedIter->current();
538 $output .= $literalIter->current();
539 $translatedIter->next();
540 $literalIter->next();
558 if ( trim( $text ) ) {
560 $text = $this->mTables[$variant]->replace( $text );
575 foreach ( $this->mVariants as $variant ) {
576 $ret[$variant] = $this->
translate( $text, $variant );
592 $newConvRuleTitle = $convRule->
getTitle();
593 if ( $newConvRuleTitle ) {
595 $this->mConvRuleTitle = $newConvRuleTitle;
601 foreach ( $convTable as $variant => $pair ) {
607 if ( $action ==
'add' ) {
609 foreach ( $pair as $from => $to ) {
610 $this->mTables[$v]->setPair( $from, $to );
612 } elseif ( $action ==
'remove' ) {
613 $this->mTables[$v]->removeArray( $pair );
650 if ( $variant ===
null ) {
654 $cache = MediaWikiServices::getInstance()->getLocalServerObjectCache();
655 $key =
$cache->makeKey(
'languageconverter',
'namespace-text', $index, $variant );
656 $nsVariantText =
$cache->get( $key );
657 if ( $nsVariantText !==
false ) {
658 return $nsVariantText;
662 $nsConvMsg =
wfMessage(
'conversion-ns' . $index )->inLanguage( $variant );
663 if ( $nsConvMsg->exists() ) {
664 $nsVariantText = $nsConvMsg->plain();
669 if ( $nsVariantText ===
false ) {
670 $nsConvMsg =
wfMessage(
'conversion-ns' . $index )->inContentLanguage();
671 if ( $nsConvMsg->exists() ) {
672 $nsVariantText = $this->
translate( $nsConvMsg->plain(), $variant );
676 if ( $nsVariantText ===
false ) {
678 $mLangObj = MediaWikiServices::getInstance()
679 ->getLanguageFactory()
680 ->getLanguage( $variant );
684 $cache->set( $key, $nsVariantText, 60 );
686 return $nsVariantText;
709 return $this->
convertTo( $text, $variant );
727 $this->mConvRuleTitle =
false;
743 $length = strlen( $text );
744 $shouldConvert = !$this->
guessVariant( $text, $variant );
747 $noScript =
'<script.*?>.*?<\/script>(*SKIP)(*FAIL)';
748 $noStyle =
'<style.*?>.*?<\/style>(*SKIP)(*FAIL)';
750 $noHtml =
'<(?:[^>=]*+(?>[^>=]*+=\s*+(?:"[^"]*"|\'[^\']*\'|[^\'">\s]*+))*+[^>=]*+>|.*+)(*SKIP)(*FAIL)';
751 while ( $startPos < $length && $continue ) {
752 $continue = preg_match(
754 "/$noScript|$noStyle|$noHtml|-\{/",
763 $fragment = substr( $text, $startPos );
764 $out .= $shouldConvert ? $this->
autoConvert( $fragment, $variant ) : $fragment;
772 $fragment = substr( $text, $startPos, $pos - $startPos );
773 $out .= $shouldConvert ? $this->
autoConvert( $fragment, $variant ) : $fragment;
797 if ( $text[$startPos] !==
'-' || $text[$startPos + 1] !==
'{' ) {
798 throw new MWException( __METHOD__ .
': invalid input string' );
803 $warningDone =
false;
804 $length = strlen( $text );
806 while ( $startPos < $length ) {
808 preg_match(
'/-\{|\}-/', $text, $m, PREG_OFFSET_CAPTURE, $startPos );
819 $inner .= substr( $text, $startPos, $pos - $startPos );
827 if ( $depth >= $this->mMaxDepth ) {
829 if ( !$warningDone ) {
830 $inner .=
'<span class="error">' .
831 wfMessage(
'language-converter-depth-warning' )
832 ->numParams( $this->mMaxDepth )->inContentLanguage()->text() .
846 $rule->parse( $variant );
848 return $rule->getDisplay();
850 throw new MWException( __METHOD__ .
': invalid regex match' );
855 if ( $startPos < $length ) {
856 $inner .= substr( $text, $startPos );
859 return '-{' . $this->
autoConvert( $inner, $variant );
874 # If the article has already existed, there is no need to
875 # check it again, otherwise it may cause a fault.
876 if ( is_object( $nt ) && $nt->exists() ) {
881 $isredir =
$wgRequest->getText(
'redirect',
'yes' );
883 if ( $action ==
'edit' &&
$wgRequest->getBool(
'redlink' ) ) {
886 $linkconvert =
$wgRequest->getText(
'linkconvert',
'yes' );
893 if ( $disableLinkConversion ||
894 ( !$ignoreOtherCond &&
897 || $action ==
'submit'
898 || $linkconvert ==
'no' ) ) ) {
902 if ( is_object( $nt ) ) {
903 $ns = $nt->getNamespace();
913 foreach ( $variants as $v ) {
915 $varnt = Title::newFromText( $v, $ns );
916 if ( $varnt !==
null ) {
917 $linkBatch->addObj( $varnt );
924 $linkBatch->execute();
926 foreach ( $titles as $varnt ) {
927 if ( $varnt->getArticleID() > 0 ) {
929 $link = $varnt->getText();
943 return '!' . $variant;
967 $class = static::class;
968 throw new MWException(
"Must implement loadDefaultTables() method in class $class" );
979 if ( $this->mTablesLoaded ) {
983 $this->mTablesLoaded =
true;
987 $cacheKey =
$cache->makeKey(
'conversiontables', $this->mMainLanguageCode );
989 $this->mTables =
$cache->get( $cacheKey );
991 if ( !$this->mTables || !array_key_exists( self::CACHE_VERSION_KEY, $this->mTables ) ) {
996 foreach ( $this->mVariants as $var ) {
1000 $this->mTables[$var]->mergeArray( $cached );
1004 $this->mTables[self::CACHE_VERSION_KEY] =
true;
1006 $cache->set( $cacheKey, $this->mTables, 43200 );
1024 if ( $this->mTables ) {
1026 unset( $this->mTables );
1029 $this->mTablesLoaded =
false;
1053 static $parsed = [];
1055 $key =
'Conversiontable/' . $code;
1057 $key .=
'/' . $subpage;
1059 if ( array_key_exists( $key, $parsed ) ) {
1063 $parsed[$key] =
true;
1065 if ( $subpage ===
'' ) {
1066 $messageCache = MediaWikiServices::getInstance()->getMessageCache();
1067 $txt = $messageCache->getMsgFromNamespace( $key, $code );
1072 $revision = MediaWikiServices::getInstance()
1073 ->getRevisionLookup()
1074 ->getRevisionByTitle(
$title );
1076 $model = $revision->getSlot(
1082 $txt = $revision->getContent(
1093 # Nothing to parse if there's no text
1094 if ( $txt ===
false || $txt ===
null || $txt ===
'' ) {
1100 $linkhead = $this->mLangObj->getNsText(
NS_MEDIAWIKI ) .
1104 foreach ( $subs as $sub ) {
1105 $link = explode(
']]', $sub, 2 );
1106 if ( count( $link ) != 2 ) {
1109 $b = explode(
'|', $link[0], 2 );
1110 $b = explode(
'/', trim( $b[0] ), 3 );
1111 if ( count( $b ) == 3 ) {
1117 if ( $b[0] == $linkhead && $b[1] == $code ) {
1118 $sublinks[] = $sublink;
1126 foreach ( $blocks as $block ) {
1132 $mappings = explode(
'}-', $block, 2 )[0];
1133 $stripped = str_replace( [
"'",
'"',
'*',
'#' ],
'', $mappings );
1135 foreach ( $table as
$t ) {
1136 $m = explode(
'=>',
$t, 3 );
1137 if ( count( $m ) != 2 ) {
1141 $tt = explode(
'//', $m[1], 2 );
1142 $ret[trim( $m[0] )] = trim( $tt[0] );
1148 foreach ( $sublinks as $link ) {
1154 if ( $this->mUcfirst ) {
1155 foreach ( $ret as $k => $v ) {
1156 $ret[$this->mLangObj->ucfirst( $k )] = $this->mLangObj->ucfirst( $v );
1171 # don't mark if already marked
1172 if ( strpos( $text,
'-{' ) || strpos( $text,
'}-' ) ) {
1176 $ret =
"-{R|$text}-";
1200 $t = explode(
'/', $linkTarget->
getDBkey(), 3 );
1202 if ( $c > 1 &&
$t[0] ==
'Conversiontable' ) {
1215 if ( $this->mVarSeparatorPattern ===
null ) {
1227 $expandedVariants = [];
1228 foreach ( $this->mVariants as $variant ) {
1229 $expandedVariants[ $variant ] = 1;
1231 $expandedVariants[ LanguageCode::bcp47( $variant ) ] = 1;
1234 foreach ( LanguageCode::getDeprecatedCodeMapping() as $old => $new ) {
1235 if ( isset( $expandedVariants[ $new ] ) ) {
1236 $expandedVariants[ $old ] = 1;
1241 foreach ( $expandedVariants as $variant => $ignore ) {
1243 $pat .= $variant .
'\s*:|';
1245 $pat .=
'[^;]*?=>\s*' . $variant .
'\s*:|';
1248 $this->mVarSeparatorPattern = $pat;
1250 return $this->mVarSeparatorPattern;
1275 return $variant && ( $variant === $this->
validateVariant( $variant ) );
1287 return htmlspecialchars( $this->
convert( $text ) );
$wgDefaultLanguageVariant
Default variant code, if false, the default will be the language code.
$wgDisableLangConversion
Whether to enable language variant conversion.
$wgDisabledVariants
Disabled variants array of language variant conversion.
$wgLanguageConverterCacheType
The cache type for storing language conversion tables, which are used when parsing certain text and i...
$wgDisableTitleConversion
Whether to enable language variant conversion for links.
deprecatePublicProperty( $property, $version, $class=null, $component=null)
Mark a property as deprecated.
trait DeprecationHelper
Use this trait in classes which have properties for which public access is deprecated.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
if(! $wgDBerrorLogTZ) $wgRequest
The rules used for language conversion, this processes the rules extracted by Parser from the -{ }- w...
getRulesAction()
Return how deal with conversion rules.
getConvTable()
Get conversion table.
getTitle()
Get converted title.
Base class for multi-variant language conversion.
getPreferredVariant()
Get preferred language variant.
validateVariant( $variant=null)
Validate the variant and return an appropriate strict internal variant code if one exists.
getDefaultVariant()
Get default variant.
__construct( $langobj, $maincode, $variants=[], $variantfallbacks=[], $flags=[], $manualLevel=[])
recursiveConvertTopLevel( $text, $variant, $depth=0)
Recursively convert text on the outside.
loadTables( $fromCache=true)
Load conversion tables either from the cache or the disk.
getHeaderVariant()
Determine the language variant from the Accept-Language header.
static array $languagesWithVariants
languages supporting variants
hasVariant( $variant)
Strict check if the language has the specific variant.
autoConvert( $text, $toVariant=false)
Dictionary-based conversion.
recursiveConvertRule( $text, $variant, &$startPos, $depth=0)
Recursively convert text on the inside.
parseCachedTable( $code, $subpage='', $recursive=true)
Parse the conversion table stored in the cache.
getVarSeparatorPattern()
Get the cached separator pattern for ConverterRule::parseRules()
convertNamespace( $index, $variant=null)
Get the namespace display name in the preferred variant.
getExtraHashOptions()
Returns language specific hash options.
getVariantFallbacks( $variant)
In case some variant is not defined in the markup, we need to have some fallback.
markNoConversion( $text, $noParse=false)
Enclose a string with the "no conversion" tag.
translate( $text, $variant)
Translate a string to a variant.
getVariants()
Get all valid variants.
findVariantLink(&$link, &$nt, $ignoreOtherCond=false)
If a language supports multiple variants, it is possible that non-existing link in one variant actual...
convert( $text)
Convert text to different variants of a language.
convertTitle(LinkTarget $linkTarget)
Auto convert a LinkTarget object to a readable string in the preferred variant.
postLoadTables()
Hook for post processing after conversion tables are loaded.
getURLVariant()
Get the variant specified in the URL.
loadDefaultTables()
Load default conversion tables.
getUserVariant(User $user)
Determine if the user has a variant set.
autoConvertToAllVariants( $text)
Call translate() to convert text to all valid variants.
guessVariant( $text, $variant)
Guess if a text is written in a variant.
convertHtml( $text)
Perform output conversion on a string, and encode for safe HTML output.
hasVariants()
Check if this is a language with variants.
applyManualConv(ConverterRule $convRule)
Apply manual conversion rules.
convertTo( $text, $variant)
Same as convert() except a extra parameter to custom variant.
updateConversionTable(LinkTarget $linkTarget)
Refresh the cache of conversion tables when MediaWiki:Conversiontable* is updated.
convertCategoryKey( $key)
Convert the sorting key for category links.
getConvRuleTitle()
Get the title produced by the conversion rule.
ReplacementArray[] bool[] $mTables
reloadTables()
Reload the conversion tables.
Internationalisation code See https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation for more...
getFormattedNsText( $index)
A convenience function that returns the same thing as getNsText() except with '_' changed to ' ',...
Class representing a list of titles The execute() method checks them all for existence and adds them ...
Wrapper around strtr() that holds replacements.
static explode( $separator, $subject)
Workalike for explode() with limited memory usage.
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
isSafeToLoad()
Test if it's safe to load this User object.
getOption( $oname, $defaultOverride=null, $ignoreHidden=false)
Get the user's current setting for a given option.
isLoggedIn()
Get whether the user is registered.
const CONTENT_MODEL_WIKITEXT
The shared interface for all language converters.