XAMPPRocky / fluent-templates

Easily add Fluent to your Rust project.
Apache License 2.0
136 stars 27 forks source link

Compile time i18n macro #53

Open XAMPPRocky opened 12 months ago

XAMPPRocky commented 12 months ago

Discussed in https://github.com/XAMPPRocky/fluent-templates/discussions/52

Originally posted by **patefacio** September 5, 2023 For compile-time support is it possible to get a compile error when compiling if the lookup fails? I tried cargo-i18n with i18n-embed and in that setup a mispelling will trigger a compile error. My issue with that setup was I could not figure out how to make it work in wasm. This project works just fine in wasm but the forced breakage at compile time would be great. `assert_eq!("Hello World!", LOCALES.lookup(&US_ENGLISH, "hello-world-ooops"));`

Thank you for question! I think this should be possible if implemented as procedural macro. I don't have much time to work on this at the moment, but I'd be happy to review a PR for it. We already have a macro crate, so what would work would be to add a new macro similar to the existing one, but that uses the ArcLoader to load localisations into the macro itself, and then provides a panic message when it's not found using a separate lookup macro.

mondeja commented 5 months ago

My issue with that setup was I could not figure out how to make it work in wasm.

There is no special treatment needed for fluent-templates targetting wasm, see leptos-fluent.

joshchngs commented 5 months ago

I've just started using Fluent in a project, and thought of a similar way to get compile-time checking. If we generate helper functions for every message, we can also check the variables (and get autocomplete, maybe?).

For example:

# This .ftl file
issue-control-conflict-summary =
    Control Input '{ $input-name }' on Processing Object '{ $po-display-name }' has conflicting inputs
issue-control-conflict-details =
    Driving a Control Input with more than one signal can cause fighting.

would generate

mod tr {
    use std::collections::HashMap;

    use fluent::FluentValue;
    use fluent_templates::Loader;
    use unic_langid::LanguageIdentifier;

    pub fn issue_control_conflict_summary(
        lang: &LanguageIdentifier,
        input_name: FluentValue<'_>,
        po_display_name: FluentValue<'_>,
    ) -> Option<String> {
        let mut args = HashMap::with_capacity(2);
        args.insert("input-name", input_name);
        args.insert("po-display-name", po_display_name);

        super::LOCALES.try_lookup_with_args(lang, "issue-control-conflict-summary", &args)
    }

    pub fn issue_control_conflict_details(lang: &LanguageIdentifier) -> Option<String> {
        super::LOCALES.try_lookup(lang, "issue-control-conflict-details")
    }
}

I presume somebody has thought of this before, but has anybody implemented it?