Add German translations for species enum values (plant_layer, drought_tolerance, pollination_type, succession_stage, native_status)

This commit is contained in:
2026-03-15 21:50:41 +01:00
parent aa36c846d7
commit b24038cb57
3 changed files with 72 additions and 28 deletions
+41
View File
@@ -205,6 +205,8 @@ pub fn t(lang: &str, key: &str) -> &'static str {
("de", "sources.intro") => "HerbAPI aggregiert Pflanzendaten aus mehreren offenen Quellen. Wir danken diesen Projekten, dass sie botanisches Wissen frei zug\u{00e4}nglich machen.",
("de", "species_species") => "Arten",
("de", "loading_species_data") => "Lade Artdaten\u{2026}",
("de", "specialists") => "Spezialisten",
("de", "caterpillar_hosts") => "Raupen-Wirtspflanzen",
// ── English defaults ──────────────────────────────────────
@@ -385,7 +387,46 @@ pub fn t(lang: &str, key: &str) -> &'static str {
(_, "sources.intro") => "HerbAPI aggregates plant data from multiple open sources. We are grateful to these projects for making botanical knowledge freely available.",
(_, "species_species") => "species",
(_, "loading_species_data") => "Loading species data\u{2026}",
(_, "specialists") => "specialists",
(_, "caterpillar_hosts") => "caterpillar hosts",
_ => "???",
}
}
/// Translate a stored enum/tag value to the display language.
/// Falls back to the raw value for unknown combinations.
pub fn t_val<'a>(lang: &str, value: &'a str) -> &'a str {
match (lang, value) {
// plant_layer
("de", "canopy") => "Baumschicht",
("de", "understory") => "Unterholz",
("de", "shrub") => "Strauch",
("de", "herbaceous") => "Krautig",
("de", "ground_cover") => "Bodendecker",
("de", "vine") => "Kletterpflanze",
("de", "root") => "Wurzelschicht",
// drought_tolerance
("de", "none") => "keine",
("de", "low") => "gering",
("de", "moderate") => "mittel",
("de", "high") => "hoch",
("de", "very_high") => "sehr hoch",
// pollination_type
("de", "insect") => "Insekten",
("de", "wind") => "Wind",
("de", "self") => "Selbstbest\u{00e4}ubung",
("de", "water") => "Wasser",
("de", "Insect-pollinated") => "Insektenbest\u{00e4}ubt",
("de", "Wind-pollinated") => "Windbest\u{00e4}ubt",
// succession_stage
("de", "pioneer") => "Pionierstadium",
("de", "secondary") => "Sekund\u{00e4}rstadium",
("de", "climax") => "Klimaxstadium",
// native_status
("de", "heimische Wildform") => "Heimische Wildform",
("de", "Arch\u{00e4}ophyt") => "Arch\u{00e4}ophyt",
("de", "Neophyt") => "Neophyt",
_ => value,
}
}
+6 -5
View File
@@ -6,7 +6,7 @@ use crate::api;
use crate::app::{Lang, Route};
use crate::components::planting_calendar::PlantingCalendar;
use crate::components::table_controls::*;
use crate::i18n::{pick_desc, pick_name, t};
use crate::i18n::{pick_desc, pick_name, t, t_val};
/// Convert a month-number array (1=Jan..12=Dec) to a comma-separated string of abbreviations.
fn months_display(months: &Option<Vec<i32>>) -> String {
@@ -418,8 +418,9 @@ pub fn CultivarDetail(slug: String) -> Element {
.filter(|s| !s.is_empty())
.map(|z| format!("USDA Zone {z}"));
let light = infer_light_from_layer(&sp.plant_layer);
let drought = sp.drought_tolerance.clone()
.filter(|s| !s.is_empty());
let drought = sp.drought_tolerance.as_deref()
.filter(|s| !s.is_empty())
.map(|v| t_val(l, v).to_string());
let usda = sp.hardiness_zone_usda.clone()
.filter(|s| !s.is_empty());
(frost, light, drought, usda)
@@ -839,8 +840,8 @@ pub fn CultivarDetail(slug: String) -> Element {
(None, Some(mx)) => format!("< {mx}"),
_ => "\u{2014}".to_string(),
};
let sp_layer = opt_str(&sp.plant_layer);
let sp_drought = opt_str(&sp.drought_tolerance);
let sp_layer = sp.plant_layer.as_deref().map(|v| t_val(l, v).to_string()).unwrap_or_else(|| "\u{2014}".to_string());
let sp_drought = sp.drought_tolerance.as_deref().map(|v| t_val(l, v).to_string()).unwrap_or_else(|| "\u{2014}".to_string());
let sp_usda = opt_str(&sp.hardiness_zone_usda);
let sp_nfix = opt_bool_t(sp.nitrogen_fixer, l);
let sp_dynacc = opt_bool_t(sp.dynamic_accumulator, l);
+25 -23
View File
@@ -7,7 +7,7 @@ use crate::api;
use crate::app::{Lang, Route};
use crate::components::plant_card::PlantCard;
use crate::components::table_controls::*;
use crate::i18n::{pick_desc, pick_name, t};
use crate::i18n::{pick_desc, pick_name, t, t_val};
const STORAGE_KEY_COLS: &str = "herbapi_species_cols";
const STORAGE_KEY_PP: &str = "herbapi_species_pp";
@@ -140,13 +140,13 @@ pub fn SpeciesList() -> Element {
page.set(1);
},
option { value: "", "{t(&l, \"filter.all\")}" }
option { value: "canopy", "Canopy" }
option { value: "understory", "Understory" }
option { value: "shrub", "Shrub" }
option { value: "herbaceous", "Herbaceous" }
option { value: "ground_cover", "Ground Cover" }
option { value: "vine", "Vine" }
option { value: "root", "Root" }
option { value: "canopy", "{t_val(&l, \"canopy\")}" }
option { value: "understory", "{t_val(&l, \"understory\")}" }
option { value: "shrub", "{t_val(&l, \"shrub\")}" }
option { value: "herbaceous", "{t_val(&l, \"herbaceous\")}" }
option { value: "ground_cover", "{t_val(&l, \"ground_cover\")}" }
option { value: "vine", "{t_val(&l, \"vine\")}" }
option { value: "root", "{t_val(&l, \"root\")}" }
}
}
div { class: "filter-group",
@@ -158,11 +158,11 @@ pub fn SpeciesList() -> Element {
page.set(1);
},
option { value: "", "{t(&l, \"filter.all\")}" }
option { value: "none", "None" }
option { value: "low", "Low" }
option { value: "moderate", "Moderate" }
option { value: "high", "High" }
option { value: "very_high", "Very High" }
option { value: "none", "{t_val(&l, \"none\")}" }
option { value: "low", "{t_val(&l, \"low\")}" }
option { value: "moderate", "{t_val(&l, \"moderate\")}" }
option { value: "high", "{t_val(&l, \"high\")}" }
option { value: "very_high", "{t_val(&l, \"very_high\")}" }
}
}
div { class: "filter-group filter-checkbox",
@@ -297,7 +297,7 @@ pub fn SpeciesList() -> Element {
td { class: "species-name", em { "{family_name}" } }
}
if is_col_visible(&vis, "plant_layer") {
td { "{s.plant_layer.as_deref().unwrap_or(\"-\")}" }
td { "{s.plant_layer.as_deref().map(|v| t_val(&l, v)).unwrap_or(\"-\")}" }
}
if is_col_visible(&vis, "nitrogen_fixer") {
td {
@@ -331,7 +331,7 @@ pub fn SpeciesList() -> Element {
}
}
if is_col_visible(&vis, "drought_tolerance") {
td { "{s.drought_tolerance.as_deref().unwrap_or(\"-\")}" }
td { "{s.drought_tolerance.as_deref().map(|v| t_val(&l, v)).unwrap_or(\"-\")}" }
}
if is_col_visible(&vis, "hardiness_zone_usda") {
td { "{s.hardiness_zone_usda.as_deref().unwrap_or(\"-\")}" }
@@ -362,7 +362,8 @@ pub fn SpeciesList() -> Element {
s if s.contains("eophyt") => "native-badge native-badge-neophyt",
_ => "native-badge",
};
rsx! { span { class: "{badge_class}", "{ns}" } }
let translated = t_val(&l, ns);
rsx! { span { class: "{badge_class}", "{translated}" } }
},
_ => rsx! { "-" },
}
@@ -507,11 +508,11 @@ pub fn SpeciesDetail(slug: String) -> Element {
};
// Ecology
let layer = os(&s.plant_layer);
let succession = os(&s.succession_stage);
let layer = s.plant_layer.as_deref().map(|v| t_val(l, v).to_string()).unwrap_or_else(|| em.to_string());
let succession = s.succession_stage.as_deref().map(|v| t_val(l, v).to_string()).unwrap_or_else(|| em.to_string());
let wildlife = os(&s.wildlife_value);
let native = os(&s.native_range);
let pollination = os(&s.pollination_type);
let pollination = s.pollination_type.as_deref().map(|v| t_val(l, v).to_string()).unwrap_or_else(|| em.to_string());
// Growing requirements
let soil_moist = os(&s.soil_moisture);
@@ -521,7 +522,7 @@ pub fn SpeciesDetail(slug: String) -> Element {
(None, Some(mx)) => format!("< {mx}"),
_ => em.to_string(),
};
let drought = os(&s.drought_tolerance);
let drought = s.drought_tolerance.as_deref().map(|v| t_val(l, v).to_string()).unwrap_or_else(|| em.to_string());
let salt = os(&s.salt_tolerance);
let usda = os(&s.hardiness_zone_usda);
let at_zone = os(&s.hardiness_zone_at);
@@ -810,7 +811,7 @@ pub fn SpeciesDetail(slug: String) -> Element {
th { "{t(l, \"field.wild_bees\")}" }
td {
match (s.wild_bee_count, s.wild_bee_specialist_count) {
(Some(total), Some(spec)) => rsx! { "{total} {t(l, \"species_species\")} ({spec} specialists)" },
(Some(total), Some(spec)) => rsx! { "{total} {t(l, \"species_species\")} ({spec} {t(l, \"specialists\")})" },
(Some(total), None) => rsx! { "{total} {t(l, \"species_species\")}" },
_ => rsx! { span { class: "placeholder", "\u{2014}" } },
}
@@ -820,7 +821,7 @@ pub fn SpeciesDetail(slug: String) -> Element {
th { "{t(l, \"field.butterflies\")}" }
td {
match (s.butterfly_moth_count, s.caterpillar_host_count) {
(Some(bm), Some(ch)) => rsx! { "{bm} {t(l, \"species_species\")} ({ch} caterpillar hosts)" },
(Some(bm), Some(ch)) => rsx! { "{bm} {t(l, \"species_species\")} ({ch} {t(l, \"caterpillar_hosts\")})" },
(Some(bm), None) => rsx! { "{bm} {t(l, \"species_species\")}" },
_ => rsx! { span { class: "placeholder", "\u{2014}" } },
}
@@ -879,7 +880,8 @@ pub fn SpeciesDetail(slug: String) -> Element {
s if s.contains("eophyt") => "native-badge native-badge-neophyt",
_ => "native-badge",
};
rsx! { span { class: "{badge_class}", "{ns}" } }
let translated = t_val(l, ns);
rsx! { span { class: "{badge_class}", "{translated}" } }
},
_ => rsx! { span { class: "placeholder", "\u{2014}" } },
}