Internationalization with Numbers in Java
The number one thousand two hundred thirty-four and a half is written 1,234.5 in the United States and 1.234,5 in Germany. The comma and the period swap roles. A program that prints or reads numbers with fixed symbols will mislead half its users, sometimes by a factor of a thousand. Java's NumberFormat class formats and parses numbers according to a Locale: it knows each region's grouping separator, decimal separator, percent style, and even which digits to use. In this lesson, you will learn its factory methods, how to control decimals, and why parsing must use the right locale.
"NumberFormat and DecimalFormat in Java" introduced the class; this lesson concentrates on locales.
Methods
| Method | Description |
|---|---|
NumberFormat.getNumberInstance(locale) | It is used to format ordinary numbers. |
NumberFormat.getIntegerInstance(locale) | It formats whole numbers, rounding fractions. |
NumberFormat.getPercentInstance(locale) | It formats fractions as percentages: 0.856 becomes 86%. |
NumberFormat.getCompactNumberInstance(locale, style) | It formats large numbers briefly, such as 2M. |
NumberFormat.getCurrencyInstance(locale) | It formats money (see "Internationalization with Currency in Java"). |
format(number) | It turns a number into text. |
parse(text) | It turns text into a Number; throws ParseException if it can't. |
setMinimumFractionDigits(n), setMaximumFractionDigits(n) | They control how many decimals are shown. |
setGroupingUsed(boolean) | It turns the thousands separator on or off. |
Example: Numbers, Percentages and Compact Forms
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;
public class Numbers {
public static void main(String[] args) throws ParseException {
double value = 1234567.891;
Locale[] locales = {Locale.US, Locale.GERMANY, Locale.FRANCE,
Locale.of("en", "IN"), Locale.of("hi", "IN"),
Locale.forLanguageTag("hi-IN-u-nu-deva")};
for (Locale locale : locales) {
NumberFormat number = NumberFormat.getNumberInstance(locale);
NumberFormat percent = NumberFormat.getPercentInstance(locale);
NumberFormat compact = NumberFormat.getCompactNumberInstance(
locale, NumberFormat.Style.SHORT);
System.out.println(locale.toLanguageTag() + " "
+ number.format(value) + " | " + percent.format(0.856)
+ " | " + compact.format(1_500_000));
}
NumberFormat twoPlaces = NumberFormat.getNumberInstance(Locale.US);
twoPlaces.setMinimumFractionDigits(2);
twoPlaces.setMaximumFractionDigits(2);
System.out.println(twoPlaces.format(91) + " and "
+ twoPlaces.format(84.256));
// The same text means different numbers in different locales.
String text = "1.234";
System.out.println("US reads \"1.234\" as "
+ NumberFormat.getNumberInstance(Locale.US).parse(text));
System.out.println("Germany reads \"1.234\" as "
+ NumberFormat.getNumberInstance(Locale.GERMANY).parse(text));
}
}en-US 1,234,567.891 | 86% | 2M
de-DE 1.234.567,891 | 86 % | 2 Mio.
fr-FR 1 234 567,891 | 86 % | 2 M
en-IN 1,234,567.891 | 86% | 15L
hi-IN 1,234,567.891 | 86% | 15 लाख
hi-IN-u-nu-deva १,२३४,५६७.८९१ | ८६% | १५ लाख
91.00 and 84.26
US reads "1.234" as 1.234
Germany reads "1.234" as 1234Explanation:
- Separators: the US writes
1,234,567.891; Germany swaps the two symbols; France groups with a narrow no-break space and uses a decimal comma. - Percent:
0.856is multiplied by 100 and rounded. Germany and France put a space before the sign. - Compact numbers shorten 1,500,000. English gives "2M" (rounded to a whole number by default), German "2 Mio.", and the Indian locales "15L" and "15 लाख", because India counts in lakhs (1 lakh = 100,000).
- Digits: the last locale,
hi-IN-u-nu-deva, asks for Devanagari digits through a locale extension. The same number appears as१,२३४,५६७.८९१. - Fraction digits: with minimum and maximum set to 2,
91prints as91.00and84.256rounds to84.26. - Parsing: the text
1.234is the number 1.234 to a US reader and 1234 to a German reader. Parsing with the wrong locale gives a wrong number with no error.
Numbers for People vs Numbers for Programs
| Reader | Use |
|---|---|
| A person | NumberFormat with that person's locale |
| A program (JSON, CSV, a database, a URL) | String.valueOf, Double.toString, BigDecimal.toPlainString(), and Double.parseDouble to read. These always use a period and no grouping. |
Methods that take a format string follow the default locale: String.format("%,.2f", x) prints a decimal comma on a German system. For data, pass Locale.ROOT as the first argument.
Common Mistakes
Parsing user input with Double.parseDouble. It accepts only the plain form with a period; "1.234,5" fails, and "1,234" fails too.
Writing data files with the default locale. A file written on a German machine can't be read on an American one.
Assuming three-digit groups. Grouping rules belong to the locale data; never insert separators by hand.
Ignoring what parse accepted. It stops at the first character it doesn't understand; "12abc" parses as 12. Use parse(text, position) and check the position when the whole text must be a number.
Practice
- Easy: Format 0.075 as a percentage with one decimal place for
Locale.USandLocale.GERMANY. - Harder: Write a method that parses a number typed by a user in a given locale and rejects input with leftover characters, using
ParsePosition.
Key Takeaways
Was this tutorial helpful?