Localized Currency and Dates in Java

dimitrilc
Introduction

When working with an application with a global user base, there is usually a need to display text, numbers, currency, date, and time in multiple localized formats. For example, the en-US (English-USA) version of the date

September 22, 2021 

is written as

22 сентября 2021 г.

when the locale used is ru-RU (Russian-Russia).

In this tutorial, we will learn how to format currency and date in localized formats using java.time.* and java.text.* classes.

Goals

At the end of this tutorial, you would have learned:

  1. How to format currency based on locale.
  2. How to format dates based on locale.
Prerequisite Knowledge
  1. Basic Java.
Tools Required
  1. A Java IDE.
Locale Overview

Before proceeding further, let us discuss an important concept: java.util.Locale.

A Locale object is an encapsulation of data used to specify the language, the country, and/or the variant of a geographic region.

There are 3 Locale constructors:

    public Locale(String language)
    public Locale(String language, String country)
    public Locale(String language, String country, String variant)

When creating a Locale object (the first constructor), you must at least pass in a language code, which must be in the form of “an ISO 639 alpha-2 or alpha-3 language code, or a language subtag up to 8 characters in length.” For this tutorial, we will only be using the ISO 639-1 alpha-2 language code, which is in the form of lowercase 2 letters abbreviation (i.e, “en”, “es”).

For the second constructor, we can pass in a country code as well, which must be in the form of “An ISO 3166 alpha-2 country code or a UN M.49 numeric-3 area code.” For this tutorial, we will be using the ISO 3166 alpha-2 country code, which is in the form of uppercase 2 letters abbreviation (i.e, “US”, “UK”).

We do not concern ourselves with the locale variant in this tutorial.

Besides using constructors, Locale objects can also be created using the convenient constants that the Locale class provides, such as

Locale.UK

or

Locale.CANADA

After retrieving the Locale object, we can then pass it to DateTimeFormatter or NumberFormat instances to localize the data.

Project Setup

Now that we have finished talking about concepts, perform the steps below to set up our project:

  1. Create a new Java project.
  2. Create a package com.example.
  3. Create a Java class called Entry.java. This is where our main() method lives.
Localizing Dates

First, create a method called localizeDate() that takes a LocalDate argument and a Locale argument. This method will return the formatted String representation of a localized date.

    private static String localizeDate(LocalDate date, Locale locale){ //7
       DateTimeFormatter formatter = DateTimeFormatter //8
               .ofLocalizedDate(FormatStyle.LONG) //9
               .withLocale(locale); //10

       return formatter.format(date); //11
    }

DateTimeFormatter does not have a public constructor, so we have to use the static factory methods to create an instance of DateTimeFormatter. The static factory method used has this signature:

public static DateTimeFormatter ofLocalizedDate(FormatStyle dateStyle)

It requires a FormatStyle enum as an argument. So we pass to it a FormatStyle.LONG constant at line 9. There are 4 constants in total in FormatStyle, SHORT, MEDIUM, LONG, and FULL. You can experiment with passing in different constants to see how the output changes.

On line 10, we use the withLocale() method to return a copy of the previous instance (including FormatStyle choice) with our choice of Locale.

To format the date, we only have to call the format() method at line 11.

Back in main(), create a new array of Locales and one LocalDate object to pass to our localizeDate() method.

    //Pre-made locales
    Locale[] locales = {
           Locale.US,
           Locale.JAPAN,
           Locale.FRANCE,
           Locale.KOREA,
           new Locale("es", "PE")
    }; //1

    LocalDate now = LocalDate.now(); //2

We need an array of Locales because it is interesting to observe how different Locales can produce different output. The first 4 Locale objects in the array were obtained using the convenient constants in Locale. The last Locale object was created using the constructor (which I have explained earlier) using the language and country codes. “PE” is the ISO 3166-2 country code for Peru and “es” is the ISO 639 alpha-2 language code for Spanish.

Now loop through the array and call the localizeDate() method against each Locale using the same LocalDate object now.

    //localized date loop
    for (Locale locale : locales){
       System.out.println("Locale: " + locale + ", date: " + localizeDate(now, locale)); //3
    } //4

The code prints:

    Locale: en_US, date: September 22, 2021
    Locale: ja_JP, date: 2021年9月22日
    Locale: fr_FR, date: 22 septembre 2021
    Locale: ko_KR, date: 2021년 9월 22일
    Locale: es_PE, date: 22 de setiembre de 2021

As you can see, the ordering of the day of month, the month, and the year have all been automatically switched up depending on the Locale.

Localizing Currency

Next, we will create a method to localize currency (aka printing money in a specific format based on the Locale).

    private static String localizeCurrency(BigDecimal money, Locale locale){ //12
       return NumberFormat //13
               .getCurrencyInstance(locale) //14
               .format(money); //15
    }

In the localizeCurrency() method above, we use a similar approach to the localizeDate() method, but we have to use a different class to format monetary values, NumberFormat.

NumberFormat can format more than just currency, but we are not concerned with the other types for now; that is why we used the factory method getCurrencyInstance() at line 14 to get an instance specialized in formatting currencies. We also passed in the Locale object so the NumberFormat instance knows which Locale should the currency format be in.

Back in main(), create a random monetary value:

    BigDecimal money = BigDecimal.valueOf(1_484.2428127233); //5

Since we already declared our Locale array previously, we can just re-use it for our currency loop.

    //localized currency loop
    for (Locale locale : locales){
       System.out.println("Locale: " + locale + ", money: " + localizeCurrency(money, locale)); //6
    } //7

When executed, the code prints:

    Locale: en_US, money: $1,484.24
    Locale: ja_JP, money: ¥1,484
    Locale: fr_FR, money: 1 484,24 €
    Locale: ko_KR, money: ₩1,484
    Locale: es_PE, money: S/ 1,484.24

All of the currency symbols, number formatting, and currency symbols have been automatically added for our convenience. The last currency symbol of “S/” is not an error. “S/” is the official currency notation for the Peruvian Sol.

Solution Code
    package com.example;

    import java.math.BigDecimal;
    import java.text.NumberFormat;
    import java.time.LocalDate;
    import java.time.format.DateTimeFormatter;
    import java.time.format.FormatStyle;
    import java.util.Locale;

    public class Entry {

       public static void main(String[] args){
           //Pre-made locales
           Locale[] locales = {
                   Locale.US,
                   Locale.JAPAN,
                   Locale.FRANCE,
                   Locale.KOREA,
                   new Locale("es", "PE")
           }; //1

           LocalDate now = LocalDate.now(); //2

           //localized date loop
           for (Locale locale : locales){
               System.out.println("Locale: " + locale + ", date: " + localizeDate(now, locale)); //3
           } //4

           BigDecimal money = BigDecimal.valueOf(1_484.2428127233); //5

           //localized currency loop
           for (Locale locale : locales){
               System.out.println("Locale: " + locale + ", money: " + localizeCurrency(money, locale)); //6
           } //7
       }

       private static String localizeDate(LocalDate date, Locale locale){ //7
           DateTimeFormatter formatter = DateTimeFormatter //8
                   .ofLocalizedDate(FormatStyle.LONG) //9
                   .withLocale(locale); //10

           return formatter.format(date); //11
       }

       private static String localizeCurrency(BigDecimal money, Locale locale){ //12
           return NumberFormat //13
                   .getCurrencyInstance(locale) //14
                   .format(money); //15
       }

    }
Summary

We have learned how to localize dates and currency in this tutorial.

Please keep in mind that we have only formatted the representation of the monetary value, and we did not convert the monetary value by exchange rates at all. The number 100 can be represented as $100 USD or ¥100 YEN, but that does not mean $100 is equal to ¥100.

The full project code can be found here https://github.com/dmitrilc/DaniwebLocaleCurrency/tree/master

52 Views
About the Author

My name is Dimitri Nguyen. I am a Java Developer specializing in backend development on the Java/Spring/MySQL stack.

I can also work on the frontend using Angular/Typescript/JS/HTML/CSS and native Android with Kotlin.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts learning and sharing knowledge.