leptos_fluent_macros/
lib.rs

1#![deny(missing_docs)]
2#![forbid(unsafe_code)]
3
4//! Macros for [leptos-fluent].
5//!
6//! [leptos-fluent]: https://crates.io/crates/leptos-fluent
7
8extern crate proc_macro;
9
10pub(crate) mod cookie;
11mod exprpath;
12mod files_tracker;
13#[cfg(not(feature = "ssr"))]
14pub(crate) mod fluent_entries;
15mod fluent_resources;
16mod languages;
17mod loader;
18#[cfg(not(feature = "ssr"))]
19pub(crate) mod tr_macros;
20#[cfg(not(feature = "ssr"))]
21mod translations_checker;
22#[cfg(not(feature = "ssr"))]
23mod translations_filler;
24
25pub(crate) use exprpath::evaluate_exprpath;
26use files_tracker::build_files_tracker_quote;
27#[cfg(not(feature = "ssr"))]
28pub(crate) use fluent_resources::FluentResources;
29pub(crate) use fluent_resources::{
30    build_fluent_resources_and_file_paths, FluentFilePaths,
31};
32use languages::build_languages_quote;
33pub(crate) use languages::ParsedLanguage;
34use loader::{I18nLoader, LitBoolExprOrIdent, LitBoolOrStr, TokenStreamStr};
35use quote::{quote, ToTokens};
36
37#[cfg(feature = "debug")]
38#[inline(always)]
39pub(crate) fn debug(msg: &str) {
40    #[allow(clippy::print_stdout)]
41    {
42        println!("[leptos-fluent/debug] {msg}");
43    };
44}
45
46/// Create the i18n context for internationalization.
47///
48/// [Reference](https://mondeja.github.io/leptos-fluent/latest/leptos_fluent.html)
49///
50/// # Example
51///
52/// ```rust,ignore
53/// use fluent_templates::static_loader;
54/// use leptos::prelude::*;
55/// use leptos_fluent::leptos_fluent;
56///
57/// static_loader! {
58///     static TRANSLATIONS = {
59///         locales: "./locales",
60///         fallback_language: "en-US",
61///     };
62/// }
63///
64/// #[component]
65/// pub fn I18n(children: Children) -> impl IntoView {
66///     leptos_fluent! {
67///         children: children(),
68///         translations: [TRANSLATIONS],
69///         default_language: "en-US",
70///         languages: "./locales/languages.json",
71///         sync_html_tag_lang: true,
72///         sync_html_tag_dir: true,
73///         url_param: "lang",
74///         initial_language_from_url_param: true,
75///         initial_language_from_url_param_to_local_storage: true,
76///         initial_language_from_url_param_to_cookie: true,
77///         set_language_to_url_param: true,
78///         local_storage_key: "language",
79///         initial_language_from_local_storage: true,
80///         initial_language_from_local_storage_to_cookie: true,
81///         set_language_to_local_storage: true,
82///         initial_language_from_navigator: true,
83///         initial_language_from_accept_language_header: true,
84///         cookie_name: "lang",
85///         cookie_attrs: "SameSite=Strict; Secure; path=/; max-age=2592000",
86///         initial_language_from_cookie: true,
87///         set_language_to_cookie: true,
88///     }
89/// }
90///
91/// #[component]
92/// fn App() -> impl IntoView {
93///     view! {
94///         <I18n>
95///             <LanguageSelector/>
96///         </I18n>
97///     }
98/// }
99/// ```
100///
101/// See the reference with all the parameters explained in detail at
102/// <https://mondeja.github.io/leptos-fluent/latest/leptos_fluent.html>
103#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip_all))]
104#[proc_macro]
105pub fn leptos_fluent(
106    input: proc_macro::TokenStream,
107) -> proc_macro::TokenStream {
108    let I18nLoader {
109        warnings,
110        fluent_file_paths,
111        children,
112        translations,
113        languages,
114        languages_path,
115        raw_languages_path,
116        locales_path,
117        core_locales_path,
118        default_language,
119        check_translations,
120        fill_translations,
121        customise,
122        provide_meta_context,
123        sync_html_tag_lang,
124        sync_html_tag_dir,
125        initial_language_from_url_param,
126        url_param,
127        initial_language_from_url_param_to_local_storage,
128        initial_language_from_url_param_to_session_storage,
129        initial_language_from_url_param_to_cookie,
130        initial_language_from_url_param_to_server_function,
131        set_language_to_url_param,
132        local_storage_key,
133        initial_language_from_local_storage,
134        initial_language_from_local_storage_to_cookie,
135        initial_language_from_local_storage_to_session_storage,
136        initial_language_from_local_storage_to_server_function,
137        set_language_to_local_storage,
138        session_storage_key,
139        initial_language_from_session_storage,
140        initial_language_from_session_storage_to_cookie,
141        initial_language_from_session_storage_to_local_storage,
142        initial_language_from_session_storage_to_server_function,
143        set_language_to_session_storage,
144        initial_language_from_navigator,
145        initial_language_from_navigator_to_local_storage,
146        initial_language_from_navigator_to_session_storage,
147        initial_language_from_navigator_to_cookie,
148        initial_language_from_navigator_to_server_function,
149        initial_language_from_accept_language_header,
150        set_language_from_navigator,
151        cookie_name,
152        cookie_attrs,
153        initial_language_from_cookie,
154        initial_language_from_cookie_to_local_storage,
155        initial_language_from_cookie_to_session_storage,
156        initial_language_from_cookie_to_server_function,
157        set_language_to_cookie,
158        initial_language_from_server_function,
159        initial_language_from_server_function_to_cookie,
160        initial_language_from_server_function_to_local_storage,
161        set_language_to_server_function,
162        url_path,
163        initial_language_from_url_path,
164        initial_language_from_url_path_to_cookie,
165        initial_language_from_url_path_to_local_storage,
166        initial_language_from_url_path_to_session_storage,
167        initial_language_from_url_path_to_server_function,
168        #[cfg(feature = "system")]
169        initial_language_from_system,
170        #[cfg(feature = "system")]
171        initial_language_from_system_to_data_file,
172        #[cfg(feature = "system")]
173        set_language_to_data_file,
174        #[cfg(feature = "system")]
175        initial_language_from_data_file,
176        #[cfg(feature = "system")]
177        data_file_key,
178    } = syn::parse_macro_input!(input as I18nLoader);
179
180    let n_languages = languages.len();
181    let languages_quote = build_languages_quote(&languages);
182
183    // files tracker
184    let files_tracker_quote = build_files_tracker_quote(
185        &fluent_file_paths,
186        &languages_path,
187        &core_locales_path,
188    );
189
190    // Less code possible on nightly
191    #[cfg(feature = "nightly")]
192    let get_language_quote = quote! {
193        (::leptos::prelude::expect_context::<::leptos_fluent::I18n>())()
194    };
195
196    #[cfg(all(feature = "nightly", not(feature = "ssr")))]
197    let set_language_quote = quote! {
198        (::leptos::prelude::expect_context::<::leptos_fluent::I18n>())(l)
199    };
200
201    #[cfg(not(feature = "nightly"))]
202    let get_language_quote = quote! {
203        ::leptos::prelude::expect_context::<::leptos_fluent::I18n>().language.get()
204    };
205
206    #[cfg(all(not(feature = "nightly"), not(feature = "ssr")))]
207    let set_language_quote = quote! {
208        ::leptos::prelude::expect_context::<::leptos_fluent::I18n>().language.set(l)
209    };
210
211    let cookie_name_quote = match cookie_name.lit {
212        Some(ref lit) => quote! { #lit },
213        None => match cookie_name.expr {
214            Some(ref expr) => quote! { #expr },
215            None => quote! { "lf-lang" },
216        },
217    };
218
219    let cookie_attrs_quote = match cookie_attrs.lit {
220        Some(ref lit) => quote! { #lit },
221        None => match cookie_attrs.expr {
222            Some(ref expr) => quote! { #expr },
223            None => quote! { "" },
224        },
225    };
226
227    #[cfg(not(feature = "ssr"))]
228    let local_storage_key_quote = match local_storage_key.lit {
229        Some(ref lit) => quote! { #lit },
230        None => match local_storage_key.expr {
231            Some(ref expr) => quote! { #expr },
232            None => quote! { "lang" },
233        },
234    };
235
236    #[cfg(not(feature = "ssr"))]
237    let session_storage_key_quote = match session_storage_key.lit {
238        Some(ref lit) => quote! { #lit },
239        None => match session_storage_key.expr {
240            Some(ref expr) => quote! { #expr },
241            None => quote! { "lang" },
242        },
243    };
244
245    #[cfg(feature = "system")]
246    let data_file_key_quote = match data_file_key.lit {
247        Some(ref lit) => quote! { #lit },
248        None => match data_file_key.expr {
249            Some(ref expr) => quote! { #expr },
250            None => quote! { "leptos-fluent" },
251        },
252    };
253
254    // discover from system language (desktop apps)
255    #[cfg(all(feature = "system", not(feature = "ssr")))]
256    let initial_language_from_system_quote: proc_macro2::TokenStream = {
257        let initial_language_from_system_to_data_file_quote: proc_macro2::TokenStream = {
258            let effect_quote = quote! {
259                ::leptos_fluent::data_file::set(
260                    #data_file_key_quote,
261                    &l.id.to_string(),
262                );
263            };
264
265            initial_language_from_system_to_data_file.iter().map(|param| {
266                match param.expr {
267                    Some(ref expr) => {
268                        let q = quote! {
269                            if lang.is_none() && #expr && !#data_file_key_quote.is_empty() {
270                                #effect_quote
271                            }
272                        };
273                        match param.exprpath {
274                            Some(ref path) => quote!(#path{#q}),
275                            None => q,
276                        }
277                    },
278                    None => quote!(),
279                }
280            }).collect()
281        };
282
283        let effect_quote = quote! {
284            if let Ok(l) = ::leptos_fluent::__reexports::current_locale::current_locale() {
285                lang = ::leptos_fluent::l(&l, &LANGUAGES);
286                if let Some(l) = lang {
287                    #initial_language_from_system_to_data_file_quote
288                }
289            }
290        };
291
292        initial_language_from_system
293            .iter()
294            .map(|param| match param.expr {
295                Some(ref expr) => {
296                    let q = quote! {
297                        if #expr && lang.is_none() {
298                            #effect_quote
299                        }
300                    };
301                    match param.exprpath {
302                        Some(ref path) => quote!(#path{#q}),
303                        None => q,
304                    }
305                }
306                None => quote!(),
307            })
308            .collect()
309    };
310
311    #[cfg(all(not(feature = "system"), not(feature = "ssr")))]
312    let initial_language_from_system_quote = quote! {};
313
314    #[cfg(all(feature = "system", feature = "ssr"))]
315    {
316        _ = data_file_key;
317        _ = initial_language_from_system;
318        _ = initial_language_from_system_to_data_file;
319    }
320
321    #[cfg(feature = "system")]
322    let sync_language_with_data_file_quote: proc_macro2::TokenStream =
323        set_language_to_data_file
324            .iter()
325            .map(|param| {
326                let set_language_to_data_file_quote = match param.expr {
327                    Some(ref expr) => quote! {
328                        if #expr {
329                            #data_file_key_quote
330                        } else {
331                            ""
332                        }
333                    },
334                    None => quote! { "" },
335                };
336
337                // TODO: optimize checking if empty at compile time when literal
338                let effect_quote = quote! {
339                    ::leptos::prelude::Effect::new(move |_| {
340                        if #set_language_to_data_file_quote.is_empty() {
341                            return;
342                        }
343                        ::leptos_fluent::data_file::set(
344                            #set_language_to_data_file_quote,
345                            &#get_language_quote.id.to_string(),
346                        );
347                    });
348                };
349
350                match param.expr {
351                    Some(ref expr) => {
352                        let q = quote!(if #expr {#effect_quote});
353                        match param.exprpath {
354                            Some(ref path) => quote!(#path{#q}),
355                            None => q,
356                        }
357                    }
358                    None => quote!(),
359                }
360            })
361            .collect();
362
363    #[cfg(not(feature = "system"))]
364    let sync_language_with_data_file_quote = quote! {};
365
366    #[cfg(all(feature = "system", not(feature = "ssr")))]
367    let initial_language_from_data_file_quote: proc_macro2::TokenStream =
368        initial_language_from_data_file
369            .iter()
370            .map(|param| {
371                let initial_language_from_data_file_quote = match param.expr {
372                    Some(ref expr) => quote! {
373                        if #expr {
374                            #data_file_key_quote
375                        } else {
376                            ""
377                        }
378                    },
379                    None => quote! { "" },
380                };
381
382                let effect_quote = quote! {
383                    if #initial_language_from_data_file_quote.is_empty() {
384                        return;
385                    }
386                    if let Some(l) = ::leptos_fluent::data_file::get(
387                        #initial_language_from_data_file_quote
388                    ) {
389                        lang = ::leptos_fluent::l(&l, &LANGUAGES);
390                    }
391                };
392
393                match param.expr {
394                    Some(ref expr) => {
395                        let q = quote! {
396                            if #expr && lang.is_none() {
397                                #effect_quote
398                            }
399                        };
400                        match param.exprpath {
401                            Some(ref path) => quote!(#path{#q}),
402                            None => q,
403                        }
404                    }
405                    None => quote!(),
406                }
407            })
408            .collect();
409
410    #[cfg(all(not(feature = "system"), not(feature = "ssr")))]
411    let initial_language_from_data_file_quote = quote! {};
412
413    #[cfg(all(feature = "system", feature = "ssr"))]
414    {
415        _ = initial_language_from_data_file;
416    }
417
418    let initial_language_from_server_function_quote: proc_macro2::TokenStream = {
419        let set_to_cookie_quote: proc_macro2::TokenStream = {
420            let effect_quote = quote! {
421                ::leptos_fluent::cookie::set(
422                    #cookie_name_quote,
423                    &l.id.to_string(),
424                    &#cookie_attrs_quote
425                );
426            };
427
428            initial_language_from_server_function_to_cookie
429                .iter()
430                .map(|param| match param.expr {
431                    Some(ref expr) => {
432                        let q = quote!(if #expr {#effect_quote});
433                        match param.exprpath {
434                            Some(ref path) => quote!(#path{#q}),
435                            None => q,
436                        }
437                    }
438                    None => quote!(),
439                })
440                .collect()
441        };
442
443        #[cfg(not(feature = "ssr"))]
444        let set_to_local_storage_quote: proc_macro2::TokenStream = {
445            let effect_quote = quote! {
446                ::leptos_fluent::local_storage::set(
447                    #local_storage_key_quote,
448                    &l.id.to_string()
449                );
450            };
451
452            initial_language_from_server_function_to_local_storage
453                .iter()
454                .map(|param| match param.expr {
455                    Some(ref expr) => {
456                        let q = quote!(if #expr {#effect_quote});
457                        match param.exprpath {
458                            Some(ref path) => quote!(#path{#q}),
459                            None => q,
460                        }
461                    }
462                    None => quote!(),
463                })
464                .collect()
465        };
466
467        #[cfg(feature = "ssr")]
468        let set_to_local_storage_quote = {
469            _ = initial_language_from_server_function_to_local_storage;
470
471            quote! {}
472        };
473
474        initial_language_from_server_function
475            .iter()
476            .map(|param| {
477                let ident = &param.expr;
478                let effect_quote = quote! {
479                    ::leptos::task::spawn(async move {
480                        let lang_result = #ident().await;
481                        if let Ok(maybe_lang) = lang_result {
482                            if let Some(l) = maybe_lang {
483                                lang = ::leptos_fluent::l(&l, &LANGUAGES);
484                                if let Some(l) = lang {
485                                    #set_to_cookie_quote
486                                    #set_to_local_storage_quote
487                                }
488                            }
489                        }
490                    });
491                };
492
493                match param.expr {
494                    Some(_) => {
495                        let quote = quote! {
496                            if lang.is_none() {
497                                #effect_quote
498                            }
499                        };
500                        match param.exprpath {
501                            Some(ref path) => quote!(#path{#quote}),
502                            None => quote,
503                        }
504                    }
505                    None => quote!(),
506                }
507            })
508            .collect()
509    };
510
511    let sync_language_with_server_function_quote: proc_macro2::TokenStream =
512        set_language_to_server_function.iter().map(|param| {
513            let ident = &param.expr;
514            let effect_quote = quote! {
515                ::leptos::prelude::Effect::new(move |_| {
516                    ::leptos::task::spawn(async {
517                        _ = #ident(#get_language_quote.id.to_string()).await;
518                    });
519                });
520            };
521
522            match param.expr {
523                Some(_) => match param.exprpath {
524                    Some(ref path) => quote! { #path{#effect_quote} },
525                    None => effect_quote,
526                },
527                None => quote!(),
528            }
529        }).collect();
530
531    let initial_language_from_url_path_to_server_function_quote: proc_macro2::TokenStream =
532        initial_language_from_url_path_to_server_function.iter().map(|param| {
533            match param.expr {
534                Some(ref ident) => {
535                    let quote = quote! {
536                        ::leptos::task::spawn(async move {
537                            _ = #ident(l.id.to_string()).await;
538                        });
539                    };
540                    match param.exprpath {
541                        Some(ref path) => quote!(#path{#quote}),
542                        None => quote,
543                    }
544                },
545                None => quote!(),
546            }
547        }).collect();
548
549    let initial_language_from_url_path_quote: proc_macro2::TokenStream = {
550        if let Some(ref ident) = url_path {
551            initial_language_from_url_path.iter().map(|param| {
552                #[cfg(not(feature = "ssr"))]
553                let effect_quote = {
554                    let to_cookie_effect_quote: proc_macro2::TokenStream =
555                        initial_language_from_url_path_to_cookie.iter().map(|param| {
556                            let effect_quote = quote! {
557                                ::leptos_fluent::cookie::set(
558                                    #cookie_name_quote,
559                                    &l.id.to_string(),
560                                    &#cookie_attrs_quote
561                                );
562                            };
563
564                            match param.expr {
565                                Some(ref expr) => {
566                                    let q = quote! {
567                                        if #expr {
568                                            #effect_quote
569                                        }
570                                    };
571                                    match param.exprpath {
572                                        Some(ref path) => quote!(#path{#q}),
573                                        None => q,
574                                    }
575                                },
576                                None => quote!(),
577                            }
578                        }).collect();
579
580                    let to_local_storage_effect_quote: proc_macro2::TokenStream =
581                        initial_language_from_url_path_to_local_storage.iter().map(|param| {
582                            let effect_quote = quote! {
583                                ::leptos_fluent::local_storage::set(
584                                    #local_storage_key_quote,
585                                    &l.id.to_string()
586                                );
587                            };
588
589                            match param.expr {
590                                Some(ref expr) => {
591                                    let q = quote! {
592                                        if #expr {
593                                            #effect_quote
594                                        }
595                                    };
596                                    match param.exprpath {
597                                        Some(ref path) => quote!(#path{#q}),
598                                        None => q,
599                                    }
600                                },
601                                None => quote!(),
602                            }
603                        }).collect();
604
605                    let to_session_storage_effect_quote: proc_macro2::TokenStream =
606                        initial_language_from_url_path_to_session_storage.iter().map(|param| {
607                            let effect_quote = quote! {
608                                ::leptos_fluent::session_storage::set(
609                                    #local_storage_key_quote,
610                                    &l.id.to_string()
611                                );
612                            };
613
614                            match param.expr {
615                                Some(ref expr) => {
616                                    let q = quote! {
617                                        if #expr {
618                                            #effect_quote
619                                        }
620                                    };
621                                    match param.exprpath {
622                                        Some(ref path) => quote!(#path{#q}),
623                                        None => q,
624                                    }
625                                },
626                                None => quote!(),
627                            }
628                        }).collect();
629
630                    quote! {
631                        if let Some(url_path) = ::leptos_fluent::url::path::get() {
632                            lang = ::leptos_fluent::l(#ident(&url_path), &LANGUAGES);
633                            if let Some(l) = lang {
634                                #to_cookie_effect_quote
635                                #to_local_storage_effect_quote
636                                #to_session_storage_effect_quote
637                                #initial_language_from_url_path_to_server_function_quote
638                            }
639                        }
640                    }
641                };
642
643                #[cfg(all(feature = "ssr", feature = "actix"))]
644                let effect_quote = quote! {
645                    if let Some(req) = ::leptos::prelude::use_context::<::leptos_actix::Request>() {
646                        lang = ::leptos_fluent::l(#ident(&req.path()), &LANGUAGES);
647                        if let Some(l) = lang {
648                            #initial_language_from_url_path_to_server_function_quote
649                        }
650                    }
651                };
652
653                #[cfg(all(feature = "ssr", feature = "axum"))]
654                let effect_quote = quote! {
655                    if let Some(req) = ::leptos::prelude::use_context::<::axum::http::request::Parts>() {
656                        lang = ::leptos_fluent::l(#ident(&req.uri.path()), &LANGUAGES);
657                        if let Some(l) = lang {
658                            #initial_language_from_url_path_to_server_function_quote
659                        }
660                    }
661                };
662
663                #[cfg(all(feature = "ssr", not(feature = "axum"), not(feature = "actix")))]
664                let effect_quote = {
665                    _ = initial_language_from_url_path_to_server_function_quote;
666                    quote! {}
667                };
668
669                let quote = quote! {
670                    if lang.is_none() {
671                        #effect_quote
672                    }
673                };
674
675                match param.exprpath {
676                    Some(ref path) => quote! {
677                        #path{#quote}
678                    },
679                    None => quote,
680                }
681            }).collect()
682        } else {
683            quote!()
684        }
685    };
686
687    let sync_html_tag_quote: proc_macro2::TokenStream = {
688        // TODO: handle other attributes in Leptos v0.7
689        // See https://github.com/leptos-rs/leptos/issues/2856
690
691        let sync_html_tag_lang_bool_quote: proc_macro2::TokenStream = {
692            let quote = sync_html_tag_lang
693                .iter()
694                .map(|param| match param.expr {
695                    Some(ref expr) => {
696                        let q = quote! { #expr };
697                        match param.exprpath {
698                            Some(ref path) => quote!(#path{#q}),
699                            None => q,
700                        }
701                    }
702                    None => quote! { false },
703                })
704                .collect::<proc_macro2::TokenStream>();
705
706            match quote.is_empty() {
707                true => quote! { false },
708                false => quote! { #quote },
709            }
710        };
711
712        let sync_html_tag_dir_bool_quote: proc_macro2::TokenStream = {
713            let quote = sync_html_tag_dir
714                .iter()
715                .map(|param| match param.expr {
716                    Some(ref expr) => {
717                        let q = quote! { #expr };
718                        match param.exprpath {
719                            Some(ref path) => quote!(#path{#q}),
720                            None => q,
721                        }
722                    }
723                    None => quote! { false },
724                })
725                .collect::<proc_macro2::TokenStream>();
726
727            match quote.is_empty() {
728                true => quote! { false },
729                false => quote! { #quote },
730            }
731        };
732
733        let attr_lang_quote =
734            match sync_html_tag_lang_bool_quote.to_string() == "false" {
735                true => quote! {},
736                false => {
737                    quote! { attr:lang=|| {#get_language_quote.id.to_string()} }
738                }
739            };
740        let attr_dir_quote = match sync_html_tag_dir_bool_quote.to_string()
741            == "false"
742        {
743            true => quote! {},
744            false => quote! { attr:dir=|| {#get_language_quote.dir.as_str()} },
745        };
746
747        match attr_lang_quote.is_empty() && attr_dir_quote.is_empty() {
748            true => quote! {},
749            false => quote! {{
750                use ::leptos_fluent::__reexports::leptos_meta::{provide_meta_context, Html};
751                provide_meta_context();
752                ::leptos::prelude::view! {
753                    <Html #attr_lang_quote #attr_dir_quote/>
754                }
755            }},
756        }
757    };
758
759    let url_param_quote = match url_param.lit {
760        Some(ref lit) => quote! { #lit },
761        None => match url_param.expr {
762            Some(ref expr) => quote! { #expr },
763            None => quote! { "lang" },
764        },
765    };
766
767    #[cfg(not(feature = "ssr"))]
768    let sync_language_with_local_storage_quote: proc_macro2::TokenStream = {
769        let effect_quote = quote! {
770            ::leptos::prelude::Effect::new(move |_| {
771                ::leptos_fluent::local_storage::set(
772                    #local_storage_key_quote,
773                    &#get_language_quote.id.to_string()
774                );
775            });
776        };
777
778        set_language_to_local_storage
779            .iter()
780            .map(|param| match param.expr {
781                Some(ref expr) => {
782                    let q = quote! {
783                        if #expr {
784                            #effect_quote
785                        }
786                    };
787                    match param.exprpath {
788                        Some(ref path) => quote!(#path{#q}),
789                        None => q,
790                    }
791                }
792                None => quote!(),
793            })
794            .collect()
795    };
796
797    #[cfg(feature = "ssr")]
798    let sync_language_with_local_storage_quote = quote!();
799
800    #[cfg(not(feature = "ssr"))]
801    let sync_language_with_session_storage_quote: proc_macro2::TokenStream = {
802        let effect_quote = quote! {
803            ::leptos::prelude::Effect::new(move |_| {
804                ::leptos_fluent::session_storage::set(
805                    #session_storage_key_quote,
806                    &#get_language_quote.id.to_string()
807                );
808            });
809        };
810
811        set_language_to_session_storage
812            .iter()
813            .map(|param| match param.expr {
814                Some(ref expr) => {
815                    let q = quote! {
816                        if #expr {
817                            #effect_quote
818                        }
819                    };
820                    match param.exprpath {
821                        Some(ref path) => quote!(#path{#q}),
822                        None => q,
823                    }
824                }
825                None => quote!(),
826            })
827            .collect()
828    };
829
830    #[cfg(feature = "ssr")]
831    let sync_language_with_session_storage_quote = quote!();
832
833    let initial_language_from_url_param_quote: proc_macro2::TokenStream = {
834        #[cfg(all(feature = "hydrate", not(feature = "ssr")))]
835        let hydrate_rerender_quote = quote! {
836            ::leptos::prelude::Effect::new(move |prev: Option<()>| {
837                if prev.is_none() {
838                    #set_language_quote;
839                }
840            });
841        };
842
843        #[cfg(all(not(feature = "hydrate"), not(feature = "ssr")))]
844        let hydrate_rerender_quote = quote! {};
845
846        #[cfg(not(feature = "ssr"))]
847        let set_to_local_storage_quote: proc_macro2::TokenStream = {
848            let effect_quote = quote! {
849                ::leptos_fluent::local_storage::set(
850                    #local_storage_key_quote,
851                    &l.id.to_string()
852                );
853            };
854
855            initial_language_from_url_param_to_local_storage
856                .iter()
857                .map(|param| match param.expr {
858                    Some(ref expr) => {
859                        let q = quote!(if #expr {#effect_quote});
860                        match param.exprpath {
861                            Some(ref path) => quote!(#path{#q}),
862                            None => q,
863                        }
864                    }
865                    None => quote!(),
866                })
867                .collect()
868        };
869
870        #[cfg(not(feature = "ssr"))]
871        let set_to_session_storage_quote: proc_macro2::TokenStream = {
872            let effect_quote = quote! {
873                ::leptos_fluent::session_storage::set(
874                    #session_storage_key_quote,
875                    &l.id.to_string()
876                );
877            };
878
879            initial_language_from_url_param_to_session_storage
880                .iter()
881                .map(|param| match param.expr {
882                    Some(ref expr) => {
883                        let q = quote!(if #expr {#effect_quote});
884                        match param.exprpath {
885                            Some(ref path) => quote!(#path{#q}),
886                            None => q,
887                        }
888                    }
889                    None => quote!(),
890                })
891                .collect()
892        };
893
894        #[cfg(not(feature = "ssr"))]
895        let set_to_cookie_quote: proc_macro2::TokenStream = {
896            let effect_quote = quote! {
897                ::leptos_fluent::cookie::set(
898                    #cookie_name_quote,
899                    &l.id.to_string(),
900                    &#cookie_attrs_quote
901                );
902            };
903
904            initial_language_from_url_param_to_cookie
905                .iter()
906                .map(|param| match param.expr {
907                    Some(ref expr) => {
908                        let q = quote!(if #expr {#effect_quote});
909                        match param.exprpath {
910                            Some(ref path) => quote!(#path{#q}),
911                            None => q,
912                        }
913                    }
914                    None => quote!(),
915                })
916                .collect()
917        };
918
919        #[cfg(feature = "ssr")]
920        {
921            _ = initial_language_from_url_param_to_local_storage;
922            _ = initial_language_from_url_param_to_session_storage;
923            _ = initial_language_from_url_param_to_cookie;
924        }
925
926        let set_to_server_function_quote: proc_macro2::TokenStream =
927            initial_language_from_url_param_to_server_function
928                .iter()
929                .map(|param| match param.expr {
930                    Some(ref ident) => {
931                        let quote = quote! {
932                            ::leptos::task::spawn(async move {
933                                _ = #ident(l.id.to_string()).await;
934                            });
935                        };
936                        match param.exprpath {
937                            Some(ref path) => quote!(#path{#quote}),
938                            None => quote,
939                        }
940                    }
941                    None => quote!(),
942                })
943                .collect();
944
945        #[cfg(not(feature = "ssr"))]
946        let parse_language_quote = quote! {
947            if let Some(l) = ::leptos_fluent::url::param::get(#url_param_quote) {
948                lang = ::leptos_fluent::l(&l, &LANGUAGES);
949                if let Some(l) = lang {
950                    #hydrate_rerender_quote
951                    #set_to_local_storage_quote
952                    #set_to_session_storage_quote
953                    #set_to_cookie_quote
954                    #set_to_server_function_quote
955                }
956            }
957        };
958
959        #[cfg(all(feature = "ssr", any(feature = "actix", feature = "axum")))]
960        let lang_parser_quote = quote! {
961            let mut maybe_lang = None;
962            for (key, value) in uri_query.split('&').map(|pair| {
963                let mut split = pair.splitn(2, '=');
964                (split.next().unwrap_or(""), split.next().unwrap_or(""))
965            }) {
966                if key == #url_param_quote {
967                    maybe_lang = Some(value);
968                    break;
969                }
970            }
971
972            if let Some(l) = maybe_lang {
973                lang = ::leptos_fluent::l(&l, &LANGUAGES);
974                if let Some(l) = lang {
975                    #set_to_server_function_quote
976                }
977            }
978        };
979
980        #[cfg(all(feature = "ssr", feature = "actix"))]
981        let parse_language_quote = quote! {
982            if let Some(req) = ::leptos::prelude::use_context::<::leptos_actix::Request>() {
983                let uri_query = req.uri().query().unwrap_or("");
984                #lang_parser_quote
985            }
986        };
987
988        #[cfg(all(feature = "ssr", feature = "axum"))]
989        let parse_language_quote = quote! {
990            if let Some(req) = ::leptos::prelude::use_context::<::axum::http::request::Parts>() {
991                let uri_query = req.uri.query().unwrap_or("");
992                #lang_parser_quote
993            }
994        };
995
996        // Other SSR framework or the user is not using any
997        #[cfg(all(
998            feature = "ssr",
999            not(feature = "actix"),
1000            not(feature = "axum"),
1001        ))]
1002        let parse_language_quote = quote! {};
1003
1004        initial_language_from_url_param
1005            .iter()
1006            .map(|param| match param.expr {
1007                Some(ref expr) => match parse_language_quote.is_empty() {
1008                    true => quote!(),
1009                    false => {
1010                        let q = quote! {
1011                            if #expr {
1012                                #parse_language_quote
1013                            }
1014                        };
1015                        match param.exprpath {
1016                            Some(ref path) => quote!(#path{#q}),
1017                            None => q,
1018                        }
1019                    }
1020                },
1021                None => quote!(),
1022            })
1023            .collect()
1024    };
1025
1026    #[cfg(not(feature = "ssr"))]
1027    let initial_language_from_local_storage_quote: proc_macro2::TokenStream = {
1028        let set_cookie_quote = quote! {
1029            ::leptos_fluent::cookie::set(
1030                #cookie_name_quote,
1031                &l.id.to_string(),
1032                &#cookie_attrs_quote
1033            );
1034        };
1035
1036        let initial_language_from_local_storage_to_cookie_quote: proc_macro2::TokenStream =
1037            initial_language_from_local_storage_to_cookie.iter().map(|param| {
1038                match param.expr {
1039                    Some(ref expr) => {
1040                        let q = quote! {
1041                            if #expr {
1042                                #set_cookie_quote
1043                            }
1044                        };
1045                        match param.exprpath {
1046                            Some(ref path) => quote!(#path{#q}),
1047                            None => q,
1048                        }
1049                    },
1050                    None => quote!(),
1051                }
1052            }).collect();
1053
1054        let set_session_storage_quote = quote! {
1055            ::leptos_fluent::session_storage::set(
1056                #session_storage_key_quote,
1057                &l.id.to_string()
1058            );
1059        };
1060
1061        let initial_language_from_local_storage_to_session_storage_quote: proc_macro2::TokenStream =
1062            initial_language_from_local_storage_to_session_storage.iter().map(|param| {
1063                match param.expr {
1064                    Some(ref expr) => {
1065                        let q = quote! {
1066                            if #expr {
1067                                #set_session_storage_quote
1068                            }
1069                        };
1070                        match param.exprpath {
1071                            Some(ref path) => quote!(#path{#q}),
1072                            None => q,
1073                        }
1074                    },
1075                    None => quote!(),
1076                }
1077            }).collect();
1078
1079        let initial_language_from_local_storage_to_server_function_quote: proc_macro2::TokenStream =
1080            initial_language_from_local_storage_to_server_function.iter().map(|param| {
1081                match param.expr {
1082                    Some(ref ident) => {
1083                        let quote = quote! {
1084                            ::leptos::task::spawn(async move {
1085                                _ = #ident(l.id.to_string()).await;
1086                            });
1087                        };
1088                        match param.exprpath {
1089                            Some(ref path) => quote!(#path{#quote}),
1090                            None => quote,
1091                        }
1092                    },
1093                    None => quote!(),
1094                }
1095            }).collect();
1096
1097        let local_storage_get_quote = quote! {
1098            if let Some(l) = ::leptos_fluent::local_storage::get(#local_storage_key_quote)
1099            {
1100                lang = ::leptos_fluent::l(&l, &LANGUAGES);
1101                if let Some(l) = lang {
1102                    #initial_language_from_local_storage_to_cookie_quote
1103                    #initial_language_from_local_storage_to_session_storage_quote
1104                    #initial_language_from_local_storage_to_server_function_quote
1105                }
1106            }
1107        };
1108
1109        initial_language_from_local_storage
1110            .iter()
1111            .map(|param| match param.expr {
1112                Some(ref expr) => {
1113                    let q = quote! {
1114                        if #expr && lang.is_none() {
1115                            #local_storage_get_quote
1116                        }
1117                    };
1118                    match param.exprpath {
1119                        Some(ref path) => quote!(#path{#q}),
1120                        None => q,
1121                    }
1122                }
1123                None => quote!(),
1124            })
1125            .collect()
1126    };
1127
1128    #[cfg(not(feature = "ssr"))]
1129    let initial_language_from_session_storage_quote: proc_macro2::TokenStream = {
1130        let set_cookie_quote = quote! {
1131            ::leptos_fluent::cookie::set(
1132                #cookie_name_quote,
1133                &l.id.to_string(),
1134                &#cookie_attrs_quote
1135            );
1136        };
1137
1138        let initial_language_from_session_storage_to_cookie_quote: proc_macro2::TokenStream =
1139            initial_language_from_session_storage_to_cookie.iter().map(|param| {
1140                match param.expr {
1141                    Some(ref expr) => {
1142                        let q = quote! {
1143                            if #expr {
1144                                #set_cookie_quote
1145                            }
1146                        };
1147                        match param.exprpath {
1148                            Some(ref path) => quote!(#path{#q}),
1149                            None => q,
1150                        }
1151                    },
1152                    None => quote!(),
1153                }
1154            }).collect();
1155
1156        let set_local_storage_quote = quote! {
1157            ::leptos_fluent::local_storage::set(
1158                #local_storage_key_quote,
1159                &l.id.to_string()
1160            );
1161        };
1162
1163        let initial_language_from_session_storage_to_local_storage_quote: proc_macro2::TokenStream =
1164            initial_language_from_session_storage_to_local_storage.iter().map(|param| {
1165                match param.expr {
1166                    Some(ref expr) => {
1167                        let q = quote! {
1168                            if #expr {
1169                                #set_local_storage_quote
1170                            }
1171                        };
1172                        match param.exprpath {
1173                            Some(ref path) => quote!(#path{#q}),
1174                            None => q,
1175                        }
1176                    },
1177                    None => quote!(),
1178                }
1179            }).collect();
1180
1181        let initial_language_from_session_storage_to_server_function_quote: proc_macro2::TokenStream =
1182            initial_language_from_session_storage_to_server_function.iter().map(|param| {
1183                match param.expr {
1184                    Some(ref ident) => {
1185                        let quote = quote! {
1186                            ::leptos::task::spawn(async move {
1187                                _ = #ident(l.id.to_string()).await;
1188                            });
1189                        };
1190                        match param.exprpath {
1191                            Some(ref path) => quote!(#path{#quote}),
1192                            None => quote,
1193                        }
1194                    },
1195                    None => quote!(),
1196                }
1197            }).collect();
1198
1199        let session_storage_get_quote = quote! {
1200            if let Some(l) = ::leptos_fluent::session_storage::get(#session_storage_key_quote)
1201            {
1202                lang = ::leptos_fluent::l(&l, &LANGUAGES);
1203                if let Some(l) = lang {
1204                    #initial_language_from_session_storage_to_cookie_quote
1205                    #initial_language_from_session_storage_to_local_storage_quote
1206                    #initial_language_from_session_storage_to_server_function_quote
1207                }
1208            }
1209        };
1210
1211        initial_language_from_session_storage
1212            .iter()
1213            .map(|param| match param.expr {
1214                Some(ref expr) => {
1215                    let q = quote! {
1216                        if #expr && lang.is_none() {
1217                            #session_storage_get_quote
1218                        }
1219                    };
1220                    match param.exprpath {
1221                        Some(ref path) => quote!(#path{#q}),
1222                        None => q,
1223                    }
1224                }
1225                None => quote!(),
1226            })
1227            .collect()
1228    };
1229
1230    #[cfg(feature = "ssr")]
1231    {
1232        _ = initial_language_from_local_storage;
1233        _ = initial_language_from_local_storage_to_cookie;
1234        _ = initial_language_from_local_storage_to_session_storage;
1235        _ = initial_language_from_local_storage_to_server_function;
1236        _ = initial_language_from_session_storage;
1237        _ = initial_language_from_session_storage_to_cookie;
1238        _ = initial_language_from_session_storage_to_local_storage;
1239        _ = initial_language_from_session_storage_to_server_function;
1240    }
1241
1242    let sync_language_with_url_param_quote: proc_macro2::TokenStream = {
1243        let effect_quote = quote! {
1244            ::leptos::prelude::Effect::new(move |_| {
1245                ::leptos_fluent::url::param::set(
1246                    #url_param_quote,
1247                    &#get_language_quote.id.to_string()
1248                );
1249            });
1250        };
1251
1252        set_language_to_url_param
1253            .iter()
1254            .map(|param| match param.expr {
1255                Some(ref expr) => {
1256                    let q = quote! {
1257                        if #expr {
1258                            #effect_quote
1259                        }
1260                    };
1261                    match param.exprpath {
1262                        Some(ref path) => quote!(#path{#q}),
1263                        None => q,
1264                    }
1265                }
1266                None => quote!(),
1267            })
1268            .collect()
1269    };
1270
1271    #[cfg(not(feature = "ssr"))]
1272    let initial_language_from_navigator_quote: proc_macro2::TokenStream = {
1273        let initial_language_from_navigator_to_local_storage_quote: proc_macro2::TokenStream = {
1274            let effect_quote = quote! {
1275                ::leptos_fluent::local_storage::set(
1276                    #local_storage_key_quote,
1277                    &l.id.to_string()
1278                );
1279            };
1280
1281            initial_language_from_navigator_to_local_storage.iter().map(|param| {
1282                match param.expr {
1283                    Some(ref expr) => {
1284                        let q = quote!(if #expr {#effect_quote});
1285                        match param.exprpath {
1286                            Some(ref path) => quote!(#path{#q}),
1287                            None => q,
1288                        }
1289                    },
1290                    None => quote!(),
1291                }
1292            }).collect()
1293        };
1294
1295        let initial_language_from_navigator_to_session_storage_quote: proc_macro2::TokenStream = {
1296            let effect_quote = quote! {
1297                ::leptos_fluent::session_storage::set(
1298                    #session_storage_key_quote,
1299                    &l.id.to_string()
1300                );
1301            };
1302
1303            initial_language_from_navigator_to_session_storage.iter().map(|param| {
1304                match param.expr {
1305                    Some(ref expr) => {
1306                        let q = quote!(if #expr {#effect_quote});
1307                        match param.exprpath {
1308                            Some(ref path) => quote!(#path{#q}),
1309                            None => q,
1310                        }
1311                    },
1312                    None => quote!(),
1313                }
1314            }).collect()
1315        };
1316
1317        let initial_language_from_navigator_to_cookie_quote: proc_macro2::TokenStream = {
1318            let effect_quote = quote! {
1319                ::leptos_fluent::cookie::set(
1320                    #cookie_name_quote,
1321                    &l.id.to_string(),
1322                    &#cookie_attrs_quote
1323                );
1324            };
1325
1326            initial_language_from_navigator_to_cookie.iter().map(|param| {
1327                match param.expr {
1328                    Some(ref expr) => {
1329                        let q = quote!(if #expr {#effect_quote});
1330                        match param.exprpath {
1331                            Some(ref path) => quote!(#path{#q}),
1332                            None => q,
1333                        }
1334                    },
1335                    None => quote!(),
1336                }
1337            }).collect()
1338        };
1339
1340        let initial_language_from_navigator_to_server_function_quote: proc_macro2::TokenStream =
1341            initial_language_from_navigator_to_server_function.iter().map(|param| {
1342                match param.expr {
1343                    Some(ref ident) => {
1344                        let quote = quote! {
1345                            ::leptos::task::spawn(async move {
1346                                _ = #ident(l.id.to_string()).await;
1347                            });
1348                        };
1349                        match param.exprpath {
1350                            Some(ref path) => quote!(#path{#quote}),
1351                            None => quote,
1352                        }
1353                    },
1354                    None => quote!(),
1355                }
1356            }).collect();
1357
1358        let window_navigator_languages_quote = quote! {
1359            let languages = ::leptos::prelude::window().navigator().languages().to_vec();
1360            for raw_language in languages {
1361                let language = raw_language.as_string();
1362                if language.is_none() {
1363                    continue;
1364                }
1365                lang = ::leptos_fluent::l(&language.unwrap(), &LANGUAGES);
1366                if let Some(l) = lang {
1367                    #initial_language_from_navigator_to_local_storage_quote
1368                    #initial_language_from_navigator_to_session_storage_quote
1369                    #initial_language_from_navigator_to_cookie_quote
1370                    #initial_language_from_navigator_to_server_function_quote
1371                    break;
1372                }
1373            }
1374        };
1375
1376        initial_language_from_navigator
1377            .iter()
1378            .map(|param| match param.expr {
1379                Some(ref expr) => {
1380                    let q = quote! {
1381                        if #expr && lang.is_none() {
1382                            #window_navigator_languages_quote
1383                        }
1384                    };
1385                    match param.exprpath {
1386                        Some(ref path) => quote!(#path{#q}),
1387                        None => q,
1388                    }
1389                }
1390                None => quote!(),
1391            })
1392            .collect()
1393    };
1394
1395    #[cfg(feature = "ssr")]
1396    {
1397        _ = initial_language_from_navigator;
1398        _ = initial_language_from_navigator_to_local_storage;
1399        _ = initial_language_from_navigator_to_cookie;
1400        _ = initial_language_from_navigator_to_server_function;
1401    }
1402
1403    let set_language_from_navigator_quote: proc_macro2::TokenStream = {
1404        #[cfg(not(feature = "ssr"))]
1405        {
1406            #[cfg(feature = "debug")]
1407            let log_language_quote = quote! {
1408                ::leptos::logging::log!("[leptos-fluent/debug] Language changed to {:?}", &l);
1409            };
1410
1411            #[cfg(not(feature = "debug"))]
1412            let log_language_quote = quote!();
1413
1414            let effect_quote = quote! {
1415                use ::leptos_fluent::__reexports::wasm_bindgen::JsCast;
1416                let closure: Box<dyn FnMut(_)> = Box::new(
1417                    move |_: ::leptos_fluent::__reexports::web_sys::Window| {
1418                        let languages = ::leptos::prelude::window().navigator().languages().to_vec();
1419                        for raw_language in languages {
1420                            let language = raw_language.as_string();
1421                            if language.is_none() {
1422                                continue;
1423                            }
1424                            let l = ::leptos_fluent::l(&language.unwrap(), &LANGUAGES);
1425                            #log_language_quote
1426                            if let Some(l) = l {
1427                                #set_language_quote;
1428                                break;
1429                            }
1430                        }
1431                    }
1432                );
1433                let cb = ::leptos_fluent::__reexports::wasm_bindgen::closure::Closure::wrap(
1434                    closure
1435                );
1436                ::leptos::prelude::window().add_event_listener_with_callback(
1437                    "languagechange",
1438                    cb.as_ref().unchecked_ref()
1439                ).expect("Failed to add event listener for window languagechange");
1440                cb.forget();
1441            };
1442
1443            set_language_from_navigator
1444                .iter()
1445                .map(|param| match param.expr {
1446                    Some(ref expr) => {
1447                        let q = quote!(if #expr {#effect_quote});
1448                        match param.exprpath {
1449                            Some(ref path) => quote!(#path{#q}),
1450                            None => q,
1451                        }
1452                    }
1453                    None => quote!(),
1454                })
1455                .collect()
1456        }
1457
1458        #[cfg(feature = "ssr")]
1459        {
1460            _ = set_language_from_navigator;
1461            quote!()
1462        }
1463    };
1464
1465    // Accept-Language header
1466    //   Actix
1467    #[cfg(all(feature = "actix", feature = "ssr"))]
1468    let initial_language_from_accept_language_header_quote: proc_macro2::TokenStream = {
1469        let effect_quote = quote! {
1470            if let Some(req) = ::leptos::prelude::use_context::<::leptos_actix::Request>() {
1471                let maybe_header = req
1472                    .headers()
1473                    .get(::actix_web::http::header::ACCEPT_LANGUAGE)
1474                    .and_then(|header| header.to_str().ok());
1475
1476                if let Some(header) = maybe_header {
1477                    let langs = ::leptos_fluent::http_header::parse(header);
1478                    for l in langs {
1479                        if let Some(l) = ::leptos_fluent::l(&l, &LANGUAGES) {
1480                            lang = Some(l);
1481                            break;
1482                        }
1483                    }
1484                }
1485            }
1486        };
1487
1488        initial_language_from_accept_language_header.iter().map(|param| {
1489            match param.expr {
1490                Some(ref expr) => {
1491                    let q = quote! {
1492                        if #expr && lang.is_none() {
1493                            #effect_quote
1494                        }
1495                    };
1496                    match param.exprpath {
1497                        Some(ref path) => quote!(#path{#q}),
1498                        None => q,
1499                    }
1500                },
1501                None => quote!(),
1502            }
1503        }).collect()
1504    };
1505
1506    //   Axum
1507    #[cfg(all(feature = "axum", feature = "ssr"))]
1508    let initial_language_from_accept_language_header_quote: proc_macro2::TokenStream = {
1509        let effect_quote = quote! {
1510            if let Some(req) = ::leptos::prelude::use_context::<::axum::http::request::Parts>() {
1511                let maybe_header = req
1512                    .headers
1513                    .get(::axum::http::header::ACCEPT_LANGUAGE)
1514                    .and_then(|header| header.to_str().ok());
1515
1516                if let Some(header) = maybe_header {
1517                    let langs = ::leptos_fluent::http_header::parse(header);
1518                    for l in langs {
1519                        if let Some(l) = ::leptos_fluent::l(&l, &LANGUAGES) {
1520                            lang = Some(l);
1521                            break;
1522                        }
1523                    }
1524                }
1525            }
1526        };
1527
1528        initial_language_from_accept_language_header.iter().map(|param| {
1529            match param.expr {
1530                Some(ref expr) => {
1531                    let q = quote! {
1532                        if #expr && lang.is_none() {
1533                            #effect_quote
1534                        }
1535                    };
1536                    match param.exprpath {
1537                        Some(ref path) => quote!(#path{#q}),
1538                        None => q,
1539                    }
1540                },
1541                None => quote!(),
1542            }
1543        }).collect()
1544    };
1545
1546    //   Other SSR framework or the user is not using any
1547    #[cfg(all(not(feature = "actix"), not(feature = "axum"), feature = "ssr"))]
1548    let initial_language_from_accept_language_header_quote = quote! {};
1549
1550    #[cfg(not(feature = "ssr"))]
1551    {
1552        _ = initial_language_from_accept_language_header;
1553    }
1554
1555    // Cookie
1556    let initial_language_from_cookie_to_server_function_quote: proc_macro2::TokenStream =
1557        initial_language_from_cookie_to_server_function.iter().map(|param| {
1558            match param.expr {
1559                Some(ref ident) => {
1560                    let quote = quote! {
1561                        ::leptos::task::spawn(async move {
1562                            _ = #ident(l.id.to_string()).await;
1563                        });
1564                    };
1565                    match param.exprpath {
1566                        Some(ref path) => quote!(#path{#quote}),
1567                        None => quote,
1568                    }
1569                },
1570                None => quote!(),
1571            }
1572        }).collect();
1573
1574    #[cfg(not(feature = "ssr"))]
1575    let initial_language_from_cookie_quote: proc_macro2::TokenStream = {
1576        let initial_language_from_cookie_to_local_storage_quote: proc_macro2::TokenStream = {
1577            let effect_quote = quote! {
1578                ::leptos_fluent::local_storage::set(
1579                    #local_storage_key_quote,
1580                    &l.id.to_string()
1581                );
1582            };
1583
1584            initial_language_from_cookie_to_local_storage.iter().map(|param| {
1585                match param.expr {
1586                    Some(ref expr) => {
1587                        let q = quote!(if #expr {#effect_quote});
1588                        match param.exprpath {
1589                            Some(ref path) => quote!(#path{#q}),
1590                            None => q,
1591                        }
1592                    },
1593                    None => quote!(),
1594                }
1595            }).collect()
1596        };
1597
1598        let initial_language_from_cookie_to_session_storage_quote: proc_macro2::TokenStream = {
1599            let effect_quote = quote! {
1600                ::leptos_fluent::session_storage::set(
1601                    #session_storage_key_quote,
1602                    &l.id.to_string()
1603                );
1604            };
1605
1606            initial_language_from_cookie_to_session_storage.iter().map(|param| {
1607                match param.expr {
1608                    Some(ref expr) => {
1609                        let q = quote!(if #expr {#effect_quote});
1610                        match param.exprpath {
1611                            Some(ref path) => quote!(#path{#q}),
1612                            None => q,
1613                        }
1614                    },
1615                    None => quote!(),
1616                }
1617            }).collect()
1618        };
1619
1620        let parse_client_cookie_quote = quote! {
1621            if let Some(cookie) = ::leptos_fluent::cookie::get(#cookie_name_quote) {
1622                if let Some(l) = ::leptos_fluent::l(&cookie, &LANGUAGES) {
1623                    lang = Some(l);
1624                    #initial_language_from_cookie_to_local_storage_quote
1625                    #initial_language_from_cookie_to_session_storage_quote
1626                    #initial_language_from_cookie_to_server_function_quote
1627                }
1628            }
1629        };
1630
1631        initial_language_from_cookie
1632            .iter()
1633            .map(|param| match param.expr {
1634                Some(ref expr) => {
1635                    let q = quote! {
1636                        if #expr && lang.is_none() {
1637                            #parse_client_cookie_quote
1638                        }
1639                    };
1640                    match param.exprpath {
1641                        Some(ref path) => quote!(#path{#q}),
1642                        None => q,
1643                    }
1644                }
1645                None => quote!(),
1646            })
1647            .collect()
1648    };
1649
1650    #[cfg(not(feature = "ssr"))]
1651    let sync_language_with_cookie_quote: proc_macro2::TokenStream = {
1652        let effect_quote = quote! {
1653            ::leptos::prelude::Effect::new(move |_| {
1654                ::leptos_fluent::cookie::set(
1655                    #cookie_name_quote,
1656                    &#get_language_quote.id.to_string(),
1657                    &#cookie_attrs_quote
1658                );
1659            });
1660        };
1661
1662        set_language_to_cookie
1663            .iter()
1664            .map(|param| match param.expr {
1665                Some(ref expr) => {
1666                    let q = quote! {
1667                        if #expr {
1668                            #effect_quote
1669                        }
1670                    };
1671                    match param.exprpath {
1672                        Some(ref path) => quote!(#path{#q}),
1673                        None => q,
1674                    }
1675                }
1676                None => quote!(),
1677            })
1678            .collect()
1679    };
1680
1681    #[cfg(feature = "ssr")]
1682    {
1683        _ = initial_language_from_cookie;
1684        _ = initial_language_from_cookie_to_local_storage;
1685        _ = initial_language_from_cookie_to_session_storage;
1686        _ = cookie_attrs;
1687        _ = set_language_to_cookie;
1688    }
1689
1690    //   Actix
1691    #[cfg(all(feature = "ssr", feature = "actix"))]
1692    let initial_language_from_cookie_quote: proc_macro2::TokenStream = {
1693        let effect_quote = quote! {
1694            if let Some(req) = ::leptos::prelude::use_context::<::leptos_actix::Request>() {
1695                let maybe_cookie = req
1696                    .cookie(#cookie_name_quote)
1697                    .and_then(|cookie| Some(cookie.value().to_string()));
1698
1699                if let Some(cookie) = maybe_cookie {
1700                    if let Some(l) = ::leptos_fluent::l(&cookie, &LANGUAGES) {
1701                        lang = Some(l);
1702                        #initial_language_from_cookie_to_server_function_quote
1703                    }
1704                }
1705            }
1706        };
1707
1708        initial_language_from_cookie
1709            .iter()
1710            .map(|param| match param.expr {
1711                Some(ref expr) => {
1712                    let q = quote! {
1713                        if #expr && lang.is_none() {
1714                            #effect_quote
1715                        }
1716                    };
1717                    match param.exprpath {
1718                        Some(ref path) => quote!(#path{#q}),
1719                        None => q,
1720                    }
1721                }
1722                None => quote!(),
1723            })
1724            .collect()
1725    };
1726
1727    //     TODO: Set in Set-Cookie header?
1728    #[cfg(all(feature = "ssr", feature = "actix"))]
1729    let sync_language_with_cookie_quote = quote! {};
1730
1731    //   Axum
1732    #[cfg(all(feature = "ssr", feature = "axum"))]
1733    let initial_language_from_cookie_quote: proc_macro2::TokenStream = {
1734        let effect_quote = quote! {
1735            if let Some(req) = ::leptos::prelude::use_context::<::axum::http::request::Parts>() {
1736                let maybe_cookie = req
1737                    .headers
1738                    .get(::axum::http::header::COOKIE)
1739                    .and_then(|header| header.to_str().ok())
1740                    .and_then(|cookie| {
1741                        let cookie = cookie.split(';').find(|c| c.trim_start().starts_with(#cookie_name_quote));
1742                        cookie.map(|c| c.split('=').nth(1).unwrap().trim_start().to_string())
1743                    });
1744
1745                if let Some(cookie) = maybe_cookie {
1746                    if let Some(l) = ::leptos_fluent::l(&cookie, &LANGUAGES) {
1747                        lang = Some(l);
1748                        #initial_language_from_cookie_to_server_function_quote
1749                    }
1750                }
1751            }
1752        };
1753
1754        initial_language_from_cookie
1755            .iter()
1756            .map(|param| match param.expr {
1757                Some(ref expr) => {
1758                    let q = quote! {
1759                        if #expr && lang.is_none() {
1760                            #effect_quote
1761                        }
1762                    };
1763                    match param.exprpath {
1764                        Some(ref path) => quote!(#path{#q}),
1765                        None => q,
1766                    }
1767                }
1768                None => quote!(),
1769            })
1770            .collect()
1771    };
1772
1773    //     TODO: Set in Set-Cookie header?
1774    #[cfg(all(feature = "ssr", feature = "axum"))]
1775    let sync_language_with_cookie_quote = quote! {};
1776
1777    //   Other SSR frameworks or the user is not using any
1778    #[cfg(all(not(feature = "actix"), not(feature = "axum"), feature = "ssr"))]
1779    let initial_language_from_cookie_quote = quote! {};
1780    #[cfg(all(
1781        not(feature = "actix"),
1782        not(feature = "axum"),
1783        feature = "ssr"
1784    ))]
1785    {
1786        _ = initial_language_from_cookie_to_server_function;
1787    };
1788
1789    #[cfg(all(not(feature = "actix"), not(feature = "axum"), feature = "ssr"))]
1790    let sync_language_with_cookie_quote = quote! {};
1791
1792    let initial_language_quote = {
1793        #[cfg(not(feature = "ssr"))]
1794        quote! {
1795            #initial_language_from_server_function_quote
1796            #initial_language_from_data_file_quote
1797            #initial_language_from_system_quote
1798            #initial_language_from_url_param_quote
1799            #initial_language_from_url_path_quote
1800            #initial_language_from_cookie_quote
1801            #initial_language_from_local_storage_quote
1802            #initial_language_from_session_storage_quote
1803            #initial_language_from_navigator_quote
1804        }
1805
1806        #[cfg(feature = "ssr")]
1807        quote! {
1808            #initial_language_from_server_function_quote
1809            #initial_language_from_url_param_quote
1810            #initial_language_from_url_path_quote
1811            #initial_language_from_cookie_quote
1812            #initial_language_from_accept_language_header_quote
1813        }
1814    };
1815
1816    let leptos_fluent_provide_meta_context_quote: proc_macro2::TokenStream = {
1817        let maybe_litstr_param =
1818            |lit: &Option<String>| -> proc_macro2::TokenStream {
1819                match lit {
1820                    Some(ref lit) => quote! { #lit },
1821                    None => quote! { None },
1822                }
1823            };
1824
1825        let maybe_some_litstr_param =
1826            |lit: &Option<String>| -> proc_macro2::TokenStream {
1827                match lit {
1828                    Some(ref lit) => quote! { Some(#lit) },
1829                    None => quote! { None },
1830                }
1831            };
1832
1833        let litstr_or_default = |lit: &Option<syn::LitStr>,
1834                                 expr: &Option<TokenStreamStr>,
1835                                 default_: &'static str|
1836         -> proc_macro2::TokenStream {
1837            match lit {
1838                Some(ref lit) => quote! { #lit },
1839                None => match expr {
1840                    Some(ref expr) => expr.into_token_stream(),
1841                    None => quote! { #default_ },
1842                },
1843            }
1844        };
1845
1846        let lit_bool_expr_or_idents =
1847            |params: &[LitBoolExprOrIdent]| -> proc_macro2::TokenStream {
1848                if params.is_empty() {
1849                    return quote! { false };
1850                }
1851
1852                params
1853                    .iter()
1854                    .map(|param| {
1855                        let quote = match &param.expr {
1856                            Some(ident) => match ident
1857                                .to_token_stream()
1858                                .to_string()
1859                                .as_str()
1860                            {
1861                                "false" => quote! { false },
1862                                _ => quote! { true },
1863                            },
1864                            None => quote! { false },
1865                        };
1866
1867                        match param.exprpath {
1868                            Some(ref path) => quote!(#path{#quote}),
1869                            None => quote,
1870                        }
1871                    })
1872                    .collect()
1873            };
1874
1875        let maybe_some_litbool_or_litstr_param =
1876            |param: &Option<LitBoolOrStr>| {
1877                match param {
1878                    Some(LitBoolOrStr::Bool(lit_bool)) => quote!(#lit_bool),
1879                    Some(LitBoolOrStr::Str(_)) => quote!(true), // TODO: add str
1880                    None => quote!(None),
1881                }
1882            };
1883
1884        provide_meta_context.iter().map(|param| {
1885            match param.lit.unwrap_or(false) {
1886                true => {
1887                    let core_locales_quote = maybe_litstr_param(&core_locales_path);
1888                    let default_language_quote = match &default_language {
1889                        Some(ref lang, ..) => {
1890                            let code = &lang.0;
1891                            quote!(Some(#code))
1892                        },
1893                        None => quote!(None)
1894                    };
1895                    let languages_quote =
1896                        maybe_some_litstr_param(&raw_languages_path);
1897                    let translations_quote = if translations.is_some() {
1898                        quote!(true)
1899                    } else {
1900                        quote!(false)
1901                    };
1902                    let check_translations_quote =
1903                        maybe_some_litbool_or_litstr_param(&check_translations);
1904                    let fill_translations_quote =
1905                        maybe_some_litstr_param(&fill_translations);
1906                    let sync_html_tag_lang_quote =
1907                        lit_bool_expr_or_idents(&sync_html_tag_lang);
1908                    let sync_html_tag_dir_quote =
1909                        lit_bool_expr_or_idents(&sync_html_tag_dir);
1910                    let url_param_quote =
1911                        litstr_or_default(&url_param.lit, &url_param.expr, "lang");
1912                    let initial_language_from_url_param_quote =
1913                        lit_bool_expr_or_idents(&initial_language_from_url_param);
1914                    let initial_language_from_url_param_to_local_storage =
1915                        lit_bool_expr_or_idents(
1916                            &initial_language_from_url_param_to_local_storage,
1917                        );
1918                    let initial_language_from_url_param_to_session_storage =
1919                        lit_bool_expr_or_idents(
1920                            &initial_language_from_url_param_to_session_storage,
1921                        );
1922                    let initial_language_from_url_param_to_cookie_quote =
1923                        lit_bool_expr_or_idents(&initial_language_from_url_param_to_cookie);
1924                    let initial_language_from_url_param_to_server_function_quote =
1925                        lit_bool_expr_or_idents(
1926                            &initial_language_from_url_param_to_server_function,
1927                        );
1928                    let set_language_to_url_param_quote =
1929                        lit_bool_expr_or_idents(&set_language_to_url_param);
1930                    let local_storage_key_quote = litstr_or_default(
1931                        &local_storage_key.lit,
1932                        &local_storage_key.expr,
1933                        "lang",
1934                    );
1935                    let initial_language_from_local_storage_quote =
1936                        lit_bool_expr_or_idents(&initial_language_from_local_storage);
1937                    let initial_language_from_local_storage_to_cookie_quote =
1938                        lit_bool_expr_or_idents(
1939                            &initial_language_from_local_storage_to_cookie,
1940                        );
1941                    let initial_language_from_local_storage_to_session_storage_quote =
1942                        lit_bool_expr_or_idents(
1943                            &initial_language_from_local_storage_to_session_storage,
1944                        );
1945                    let initial_language_from_local_storage_to_server_function_quote =
1946                        lit_bool_expr_or_idents(
1947                            &initial_language_from_local_storage_to_server_function,
1948                        );
1949                    let set_language_to_local_storage_quote =
1950                        lit_bool_expr_or_idents(&set_language_to_local_storage);
1951                    let session_storage_key_quote = litstr_or_default(
1952                        &session_storage_key.lit,
1953                        &session_storage_key.expr,
1954                        "lang",
1955                    );
1956                    let initial_language_from_session_storage_quote =
1957                        lit_bool_expr_or_idents(&initial_language_from_session_storage);
1958                    let initial_language_from_session_storage_to_cookie_quote =
1959                        lit_bool_expr_or_idents(
1960                            &initial_language_from_session_storage_to_cookie,
1961                        );
1962                    let initial_language_from_session_storage_to_local_storage_quote =
1963                        lit_bool_expr_or_idents(
1964                            &initial_language_from_session_storage_to_local_storage,
1965                        );
1966                    let initial_language_from_session_storage_to_server_function_quote =
1967                        lit_bool_expr_or_idents(
1968                            &initial_language_from_session_storage_to_server_function,
1969                        );
1970                    let set_language_to_session_storage_quote =
1971                        lit_bool_expr_or_idents(&set_language_to_session_storage);
1972                    let initial_language_from_navigator_quote =
1973                        lit_bool_expr_or_idents(&initial_language_from_navigator);
1974                    let initial_language_from_navigator_to_local_storage_quote =
1975                        lit_bool_expr_or_idents(
1976                            &initial_language_from_navigator_to_local_storage,
1977                        );
1978                    let initial_language_from_navigator_to_session_storage_quote =
1979                        lit_bool_expr_or_idents(
1980                            &initial_language_from_navigator_to_session_storage,
1981                        );
1982                    let initial_language_from_navigator_to_cookie_quote =
1983                        lit_bool_expr_or_idents(&initial_language_from_navigator_to_cookie);
1984                    let initial_language_from_navigator_to_server_function_quote =
1985                        lit_bool_expr_or_idents(
1986                            &initial_language_from_navigator_to_server_function,
1987                        );
1988                    let set_language_from_navigator_quote =
1989                        lit_bool_expr_or_idents(&set_language_from_navigator);
1990                    let initial_language_from_accept_language_header_quote =
1991                        lit_bool_expr_or_idents(
1992                            &initial_language_from_accept_language_header,
1993                        );
1994                    let cookie_name_quote = litstr_or_default(
1995                        &cookie_name.lit,
1996                        &cookie_name.expr,
1997                        "lf-lang",
1998                    );
1999                    let cookie_attrs_quote =
2000                        litstr_or_default(&cookie_attrs.lit, &cookie_attrs.expr, "");
2001                    let initial_language_from_cookie_quote =
2002                        lit_bool_expr_or_idents(&initial_language_from_cookie);
2003                    let initial_language_from_cookie_to_local_storage_quote =
2004                        lit_bool_expr_or_idents(
2005                            &initial_language_from_cookie_to_local_storage,
2006                        );
2007                    let initial_language_from_cookie_to_session_storage_quote =
2008                        lit_bool_expr_or_idents(
2009                            &initial_language_from_cookie_to_session_storage,
2010                        );
2011                    let initial_language_from_cookie_to_server_function_quote =
2012                        lit_bool_expr_or_idents(
2013                            &initial_language_from_cookie_to_server_function,
2014                        );
2015                    let set_language_to_cookie_quote =
2016                        lit_bool_expr_or_idents(&set_language_to_cookie);
2017                    let initial_language_from_server_function_quote =
2018                        lit_bool_expr_or_idents(
2019                            &initial_language_from_server_function,
2020                        );
2021                    let initial_language_from_server_function_to_cookie_quote =
2022                        lit_bool_expr_or_idents(
2023                            &initial_language_from_server_function_to_cookie,
2024                        );
2025                    let initial_language_from_server_function_to_local_storage_quote =
2026                        lit_bool_expr_or_idents(
2027                            &initial_language_from_server_function_to_local_storage,
2028                        );
2029                    let set_language_to_server_function_quote =
2030                        lit_bool_expr_or_idents(&set_language_to_server_function);
2031                    let url_path_quote = if url_path.is_some() {quote!{true}} else {quote!{false}};
2032                    let initial_language_from_url_path_quote =
2033                        lit_bool_expr_or_idents(&initial_language_from_url_path);
2034                    let initial_language_from_url_path_to_cookie_quote =
2035                        lit_bool_expr_or_idents(&initial_language_from_url_path_to_cookie);
2036                    let initial_language_from_url_path_to_local_storage_quote =
2037                        lit_bool_expr_or_idents(
2038                            &initial_language_from_url_path_to_local_storage,
2039                        );
2040                    let initial_language_from_url_path_to_session_storage_quote =
2041                        lit_bool_expr_or_idents(
2042                            &initial_language_from_url_path_to_session_storage,
2043                        );
2044                    let initial_language_from_url_path_to_server_function_quote =
2045                        lit_bool_expr_or_idents(
2046                            &initial_language_from_url_path_to_server_function,
2047                        );
2048
2049                    let system_quote = {
2050                        #[cfg(not(feature = "system"))]
2051                        quote! {}
2052
2053                        #[cfg(feature = "system")]
2054                        {
2055                            let initial_language_from_system_quote =
2056                                lit_bool_expr_or_idents(&initial_language_from_system);
2057                            let initial_language_from_data_file_quote =
2058                                lit_bool_expr_or_idents(&initial_language_from_data_file);
2059                            let initial_language_from_system_to_data_file_quote =
2060                                lit_bool_expr_or_idents(
2061                                    &initial_language_from_system_to_data_file,
2062                                );
2063                            let set_language_to_data_file_quote =
2064                                lit_bool_expr_or_idents(&set_language_to_data_file);
2065                            let data_file_key_quote = litstr_or_default(
2066                                &data_file_key.lit,
2067                                &data_file_key.expr,
2068                                "leptos-fluent",
2069                            );
2070
2071                            quote! {
2072                                initial_language_from_system: #initial_language_from_system_quote,
2073                                initial_language_from_data_file: #initial_language_from_data_file_quote,
2074                                initial_language_from_system_to_data_file: #initial_language_from_system_to_data_file_quote,
2075                                set_language_to_data_file: #set_language_to_data_file_quote,
2076                                data_file_key: #data_file_key_quote,
2077                            }
2078                        }
2079                    };
2080
2081                    let quote = quote! {
2082                        const meta: ::leptos_fluent::LeptosFluentMeta = ::leptos_fluent::LeptosFluentMeta {
2083                            locales: #locales_path,
2084                            core_locales: #core_locales_quote,
2085                            languages: #languages_quote,
2086                            default_language: #default_language_quote,
2087                            translations: #translations_quote,
2088                            check_translations: #check_translations_quote,
2089                            fill_translations: #fill_translations_quote,
2090                            sync_html_tag_lang: #sync_html_tag_lang_quote,
2091                            sync_html_tag_dir: #sync_html_tag_dir_quote,
2092                            url_param: #url_param_quote,
2093                            initial_language_from_url_param: #initial_language_from_url_param_quote,
2094                            initial_language_from_url_param_to_local_storage: #initial_language_from_url_param_to_local_storage,
2095                            initial_language_from_url_param_to_session_storage: #initial_language_from_url_param_to_session_storage,
2096                            initial_language_from_url_param_to_cookie: #initial_language_from_url_param_to_cookie_quote,
2097                            initial_language_from_url_param_to_server_function: #initial_language_from_url_param_to_server_function_quote,
2098                            set_language_to_url_param: #set_language_to_url_param_quote,
2099                            local_storage_key: #local_storage_key_quote,
2100                            initial_language_from_local_storage: #initial_language_from_local_storage_quote,
2101                            initial_language_from_local_storage_to_cookie: #initial_language_from_local_storage_to_cookie_quote,
2102                            initial_language_from_local_storage_to_session_storage: #initial_language_from_local_storage_to_session_storage_quote,
2103                            initial_language_from_local_storage_to_server_function: #initial_language_from_local_storage_to_server_function_quote,
2104                            set_language_to_local_storage: #set_language_to_local_storage_quote,
2105                            session_storage_key: #session_storage_key_quote,
2106                            initial_language_from_session_storage: #initial_language_from_session_storage_quote,
2107                            initial_language_from_session_storage_to_cookie: #initial_language_from_session_storage_to_cookie_quote,
2108                            initial_language_from_session_storage_to_local_storage: #initial_language_from_session_storage_to_local_storage_quote,
2109                            initial_language_from_session_storage_to_server_function: #initial_language_from_session_storage_to_server_function_quote,
2110                            set_language_to_session_storage: #set_language_to_session_storage_quote,
2111                            initial_language_from_navigator: #initial_language_from_navigator_quote,
2112                            initial_language_from_navigator_to_local_storage: #initial_language_from_navigator_to_local_storage_quote,
2113                            initial_language_from_navigator_to_session_storage: #initial_language_from_navigator_to_session_storage_quote,
2114                            initial_language_from_navigator_to_cookie: #initial_language_from_navigator_to_cookie_quote,
2115                            initial_language_from_navigator_to_server_function: #initial_language_from_navigator_to_server_function_quote,
2116                            set_language_from_navigator: #set_language_from_navigator_quote,
2117                            initial_language_from_accept_language_header: #initial_language_from_accept_language_header_quote,
2118                            cookie_name: #cookie_name_quote,
2119                            cookie_attrs: #cookie_attrs_quote,
2120                            initial_language_from_cookie: #initial_language_from_cookie_quote,
2121                            initial_language_from_cookie_to_local_storage: #initial_language_from_cookie_to_local_storage_quote,
2122                            initial_language_from_cookie_to_session_storage: #initial_language_from_cookie_to_session_storage_quote,
2123                            initial_language_from_cookie_to_server_function: #initial_language_from_cookie_to_server_function_quote,
2124                            set_language_to_cookie: #set_language_to_cookie_quote,
2125                            initial_language_from_server_function: #initial_language_from_server_function_quote,
2126                            initial_language_from_server_function_to_cookie: #initial_language_from_server_function_to_cookie_quote,
2127                            initial_language_from_server_function_to_local_storage: #initial_language_from_server_function_to_local_storage_quote,
2128                            set_language_to_server_function: #set_language_to_server_function_quote,
2129                            url_path: #url_path_quote,
2130                            initial_language_from_url_path: #initial_language_from_url_path_quote,
2131                            initial_language_from_url_path_to_cookie: #initial_language_from_url_path_to_cookie_quote,
2132                            initial_language_from_url_path_to_local_storage: #initial_language_from_url_path_to_local_storage_quote,
2133                            initial_language_from_url_path_to_session_storage: #initial_language_from_url_path_to_session_storage_quote,
2134                            initial_language_from_url_path_to_server_function: #initial_language_from_url_path_to_server_function_quote,
2135                            provide_meta_context: true,
2136                            #system_quote
2137                        };
2138                        ::leptos::context::provide_context::<::leptos_fluent::LeptosFluentMeta>(meta);
2139                    };
2140
2141                    match param.exprpath {
2142                        Some(ref path) => quote! { #path{#quote}; },
2143                        None => quote,
2144                    }
2145                }
2146                false => quote!(),
2147            }
2148        }).collect()
2149    };
2150
2151    let warnings_quote: proc_macro2::TokenStream =
2152        warnings.iter().map(|warning| quote!(#warning)).collect();
2153    let other_quotes = quote! {
2154        #sync_language_with_server_function_quote
2155        #sync_language_with_local_storage_quote
2156        #sync_language_with_session_storage_quote
2157        #sync_language_with_url_param_quote
2158        #sync_language_with_cookie_quote
2159        #sync_language_with_data_file_quote
2160        #set_language_from_navigator_quote
2161        #files_tracker_quote
2162        #leptos_fluent_provide_meta_context_quote
2163        #warnings_quote
2164    };
2165
2166    let initial_language_index = match default_language {
2167        Some((_, index)) => index,
2168        None => 0,
2169    };
2170
2171    let (fluent_templates_quote, translations_quote) = match translations {
2172        Some(ref translations) => (quote!(), quote!(#translations)),
2173        None => {
2174            let fallback_language =
2175                &languages[initial_language_index].0.to_string();
2176
2177            let core_locales_quote = match &core_locales_path {
2178                Some(ref path) => quote!(core_locales: #path,),
2179                None => quote!(),
2180            };
2181
2182            let (customise_quote, warnings_quote) = {
2183                #[cfg(feature = "disable-unicode-isolating-marks")]
2184                {
2185                    (
2186                        customise.map_or(
2187                            quote!(customise: |bundle| bundle.set_use_isolating(false),),
2188                            |c| quote! {
2189                                customise: |bundle| {
2190                                    bundle.set_use_isolating(false);
2191                                    let customise: fn(&mut ::leptos_fluent::__reexports::fluent_templates::FluentBundle<&::leptos_fluent::__reexports::fluent_bundle::FluentResource>) = #c;
2192                                    customise(bundle);
2193                                },
2194                            }
2195                        ),
2196                        {
2197                            // warning for `disable-unicode-isolating-marks` feature
2198                            // TODO: remove it in v0.3.0
2199                            let warning = proc_macro_warning::FormattedWarning::new_deprecated(
2200                                "disable_unicode_isolating_marks",
2201                                "The feature `disable-unicode-isolating-marks` has been deprecated \
2202                                and will be removed in v0.3.0. Use the `customise` argument of \
2203                                `leptos_fluent!` macro with the value \
2204                                `|bundle| bundle.set_use_isolating(false)` instead to disable Unicode \
2205                                isolating marks in translations.",
2206                                proc_macro2::Span::call_site(),
2207                            );
2208                            quote!(#warning)
2209                        },
2210                    )
2211                }
2212
2213                #[cfg(not(feature = "disable-unicode-isolating-marks"))]
2214                {
2215                    (
2216                        customise
2217                            .map_or(quote!(), |c| quote! { customise: #c, }),
2218                        quote!(),
2219                    )
2220                }
2221            };
2222
2223            (
2224                quote! {
2225                    use ::leptos_fluent::__reexports::fluent_templates::{static_loader, self};
2226                    static_loader! {
2227                        static TRS = {
2228                            locales: #locales_path,
2229                            fallback_language: #fallback_language,
2230                            #core_locales_quote
2231                            #customise_quote
2232                        };
2233                    }
2234                    #warnings_quote
2235                },
2236                quote!(vec![&TRS]),
2237            )
2238        }
2239    };
2240
2241    let init_quote = quote! {
2242        {
2243            let mut lang: Option<&'static ::leptos_fluent::Language> = None;
2244            #initial_language_quote;
2245
2246            let initial_lang = if let Some(l) = lang {
2247                l
2248            } else {
2249                LANGUAGES[#initial_language_index]
2250            };
2251
2252            #fluent_templates_quote;
2253
2254            let i18n = ::leptos_fluent::I18n {
2255                language: ::leptos::prelude::RwSignal::new(initial_lang),
2256                languages: &LANGUAGES,
2257                translations: ::leptos::prelude::Signal::derive(move || #translations_quote),
2258            };
2259            ::leptos::context::provide_context::<::leptos_fluent::I18n>(i18n);
2260            i18n
2261        }
2262    };
2263
2264    let children_quote: proc_macro2::TokenStream = children
2265        .iter()
2266        .map(|param| {
2267            let expr = param.expr.as_ref().unwrap();
2268            match param.exprpath {
2269                Some(ref path) => quote!(#path{#expr}),
2270                None => quote!(#expr),
2271            }
2272        })
2273        .collect();
2274
2275    let quote = quote! {
2276        let i18n = {
2277            const LANGUAGES: [&::leptos_fluent::Language; #n_languages] =
2278                #languages_quote;
2279            let i18n = #init_quote;
2280            #other_quotes
2281            i18n
2282        };
2283        {
2284            use ::leptos::context::Provider;
2285            ::leptos::prelude::view! {
2286                <Provider value={i18n}>
2287                    #sync_html_tag_quote
2288                    {#children_quote}
2289                </Provider>
2290            }
2291        }
2292    };
2293
2294    #[cfg(feature = "debug")]
2295    debug(&format!("\n{}", &quote.to_string()));
2296
2297    #[cfg(feature = "tracing")]
2298    tracing::trace!("{}", &quote.to_string());
2299
2300    proc_macro::TokenStream::from(quote)
2301}
2302
2303#[cfg(test)]
2304mod tests {
2305    use trybuild;
2306
2307    #[test]
2308    fn leptos_fluent_trybuild_pass() {
2309        let t = trybuild::TestCases::new();
2310
2311        #[cfg(feature = "nightly")]
2312        t.pass("tests/ui/leptos_fluent/nightly/pass/*.rs");
2313
2314        // some tests are flaky on nightly
2315        #[cfg(not(feature = "nightly"))]
2316        {
2317            t.pass("tests/ui/leptos_fluent/nightly/pass/*.rs");
2318            t.pass("tests/ui/leptos_fluent/stable/pass/*.rs");
2319        }
2320    }
2321
2322    #[cfg(not(feature = "nightly"))]
2323    #[test]
2324    fn leptos_fluent_trybuild_fail() {
2325        let t = trybuild::TestCases::new();
2326        t.compile_fail("tests/ui/leptos_fluent/stable/fail/*.rs");
2327    }
2328}