Plurals
Babel provides first class support for plurals with the Quantities
class.
- en.conf
-
source
bicycles = { 0 = "There are no bicycles in Beijing" 1 = "There is one bicycle in Beijing" "*" = "There are 9 million bicycles in Beijing" }
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(bicycles: Quantities[String])
val i18n = Loader
.default[IO]
.load("plurals", Set(Locales.en))
.map(Decoder[I18n].decodeAll)
.rethrow
.map(_.withFallback(Locales.en))
.flatMap(_.liftTo[IO](new IllegalStateException("Translations for en missing")))
.map(_.apply(Locales.en))
.unsafeRunSync()
// i18n: I18n = I18n(
// bicycles = Quantities(
// default = "There are 9 million bicycles in Beijing",
// quantities = List(
// Element(
// quantity = Exact(value = 0),
// value = "There are no bicycles in Beijing"
// ),
// Element(
// quantity = Exact(value = 1),
// value = "There is one bicycle in Beijing"
// )
// )
// )
// )
i18n.bicycles(0)
// res0: String = "There are no bicycles in Beijing"
i18n.bicycles(1)
// res1: String = "There is one bicycle in Beijing"
i18n.bicycles(100)
// res2: String = "There are 9 million bicycles in Beijing"
Using plurals with arguments
Of course, you will most likely want to reference the given quantity in your translations, which was painfully omitted in the example above. This can be achieved by combining the StringFormat
feature with Quantities
.
- en.conf
-
source
bicycles = { 0 = "There are no bicycles in Beijing" 1 = "There is one bicycle in Beijing" "*" = "There are {0} bicycles in Beijing" }
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(bicycles: Quantities[StringFormat1])
val i18n = Loader
.default[IO]
.load("plurals-arguments", Set(Locales.en))
.map(Decoder[I18n].decodeAll)
.rethrow
.map(_.withFallback(Locales.en))
.flatMap(_.liftTo[IO](new IllegalStateException("Translations for en missing")))
.map(_.apply(Locales.en))
.unsafeRunSync()
// i18n: I18n = I18n({0: "There are no bicycles in Beijing", 1: "There is one bicycle in Beijing", *: "There are {0} bicycles in Beijing"})
i18n.bicycles(0)
// res4: StringFormat1 = There are no bicycles in Beijing
i18n.bicycles(100)("100")
// res5: String = "There are 100 bicycles in Beijing"
i18n.bicycles(9000000)("9 million")
// res6: String = "There are 9 million bicycles in Beijing"
Plural ranges
Some languages have very complex plural rules. These can be mapped with quantity ranges.
- en.conf
-
source
ranges = { 1 = "Exact" "10-20" = "Range 10 to 20" "*" = "Fallback" }
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(ranges: Quantities[String])
val i18n = Loader
.default[IO]
.load("plurals-ranges", Set(Locales.en))
.map(Decoder[I18n].decodeAll)
.rethrow
.map(_.withFallback(Locales.en))
.flatMap(_.liftTo[IO](new IllegalStateException("Translations for en missing")))
.map(_.apply(Locales.en))
.unsafeRunSync()
// i18n: I18n = I18n({1: "Exact", 10-20: "Range 10 to 20", *: "Fallback"})
i18n.ranges(0)
// res8: String = "Fallback"
i18n.ranges(1)
// res9: String = "Exact"
i18n.ranges(9)
// res10: String = "Fallback"
i18n.ranges(10)
// res11: String = "Range 10 to 20"
i18n.ranges(11)
// res12: String = "Range 10 to 20"
i18n.ranges(20)
// res13: String = "Range 10 to 20"
i18n.ranges(21)
// res14: String = "Fallback"
0.5.3