diff options
author | Kevin Deldycke <kevin@deldycke.com> | 2017-04-07 16:09:14 +0200 |
---|---|---|
committer | Kevin Deldycke <kevin@deldycke.com> | 2017-05-17 18:00:10 +0200 |
commit | d97de31509f3394e72a8dfc066cf77aabe2e1122 (patch) | |
tree | ef739144270ee46a869bf979dce6e68962820f66 /babel | |
parent | 23adb4fac4d8f5a84ecd13cb2cb10727da9e8cf3 (diff) | |
download | babel-d97de31509f3394e72a8dfc066cf77aabe2e1122.tar.gz |
Add currency utilities and helpers.
Diffstat (limited to 'babel')
-rw-r--r-- | babel/core.py | 1 | ||||
-rw-r--r-- | babel/numbers.py | 109 |
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) |