Argument passing
Babel has helpers like StringFormat1
, StringFormat2
, …, StringFormat22
. to help you inject arguments into Strings. In that regard it is similar to java.lang.String.format
or java.text.MessageFormat.format
but provides better compile time checks.
Babel’s StringFormat
does not automatically render arbitrary types (as String.format
does for numeric values). It only accepts String
values, so the caller is responsible for converting the value to a renderable format beforehand.
- Parameter placeholders are encoded in the style of
java.text.MessageFormat
, starting at index0
(e.g.{0}
,{1}
, …) - Each placeholder index may be used at most once (e.g.
"lorem {0} dolar {0}"
is not allowed) - The order of the placeholders may vary across translations (e.g.
"The {0} {1} is fast"
and"El {1} {0} es rapido"
) - There can not be more placeholders than arguments (e.g.
StringFormat1
can not be decoded when there is{0}
and{1}
as there is only 1 parameter) - It is a decoding error to define placeholder indices that exceed the
StringFormatN
arity (e.g.StringFormat2
may only use{0}
and{1}
, but not{2}
) - Not all placeholders must be used (e.g.
"Good afternoon, {0}. Today it's {1} degrees."
and"Guten Tag, sehr geehrte Damen und Herren. Heute sind es {1} Grad."
)
- en.conf
-
source
weather = "The temperature in {0} is {1} degrees Celsius."
- de.conf
-
source
weather = "Es sind {1} Grad Celsius in {0}."
Note
In the German version, the parameters are called in swapped order.
import cats.effect._
import cats.effect.unsafe.implicits.global
import cats.syntax.all._
import io.taig.babel._
import io.taig.babel.generic.auto._
final case class I18n(weather: StringFormat2)
val i18ns = Loader
.default[IO]
.load("arguments", Set(Locales.en, Locales.de))
.map(Decoder[I18n].decodeAll)
.rethrow
.map(_.withFallback(Locales.en))
.flatMap(_.liftTo[IO](new IllegalStateException("Translations for en missing")))
.unsafeRunSync()
// i18ns: NonEmptyTranslations[I18n] = NonEmptyTranslations(en -> I18n(The temperature in {0} is {1} degrees Celsius.),Translations(Map(de -> I18n(Es sind {1} Grad Celsius in {0}.))))
i18ns(Locales.en).weather("Cape Town", "26")
// res0: String = "The temperature in Cape Town is 26 degrees Celsius."
i18ns(Locales.de).weather("Frankfurt am Main", "18")
// res1: String = "Es sind 18 Grad Celsius in Frankfurt am Main."
Compile-time guarantees
In the above example we have used StringFormat2
, which means that exactly 2 arguments must be passed. Otherwise a compile error will occur.
i18ns(Locales.en).weather("Stockholm")
// error:
// missing argument for parameter v1 of method apply in class StringFormat2: (v0: String, v1: String): String
However, it is allowed to omit parameters.
Decoder[StringFormat2]
.decode(Babel.text("It's always rainy in {0}"))
.map(_.apply("London", "3"))
// res3: Either[Error, String] = Right(value = "It's always rainy in London")
0.5.3