Almost every important application has to support several languages sooner or later. How to achieve this goal with the MVC API 1.0 is described below with an example.
In MVC 1.0, the MvcContext
can be used to retrieve the Locale
for each request via MvcContext#getLocale()
. The Locale
is taken from the HTTP header Accept-Language
by the default LocaleResolver
of the used implementation.
If the header is missing, the system default Locale
is used.
A simple web application is used for the example. The project layout for this is as follows:
In addition, these are the required Maven dependencies:
<repositories>
<repository>
<id>sonatype-oss-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>javax.mvc</groupId>
<artifactId>javax.mvc-api</artifactId>
<version>1.0.0</version>
</dependency>
<!-- Change if your application server doesn't use Jersey -->
<dependency>
<groupId>org.eclipse.krazo</groupId>
<artifactId>krazo-jersey</artifactId>
<version>1.1.0-M1</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>8.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
Attention: The project was implemented on Payara 5 and therefore uses the jersey implementation from Eclipse Krazo. For Wildfly or OpenLiberty / TomEE please use the appropriate artifacts.
The translations used later in the application are stored in the respective messages.properties
:
messages.properties
greeting=Hello!
farewell=Good bye!
messages_de.properties
greeting=Hallo!
messages_fr.properties
greeting=Bonjour!
The application uses an "empty" JAX-RS application and a controller whose only method returns the index.jsp
template. This works similar to the Hello World example and is therefore not explained in detail.
The starting point of this approach to the internationalization of MVC applications is the class Messages
. This provides a single
method get(String)
, which determines the correct translation based on the key of a key-value pair set in the messages_XYZ.properties
.
/**
* Provides I18n messages for the UI per request. To get the correct locale, the method {@link MvcContext#getLocale()} is used.
* This method uses the built-in {@link javax.mvc.locale.LocaleResolver} of the used MVC Implementation.
*
* @author Tobias Erdle
* @see MvcContext#getLocale()
* @see javax.mvc.locale.LocaleResolver
*/
@RequestScoped
@Named("msg")
public class Messages {
private static final String BASE_NAME = "messages";
@Inject
private MvcContext mvcContext;
/**
* Get the assigned message to some key based on the {@link java.util.Locale} of the current request.
*
* @param key the message key to use
* @return the correct translation assigned to the key for the request locale, a fallback translation or
* a placeholder for unknown keys.
*/
public final String get(final String key) {
final var bundle = ResourceBundle.getBundle(BASE_NAME, mvcContext.getLocale());
return bundle.containsKey(key) ? bundle.getString(key) : formatUnknownKey(key);
}
private static String formatUnknownKey(final String key) {
return String.format("???%s???", key);
}
}
Since the class is annotated with @Named
, it can be used directly in MVC templates (if the ViewEngine
supports CDI). Since
the ViewEngine
for JSPs can do this, the Messages
classes in the example template will be called as follows:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>I18n Index</title>
</head>
<body>
<div id="known-message">
<p>${msg.get("greeting")}</p>
</div>
<div id="fallback-message">
<p>${msg.get("farewell")}</p>
</div>
<div id="unknown-message">
<p>${msg.get("something-unknown")}</p>
</div>
</body>
</html>
With each call to get(String)
the appropriate ResourceBundle
for the Locale
occurring in the request is now loaded and searched for a translation of the specified key. If the key is found, but none of the found ResourceBundles
matches, the default Locale
's messages.properties
or the general messages.properties
is used.
If you now execute the example with different Locale
s, you get the following HTML pages according to the messages.properties
:
curl -X GET http://localhost:8080/mvc-language-examples/mvc/greeting -H 'Accept-Language: en'
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>I18n Index</title>
</head>
<body>
<div id="known-message">
<p>Hello!</p>
</div>
<div id="fallback-message">
<p>Good bye!</p>
</div>
<div id="unknown-message">
<p>???something-unknown???</p>
</div>
</body>
</html>
curl -X GET http://localhost:8080/mvc-language-examples/mvc/greeting -H 'Accept-Language: de'
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>I18n Index</title>
</head>
<body>
<div id="known-message">
<p>Hallo!</p>
</div>
<div id="fallback-message">
<p>Good bye!</p>
</div>
<div id="unknown-message">
<p>???something-unknown???</p>
</div>
</body>
</html>
curl -X GET http://localhost:8080/mvc-language-examples/mvc/greeting -H 'Accept-Language: fr'
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>I18n Index</title>
</head>
<body>
<div id="known-message">
<p>Bonjour!</p>
</div>
<div id="fallback-message">
<p>Good bye!</p>
</div>
<div id="unknown-message">
<p>???something-unknown???</p>
</div>
</body>
</html>