Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue with leptos_fluent and Islands feature in Leptos #245

Closed
NCura opened this issue Oct 1, 2024 · 2 comments · Fixed by #246
Closed

Issue with leptos_fluent and Islands feature in Leptos #245

NCura opened this issue Oct 1, 2024 · 2 comments · Fixed by #246
Labels
question Further information is requested

Comments

@NCura
Copy link
Contributor

NCura commented Oct 1, 2024

Hello,

I'm trying to implement islands in one of my websites, and I've encountered an issue while using leptos_fluent with the islands feature. I’ve already opened an issue in the main Leptos repo regarding my initial problem, and after resolving it, I ran into this issue with leptos_fluent.

Here’s what I’ve done so far:

  • Created a new project using cargo leptos new --git leptos-rs/start-axum.
  • Enabled the islands feature:
    • In Cargo.toml, added the "experimental-islands" feature to leptos and leptos_axum.
    • In src/lib.rs, replaced leptos::mount_to_body(App); with leptos::leptos_dom::HydrationCtx::stop_hydrating();.
  • Added leptos-fluent dependencies in Cargo.toml:
leptos-fluent = { version = "0.1.21", features = ["nightly"] }
fluent-templates = { version = "0.11.0", default-features = false, features = ["macros", "walkdir"] }
  • In src/app.rs, replaced #[component] with #[island] for the HomePage function.

  • Ran cargo leptos watch and verified that the site works as expected, with the counter incrementing on button click.

  • In src/app.rs, added the following translations setup:

static_loader! {
    static TRANSLATIONS = {
        locales: "./locales",
        fallback_language: "en",
    };
}

pub static COMPOUND: &[&Lazy<StaticLoader>] = &[&TRANSLATIONS, &TRANSLATIONS];

And inside the App() function:

let i18n = leptos_fluent! {
    translations: [TRANSLATIONS, TRANSLATIONS] + COMPOUND,
    languages: "./locales/languages.json",
    locales: "./locales",
    sync_html_tag_lang: true,
    sync_html_tag_dir: true,
    cookie_name: "lang",
    cookie_attrs: "SameSite=Strict; Secure; path=/; max-age=600",
    initial_language_from_cookie: true,
    initial_language_from_cookie_to_localstorage: true,
    set_language_to_cookie: true,
    url_param: "lang",
    initial_language_from_url_param: true,
    initial_language_from_url_param_to_localstorage: true,
    initial_language_from_url_param_to_cookie: true,
    set_language_to_url_param: true,
    localstorage_key: "language",
    initial_language_from_localstorage: true,
    initial_language_from_localstorage_to_cookie: true,
    set_language_to_localstorage: true,
    initial_language_from_navigator: true,
    initial_language_from_navigator_to_localstorage: true,
    initial_language_from_accept_language_header: true,
};
provide_context(i18n);
  • In the HomePage function:
#[island]
fn HomePage() -> impl IntoView {
    let i18n = use_context::<I18n>().expect("to have found the i18n provided in App");
    let (count, set_count) = create_signal(0);
    let on_click = move |_| set_count.update(|count| *count += 1);

    view! {
        <button on:click=on_click>{move_tr!(i18n, "click-me")}" "{count}</button>
    }
}

However, when I visit the website, I get the following error in the browser console, and the button no longer works:

Uncaught (in promise) RuntimeError: unreachable
    at test_islands.wasm.__rust_start_panic (test-islands.wasm:0x1ec288)
    ...

I’ve managed to make it work by replacing, in HomePage,

let i18n = use_context::<I18n>().expect("to have found the i18n provided in App");

with

let i18n = leptos_fluent! {
  translations: [TRANSLATIONS, TRANSLATIONS] + COMPOUND,
  languages: "./locales/languages.json",
  locales: "./locales"
};

However, this creates a new i18n instance, which differs from the one provided in App, making it inconvenient to manage translations consistently. (I can even remove the i18n parameter from the move_tr!, and simply use the leptos_fluent! macro without assigning it to a variable.)

Do you have any suggestions on how to resolve this? I’d be happy to submit a PR with an example of leptos_fluent working alongside islands if we can find a good solution.

Thanks in advance for your help!

@mondeja
Copy link
Owner

mondeja commented Oct 1, 2024

Is the expected behaviour. In islands mode, only #[island] contents are interactive. The unreachable error is produced because you're trying to access from the client to a context that was provided in the server, so the context doesn't exists.

However, this creates a new i18n instance, which differs from the one provided in App

This statement is not totally true. Your code creates an instance on the server (inside App) and another instance is shipped to and instanciated in the client (in HomePage). It creates 2 instances on 2 different runs, one in the server and one in the client.

You'll probably end shipping a lot of translations to the client if you use the same ./locales directory. Just use different locales for the translations of each island. Note that this approach that leptos-fluent uses of allowing multiple cheap instances with a macro fits perfectly for the islands mode.

Another thing that I think that you're concerned about is how to define the same arguments for both instances. Well, note that some of the arguments only have sense for the server and others for the client. As examples, sync_html_tag_lang is ignored on server (inside #[component]) and initial_language_from_accept_language_header is ignored on client (inside #[island]). So is an imaginary problem.

Note in your example that you're providing the i18n context, but leptos_fluent! do that internally anyways. These two are equivalent:

leptos_fluent!{ ... };
let i18n = leptos_fluent!{ ... };
provide_context(i18n);

If you want the same instance parameters in all islands, just provide one with leptos_fluent! in a parent island and the context will be available to their children (see Passing Context Between Islands). If you want one i18n instance per island defining a different locales directory and maintaining the same arguments for all other island invocations, wrap the leptos_fluent! call in a macro, something like:

macro_rules! i18n {
    ($translations:expr$(,)?) => {
        leptos_fluent! {
            translations: [$translations],
            // ... rest of your arguments
        }
    };
}

Probably there are bugs or quirks that I'm not aware about because I didn't used this mode so much. Feel free to open a PR with an island example or documentation about that, it would be really useful for other users.

@mondeja mondeja added the question Further information is requested label Oct 1, 2024
@NCura
Copy link
Contributor Author

NCura commented Oct 2, 2024

I've submitted a pull request with an islands example. While there are still some areas for improvement, it provides a foundation to build upon.

@mondeja mondeja linked a pull request Oct 7, 2024 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants