Documentation

You are viewing the documentation for the 2.6.0 release. The latest stable release series is 2.6.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 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, Langs}
import play.api.mvc.{BaseController, 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:

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(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