I have a monetary amount that is entered by a user and want to validate it. I have written a JSF validator but am having trouble getting this to work in all circumstances. Here is my scenario:
I have users in different Locales and therefore I need to cope with the various methods of input and want to allow the following
English
1234
1,234
1234.56
1,234.5
German & Spanish
1234
1.234
1234,56
1.234,5
French
1234
1 234
1234,56
1 234,5
My problem is with French as options 2 & 4 are seen as invalid using this code as the parsing stops at the space.
public void validate(final FacesContext pContext,
final UIComponent pComponent,
final Object pValue) {
boolean isValid = true;
final Locale locale = (Locale)pComponent.getAttributes().get(USERS_LOCALE);
final Currency currency = (Currency)pComponent.getAttributes().get(CURRENCY);
final NumberFormat formatter = NumberFormat.getNumberInstance(locale);
formatter.setGroupingUsed(true);
formatter.setMinimumFractionDigits(currency.getDefaultFractionDigits());
formatter.setMaximumFractionDigits(currency.getDefaultFractionDigits());
final ParsePosition pos = new ParsePosition(0);
final String stringValue = (String)pValue;
if (pos.getIndex() != stringValue.length() || pos.getErrorIndex() != -1) {
isValid = false;
}
...
I also want to ensure that the following are treated as invalid but they all parse successfully (except for French of course)
1,234,9.56 (Invalid grouping)
1,234.567 (too many decimal places for the currency)
Any help will be much appreciated
Ian
The French thousands' separator is actually a non-breaking space, \u00a0. If your input uses a regular space you can change the input:
input = input.replace(' ', '\u00a0');
Another thing you can do is change the grouping symbol to a regular space:
DecimalFormat decimalFormatter = (DecimalFormat) formatter;
DecimalFormatSymbols symbols = decimalFormatter.getDecimalFormatSymbols();
symbols.setGroupingSeparator(' ');
decimalFormatter.setDecimalFormatSymbols(symbols);
Can't recommend this, though. The new formatter won't accept numbers that use a non-breaking space as the grouping character.
Related
I have a method that convert a number into a currency, for "USD" and "GBP" it's working good, but with "EUR" the NumberFormat it's rendering a string with a space between the symbol and the number € 1.207.987,00 rather then dollar and pound "$1,207,987.00", "£1,207,987.00". I tried use replace and replace all to remove this but nothing works for me, follow the code:
public static void main(String[] argsd) {
Number rawNumber = 120798700;
NumberFormat numberFormat = NumberFormat.getCurrencyInstance(Locale.forLanguageTag("nl"));
Currency currency = Currency.getInstance("EUR");
numberFormat.setCurrency(currency);
String numberRemoveSpaces = numberFormat.format((rawNumber.floatValue() / 100)).replaceAll("\\s+", "");
System.out.println(numberRemoveSpaces);
}
If you want to remove it, try this:
String numberRemoveSpaces = numberFormat.format((rawNumber.floatValue() / 100)).replaceAll("\\p{Z}","");
That removes any kind of whitespace or invisible separator.
You can use the following, instead of your current replaceAll():
replaceFirst("\\u00A0", "")
The Unicode value of U+00A0 is a non-breaking space (see here). This is the specific character being used to separate the currency symbol from the amount.
You can also choose to build a custom format as follows:
NumberFormat nf = NumberFormat.getCurrencyInstance();
DecimalFormatSymbols dfs = new DecimalFormatSymbols();
dfs.setCurrencySymbol("€");
dfs.setGroupingSeparator('.');
dfs.setMonetaryDecimalSeparator(',');
((DecimalFormat) nf).setDecimalFormatSymbols(dfs);
System.out.println(nf.format(rawNumber.floatValue() / 100));
This also gives the same output:
€1.207.987,00
Thanks guys for all the answers but according with http://www.bubblefoundry.com/blog/2013/11/formatting-dutch-currency-amounts/ the default format for Netherlands currency have the space between the symbol and number, so i'll keep with the space.
I am not sure if the same question is asked by any other. I have not seen this question in stackoverflow so far, hence posting the same.
I have a requirement where I need to format the numbers based on the currency.
Let me explain the above table to give you more context of my question. For UnitedStates, numbers are formatted as USD976,543.21, here the currency symbol is USD which is placed in the front before numbers. This is little different for fr-FR locale, here € symbol is placed after the digits.
All my formatting is happening in the front end and not the backend. I am giving a format in the form of # to the FE, then our home grown code formats the digits in that format. Example, for United states, I am supplying USD###,###,###.####. Here, I need to let the front end know the below information so that front end formats the same in a prompt manner.
My question is:
Is there any way to get the below information in the backend:
Where to place the currency, start of the number or towards ending?
Is there any space between number and symbol? Look at USD976,543.21 and 976 543,21 € . There is space between currency symbol and number for EURO but not for USD.
I am using java.util.Currency.
I found this question while searching for a way to determine whether the currency symbol should be placed at the start or end of a currency for a specific locale. For others who might be looking for something similar, I ended up creating the following class to test this:
public class Test {
private static Locale[] locales = Locale.getAvailableLocales();
static double decimal = 789456123.098;
public static void main(String[] args) {
for(Locale locale : locales){
DecimalFormat decimalFormatter = (DecimalFormat)NumberFormat.getInstance(locale);
NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance(locale);
DecimalFormatSymbols symbols = decimalFormatter.getDecimalFormatSymbols();
String currencySymbol = symbols.getCurrencySymbol();
String formattedCurrency = currencyFormatter.format(decimal);
System.out.print(formattedCurrency + ";" + locale.getDisplayCountry() + ";" + locale.getDisplayLanguage() );
System.out.println(";" + currencySymbol + ";" + currencySymbolAtStart(currencySymbol, formattedCurrency, true));
}
}
private static boolean currencySymbolAtStart(String currencySymbol, String formattedCurrency, boolean defaultAtStart){
if(formattedCurrency.startsWith(currencySymbol)){
return true;
}else if(formattedCurrency.endsWith(currencySymbol)){
return false;
}else{
return defaultAtStart;
}
}
}
Pasting the results into a spreadsheet looks something like this:
There is one caveat here to be aware of. For languages that are read right to left, the currency symbol might appear on the right in the formatted text, yet technically, since you are reading from right to left, the currency still "starts with" the currency symbol, hence the result will return "true". See Egypt-Arabic in the screen snip above...
Great explanation and code is here: https://drivy.engineering/multi-currency-java/
I would say use the standard, instead of pleasing your designers.
You can use Globalizejs for your requirement
A JavaScript library for internationalization and localization that leverages the official Unicode CLDR JSON data
It's pretty easy to start with and you need not worry about placing the currency code in front or at the end, The library will take care according to locale and currencyCode.
Example:
var formatter;
Globalize.locale( "en" );
formatter = Globalize.currencyFormatter( "USD" );
formatter( 9.99 );
// > "$9.99"
in different locale:
var deFormatter = Globalize( "de" ).currencyFormatter( "EUR" );
deFormatter( 9.99 );
// > "9,99 €"
The NumberFormat.getCurrencyInstance() can be used to format a number according to the currency of a particular locale in the backend. And the formatted currency can be passed to FE for rendering. For example:
double num = 976_543.21;
NumberFormat defaultFormat = NumberFormat.getCurrencyInstance(new Locale("fr","FR"));
System.out.println(defaultFormat.format(num)); //output 976 543,21 €
Locale us = new Locale("en", "US");
NumberFormat usFormat = NumberFormat.getCurrencyInstance(us);
System.out.println(usFormat.format(num)); //output $976,543.21
is there a class in Java that lets you format a number like "102203345.32" to this "102.203.345,32" and return a string type?
I would like to obtain a String where the thousands are separated by the '.' and the decimals are separated by a comma ','.
Could someone help me please? I found a class DecimalFormat and I tried to customize it:
public class CustomDecimalFormat {
static public String customFormat(String pattern, double value ) {
DecimalFormat myFormatter = new DecimalFormat(pattern);
String output = myFormatter.format(value);
return output;
}
}
but when I call the customFormat method like this: CustomDecimalFormat.customFormat("###.###,00") I get an exception...
What should I do?
Thanks!
Be sure to read and understand the Special Pattern Characters section of the Javadoc, especially this note:
The characters listed here are used in non-localized patterns. Localized patterns use the corresponding characters taken from this formatter's DecimalFormatSymbols object instead, and these characters lose their special status.
If you have done that, it should be clear to you that you must use the appropriate constructor and supply the appropriately configured separator/grouping chars, whereas in the pattern itself the dot and the comma have a special meaning.
All the complexity above is there for your convenience, actually: it allows you to customize the number format and have it localized.
Here's a code sample which worked for me:
final DecimalFormatSymbols syms = new DecimalFormatSymbols();
syms.setDecimalSeparator(',');
syms.setGroupingSeparator('.');
DecimalFormat myFormatter = new DecimalFormat("###,###.00", syms);
System.out.println(myFormatter.format(1234.12));
You can also use a variant where you apply the localized pattern, for more intuitive code:
final DecimalFormatSymbols syms = new DecimalFormatSymbols();
syms.setDecimalSeparator(',');
syms.setGroupingSeparator('.');
DecimalFormat myFormatter = new DecimalFormat("", syms);
myFormatter.applyLocalizedPattern("###.###,00");
System.out.println(myFormatter.format(1234.12));
First of all,
you misplaced the comma and decimal points. Your format should be : ###,###.00 instead of ###.###,00 ...
Also check with your Locale, it has an effect on the format. See the link below.
http://docs.oracle.com/javase/tutorial/i18n/format/decimalFormat.html
You can try GERMAN number format
NumberFormat.getNumberInstance(Locale.GERMAN).format(102203345.32)
I found a bug in my app where my currency strings were not showing the correct grouping and decimal separators when using an international format. I extracted the code to try to isolate and demonstrate the problem. In the code below, I create a US formatter and a World formatter. They both use the same currency pattern, but the USA formatter uses a period as the decimal separator and the World formatter uses a comma as the decimal separator.
When I ask the World formatter to tell me what its separators are, it reports them correctly. However, when I have it generate the string for my value, it doesn't do it properly. It strangely uses the period for both the grouping AND the decimal separator.
public static void main(String[] args) throws Exception {
String currencyFormat = "¤#,##0.00;-¤#,##0.00";
NumberFormat currencyUSA = NumberFormat.getCurrencyInstance();
DecimalFormat decimalCurrencyUSA = (DecimalFormat)currencyUSA;
decimalCurrencyUSA.applyPattern(currencyFormat);
NumberFormat currencyWorld = NumberFormat.getCurrencyInstance();
DecimalFormat decimalCurrencyWorld = (DecimalFormat)currencyWorld;
decimalCurrencyWorld.applyPattern(currencyFormat);
DecimalFormatSymbols decimalFormatSymbols = decimalCurrencyWorld.getDecimalFormatSymbols();
//DecimalFormatSymbols decimalFormatSymbols = new DecimalFormatSymbols(Locale.GERMAN);
System.out.println(String.format("Original grouping: %c decimal: %c", decimalFormatSymbols.getGroupingSeparator(), decimalFormatSymbols.getDecimalSeparator()));
decimalFormatSymbols.setDecimalSeparator(',');
decimalFormatSymbols.setGroupingSeparator('.');
decimalCurrencyWorld.setDecimalFormatSymbols(decimalFormatSymbols);
System.out.println(String.format(" After grouping: %c decimal: %c", decimalFormatSymbols.getGroupingSeparator(), decimalFormatSymbols.getDecimalSeparator()));
String moneyUSA = currencyUSA.format(1500.75);
String moneyWorld = currencyWorld.format(1500.75);
DecimalFormatSymbols decimalFormatSymbols2 = decimalCurrencyWorld.getDecimalFormatSymbols();
System.out.println(String.format(" Real grouping: %c decimal: %c", decimalFormatSymbols2.getGroupingSeparator(), decimalFormatSymbols2.getDecimalSeparator()));
System.out.println(String.format(" USA String: %s", moneyUSA));
System.out.println(String.format("World String: %s", moneyWorld));
Number numberUSA = currencyUSA.parse(moneyUSA);
Number numberWorld = currencyWorld.parse(moneyWorld);
System.out.println(String.format(" USA Number: %f", numberUSA));
System.out.println(String.format("World Number: %f", numberWorld));
}
When I run the above code (on my Mac OS X 10.9, Eclipse Indigo, Java 1.6), the output is:
Original grouping: , decimal: .
After grouping: . decimal: ,
Real grouping: . decimal: ,
USA String: $1,500.75
World String: $1.500.75
USA Number: 1500.750000
World Number: 1.500000
The World currency value should be $1.500,75 however it used a period in both places.
EDIT:
I need to control the separators specifically. I can respect the user's Locale when it comes to the currency symbol, but due to the requirements of this app, I need to provide the user the ability to swap the period/comma when it comes to the separators. I can't just rely on the Locale to control these things.
The problem is that I'm clearly setting those values in my formatters and they aren't being respected.
EDIT 2: I added code to then parse the previously generated money string to see if the Decimal separators were being respected for parsing, but not for formatting. It appears that during parsing, the period is still being treated as the decimal separator as well because the World Number came out as 1.5 instead of 1500.75.
From Customizing Formats
¤ currency sign; replaced by currency symbol; if doubled, replaced by international currency symbol; if present in a pattern, the monetary decimal separator is used instead of the decimal separator
So:
decimalFormatSymbols.setMonetaryDecimalSeparator(',');
Will do it.
If you change
DecimalFormatSymbols decimalFormatSymbols = decimalCurrencyWorld.getDecimalFormatSymbols();
to
DecimalFormatSymbols decimalFormatSymbols = new DecimalFormatSymbols(Locale.GERMAN);
(or some other Locale) you will find that the separators work properly.
decimalFormatSymbols.setDecimalSeparator(','); apparently does not change the format (although setGroupingSeparator() does, as you have discovered).
In Java, I am trying to get DecimalFormat to enforce the sign on an exponent sign. When it is positive I need a plus sign to appear. From what I have read this seems like a no brainer, but for myself it always throws up an error. I appreciate that there may be other methods to achieve my goal, but I would like to understand why in this specific method the error is occurring.
Double result = 123.456;
String sresult;
//This works
NumberFormat formatter = new DecimalFormat("0.00000E00");
sresult = formatter.format(result);
System.out.println(sresult); //1.23456E02
//This doesn't work
formatter = new DecimalFormat("0.00000E+00"); //Want to enforce the sign to appear
sresult = formatter.format(result);
System.out.println(sresult); //Expected 1.23456E+02 but error occurs
The error which is thrown up:
Exception in thread "main" java.lang.IllegalArgumentException:
Malformed exponential pattern "0.00000E+00"
at java.text.DecimalFormat.applyPattern(Unknown Source)
at java.text.DecimalFormat.(Unknown Source)
at deccheck.main(deccheck.java:13)
I appreciate any insight.
Thanks,
Mark
I would like to extend the solution by j flemm. The "E" and "-" are no constants, they can be set as DecimalFormatSymbols. Therefore the substitutions must respect this:
public static String hoola(final String s, final DecimalFormatSymbols symbols) {
String result;
final String expo = symbols.getExponentSeparator();
final char minus = symbols.getMinusSign();
if (!s.contains(expo + minus)) { // don't blast a negative sign
result = s.replace(expo, expo + '+');
} else {result=s;}
return result;
}
/**
* #param args
*/
public static void main(final String[] args) {
final DecimalFormat decForm = (DecimalFormat) NumberFormat
.getInstance(Locale.GERMAN);
final DecimalFormatSymbols newSymbols = new DecimalFormatSymbols(
Locale.GERMAN);
newSymbols.setExponentSeparator("*10^");
newSymbols.setMinusSign('\u2212');
decForm.setDecimalFormatSymbols(newSymbols);
decForm.applyPattern("0.00000E00");
System.out.println(hoola(decForm.format(1234.567), decForm
.getDecimalFormatSymbols()));
System.out.println(hoola(decForm.format(000.00567), decForm
.getDecimalFormatSymbols()));
}
Result is:
1,23457*10^+03
5,67000*10^−03
Easy way:
formatter = new DecimalFormat("0.00000E00"); // Want to enforce the sign to appear
sresult = formatter.format(result);
if (!sresult.contains("E-")) { //don't blast a negative sign
sresult = sresult.replace("E", "E+");
}
System.out.println(sresult);
Outputs 1.23456E+02 for your example.
But I don't believe there's a way to do it from inside the DecimalFormat pattern. Or at least the javadoc doesn't indicate there is one.
Edit: trashgod brings up a good point. You'd probably want to get positive and negative signs from DecimalFormatSymbols if you plan on localizing this to different regions.
Edit 2: Andrei pointed out that E is also a localization variable. Shows what I know about localization.
I don't think the '+' character is an accepted character in a pattern (after studying the javadocs). And the pattern for for the exponential part si described as:
Exponent:
E MinimumExponent
MinimumExponent:
0 MinimumExponent(opt)
If you add anything there it will confuse the parser.
I think your only option is to give it the normal pattern (without '+') and then get the String and use regex to add the '+' there.
sresult = formatter.format(result);
sresult = sresult.replaceAll("E([^\\-]+)", "E+$1");
The best option might be extending DecimalFormat and doing this in the overwritten format method
#j flemm's approach is appealing because "Negative exponents are formatted using the localized minus sign, not the prefix and suffix from the pattern."—DecimalFormat.