§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.api.i18n.Lang instances. To access the languages supported by your application, you can inject a play.api.i18n.Langs component into your class:
import javax.inject.Inject
import play.api.i18n.Lang
import play.api.i18n.Langs
import play.api.mvc.BaseController
import play.api.mvc.ControllerComponents
class ScalaI18nService @Inject()(langs: Langs) {
val availableLangs: Seq[Lang] = langs.availables
}
An individual play.api.i18n.Lang can be converted to a java.util.Locale object by using lang.toLocale:
val locale: java.util.Locale = lang.toLocale§Externalizing messages
You can externalize messages in the conf/messages.xxx files.
The default conf/messages file matches all languages. Additionally you can specify language-specific message 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.api.i18n.MessagesApi object:
import play.api.i18n.MessagesApi
class MyService @Inject()(langs: Langs, messagesApi: MessagesApi) {
val lang: Lang = langs.availables.head
val title: String = messagesApi("home.title")(lang)
}
You can also make the language implicit rather than declare it:
class MyOtherService @Inject()(langs: Langs, messagesApi: MessagesApi) {
implicit val lang: Lang = langs.availables.head
lazy val title: String = messagesApi("home.title")
}§Using Messages and MessagesProvider
Because it’s common to want to use messages without having to provide an argument, you can wrap a given Lang together with the MessagesApi to create a play.api.i18n.Messages instance. The play.api.i18n.MessagesImpl case class implements the Messages trait if you want to create one directly:
val messages: Messages = MessagesImpl(lang, messagesApi)
val title: String = messages("home.title")
You can also use Singleton object methods with an implicit play.api.i18n.MessagesProvider:
implicit val messagesProvider: MessagesProvider = {
MessagesImpl(lang, messagesApi)
}
// uses implicit messages
val title2 = Messages("home.title")
A play.api.i18n.MessagesProvider is a trait that can provide a Messages object on demand. An instance of Messages extends MessagesProvider and returns itself.
MessagesProvider is most useful when extended by something that is not a Messages:
implicit val customMessagesProvider: MessagesProvider = new MessagesProvider {
// resolve messages at runtime
override def messages: Messages = { ... }
}
// uses implicit messages
val title3: String = Messages("home.title")§Using Messages with Controllers
You can add Messages support to your request by extending MessagesAbstractController or MessagesBaseController:
import javax.inject.Inject
import play.api.i18n._
class MyMessagesController @Inject()(mcc: MessagesControllerComponents) extends MessagesAbstractController(mcc) {
def index = Action { implicit request: MessagesRequest[AnyContent] =>
val messages: Messages = request.messages
val message: String = messages("info.error")
Ok(message)
}
def messages2 = Action { implicit request: MessagesRequest[AnyContent] =>
val lang: Lang = request.messages.lang
val message: String = messagesApi("info.error")(lang)
Ok(message)
}
def messages4 = Action { implicit request: MessagesRequest[AnyContent] =>
// MessagesRequest is an implicit MessagesProvider
Ok(views.html.formpage())
}
}
Or by adding the play.api.i18n.I18nSupport trait to your controller and ensuring an instance of MessagesApi is in scope, which will use implicits to convert a request.
import javax.inject.Inject
import play.api.i18n._
class MySupportController @Inject()(val controllerComponents: ControllerComponents)
extends BaseController
with I18nSupport {
def index = Action { implicit request =>
// type enrichment through I18nSupport
val messages: Messages = request.messages
val message: String = messages("info.error")
Ok(message)
}
def messages2 = Action { implicit request =>
// type enrichment through I18nSupport
val lang: Lang = request.lang
val message: String = messagesApi("info.error")(lang)
Ok(message)
}
def messages3 = Action { request =>
// direct access with no implicits required
val messages: Messages = messagesApi.preferred(request)
val lang = messages.lang
val message: String = messages("info.error")
Ok(message)
}
def messages4 = Action { implicit request =>
// takes implicit Messages, converted using request2messages
// template defined with @()(implicit messages: Messages)
Ok(views.html.formpage())
}
}
All the form helpers in Twirl templates take MessagesProvider, and it is assumed that a MessagesProvider is passed into the template as an implicit parameter when processing a form.
@(form: Form[Foo])(implicit messages: MessagesProvider)
@helper.inputText(field = form("name")) @* <- takes MessagesProvider *@
§Retrieving supported language from an HTTP request
You can retrieve the languages supported by a specific HTTP request:
def index = Action { request =>
Ok("Languages: " + request.acceptLanguages.map(_.code).mkString(", "))
}§Request Types
The I18nSupport trait adds the following methods to a Request:
request.messagesreturns an instance ofMessages, using an implicitMessagesApirequest.langreturns the preferredLang, using an implicitMessagesApi
The preferred language is extracted from the Accept-Language header (and optionally the language cookie) and matching one of the MessagesApi supported languages using messagesApi.preferred.
§Language Cookie Support
The I18nSupport also adds two convenient methods to Result:
result.withLang(lang: Lang)is used to set the language using Play’s language cookie.result.clearingLangis used to clear the language cookie.
For example:
def homePageInFrench = Action {
Redirect("/user/home").withLang(Lang("fr"))
}
def homePageWithDefaultLang = Action {
Redirect("/user/home").clearingLang
}
The withLang method sets the cookie named PLAY_LANG for future requests, while clearingLang discards the cookie, and Play will choose the language based on the client’s Accept-Language header.
The cookie name can be changed by changing the configuration parameter: play.i18n.langCookieName.
§Implicit Lang Conversion
The LangImplicits trait can be declared on a controller to implicitly convert a request to a Messages given an implicit Lang instance.
import play.api.i18n.LangImplicits
class MyClass @Inject()(val messagesApi: MessagesApi) extends LangImplicits {
def convertToMessage: Unit = {
implicit val lang = Lang("en")
val messages: Messages = lang2Messages // implicit conversion
}
}§Messages format
Messages are formatted using the java.text.MessageFormat library. For example, assuming you have message defined like:
files.summary=The disk {1} contains {0} file(s).
You can then specify parameters as:
Messages("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:
messagesApi("info.error") == "You aren't logged in!"
messagesApi("example.formatting") == "When using MessageFormat, '{0}' is replaced with the first parameter."§Explicit MessagesApi
The default implementation of MessagesApi is DefaultMessagesApi. You can see unit testing and functional testing examples in the testing section of the documentation.
You can also use Helpers.stubMessagesApi() in testing to provide a premade empty MessagesApi.
Next: Dependency injection