Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
GettextPlural.php
Go to the documentation of this file.
1<?php
7namespace MediaWiki\Extension\Translate\Utilities;
8
10use InvalidArgumentException;
12
15 private const PRE = '{{PLURAL:GETTEXT|';
16 private const POST = '}}';
17
24 public static function getPluralRule( $code ) {
25 global $wgTranslateDocumentationLanguageCode;
26
27 if ( $code === $wgTranslateDocumentationLanguageCode ) {
28 return 'nplurals=1; plural=0;';
29 }
30
31 $rulefile = __DIR__ . '/../../data/plural-gettext.txt';
32 $rules = file_get_contents( $rulefile );
33 foreach ( explode( "\n", $rules ) as $line ) {
34 if ( trim( $line ) === '' ) {
35 continue;
36 }
37 [ $rulecode, $rule ] = explode( "\t", $line );
38 if ( $rulecode === $code ) {
39 return $rule;
40 }
41 }
42
43 return '';
44 }
45
53 public static function getPluralCount( $rule ) {
54 $m = [];
55 $ok = preg_match( '/nplurals=([0-9]+).*;/', $rule, $m );
56 if ( !$ok ) {
57 throw new InvalidArgumentException( "Rule $rule is malformed" );
58 }
59 return (int)$m[ 1 ];
60 }
61
68 public static function hasPlural( $text ) {
69 return strpos( $text, self::PRE ) !== false;
70 }
71
78 public static function flatten( array $forms ) {
79 return self::PRE . implode( '|', $forms ) . self::POST;
80 }
81
93 public static function unflatten( $text, $expectedPluralCount ) {
94 [ $template, $instanceMap ] = self::parsePluralForms( $text );
95 return self::expandTemplate( $template, $instanceMap, $expectedPluralCount );
96 }
97
104 private static function armour( $text ) {
105 // |/| is commonly used in KDE to support inflections. It needs to be escaped
106 // to avoid it messing up the plural markup.
107 $replacements = [
108 '|/|' => TranslateUtils::getPlaceholder(),
109 ];
110 // {0} is a common variable format
111 preg_match_all( '/\{\d+\}/', $text, $matches );
112 foreach ( $matches[0] as $m ) {
113 $replacements[$m] = TranslateUtils::getPlaceholder();
114 }
115
116 $text = strtr( $text, $replacements );
117 $map = array_flip( $replacements );
118
119 return [ $text, $map ];
120 }
121
129 private static function unarmour( $text, array $map ) {
130 return strtr( $text, $map );
131 }
132
139 public static function parsePluralForms( $text ) {
140 $m = [];
141 $pre = preg_quote( self::PRE, '/' );
142 $post = preg_quote( self::POST, '/' );
143
144 [ $armouredText, $armourMap ] = self::armour( $text );
145
146 $ok = preg_match_all( "/$pre(.*)$post/Us", $armouredText, $m );
147 if ( $ok === false ) {
148 throw new GettextPluralException( "Plural regular expression failed for text: $text" );
149 }
150
151 $template = $armouredText;
152 $instanceMap = [];
153
154 foreach ( $m[0] as $instanceIndex => $instanceText ) {
155 $ph = TranslateUtils::getPlaceholder();
156
157 // Using preg_replace instead of str_replace because of the limit parameter
158 $pattern = '/' . preg_quote( $instanceText, '/' ) . '/';
159 $template = preg_replace( $pattern, $ph, $template, 1 );
160
161 $instanceForms = explode( '|', $m[ 1 ][ $instanceIndex ] );
162 foreach ( $instanceForms as $i => $v ) {
163 $instanceForms[ $i ] = self::unarmour( $v, $armourMap );
164 }
165
166 $instanceMap[$ph] = $instanceForms;
167 }
168
169 $template = self::unarmour( $template, $armourMap );
170 return [ $template, $instanceMap ];
171 }
172
181 public static function expandTemplate( $template, array $instanceMap, $expectedPluralCount ) {
182 $formArray = [];
183 for ( $formIndex = 0; $formIndex < $expectedPluralCount; $formIndex++ ) {
184 // Start with the whole string
185 $form = $template;
186
187 // Loop over each plural markup instance and replace it with the plural form belonging
188 // to the current index
189 foreach ( $instanceMap as $ph => $instanceForms ) {
190 // For missing forms, fall back to empty text.
191 // Extra forms are excluded because $formIndex < $expectedPluralCount
192 $replacement = $instanceForms[ $formIndex ] ?? '';
193 $form = str_replace( $ph, $replacement, $form );
194 }
195
196 $formArray[ $formIndex ] = $form;
197 }
198
199 return $formArray;
200 }
201}
Identifies Gettext plural exceptions.
static parsePluralForms( $text)
Parses plural markup into a structure form.
static unflatten( $text, $expectedPluralCount)
Format translation with plural forms as array of forms.
static expandTemplate( $template, array $instanceMap, $expectedPluralCount)
Gives fully expanded forms given a template and parsed plural markup instances.
static getPluralRule( $code)
Returns Gettext plural rule for given language.
static flatten(array $forms)
Format plural forms as single string suitable for translation.
static getPluralCount( $rule)
Returns how many plural forms are expected by a given plural rule.
static hasPlural( $text)
Quick way to check if the text contains plural syntax.
Essentially random collection of helper functions, similar to GlobalFunctions.php.