Ranges

We just talked about plurals, which are standardized, but we have a little unorthodox features that I called ranges.

They are based around a count and display different translations based on this count.

To declare them the key takes a sequence where each element is a sequence with the first element being the value, and the other element the count to match against:

{
  "click_count": [
    ["You have not clicked yet", 0],
    ["You clicked once", 1],
    ["You clicked {{ count }} times", "_"]
  ]
}

Multiple exact values

You can declare multiple counts to match against:

{
  "click_count": [
    ["0 or 5", 0, 5],
    ["1, 2, 3 or 4", 1, 2, 3, 4],
    ["You clicked {{ count }} times", "_"]
  ]
}

Ranges

You can also declare a range where the translations is used:

{
  "click_count": [
    ["0 or 5", 0, 5],
    ["1, 2, 3 or 4", "1..=4"],
    ["You clicked {{ count }} times", "_"]
  ]
}

You can use all Rust ranges syntax: s..e, ..e, s.., s..=e, ..=e or even .. ( .. will be considered fallback _)

Number type

By default the count is expected to be an i32, but you can change that by specifying the type as the first element of the sequence:

{
  "click_count": [
    "u32",
    ["You have not clicked yet", 0],
    ["You clicked once", 1],
    ["You clicked {{ count }} times", "_"]
  ]
}

Now you only have to cover the u32 range.

The supported types are i8, i16, i32, i64, u8, u16, u32, u64, f32 and f64.

Fallback

If all the given counts don't fill the range of the number type, you can use a fallback ("_" or "..") as seen above, but it can be completely omitted on the last element of the sequence:

{
  "click_count": [
    ["You have not clicked yet", 0],
    ["You clicked once", 1],
    ["You clicked {{ count }} times"]
  ]
}

Fallbacks are not required if you already cover the full number range:

{
  "click_count": [
    "u8",
    ["You have not clicked yet", 0],
    ["1 to 254", "1..=254"],
    ["255", 255]
  ]
}

Fallbacks are always required for f32 and f64.

Order

The order of the ranges matter, for example:

{
  "click_count": [
    ["first", "0..5"],
    ["second", "0..=5"],
    ["You clicked {{ count }} times"]
  ]
}

Here "second" will only be printed if count is 5, if 0 <= count < 5 then "first" will be printed.

Mix ranges with exact values

You can totally mix them, this is valid:

{
  "click_count": [
    ["first", 0, "3..5", "10..=56"],
    ["second", "0..3", "..78"],
    ["You clicked {{ count }} times"]
  ]
}

Use interpolation

The "You clicked {{ count }} times" kind of gave it away, but you can use interpolation in your ranges, this is valid:

{
  "click_count": [
    ["<b>first</b>", 0, "3..5", "10..=56"],
    ["<i>second</i>", "0..3", "..78"],
    ["You clicked {{ count }} times and have {{ banana_count }} bananas"]
  ]
}

With ranges, {{ count }} is a special variable that refers to the count provided to the range, so you don't need to also provide it:

{
  "click_count": [
    ["You have not clicked yet", 0],
    ["You clicked once", 1],
    ["You clicked {{ count }} times"]
  ]
}
#![allow(unused)]
fn main() {
t!(i18n, click_count, count = || 0);
}

Will result in "You have not clicked yet" and

#![allow(unused)]
fn main() {
t!(i18n, click_count, count = || 5);
}

Will result in "You clicked 5 times".

Providing count will create an error:

#![allow(unused)]
fn main() {
t!(i18n, click_count, count = 12, count = || 5); // compilation error
}

What if I need multiple counts ?

If you need multiple counts, for example:

{
  "key": "{{ boys_count }} boys and {{ girls_count }} girls"
}

There isn't a way to represent this in a single key, You will need Foreign keys that you can read about in a future chapter.