Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

i18n error: controller and templates uses different implicit languages

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.

like image 793
Freewind Avatar asked Mar 16 '12 06:03

Freewind


1 Answers

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.

like image 73
Julien Richard-Foy Avatar answered Jan 03 '23 13:01

Julien Richard-Foy