Add DE/EN language toggle with bilingual descriptions

- Language switcher in sidebar (DE/EN buttons, persists to localStorage)
- i18n module with pick_desc/pick_name helpers for language-aware fallback
- All detail/list pages use language context for names and descriptions
- Species/Cultivar types updated with description_de/description_en
- Common Name column added to species/families lists
This commit is contained in:
2026-03-15 13:01:38 +01:00
parent efa05b2d44
commit 3ecfdfadf2
9 changed files with 244 additions and 63 deletions
+34
View File
@@ -1,8 +1,13 @@
use dioxus::prelude::*;
use gloo_storage::{LocalStorage, Storage};
use crate::api;
use crate::types::MeResponse;
/// Global language signal shared via context. Values: "de" or "en".
#[derive(Clone, Copy)]
pub struct Lang(pub Signal<String>);
#[derive(Routable, Clone, Debug, PartialEq)]
#[rustfmt::skip]
pub enum Route {
@@ -43,6 +48,15 @@ pub fn App() -> Element {
#[component]
fn Layout() -> Element {
// Language state: load from localStorage, default "de"
let lang_signal = use_signal(|| {
LocalStorage::get::<String>("herbapi_lang").unwrap_or_else(|_| "de".to_string())
});
use_context_provider(|| Lang(lang_signal));
let mut lang = use_context::<Lang>().0;
let current_lang = lang.read().clone();
// Try to get current user (may be None for public access)
let auth = use_resource(|| async { api::get_current_user().await.ok() });
let user: Option<MeResponse> = auth.read().as_ref().and_then(|r| r.clone());
@@ -66,6 +80,26 @@ fn Layout() -> Element {
NavLink { to: Route::SearchPage {}, label: "Search" }
NavLink { to: Route::Sources {}, label: "Sources" }
}
div { class: "sidebar-lang",
div { class: "lang-toggle",
button {
class: if current_lang == "de" { "lang-btn lang-btn-active" } else { "lang-btn" },
onclick: move |_| {
lang.set("de".to_string());
let _ = LocalStorage::set("herbapi_lang", "de".to_string());
},
"DE"
}
button {
class: if current_lang == "en" { "lang-btn lang-btn-active" } else { "lang-btn" },
onclick: move |_| {
lang.set("en".to_string());
let _ = LocalStorage::set("herbapi_lang", "en".to_string());
},
"EN"
}
}
}
div { class: "sidebar-user",
if let Some(ref u) = user {
span { class: "user-name", "{u.nickname.as_deref().or(u.name.as_deref()).unwrap_or(&u.email)}" }