Play Router:如何添加对语言敏感的 URL 重定向规则?
Play Router: How to add a language-sensitive URL redirect rule?
我有一个国际化的 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)))
}
OK 找到了一个可能的解决方案,即添加第二条顶级路线,该路线将采取任何可能的目标,包括 /
,我的 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/
但是,我很乐意学习(并接受)better/simpler 解决方案。
使用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
方法中。
希望这就是你想要的 ;)
我有一个国际化的 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)))
}
OK 找到了一个可能的解决方案,即添加第二条顶级路线,该路线将采取任何可能的目标,包括 /
,我的 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/
但是,我很乐意学习(并接受)better/simpler 解决方案。
使用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
方法中。
希望这就是你想要的 ;)