feat: localization support

This commit is contained in:
Felipe 2025-03-14 22:46:50 -03:00
parent e3e92a158a
commit f48d78d03b
Signed by: pitbuster
SSH key fingerprint: SHA256:HDYu2Pm4/TmSX8GBwV49UvFWr1Ljg8XlHxKeCpjJpOk
9 changed files with 106 additions and 26 deletions

View file

@ -1 +1,6 @@
not-found = We couldn't find that page. not-found = We couldn't find that page.
title = Hypergeometric Distribution Calculator
population = Population Size
successes = Successes in Population
sample = Sample Size
sample_successes = Successes in Sample

View file

@ -1 +1,6 @@
not-found = No pudimos encontrar esta página. not-found = No pudimos encontrar esta página.
title = Calculadora Distribución Hipergeométrica
population = Tamaño población
successes = Éxitos en la población
sample = Tamaño de la muestra
sample_successes = Éxitos en la muestra

View file

@ -32,6 +32,11 @@ h6 {
padding: 2rem; padding: 2rem;
} }
h1.title {
width: 80vw;
max-inline-size: 80vw;
}
p { p {
margin: var(--size-6); margin: var(--size-6);
} }
@ -59,7 +64,7 @@ form input {
form > div.results { form > div.results {
display: grid; display: grid;
grid-template-columns: 1fr 6em 6em 1fr; grid-template-columns: 1fr 8em 5em 1fr;
min-width: 20em; min-width: 20em;
max-width: 30em; max-width: 30em;
width: 30vw; width: 30vw;

View file

@ -1,9 +1,10 @@
//! Hypergeometric Distribution Calculator //! Hypergeometric Distribution Calculator
use leptos::html::ElementChild; use leptos::html::ElementChild;
use leptos::prelude::{ use leptos::prelude::{
signal, ClassAttribute, Get, GlobalAttributes, OnTargetAttribute, PropAttribute, Set, ClassAttribute, Get, GlobalAttributes, OnTargetAttribute, PropAttribute, Set, signal,
}; };
use leptos::{component, view, IntoView}; use leptos::{IntoView, component, view};
use leptos_fluent::move_tr;
use crate::calc::hyper_geometric; use crate::calc::hyper_geometric;
@ -26,7 +27,7 @@ pub fn Calculator() -> impl IntoView {
view! { view! {
<form> <form>
<p> <p>
<label for="population">Population Size</label> <label for="population">{move_tr!("population")}</label>
<input <input
id="population" id="population"
type="number" type="number"
@ -38,7 +39,7 @@ pub fn Calculator() -> impl IntoView {
/> />
</p> </p>
<p> <p>
<label for="successes">Successes in Population</label> <label for="successes">{move_tr!("successes")}</label>
<input <input
id="successes" id="successes"
type="number" type="number"
@ -51,7 +52,7 @@ pub fn Calculator() -> impl IntoView {
/> />
</p> </p>
<p> <p>
<label for="sample">Sample Size</label> <label for="sample">{move_tr!("sample")}</label>
<input <input
id="sample" id="sample"
type="number" type="number"
@ -64,7 +65,7 @@ pub fn Calculator() -> impl IntoView {
/> />
</p> </p>
<p> <p>
<label for="sample_successes">Successes in Sample</label> <label for="sample_successes">{move_tr!("sample_successes")}</label>
<input <input
id="sample_successes" id="sample_successes"
type="number" type="number"

View file

@ -0,0 +1,55 @@
//! Localization selector
use fluent_templates::static_loader;
use leptos::html::ElementChild;
use leptos::prelude::{Children, GlobalAttributes, OnAttribute, PropAttribute, Set};
use leptos::{IntoView, component, view};
use leptos_fluent::{Language, expect_i18n, leptos_fluent};
static_loader! {
pub static TRANSLATIONS = {
locales: "./locales",
fallback_language: "en",
};
}
/// This component provide localization context to every children component.
#[component]
pub fn I18n(children: Children) -> impl IntoView {
leptos_fluent! {
children: children(),
translations: [TRANSLATIONS],
locales: "./locales",
check_translations: "./src/**/*.rs",
}
}
#[component]
pub fn LanguageSelector() -> impl IntoView {
// Use `expect_i18n()` to get the current i18n context:
let i18n = expect_i18n();
view! {
<label for="language">"A/文:"</label>
<select id="language">
{move || {
i18n.languages.iter().map(|lang| render_language(lang)).collect::<Vec<_>>()
}}
</select>
}
}
fn render_language(lang: &'static Language) -> impl IntoView {
// Passed as atrribute, `Language` is converted to their code,
// so `<input id=lang` becomes `<input id=lang.id.to_string()`
let i18n = expect_i18n();
view! {
<option
id=lang
value=lang
prop:selected=lang.is_active()
on:click=move |_| i18n.language.set(lang)
>
{lang.name}
</option>
}
}

View file

@ -1 +1,2 @@
pub mod calculator; pub mod calculator;
pub mod localization;

View file

@ -1,6 +1,5 @@
use fluent_templates::static_loader;
use leptos::prelude::{AddAnyAttr, IntoAttribute}; use leptos::prelude::{AddAnyAttr, IntoAttribute};
use leptos::{component, view, IntoView}; use leptos::{IntoView, component, view};
use leptos_meta::*; use leptos_meta::*;
use leptos_router::{components::*, path}; use leptos_router::{components::*, path};
@ -12,14 +11,6 @@ mod pages;
// Top-Level pages // Top-Level pages
use crate::pages::home::Home; use crate::pages::home::Home;
// Localization
static_loader! {
pub static TRANSLATIONS = {
locales: "./locales",
fallback_language: "en",
};
}
/// An app router which renders the homepage and handles 404's /// An app router which renders the homepage and handles 404's
#[component] #[component]
pub fn App() -> impl IntoView { pub fn App() -> impl IntoView {
@ -30,7 +21,7 @@ pub fn App() -> impl IntoView {
<Html attr:lang="en" attr:dir="ltr" attr:data-theme="light" /> <Html attr:lang="en" attr:dir="ltr" attr:data-theme="light" />
// sets the document title // sets the document title
<Title text="Welcome to Leptos CSR" /> <Title text="Hypergeometric Calculator" />
// injects metadata in the <head> of the page // injects metadata in the <head> of the page
<Meta charset="UTF-8" /> <Meta charset="UTF-8" />

View file

@ -2,9 +2,11 @@ use leptos::attr::global::ClassAttribute;
use leptos::error::ErrorBoundary; use leptos::error::ErrorBoundary;
use leptos::html::ElementChild; use leptos::html::ElementChild;
use leptos::prelude::{CollectView, Get}; use leptos::prelude::{CollectView, Get};
use leptos::{component, view, IntoView}; use leptos::{IntoView, component, view};
use leptos_fluent::move_tr;
use crate::components::calculator::Calculator; use crate::components::calculator::Calculator;
use crate::components::localization::{I18n, LanguageSelector};
/// Default Home Page /// Default Home Page
#[component] #[component]
@ -28,13 +30,21 @@ pub fn Home() -> impl IntoView {
</ul> </ul>
} }
}> }>
<div class="container"> <I18n>
<h1>"Hypergeometric Distribution Calculator"</h1> <header>
<LanguageSelector />
<div class="calculator"> </header>
<div class="container">
<Title />
<Calculator /> <Calculator />
</div> </div>
</div> </I18n>
</ErrorBoundary> </ErrorBoundary>
} }
} }
/// Title
#[component]
pub fn Title() -> impl IntoView {
view! {<h1 class="title">{move_tr!("title")}</h1>}
}

View file

@ -1,8 +1,15 @@
use leptos::html::ElementChild; use leptos::html::ElementChild;
use leptos::{component, view, IntoView}; use leptos::{IntoView, component, view};
use leptos_fluent::move_tr;
use crate::components::localization::I18n;
/// 404 Not Found Page /// 404 Not Found Page
#[component] #[component]
pub fn NotFound() -> impl IntoView { pub fn NotFound() -> impl IntoView {
view! { <h1>"We couldn't find that page."</h1> } view! {
<I18n>
<h1>{move_tr!("not-found")}</h1>
</I18n>
}
} }