在我的Scala项目中,我使用Twirl模板引擎。模板文件结构与俄语和英语语言重复,例如我有以下两条路径:en.Send.txt.MonoEnsure
和ru.Send.txt.MonoEnsure
在我的代码中,我希望能够动态加载en
或ru
模板,如下所示:
def render(lang: String) = lang.Send.txt.MonoEnsure("hi")
render("en") // does not work, just to illustrate my point
我怎样才能做到这一点?
答案 0 :(得分:3)
这不是您问题的直接答案,而是解决问题的不同解决方案。另一个答案解决了如何在技术上实现你想要的东西,但使用反射进行一些国际化是不必要的脆弱,绝对不推荐。
正如评论中所提到的,当您在Play应用程序的上下文中使用Twirl时,Play会为您提供自己的国际化方式。由于您未在应用程序中使用Play,因此无法使用它。但是你不需要 来使用Play的特定方式进行国际化。您可以轻松构建自己的基本国际化构造,而不会引入额外的依赖关系,我将在此答案中展示。
首先,这种方法应该更加干燥。其次,它将您的布局与您恰好使用的语言分离。最后,它完全是类型安全的,不使用反射。
首先,创建一个表示语言的类,包装Properties
对象。
// Language.scala
class Language(filename: String) {
val properties = new java.util.Properties()
properties.loadFromXML(new FileInputStream(filename))
def apply(key: String) = properties.getProperty(key, s"Key $key not found.")
}
object Language {
val English = new Language("path/to/english.xml")
val Russian = new Language("path/to/russian.xml")
}
还使用Properties XML format定义一些翻译:
<!-- path/to/english.xml -->
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<entry key="hello.world">Hello World</entry>
</properties>
<!-- path/to/russian.xml -->
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<entry key="hello.world">привет мир</entry>
</properties>
然后,在渲染时,您可以隐式使用您正在使用的语言:
implicit val language: Language = determineLanguage() // Your logic for determining the language you want to use
// Do other things here..
// Render template
Send.txt.MonoEnsure("hi")
然后,在您的模板中,使用以下语言:
@(arg1: Any, arg2: Any)(implicit lang: Language)
<html>
<body>
<p>@lang("hello.world")</p>
</body
</html>
其中,将输出显示“Hello World”或“приветмир”的页面,具体取决于所选择的语言。
更新:我的第一个建议是使用Java的默认属性文件格式,但事实证明它使用了不支持西里尔字符的Latin-1编码,因此很难与俄语一起使用。因此,我更新了我的答案,而不是使用(不幸的是更详细)属性XML格式,它使用UTF-8编码,因此支持西里尔字符。
答案 1 :(得分:3)
我认为这是应该实现的代码:
import play.twirl.api.Template1
def getTemplate[T](name : String)(implicit man: Manifest[T]) : T =
Class.forName(name + "$").getField("MODULE$").get(man.erasure).asInstanceOf[T]
def render(lang: String) =
getTemplate[Template1[String,String]](s"$lang.Send.txt.MonoEnsure").render("hi")
render("en")
模板编译为BaseScalaTemplate
,因此您可以使用反射调用它。您只需要知道模板的参数数量,以便可以将其作为特征play.api.twirl.TemplateX
的实例加载。在这种情况下Template1[String, String]
(参数的第一个字符串和渲染响应类型的第二个字符串)。
检查此主题以获取有关scala How do I call a Scala Object method using reflection?
中反射的更多信息