123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503 |
- <?php
- /**
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup Language
- */
- /**
- * Parser for rules of language conversion , parse rules in -{ }- tag.
- * @ingroup Language
- * @author fdcn <fdcn64@gmail.com>, PhiLiP <philip.npc@gmail.com>
- */
- class ConverterRule {
- public $mText; // original text in -{text}-
- public $mConverter; // LanguageConverter object
- public $mRuleDisplay = '';
- public $mRuleTitle = false;
- public $mRules = '';// string : the text of the rules
- public $mRulesAction = 'none';
- public $mFlags = [];
- public $mVariantFlags = [];
- public $mConvTable = [];
- public $mBidtable = [];// array of the translation in each variant
- public $mUnidtable = [];// array of the translation in each variant
- /**
- * Constructor
- *
- * @param string $text The text between -{ and }-
- * @param LanguageConverter $converter
- */
- public function __construct( $text, $converter ) {
- $this->mText = $text;
- $this->mConverter = $converter;
- }
- /**
- * Check if variants array in convert array.
- *
- * @param array|string $variants Variant language code
- * @return string Translated text
- */
- public function getTextInBidtable( $variants ) {
- $variants = (array)$variants;
- if ( !$variants ) {
- return false;
- }
- foreach ( $variants as $variant ) {
- if ( isset( $this->mBidtable[$variant] ) ) {
- return $this->mBidtable[$variant];
- }
- }
- return false;
- }
- /**
- * Parse flags with syntax -{FLAG| ... }-
- * @private
- */
- function parseFlags() {
- $text = $this->mText;
- $flags = [];
- $variantFlags = [];
- $sepPos = strpos( $text, '|' );
- if ( $sepPos !== false ) {
- $validFlags = $this->mConverter->mFlags;
- $f = StringUtils::explode( ';', substr( $text, 0, $sepPos ) );
- foreach ( $f as $ff ) {
- $ff = trim( $ff );
- if ( isset( $validFlags[$ff] ) ) {
- $flags[$validFlags[$ff]] = true;
- }
- }
- $text = strval( substr( $text, $sepPos + 1 ) );
- }
- if ( !$flags ) {
- $flags['S'] = true;
- } elseif ( isset( $flags['R'] ) ) {
- $flags = [ 'R' => true ];// remove other flags
- } elseif ( isset( $flags['N'] ) ) {
- $flags = [ 'N' => true ];// remove other flags
- } elseif ( isset( $flags['-'] ) ) {
- $flags = [ '-' => true ];// remove other flags
- } elseif ( count( $flags ) == 1 && isset( $flags['T'] ) ) {
- $flags['H'] = true;
- } elseif ( isset( $flags['H'] ) ) {
- // replace A flag, and remove other flags except T
- $temp = [ '+' => true, 'H' => true ];
- if ( isset( $flags['T'] ) ) {
- $temp['T'] = true;
- }
- if ( isset( $flags['D'] ) ) {
- $temp['D'] = true;
- }
- $flags = $temp;
- } else {
- if ( isset( $flags['A'] ) ) {
- $flags['+'] = true;
- $flags['S'] = true;
- }
- if ( isset( $flags['D'] ) ) {
- unset( $flags['S'] );
- }
- // try to find flags like "zh-hans", "zh-hant"
- // allow syntaxes like "-{zh-hans;zh-hant|XXXX}-"
- $variantFlags = array_intersect( array_keys( $flags ), $this->mConverter->mVariants );
- if ( $variantFlags ) {
- $variantFlags = array_flip( $variantFlags );
- $flags = [];
- }
- }
- $this->mVariantFlags = $variantFlags;
- $this->mRules = $text;
- $this->mFlags = $flags;
- }
- /**
- * Generate conversion table.
- * @private
- */
- function parseRules() {
- $rules = $this->mRules;
- $bidtable = [];
- $unidtable = [];
- $variants = $this->mConverter->mVariants;
- $varsep_pattern = $this->mConverter->getVarSeparatorPattern();
- // Split according to $varsep_pattern, but ignore semicolons from HTML entities
- $rules = preg_replace( '/(&[#a-zA-Z0-9]+);/', "$1\x01", $rules );
- $choice = preg_split( $varsep_pattern, $rules );
- $choice = str_replace( "\x01", ';', $choice );
- foreach ( $choice as $c ) {
- $v = explode( ':', $c, 2 );
- if ( count( $v ) != 2 ) {
- // syntax error, skip
- continue;
- }
- $to = trim( $v[1] );
- $v = trim( $v[0] );
- $u = explode( '=>', $v, 2 );
- // if $to is empty (which is also used as $from in bidtable),
- // strtr() could return a wrong result.
- if ( count( $u ) == 1 && $to !== '' && in_array( $v, $variants ) ) {
- $bidtable[$v] = $to;
- } elseif ( count( $u ) == 2 ) {
- $from = trim( $u[0] );
- $v = trim( $u[1] );
- // if $from is empty, strtr() could return a wrong result.
- if ( array_key_exists( $v, $unidtable )
- && !is_array( $unidtable[$v] )
- && $from !== ''
- && in_array( $v, $variants ) ) {
- $unidtable[$v] = [ $from => $to ];
- } elseif ( $from !== '' && in_array( $v, $variants ) ) {
- $unidtable[$v][$from] = $to;
- }
- }
- // syntax error, pass
- if ( !isset( $this->mConverter->mVariantNames[$v] ) ) {
- $bidtable = [];
- $unidtable = [];
- break;
- }
- }
- $this->mBidtable = $bidtable;
- $this->mUnidtable = $unidtable;
- }
- /**
- * @private
- *
- * @return string
- */
- function getRulesDesc() {
- $codesep = $this->mConverter->mDescCodeSep;
- $varsep = $this->mConverter->mDescVarSep;
- $text = '';
- foreach ( $this->mBidtable as $k => $v ) {
- $text .= $this->mConverter->mVariantNames[$k] . "$codesep$v$varsep";
- }
- foreach ( $this->mUnidtable as $k => $a ) {
- foreach ( $a as $from => $to ) {
- $text .= $from . '⇒' . $this->mConverter->mVariantNames[$k] .
- "$codesep$to$varsep";
- }
- }
- return $text;
- }
- /**
- * Parse rules conversion.
- * @private
- *
- * @param string $variant
- *
- * @return string
- */
- function getRuleConvertedStr( $variant ) {
- $bidtable = $this->mBidtable;
- $unidtable = $this->mUnidtable;
- if ( count( $bidtable ) + count( $unidtable ) == 0 ) {
- return $this->mRules;
- } else {
- // display current variant in bidirectional array
- $disp = $this->getTextInBidtable( $variant );
- // or display current variant in fallbacks
- if ( $disp === false ) {
- $disp = $this->getTextInBidtable(
- $this->mConverter->getVariantFallbacks( $variant ) );
- }
- // or display current variant in unidirectional array
- if ( $disp === false && array_key_exists( $variant, $unidtable ) ) {
- $disp = array_values( $unidtable[$variant] )[0];
- }
- // or display frist text under disable manual convert
- if ( $disp === false && $this->mConverter->mManualLevel[$variant] == 'disable' ) {
- if ( count( $bidtable ) > 0 ) {
- $disp = array_values( $bidtable )[0];
- } else {
- $disp = array_values( array_values( $unidtable )[0] )[0];
- }
- }
- return $disp;
- }
- }
- /**
- * Similar to getRuleConvertedStr(), but this prefers to use original
- * page title if $variant === $this->mConverter->mMainLanguageCode
- * and may return false in this case (so this title conversion rule
- * will be ignored and the original title is shown).
- *
- * @since 1.22
- * @param string $variant The variant code to display page title in
- * @return string|bool The converted title or false if just page name
- */
- function getRuleConvertedTitle( $variant ) {
- if ( $variant === $this->mConverter->mMainLanguageCode ) {
- // If a string targeting exactly this variant is set,
- // use it. Otherwise, just return false, so the real
- // page name can be shown (and because variant === main,
- // there'll be no further automatic conversion).
- $disp = $this->getTextInBidtable( $variant );
- if ( $disp ) {
- return $disp;
- }
- if ( array_key_exists( $variant, $this->mUnidtable ) ) {
- $disp = array_values( $this->mUnidtable[$variant] )[0];
- }
- // Assigned above or still false.
- return $disp;
- } else {
- return $this->getRuleConvertedStr( $variant );
- }
- }
- /**
- * Generate conversion table for all text.
- * @private
- */
- function generateConvTable() {
- // Special case optimisation
- if ( !$this->mBidtable && !$this->mUnidtable ) {
- $this->mConvTable = [];
- return;
- }
- $bidtable = $this->mBidtable;
- $unidtable = $this->mUnidtable;
- $manLevel = $this->mConverter->mManualLevel;
- $vmarked = [];
- foreach ( $this->mConverter->mVariants as $v ) {
- /* for bidirectional array
- fill in the missing variants, if any,
- with fallbacks */
- if ( !isset( $bidtable[$v] ) ) {
- $variantFallbacks =
- $this->mConverter->getVariantFallbacks( $v );
- $vf = $this->getTextInBidtable( $variantFallbacks );
- if ( $vf ) {
- $bidtable[$v] = $vf;
- }
- }
- if ( isset( $bidtable[$v] ) ) {
- foreach ( $vmarked as $vo ) {
- // use syntax: -{A|zh:WordZh;zh-tw:WordTw}-
- // or -{H|zh:WordZh;zh-tw:WordTw}-
- // or -{-|zh:WordZh;zh-tw:WordTw}-
- // to introduce a custom mapping between
- // words WordZh and WordTw in the whole text
- if ( $manLevel[$v] == 'bidirectional' ) {
- $this->mConvTable[$v][$bidtable[$vo]] = $bidtable[$v];
- }
- if ( $manLevel[$vo] == 'bidirectional' ) {
- $this->mConvTable[$vo][$bidtable[$v]] = $bidtable[$vo];
- }
- }
- $vmarked[] = $v;
- }
- /* for unidirectional array fill to convert tables */
- if ( ( $manLevel[$v] == 'bidirectional' || $manLevel[$v] == 'unidirectional' )
- && isset( $unidtable[$v] )
- ) {
- if ( isset( $this->mConvTable[$v] ) ) {
- $this->mConvTable[$v] = $unidtable[$v] + $this->mConvTable[$v];
- } else {
- $this->mConvTable[$v] = $unidtable[$v];
- }
- }
- }
- }
- /**
- * Parse rules and flags.
- * @param string $variant Variant language code
- */
- public function parse( $variant = null ) {
- if ( !$variant ) {
- $variant = $this->mConverter->getPreferredVariant();
- }
- $this->parseFlags();
- $flags = $this->mFlags;
- // convert to specified variant
- // syntax: -{zh-hans;zh-hant[;...]|<text to convert>}-
- if ( $this->mVariantFlags ) {
- // check if current variant in flags
- if ( isset( $this->mVariantFlags[$variant] ) ) {
- // then convert <text to convert> to current language
- $this->mRules = $this->mConverter->autoConvert( $this->mRules,
- $variant );
- } else {
- // if current variant no in flags,
- // then we check its fallback variants.
- $variantFallbacks =
- $this->mConverter->getVariantFallbacks( $variant );
- if ( is_array( $variantFallbacks ) ) {
- foreach ( $variantFallbacks as $variantFallback ) {
- // if current variant's fallback exist in flags
- if ( isset( $this->mVariantFlags[$variantFallback] ) ) {
- // then convert <text to convert> to fallback language
- $this->mRules =
- $this->mConverter->autoConvert( $this->mRules,
- $variantFallback );
- break;
- }
- }
- }
- }
- $this->mFlags = $flags = [ 'R' => true ];
- }
- if ( !isset( $flags['R'] ) && !isset( $flags['N'] ) ) {
- // decode => HTML entities modified by Sanitizer::removeHTMLtags
- $this->mRules = str_replace( '=>', '=>', $this->mRules );
- $this->parseRules();
- }
- $rules = $this->mRules;
- if ( !$this->mBidtable && !$this->mUnidtable ) {
- if ( isset( $flags['+'] ) || isset( $flags['-'] ) ) {
- // fill all variants if text in -{A/H/-|text}- is non-empty but without rules
- if ( $rules !== '' ) {
- foreach ( $this->mConverter->mVariants as $v ) {
- $this->mBidtable[$v] = $rules;
- }
- }
- } elseif ( !isset( $flags['N'] ) && !isset( $flags['T'] ) ) {
- $this->mFlags = $flags = [ 'R' => true ];
- }
- }
- $this->mRuleDisplay = false;
- foreach ( $flags as $flag => $unused ) {
- switch ( $flag ) {
- case 'R':
- // if we don't do content convert, still strip the -{}- tags
- $this->mRuleDisplay = $rules;
- break;
- case 'N':
- // process N flag: output current variant name
- $ruleVar = trim( $rules );
- if ( isset( $this->mConverter->mVariantNames[$ruleVar] ) ) {
- $this->mRuleDisplay = $this->mConverter->mVariantNames[$ruleVar];
- } else {
- $this->mRuleDisplay = '';
- }
- break;
- case 'D':
- // process D flag: output rules description
- $this->mRuleDisplay = $this->getRulesDesc();
- break;
- case 'H':
- // process H,- flag or T only: output nothing
- $this->mRuleDisplay = '';
- break;
- case '-':
- $this->mRulesAction = 'remove';
- $this->mRuleDisplay = '';
- break;
- case '+':
- $this->mRulesAction = 'add';
- $this->mRuleDisplay = '';
- break;
- case 'S':
- $this->mRuleDisplay = $this->getRuleConvertedStr( $variant );
- break;
- case 'T':
- $this->mRuleTitle = $this->getRuleConvertedTitle( $variant );
- $this->mRuleDisplay = '';
- break;
- default:
- // ignore unknown flags (but see error case below)
- }
- }
- if ( $this->mRuleDisplay === false ) {
- $this->mRuleDisplay = '<span class="error">'
- . wfMessage( 'converter-manual-rule-error' )->inContentLanguage()->escaped()
- . '</span>';
- }
- $this->generateConvTable();
- }
- /**
- * Checks if there are conversion rules.
- * @return bool
- */
- public function hasRules() {
- return $this->mRules !== '';
- }
- /**
- * Get display text on markup -{...}-
- * @return string
- */
- public function getDisplay() {
- return $this->mRuleDisplay;
- }
- /**
- * Get converted title.
- * @return string
- */
- public function getTitle() {
- return $this->mRuleTitle;
- }
- /**
- * Return how deal with conversion rules.
- * @return string
- */
- public function getRulesAction() {
- return $this->mRulesAction;
- }
- /**
- * Get conversion table. (bidirectional and unidirectional
- * conversion table)
- * @return array
- */
- public function getConvTable() {
- return $this->mConvTable;
- }
- /**
- * Get conversion rules string.
- * @return string
- */
- public function getRules() {
- return $this->mRules;
- }
- /**
- * Get conversion flags.
- * @return array
- */
- public function getFlags() {
- return $this->mFlags;
- }
- }
|