原理
- 遍历目录下messages*.properties文件
- 从文件名中取出locale,比如messages_en_US.properties中en和US分别为语言名和国家名
- 解析properties文件的k=v键值对,有等号,k和v不为空的情况下构造一个Map,k其实为messageCode, v为messageFormat
- 最后是根据locale和messageCode查询messageFormat
由于scala有强大的option,flatMap等方法,spring的messageSource的defaultMessage作为调用者的可选项,即Option.getOrElse,参照代码最下方的示例
测试结果
Some(Hello, XnnYygn!) Some(Hello, XnnYygn!!!) Some(你好,XnnYygn!) default message
源代码
import java.util.Locale
import java.text.MessageFormat
import java.io.{File => JFile, FilenameFilter}
import scala.io.Source
trait MessageSource {
def get(messageCode: String, locale: Locale,
arguments: Array[Any]): Option[String]
}
abstract class AbstractMessageSource extends MessageSource{
protected def getMessageFormat(
messageCode: String, locale: Locale): Option[MessageFormat]
def get(messageCode: String, locale: Locale,
arguments: Array[Any] = Array[Any]()) = {
getMessageFormat(messageCode, locale).map(_.format(arguments))
}
}
class PropertyFilesMessageSource(
formats: Map[Locale, Map[String, MessageFormat]])
extends AbstractMessageSource {
protected def getMessageFormat(messageCode: String, locale: Locale) =
formats.get(locale).flatMap(_.get(messageCode))
}
object PropertyFilesMessageSource {
private final val PROPERTIES_NAME_REGEX =
"^messages(_([a-z]+))?(_([A-Z]+))?\\.properties$".r
def apply(dir: JFile, encoding: String): PropertyFilesMessageSource = {
new PropertyFilesMessageSource(dir.listFiles.flatMap {f =>
PROPERTIES_NAME_REGEX.findFirstMatchIn(f.getName).map {m =>
val language = nullToEmpty(m.group(2))
val country = nullToEmpty(m.group(4))
(new Locale(language, country), parseMessages(f, encoding))
}
}.toMap)
}
private def nullToEmpty(str: String): String = Option(str).getOrElse("")
private def parseMessages(
f: JFile, encoding: String): Map[String, MessageFormat] = {
Source.fromFile(f, encoding).getLines.flatMap { line =>
val index = line.indexOf('=')
if(index <= 0) None
else {
val key = line.substring(0, index).trim
val value = line.substring(index + 1).trim
if(key.length == 0 || value.length == 0) None
else Some((key, new MessageFormat(value)))
}
}.toMap
}
}
object MessageSourceRunner {
def main(args: Array[String]): Unit = {
val ms = PropertyFilesMessageSource(new JFile("messages"), "UTF-8")
println(ms.get("greeting", new Locale("", ""), Array("XnnYygn")))
println(ms.get("greeting", Locale.US, Array("XnnYygn")))
println(ms.get("greeting", Locale.SIMPLIFIED_CHINESE, Array("XnnYygn")))
println(ms.get(
"no_such_message", Locale.getDefault).getOrElse("default message"))
}
}