Play Router:如何添加对语言敏感的URL重定向规则?

时间:2019-10-09 17:10:05

标签: scala playframework routes play-framework-2.7

我有一个国际化的Scala Play 2.7.x WebApp,并且具有通常的路由,例如

GET /               controllers.ApplicationController.index
GET /page/somePage/ controllers.SomeController.somePage
GET /contact        controllers.ContactController.view

现在,我想添加一条新的路由,该路由将基本上更改为语言重定向到任何目标路由。我通过在routes顶部添加一条附加路由来实现此用例,

GET /$lang<(en|es)> controllers.ApplicationController.langRedirect(lang: String, target: String = "")

这个想法是,每次您进行例如

http://localhost:9000/en => will go to home page in english
http://localhost:9000/en/contact => will go to contact page in english
http://localhost:9000/es => will go to home page in spanish
http://localhost:9000/es/contact => will go to contact page in spanish

,依此类推。不幸的是,它并不总是有效,例如/en/page/somePage/之前包含的内容将无法正确匹配第一个规则:

 GET /$lang<(en|es)> controllers.ApplicationController.langRedirect(lang: String, target: String = "")

大概是由于中间的/ ...我该如何解决?

为完整起见,这是我的ApplicationController.langRedirect(...)实现:

def langRedirect(lang: String, target: String = "") = silhouette.UserAwareAction.async { implicit request =>
    Future.successful(Redirect("/" + target).withLang(Lang(lang)))
}

2 个答案:

答案 0 :(得分:1)

使用Router.withPrefix,您可以在所有路线中添加语言代码前缀。

这里是一个例子。

package handlers

import javax.inject.Inject
import play.api.http._
import play.api.i18n.{ Langs, Lang }
import play.api.mvc.{ Handler, RequestHeader }

class I18nRequestHandler @Inject()(
    webCommands: play.core.WebCommands,
    optDevContext: play.api.OptionalDevContext,
    router: play.api.routing.Router,
    errorHandler: HttpErrorHandler,
    configuration: HttpConfiguration,
    filters: HttpFilters,
    langs: Langs)
  extends DefaultHttpRequestHandler(
    webCommands, optDevContext, router, errorHandler, configuration, filters) {

  def getLang(request: RequestHeader): Lang = {
    // Get the first path
    request.path.tail.split('/').headOption
      .flatMap(path => Lang.get(path))
      // language from the fist path, if it is in "play.i18n.langs (application.conf)"
      .filter(lang => langs.availables.exists(_ == lang))
      // Or preferred language, refereeing "Accept-Languages"
      .getOrElse(langs.preferred(request.acceptLanguages))
  }

  override def handlerForRequest(request: RequestHeader): (RequestHeader, Handler) = {
    // To use the language code from the path with MessagesApi,
    // Replace "Accept-Languages" to the language from the path.
    val requestWithLang = request.withHeaders(
      request.headers.replace(HeaderNames.ACCEPT_LANGUAGE -> getLang(request).code))
    super.handlerForRequest(requestWithLang)
  }

  override def routeRequest(request: RequestHeader): Option[Handler] = {
    val lang = getLang(request)
    request.path.tail.split('/').headOption
      // If the first path is right language code (if not, Not Found)
      .filter(_ == lang.code)
      // Route this request with language code prefix
      .flatMap(_ => router.withPrefix("/" + lang.code).handlerFor(request))
  }
}

要启用I18nRequestHandler,必须将其添加到“ application.conf”。

play.http.requestHandler = "handlers.I18nRequestHandler"

还将支持的语言添加到“ application.conf”。

play.i18n.langs = [ "en", "es" ]

此代码强制所有路由都具有语言代码前缀。如果您需要特殊的路由(例如“ /”)来让用户选择其语言,请创建自定义路由并将其添加到routeRequest方法中。

希望这就是您想要的;)

答案 1 :(得分:0)

确定找到了可能的解决方案,该方法是添加第二条最主要的路线,该路线将以任何可能的目标作为目标,包括/,现在我的routes文件的顶部看起来像这样:

GET /$lang<(en|es)> controllers.ApplicationController.langRedirect(lang: String, target: String = "")
GET /$lang<(en|es)>/*target controllers.ApplicationController.langRedirect(lang: String, target: String = "")    

GET /               controllers.ApplicationController.index
GET /page/somePage/ controllers.SomeController.somePage
GET /contact        controllers.ContactController.view

为什么我需要两个?因为首页只能是http://localhost:9000/en,而不能是http://localhost:9000/en/

但是,我将很高兴学习(并接受)更好/更简单的解决方案。