我有Scala Play 2.7.x,对于包含大量文本的视图,而不是使用MessagesApi
,我只是按如下语言将它们分隔开:
/views
/en
something.scala.html
/es
something.scala.html
然后在我的Controller实现中,我将执行以下操作:
def doSomething() = Action { request =>
val lang = request.lang
lang.code match {
case "en" => Future.successful(Ok(views.html.en.something()))
case "es" => Future.successful(Ok(views.html.es.something()))
case _ => Future.successful(Ok(views.html.en.something()))
}
}
}
但是这很容易出错,而且显然不可持续,因为每次我支持一种新语言时,我都需要重新编译。我宁愿这样的事情(不起作用):
def doSomething() = Action { request =>
implicit val lang = request.lang
val dynamicViewTarget = s"view.html.$lang.something()"
Future.successful(Ok(dynamicViewTarget))
}
如何实现?也许更干净的方法是将lang
作为路由资源的一部分并直接进入该视图。或这两种想法的结合。
经过一些研究,我得出了以下完整的解决方案。它是一个模块插件,可以动态解析“结果”目标视图(经过测试可在Scala 2.12.8和Play 2.7.3上运行):
package modules
import javax.inject.{ Inject, Provider, Singleton }
import play.api.Application
import play.api.i18n.Lang
import play.api.mvc.{ Result, Results }
import play.twirl.api.Html
/**
* The dynamic template loader module implementation.
* @param app the Play Application instance.
*/
@Singleton
class DynamicTemplateLoader @Inject() (app: Provider[Application]) {
import Results._
import reflect.runtime.universe._
/**
* Returns the dynamically resolved play Result corresponding to a view function name using Reflection.
*
* @param target the target view name, note it's the name only e.g. "index"
* @param args the formal arguments the view requires e.g. Seq(form, credentials)
* @param implicitArgs the implicit arguments that the view requires e.g. Seq(request, messages, webJarsUtil,
* assets, configuration)
* @param langOpt the language to default to, if the language is defined then it will branch to the specific language
* otherwise will omit the language.
* @return the dynamically resolved play Result corresponding to a view function name using Reflection.
*/
def resolve(target: String, args: Seq[Any], implicitArgs: Seq[Any])(implicit langOpt: Option[Lang]): Result = {
val loader = app.get().classloader
val currentMirror = runtimeMirror(loader)
val templateName = langOpt.map { lang => s"views.html.${lang.code}.$target" }.getOrElse(s"views.html.$target")
val moduleMirror = currentMirror.reflectModule(currentMirror.staticModule(templateName))
val methodSymbol = moduleMirror.symbol.typeSignature.decl(TermName("apply")).asMethod
val instanceMirror = currentMirror.reflect(moduleMirror.instance)
val methodMirror = instanceMirror.reflectMethod(methodSymbol)
val allArgs = args ++ implicitArgs
Ok(methodMirror.apply(allArgs: _*).asInstanceOf[Html])
}
}
示例用法:
def doSomething() = Action { request =>
implicit val langOpt = Option(request.lang)
val target = "something"
val args = Seq(credentials) // e.g. user credentials
val implicitArgs = Seq(request, messages, webJarsUtil, assets, configuration)
Future.successful(dynamicTemplateLoader.resolve(target, args, implicitArgs))
}
最后是样本views/en/something.scala.html
@import play.api.i18n.Messages
@import play.api.mvc.RequestHeader
@import org.webjars.play.WebJarsUtil
@import controllers.AssetsFinder
@import play.api.data._
@import play.api.Configuration
@(credentials: Credentials)(implicit request: RequestHeader, messages: Messages, webJarsUtil: WebJarsUtil, assets: AssetsFinder, configuration: Configuration) {
<div class="TODO">
<div>
}