ZF-8806: Problem using Zend_Currency with setlocale()

Description

We have a problem with Zend_Currency getting the right format. The code below describe our problem:

<?php
setlocale(LC_ALL, 'de_DE');
$locale   = new Zend_Locale('de_DE');
$currency = new Zend_Currency(null, $locale);
echo $currency->toCurrency(25);

The toCurrency() function echo's: 250,00 € instead of 25,00 €. If you comment out setLocale(LC_ALL, 'de_DE'), everything work as aspected. We use this script on a debian machine with PHP 5.2.6 and Zend Framework 1.8.5 and the language-package 'de_DE' (ISO-8859-1) installed.

Comments

On trunk I get '25,00 €'. Could you please test if this issue also occurs on trunk?

We used the actual trunk and the problem still exists.

If your have

Are you sure that your language-pack 'de_DE' is ISO-8859-1 and not UTF-8 and is installed on your server? Otherwise the php-internal function setlocale() would return false and the code will work as aspected.

Thomas is German (or somewhere close to Germany), I will leave that up to him as he has probably installed those already.

You should note that Zend_Currency and it's dependend classes don't use setlocale().

And it should also be noted that the value "25" will never return 250... this could only occur when you give "25.0" and the value does not match the locale.

Btw: You are using 1.8.5... As I remember Zend_Locale had a bug in past according to extended locales which has been fixed several months ago.

Something like "de-DE@ISO-8859-1,Latn" would not have worked in such an installation when you use automatic detection of locales. There was no problem when you set the locale manually.

I also have this issue. I have a system using the Zend_Currency class. If I set the locale to en_GB and use Zend_Currency to display a number (say 500) it will return £500.00 as expected. If however I set the locale to de_DE it returns 5.000,00. I have tracked down what's happening, and the cause of the problem is in the Zend_Locale_Format::toNumber() method which is called in line 150 of Zend_Currency::toCurrency() (v1.9.7). I have also identified the issue as existing in v1.8.2 and 1.8.5.

Example 1 - passing in a $valueof (string)500.00 In this example, the value being passed to is Zend_Locale_Format::toNumber() in my example is a string '500.00'. (note that I've used a '.' for the decimal despite the locale being de_DE - should be a ','. The rest of my description takes place in Zend_Locale_Format::toNumber() and any line numbers refer to that method in v1.9.7.

line 295 normalises the $value, which essentially removes the thousand separator and any + sign, and this is the problem. Since I'm passing in an english formed number and the locale is de_DE, it strips out the de_DE thousand sep (a .) changing my $value from 500.00 to 50000, and this is the amount it returns as $value.

line 340 runs Zend_Locale_Math::round() which works fine, changing my $value from 50000 to 50000.0. No value change there, and it's still a string (which is fine).

line 361 runs the normalise method again, this time repeating the same issue as before - stripping out the de_DE thousand separator (a '.') thus changing my $value from 50000.0 to 500000.

Example 2 - passing in a $value of (int)500 In this example the $value being passed to toNumber() is an int. We are still using a locale of de_DE.

This time the line 295 returns a string '500' which is fine. There were no thousand or decimal characters to get confused.

Line 340 runs as expected, changing the string from '500' to '500.0' (again, note the '.' not ',', even though our locale is de_DE which uses ',' for decimal and '.' for the thousand seperator).

Line 361 runs again, and deletes the thousand seperator from the locale data (the '.') changing my number from 500.0 to 5000, and that is what gets returned.

Example 3 - passing in a $value of (double)1500.50 This causes the same issues as Example 2, except this time line 340 round() changes my converted string of '1500.5' to '15005.0'. I'll let you debug it to work out why.

Looking through, it seems that the Zend_Locale_Math::normalize() method is assuming that it is getting an int or double type value, which actually works fine. The problem is that there is no validation on this, and if you pass it a string everything goes wrong. This issue is resolved if you convert the $value being passed to the normalize method to either an int or double before running it through - the system takes care of things for you at that point, although I am still testing this out as a solution.

This is a pretty critical issue for us: we run a browser based app with a large finance componant and as we are moving into Europe we are finding that this is making some parts of our system totally unuseable. I'm kind of suprised that this hasnt come up before!

Ok - so the box is a CentOS 5.4 server running latest build of Zend Core. The linux locale is set to en_GB.UTF-8 just incase that makes a difference.

Thanks for this detailed report.

I'll try to reproduce this in my envirinments with the given description... when I need additional infos I'll come back to you.

Related to your example 1: When you define that your input IS german but give a english string then there is no issue at all.

In german the string "500.00" is like giving the integer 50000.

So your output of 50000 IS correct. When you would not have given a string, but a integer or a float then the things are different again.

Related to your example 2: I can not verify this behaviour. Line 340 detects the point by strpos. Line 361 is a bracket. But Line 373 localizes the previous normalized value. The value itself is build later.

Related to your example 3: I can also not verify this behaviour.

As the given linenumbers and also the described results are not reproducable with the actual release I think that this already has been fixed in past.

Try the actual 1.10RC... several changes which have been done in the last months are integrated in this release. I expect that this issue has already be fixed in past.

Closing as fixed for 1.10