:copyright: (c) 2013 by the Babel Team.
:license: BSD, see LICENSE for more details.
"""
-
+import decimal
import re
_fallback_tag = 'other'
+def extract_operands(source):
+ """Extract operands from a decimal, a float or an int, according to
+ `CLDR rules`_.
+
+ .. _`CLDR rules`: http://www.unicode.org/reports/tr35/tr35-33/tr35-numbers.html#Operands
+ """
+ n = abs(source)
+ i = int(n)
+ if isinstance(n, float):
+ n = i if i == n else decimal.Decimal(n)
+
+ if isinstance(n, decimal.Decimal):
+ dec_tuple = n.as_tuple()
+ exp = dec_tuple.exponent
+ fraction_digits = dec_tuple.digits[exp:] if exp < 0 else ()
+ trailing = ''.join(str(d) for d in fraction_digits)
+ no_trailing = trailing.rstrip('0')
+ v = len(trailing)
+ w = len(no_trailing)
+ f = int(trailing or 0)
+ t = int(no_trailing or 0)
+ else:
+ v = w = f = t = 0
+ return n, i, v, w, f, t
+
+
class PluralRule(object):
"""Represents a set of language pluralization rules. The constructor
accepts a list of (tag, expr) tuples or a dict of `CLDR rules`_. The
def __call__(self, n):
if not hasattr(self, '_func'):
self._func = to_python(self)
- return self._func(n)
+ return self._func(*extract_operands(n))
def to_javascript(rule):
'WITHIN': within_range_list,
'MOD': cldr_modulo
}
- to_python = _PythonCompiler().compile
- result = ['def evaluate(n):']
+ to_python_func = _PythonCompiler().compile
+ result = [
+ 'def evaluate(n, v=0, w=0, f=0, t=0):',
+ ' i = int(n)',
+ ]
for tag, ast in PluralRule.parse(rule).abstract:
# the str() call is to coerce the tag to the native string. It's
# a limited ascii restricted set of tags anyways so that is fine.
- result.append(' if (%s): return %r' % (to_python(ast), str(tag)))
+ result.append(' if (%s): return %r' % (to_python_func(ast), str(tag)))
result.append(' return %r' % _fallback_tag)
code = compile('\n'.join(result), '<rule>', 'exec')
eval(code, namespace)
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at http://babel.edgewall.org/log/.
+import decimal
import doctest
import unittest
import pytest
-
+from decimal import Decimal as Dec
from babel import plural
MALFORMED_TOKEN_TESTS = (
- ('a = 1'), ('n ! 2'),
+ 'a = 1', 'n ! 2',
)
plural.value_node(100))),
(make_range_list((1, 19)))))))
))
+
+
+EXTRACT_OPERANDS_TESTS = (
+ (1, 1, 1, 0, 0, 0, 0),
+ ('1.0', '1.0', 1, 1, 0, 0, 0),
+ ('1.00', '1.00', 1, 2, 0, 0, 0),
+ ('1.3', '1.3', 1, 1, 1, 3, 3),
+ ('1.30', '1.30', 1, 2, 1, 30, 3),
+ ('1.03', '1.03', 1, 2, 2, 3, 3),
+ ('1.230', '1.230', 1, 3, 2, 230, 23),
+ (-1, 1, 1, 0, 0, 0, 0),
+)
+
+
+@pytest.mark.parametrize('source,n,i,v,w,f,t', EXTRACT_OPERANDS_TESTS)
+def test_extract_operands(source, n, i, v, w, f, t):
+ assert (plural.extract_operands(decimal.Decimal(source)) ==
+ decimal.Decimal(n), i, v, w, f, t)