How to localize Spring applications like a Pro

How to localize Spring applications like a Pro

You've built a Spring application and now want to support other languages. This article explains how to internationalize and then localize Spring applications using Spring 4 with Thymeleaf templates.

Internationalization is the process of making your Spring application ready for multilingual support. Once internationalized you can localize spring applications by translating it into other languages.

Getting Started with Spring i18n: Preparing Your View Templates

The first step towards a localized application is to extract all static text from your templates into a localization file. Later, this is used to look up the required text bits according to the selected language.

Let’s have a look at our homepage template src/main/resources/templates/home.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
    <h1>Welcome to my site!</h1>
    <p>This is my English intro.</p>
</body>
</html>

After replacing all static text with placeholders, it should look something like this:

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
    <h1 th:text="#{headline}"></h1>
    <p th:text="#{intro_text}"></p>
</body>
</html>

Storing Translations

Next, create your default resource bundle localization file and populate it with the extracted text bits src/main/resources/messages.properties:

headline = Welcome to my site!
intro_text = This is my English intro.

Spring MessageSource resource bundles are the default file format used for localizing most Java apps. Spring also comes with great support for storing and retrieving translations from those files. The messages.properties file now holds your original copy. Many teams work with English as source language, but any other language works as well.

Localization Support

To effectively tell your Spring application which language to currently use, you must configure a LocaleResolver:

public class Application extends WebMvcConfigurerAdapter {
    ...
    @Bean
    public LocaleResolver localeResolver() {
        SessionLocaleResolver slr = new SessionLocaleResolver();
        slr.setDefaultLocale(Locale.ENGLISH);
        return slr;
    }
    ...
}

In this case we use the SessionLocaleResolver which stores the selected locale (= language) in the user’s session. Also, we set the default locale to be English.

Next, let’s add a way to make the locale selectable via a query parameter in our URL. We can do this by adding a LocaleChangeInterceptor:

public class Application extends WebMvcConfigurerAdapter {

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

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

This interceptor allows us to overwrite the current locale by providing a locale name via the lang parameter, e.g. http://localhost:8080/?lang=de.

Adding a New Language

Let’s now add a second language to our project. In this example we will add German as the second language. We create a copy of the initial messages.properties . Next, we append the locale name (i.e. messages_de.properties) and translate the content within the file:

headline = Willkommen auf meiner Seite!
intro_text = Dies ist meine deutsche Einleitung.

After restarting your application, you should still see the original English content. However, when you append the lang parameter to the URL, you will see the German translations. Congratulations, you’ve just completed your first localization!

Localize Spring Applications – Next level

This was all pretty straightforward. With time your application might grow and so will your resource bundle. Maybe it will become hard to maintain. Also, your translators might not be able to work with the .properties format. At this point a software like Phrase can help you organize your translations. Phrase can be integrated in a few minutes and makes translation content accessible through a web interface for translators.

First, sign up for a free trial and create a project for your Spring app using the getting-started wizard. Next, download the Phrase CLI client and run it within your project root folder to initialize your project:

$ phraseapp init

After following the init wizard, you should be able to upload your existing translations to Phrase with a simple command:

$ phraseapp push

All your translations are now available for editing within Phrase’s Translation Center. Once the translators have finished their work, you can download the finished translations back into your project using the command:

$ phraseapp pull

With minimal effort you just provided your teammates or external translators with an easy way to work on texts used on your website.

Editing Translations In-Context: WYSIWYG

This is already a quite professional translation workflow. But we can go even one step further and add an in-page editor for the translations used in our Spring application.

First, we need to override the default MessageSource with the PhraseMessageSource:

public class Application extends WebMvcConfigurerAdapter {
    ...
    @Bean
    @Conditional(PhraseAppEnabledCondition.class)
    public MessageSource messageSource() {
        return new PhraseAppMessageSource();
    }

    @Bean
    public PhraseAppJavascriptHeader phraseAppJavascriptHeader() {
        return new PhraseAppJavascriptHeader();
    }
    ...
}

Now, let’s add the Phrase In-Context Editor by adding a small bit of Javascript to the head of our templates:

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <script th:utext="${@phraseAppJavascriptHeader.getHeader()}"></script>
</head>
<body>
    <h1 th:text="#{headline}"></h1>
    <p th:text="#{intro_text}"></p>
</body>
</html>

As a final step, make sure to have the correct Phrase project id provided in the configuration class:

public class PhraseAppConfiguration {
    public static final boolean PHRASEAPP_ENABLED = false;
    public static final String PHRASEAPP_PROJECT_ID = "____YOUR_PROJECT_ID____";
    public static final String PHRASEAPP_PREFIX = "{{__";
    public static final String PHRASEAPP_SUFFIX = "__}}";
}

After restarting your application you will now see the Phrase In-Context Editor. Sign in using your Phrase credentials to start editing your translations directly on your web application, like a pro.

To see how the In-Context editor looks like, you can also head over to our demo.

You can find all source code from this example on GitHub.
Thanks to Szilard Bozoki (beerides.com) for suggesting this integration and providing example Spring code on it! 

Further Reading:

5 (100%) 6 votes
Comments