7

I'd like to change the thousands separator such that {:,}.format(1234) in Python uses a different character. The separator should be '\u066c'.

How can I set this without affecting any other locals settings?

EDIT: Any other suggestion for a unimposing separator viable in a fixed with font is welcome!

Gere
  • 12,075
  • 18
  • 62
  • 94
  • See [this very similar question](http://stackoverflow.com/questions/5513615/add-decimal-mark-thousands-separators-to-a-number) and [PEP 378 - Format Specifier for Thousands Separator](https://www.python.org/dev/peps/pep-0378/) – smci Oct 20 '16 at 12:55

2 Answers2

9

Your options are to either take the , formatted output and replace the commas, switch locales and use the 'n' number format (which will format the number based on the current locale), or use a third party library like babel. The latter gives you full locale control over number formatting, for example, provided there is a locale that uses U+066C as the thousands separator.

With the format() function, the first option is quite straight-forward really:

>>> format(1234, ',').replace(',', '\u066c')
'1٬234'

I have yet to find a locale that directly would use \u066c for Western Arabic numerals however; U+066C is commonly used only with Eastern Arabic numerals instead. Babel doesn't include any such locale data, at least.

You can pass any babel Locale object in to the babel.numbers.format_number() function, so if you need a custom separator you can clone an existing locale and set the Locale.number_symbols['group'] value:

from copy import deepcopy
from babel import Locale

us_locale = Locale('en', 'US')
base_locale.number_symbols   # ensure instance has been populated
altered_locale = deepcopy(us_locale)
altered_locale.number_symbols['group'] = '\u066c'

Note that you have to access an attribute (or the ._data property) to trigger loading the locale configuration, before copying. Otherwise, the data between the original (source) locale and the altered locale will be shared (so the us_locale object in my snippet above would have the same number separator.

Using the altered_locale object now results in the expected output:

>>> from babel.numbers import format_number
>>> format_number(1234, locale=altered_locale)
'1٬234'
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • 1
    Regarding the cloning of the `Locale`, you need to access one of the `Locale`'s properties before deep copying, otherwise you will overwrite the base locale. `base_locale = Locale("en", "US")` then just acces a property `base_locale.number_symbols`, this loads the data, and then copy `altered_locale = deepcopy(base_locale)` then you can alter the locale withouth influencing any other locales. – Villiers Strauss Aug 26 '20 at 10:50
  • Careful! I downvoted this answer because the deepcopy operation does not work as expected. See Viliers's comment just above that explains the problem – Edward Gaere Feb 07 '22 at 14:51
  • @VilliersStraus ah, indeed, I've updated my answer to avoid that issue. – Martijn Pieters Feb 07 '22 at 15:04
  • @EdwardGaere: I've fixed that issue now. – Martijn Pieters Feb 07 '22 at 15:04
3

Taking Martijn's excellent answer further for use in Django, on how to override a locale in a Django template.

File myapp/templatetags/extra.py

from django import template
from django.utils.translation import get_language
from copy import deepcopy
from babel import Locale
from babel.numbers import format_number

register = template.Library()

@register.filter
def currency(value):
    lang = get_language()
    locale = deepcopy(Locale(lang))
    if lang == 'ru':  # for example if russian
        locale.number_symbols['group'] = '.'
    return format_number(value, locale=locale)

In template:

{% load extra %}
{{ price|currency }}
Wtower
  • 18,848
  • 11
  • 103
  • 80