From 7b6616ce556872a2ec501725b8f79ea400799406 Mon Sep 17 00:00:00 2001 From: alex bodnaru Date: Mon, 22 Feb 2016 17:19:30 +0200 Subject: [PATCH 1/7] added the format_babel method to show the units as translated in babel --- pint/formatting.py | 28 +++++++++++++++++++++++++--- pint/quantity.py | 15 +++++++++++++++ pint/unit.py | 15 +++++++++++++++ pint/util.py | 3 +++ 4 files changed, 58 insertions(+), 3 deletions(-) diff --git a/pint/formatting.py b/pint/formatting.py index ba9cc1d..03e5acc 100644 --- a/pint/formatting.py +++ b/pint/formatting.py @@ -13,6 +13,8 @@ from __future__ import division, unicode_literals, print_function, absolute_impo import re +from .babel_units import babel_units, babel_forms + __JOIN_REG_EXP = re.compile("\{\d*\}") @@ -100,7 +102,8 @@ _FORMATS = { def formatter(items, as_ratio=True, single_denominator=False, product_fmt=' * ', division_fmt=' / ', power_fmt='{0} ** {1}', - parentheses_fmt='({0})', exp_call=lambda x: '{0:n}'.format(x)): + parentheses_fmt='({0})', exp_call=lambda x: '{0:n}'.format(x), + locale=None, form='long', plural_form='one'): """Format a list of (name, exponent) pairs. :param items: a list of (name, exponent) pairs. @@ -126,6 +129,24 @@ def formatter(items, as_ratio=True, single_denominator=False, pos_terms, neg_terms = [], [] for key, value in sorted(items): + if locale and form and plural_form: + _key = babel_units.get(key) + if _key: + patterns = locale.unit_patterns + for _form in [form] + babel_forms: + pattern_key = _key + ':' + _form + if pattern_key in patterns: + plural = plural_form + if value <= 0: + plural = 'one' + pattern = patterns[pattern_key][plural] + key = pattern.replace('{0}', '').strip() + break + per_form = 'compound:per:' + form + if per_form in patterns: + division_fmt = patterns[per_form] + power_fmt = '{0}{1}' + exp_call = _pretty_fmt_exponent if value == 1: pos_terms.append(key) elif value > 0: @@ -177,12 +198,13 @@ def _parse_spec(spec): return result -def format_unit(unit, spec): +def format_unit(unit, spec, **kwspec): if not unit: return 'dimensionless' spec = _parse_spec(spec) - fmt = _FORMATS[spec] + fmt = dict(_FORMATS[spec]) + fmt.update(kwspec) result = formatter(unit.items(), **fmt) if spec == 'L': diff --git a/pint/quantity.py b/pint/quantity.py index 82f9aa7..0d24e51 100644 --- a/pint/quantity.py +++ b/pint/quantity.py @@ -141,6 +141,21 @@ class _Quantity(SharedRegistryObject): format(obj.magnitude, remove_custom_flags(spec)), format(obj.units, spec)).replace('\n', '') + def format_babel(self, spec='', **kwspec): + spec = spec or self.default_format + + # standard cases + if '#' in spec: + spec = spec.replace('#', '') + obj = self.to_compact() + else: + obj = self + kwspec = dict(kwspec) + kwspec['plural_form'] = kwspec['locale'].plural_form(obj.magnitude) + return '{0} {1}'.format( + format(obj.magnitude, remove_custom_flags(spec)), + obj.units.format_babel(spec, **kwspec)).replace('\n', '') + # IPython related code def _repr_html_(self): return self.__format__('H') diff --git a/pint/unit.py b/pint/unit.py index cac4220..20003a4 100644 --- a/pint/unit.py +++ b/pint/unit.py @@ -111,6 +111,21 @@ class _Unit(SharedRegistryObject): return '%s' % (format(units, spec)) + def format_babel(self, spec='', **kwspec): + spec = spec or self.default_format + + if '~' in spec: + if self.dimensionless: + return '' + units = UnitsContainer(dict((self._REGISTRY._get_symbol(key), + value) + for key, value in self._units.items())) + spec = spec.replace('~', '') + else: + units = self._units + + return '%s' % (units.format_babel(spec, **kwspec)) + # IPython related code def _repr_html_(self): return self.__format__('H') diff --git a/pint/util.py b/pint/util.py index 0374696..b0dd670 100644 --- a/pint/util.py +++ b/pint/util.py @@ -325,6 +325,9 @@ class UnitsContainer(Mapping): def __format__(self, spec): return format_unit(self, spec) + def format_babel(self, spec, **kwspec): + return format_unit(self, spec, **kwspec) + def __copy__(self): return UnitsContainer(self._d) From 87a92a2e9e544db551f2814730e6903283b39ae9 Mon Sep 17 00:00:00 2001 From: alex bodnaru Date: Mon, 22 Feb 2016 21:43:28 +0200 Subject: [PATCH 2/7] properly added new files to git and added test --- pint/babel_units.py | 139 +++++++++++++++++++++++++++++++++++ pint/compat/__init__.py | 9 +++ pint/formatting.py | 5 +- pint/testsuite/helpers.py | 6 +- pint/testsuite/test_babel.py | 30 ++++++++ pint/xtranslated.txt | 26 +++++++ 6 files changed, 213 insertions(+), 2 deletions(-) create mode 100644 pint/babel_units.py create mode 100644 pint/testsuite/test_babel.py create mode 100644 pint/xtranslated.txt diff --git a/pint/babel_units.py b/pint/babel_units.py new file mode 100644 index 0000000..71fe8dc --- /dev/null +++ b/pint/babel_units.py @@ -0,0 +1,139 @@ +# -*- coding: utf-8 -*- +""" + pint.babel + ~~~~~~~~~~ + + :copyright: 2016 by Pint Authors, see AUTHORS for more details. + :license: BSD, see LICENSE for more details. +""" + +babel_units = dict( + standard_gravity='acceleration-g-force', + millibar='pressure-millibar', + metric_ton='mass-metric-ton', + megawatt='power-megawatt', + degF='temperature-fahrenheit', + dietary_calorie='energy-foodcalorie', + millisecond='duration-millisecond', + mph='speed-mile-per-hour', + acre_foot='volume-acre-foot', + mebibit='digital-megabit', + gibibit='digital-gigabit', + tebibit='digital-terabit', + mebibyte='digital-megabyte', + kibibyte='digital-kilobyte', + mm_Hg='pressure-millimeter-of-mercury', + month='duration-month', + kilocalorie='energy-kilocalorie', + cubic_mile='volume-cubic-mile', + arcsecond='angle-arc-second', + byte='digital-byte', + metric_cup='volume-cup-metric', + kilojoule='energy-kilojoule', + meter_per_second_squared='acceleration-meter-per-second-squared', + pint='volume-pint', + square_centimeter='area-square-centimeter', + in_Hg='pressure-inch-hg', + milliampere='electric-milliampere', + arcminute='angle-arc-minute', + MPG='consumption-mile-per-gallon', + hertz='frequency-hertz', + day='duration-day', + mps='speed-meter-per-second', + kilometer='length-kilometer', + square_yard='area-square-yard', + kelvin='temperature-kelvin', + kilogram='mass-kilogram', + kilohertz='frequency-kilohertz', + megahertz='frequency-megahertz', + meter='length-meter', + cubic_inch='volume-cubic-inch', + kilowatt_hour='energy-kilowatt-hour', + second='duration-second', + yard='length-yard', + light_year='length-light-year', + millimeter='length-millimeter', + metric_horsepower='power-horsepower', + gibibyte='digital-gigabyte', + ## 'temperature-generic', + liter='volume-liter', + turn='angle-revolution', + microsecond='duration-microsecond', + pound='mass-pound', + ounce='mass-ounce', + calorie='energy-calorie', + centimeter='length-centimeter', + inch='length-inch', + centiliter='volume-centiliter', + troy_ounce='mass-ounce-troy', + gream='mass-gram', + kilowatt='power-kilowatt', + knot='speed-knot', + lux='light-lux', + hectoliter='volume-hectoliter', + microgram='mass-microgram', + degC='temperature-celsius', + tablespoon='volume-tablespoon', + cubic_yard='volume-cubic-yard', + square_foot='area-square-foot', + tebibyte='digital-terabyte', + square_inch='area-square-inch', + carat='mass-carat', + hectopascal='pressure-hectopascal', + gigawatt='power-gigawatt', + watt='power-watt', + micrometer='length-micrometer', + volt='electric-volt', + bit='digital-bit', + gigahertz='frequency-gigahertz', + teaspoon='volume-teaspoon', + ohm='electric-ohm', + joule='energy-joule', + cup='volume-cup', + square_mile='area-square-mile', + nautical_mile='length-nautical-mile', + square_meter='area-square-meter', + mile='length-mile', + acre='area-acre', + nanometer='length-nanometer', + hour='duration-hour', + astronomical_unit='length-astronomical-unit', + liter_per_100kilometers ='consumption-liter-per-100kilometers', + megaliter='volume-megaliter', + ton='mass-ton', + hectare='area-hectare', + square_kilometer='area-square-kilometer', + kibibit='digital-kilobit', + mile_scandinavian='length-mile-scandinavian', + liter_per_kilometer='consumption-liter-per-kilometer', + century='duration-century', + cubic_foot='volume-cubic-foot', + deciliter='volume-deciliter', + ##pint='volume-pint-metric', + cubic_meter='volume-cubic-meter', + cubic_kilometer='volume-cubic-kilometer', + quart='volume-quart', + cc='volume-cubic-centimeter', + pound_force_per_square_inch='pressure-pound-per-square-inch', + milligram='mass-milligram', + kph='speed-kilometer-per-hour', + minute='duration-minute', + parsec='length-parsec', + picometer='length-picometer', + degree='angle-degree', + milliwatt='power-milliwatt', + week='duration-week', + ampere='electric-ampere', + milliliter='volume-milliliter', + decimeter='length-decimeter', + fluid_ounce='volume-fluid-ounce', + nanosecond='duration-nanosecond', + foot='length-foot', + karat='proportion-karat', + year='duration-year', + gallon='volume-gallon', + radian='angle-radian', +) + +babel_forms = ['narrow', 'short', 'long'] + diff --git a/pint/compat/__init__.py b/pint/compat/__init__.py index 7442967..433c30d 100644 --- a/pint/compat/__init__.py +++ b/pint/compat/__init__.py @@ -127,3 +127,12 @@ except ImportError: ufloat = None HAS_UNCERTAINTIES = False +try: + from babel import Locale as Loc + HAS_BABEL = True + HAS_PROPER_BABEL = hasattr(Loc, 'unit_patterns') +except ImportError: + HAS_PROPER_BABEL = HAS_BABEL = False + +if not HAS_PROPER_BABEL: + Loc = None diff --git a/pint/formatting.py b/pint/formatting.py index 03e5acc..9e58ec8 100644 --- a/pint/formatting.py +++ b/pint/formatting.py @@ -114,6 +114,9 @@ def formatter(items, as_ratio=True, single_denominator=False, :param division_fmt: the format used for division. :param power_fmt: the format used for exponentiation. :param parentheses_fmt: the format used for parenthesis. + :param locale: the locale object as defined in babel. + :param form: the length of the translated unit, as defined in babel cldr. + :param plural_form: the plural form, calculated as defined in babel. :return: the formula as a string. """ @@ -129,7 +132,7 @@ def formatter(items, as_ratio=True, single_denominator=False, pos_terms, neg_terms = [], [] for key, value in sorted(items): - if locale and form and plural_form: + if locale and form and plural_form and hasattr(locale, 'unit_patterns'): _key = babel_units.get(key) if _key: patterns = locale.unit_patterns diff --git a/pint/testsuite/helpers.py b/pint/testsuite/helpers.py index 6b63d8f..a3455a5 100644 --- a/pint/testsuite/helpers.py +++ b/pint/testsuite/helpers.py @@ -7,7 +7,7 @@ import doctest from distutils.version import StrictVersion import re -from pint.compat import unittest, HAS_NUMPY, HAS_UNCERTAINTIES, NUMPY_VER, PYTHON3 +from pint.compat import unittest, HAS_NUMPY, HAS_PROPER_BABEL, HAS_UNCERTAINTIES, NUMPY_VER, PYTHON3 def requires_numpy18(): @@ -30,6 +30,10 @@ def requires_not_numpy(): return unittest.skipIf(HAS_NUMPY, 'Requires NumPy is not installed.') +def requires_proper_babel(): + return unittest.skipUnless(HAS_PROPER_BABEL, 'Requires Babel with units support') + + def requires_uncertainties(): return unittest.skipUnless(HAS_UNCERTAINTIES, 'Requires Uncertainties') diff --git a/pint/testsuite/test_babel.py b/pint/testsuite/test_babel.py new file mode 100644 index 0000000..ad10b0b --- /dev/null +++ b/pint/testsuite/test_babel.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +from __future__ import division, unicode_literals, print_function, absolute_import + +from pint.compat import Loc +from pint.testsuite import helpers, BaseTestCase +from pint import UnitRegistry + +class TestBabel(BaseTestCase): + + @helpers.requires_proper_babel() + def test_babel(self): + ureg = UnitRegistry() + ureg.load_definitions('../xtranslated.txt') + locale = Loc('fr', 'FR') + + distance = 24.0 * ureg.meter + self.assertEqual( + distance.format_babel(locale=locale, form='long'), + u'24.0 mètres' + ) + time = 8.0 * ureg.second + self.assertEqual( + time.format_babel(locale=locale, form='long'), + u'8.0 secondes' + ) + velocity = distance / time ** 2 + self.assertEqual( + velocity.format_babel(locale=locale, form='long'), + u'0.375 mètre par seconde²' + ) diff --git a/pint/xtranslated.txt b/pint/xtranslated.txt new file mode 100644 index 0000000..3ccfd9d --- /dev/null +++ b/pint/xtranslated.txt @@ -0,0 +1,26 @@ + +# a few unit definitions added to use the translations by unicode cldr + +dietary_calorie = 1000 * calorie = Calorie +metric_cup = liter / 4 +mps = meter / second +square_inch = inch ** 2 = sq_in +square_mile = mile ** 2 = sq_mile +square_meter = kilometer ** 2 = sq_m +square_kilometer = kilometer ** 2 = sq_km +mile_scandinavian = 10000 * meter +century = 100 * year +cubic_mile = 1 * mile ** 3 = cu_mile = cubic_miles +cubic_yard = 1 * yard ** 3 = cu_yd = cubic_yards +cubic_foot = 1 * foot ** 3 = cu_ft = cubic_feet +cubic_inch = 1 * inch ** 3 = cu_in = cubic_inches +cubic_meter = 1 * meter ** 3 = cu_m +cubic_kilometer = 1 * kilometer ** 3 = cu_km +karat = [purity] = Karat + +[consumption] = [volume] / [length] +liter_per_kilometer = liter / kilometer +liter_per_100kilometers = liter / (100 * kilometers) + +[US_consumption] = [length] / [volume] +MPG = mile / gallon From 5ec6c01c369563bc0f825a4d9a1fe8662f6d6c24 Mon Sep 17 00:00:00 2001 From: "alexbodn@gmail.com" Date: Fri, 25 Mar 2016 04:35:09 +0300 Subject: [PATCH 3/7] added translation of the measuring systems by locale. renamed the babel_units module to babel_names --- pint/{babel_units.py => babel_names.py} | 6 ++++++ pint/formatting.py | 2 +- pint/systems.py | 11 +++++++++++ pint/testsuite/test_babel.py | 9 ++++++++- 4 files changed, 26 insertions(+), 2 deletions(-) rename pint/{babel_units.py => babel_names.py} (98%) diff --git a/pint/babel_units.py b/pint/babel_names.py similarity index 98% rename from pint/babel_units.py rename to pint/babel_names.py index 71fe8dc..fba83b5 100644 --- a/pint/babel_units.py +++ b/pint/babel_names.py @@ -135,5 +135,11 @@ babel_units = dict( radian='angle-radian', ) +babel_systems = dict( + mks='metric', + imperial='uksystem', + US='ussystem', +) + babel_forms = ['narrow', 'short', 'long'] diff --git a/pint/formatting.py b/pint/formatting.py index 9e58ec8..6ff13de 100644 --- a/pint/formatting.py +++ b/pint/formatting.py @@ -13,7 +13,7 @@ from __future__ import division, unicode_literals, print_function, absolute_impo import re -from .babel_units import babel_units, babel_forms +from .babel_names import babel_units, babel_forms __JOIN_REG_EXP = re.compile("\{\d*\}") diff --git a/pint/systems.py b/pint/systems.py index 92e7560..43a7ecc 100644 --- a/pint/systems.py +++ b/pint/systems.py @@ -15,6 +15,7 @@ import re from .unit import Definition, UnitDefinition, DefinitionSyntaxError, RedefinitionError from .util import to_units_container, SharedRegistryObject, SourceIterator +from .babel_names import babel_systems class _Group(SharedRegistryObject): @@ -331,6 +332,16 @@ class _System(SharedRegistryObject): self.invalidate_members() + def format_babel(self, locale): + """translate the name of the system + + :type locale: Locale + """ + if locale and self.name in babel_systems: + name = babel_systems[self.name] + return locale.measurement_systems[name] + return self.name + @classmethod def from_lines(cls, lines, get_root_func): lines = SourceIterator(lines) diff --git a/pint/testsuite/test_babel.py b/pint/testsuite/test_babel.py index ad10b0b..8e3d152 100644 --- a/pint/testsuite/test_babel.py +++ b/pint/testsuite/test_babel.py @@ -4,13 +4,15 @@ from __future__ import division, unicode_literals, print_function, absolute_impo from pint.compat import Loc from pint.testsuite import helpers, BaseTestCase from pint import UnitRegistry +import os class TestBabel(BaseTestCase): @helpers.requires_proper_babel() def test_babel(self): ureg = UnitRegistry() - ureg.load_definitions('../xtranslated.txt') + dirname = os.path.dirname(__file__) + ureg.load_definitions(os.path.join(dirname, '../xtranslated.txt')) locale = Loc('fr', 'FR') distance = 24.0 * ureg.meter @@ -28,3 +30,8 @@ class TestBabel(BaseTestCase): velocity.format_babel(locale=locale, form='long'), u'0.375 mètre par seconde²' ) + mks = ureg.get_system('mks') + self.assertEqual( + mks.format_babel(locale=locale), + u'métrique' + ) From d32f4c07a4e2e4ffad8a21705f59cb979bb44de8 Mon Sep 17 00:00:00 2001 From: "alexbodn@gmail.com" Date: Fri, 25 Mar 2016 11:56:04 +0300 Subject: [PATCH 4/7] changed syntax in test_babel to hopefully overcome error in python 3.2 --- pint/testsuite/test_babel.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/pint/testsuite/test_babel.py b/pint/testsuite/test_babel.py index 8e3d152..d40b097 100644 --- a/pint/testsuite/test_babel.py +++ b/pint/testsuite/test_babel.py @@ -18,20 +18,16 @@ class TestBabel(BaseTestCase): distance = 24.0 * ureg.meter self.assertEqual( distance.format_babel(locale=locale, form='long'), - u'24.0 mètres' - ) + u'24.0 mètres') time = 8.0 * ureg.second self.assertEqual( time.format_babel(locale=locale, form='long'), - u'8.0 secondes' - ) + u'8.0 secondes') velocity = distance / time ** 2 self.assertEqual( velocity.format_babel(locale=locale, form='long'), - u'0.375 mètre par seconde²' - ) + u'0.375 mètre par seconde²') mks = ureg.get_system('mks') self.assertEqual( mks.format_babel(locale=locale), - u'métrique' - ) + u'métrique') From 12b3d9f8f1deaeb50616158c65e6acdfb4520d3f Mon Sep 17 00:00:00 2001 From: "alexbodn@gmail.com" Date: Fri, 25 Mar 2016 12:13:14 +0300 Subject: [PATCH 5/7] another attempt to overcome syntax error with python 3.2 on travis --- pint/testsuite/test_babel.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pint/testsuite/test_babel.py b/pint/testsuite/test_babel.py index d40b097..8e86820 100644 --- a/pint/testsuite/test_babel.py +++ b/pint/testsuite/test_babel.py @@ -18,16 +18,16 @@ class TestBabel(BaseTestCase): distance = 24.0 * ureg.meter self.assertEqual( distance.format_babel(locale=locale, form='long'), - u'24.0 mètres') + u"24.0 mètres") time = 8.0 * ureg.second self.assertEqual( time.format_babel(locale=locale, form='long'), - u'8.0 secondes') + u"8.0 secondes") velocity = distance / time ** 2 self.assertEqual( velocity.format_babel(locale=locale, form='long'), - u'0.375 mètre par seconde²') + u"0.375 mètre par seconde²") mks = ureg.get_system('mks') self.assertEqual( mks.format_babel(locale=locale), - u'métrique') + u"métrique") From 79d446a116a23aea8cda676f5dbe390ab7dc283a Mon Sep 17 00:00:00 2001 From: "alexbodn@gmail.com" Date: Fri, 25 Mar 2016 12:28:33 +0300 Subject: [PATCH 6/7] another attempt to overcome syntax error with python 3.2 on travis --- pint/testsuite/test_babel.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pint/testsuite/test_babel.py b/pint/testsuite/test_babel.py index 8e86820..f85e163 100644 --- a/pint/testsuite/test_babel.py +++ b/pint/testsuite/test_babel.py @@ -18,16 +18,20 @@ class TestBabel(BaseTestCase): distance = 24.0 * ureg.meter self.assertEqual( distance.format_babel(locale=locale, form='long'), - u"24.0 mètres") + "24.0 mètres" + ) time = 8.0 * ureg.second self.assertEqual( time.format_babel(locale=locale, form='long'), - u"8.0 secondes") + "8.0 secondes" + ) velocity = distance / time ** 2 self.assertEqual( velocity.format_babel(locale=locale, form='long'), - u"0.375 mètre par seconde²") + "0.375 mètre par seconde²" + ) mks = ureg.get_system('mks') self.assertEqual( mks.format_babel(locale=locale), - u"métrique") + "métrique" + ) From 26dd4baf47ed7753b2843a7e4eed46d1ea25880a Mon Sep 17 00:00:00 2001 From: "alexbodn@gmail.com" Date: Sat, 6 Aug 2016 15:42:31 +0300 Subject: [PATCH 7/7] adjusted to cope with upstream babel tip --- pint/babel_names.py | 11 +++++--- pint/compat/__init__.py | 5 ++-- pint/formatting.py | 49 ++++++++++++++++++++---------------- pint/quantity.py | 6 ++++- pint/systems.py | 8 +++--- pint/testsuite/test_babel.py | 16 ++++++------ 6 files changed, 57 insertions(+), 38 deletions(-) diff --git a/pint/babel_names.py b/pint/babel_names.py index fba83b5..45601bd 100644 --- a/pint/babel_names.py +++ b/pint/babel_names.py @@ -7,7 +7,9 @@ :license: BSD, see LICENSE for more details. """ -babel_units = dict( +from pint.compat import HAS_PROPER_BABEL + +_babel_units = dict( standard_gravity='acceleration-g-force', millibar='pressure-millibar', metric_ton='mass-metric-ton', @@ -135,11 +137,14 @@ babel_units = dict( radian='angle-radian', ) -babel_systems = dict( +if not HAS_PROPER_BABEL: + _babel_units = dict() + +_babel_systems = dict( mks='metric', imperial='uksystem', US='ussystem', ) -babel_forms = ['narrow', 'short', 'long'] +_babel_lengths = ['narrow', 'short', 'long'] diff --git a/pint/compat/__init__.py b/pint/compat/__init__.py index 433c30d..74a357e 100644 --- a/pint/compat/__init__.py +++ b/pint/compat/__init__.py @@ -129,10 +129,11 @@ except ImportError: try: from babel import Locale as Loc + from babel import units as babel_units HAS_BABEL = True - HAS_PROPER_BABEL = hasattr(Loc, 'unit_patterns') + HAS_PROPER_BABEL = hasattr(babel_units, 'format_unit') except ImportError: HAS_PROPER_BABEL = HAS_BABEL = False if not HAS_PROPER_BABEL: - Loc = None + Loc = babel_units = None diff --git a/pint/formatting.py b/pint/formatting.py index 6ff13de..11940ac 100644 --- a/pint/formatting.py +++ b/pint/formatting.py @@ -13,7 +13,8 @@ from __future__ import division, unicode_literals, print_function, absolute_impo import re -from .babel_names import babel_units, babel_forms +from .babel_names import _babel_units, _babel_lengths +from pint.compat import babel_units, Loc __JOIN_REG_EXP = re.compile("\{\d*\}") @@ -103,7 +104,7 @@ _FORMATS = { def formatter(items, as_ratio=True, single_denominator=False, product_fmt=' * ', division_fmt=' / ', power_fmt='{0} ** {1}', parentheses_fmt='({0})', exp_call=lambda x: '{0:n}'.format(x), - locale=None, form='long', plural_form='one'): + locale=None, babel_length='long', babel_plural_form='one'): """Format a list of (name, exponent) pairs. :param items: a list of (name, exponent) pairs. @@ -115,8 +116,8 @@ def formatter(items, as_ratio=True, single_denominator=False, :param power_fmt: the format used for exponentiation. :param parentheses_fmt: the format used for parenthesis. :param locale: the locale object as defined in babel. - :param form: the length of the translated unit, as defined in babel cldr. - :param plural_form: the plural form, calculated as defined in babel. + :param babel_length: the length of the translated unit, as defined in babel cldr. + :param babel_plural_form: the plural form, calculated as defined in babel. :return: the formula as a string. """ @@ -132,24 +133,28 @@ def formatter(items, as_ratio=True, single_denominator=False, pos_terms, neg_terms = [], [] for key, value in sorted(items): - if locale and form and plural_form and hasattr(locale, 'unit_patterns'): - _key = babel_units.get(key) - if _key: - patterns = locale.unit_patterns - for _form in [form] + babel_forms: - pattern_key = _key + ':' + _form - if pattern_key in patterns: - plural = plural_form - if value <= 0: - plural = 'one' - pattern = patterns[pattern_key][plural] - key = pattern.replace('{0}', '').strip() - break - per_form = 'compound:per:' + form - if per_form in patterns: - division_fmt = patterns[per_form] - power_fmt = '{0}{1}' - exp_call = _pretty_fmt_exponent + if locale and babel_length and babel_plural_form and key in _babel_units: + _key = _babel_units[key] + locale = Loc.parse(locale) + unit_patterns = locale._data['unit_patterns'] + compound_unit_patterns = locale._data["compound_unit_patterns"] + plural = 'one' if abs(value) <= 0 else babel_plural_form + if babel_length not in _babel_lengths: + other_lengths = [ + _babel_length for _babel_length in reversed(_babel_lengths) \ + if babel_length != _babel_length + ] + else: + other_lengths = [] + for _babel_length in [babel_length] + other_lengths: + pat = unit_patterns.get(_key, {}).get(_babel_length, {}).get(plural) + print(plural, _babel_length, pat) + if pat is not None: + key = pat.replace('{0}', '').strip() + break + division_fmt = compound_unit_patterns.get("per", {}).get(babel_length, division_fmt) + power_fmt = '{0}{1}' + exp_call = _pretty_fmt_exponent if value == 1: pos_terms.append(key) elif value > 0: diff --git a/pint/quantity.py b/pint/quantity.py index 0d24e51..bfafe65 100644 --- a/pint/quantity.py +++ b/pint/quantity.py @@ -23,6 +23,7 @@ from .compat import string_types, ndarray, np, _to_magnitude, long_type from .util import (logger, UnitsContainer, SharedRegistryObject, to_units_container, infer_base_unit, fix_str_conversions) +from pint.compat import Loc def _eq(first, second, check_all): @@ -151,7 +152,10 @@ class _Quantity(SharedRegistryObject): else: obj = self kwspec = dict(kwspec) - kwspec['plural_form'] = kwspec['locale'].plural_form(obj.magnitude) + if 'length' in kwspec: + kwspec['babel_length'] = kwspec.pop('length') + kwspec['locale'] = Loc.parse(kwspec['locale']) + kwspec['babel_plural_form'] = kwspec['locale'].plural_form(obj.magnitude) return '{0} {1}'.format( format(obj.magnitude, remove_custom_flags(spec)), obj.units.format_babel(spec, **kwspec)).replace('\n', '') diff --git a/pint/systems.py b/pint/systems.py index 43a7ecc..95ea978 100644 --- a/pint/systems.py +++ b/pint/systems.py @@ -15,7 +15,8 @@ import re from .unit import Definition, UnitDefinition, DefinitionSyntaxError, RedefinitionError from .util import to_units_container, SharedRegistryObject, SourceIterator -from .babel_names import babel_systems +from .babel_names import _babel_systems +from pint.compat import Loc class _Group(SharedRegistryObject): @@ -337,8 +338,9 @@ class _System(SharedRegistryObject): :type locale: Locale """ - if locale and self.name in babel_systems: - name = babel_systems[self.name] + if locale and self.name in _babel_systems: + name = _babel_systems[self.name] + locale = Loc.parse(locale) return locale.measurement_systems[name] return self.name diff --git a/pint/testsuite/test_babel.py b/pint/testsuite/test_babel.py index f85e163..0194977 100644 --- a/pint/testsuite/test_babel.py +++ b/pint/testsuite/test_babel.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- from __future__ import division, unicode_literals, print_function, absolute_import -from pint.compat import Loc from pint.testsuite import helpers, BaseTestCase from pint import UnitRegistry import os @@ -13,25 +12,28 @@ class TestBabel(BaseTestCase): ureg = UnitRegistry() dirname = os.path.dirname(__file__) ureg.load_definitions(os.path.join(dirname, '../xtranslated.txt')) - locale = Loc('fr', 'FR') distance = 24.0 * ureg.meter self.assertEqual( - distance.format_babel(locale=locale, form='long'), + distance.format_babel(locale='fr_FR', length='long'), "24.0 mètres" ) time = 8.0 * ureg.second self.assertEqual( - time.format_babel(locale=locale, form='long'), + time.format_babel(locale='fr_FR', length='long'), "8.0 secondes" ) - velocity = distance / time ** 2 self.assertEqual( - velocity.format_babel(locale=locale, form='long'), + time.format_babel(locale='ro', length='short'), + "8.0 s" + ) + acceleration = distance / time ** 2 + self.assertEqual( + acceleration.format_babel(locale='fr_FR', length='long'), "0.375 mètre par seconde²" ) mks = ureg.get_system('mks') self.assertEqual( - mks.format_babel(locale=locale), + mks.format_babel(locale='fr_FR'), "métrique" )