Skip to content

Commit 2f257df

Browse files
authored
Merge pull request #5 from jonasraoni/bugfix/master/limit-plural-forms-charset
Updated the plural form conversion to accept a limited character set
2 parents 84ca009 + 84286e2 commit 2f257df

1 file changed

Lines changed: 16 additions & 36 deletions

File tree

src/Translator.php

Lines changed: 16 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)