In Java, why does the decimal separator follow the

2019-04-06 14:40发布

问题:

I'm working on an international project and have observed that, in Java, the choice of the decimal separator is based on the Locale's language, not its country. For example:

DecimalFormat currencyFormatter = (DecimalFormat) NumberFormat.getInstance(new Locale("it","IT"));
System.out.println(currencyFormatter.format(-123456.78));

currencyFormatter = (DecimalFormat) NumberFormat.getInstance(new Locale("en","IT"));
System.out.println(currencyFormatter.format(-123456.78));

produces the following output:

-123.456,78
-123,456.78

I would have expected it to follow the country, since if I'm in the country, whether I speak English or Italian the numbers in that country are written with the decimal separator as comma.

My question is two-fold:

  1. Does anyone know why this behaviour follows the language?
  2. More importantly, can anyone offer details as to how to modify the default behaviour in Java to follow the language instead?

Thanks.

回答1:

It is very, very likely that there is no known "en_IT" locale on your system and that Java knows of no such locale.

Therefore you'll get the resources for the closest matching Locale, as per this note in the JavaDoc of Locale:

When you ask for a resource for a particular locale, you get back the best available match, not necessarily precisely what you asked for. For more information, look at ResourceBundle.

The relevant documentation in ResourceBundle shows that the country alone is never attempted as the identifier to find a given resource. Only the language, the language + the country and the language + the country + the variant are used.



回答2:

1.- Does anyone know why this behaviour follows the language?

One of the first aspects that change in an application according to its locale is the language. Think of Switzerland, where they have both French and German as official languages. Using the country in this scenario makes no sense because, besides currency there are other aspect ( such as display messages, to take into consideration )

Same goes for a number of countries ( Canada is other that comes to my mind).

The country is an option to specialize that language.

Since there is no "Italian English" en_IT doesn't make much sense. So it takes "english" and that's why the returned instance is the one with "en" English.

2.- More importantly, can anyone offer details as to how to modify the default behavior in Java to follow the language instead?

You mean, the country right?

The answer is in the method: NumberFormat.getAvailableLocales() which in turn leads you to: NumberFormatProvider which is an abstract class you can extend to return the correct NumberFormat for "en_IT" ( which pretty much will return it_IT )

How to install that new class goes beyond my knowledge, but I think you'll have to hack it somewhere in the jre directory.

EDIT

As I suspect the class should be installed under jre/ext folder and that's it.

There is a project somewhere else which did something similar to support "gl_ES" ( Galician )

Here's instructions on how to install it and what it is all about:

http://www.javagalician.org/install.html

So, basically, if you want to provide a bunch of instances for Italy ( or even better a single one ) , you just have to create one instance of the NumberFormatProvider and have it responding all the languages available.



回答3:

But if you are an Englishman living in Italy, chances are you still write your numbers the English way and count your money in pounds inside your head - and vice versa ;-)

So to me this behaviour looks perfectly logical. And I am afraid there may not be a way to get what you want :-(

Update based on the comment of Oscar Reyes: In Java 6, NumberFormatProvider enables you to achieve your goal.



回答4:

Modifying all of Java is a big order; as far as I know, there's no way to do this at the JVM level. However, you can manually set the number format for your particular classes/projects to pretty much any locale (or even imaginary system) with the DecimalFormatSymbols class. See "Altering the Formatting Symbols" at the Java Tutorials page on format customization for examples and more information.

Granted, this is not an optimal approach if your program might be used in more than three or four different locations, but it could work if you're only supporting a few.



回答5:

Your assumption "I would have expected it to follow the country, since if I'm in the country, whether I speak English or Italian the numbers in that country are written with the decimal separator as comma." is wrong. Several bilingual countries have language specific formatting rules for numbers. Among the countries with formatting rules in Sun's JDK, this applies to Canada, India and Luxembourg.



回答6:

I think you might be able to override the NumberFormat behavior per Locale using LocaleServiceProvider. This would require Java 6 though ...



回答7:

As people have noted, the problem is most likely that java can't find your exact local, so it went to what it thought was closest. If you do have a DecimalFormat object, you can manually change the separators if you'd like.

DecimalFormat format = ...;
DecimalFormatSymbols symbols = format.getDecimalFormatSymbols();
symbols.setDecimalSeparator(',');
symbols.setGroupingSeparator('.');
format.setDecimalFormatSymbols(symbols);


回答8:

I would have expected it to follow the country, since if I'm in the country, whether I speak English or Italian the numbers in that country are written with the decimal separator as comma.

No. Some countries with many official languages such as Canada (two official languages) have different formatting convention based on the language:

-1 234,56 (Locale.CANADA_FRENCH)
-1,234.56 (Locale.CANADA (should probably be named CANADA_ENGLISH))