Search code examples
scalalocalizationplayframework-2.2

Play Framework 2.2.0 - Force the language using Filter and Global object


I've been trying to force the locale into the Request object depending on the host name of the application. This is done by reading a configuration Map

package controllers

import play.api.mvc._
import play.api.i18n.Lang
import play.api.Play.current
import scala.actors.Future

object Global extends WithFilters(ForceLocalization)

object ForceLocalization extends Filter {

  override def apply(next: (RequestHeader) => Future[SimpleResult])
                    (rh: RequestHeader): Future[SimpleResult] = {
    val arr = models.DomainSettings.forDomain(rh.domain).locale.split("_") // -> "en_US"
    val language = arr(0) // -> "en"
    val country = if ( arr.length == 1 ) "" else arr(1) // -> "US"
    f(rh).map( _.withLang(Lang(language, country)) ) 
  }

}

The code doesn't compile. It says:

type mismatch:
  [error]  found   : play.api.mvc.PlainResult
  [error]  required: play.api.mvc.SimpleResult
  [error]     f(rh).map( _.withLang(Lang(language, country)) ) 

Any suggestion?

Notice: I might probably have used a bad approach to achieve my goal, so if you have something better to suggest, I'll be glad to have your advice.


Solution

  • Hmmm - seems like you've hit a bug related to Play's transitioning from PlainResult to SimpleResult.

    When I look in the 2.2.0 codebase, PlainResult is:

    sealed trait PlainResult extends Result with WithHeaders[PlainResult]
    

    and SimpleResult is defined as:

    case class SimpleResult(...) extends PlainResult
    

    ... which means that all of the WithHeaders methods will be returning PlainResults - which is wrong.

    I note that in the 2.2.x codebase on GitHub, it's been rectified:

    case class SimpleResult(...) extends PlainResult with WithHeaders[SimpleResult]
    

    ... and if you're happy to use a Release Candidate, you can use 2.2.2-RC1 that contains the fix, according to the release notes.

    If not, employ a helper method to work around the problem, by doing what WithHeaders.withLang does anyway:

    object ForceLocalization extends Filter {
    
      override def apply(next: (RequestHeader) => Future[SimpleResult])
                        (rh: RequestHeader): Future[SimpleResult] = {
        ...
        next(rh).map( withLang(_, Lang(language, country)) )
      }
    
      private def withLang(sr:SimpleResult, lang:Lang) = {
        sr.withCookies(Cookie(Play.langCookieName, lang.code))
      }
    }