Idiomatic way to remove country code from currency format? - java

Somewhere between Java 11 and 17 currency formatting changed to where this:
NumberFormat.getCurrencyInstance(Locale.CANADA_FRENCH).format(100.00)
would print 100,00 $ CA instead of 100,00 $.
Is there a better way than this to remove the country code CA?
var currencyFormat = NumberFormat.getCurrencyInstance(Locale.CANADA_FRENCH);
if (currencyFormat instanceof DecimalFormat decimalFormat) {
var symbols = DecimalFormatSymbols.getInstance(Locale.CANADA_FRENCH);
symbols.setCurrencySymbol("$");
decimalFormat.setDecimalFormatSymbols(symbols);
}
Seems a bit much just to get back something that was the default behavior up until recently.

I dug a bit into this, the JDK locale data comes from Unicode CLDR by default, and it seems they reverted from $ CA to $ back in August, see CLDR-14862 and this commit (expand common/main/fr_CA.xml and then go to lines 5914/5923).
This was part of v40, released in October, so too late for JDK 17 whose doc says it uses CLDR v35.1
(which was introduced in Java 13)
but it seems it was updated to v39 in April 2021 and
they forgot the release note
(JDK 16 appears to have been upgraded to v38 already).
CLDR v40 is planned for JDK 19.
You may want to run your application using the COMPAT locales first, with
-Djava.locale.providers=COMPAT,CLDR,SPI
(found here but see also LocaleServiceProvider)
This will use the locales compatible with Java 8, where this issue is not present.

Related

Check if a Java version is greater than a certain iteration in Java?

I wish to check if a user's Java version is at least 1.8.0_171. I mean that specific iteration or higher, meaning 1.8.0_151, for instance, would not work.
I planned to originally use org.apache.commons.lang3.SystemUtils' isJavaVersionAtLeast(JavaVersion requiredVersion) method, but it seems that you cannot specify the iteration number.
Based on this and Java's changing way of representing version numbers in Java (e.g. 1.8 then 9), what is the best way to check the Java version of the user in the Java program?
Edit:
This was marked as a duplicate of this question; however, I think it is different in that it asks how to compare the java version with a certain version given the changes in format of how the java version is shown.
Even with the versioning change, I think the solution is still as simple as using the following boolean expression:
"1.8.0_171".compareTo(System.getProperty("java.version")) <= 0
If the user's java.version property is any less than 1.8.0_171, then the above expression returns false, and vice versa. This works for using "9" or "10" in place of the java.version property as well.

Changing DecimalFormatSymbols symbols for language in java sdk

I'm using a 3rd party software built in Java that displays numbers. This software is multilanguage and one of the languages that we use is Euskera (eu, eu_ES). Number format is shown wrong in this language (123,456.89 instead of 123.456,89).
Searching more in-depth and decompiling some classes I've seen that number formatting is done with DecimalFormatSymbols and DecimalFormat so I've made a junit test to see if the issue is from this 3rd party software or from java.
Locale locale = new Locale("eu");
DecimalFormatSymbols decimalFormatSymbols = new DecimalFormatSymbols(locale);
String pattern = "#,###.##";
DecimalFormat decimalFormat = new DecimalFormat(pattern, decimalFormatSymbols);
String formatted = decimalFormat.format(1234567.89765);
assertEquals("1.234.567,9", formatted);
After running this test I've seen is Java who is formatting this way.
In one hand I've downloaded the last version of this 3rd party software because is open source and I could make a little workaround that worked. On the other hand, we use a version from 6 years ago that can't be upgraded because os system requirements and this version are in Sourceforge's CVS which I was unable to download.
Is there any way I can change the grouping separator and decimal separator for Euskera in Java level?
Yes, you can but it's a bit of a palaver. Essentially, you can create a custom NumberFormatProvider that does something different for eu_ES and delegates to the original provider for all other locales. You'll have to put it in a JAR with a META-INF/services/xxxx file and include it on the classpath.
See this question: Java override locale setting for specific locale
And more instructions here:
LocaleServiceProvider JavaDoc
Tutorial on the Java Extension Mechanism

Which "default Locale" is which?

With UNIX locales, the breakdown of which means what is relatively well documented.
LC_COLLATE (string collation)
LC_CTYPE (character conversion)
LC_MESSAGES (messages shown in UI)
LC_MONETARY (formatting of monetary values)
LC_NUMERIC (formatting of non-monetary numeric values)
LC_TIME (formatting of date and time values)
LANG (fallback if any of the above are not set)
Java has a different categorisation which doesn't quite match the real world (as usual):
Locale.getDefault()
Locale.getDefault(Locale.Category.DISPLAY)
Locale.getDefault(Locale.Category.FORMAT)
If you read the documentation on these, Locale.getDefault(Locale.Category.DISPLAY) appears to correspond to LC_MESSAGES while Locale.getDefault(Locale.Category.FORMAT) appears to correspond to some combination of LC_MONETARY+LC_NUMERIC+LC_TIME.
There are problems, though.
If you read the JDK source, you start to find many worrying things. For instance, ResourceBundle.getBundle(String) - which is entirely about string messages - uses Locale.getDefault(), not Locale.getDefault(Locale.Category.DISPLAY).
So I guess what I want to know is:
Which of these methods is supposed to be used for which purpose?
Related, but I made a little test program to see which Java locales corresponded to which UNIX locales and got even more surprising results.
import java.util.Locale;
public class Test {
public static void main(String[] args) {
System.out.println(" Unqualified: " + Locale.getDefault());
System.out.println(" Display: " + Locale.getDefault(Locale.Category.DISPLAY));
System.out.println(" Format: " + Locale.getDefault(Locale.Category.FORMAT));
}
}
Locales according to my shell:
$ locale
LANG="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_CTYPE="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_ALL="en_US.UTF-8"
Output of the program:
$ java Test
Unqualified: en_AU
Display: en_AU
Format: en_AU
So it turns out Java doesn't even get it from the UNIX locale. It must be using some other back door to get the settings without using those.
It's hard to understand what you are asking here. Instead, you make a statement that reveals that you're not necessary a Java programmer. It's OK, it does not matter really.
Few things to clarify:
The Locale class is in JDK since Java 1.1
Things like Locale.Builder, Locale.Category and many others are here from Java 7 (JDK 1.7)
Locale-aware classes and methods like DateFormat, NumberFormat, Collator, ResourceBundle, String.toLowerCase(Locale), String.toUpperCase(Locale) and many, many more are here for quite a long time each (long before JDK 1.7)
Prior to Java 7/JDK 1.7 there was only one method of acquiring current OS Locale - call Locale.getDefault() (that is without parameters)
In other words, prior to Java 7, Java's Locale Model was as simple as one system property composed of a language, a country and an optional locale variant. That has changed with Java 7 (end was further extended with Java 8...) and now you have two system properties, one for formatting and one for displaying user interface messages.
The problem is, there is substantial amount of legacy code written in Java and this could shouldn't break when you upgrade the platform. And that is exactly why you still have parameterless Locale.getDefault() around. Moreover (you may test it yourself), Locale.getDefault() is basically interchangeable with Locale.getDefault(Locale.Category.DISPLAY).
Now, I said formatting and user interface messages. Basically, formatting is not only formatting, but things like character case conversion (LC_CTYPE), collation (LC_COLLATE) as well. Sort of anything but user interface messages. Sort of, because default character encoding (which depends on an OS, BTW) is not part of Locale. Instead you need to call Charset.defaultCharset().
And the fallback rules (built in Java, not read from OS) could be worked out with ResourceBundle.Control class. And as we know, it is rather related to UI category...
The reason why Java Locale Model is different from POSIX (not UNIX, it's more universal), is the simple fact that there are quite a few platforms out there. And these platforms doesn't necessary use POSIX... I mean not only Operating Systems, but things like web... Java is striving to be universal and versatile. As the result Java's Locale Model is convoluted, tough luck.
I have to add that nowadays, it's not only the language and the country, but there are also things like preferred script, calendar system, numbering system, specific collation settings and possibly more. It even works sometimes.

Formatting a table in Java 1.4

I'm required (by my teacher) to use Java 1.4 and I need to use a Formatter to format my output into a table. Is there any other way I can do this?
Right now, I have it looking like this:
System.out.printf("%-15s %-15s %-15s %n", "Name", "Day", "Time");
for (int i = 0; i < show.size(); i++){
System.out.printf("%-15s %-15s %-4d %n", ((showInfo)show.get(i)).name, ((showInfo)show.get(i)).day, ((showInfo)show.get(i)).time);
And it works on the IDE, but when I run it in my command prompt (1.4) it gives me errors. Is there any other way to make the table?
Any help would be great! Thanks in advance!
Java 1.4 doesn't have PrintStream.printf(), it was added in Java 1.5.
You need to make sure that you actually use Java 1.4 in your IDE to be sure that you only use methods that exist in that Java version.
It has been a while since I used Java 1.4, but you have to do the formatting yourself, eg by left padding the string if it is too short.
BTW: Tell your teacher to get with the times, Java 1.4 was released in 2002, Java 5 (1.5) in 2004, Java 6 (1.6) in 2006 and Java 7 (1.7) in 2011. What use is it to constrain students by using outdated and insecure(!) versions of Java?

Rounding Half Up with Decimal Format in Android

I want to set the Rounding Mode to HALF_UP on my DecimalFormat, but eclipse is telling me that setRoundingMode() is not available on the DecimalFormat class. My project properties (and the overall Eclipse properties) are using the 1.6 compiler. The developer.android.com site says that I can use either Java 5 or 6 so I'm not sure what the problem is.
import java.math.RoundingMode;
import java.text.DecimalFormat;
completedValueFormatter = NumberFormat.getNumberInstance();
DecimalFormat completedDecimalFormat = (DecimalFormat)completedValueFormatter;
completedDecimalFormat.setRoundingMode(RoundingMode.HALF_UP);
I've also tried using the android tools to generate an ant-based project, tried this code in the project and also got the same compile error. So it doesn't appear to be related to Eclipse. It seems related to the Android API.
Any suggestions?
This doesn't truly answer why I can't use the Java 6 .setRoundingMode(RoundingMode) method in DecimalFormat, but it is at least a work-around.
int numDigitsToShow = this.completedValueFormatter.getMaximumFractionDigits();
BigDecimal bigDecimal = new BigDecimal(valueToBeRounded);
BigDecimal roundedBigDecimal = bigDecimal.setScale(numDigitsToShow, RoundingMode.HALF_UP);
return this.completedValueFormatter.format(roundedBigDecimal.doubleValue());
I create a BigDecimal with the value I need to round, then I get a BigDecimal of that value with the scale set to the number of digits I need to round my values to. Then I pass that rounded value off to my original NumberFormat for conversion to String.
If anyone has a better solution, I'm all ears!
Here is what I suspect the problem is, (assuming I am reading the docs properly) and its a doozy:
According to the java.text.DecimalFormat API documentation, you are not actually getting the Runtime Implimentation of the Java 1.6 RE, but are getting an android "Enhanced Version" that clearly doesn't include the setRoundingMode, which frankly bites.
"This is an enhanced version of DecimalFormat that is based on the standard version in the RI. New or changed functionality is labeled NEW."
A weakness in Java for many many many years has been the DecimalFormat class defaulted to HALF_ROUND_UP and had no way to change that, until JVM 1.6. Pity to see Android is keeping this need to kludge alive.
So looks like we are stuck Kludging BigDecimal scale Settings to format output all over any app that needs it, instead of simply being able to rely on a formatter call alone to get the job done. Not the end of the world, but very disappointing Google.
Of course that same doc says that setRondingMode() works, so perhaps this is a all out BUG??
I guess this would be the best option
http://download.oracle.com/javase/1.4.2/docs/api/java/lang/Math.html#ceil(double)

Categories

Resources