fluent-bundle 0.10.2

A localization system designed to unleash the entire expressive power of natural language translations.
Documentation
// This is an example of an application which uses a custom formatter
// to format selected types of values.
//
// This allows users to plug their own number formatter to Fluent.
use std::sync::Mutex;

use intl_memoizer::IntlLangMemoizer;
use unic_langid::LanguageIdentifier;

use fluent_bundle::types::{FluentNumber, FluentNumberOptions, FluentValue};
use fluent_bundle::{FluentArgs, FluentBundle, FluentResource};

fn custom_formatter(num: &FluentValue, _intls: &Mutex<IntlLangMemoizer>) -> Option<String> {
    match num {
        FluentValue::Number(n) => Some(format!("CUSTOM({})", n.value).into()),
        _ => None,
    }
}

fn main() {
    // 1. Bootstrap a FluentBundle with a number of messages which use
    //    number formatting in different forms.
    let ftl_string = String::from(
        "
key-implicit = Here is an implicitly encoded number: { 5 }.
key-explicit = Here is an explicitly encoded number: { NUMBER(5) }.
key-var-implicit = Here is an implicitly encoded variable: { $num }.
key-var-explicit = Here is an explicitly encoded variable: { NUMBER($num) }.
key-var-with-arg = Here is a variable formatted with an argument { NUMBER($num, minimumFractionDigits: 5) }.
    ",
    );
    let res = FluentResource::try_new(ftl_string).expect("Could not parse an FTL string.");

    let lang: LanguageIdentifier = "en".parse().unwrap();
    let mut bundle = FluentBundle::new(&[lang]);
    bundle
        .add_resource(res)
        .expect("Failed to add FTL resources to the bundle.");
    bundle
        .add_function("NUMBER", |positional, named| {
            match positional.get(0) {
                Some(FluentValue::Number(n)) => {
                    let mut num = n.clone();
                    // This allows us to merge the arguments provided
                    // as arguments to the function into the new FluentNumber.
                    num.options.merge(named);
                    FluentValue::Number(num)
                }
                _ => FluentValue::None,
            }
        })
        .expect("Failed to add a function.");
    bundle.set_use_isolating(false);

    let mut errors = vec![];

    // 2. First, we're going to format the number using the implicit formatter.
    //    At the moment the number will be formatted in a very dummy way, since
    //    we do not have a locale aware number formatter available yet.
    let msg = bundle
        .get_message("key-implicit")
        .expect("Message doesn't exist.");
    let pattern = msg.value.expect("Message has no value.");
    let value = bundle.format_pattern(&pattern, None, &mut errors);
    assert_eq!(value, "Here is an implicitly encoded number: 5.");
    println!("{}", value);

    // 3. Next, we're going to plug our custom formatter.
    bundle.set_formatter(Some(custom_formatter));

    // 4. Now, when you attempt to format a number, the custom formatter
    //    will be used instead of the default one.
    let msg = bundle
        .get_message("key-implicit")
        .expect("Message doesn't exist.");
    let pattern = msg.value.expect("Message has no value.");
    let value = bundle.format_pattern(&pattern, None, &mut errors);
    assert_eq!(value, "Here is an implicitly encoded number: CUSTOM(5).");
    println!("{}", value);

    // 5. The same custom formatter will be used for explicitly formatter numbers,
    //    and variables of type number.
    let msg = bundle
        .get_message("key-explicit")
        .expect("Message doesn't exist.");
    let pattern = msg.value.expect("Message has no value.");
    let value = bundle.format_pattern(&pattern, None, &mut errors);
    assert_eq!(value, "Here is an explicitly encoded number: CUSTOM(5).");
    println!("{}", value);

    let msg = bundle
        .get_message("key-var-implicit")
        .expect("Message doesn't exist.");
    let pattern = msg.value.expect("Message has no value.");
    let mut args = FluentArgs::new();
    args.insert("num", FluentValue::from(-15));
    let value = bundle.format_pattern(&pattern, Some(&args), &mut errors);
    assert_eq!(
        value,
        "Here is an implicitly encoded variable: CUSTOM(-15)."
    );
    println!("{}", value);

    let msg = bundle
        .get_message("key-var-explicit")
        .expect("Message doesn't exist.");
    let pattern = msg.value.expect("Message has no value.");
    let mut args = FluentArgs::new();
    args.insert("num", FluentValue::from(-15));
    let value = bundle.format_pattern(&pattern, Some(&args), &mut errors);
    assert_eq!(
        value,
        "Here is an explicitly encoded variable: CUSTOM(-15)."
    );
    println!("{}", value);

    // 6. The merging operation on FluentNumber options allows the
    //    options provided from the localizer to be merged into the
    //    default ones and ones provided by the developer.
    let msg = bundle
        .get_message("key-var-explicit")
        .expect("Message doesn't exist.");
    let pattern = msg.value.expect("Message has no value.");
    let mut args = FluentArgs::new();
    let num = FluentNumber::new(
        25.2,
        FluentNumberOptions {
            maximum_fraction_digits: Some(8),
            minimum_fraction_digits: Some(1),
            ..Default::default()
        },
    );
    args.insert("num", num.into());
    let value = bundle.format_pattern(&pattern, Some(&args), &mut errors);

    // Notice, that since we specificed minimum and maximum fraction digits options
    // to be 1 and 8 when construction the argument, and then the minimum fraction
    // digits option has been overridden in the localization the formatter
    // will received options:
    //  - minimum_fraction_digits: Some(5)
    //  - maximum_fraction_digits: Some(8)
    assert_eq!(
        value,
        "Here is an explicitly encoded variable: CUSTOM(25.2)."
    );
    println!("{}", value);
}