Basic usage
Minimal skeleton
The most basic CSR app is reproduced here:
.
├── 📄 Cargo.toml
├── 📁 locales
│   ├── 📁 en
│   │   └── 📄 main.ftl
│   └── 📁 es
│       └── 📄 main.ftl
└── 📁 src
    ├── 📄 main.rs
    └── 📄 lib.rs
# locales/en/main.ftl
select-a-language = Select a language:
language-selected-is = The selected language is { $lang }.
# locales/es/main.ftl
select-a-language = Selecciona un idioma:
language-selected-is = El idioma seleccionado es { $lang }.
// src/lib.rs
use leptos::prelude::*;
use leptos_fluent::{I18n, leptos_fluent, move_tr, Language};
#[component]
pub fn I18nProvider(children: Children) -> impl IntoView {
    leptos_fluent! {
        children: children(),
        locales: "./locales",
        default_language: "en",
    }
}
#[component]
pub fn App() -> impl IntoView {
    view! {
        <I18nProvider>
            <LanguageSelector/>
        </I18nProvider>
    }
}
#[component]
fn LanguageSelector() -> impl IntoView {
    let i18n = expect_context::<I18n>();
    view! {
        <p>{move_tr!("select-a-language")}</p>
        <fieldset>
            {move || {
                i18n.languages.iter().map(|lang| render_language(lang)).collect::<Vec<_>>()
            }}
        </fieldset>
        <p>
            {move_tr!(
                 "language-selected-is",
                 { "lang" => i18n.language.get().name }
            )}
        </p>
    }
}
fn render_language(lang: &'static Language) -> impl IntoView {
    let i18n = expect_context::<I18n>();
    // Passed as atrribute, `Language` is converted to their code,
    // so `<input id=lang` becomes `<input id=lang.id.to_string()`
    view! {
        <div>
            <label for=lang>{lang.name}</label>
            <input
                id=lang
                value=lang
                name="language"
                checked=i18n.language.get() == lang
                on:click=move |_| i18n.language.set(lang)
                type="radio"
            />
        </div>
    }
}
// src/main.rs
pub fn main() {
    console_error_panic_hook::set_once();
    leptos::mount::mount_to_body(minimal_example::App);
}
# Cargo.toml
[package]
name = "minimal-example"
edition = "2021"
version = "0.1.0"
[lib]
name = "minimal_example"
path = "src/lib.rs"
[dependencies]
leptos = { version = "0.8", features = ["csr"] }
leptos-fluent = "0.2"
console_error_panic_hook = "0.1"
# Using cargo-leptos
[package.metadata.leptos]
watch-additional-files = ["locales"]
Translating messages
Use the move_tr! macro to translate a string. The macro takes the key of the
translation and an optional object with the variables to interpolate:
move_tr!("select-a-language")
move_tr!("language-selected-is", { "lang" => i18n.language.get().name })
Additionally, use the tr! macro to translate a string inside
a reactive context. Note that if is not inside a reactive context,
the translation won't be updated on the fly when the language changes.
This can lead to warnings in console output like:
At `./path/to/file.rs:ln`, you access a signal or memo (defined at
`./path/to/file.rs:ln`) outside of a reactive context. This might mean your
app is not responding to changes in signal values in the way you expect.
Can be fixed by replacing calls to tr! with move_tr! or wrapping the
tr! calls in a reactive context.
The previous code could be rewritten as:
move || tr!("select-a-language")
move || tr!("language-selected-is", { "lang" => i18n.language.get().name })
The main difference is that move_tr! encapsulates the movement in a
leptos::Signal, strictly would be rewritten as:
Signal::derive(move || tr!("select-a-language"))
Retrieving the I18n context
use leptos::prelude::expect_context;
use leptos_fluent::I18n;
let i18n = expect_context::<I18n>();
let i18n = use_context::<I18n>().expect("No `I18n` context found");
Using the I18n context
The i18n context has the following fields:
language: A read-write signal with a pointer to the static current active language.languages: A pointer to a static list of pointers of the static available languages.translations: A signal to the vector of fluent-templates loaders that stores the translations.
Update language
To update the language, use the set method of language:
use leptos::prelude::expect_context;
use leptos_fluent::I18n;
let i18n = expect_context::<I18n>();
i18n.language.set(lang);
When nightly feature is enabled, can be updated passing a new language to the
context as a function with:
let i18n = expect_context::<I18n>();
i18n(lang);
Get active language
To get the current active language, use get method of language field:
use leptos::prelude::*;
use leptos_fluent::I18n;
let i18n = expect_context::<I18n>();
let lang = i18n.language.get();
With nightly feature enabled, can get the active language calling the context
as a function:
use leptos::prelude::*;
use leptos_fluent::I18n;
let i18n = expect_context::<I18n>();
let lang = i18n();
Get available languages
To get the available languages, iterate over the languages field:
i18n.languages.iter()
Check if a language is active
To check if a language is the active one, use:
use leptos::prelude::*;
use leptos_fluent::I18n;
let i18n = expect_context::<I18n>();
lang == i18n.language.get()