Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Converting money to localized string fails in german locale. #30114

Closed
@addiks

Description

@addiks

Symfony version(s) affected: I'm using 2.8.34
(but the problematic code is also in the master branch, so all versions should be affected)

Description
In PHP, when converting a float value to string, the character used as a decimal-seperator depends on the locale. In an english locale, the full-stop "." is used ("123.45") while in german (f.e.) the comma "," is used ("123,45"). The PHP internal function "is_numeric" does not recognize the comma and would only consider the first representation ("123.45") as a number, but not the second one:

php > setlocale(LC_ALL, "de_DE.UTF-8");
php > var_dump(12 / 34);
float(0,35294117647059)

php > setlocale(LC_ALL, "en_US.UTF-8");
php > var_dump(12 / 34);
float(0.35294117647059)

php > var_dump(is_numeric("0.35294117647059"));
bool(true)
php > var_dump(is_numeric("0,35294117647059"));
bool(false)

php > setlocale(LC_ALL, "de_DE.UTF-8");
php > var_dump(is_numeric((string)(12 / 34)));
bool(false)

php > setlocale(LC_ALL, "en_US.UTF-8");
php > var_dump(is_numeric((string)(12 / 34)));
bool(true)

There is at least one piece of code in symfony where this behaviour leads to code breaking in certain locales and not in others. There are probably more but i have not checked yet. I have just stumbled over one such code in the MoneyToLocalizedStringTransformer on line 61 and NumberToLocalizedStringTransformer on line 113:

    public function transform($value)
    {
        if (null !== $value && 1 !== $this->divisor) {
            ...
            $value = (string) ($value / $this->divisor);
        }

        return parent::transform($value);
    }

In an english locale, $value contains something like "123.45", which passes the is_number check.
In an german locale, $value contains something like "123,45", which fails the is_number check.

The call "parent::transform($value)" from above directly calls the code below:

    public function transform($value)
    {
        ...
        if (!is_numeric($value)) {
            throw new TransformationFailedException('Expected a numeric.');
        }
        ...

This piece of code will now fail in a german locale and pass in an english one.

How to reproduce
Switch to a german locale ("de_DE.UTF-8") and use the Money transformers. I do with the Tbbc\MoneyBundle, but the problematic code is in symfony (see above) and not that bundle.

Possible Solution
I don't know if that is the best solution, but a simple "$value = str_replace(',', '.', $value);" in the MoneyToLocalizedStringTransformer directly after calculating and string-casting the $value should do the trick at least for me; I'm not sure about other locales. Maybe more research is required here for a better solution. This works for me:

    public function transform($value)
    {
        if (null !== $value && 1 !== $this->divisor) {
            ...
            $value = (string) ($value / $this->divisor);
            $value = str_replace(",", ".", $value);
        }

        return parent::transform($value);
    }

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions