@@ -69,7 +69,7 @@ public function addTranslations(array $translations): self
6969 // Slap on a return keyword and semicolon at the end.
7070 $ this ->plurals [$ domain ] = [
7171 'count ' => (int ) str_replace ('nplurals= ' , '' , $ count ),
72- 'code ' => str_replace ( ' plural= ' , ' return ', str_replace ('n ' , '$n ' , $ code )). '; ' ,
72+ 'code ' => ' return ' . static :: fixTerseIfs ( rtrim ( str_replace ('plural= ' , '' , $ code ), ' ; ' )) . '; ' ,
7373 ];
7474 }
7575
@@ -195,7 +195,7 @@ protected function getPluralIndex(?string $domain, int $n, bool $fallback): int
195195 }
196196
197197 if (!isset ($ this ->plurals [$ domain ]['function ' ])) {
198- $ code = self :: fixTerseIfs ( $ this ->plurals [$ domain ]['code ' ]) ;
198+ $ code = $ this ->plurals [$ domain ]['code ' ];
199199 $ this ->plurals [$ domain ]['function ' ] = eval ("return function ( \$n) { $ code }; " );
200200 }
201201
@@ -207,45 +207,25 @@ protected function getPluralIndex(?string $domain, int $n, bool $fallback): int
207207 }
208208
209209 /**
210- * This function will recursively wrap failure states in brackets if they contain a nested terse if.
210+ * This function prepares the gettext plural form expression to be evaluated by PHP
211211 *
212- * This because PHP can not handle nested terse if's unless they are wrapped in brackets .
212+ * Nested ternary IFs will be enclosed by parenthesis and the variable "n" will be prefixed by "$" .
213213 *
214- * This code probably only works for the gettext plural decision codes.
215- *
216- * return ($n==1 ? 0 : $n%10>=2 && $n%10<=4 && ($n%100<10 || $n%100>=20) ? 1 : 2);
214+ * $n==1 ? 0 : $n%10>=2 && $n%10<=4 && ($n%100<10 || $n%100>=20) ? 1 : 2
217215 * becomes
218- * return ( $n==1 ? 0 : ($n%10>=2 && $n%10<=4 && ($n%100<10 || $n%100>=20) ? 1 : 2));
216+ * $n==1 ? 0 : ($n%10>=2 && $n%10<=4 && ($n%100<10 || $n%100>=20) ? 1 : 2)
219217 */
220- private static function fixTerseIfs (string $ code , bool $ inner = false ): string
221- {
222- /*
223- * (?P<expression>[^?]+) Capture everything up to ? as 'expression'
224- * \? ?
225- * (?P<success>[^:]+) Capture everything up to : as 'success'
226- * : :
227- * (?P<failure>[^;]+) Capture everything up to ; as 'failure'
228- */
229- preg_match ('/(?P<expression>[^?]+)\?(?P<success>[^:]+):(?P<failure>[^;]+)/ ' , $ code , $ matches );
230-
231- // If no match was found then no terse if was present
232- if (!isset ($ matches [0 ])) {
233- return $ code ;
218+ private static function fixTerseIfs (string $ pluralForms ): string
219+ {
220+ if (preg_match ('/[^<>|%&!?:=n()\d\s]/ ' , $ pluralForms )) {
221+ throw new InvalidArgumentException ('Invalid plural form expression ' );
234222 }
235-
236- $ expression = $ matches ['expression ' ];
237- $ success = $ matches ['success ' ];
238- $ failure = $ matches ['failure ' ];
239-
240- // Go look for another terse if in the failure state.
241- $ failure = static ::fixTerseIfs ($ failure , true );
242- $ code = $ expression .' ? ' .$ success .' : ' .$ failure ;
243-
244- if ($ inner ) {
245- return "( $ code) " ;
223+ $ pieces = explode (': ' , str_replace ('n ' , '$n ' , $ pluralForms ));
224+ $ last = array_pop ($ pieces );
225+ $ pluralForms = '' ;
226+ foreach ($ pieces as $ piece ) {
227+ $ pluralForms .= "$ piece:( " ;
246228 }
247-
248- // note the semicolon. We need that for executing the code.
249- return "$ code; " ;
229+ return $ pluralForms . $ last . str_repeat (') ' , count ($ pieces ));
250230 }
251231}
0 commit comments