§Internationalization with Messages
§Specifying languages supported by your application
You specify languages for your application using language tags, specially formatted strings that identify a specific language. Language tags can specify simple languages, such as “en” for English, a specific regional dialect of a language (such as “en-AU” for English as used in Australia), a language and a script (such as “az-Latn” for Azerbaijani written in Latin script), or a combination of several of these (such as “zh-cmn-Hans-CN” for Chinese, Mandarin, Simplified script, as used in China).
To start you need to specify the languages supported by your application in the conf/application.conf
file:
play.i18n.langs = [ "en", "en-US", "fr" ]
These language tags will be used to create play.i18n.Lang
instances. To access the languages supported by your application, you can inject a play.i18n.Langs
component into your class:
import play.i18n.Langs;
import play.i18n.Messages;
import play.i18n.MessagesApi;
import javax.inject.Inject;
import java.util.Collection;
import java.util.Collections;
import java.util.Locale;
public class MyService {
private final Langs langs;
@Inject
public MyService(Langs langs) {
this.langs = langs;
}
}
An individual play.i18n.Lang
can be converted to a java.util.Locale
object by using lang.toLocale()
method:
java.util.Locale locale = lang.toLocale();
§Externalizing messages
You can externalize messages in the conf/messages.xxx
files.
The default conf/messages
file matches all languages. You can specify additional language messages files, such as conf/messages.fr
or conf/messages.en-US
.
Messages are available through the MessagesApi
instance, which can be added via injection. You can then retrieve messages using the play.i18n.Messages
object:
class SomeService {
private final play.i18n.MessagesApi messagesApi;
@Inject
SomeService(MessagesApi messagesApi) {
this.messagesApi = messagesApi;
}
public void message() {
Collection<Lang> candidates = Collections.singletonList(new Lang(Locale.US));
Messages messages = messagesApi.preferred(candidates);
String message = messages.at("home.title");
}
}
The current language is available via the lang
field in the current Context
. If there’s no current Context
then the default language is used. The Context
’s lang
value is determined by:
- Seeing if the
Context
’slang
field has been set explicitly. - Looking for a
PLAY_LANG
cookie in the request. - Looking at the
Accept-Language
headers of the request. - Using the application’s default language.
You can change the Context
’s lang
field by calling changeLang
or setTransientLang
. The changeLang
method will change the field and also set a PLAY_LANG
cookie for future requests. The setTransientLang
will set the field for the current request, but doesn’t set a cookie. See below for example usage.
If you don’t want to use the current language you can specify a message’s language explicitly:
String title = messagesApi.get(Lang.forCode("fr"), "hello");
Note that you should inject the play.i18n.MessagesApi
class, using dependency injection. For example, using Guice you would do the following:
public class MyClass {
private final play.i18n.MessagesApi messagesApi;
@Inject
public MyClass(MessagesApi messagesApi) {
this.messagesApi = messagesApi;
}
}
§Use in Controllers
If you are in a Controller, you get the Messages
instance through Http.Context
, using Http.Context.current().messages()
:
public Result index() {
Messages messages = Http.Context.current().messages();
String hello = messages.at("hello");
return ok(indextemplate.render());
}
Please note that because the Http.Context
depends on a thread local variable, if you are referencing Http.Context.current().messages()
from inside a CompletionStage
block that may be in a different thread, you may need to use HttpExecutionContext.current()
to make the HTTP context available to the thread. Please see Handling asynchronous results for more details.
To use Messages
as part of form processing, please see Handling form submission.
§Use in templates
Once you have the Messages object, you can pass it into the template:
@(implicit messages: play.i18n.Messages)
@messages.at("hello")
You can also use the Scala Messages
object from within templates. The Scala Messages
object has a shorter form that’s equivalent to messages.at
which many people find useful.
If you use the Scala Messages
object remember not to import the Java play.i18n.Messages
class or they will conflict!
@Messages("hello")
Localized templates that use messages.at
or the Scala Messages
object are invoked like normal:
public Result index() {
return ok(indextemplate.render()); // "hello"
}
If you want to change the language for the template you can call changeLang
on the current Context
. This will change the language for the current request, and set the language into a cookie so that the language is changed for future requests:
public Result index() {
Lang lang = Lang.forCode("fr");
Messages messages = messagesApi.preferred(request().addAttr(Messages.Attrs.CurrentLang, lang));
return ok(hellotemplate.render(messages)).withLang(lang, messagesApi); // "bonjour"
}
If you just want to change the language, but only for the current request and not for future requests, call setTransientLang
:
public Result index() {
ctx().setTransientLang("en-US");
return ok(indextemplate.render()); // "howdy"
}
§Formatting messages
Messages are formatted using the java.text.MessageFormat
library. For example, if you have defined a message like this:
files.summary=The disk {1} contains {0} file(s).
You can then specify parameters as:
Messages.get("files.summary", d.files.length, d.name)
§Notes on apostrophes
Since Messages uses java.text.MessageFormat
, please be aware that single quotes are used as a meta-character for escaping parameter substitutions.
For example, if you have the following messages defined:
info.error=You aren''t logged in!
example.formatting=When using MessageFormat, '''{0}''' is replaced with the first parameter.
you should expect the following results:
String errorMessage = messages.at("info.error");
Boolean areEqual = errorMessage.equals("You aren't logged in!");
String errorMessage = messages.at("example.formatting");
Boolean areEqual = errorMessage.equals("When using MessageFormat, '{0}' is replaced with the first parameter.");
§Retrieving supported languages from an HTTP request
You can retrieve a specific HTTP request’s supported languages:
public Result index() {
List<Lang> langs = request().acceptLanguages();
String codes = langs.stream().map(Lang::code).collect(joining(","));
return ok(codes);
}
§Using explicit MessagesApi
The default implementation of MessagesApi
is backed by a DefaultMessagesApi
instance which is a Scala API. But you can instantiate a DefaultMessagesApi
and manually inject it into the MessagesApi
like:
private MessagesApi explicitMessagesApi() {
return new play.i18n.MessagesApi(
new play.api.i18n.DefaultMessagesApi(
Collections.singletonMap(Lang.defaultLang().code(), Collections.singletonMap("foo", "bar")),
new play.api.i18n.DefaultLangs().asJava())
);
}
If you need a MessagesApi
instance for unit testing, you can also use play.test.Helpers.stubMessagesApi()
. See Testing your application for more details.
Next: Dependency Injection