1. Introduction

In this tutorial, we'll learn how to format currencies by locale using Thymeleaf.

2. Maven Dependencies

Let's begin by importing the Spring Boot Thymeleaf dependency:

<dependency>
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-thymeleaf</artifactId> 
    <version>2.2.7.RELEASE</version>
</dependency>

3. Project Setup

Our project will be a simple Spring web application that displays currencies based on the user's locale. Let's create our Thymeleaf template, currencies.html, in resources/templates/currencies:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Currency table</title>
    </head>
</html>

We can also create a controller class that will handle our requests:

@Controller
public class CurrenciesController {
    @GetMapping(value = "/currency")
    public String exchange(
      @RequestParam(value = "amount") String amount, Locale locale) {
        return "currencies/currencies";
    }
}

4. Formatting

When it comes to currencies, we need to format them based on the requester's locale.

In this case, we'll send the Accept-Language header with each request to represent our user's locale.

4.1. Currency

The Numbers class, provided by Thymeleaf, has support for formatting currencies. So, let's update our view with a call to the formatCurrency method

<p th:text="${#numbers.formatCurrency(param.amount)}"></p>

When we run our example, we'll see the currency properly formatted:

@Test
public void whenCallCurrencyWithUSALocale_ThenReturnProperCurrency() throws Exception {
    mockMvc.perform(MockMvcRequestBuilders.get("/currency")
      .header("Accept-Language", "en-US")
      .param("amount", "10032.5"))
      .andExpect(status().isOk())
      .andExpect(content().string(containsString("$10,032.50")));
}

Since we set the Accept-Language header to the United States, the currency is formatted with a decimal point and a dollar sign.

4.2. Currency Arrays

We can also use the Numbers class to format arrays. As a result, we'll add another request parameter to our controller:

@GetMapping(value = "/currency")
public String exchange(
  @RequestParam(value = "amount") String amount,
  @RequestParam(value = "amountList") List amountList, Locale locale) {
    return "currencies/currencies";
}

Next, we can update our view to include a call to the listFormatCurrency method:

<p th:text="${#numbers.listFormatCurrency(param.amountList)}"></p>

Now let's see what the result looks like:

@Test
public void whenCallCurrencyWithUkLocaleWithArrays_ThenReturnLocaleCurrencies() throws Exception {
    mockMvc.perform(MockMvcRequestBuilders.get("/currency")
      .header("Accept-Language", "en-GB")
      .param("amountList", "10", "20", "30"))
      .andExpect(status().isOk())
      .andExpect(content().string(containsString("£10.00, £20.00, £30.00")));
}

The result shows the currency list with the proper United Kingdom formatting added.

4.3. Trailing Zeros

Using Strings#replace, we can remove the trailing zeros.

<p th:text="${#strings.replace(#numbers.formatCurrency(param.amount), '.00', '')}"></p>

Now we can see the full amount without trailing double zeros:

@Test
public void whenCallCurrencyWithUSALocaleWithoutDecimal_ThenReturnCurrencyWithoutTrailingZeros()
  throws Exception {
    mockMvc.perform(MockMvcRequestBuilders.get("/currency")
      .header("Accept-Language", "en-US")
      .param("amount", "10032"))
      .andExpect(status().isOk())
      .andExpect(content().string(containsString("$10,032")));
}

4.4. Decimals

Depending on the locale, decimals may be formatted differently. Therefore, if we want to replace a decimal point with a comma, we can use the formatDecimal method provided by the Numbers class:

<p th:text="${#numbers.formatDecimal(param.amount, 1, 2, 'COMMA')}"></p>

Let's see the outcome in a test:

@Test
public void whenCallCurrencyWithUSALocale_ThenReturnReplacedDecimalPoint() throws Exception {
    mockMvc.perform(MockMvcRequestBuilders.get("/currency")
      .header("Accept-Language", "en-US")
      .param("amount", "1.5"))
      .andExpect(status().isOk())
      .andExpect(content().string(containsString("1,5")));
}

The value will be formatted as “1,5”.

5. Conclusion

In this short tutorial, we showed how Thymeleaf can be used with Spring Web to handle currencies using the user's Locale.

As always, the code is available over on GitHub.