Implicit parameters and combined views in Play/Scala

1.7k views Asked by At

This question builds upon a case I found here

index.scala.html

@(text: String)(implicit messages: Messages)

@main("Fancy title") {
  <h1>@messages("header.index")</h1>
  <div>@text</div>
}

main.scala.html

@(title: String)(content: Html)(implicit messages: Messages)
  <html>
  <head>
    <title>@title</title>
  </head>
  <body>
    <h1>@messages("header.main")</h1>
    @content
  <body>
</html>

In this example I have index calling main and I would like both to access messages.

The compiler gives me "could not find implicit value for parameter messages: play.api.i18n.Messages" inside of index but if I remove the implicit parameter declaration from main then index works fine and gets the message. It seems like the compiler is telling me that it doesn't know how to pass the implicit parameter down the line.

Before trying with workarounds I would like to understand why this doesn't work.

3

There are 3 answers

3
Soroosh Sarabadani On

In Play 2.4 you need to Inject MessageAPI in your controller and call preferred menu in your action to create message. if you define it as implicit in your action. Then everything will work.

1
ChrisEx On

First of all let me add the code for the controller in order to make my original example clearer.

Application.scala (original)

class Application @Inject() (val messagesApi: MessagesApi) extends Controller with I18nSupport { 
   def index = Action { implicit request => 
      Ok(views.html.index("blah bla blah")) 
}

After studying the Play Framework docs in greater detail (here) I learned about an alternative approach for accessing messages from within the templates (which is probably cleaner as well).

Application.scala (new)

class Application extends Controller { 
   def index = Action {
      Ok(views.html.index("blah bla blah"))
   }
}

index.scala.html (new)

@import play.i18n._
@(text: String)
@main("Fancy title") {
  <h1>@Messages.get("header.index")</h1>
  <div>@text</div>
}

main.scala.html (new)

@import play.i18n._
@(title: String)(content: Html)
  <html>
    <head>
      <title>@title</title>
    </head>
    <body>
      <h1>@Messages.get("header.main")</h1>
      @content
    <body>
  </html>

The controller does not need all the additional infrastructure anymore.

The views need to add an import statement but lose the implicit param declaration.

The Messages are accessed using @Messages.get("xyz") instead of @Messages("xyz").


For the time being this approach suits my needs.

0
flurdy On

Play framework will implicitly convert your request to the MessagesApi for your view. However, you do need to include the request => implicit parameter in your controller method. Also include the I18nSupport trait in your controller.

import play.api.i18n.{MessagesApi, I18nSupport}

@Singleton
class HomeController @Inject() (cc: ControllerComponents)
extends AbstractController(cc) with I18nSupport {

  def showIndex = Action { implicit request =>
    Ok(views.html.index("All the langs"))
  }
}