3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
23 * Parser for rules of language conversion , parse rules in -{ }- tag.
25 * @author fdcn <fdcn64@gmail.com>, PhiLiP <philip.npc@gmail.com>
28 public $mText; // original text in -{text}-
29 public $mConverter; // LanguageConverter object
30 public $mRuleDisplay = '';
31 public $mRuleTitle = false;
32 public $mRules = '';// string : the text of the rules
33 public $mRulesAction = 'none';
34 public $mFlags = array();
35 public $mVariantFlags = array();
36 public $mConvTable = array();
37 public $mBidtable = array();// array of the translation in each variant
38 public $mUnidtable = array();// array of the translation in each variant
43 * @param string $text The text between -{ and }-
44 * @param LanguageConverter $converter
46 public function __construct( $text, $converter ) {
48 $this->mConverter
= $converter;
52 * Check if variants array in convert array.
54 * @param array|string $variants Variant language code
55 * @return string Translated text
57 public function getTextInBidtable( $variants ) {
58 $variants = (array)$variants;
62 foreach ( $variants as $variant ) {
63 if ( isset( $this->mBidtable
[$variant] ) ) {
64 return $this->mBidtable
[$variant];
71 * Parse flags with syntax -{FLAG| ... }-
74 function parseFlags() {
77 $variantFlags = array();
79 $sepPos = strpos( $text, '|' );
80 if ( $sepPos !== false ) {
81 $validFlags = $this->mConverter
->mFlags
;
82 $f = StringUtils
::explode( ';', substr( $text, 0, $sepPos ) );
83 foreach ( $f as $ff ) {
85 if ( isset( $validFlags[$ff] ) ) {
86 $flags[$validFlags[$ff]] = true;
89 $text = strval( substr( $text, $sepPos +
1 ) );
94 } elseif ( isset( $flags['R'] ) ) {
95 $flags = array( 'R' => true );// remove other flags
96 } elseif ( isset( $flags['N'] ) ) {
97 $flags = array( 'N' => true );// remove other flags
98 } elseif ( isset( $flags['-'] ) ) {
99 $flags = array( '-' => true );// remove other flags
100 } elseif ( count( $flags ) == 1 && isset( $flags['T'] ) ) {
102 } elseif ( isset( $flags['H'] ) ) {
103 // replace A flag, and remove other flags except T
104 $temp = array( '+' => true, 'H' => true );
105 if ( isset( $flags['T'] ) ) {
108 if ( isset( $flags['D'] ) ) {
113 if ( isset( $flags['A'] ) ) {
117 if ( isset( $flags['D'] ) ) {
118 unset( $flags['S'] );
120 // try to find flags like "zh-hans", "zh-hant"
121 // allow syntaxes like "-{zh-hans;zh-hant|XXXX}-"
122 $variantFlags = array_intersect( array_keys( $flags ), $this->mConverter
->mVariants
);
123 if ( $variantFlags ) {
124 $variantFlags = array_flip( $variantFlags );
128 $this->mVariantFlags
= $variantFlags;
129 $this->mRules
= $text;
130 $this->mFlags
= $flags;
134 * Generate conversion table.
137 function parseRules() {
138 $rules = $this->mRules
;
140 $unidtable = array();
141 $variants = $this->mConverter
->mVariants
;
142 $varsep_pattern = $this->mConverter
->getVarSeparatorPattern();
144 // Split according to $varsep_pattern, but ignore semicolons from HTML entities
145 $rules = preg_replace( '/(&[#a-zA-Z0-9]+);/', "$1\x01", $rules );
146 $choice = preg_split( $varsep_pattern, $rules );
147 $choice = str_replace( "\x01", ';', $choice );
149 foreach ( $choice as $c ) {
150 $v = explode( ':', $c, 2 );
151 if ( count( $v ) != 2 ) {
152 // syntax error, skip
157 $u = explode( '=>', $v, 2 );
158 // if $to is empty, strtr() could return a wrong result
159 if ( count( $u ) == 1 && $to && in_array( $v, $variants ) ) {
161 } elseif ( count( $u ) == 2 ) {
162 $from = trim( $u[0] );
164 if ( array_key_exists( $v, $unidtable )
165 && !is_array( $unidtable[$v] )
167 && in_array( $v, $variants ) ) {
168 $unidtable[$v] = array( $from => $to );
169 } elseif ( $to && in_array( $v, $variants ) ) {
170 $unidtable[$v][$from] = $to;
173 // syntax error, pass
174 if ( !isset( $this->mConverter
->mVariantNames
[$v] ) ) {
176 $unidtable = array();
180 $this->mBidtable
= $bidtable;
181 $this->mUnidtable
= $unidtable;
189 function getRulesDesc() {
190 $codesep = $this->mConverter
->mDescCodeSep
;
191 $varsep = $this->mConverter
->mDescVarSep
;
193 foreach ( $this->mBidtable
as $k => $v ) {
194 $text .= $this->mConverter
->mVariantNames
[$k] . "$codesep$v$varsep";
196 foreach ( $this->mUnidtable
as $k => $a ) {
197 foreach ( $a as $from => $to ) {
198 $text .= $from . '⇒' . $this->mConverter
->mVariantNames
[$k] .
199 "$codesep$to$varsep";
206 * Parse rules conversion.
209 * @param string $variant
213 function getRuleConvertedStr( $variant ) {
214 $bidtable = $this->mBidtable
;
215 $unidtable = $this->mUnidtable
;
217 if ( count( $bidtable ) +
count( $unidtable ) == 0 ) {
218 return $this->mRules
;
220 // display current variant in bidirectional array
221 $disp = $this->getTextInBidtable( $variant );
222 // or display current variant in fallbacks
224 $disp = $this->getTextInBidtable(
225 $this->mConverter
->getVariantFallbacks( $variant ) );
227 // or display current variant in unidirectional array
228 if ( !$disp && array_key_exists( $variant, $unidtable ) ) {
229 $disp = array_values( $unidtable[$variant] );
232 // or display frist text under disable manual convert
233 if ( !$disp && $this->mConverter
->mManualLevel
[$variant] == 'disable' ) {
234 if ( count( $bidtable ) > 0 ) {
235 $disp = array_values( $bidtable );
238 $disp = array_values( $unidtable );
239 $disp = array_values( $disp[0] );
248 * Similar to getRuleConvertedStr(), but this prefers to use original
249 * page title if $variant === $this->mConverter->mMainLanguageCode
250 * and may return false in this case (so this title conversion rule
251 * will be ignored and the original title is shown).
254 * @param string $variant The variant code to display page title in
255 * @return string|bool The converted title or false if just page name
257 function getRuleConvertedTitle( $variant ) {
258 if ( $variant === $this->mConverter
->mMainLanguageCode
) {
259 // If a string targeting exactly this variant is set,
260 // use it. Otherwise, just return false, so the real
261 // page name can be shown (and because variant === main,
262 // there'll be no further automatic conversion).
263 $disp = $this->getTextInBidtable( $variant );
267 if ( array_key_exists( $variant, $this->mUnidtable
) ) {
268 $disp = array_values( $this->mUnidtable
[$variant] );
271 // Assigned above or still false.
274 return $this->getRuleConvertedStr( $variant );
279 * Generate conversion table for all text.
282 function generateConvTable() {
283 // Special case optimisation
284 if ( !$this->mBidtable
&& !$this->mUnidtable
) {
285 $this->mConvTable
= array();
289 $bidtable = $this->mBidtable
;
290 $unidtable = $this->mUnidtable
;
291 $manLevel = $this->mConverter
->mManualLevel
;
294 foreach ( $this->mConverter
->mVariants
as $v ) {
295 /* for bidirectional array
296 fill in the missing variants, if any,
298 if ( !isset( $bidtable[$v] ) ) {
300 $this->mConverter
->getVariantFallbacks( $v );
301 $vf = $this->getTextInBidtable( $variantFallbacks );
307 if ( isset( $bidtable[$v] ) ) {
308 foreach ( $vmarked as $vo ) {
309 // use syntax: -{A|zh:WordZh;zh-tw:WordTw}-
310 // or -{H|zh:WordZh;zh-tw:WordTw}-
311 // or -{-|zh:WordZh;zh-tw:WordTw}-
312 // to introduce a custom mapping between
313 // words WordZh and WordTw in the whole text
314 if ( $manLevel[$v] == 'bidirectional' ) {
315 $this->mConvTable
[$v][$bidtable[$vo]] = $bidtable[$v];
317 if ( $manLevel[$vo] == 'bidirectional' ) {
318 $this->mConvTable
[$vo][$bidtable[$v]] = $bidtable[$vo];
323 /* for unidirectional array fill to convert tables */
324 if ( ( $manLevel[$v] == 'bidirectional' ||
$manLevel[$v] == 'unidirectional' )
325 && isset( $unidtable[$v] )
327 if ( isset( $this->mConvTable
[$v] ) ) {
328 $this->mConvTable
[$v] = array_merge( $this->mConvTable
[$v], $unidtable[$v] );
330 $this->mConvTable
[$v] = $unidtable[$v];
337 * Parse rules and flags.
338 * @param string $variant Variant language code
340 public function parse( $variant = null ) {
342 $variant = $this->mConverter
->getPreferredVariant();
346 $flags = $this->mFlags
;
348 // convert to specified variant
349 // syntax: -{zh-hans;zh-hant[;...]|<text to convert>}-
350 if ( $this->mVariantFlags
) {
351 // check if current variant in flags
352 if ( isset( $this->mVariantFlags
[$variant] ) ) {
353 // then convert <text to convert> to current language
354 $this->mRules
= $this->mConverter
->autoConvert( $this->mRules
,
357 // if current variant no in flags,
358 // then we check its fallback variants.
360 $this->mConverter
->getVariantFallbacks( $variant );
361 if ( is_array( $variantFallbacks ) ) {
362 foreach ( $variantFallbacks as $variantFallback ) {
363 // if current variant's fallback exist in flags
364 if ( isset( $this->mVariantFlags
[$variantFallback] ) ) {
365 // then convert <text to convert> to fallback language
367 $this->mConverter
->autoConvert( $this->mRules
,
374 $this->mFlags
= $flags = array( 'R' => true );
377 if ( !isset( $flags['R'] ) && !isset( $flags['N'] ) ) {
378 // decode => HTML entities modified by Sanitizer::removeHTMLtags
379 $this->mRules
= str_replace( '=>', '=>', $this->mRules
);
382 $rules = $this->mRules
;
384 if ( !$this->mBidtable
&& !$this->mUnidtable
) {
385 if ( isset( $flags['+'] ) ||
isset( $flags['-'] ) ) {
386 // fill all variants if text in -{A/H/-|text} without rules
387 foreach ( $this->mConverter
->mVariants
as $v ) {
388 $this->mBidtable
[$v] = $rules;
390 } elseif ( !isset( $flags['N'] ) && !isset( $flags['T'] ) ) {
391 $this->mFlags
= $flags = array( 'R' => true );
395 $this->mRuleDisplay
= false;
396 foreach ( $flags as $flag => $unused ) {
399 // if we don't do content convert, still strip the -{}- tags
400 $this->mRuleDisplay
= $rules;
403 // process N flag: output current variant name
404 $ruleVar = trim( $rules );
405 if ( isset( $this->mConverter
->mVariantNames
[$ruleVar] ) ) {
406 $this->mRuleDisplay
= $this->mConverter
->mVariantNames
[$ruleVar];
408 $this->mRuleDisplay
= '';
412 // process D flag: output rules description
413 $this->mRuleDisplay
= $this->getRulesDesc();
416 // process H,- flag or T only: output nothing
417 $this->mRuleDisplay
= '';
420 $this->mRulesAction
= 'remove';
421 $this->mRuleDisplay
= '';
424 $this->mRulesAction
= 'add';
425 $this->mRuleDisplay
= '';
428 $this->mRuleDisplay
= $this->getRuleConvertedStr( $variant );
431 $this->mRuleTitle
= $this->getRuleConvertedTitle( $variant );
432 $this->mRuleDisplay
= '';
435 // ignore unknown flags (but see error case below)
438 if ( $this->mRuleDisplay
=== false ) {
439 $this->mRuleDisplay
= '<span class="error">'
440 . wfMessage( 'converter-manual-rule-error' )->inContentLanguage()->escaped()
444 $this->generateConvTable();
448 * Checks if there are conversion rules.
451 public function hasRules() {
452 return $this->mRules
!== '';
456 * Get display text on markup -{...}-
459 public function getDisplay() {
460 return $this->mRuleDisplay
;
464 * Get converted title.
467 public function getTitle() {
468 return $this->mRuleTitle
;
472 * Return how deal with conversion rules.
475 public function getRulesAction() {
476 return $this->mRulesAction
;
480 * Get conversion table. (bidirectional and unidirectional
484 public function getConvTable() {
485 return $this->mConvTable
;
489 * Get conversion rules string.
492 public function getRules() {
493 return $this->mRules
;
497 * Get conversion flags.
500 public function getFlags() {
501 return $this->mFlags
;