# Fluent-Typed

When using translation keys, there is often no easy way to know if they are being used
correctly and if they are being used at all. This project generatesjkjj, using the `fluent` ast,
the function definitions for the translation keys in a fluent file.

In order to guarantee the safeness, funtions are only generated for messages that
are found in all the locales. For those only found for some locales
or if the signature of the messages are different a warning is printed.

It is up to the user to load the translation resources, which gives him the liberty to
choose how they are retreived (embedded in the binary, loaded from a file, downloaded etc).

There is no need to handle any fallback language since it is guaranteed that all messages
are translated into all languages

## Usage

```toml
# in Cargo.toml
[dependencies]
fluent-typed = 0.1

[build-dependencies]
fluent-typed = { version = "0.1", features = ["build"] }
```

```rust
// in build.rs
fn main() -> std::process::ExitCode {
    // generate the src/l10n.rs file from the fluent translations found in the locales folder,
    // prefix the generated functions with "msg_" and indent the code with 4 spaces.
    // This function returns an ExitCode.
    fluent_typed::build_from_locales_folder("locales", "src/l10n.rs", "msg_", "    ")

    // Note: there are also fluent_typed::try_build_from_locales_folder which returns a Result
}
```

```rust
// in lib.rs or main.rs
mod l10n;
use l10n::{L10nResource, L10n};


fn main() {
    // The L10nResource struct is generated by the build script and contains
    // one field per fluent resource used when generating the functions.
    let resources = L10nResource {
      // contains:
      //   # $name (String) - The user's name.
      //   hello = Hello { $name }
      //   greeting = Welcome!
      base: include_str!("locales/en/base.ftl"),
      // contains:
      //   # $count (Number) - How many unread meassages.
      //   unread_messages = You have { $count } unread messages
      settings: include_str!("locales/en/settings.ftl"),
    };

    // The L10n struct is generated by the build script and has
    // one function per message in the fluent resources.
    let strs = L10n::load("en", resources).unwrap();

    // A message without arguments.
    assert_eq!("Welcome!", strs.msg_greeting());
    // A message with a string argument (AsRef<str>).
    assert_eq!("Hello world", strs.msg_hello("world"));
    // A message with a number argument (Into<FluentNumber>).
    assert_eq!("You have 2 unread messages", strs.msg_unread_messages(2));
}
```

## Type deduction

Since the fluent syntax doesn't explicitly specify the type of the translation variables, this
project uses the following rules to infer the type of the translation variables:

- String:
  - If a variable's comment contains `(String)`, as in `# $name (String) - The name.`
- Number:
  - If a variable's comment contains `(Number)`, as in `# $count (Number) - How many.`
  - If a [NUMBER](https://projectfluent.org/fluent/guide/functions.html#number-1) function is used, asin `dpi-ratio = Your DPI ratio is { NUMBER($ratio) }`
  - If a [selector](https://projectfluent.org/fluent/guide/selectors.html) only contains numbers
    and CLDR plural catagories: `zero`, `one`, `two`, `few`, `many`, and `other`. Example:
    ```
    your-rank = { NUMBER($pos, type: "ordinal") ->
       [1] You finished first!
       [one] You finished {$pos}st
       [two] You finished {$pos}nd
       [few] You finished {$pos}rd
      *[other] You finished {$pos}th
    }
    ```
