Software localization
How to Localize Spring Applications
Internationalization is the process of making your Spring application ready for multilingual support. Once internationalized, you can localize Spring applications by translating your content into any language your target users may speak.
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 the 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 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—the 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, 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.
In-context translation editing: 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.
Note » You can find all source code from this example on GitHub.