Sbt i18n is a very simple internationalization tools, that reads resources bundles files in properties files, json, or HOCON format and generate Scala code. Strings in ICU format are supported.
A configuration file like this
en = {
key1 = a text
key2 ="a parametrized text {0}"
subsection {
key3 = some text
}
plural = "{dogsCount, plural, one {One dog is} other {# dogs are}}"
}
will generate the following Scala code
import com.ibm.icu.text.MessageFormat
import java.util.Locale
object Bundle {
val languages: Map[String, I18N] = Map(("en", en))
abstract class I18N {
protected val locale: Locale
def key1: String
def key2(x0: String): String
abstract class Subsection {
def key4: String
}
def subsection: Subsection
def plural(dogsCount: Long): String
}
object en extends I18N {
override protected val locale: Locale = Locale.forLanguageTag("en")
val key1 = """a text"""
def key2(x0: String): String = new MessageFormat("""a parametrized text {0}""", locale)
.format(java.util.Map.of("0", x0))
object subsection extends Subsection {
val key4= """2"""
}
def plural(dogsCount: Long): String = new MessageFormat(
"""{dogsCount, plural, one {One dog is} other {# dogs are}}""",
locale
).format(java.util.Map.of("dogsCount", dogsCount))
}
}
The motivation here is to depart from the traditional way of doing "stringly typed" message fetching and to have a stronger coupling between the application code and the message translations. Having your build break when a translation is missing or on a faulty pattern is something you may want.
This plugin requires sbt 1.0.0+. ICU support is implemented using icu4j library.
Add the following to project/plugins.sbt:
addSbtPlugin("tech.ant8e" % "sbt-i18n" % "0.7")
Write a config file in src/main/i18n/xxx.conf
.
en = {
msg1 = a text
msg2 ="a parametrized text {0}"
}
fr = {
msg1 = un texte
msg2 ="un texte paramétré {0}"
}
And access your translations in your Scala code
val msg1 = org.example.i18n.Bundle.en.msg1
val msg2 = org.example.i18n.Bundle.en.msg2("hello")
The scala Bundle will be generated by default in package org.example.i18n
this can of course be configured:
In oder to change the output package set i18nBundlePackageName := "org.myorganisation.i18n"
in build.sbt
.
the plugin will parse all files within src/main/i18n
with the Typesafe config
library and build one single config hierarchy that will be the source of the code generation. The top level keys are
assumed to be language selectors.
The plugin will merge multiple files will no guarantee about order or conflict resolution. When two or more files defines the exact same configuration path it is undefined who will "win".
The plugin will generate one scala object named Bundle
in the specified package.
This object contains one abstract class named I18N
that defines the structure of each language.
For each language (or top level key), an object, named like the key, implementing the I18N
class is generated.
Simple message will appear as val
and parametrized messages as methods.
A message that is a valid java.text.MessageFormat pattern will be generated as a method. See MessageFormat documentation for details about the supported patterns.
ICU MessageFormat extends java.text.MessageFormat in a backward compatible way by adding three more placeholder types:
- plural
- select
- selectordinal
Apart from new placeholder types ICU also adds named parameters to all supported placeholders
en = {
msg1 = "{0, date, long}"
msg2 = "{birtDate, date, long}"
}
During generation the plugin will infer the correct argument type from the pattern.
number
will have type Double
by default and Long
when the integer
format style is selected.
Date and time will have type java.time.ZonedDateTime
. choice
being a number format will lead to a Double
.
plural
and selectordinal
will have type Long
. For the select
a new ADT will be generated and used.
By default (when no format is specified), the argument will be of type String
.
The structure of a bundle is a merge of all the keys of all the languages. Should a key not be defined in a specific
language, it will be generated as ??? [key] ???
.
In oder to break generation on missing key set i18nBreakOnMissingKeys := true
in build.sbt
.
Contributions via GitHub pull requests are gladly accepted from their original author. Along with any pull requests, please state that the contribution is your original work and that you license the work to the project under the project's open source license. Whether or not you state this explicitly, by submitting any copyrighted material via pull request, email, or other means you agree to license the material under the project's open source license and warrant that you have the legal authority to do so.
This code is open source software licensed under the Apache 2.0 License.