summaryrefslogtreecommitdiff
path: root/babel
diff options
context:
space:
mode:
authorKevin Deldycke <kevin@deldycke.com>2017-04-07 16:09:14 +0200
committerKevin Deldycke <kevin@deldycke.com>2017-05-17 18:00:10 +0200
commitd97de31509f3394e72a8dfc066cf77aabe2e1122 (patch)
treeef739144270ee46a869bf979dce6e68962820f66 /babel
parent23adb4fac4d8f5a84ecd13cb2cb10727da9e8cf3 (diff)
downloadbabel-d97de31509f3394e72a8dfc066cf77aabe2e1122.tar.gz
Add currency utilities and helpers.
Diffstat (limited to 'babel')
-rw-r--r--babel/core.py1
-rw-r--r--babel/numbers.py109
2 files changed, 98 insertions, 12 deletions
diff --git a/babel/core.py b/babel/core.py
index bb59b74..5140f49 100644
--- a/babel/core.py
+++ b/babel/core.py
@@ -45,6 +45,7 @@ def get_global(key):
The keys available are:
+ - ``all_currencies``
- ``currency_fractions``
- ``language_aliases``
- ``likely_subtags``
diff --git a/babel/numbers.py b/babel/numbers.py
index a5d30ef..9a60d72 100644
--- a/babel/numbers.py
+++ b/babel/numbers.py
@@ -20,14 +20,89 @@
# - http://www.unicode.org/reports/tr35/ (Appendix G.6)
import re
from datetime import date as date_, datetime as datetime_
+from itertools import chain
from babel.core import default_locale, Locale, get_global
-from babel._compat import decimal
+from babel._compat import decimal, string_types
+from babel.localedata import locale_identifiers
LC_NUMERIC = default_locale('LC_NUMERIC')
+class UnknownCurrencyError(Exception):
+ """Exception thrown when a currency is requested for which no data is available.
+ """
+
+ def __init__(self, identifier):
+ """Create the exception.
+ :param identifier: the identifier string of the unsupported currency
+ """
+ Exception.__init__(self, 'Unknown currency %r.' % identifier)
+
+ #: The identifier of the locale that could not be found.
+ self.identifier = identifier
+
+
+def list_currencies(locale=None):
+ """ Return a `set` of normalized currency codes.
+
+ .. versionadded:: 2.5.0
+
+ :param locale: filters returned currency codes by the provided locale.
+ Expected to be a locale instance or code. If no locale is
+ provided, returns the list of all currencies from all
+ locales.
+ """
+ # Get locale-scoped currencies.
+ if locale:
+ currencies = Locale.parse(locale).currencies.keys()
+ else:
+ currencies = get_global('all_currencies')
+ return set(currencies)
+
+
+def validate_currency(currency, locale=None):
+ """ Check the currency code is recognized by Babel.
+
+ Accepts a ``locale`` parameter for fined-grained validation, working as
+ the one defined above in ``list_currencies()`` method.
+
+ Raises a `ValueError` exception if the currency is unknown to Babel.
+ """
+ if currency not in list_currencies(locale):
+ raise UnknownCurrencyError(currency)
+
+
+def is_currency(currency, locale=None):
+ """ Returns `True` only if a currency is recognized by Babel.
+
+ This method always return a Boolean and never raise.
+ """
+ if not currency or not isinstance(currency, string_types):
+ return False
+ try:
+ validate_currency(currency, locale)
+ except UnknownCurrencyError:
+ return False
+ return True
+
+
+def normalize_currency(currency, locale=None):
+ """Returns the normalized sting of any currency code.
+
+ Accepts a ``locale`` parameter for fined-grained validation, working as
+ the one defined above in ``list_currencies()`` method.
+
+ Returns None if the currency is unknown to Babel.
+ """
+ if isinstance(currency, string_types):
+ currency = currency.upper()
+ if not is_currency(currency, locale):
+ return
+ return currency
+
+
def get_currency_name(currency, count=None, locale=LC_NUMERIC):
"""Return the name used by the locale for the specified currency.
@@ -36,10 +111,10 @@ def get_currency_name(currency, count=None, locale=LC_NUMERIC):
.. versionadded:: 0.9.4
- :param currency: the currency code
+ :param currency: the currency code.
:param count: the optional count. If provided the currency name
will be pluralized to that number if possible.
- :param locale: the `Locale` object or locale identifier
+ :param locale: the `Locale` object or locale identifier.
"""
loc = Locale.parse(locale)
if count is not None:
@@ -56,12 +131,26 @@ def get_currency_symbol(currency, locale=LC_NUMERIC):
>>> get_currency_symbol('USD', locale='en_US')
u'$'
- :param currency: the currency code
- :param locale: the `Locale` object or locale identifier
+ :param currency: the currency code.
+ :param locale: the `Locale` object or locale identifier.
"""
return Locale.parse(locale).currency_symbols.get(currency, currency)
+def get_currency_precision(currency):
+ """Return currency's precision.
+
+ Precision is the number of decimals found after the decimal point in the
+ currency's format pattern.
+
+ .. versionadded:: 2.5.0
+
+ :param currency: the currency code.
+ """
+ precisions = get_global('currency_fractions')
+ return precisions.get(currency, precisions['DEFAULT'])[0]
+
+
def get_territory_currencies(territory, start_date=None, end_date=None,
tender=True, non_tender=False,
include_details=False):
@@ -102,7 +191,7 @@ def get_territory_currencies(territory, start_date=None, end_date=None,
.. versionadded:: 2.0
- :param territory: the name of the territory to find the currency fo
+ :param territory: the name of the territory to find the currency for.
:param start_date: the start date. If not given today is assumed.
:param end_date: the end date. If not given the start date is assumed.
:param tender: controls whether tender currencies should be included.
@@ -326,12 +415,8 @@ def format_currency(number, currency, format=None, locale=LC_NUMERIC,
raise UnknownCurrencyFormatError("%r is not a known currency format"
" type" % format_type)
if currency_digits:
- fractions = get_global('currency_fractions')
- try:
- digits = fractions[currency][0]
- except KeyError:
- digits = fractions['DEFAULT'][0]
- frac = (digits, digits)
+ precision = get_currency_precision(currency)
+ frac = (precision, precision)
else:
frac = None
return pattern.apply(number, locale, currency=currency, force_frac=frac)