My main reason for doing these posts is to keep a record for my own sake of the things to watch out for when doing i18n work. It’s also a good chance to look at various interesting cases of the world, and people, being complicated.
As with a lot of Java’s i18n stuff, there was a lot of work done between Java 8 and Java 9, with Java 10 only making a few changes over Java 9.
The code used as the source of this article is here but basically it’s:
get a unicode capable output stream get a sorted list of all the available Currency types print a bunch of info about each Currency * its ISO 4217 string code * the currency symbol * the number of fractional digits * the DisplayName (in English) print the count of known Currency types
The raw results are here:
I compiled that with OpenJDK’s Java 8 version, then ran it in OpenJDK’s JVMs for
Java 8, 9 and 10. I took the outputs from each and
diffed them to see what has
changed between Java versions.
Usual Caveat: These results are from the OpenJDK JVM, on a Debian PC. Different JVMs for different OSs on different hardware may have different information. Do your own tests if you need to know for sure!
1. No new currencies
That’s the same in Java 8, 9 and 10. This surprises me becuase there is quite a long period between Java 8 and Java 10. The version of Java 8 I’m using is recently patched, but usually Java doesn’t introduce new i18n data in these patches.
I’m expecting a future version of Java to start including crypto-currencies, but for the moment (despite supporting other odd currencies) it looks like the boom in new currencies hasn’t reached the Java dev/spec teams yet.
2. New currency symbols in Java 9
If you read any of my other i18n posts this won’t be a surprise, but Java 8 used the ASCII 3-char codes for the vast majority of currency symbols; in fact only two got symbols (and neither was a dollar sign!)
code: EUR, symbol: €, fdigits: 2, name: Euro code: GBP, symbol: £, fdigits: 2, name: British Pound Sterling
… and in Java 9, they made a better job of sprinkling unicode around:
code: AUD, symbol: A$, fdigits: 2, name: Australian Dollar code: BRL, symbol: R$, fdigits: 2, name: Brazilian Real code: CAD, symbol: CA$, fdigits: 2, name: Canadian Dollar code: CNY, symbol: CN¥, fdigits: 2, name: Chinese Yuan code: HKD, symbol: HK$, fdigits: 2, name: Hong Kong Dollar code: ILS, symbol: ₪, fdigits: 2, name: Israeli New Shekel code: INR, symbol: ₹, fdigits: 2, name: Indian Rupee code: JPY, symbol: JP¥, fdigits: 0, name: Japanese Yen code: KRW, symbol: ₩, fdigits: 0, name: South Korean Won code: MXN, symbol: MX$, fdigits: 2, name: Mexican Peso code: NZD, symbol: NZ$, fdigits: 2, name: New Zealand Dollar code: TWD, symbol: NT$, fdigits: 2, name: New Taiwan Dollar code: USD, symbol: US$, fdigits: 2, name: US Dollar code: VND, symbol: ₫, fdigits: 0, name: Vietnamese Dong code: XCD, symbol: EC$, fdigits: 2, name: East Caribbean Dollar
A surprising thing here is that none of the currencies use the generic currency symbol ¤. Although it shouldn’t be used for any specific currency I am surprised they didn’t use it as the symbol for some of the placeholder currencies (mentioned later on).
The ISO (and Java) currency list also include a bunch of “supranational” currencies to make various banking and financial tasks easier. These seem to fall into two main groups.
Firstly, there are things which aren’t really currencies, but do get used to transfer currency-like values. These all have a currency code starting with an ‘X’, things like:
code: XAG, symbol: XAG, fdigits: -1, name: Silver code: XAU, symbol: XAU, fdigits: -1, name: Gold code: XBA, symbol: XBA, fdigits: -1, name: European Composite Unit
Some of these pseudo-currencies do look like they may be real ones, but the rule to follow appears to be: if its code starts with an ‘X’, it’s a weird one.
code: XCD, symbol: EC$, fdigits: 2, name: East Caribbean Dollar
4. Placeholder/test currencies
The other group of “supranational” currencies is things which really aren’t currencies at all, but placeholders for where a real currency will be used. Again they all start with an ‘X’ in their code, and they all have “-1” fraction digits (I’ll mention that below).
code: XDR, symbol: XDR, fdigits: -1, name: Special Drawing Rights code: XTS, symbol: XTS, fdigits: -1, name: Testing Currency Code code: XUA, symbol: XUA, fdigits: -1, name: ADB Unit of Account code: XXX, symbol: XXX, fdigits: -1, name: Unknown Currency
5. Fractional Digits
It’s quite often forgetten that not every currency has “pence and cents”. It’s also true that they don’t all use 2 fraction digits. I’ve dealt with Japanese Yen frequently, which has no fraction digits, but I’ve never dealt with a currency that had more than 2 fraction digits. However, they do exist, some go up to 4, which could cause interesting edge cases.
Just to throw some craziness into this mix, those pseudo-currencies I mentioned above - Java gives them “-1” fraction digits. Yup, you read that right:
code: XAG, symbol: XAG, fdigits: -1, name: Silver code: XAU, symbol: XAU, fdigits: -1, name: Gold
Now this is exceptionally weird - because the ISO standard doesn’t state “-1” it instead says that these currencies don’t have a fractional part. Specifically, they state “N.A.” or “.” rather than the “0” that some real currencies use.
In the interests of pragmatism, you probably want to explicitly catch these currencies and force those numbers to have zero decimals. Or alternatively, have a special handler case if the currency code starts with an ‘X’.
So take care formatting numbers for currencies and maybe even revisit your rounding rules for calculations. You may need to handle currencies that need extra accuracy after the decimal and to handle cases where Java throws you a nasty negative.
6. Time travelling in the USA
Most currencies have one entry, or maybe 2 if they had some interesting politics recently. But the US Dollar, it has 3 entries, ‘cos yeah America!
code: USD, symbol: USD, fdigits: 2, name: US Dollar code: USN, symbol: USN, fdigits: 2, name: US Dollar (Next day) code: USS, symbol: USS, fdigits: 2, name: US Dollar (Same day)
Wikipedia has a bit of info about why - it looks like USS may be falling out of Java’s currency list in a subsequent version.
7. Formatting things the easy way
I18n compatible formatting is never fun. These results show a bunch of things to be aware of when displaying currencies. Things like:
- Longest (in English) display name is BAM - “Bosnia-Herzegovina Convertible Mark”
- Shortest is a bunch of them which just use the 3 char code.
- The currency symbol might be a 3 (or 4!) char code, a single unicode symbol, or a symbol and 1 or 2 chars
- I mentioned fraction digits above. I’m mentioning them again here - be careful with them!
- The names and symbols may have special chars in them but the 3-char codes are always ASCII
- and a final one that caused me hassle in the past: Some countries & currencies put their symbol/code after the number, some before.
Luckily, almost all of the formatting woes you may be about to dive into are now taken care of by Java itself. The main thing to remember is:
Where possible, use the
facilities to construct the situation you’re working with; maybe something like (JShell)…
var loc = Locale.JAPAN; var nf = NumberFormat.getCurrencyInstance(loc); var amount = 123456.789; nf.setCurrency(Currency.getInstance("GBP")); nf.format(amount); ==> "£123,457"
That’s printing a monetary amount of 123456.789, in GBP currency, in the format used in Japan. So we get the GBP symbol, but the amount is rounded to fit Japanese currency formatting.
8. Rolling your own
There could be good reasons to define your own currency. Maybe you’re adding support for crypto-currencies and want to keep your existing currency code as clean as possible. Maybe you need to support some historical currencies.
Whatever the reason, the Java Currency class describes how to define new currencies using system properties. That includes specifying a start date for the currency, but no end date. So if you’re doing historical stuff, you may also need to add some sort of extra “stopper” currency to act as an end date.
9. Java 8 to 9, the big i18n push
As I’ve mentioned before, the Java team did a huge amount of i18n work for Java 9.
To summarise the changes in Java 9 I spotted from my
- Unicode All The Things!!! (ahem, except the ISO codes)
- More actual currency symbols and names, rather than slapping in the code again
- Using em-dash for date ranges
- A bunch of work adjusting display names
- and finally, GBP lost its “Stirling” in Java, although ISO still says “Pound Stirling”
10. Java 9 to 10, nah we’re good
There are literally zero diffs between the Java 9 and 10 results.
Which is nice.