Scala Play:如何动态设置每种语言的响应视图?

问题描述 投票:0回答:1

我有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 playframework
1个回答
0
投票

经过一些研究,我得出了以下完整的解决方案。它是一个模块插件,可以动态解析“结果”目标视图(经过测试可在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>
}
© www.soinside.com 2019 - 2024. All rights reserved.