Opened 11 years ago

Closed 4 years ago

Last modified 3 years ago

#20601 closed New feature (fixed)

Incorrect separators when chaining floatformat to intcomma in some locales

Reported by: c.schmitt@… Owned by: Jacob Walls
Component: Template system Version: 4.0
Severity: Normal Keywords: internationalization, humanize, contrib, float, floatcomma
Cc: merb, Alexey, Cassiano R. N. dos Santos Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

When u use floatvalue "2" and intcomma together in a template the output of intcomma won't be internationalized.

Since intcomma wont work with decimals in django 1.5.1 i tried to convert a decimal to a float in a template, but it wont give me the excepted output.
When i have the value of 1000.11 it should be 1000,11 in germany, with intcomma(float(1000,11)) i get 1.000,11. But when i use Decimal(1000,11)|floatvalue"2"|intcomma, i will get 1,000,11. Thats a bug or maybe an unwanted behavior.

Change History (28)

comment:2 by merb, 11 years ago

Cc: merb added

comment:3 by merb, 11 years ago

Owner: changed from nobody to merb
Status: newassigned

comment:4 by merb, 11 years ago

Summary: intcomma and floatvalue internationalization errorintcomma and floatformat internationalization error

in reply to:  description comment:5 by merb, 11 years ago

Replying to c.schmitt@…:

When u use floatvalue "2" and intcomma together in a template the output of intcomma won't be internationalized.

Since intcomma wont work with decimals in django 1.5.1 i tried to convert a decimal to a float in a template, but it wont give me the excepted output.
When i have the value of 1000.11 it should be 1000,11 in germany, with intcomma(float(1000,11)) i get 1.000,11. But when i use Decimal(1000,11)|floatvalue"2"|intcomma, i will get 1,000,11. Thats a bug or maybe an unwanted behavior.

i meant floatformat not floatvalue.

comment:6 by merb, 11 years ago

This is the actual humanize function:

@register.filter(is_safe=True)
def intcomma(value, use_l10n=True):
    """
    Converts an integer to a string containing commas every three digits.
    For example, 3000 becomes '3,000' and 45000 becomes '45,000'.
    """
    if settings.USE_L10N and use_l10n:
        try:
            if not isinstance(value, float):
                value = int(value)
        except (TypeError, ValueError):
            return intcomma(value, False)
        else:
            return number_format(value, force_grouping=True)
    orig = force_text(value)
    new = re.sub("^(-?\d+)(\d{3})", '\g<1>,\g<2>', orig)
    if orig == new:
        return new
    else:
        return intcomma(new, use_l10n)

The problem is that floatformat returns a SafeText type, so it isn't a instance of float and intcomma gets recalled with use_l10n=False. So this line gets called:

    new = re.sub("^(-?\d+)(\d{3})", '\g<1>,\g<2>', orig)

This line will add an , to every 3rd place on the SafeText type so a string of:
1000,11 or 1000000,11 will be
1,000,11 or 1,000,000,11 which could get converted to an integer, so the next time intcomma gets called, it thinks that the SafeText type could now get converted to an integer and will then return himself.
So this will be an uncorrect return value

Version 0, edited 11 years ago by merb (next)

comment:7 by merb, 11 years ago

Type: Cleanup/optimizationBug

comment:8 by merb, 11 years ago

Owner: merb removed
Status: assignednew

comment:9 by Claude Paroz, 11 years ago

Component: Python 2Template system
Triage Stage: UnreviewedAccepted
Type: BugNew feature

Basically, the issue is that both intcomma and floatformat take numbers (or number-convertible values) to output a string representation. So chaining them is basically broken. One strategy would be to try harder to make those string representation transformed back to numbers, but then the first transformation would be lost.

I'm of the opinion that either one of these filters should be able to do all necessary transformations. For example, we could imagine that the floatformat argument takes an optional g suffix to indicate grouping. So the result of Decimal(1000,11)|floatvalue:"2g" would be 1.000,11 for German (i.e. numberformat would be passed force_grouping=True in floatformat filter).

comment:10 by merb, 11 years ago

wouldn't it be better to make them type safe? Like raise an error if intcomma or floatformat won't get 'numbers' or 'localized_numbers'?

comment:11 by Petr Dlouhý, 8 years ago

Version: 1.51.10

This is still problem in Django 1.10.

comment:12 by Alexey, 7 years ago

Cc: Alexey added

comment:13 by Alexey, 7 years ago

Should we try to parse value to float?

Something like try to find float separator with regex, and convert it before isinstance check

comment:14 by Ronny Vedrilla, 5 years ago

For the specific German case a fix:

'|'.join(intcomma(floatformat(amount, 2)).rsplit(',', 1)).replace(',', '.').replace('|', ',')

Maybe this helps anybody...

In my opinion a currency-filter with optional thousand separators would solve this issue for most people.

Last edited 5 years ago by Ronny Vedrilla (previous) (diff)

comment:15 by Mariusz Felisiak, 5 years ago

Has patch: set
Owner: set to Ram Parameswaran
Status: newassigned
Version: 1.10master

comment:16 by Claude Paroz, 5 years ago

Patch needs improvement: set

comment:17 by Jacob Walls, 4 years ago

Owner: changed from Ram Parameswaran to Jacob Walls

comment:18 by Jacob Walls, 4 years ago

Patch needs improvement: unset
Summary: intcomma and floatformat internationalization errorIncorrect separators when chaining floatvalue to intcomma in some locales

comment:19 by Mariusz Felisiak, 4 years ago

Patch needs improvement: set

comment:20 by Jacob Walls, 4 years ago

Patch needs improvement: unset

Addressed reviews.

comment:21 by Mariusz Felisiak, 4 years ago

Triage Stage: AcceptedReady for checkin

comment:22 by Jacob Walls, 4 years ago

Summary: Incorrect separators when chaining floatvalue to intcomma in some localesIncorrect separators when chaining floatformat to intcomma in some locales

comment:23 by Mariusz Felisiak <felisiak.mariusz@…>, 4 years ago

Resolution: fixed
Status: assignedclosed

In ac6c426:

Fixed #20601 -- Allowed forcing format with thousand separators in floatformat filter.

Thanks Claude Paroz and Nick Pope for reviews.

comment:24 by Cassiano R. N. dos Santos, 3 years ago

Is this fixed for Django 2.2?

Doing

{{ variable | intcomma | floatformat:2 }}

or

{{ variable | floatformat:2 | intcomma }}

does not work.

In the first case results broken, and in second, gives, e.g. 1,000,00 when the correct would be 1.000,00 for pt-BR (brazilian portuguese).

Last edited 3 years ago by Cassiano R. N. dos Santos (previous) (diff)

in reply to:  24 comment:25 by Mariusz Felisiak, 3 years ago

Replying to Cassiano R. N. dos Santos:

Is this fixed for Django 2.2?

No, it's fixed in Django 3.2+.

comment:26 by Cassiano R. N. dos Santos, 3 years ago

Cc: Cassiano R. N. dos Santos added
Has patch: unset
Type: New featureBug
Version: dev4.0

I'm just trying the fix in Django 4.0.1 with both

{{ variable | intcomma | floatformat:2 }}  {# breaks template #}

and

{{ variable | floatformat:2 | intcomma }}  {# results i.e., 10,123,00 (would be 10.123,00) #}

But the bug persists.

In my settings.py:

LANGUAGE_CODE = 'pt-BR'
USE_I18N = True
USE_L10N = True

in reply to:  26 comment:27 by Mariusz Felisiak, 3 years ago

Has patch: set
Type: BugNew feature

Replying to Cassiano R. N. dos Santos:

But the bug persists.

I don't see the "g" suffix in your example. Also in Django 4.0 the floatformat template filter no longer depends on the USE_L10N setting and always returns localized output. Have you tried {{ variable | floatformat:"2g" }}?

comment:28 by Cassiano R. N. dos Santos, 3 years ago

Oh, that was it. It was missing the "2g". Thank you very much.

Note: See TracTickets for help on using tickets.
Back to Top