§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 validated 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:
class MyService @Inject()(langs: Langs) {
val availableLangs = langs.available
}
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:
class MyService @Inject()(langs: Langs, messagesApi: MessagesApi) {
val lang: Lang = langs.availables.head
val title = messagesApi("home.title")(lang)
}
You can also make the language implicit rather than declare it:
class MyService @Inject()(langs: Langs, messagesApi: MessagesApi) {
implicit val lang: Lang = langs.availables.head
val title = 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 = 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 title = 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 messagesProvider: MessagesProvider = new MessagesProvider {
// resolve messages at runtime
def messages = {
...
}
}
// uses implicit messages
val title = Messages("home.title")
Note: An example of a
WrappedRequest
that extendsMessagesProvider
is covered in passing messages to form helpers in the ScalaForms page.
§Using Messages with Controllers
You can add an implicit Messages
to your actions by adding the play.api.i18n.I18nSupport
trait to your controller. The play.api.i18n.I18nSupport
trait gives you an implicit Messages
value as long as there is a RequestHeader
in the implicit scope.
If you extend Controller
directly, this will require that you add val messagesApi: MessagesApi
to your controller as I18nSupport
depends on it. If you extend AbstractController
, then val messagesApi: MessagesApi
is already provided under the hood and all you have to do is extend I18nSupport
:
import javax.inject.Inject
import play.api.i18n._
class MyController @Inject()(cc: ControllerComponents) extends AbstractController(cc) 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())
}
}
The I18nSupport
is useful (if not essential) because 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 *@
Note: This is not a complete guide to form helpers. Please see showing forms in a view template in the ScalaForms page for more detailed examples.
§Request Types
I18nSupport
also adds the following methods to a Request
:
request.messages
returns an instance ofMessages
, using an implicitMessagesApi
request.lang
returns 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.clearingLang
is used to clear the language cookie.
For example:
def homePageInFrench = Action {
Redirect("/user/home").withLang("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.
class MyClass @Inject()(val messagesApi: MessagesApi) extends LangImplicits {
def convertToMessage: Unit = {
implicit val lang = Lang("en")
val messages: Messages = lang // 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."
§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(", "))
}
Next: Dependency injection