Part 4: Internationalization in Spring Boot

One of the many features provided by Spring Boot’s automatic configuration is a ResourceBundleMessageSource. This is the foundation of the support for internationalization provided by Spring as part of Spring Boot. However, resolving the user’s current locale, and being able to switch locales on demand is a little trickier, and is something that I had a hard time finding a comprehensive example of. In this installment, we’ll cover how to set up a resource bundle for messages, how to name them to support locales, and finally, how to wire a LocaleResolver and LocaleChangeInterceptor into our test application so that we can implement and test Spring’s internationalization support.

As usual, for this installment, I’ve created a copy of the code from Part 3 and created a new project called Part 4. It’s committed to Github, ready for cloning.

The Resource Bundle
First, we’ll create a resource bundle with message files for English and Spanish in our sample application. When provided a locale, the auto-configured message source can dynamically look up a message file using a default base file name – for example, the default messages file will be named “messages.properties”, whereas the file for Spanish messages will be called “messages_es.properties”. Likewise, the file for English language messages will be “messages_en.properties”. Basically, the message source resolves the file name in which to look for message properties by concatenating Spring’s default base file name “messages”, with an underscore and the locale name. The default location for these files, as specified by the auto-configuration, is “/src/main/resources”.

Create two files in /src/main/resources: messages_en.properties, and messages_es.properties.

We’ll add a couple of messages to each file, and we’ll use them to change the labels on our sample application’s based on the client’s locale.

Update “/src/main/resources/messages_en.properties”, adding the following properties and values:

/src/main/resources/messages_en.properties:

field1 = Field 1
field2 = Field 2

and likewise, for our Spanish clients, update “src/main/resources/messages_es.properties” to include the Spanish versions of the same properties and values (note that I do not speak Spanish. I typed these names into Google Translate. I think they are accurate enough for this example)

/src/main/resources/messages_es.properties:

field1 = El Campo 1
field2 = El Campo 2

Updating The View Template
The view template will need to be updated with placeholders containing the keys that Thymeleaf can use to swap in values from our message files. We’ve seen usages of ${} and *{} in these tutorials. The next one we’ll use is #{}, which is the placeholder Thymeleaf uses to bind messages, and can be used to do general string manipulation within the view.

Update our view template to include two new placeholders such that:

/src/main/resources/hello.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head lang="en">
    <meta charset="UTF-8" />
    <title>HELLO</title>
</head>
<body>
<p th:text="${message}"></p>
<form id="gizmo-form" role="form" th:action="@{/save}" method="post" th:object="${gizmo}">
    <div>
        <label for="field1" th:text="#{field1}"></label>
        <input type="text" id="field1" name="field1" th:field="${gizmo.field1}"/>
    </div>
    <div>
        <label for="field2" th:text="#{field2}"></label>
        <input type="text" id="field2" name="field2" th:field="${gizmo.field2}"/>
    </div>
    <div>
        <ul>
            <li th:each="item, stat : *{children}" class="itemRow">
                <div>
                    <label th:for="${'childField1-'+stat.index}">Field 1</label>
                    <input type="text" class="form-control quantity" name="childField1"
                           th:field="*{children[__${stat.index}__].childField1}" th:id="${'childField1-'+stat.index}"/>

                    <label th:for="${'childField2-'+stat.index}">Field 2</label>
                    <input type="text" class="form-control quantity" name="childField2"
                           th:field="*{children[__${stat.index}__].childField2}" th:id="${'childField2-'+stat.index}"/>
                </div>
            </li>
        </ul>
    </div>
    <div>
        <button type="submit">Save</button>
    </div>
</form>
</body>
</html>

You can see we’ve removed the label text (formerly “Field 1” and “Field 2″ for the ‘field1’ and ‘field2’ properties on the Gizmo object and added new th:text references containing our new binding types. In these cases, our goal will be to replace #{field1} with the value of the “field1” property in the appropriate messages file, and to replace the value of #{field2} in a similar fashion. If you were to start the server at this point, the solution would still not work. The last step ties the views to the message files.

Configuring The LocaleResolver
In order for Spring to know which message file’s values to make available to Thymeleaf, the application needs to be able to determine which locale the application is currently running in. For this, we need to configure a LocaleResolver.

In ‘src/main/java/com.rodenbostel.sample.Application.java’ file, configure a new LocaleResolver bean.

    @Bean
    public LocaleResolver localeResolver() {
        SessionLocaleResolver slr = new SessionLocaleResolver();
        slr.setDefaultLocale(Locale.US);
        return slr;
    }

This will configure a locale resolver that fits the following description, according to the Spring API:

“Implementation of LocaleResolver that uses a locale attribute in the user’s session in case of a custom setting, with a fallback to the specified default locale or the request’s accept-header locale”

The only item remaining is how to switch the locale without updating the configuration of the bean above and restarting our server or changing our default or accept-header specified locale.

Configuring a LocaleChangeInterceptor
Configuring an interceptor that is responsible or swapping out the current locale allows for easy testing by a developer, and also gives you the option of including a select list in your UI that lets the user pick the locale they prefer. Add the following bean to your ‘src/main/java/com.rodenbostel.sample.Application.java’ file:

    @Bean
    public LocaleChangeInterceptor localeChangeInterceptor() {
        LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
        lci.setParamName("lang");
        return lci;
    }

As you can see, this interceptor will look for a request parameter named ‘lang’ and will use its value to determine which locale to switch to. For example, adding ‘lang=en’ to the end of any request will render the messages from default English locale’s message file. Changing that parameter to ‘lang=es’ will render the Spanish version. For any interceptor to take effect, we need to add it to the application’s interceptor registry. In order to do that, we need to get a handle and override Spring Boot Web’s addInterceptor configuration method.

In order to do that, we need to update our ‘src/main/java/com.rodenbostel.sample.WebApplication.java’ file to extend WebMvcConfigurerAdapter. The WebMvcConfigurerAdapter’s existence is based on around activities such as this. It provides hooks to override base Spring configuration. Let’s update the class to extend this super class and add our interceptor:

package com.rodenbostel.sample;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;

import java.util.Locale;

@Configuration
@EnableAutoConfiguration
@ComponentScan
public class Application extends WebMvcConfigurerAdapter {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public LocaleResolver localeResolver() {
        SessionLocaleResolver slr = new SessionLocaleResolver();
        slr.setDefaultLocale(Locale.US);
        return slr;
    }

    @Bean
    public LocaleChangeInterceptor localeChangeInterceptor() {
        LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
        lci.setParamName("lang");
        return lci;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(localeChangeInterceptor());
    }

}

Testing
Start your server and observe the default locale in action:
Screen Shot 2014-05-13 at 5.20.00 PM
then, add our request parameter and refresh the page to change the locale so that our Spanish language file is loaded:
Screen Shot 2014-05-13 at 5.20.52 PM
As Spring Boot matures, I’m sure the available examples will be updated to include end to end samples such as this. In the mean time, I hope you found this simple example useful. Check back soon for Spring Security integration in Part 5.

Advertisements

Part 3: Form Binding

Welcome back. Now that we have Thymeleaf view templates rendering in our Spring Boot test app, the next logical topic is learning how to bind java objects to forms in these views for processing. In this installment, we’ll cover binding a simple object to a form and then take a step deeper and bind an object with a list of children to a form. These are basic examples, but should show enough to get you started in most cases, and should be step beyond what’s available in the examples from Thymeleaf and Spring.

As usual, for this installment, I’ve created a copy of the code from part 2 and created a new project called Part 3. It’s committed to Github, ready for cloning.

The Gizmo
To get started, we’ll create a simple object that will eventually back a form in our app. We’ll call it gizmo, and we’ll give it two String fields with accessors and mutators.

/src/main/java/com.rodenbostel.sample.Gizmo.java:

package com.rodenbostel.sample;

public class Gizmo {

    private String field1;
    private String field2;

    public String getField1() {
        return field1;
    }

    public void setField1(String field1) {
        this.field1 = field1;
    }

    public String getField2() {
        return field2;
    }

    public void setField2(String field2) {
        this.field2 = field2;
    }
}

Passing Gizmo To The View
Next, we’ll make a small change to our SampleController that will allow us to pass a gizmo instance to the browser in place of our previous message.

For this, change the previous version:

src/main/java/com.rodenbostel.sample.SampleController.java:

    @RequestMapping("/")
    public String index(Model model) {
        model.addAttribute("message", "HELLO!");
        return "hello";
    }

to this:

    @RequestMapping("/")
    public String index(Model model) {
        model.addAttribute("gizmo", new Gizmo());
        return "hello";
    }

Updating The View
Next, in place of displaying static text, we’ll update our view template so that it contains a form to which we can bind the instance of Gizmo. I’ll walk through the important bits after the code sample.

src/main/resources/hello.html:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head lang="en">
    <meta charset="UTF-8" />
    <title>HELLO</title>
</head>
<body>
<form id="gizmo-form" role="form" th:action="@{/save}" method="post" th:object="${gizmo}">
    <div>
        <label for="field1">Field 1</label>
        <input type="text" id="field1" name="field1" th:field="${gizmo.field1}"/>
    </div>
    <div>
        <label for="field2">Field 2</label>
        <input type="text" id="field2" name="field2" th:field="${gizmo.field2}"/>
    </div>
    <div>
        <button type="submit">Save</button>
    </div>
</form>
</body>
</html>

Notice we’ve create a form and dropped in two “th” attributes – action and object. The th:action attribute signifies to what path the form’s contents will be submitted, and th:object tells Thymeleaf where to find the object to be bound in the response parameters – in our case, in the Model we’re returning with every controller method call.

Also notice the body of the form – we’re using the th:field attribute on the input elements we’ve created. This tells Thymeleaf which fields on the th:object to bind to these inputs.

Last, you may have realized that we haven’t create a method in our controller with an @ResponseMapping that will wire to the th:action we’ve specified… We need somewhere to post this form to.

Adding The Controller Method
Add a new method to your SampleController that will respond to a post issued from our new form

/src/main/java/com.rodenbostel.SampleController:

    @RequestMapping("/save")
    public String save(Gizmo gizmo) {
        System.out.println(gizmo.getField1());
        System.out.println(gizmo.getField2());
        return "hello";
    }

Notice that we don’t do anything special to bind the incoming request to our Gizmo model object on the server side. If the properties on the bound object match the request parameter structure, the request will bind automatically. Specialized binding can be accomplished using the @RequestParam annotation.

Also notice that we’re simply printing out the contents of the Gizmo object and rendering the same view again with the same form contents. Alternatively, you can issue a redirect to the “/“ path to clear the form, following the route that is
used when the app is started.

Instead of:

return “hello“;

redirect using:

return "redirect:/";

Start up your server to view the work we’ve done so far. You should see an empty form that looks similar to this:

Screen Shot 2014-05-12 at 5.25.52 PM

and when you complete the form and submit it, you should see output in your console similar to the following:
2014-05-12 17:26:50.780 INFO 23927 — [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet ‘dispatcherServlet’: initialization completed in 9 ms
hello
world

Binding Child Objects
A common use case involves binding child objects to the same form a parent object is bound to – items on an order, addresses on a user profile, etc. The syntax for accomplishing this in Thymeleaf is a bit messy, mostly due to the fact that this type of binding requires nested, escaped quotation marks to accomplish.

Let’s add a new object to the mix, one that will be the child of Gizmo.

/src/main/java/com.rodenbostel.sample.GizmoChild:

package com.rodenbostel.sample;

public class GizmoChild {
    private String childField1;
    private String childField2;

    public String getChildField1() {
        return childField1;
    }

    public void setChildField1(String childField1) {
        this.childField1 = childField1;
    }

    public String getChildField2() {
        return childField2;
    }

    public void setChildField2(String childField2) {
        this.childField2 = childField2;
    }
}

Another simple class with simple fields. Next, let’s add an iterable collection of these objects to the Gizmo object as a property.

/src/main/java/com.rodenbostel.Gizmo:

package com.rodenbostel.sample;

import java.util.ArrayList;
import java.util.List;

public class Gizmo {

    private String field1;
    private String field2;
    private List<GizmoChild> children;

    public String getField1() {
        return field1;
    }

    public void setField1(String field1) {
        this.field1 = field1;
    }

    public String getField2() {
        return field2;
    }

    public void setField2(String field2) {
        this.field2 = field2;
    }

    public List<GizmoChild> getChildren() {
        if(children == null) {
            children = new ArrayList<GizmoChild>();
        }
        return children;
    }

    public void setChildren(List<GizmoChild> children) {
        this.children = children;
    }
}

Updating The View (again)
We’ll add a new section to our form so that we can iterate through the collection of GizmoChildren, creating a row of inputs for each element in the collection, and dynamically naming them so they bind correctly on the server side.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head lang="en">
    <meta charset="UTF-8" />
    <title>HELLO</title>
</head>
<body>
<form id="gizmo-form" role="form" th:action="@{/save}" method="post" th:object="${gizmo}">
    <div>
        <label for="field1">Field 1</label>
        <input type="text" id="field1" name="field1" th:field="${gizmo.field1}"/>
    </div>
    <div>
        <label for="field2">Field 2</label>
        <input type="text" id="field2" name="field2" th:field="${gizmo.field2}"/>
    </div>
    <div>
        <ul>
            <li th:each="item, stat : *{children}" class="itemRow">
                <div>
                    <label th:for="${'childField1-'+stat.index}">Field 1</label>
                    <input type="text" class="form-control quantity" name="childField1"
                           th:field="*{children[__${stat.index}__].childField1}" th:id="${'childField1-'+stat.index}"/>

                    <label th:for="${'childField2-'+stat.index}">Field 2</label>
                    <input type="text" class="form-control quantity" name="childField2"
                           th:field="*{children[__${stat.index}__].childField2}" th:id="${'childField2-'+stat.index}"/>
                </div>
            </li>
        </ul>
    </div>
    <div>
        <button type="submit">Save</button>
    </div>
</form>
</body>
</html>

You can see the unordered list’s first child has a “th:each” attribute. That’s Thymeleaf’s comprehension mechanism for collections. The first two parameters – item and stat basically represent the current item in the collection and the state of the iterator at the time the element is being rendered. We can use this stat variable to get the index of the current item being displayed. We also see “*{children}”. This is slightly different from earlier examples and shows how Thymeleaf can scope binding to a parent object. In this case, our form is bound to gizmo, and we can reference it’s property named ‘children’ by using an asterisk instead of a dollar sign.

For each item in the collection, we’re creating two input fields with ids of childField1-[index] and childField2-[index]. You’ll also see the “th:field” attribute used again on these new inputs. In this case we’re building the binding names for these fields dynamically. It’s easiest explained by looking at the generated html.

Start your server and let’s look at the output.

The Rendered Product
You should see a form similar to the following after you start your server.

Screen Shot 2015-01-02 at 9.50.20 AM

And when we view the source for the same page, we should see something similar to this within our form:

<form id="gizmo-form" role="form" method="post" action="/save">
    <div>
        <label for="field1">Field 1</label>
        <input type="text" id="field1" name="field1" value="" />
    </div>
    <div>
        <label for="field2">Field 2</label>
        <input type="text" id="field2" name="field2" value="" />
    </div>
    <div>
        <ul>
            <li class="itemRow">
                <div>
                    <label for="childField1-0">Field 1</label>
                    <input type="text" class="form-control quantity" name="children[0].childField1" id="childField1-0" value="" />

                    <label for="childField2-0">Field 2</label>
                    <input type="text" class="form-control quantity" name="children[0].childField2" id="childField2-0" value="" />
                </div>
            </li>
        </ul>
    </div>
    <div>
        <button type="submit">Save</button>
    </div>
</form>

You can see the names produced by Thymeleaf fit the standard Spring MVC compliant naming for child objects. They are identified uniquely as they would be an plain object notation.

Let’s update our controller to add a GizmoChild instance to our Gizmo on initial request, and print out our new child’s output on response:

/src/main/java/com.rodenbostel.sample.SampleController.java:

package com.rodenbostel.sample;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class SampleController {

    @RequestMapping("/")
    public String index(Model model) {
        Gizmo gizmo = new Gizmo();
        gizmo.getChildren().add(new GizmoChild());
        model.addAttribute("gizmo", gizmo);
        return "hello";
    }

    @RequestMapping("/save")
    public String save(Gizmo gizmo) {
        System.out.println(gizmo.getField1());
        System.out.println(gizmo.getField2());
        for(GizmoChild child : gizmo.getChildren()) {
            System.out.println(child.getChildField1());
            System.out.println(child.getChildField2());
        }
        return "redirect:/";
    }
}

As you can see, in our index method, we’re adding a child object to our Gizmo instance prior to adding to our model for display on the screen. Then, in the ‘save’ method, we’re looping through the collection of children and printing the output to our console. With our server running, by completing the form and submitting, we should see first our Gizmo’s properties, followed by the values for the fields on each child object.

Conclusion
Although this is a simplified example, this can easily be expanded to accommodate more complex use cases. Maybe you’re reading your Gizmos from a database, or maybe you need to create new Gizmos from scratch. The child collection needs to be instantiated for the Thymeleaf template to render correctly – it can be empty though. Having one child element in a collection in a new Gizmo object gives Thymeleaf a blank object to render. However, the fields Thymeleaf renders for that blank object could be created by jQuery (or another javascript library), and can be further added/removed from there.

Part 2: Adding Views Using Thymeleaf (and JSP if you want)

In Part 1 of this series, we set up a sample application using Spring Boot and some of it’s default configuration options. Now that we’ve got the shell of an application, we can start adding content to our application. By adding a single dependency to our project and using Spring Boot’s auto configuration settings, we get a free Thymeleaf configuration that’s ready to go.

For this installment, I’ve created a copy of the code from part 1 and created a new project. It’s committed to Github, ready for cloning.

Adding The Dependency

In the dependencies section of your build.gradle file, add the following entry:

compile("org.thymeleaf:thymeleaf-spring4:2.1.2.RELEASE”)

The end result should leave your build.gradle following looking like this:

buildscript {
    repositories {
        maven { url "http://repo.spring.io/libs-snapshot" }
        mavenLocal()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.0.1.RELEASE")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'spring-boot'
apply plugin: 'war'

jar {

    baseName = 'part-2'
    version =  '0.1.0'
}

repositories {
    mavenCentral()
    maven { url "http://repo.spring.io/libs-snapshot" }
}

dependencies {
    compile("org.springframework.boot:spring-boot-starter-web")
    compile("org.thymeleaf:thymeleaf-spring4:2.1.2.RELEASE")

    testCompile("junit:junit")
}

task wrapper(type: Wrapper) {
    gradleVersion = '1.11'
}

Running a simple ‘gradle build’ from the CLI will install this new dependency.

Building On Part 1

At the conclusion of Part 1, we had a single controller in our project configured as a @RestController, which returned a simple string. To start using our new Thymeleaf installation, we need to create a view, and make minor changes to our controller.

The auto-configured Thymeleaf install looks for Thymeleaf templates to be available as resources on the classpath in a folder named ‘templates’.

Create a resources folder, and a folder named ‘templates’ within it, such that your project’s directory structure appears similar to the following:

Screen Shot 2014-04-15 at 9.51.50 PM

Creating A Template

Thymeleaf templates are intended to be natural – as close to straight HTML as possible, and easily interpreted by browsers and developers alike. Thymeleaf is packed with several dialects, and takes the place of JSP in the default stack provided by Spring.

Inside of our new templates folder (/src/main/resources/templates/), we need to create a new template that we’ll use as our first view. Create a file named “hello.html” that contains the following text:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head lang="en">
    <meta charset="UTF-8" />
    <title>HELLO</title>
</head>
<body>
<p th:text="${message}"></p>
</body>
</html>

There are two important parts of this simple example: First, in the html tag, you’ll see two new attributes that set up template to be processed by Thymeleaf. This will give the page access to use the family of “th” attributes in html tags, thereby having access to the features available in the basic Thymeleaf dialect. Second, in the body of the page, there is a “p” tag with a simple “th” attribute. We’ll see the usage of this shortly.

Modifying The Controller

As previously mentioned, our @RestController must change to return a processed view template instead of a simple String, as it does now. First, open our existing controller class, SampleController.java, and change the marker interface from @RestController on it so that it reflects a simple controller (@Controller).

@Controller
public class SampleController {

In order to provide our Thymeleaf template with data to display, we need to change our method’s signature to accept a Model, which we can then begin to populate. Model is much like it was in previous versions of Spring MVC – an arbitrary collection of java objects that can be displayed and bound to views. The framework will provide our controller method with an instance of a Model object for us populate. Change the “index” method on the SampleController to have the following signature:

public String index(Model model) {

Next, we’ll populate the “message” attribute of the Model instance so that we can see our message in our newly created view. Update the SampleController to do just that:

@RequestMapping("/")
    public String index(Model model) {
        model.addAttribute("message", "HELLO!");
        return "hello";
    }

Start your server to see our new page in action.

Screen Shot 2014-04-15 at 10.53.53 PM

This is a very basic example, but you can see that the “${message}” element in our Thymeleaf template has been replaced by the element that we put in the attribute named “message” in our UI Model in our controller. Look at the page source to see the end result.

A Step Further

Let’s say you want to change the path to templates or revert to JSPs. The settings provided by Spring Boot can be easily overridden by configuring an InternalResourceViewResolver in our Application.java config file. In the example below, you can see a change to the template path as well as a change to the template technology being used.

@Bean        
    public InternalResourceViewResolver internalResourceViewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("WEB-INF/jsp/");
        resolver.setSuffix(".jsp");
        resolver.setViewClass(JstlView.class);
        return resolver;
    }

Of course, if you’ve changed to JSP, you’ll also have to add the appropriate dependencies to your build.gradle file. Both of the dependencies in question are available with embedded Tomcat, so they’ll be ‘providedRuntime’ dependencies.

    providedRuntime("javax.servlet:jstl")
    providedRuntime("org.apache.tomcat.embed:tomcat-embed-jasper")

Conclusion

Hopefully, this installment sheds additional light on the basic configuration of Thymeleaf and points you in the correct direction if you need to configure your application beyond what is provided out of the box by Spring Boot Web. Next up, we’ll review objecting binding and validation in Thymeleaf, getting deeper into the details of the Thymeleaf dialects and the functionality they provide. Again, find the complete source code for Part 2 on Github!