9

Controller:

def test = Action { implicit request =>
    import play.api.i18n._
    val msg = Messages("error.invalid")
    implicit val langInController = lang(request)
    Ok(views.html.test(langInController, msg))
}

View:

@(langInController: play.api.i18n.Lang, msg:String)(implicit request: Request[_])
<div>Lang from controller: @langInController, Message: @msg</div>
<div>Message from view: @play.api.i18n.Messages("error.required")</div>

Messages resource, conf/messages.zh-CN:

error.required=该字段必填

Trying

  1. Uses an English Firefox which sends the request header Accept-Language:en-us,en;q=0.5 to visit the test action. The result is:

    Language from controller: Lang(en,), Message: This field is required
    Message in view: 该字段必填
    
  2. Uses an Chinese Google Chrome which sends the request header Accept-Language:zh-CN,zh;q=0.8 to visit it. The result is:

    Language: Lang(zh,CN), Message: 该字段必填
    Message in view: 该字段必填
    

From the tests, we know that:

  1. The implicit language in the controller is from the request header's Accept-Language
  2. The implicit language used in template is determined by the OS

Environment:

  1. Play 2 is latest play2.1-SNAPSHOT from GitHub (2012-03-16)
  2. My OS is Windows 7 x64 Chinese version

I think Play 2 should use the same implicit language for controllers and views. I can fix it by adding something in Build.sbt:

val main = PlayProject(...) (
    templatesImport ++= Seq("utilis.TemplateMixin._")
)

Where the TemplateMixin is just:

object TemplateMixin extends play.api.mvc.Controller

(It extends Controller and is just to reuse some methods, like implicit def lang(request).)

But I think it should be done by the Play framework.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Freewind
  • 193,756
  • 157
  • 432
  • 708

1 Answers1

17

The play.api.i18n.Messages(key) function takes an additional implicit parameter of type Lang. So when you write Messages("foo") it is expanded to Messages("foo")(l), where l is a value of type Lang taken from the current implicit scope.

There’s always an available default implicit lang (which has a low priority), using your jvm default locale.

But when you are inside a Controller, an implicit value with a higher priority can be found if there is an implicit request. This value looks in the Accept-Language header of the request.

When you are inside a template, the default implicit lang will be used unless your template imports another implicit lang.

That’s why, in your example, messages computed from the Controller use the Accept-Language request header and messages computed from the View use your jvm default locale.

If you add an implicit parameter of type Lang to your template, this parameter will have a higher priority than the default lang and will be used to compute messages:

@(langInController: Lang, msg:String)(implicit request: RequestHeader, lang: Lang)

<div>Lang from controller: @langInController, Message: @msg</div>
<div>Message from view: @Messages("error.required")</div>

When you’ll call the template from a Controller action, its implicit lang will be passed, so the same lang will be used by both your Views and your Controllers.

Community
  • 1
  • 1
Julien Richard-Foy
  • 9,634
  • 2
  • 36
  • 42
  • 1
    Thanks again. A good answer should be added to the play2's documents. – Freewind Mar 16 '12 at 10:06
  • Why can't this be the default behavior? I would imagine most apps would want the implicit Land in the view to be the same one used in the controller. – Ike Mar 16 '12 at 15:01
  • Making this the default behavior would require to differentiate between templates intended to be used with Java controllers (which can’t provide implicit values) and templates intended to be used with Scala controllers. – Julien Richard-Foy Mar 16 '12 at 15:32