Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
project
drupal
Commits
0ac4b910
Commit
0ac4b910
authored
Apr 06, 2014
by
alexpott
Browse files
Issue
#1273968
by Sutharsan, Sweetchuck, penyaskito, chx: Remove eval from locale.module.
parent
08c18e12
Changes
4
Hide whitespace changes
Inline
Side-by-side
core/lib/Drupal/Component/Gettext/PoHeader.php
View file @
0ac4b910
...
...
@@ -189,10 +189,14 @@ public function __toString() {
* The Plural-Forms entry value.
*
* @return
* An array containing the number of plural forms and the converted version
* of the formula that can be evaluated with PHP later.
* An indexed array of parsed plural formula data. Containing:
* - 'nplurals': The number of plural forms defined by the plural formula.
* - 'plurals': Array of plural positions keyed by plural value.
*
* @throws Exception
*/
function
parsePluralForms
(
$pluralforms
)
{
$plurals
=
array
();
// First, delete all whitespace.
$pluralforms
=
strtr
(
$pluralforms
,
array
(
" "
=>
""
,
"
\t
"
=>
""
));
...
...
@@ -214,14 +218,31 @@ function parsePluralForms($pluralforms) {
return
FALSE
;
}
// Get PHP version of the plural formula.
$plural
=
$this
->
parseArithmetic
(
$plural
);
// If the number of plurals is zero, we return a default result.
if
(
$nplurals
==
0
)
{
return
array
(
$nplurals
,
array
(
'default'
=>
0
));
}
// Calculate possible plural positions of different plural values. All known
// plural formula's are repetitive above 100.
// For data compression we store the last position the array value
// changes and store it as default.
$element_stack
=
$this
->
parseArithmetic
(
$plural
);
$default
=
0
;
if
(
$element_stack
!==
FALSE
)
{
for
(
$i
=
0
;
$i
<=
199
;
$i
++
)
{
$plurals
[
$i
]
=
$this
->
evaluatePlural
(
$element_stack
,
$i
);
}
$default
=
$plurals
[
$i
-
1
];
$plurals
=
array_filter
(
$plurals
,
function
(
$value
)
use
(
$default
)
{
return
(
$value
!=
$default
);
});
$plurals
[
'default'
]
=
$default
;
if
(
$plural
!==
FALSE
)
{
return
array
(
$nplurals
,
$plural
);
return
array
(
$nplurals
,
$plurals
);
}
else
{
throw
new
Exception
(
'The plural formula could not be parsed.'
);
throw
new
\
Exception
(
'The plural formula could not be parsed.'
);
}
}
...
...
@@ -247,7 +268,7 @@ private function parseHeader($header) {
}
/**
* Parses and sanitizes an arithmetic formula into a
PHP expression
.
* Parses and sanitizes an arithmetic formula into a
plural element stack
.
*
* While parsing, we ensure, that the operators have the right
* precedence and associativity.
...
...
@@ -256,7 +277,7 @@ private function parseHeader($header) {
* A string containing the arithmetic formula.
*
* @return
* A
version of the formula
to evaluate
with PHP later
.
* A
stack of values and operations
to
be
evaluate
d
.
*/
private
function
parseArithmetic
(
$string
)
{
// Operator precedence table.
...
...
@@ -319,8 +340,9 @@ private function parseArithmetic($string) {
$element_stack
[]
=
$topop
;
$topop
=
array_pop
(
$operator_stack
);
}
$return
=
$element_stack
;
// Now
extract formula from
stack.
// Now
validate
stack.
$previous_size
=
count
(
$element_stack
)
+
1
;
while
(
count
(
$element_stack
)
<
$previous_size
)
{
$previous_size
=
count
(
$element_stack
);
...
...
@@ -343,12 +365,7 @@ private function parseArithmetic($string) {
}
// If only one element is left, the number of operators is appropriate.
if
(
count
(
$element_stack
)
==
1
)
{
return
$element_stack
[
0
];
}
else
{
return
FALSE
;
}
return
count
(
$element_stack
)
==
1
?
$return
:
FALSE
;
}
/**
...
...
@@ -416,4 +433,136 @@ private function tokenizeFormula($formula) {
return
$tokens
;
}
/**
* Evaluate the plural element stack using a plural value.
*
* Using an element stack, which represents a plural formula, we calculate
* which plural string should be used for a given plural value.
*
* An example of plural formula parting and evaluation:
* Plural formula: 'n!=1'
* This formula is parsed by parseArithmetic() to a stack (array) of elements:
* array(
* 0 => '$n',
* 1 => '1',
* 2 => '!=',
* );
* The evaluatePlural() method evaluates the $element_stack using the plural
* value $n. Before the actual evaluation, the '$n' in the array is replaced
* by the value of $n.
* For example: $n = 2 results in:
* array(
* 0 => '2',
* 1 => '1',
* 2 => '!=',
* );
* The stack is processed until only one element is (the result) is left. In
* every iteration the top elements of the stack, up until the first operator,
* are evaluated. After evaluation the arguments and the operator itself are
* removed and replaced by the evaluation result. This is typically 2
* arguments and 1 element for the operator.
* Because the operator is '!=' the example stack is evaluated as:
* $f = (int) 2 != 1;
* The resulting stack is:
* array(
* 0 => 1,
* );
* With only one element left in the stack (the final result) the loop is
* terminated and the result is returned.
*
* @param array $element_stack
* Array of plural formula values and operators create by parseArithmetic().
* @param integer $n
* The @count number for which we are determining the right plural position.
*
* @return integer
* Number of the plural string to be used for the given plural value.
*
* @see parseArithmetic()
* @throws Exception
*/
protected
function
evaluatePlural
(
$element_stack
,
$n
)
{
$count
=
count
(
$element_stack
);
$limit
=
$count
;
// Replace the '$n' value in the formula by the plural value.
for
(
$i
=
0
;
$i
<
$count
;
$i
++
)
{
if
(
$element_stack
[
$i
]
===
'$n'
)
{
$element_stack
[
$i
]
=
$n
;
}
}
// We process the stack until only one element is (the result) is left.
// We limit the number of evaluation cycles to prevent an endless loop in
// case the stack contains an error.
while
(
isset
(
$element_stack
[
1
]))
{
for
(
$i
=
2
;
$i
<
$count
;
$i
++
)
{
// There's no point in checking non-symbols. Also, switch(TRUE) would
// match any case and so it would break.
if
(
is_bool
(
$element_stack
[
$i
])
||
is_numeric
(
$element_stack
[
$i
]))
{
continue
;
}
$f
=
NULL
;
$length
=
3
;
$delta
=
2
;
switch
(
$element_stack
[
$i
])
{
case
'=='
:
$f
=
$element_stack
[
$i
-
2
]
==
$element_stack
[
$i
-
1
];
break
;
case
'!='
:
$f
=
$element_stack
[
$i
-
2
]
!=
$element_stack
[
$i
-
1
];
break
;
case
'<='
:
$f
=
$element_stack
[
$i
-
2
]
<=
$element_stack
[
$i
-
1
];
break
;
case
'>='
:
$f
=
$element_stack
[
$i
-
2
]
>=
$element_stack
[
$i
-
1
];
break
;
case
'<'
:
$f
=
$element_stack
[
$i
-
2
]
<
$element_stack
[
$i
-
1
];
break
;
case
'>'
:
$f
=
$element_stack
[
$i
-
2
]
>
$element_stack
[
$i
-
1
];
break
;
case
'+'
:
$f
=
$element_stack
[
$i
-
2
]
+
$element_stack
[
$i
-
1
];
break
;
case
'-'
:
$f
=
$element_stack
[
$i
-
2
]
-
$element_stack
[
$i
-
1
];
break
;
case
'*'
:
$f
=
$element_stack
[
$i
-
2
]
*
$element_stack
[
$i
-
1
];
break
;
case
'/'
:
$f
=
$element_stack
[
$i
-
2
]
/
$element_stack
[
$i
-
1
];
break
;
case
'%'
:
$f
=
$element_stack
[
$i
-
2
]
%
$element_stack
[
$i
-
1
];
break
;
case
'&&'
:
$f
=
$element_stack
[
$i
-
2
]
&&
$element_stack
[
$i
-
1
];
break
;
case
'||'
:
$f
=
$element_stack
[
$i
-
2
]
||
$element_stack
[
$i
-
1
];
break
;
case
':'
:
$f
=
$element_stack
[
$i
-
3
]
?
$element_stack
[
$i
-
2
]
:
$element_stack
[
$i
-
1
];
// This operator has 3 preceding elements, instead of the default 2.
$length
=
5
;
$delta
=
3
;
break
;
}
// If the element is an operator we remove the processed elements and
// store the result.
if
(
isset
(
$f
))
{
array_splice
(
$element_stack
,
$i
-
$delta
,
$length
,
$f
);
break
;
}
}
}
if
(
!
$limit
)
{
throw
new
\
Exception
(
'The plural formula could not be evaluated.'
);
}
return
(
int
)
$element_stack
[
0
];
}
}
core/misc/drupal.js
View file @
0ac4b910
...
...
@@ -364,13 +364,17 @@ if (window.jQuery) {
args
=
args
||
{};
args
[
'
@count
'
]
=
count
;
var
pluralDelimiter
=
drupalSettings
.
locale
.
pluralDelimiter
;
var
pluralDelimiter
=
drupalSettings
.
locale
.
pluralDelimiter
,
translations
=
Drupal
.
t
(
singular
+
pluralDelimiter
+
plural
,
args
,
options
).
split
(
pluralDelimiter
),
index
=
0
;
// Determine the index of the plural form.
var
index
=
Drupal
.
locale
.
pluralFormula
?
Drupal
.
locale
.
pluralFormula
(
args
[
'
@count
'
])
:
((
args
[
'
@count
'
]
===
1
)
?
0
:
1
);
var
translations
=
Drupal
.
t
(
singular
+
pluralDelimiter
+
plural
,
args
,
options
)
.
split
(
pluralDelimiter
);
if
(
Drupal
.
locale
.
pluralFormula
)
{
index
=
count
in
Drupal
.
locale
.
pluralFormula
?
Drupal
.
locale
.
pluralFormula
[
count
]
:
Drupal
.
locale
.
pluralFormula
[
'
default
'
];
}
else
if
(
args
[
'
@count
'
]
!==
1
)
{
index
=
1
;
}
return
translations
[
index
];
};
...
...
core/modules/locale/lib/Drupal/locale/Tests/LocalePluralFormatTest.php
View file @
0ac4b910
...
...
@@ -98,11 +98,15 @@ function testGetPluralFormat() {
1
=>
0
,
0
=>
1
,
5
=>
1
,
123
=>
1
,
235
=>
1
,
),
'fr'
=>
array
(
1
=>
0
,
0
=>
0
,
5
=>
1
,
123
=>
1
,
235
=>
1
,
),
'hr'
=>
array
(
1
=>
0
,
...
...
@@ -110,6 +114,8 @@ function testGetPluralFormat() {
0
=>
2
,
2
=>
1
,
8
=>
2
,
123
=>
1
,
235
=>
2
,
),
'hu'
=>
array
(
1
=>
-
1
,
...
...
core/modules/locale/locale.module
View file @
0ac4b910
...
...
@@ -311,9 +311,11 @@ function locale_get_plural($count, $langcode = NULL) {
// $count and statically cache the result for the combination of language
// and count, since the result will always be identical.
if
(
!
empty
(
$plural_formulas
[
$langcode
]))
{
// $n is used inside the expression in the eval().
$n
=
$count
;
$plural_indexes
[
$langcode
][
$count
]
=
@
eval
(
'return intval('
.
$plural_formulas
[
$langcode
][
'formula'
]
.
');'
);
// Plural formulas are stored as an array for 0-199. 100 is the highest
// modulo used but storing 0-99 is not enough because below 100 we often
// find exceptions (1, 2, etc).
$index
=
$count
>
199
?
100
+
(
$count
%
100
)
:
$count
;
$plural_indexes
[
$langcode
][
$count
]
=
isset
(
$plural_formulas
[
$langcode
][
'formula'
][
$index
])
?
$plural_formulas
[
$langcode
][
'formula'
][
$index
]
:
$plural_formulas
[
$langcode
][
'formula'
][
'default'
];
}
// In case there is no plural formula for English (no imported translation
// for English), use a default formula.
...
...
@@ -1272,16 +1274,16 @@ function _locale_rebuild_js($langcode = NULL) {
$data_hash
=
NULL
;
$data
=
$status
=
''
;
if
(
!
empty
(
$translations
))
{
$data
=
array
();
$data
=
array
(
'strings'
=>
$translations
,
);
$locale_plurals
=
\
Drupal
::
state
()
->
get
(
'locale.translation.plurals'
)
?:
array
();
if
(
!
empty
(
$locale_plurals
[
$language
->
id
][
'formula'
]))
{
$data
[
]
=
"
pluralFormula
: function (
\$
n) { return Number(
{
$locale_plurals
[
$language
->
id
][
'formula'
]
}
); }"
;
$data
[
'
pluralFormula
'
]
=
$locale_plurals
[
$language
->
id
][
'formula'
];
}
$data
[]
=
'strings: '
.
Json
::
encode
(
$translations
);
$data
=
'Drupal.locale = { '
.
implode
(
', '
,
$data
)
.
' };'
;
$data
=
'Drupal.locale = '
.
Json
::
encode
(
$data
)
.
';'
;
$data_hash
=
Crypt
::
hashBase64
(
$data
);
}
...
...
@@ -1342,6 +1344,7 @@ function _locale_rebuild_js($langcode = NULL) {
case
'updated'
:
watchdog
(
'locale'
,
'Updated JavaScript translation file for the language %language.'
,
array
(
'%language'
=>
$language
->
name
));
return
TRUE
;
case
'rebuilt'
:
watchdog
(
'locale'
,
'JavaScript translation file %file.js was lost.'
,
array
(
'%file'
=>
$locale_javascripts
[
$language
->
id
]),
WATCHDOG_WARNING
);
// Proceed to the 'created' case as the JavaScript translation file has
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment