Documentation

You are viewing the documentation for the 2.6.0-M4 development release. The latest stable release series is 3.0.x.

§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 extends MessagesProvider 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:

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.

The I18nSupport also adds two convenient methods to Result:

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