Add full DE/EN UI label translations across all pages
Add t() translation function to i18n.rs with ~200 key/value pairs covering navigation, card headers, field labels, buttons, filters, planting calendar rows, and general UI text in both German and English. Updated all 11 source files to use t(&lang, "key") instead of hardcoded English strings: app.rs (sidebar nav), all 7 page files (species, cultivars, families, suppliers, home, search, sources), and all 3 component files (planting_calendar, table_controls, plant_card). Boolean values (Yes/No/Annual) are also translated.
This commit is contained in:
+101
-102
@@ -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};
|
||||
use crate::i18n::{pick_desc, pick_name, t};
|
||||
|
||||
/// Convert a month-number array (1=Jan..12=Dec) to a comma-separated string of abbreviations.
|
||||
fn months_display(months: &Option<Vec<i32>>) -> String {
|
||||
@@ -48,18 +48,18 @@ fn opt_i32_suffix(val: Option<i32>, suffix: &str) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
/// Format an Option<bool> as Yes / No / em dash.
|
||||
fn opt_bool(val: Option<bool>) -> String {
|
||||
/// Format an Option<bool> as Yes / No / em dash (translated).
|
||||
fn opt_bool_t(val: Option<bool>, lang: &str) -> String {
|
||||
match val {
|
||||
Some(true) => "Yes".to_string(),
|
||||
Some(false) => "No".to_string(),
|
||||
Some(true) => t(lang, "yes").to_string(),
|
||||
Some(false) => t(lang, "no").to_string(),
|
||||
None => "\u{2014}".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Bool field: always present (non-Option).
|
||||
fn bool_display(val: bool) -> &'static str {
|
||||
if val { "Yes" } else { "No" }
|
||||
/// Bool field: always present (non-Option), translated.
|
||||
fn bool_display_t(val: bool, lang: &str) -> String {
|
||||
if val { t(lang, "yes").to_string() } else { t(lang, "no").to_string() }
|
||||
}
|
||||
|
||||
/// Format an Option<Vec<String>> as comma-separated or em dash.
|
||||
@@ -109,6 +109,7 @@ fn cultivar_columns() -> Vec<ColumnDef> {
|
||||
|
||||
#[component]
|
||||
pub fn CultivarList() -> Element {
|
||||
let lang = use_context::<Lang>().0;
|
||||
let columns = cultivar_columns();
|
||||
let mut page = use_signal(|| 1i64);
|
||||
let per_page = use_signal(|| load_per_page(STORAGE_KEY_PP, 25));
|
||||
@@ -146,16 +147,17 @@ pub fn CultivarList() -> Element {
|
||||
});
|
||||
|
||||
let current_page = *page.read();
|
||||
let l = lang.read().clone();
|
||||
|
||||
rsx! {
|
||||
div { class: "page",
|
||||
h1 { "Cultivars" }
|
||||
h1 { "{t(&l, \"page.cultivars\")}" }
|
||||
|
||||
div { class: "table-toolbar",
|
||||
div { class: "search-bar",
|
||||
input {
|
||||
r#type: "text",
|
||||
placeholder: "Search cultivars...",
|
||||
placeholder: "{t(&l, \"search.placeholder_cultivars\")}",
|
||||
value: "{search}",
|
||||
oninput: move |e| {
|
||||
search.set(e.value());
|
||||
@@ -177,7 +179,7 @@ pub fn CultivarList() -> Element {
|
||||
}
|
||||
|
||||
match &*cultivars.read() {
|
||||
None => rsx! { p { "Loading..." } },
|
||||
None => rsx! { p { "{t(&l, \"loading\")}" } },
|
||||
Some(Err(e)) => rsx! { p { class: "error", "Error: {e}" } },
|
||||
Some(Ok(data)) => {
|
||||
let smap = species_map.read();
|
||||
@@ -188,34 +190,34 @@ pub fn CultivarList() -> Element {
|
||||
};
|
||||
let vis = visible_cols.read();
|
||||
rsx! {
|
||||
p { class: "result-count", "{data.total} cultivars" }
|
||||
p { class: "result-count", "{data.total} {t(&l, \"nav.cultivars\").to_lowercase()}" }
|
||||
div { class: "table-wrap",
|
||||
table {
|
||||
thead {
|
||||
tr {
|
||||
if is_col_visible(&vis, "name") {
|
||||
th { "Name" }
|
||||
th { "{t(&l, \"field.name\")}" }
|
||||
}
|
||||
if is_col_visible(&vis, "species") {
|
||||
th { "Species" }
|
||||
th { "{t(&l, \"field.species\")}" }
|
||||
}
|
||||
if is_col_visible(&vis, "organic") {
|
||||
th { "Organic" }
|
||||
th { "{t(&l, \"field.organic\")}" }
|
||||
}
|
||||
if is_col_visible(&vis, "perennial") {
|
||||
th { "Perennial" }
|
||||
th { "{t(&l, \"field.perennial\")}" }
|
||||
}
|
||||
if is_col_visible(&vis, "description") {
|
||||
th { "Description" }
|
||||
th { "{t(&l, \"field.description\")}" }
|
||||
}
|
||||
if is_col_visible(&vis, "frost_tolerance") {
|
||||
th { "Frost Tol." }
|
||||
th { "{t(&l, \"field.frost_tol\")}" }
|
||||
}
|
||||
if is_col_visible(&vis, "growing_time") {
|
||||
th { "Growing Time" }
|
||||
th { "{t(&l, \"field.growing_time\")}" }
|
||||
}
|
||||
if is_col_visible(&vis, "days_germ") {
|
||||
th { "Days to Germ." }
|
||||
th { "{t(&l, \"field.days_germ\")}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -238,14 +240,14 @@ pub fn CultivarList() -> Element {
|
||||
if is_col_visible(&vis, "organic") {
|
||||
td {
|
||||
if c.is_organic {
|
||||
span { class: "badge organic", "Yes" }
|
||||
span { class: "badge organic", "{t(&l, \"yes\")}" }
|
||||
} else {
|
||||
"-"
|
||||
}
|
||||
}
|
||||
}
|
||||
if is_col_visible(&vis, "perennial") {
|
||||
td { if c.perennial { "Yes" } else { "Annual" } }
|
||||
td { if c.perennial { "{t(&l, \"yes\")}" } else { "{t(&l, \"annual\")}" } }
|
||||
}
|
||||
if is_col_visible(&vis, "description") {
|
||||
td { class: "cell-truncated",
|
||||
@@ -283,13 +285,13 @@ pub fn CultivarList() -> Element {
|
||||
button {
|
||||
disabled: current_page <= 1,
|
||||
onclick: move |_| page.set(current_page - 1),
|
||||
"Previous"
|
||||
"{t(&l, \"btn.previous\")}"
|
||||
}
|
||||
span { "Page {current_page} of {(data.total + data.per_page - 1) / data.per_page}" }
|
||||
span { "{t(&l, \"page\")} {current_page} {t(&l, \"of\")} {(data.total + data.per_page - 1) / data.per_page}" }
|
||||
button {
|
||||
disabled: current_page * data.per_page >= data.total,
|
||||
onclick: move |_| page.set(current_page + 1),
|
||||
"Next"
|
||||
"{t(&l, \"btn.next\")}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -359,10 +361,11 @@ pub fn CultivarDetail(slug: String) -> Element {
|
||||
rsx! {
|
||||
div { class: "page cultivar-detail",
|
||||
match &*cultivar.read() {
|
||||
None => rsx! { p { "Loading..." } },
|
||||
None => rsx! { p { "{t(&lang.read(), \"loading\")}" } },
|
||||
Some(Err(e)) => rsx! { p { class: "error", "Error: {e}" } },
|
||||
Some(Ok(c)) => {
|
||||
let current_lang = lang.read().clone();
|
||||
let l = &*current_lang;
|
||||
|
||||
// Pre-compute display strings outside of rsx
|
||||
let common_name = pick_name(¤t_lang, &c.name_de, &c.name_en, &c.name);
|
||||
@@ -370,8 +373,8 @@ pub fn CultivarDetail(slug: String) -> Element {
|
||||
let name_de = opt_str(&c.name_de);
|
||||
let name_sci = opt_str(&c.name_scientific);
|
||||
let desc = pick_desc(¤t_lang, &c.description_de, &c.description_en, &c.description);
|
||||
let organic = bool_display(c.is_organic);
|
||||
let perennial = bool_display(c.perennial);
|
||||
let organic = bool_display_t(c.is_organic, l);
|
||||
let perennial = bool_display_t(c.perennial, l);
|
||||
|
||||
// Planting schedule
|
||||
let indoor = months_display(&c.indoor_sowing_months);
|
||||
@@ -393,9 +396,9 @@ pub fn CultivarDetail(slug: String) -> Element {
|
||||
let days_germ = opt_i32_suffix(c.days_to_germination, "");
|
||||
let germ_temp = opt_f64_suffix(c.germination_temp_c, " \u{00b0}C");
|
||||
let light_req = opt_str(&c.light_requirement);
|
||||
let strat_req = opt_bool(c.stratification_required);
|
||||
let strat_req = opt_bool_t(c.stratification_required, l);
|
||||
let strat_days = opt_i32_suffix(c.stratification_days, "");
|
||||
let scar_req = opt_bool(c.scarification_required);
|
||||
let scar_req = opt_bool_t(c.scarification_required, l);
|
||||
|
||||
// Climate
|
||||
let min_temp = opt_f64_suffix(c.min_temp, " \u{00b0}C");
|
||||
@@ -407,8 +410,6 @@ pub fn CultivarDetail(slug: String) -> Element {
|
||||
let opt_light_h = opt_f64_suffix(c.optimal_light_hours_day, " h");
|
||||
|
||||
// --- Species fallback values ---
|
||||
// When a cultivar field is empty, we show species-level
|
||||
// data styled as an estimation.
|
||||
let (sp_frost_fb, sp_light_fb, sp_drought_fb, sp_usda_fb) = {
|
||||
let sp_read = species_data.read();
|
||||
match &*sp_read {
|
||||
@@ -433,7 +434,7 @@ pub fn CultivarDetail(slug: String) -> Element {
|
||||
|| sp_drought_fb.is_some()
|
||||
|| sp_usda_fb.is_some();
|
||||
|
||||
// Species image: prefer primary from images API, else primary_image_key
|
||||
// Species image
|
||||
let sp_primary_img: Option<crate::types::Image> = {
|
||||
let imgs = species_images.read();
|
||||
match &*imgs {
|
||||
@@ -460,7 +461,7 @@ pub fn CultivarDetail(slug: String) -> Element {
|
||||
|
||||
// Week-based planting calendar (full width)
|
||||
div { class: "detail-card", style: "margin-top: 1.25rem;",
|
||||
div { class: "detail-card-header", "Planting Calendar" }
|
||||
div { class: "detail-card-header", "{t(l, \"card.planting_calendar\")}" }
|
||||
div { style: "padding: 0.75rem;",
|
||||
PlantingCalendar {
|
||||
indoor_sowing_months: c.indoor_sowing_months.clone(),
|
||||
@@ -483,27 +484,27 @@ pub fn CultivarDetail(slug: String) -> Element {
|
||||
|
||||
// Card 1: Cultivar Details
|
||||
div { class: "detail-card",
|
||||
div { class: "detail-card-header", "Cultivar Details" }
|
||||
div { class: "detail-card-header", "{t(l, \"card.cultivar_details\")}" }
|
||||
table { class: "attr-table",
|
||||
tbody {
|
||||
tr {
|
||||
th { "Name" }
|
||||
th { "{t(l, \"field.name\")}" }
|
||||
td { "{c.name}" }
|
||||
}
|
||||
tr {
|
||||
th { "Common Name" }
|
||||
th { "{t(l, \"field.common_name\")}" }
|
||||
td { "{common_name}" }
|
||||
}
|
||||
tr {
|
||||
th { "Name EN" }
|
||||
th { "{t(l, \"field.name_en\")}" }
|
||||
td { class: if name_en == "\u{2014}" { "placeholder" } else { "" }, "{name_en}" }
|
||||
}
|
||||
tr {
|
||||
th { "Name DE" }
|
||||
th { "{t(l, \"field.name_de\")}" }
|
||||
td { class: if name_de == "\u{2014}" { "placeholder" } else { "" }, "{name_de}" }
|
||||
}
|
||||
tr {
|
||||
th { "Scientific Name" }
|
||||
th { "{t(l, \"field.scientific_name\")}" }
|
||||
td { class: if name_sci == "\u{2014}" { "placeholder" } else { "" },
|
||||
if name_sci != "\u{2014}" {
|
||||
em { "{name_sci}" }
|
||||
@@ -513,7 +514,7 @@ pub fn CultivarDetail(slug: String) -> Element {
|
||||
}
|
||||
}
|
||||
tr {
|
||||
th { "Species" }
|
||||
th { "{t(l, \"field.species\")}" }
|
||||
td {
|
||||
if let Some(Some(ref sp)) = *species_data.read() {
|
||||
Link { to: Route::SpeciesDetail { slug: sp.slug.clone() },
|
||||
@@ -530,15 +531,15 @@ pub fn CultivarDetail(slug: String) -> Element {
|
||||
}
|
||||
}
|
||||
tr {
|
||||
th { "Description" }
|
||||
th { "{t(l, \"field.description\")}" }
|
||||
td { class: if desc == "\u{2014}" { "placeholder" } else { "" }, "{desc}" }
|
||||
}
|
||||
tr {
|
||||
th { "Organic" }
|
||||
th { "{t(l, \"field.organic\")}" }
|
||||
td { "{organic}" }
|
||||
}
|
||||
tr {
|
||||
th { "Perennial" }
|
||||
th { "{t(l, \"field.perennial\")}" }
|
||||
td { "{perennial}" }
|
||||
}
|
||||
}
|
||||
@@ -547,35 +548,35 @@ pub fn CultivarDetail(slug: String) -> Element {
|
||||
|
||||
// Card 2: Planting Schedule
|
||||
div { class: "detail-card",
|
||||
div { class: "detail-card-header", "Planting Schedule" }
|
||||
div { class: "detail-card-header", "{t(l, \"card.planting_schedule\")}" }
|
||||
table { class: "attr-table",
|
||||
tbody {
|
||||
tr {
|
||||
th { "Indoor Sowing" }
|
||||
th { "{t(l, \"cal.indoor_sowing\")}" }
|
||||
td { class: if indoor == "\u{2014}" { "placeholder" } else { "" }, "{indoor}" }
|
||||
}
|
||||
tr {
|
||||
th { "Direct Sowing" }
|
||||
th { "{t(l, \"cal.direct_sowing\")}" }
|
||||
td { class: if direct == "\u{2014}" { "placeholder" } else { "" }, "{direct}" }
|
||||
}
|
||||
tr {
|
||||
th { "Transplanting" }
|
||||
th { "{t(l, \"cal.transplanting\")}" }
|
||||
td { class: if transplant == "\u{2014}" { "placeholder" } else { "" }, "{transplant}" }
|
||||
}
|
||||
tr {
|
||||
th { "Glasshouse" }
|
||||
th { "{t(l, \"cal.glasshouse\")}" }
|
||||
td { class: if glasshouse == "\u{2014}" { "placeholder" } else { "" }, "{glasshouse}" }
|
||||
}
|
||||
tr {
|
||||
th { "Harvesting" }
|
||||
th { "{t(l, \"cal.harvesting\")}" }
|
||||
td { class: if harvest == "\u{2014}" { "placeholder" } else { "" }, "{harvest}" }
|
||||
}
|
||||
tr {
|
||||
th { "Succession Planting" }
|
||||
th { "{t(l, \"field.succession_planting\")}" }
|
||||
td { class: if succession == "\u{2014}" { "placeholder" } else { "" }, "{succession}" }
|
||||
}
|
||||
tr {
|
||||
th { "Planting Notes" }
|
||||
th { "{t(l, \"field.planting_notes\")}" }
|
||||
td { class: if planting_notes == "\u{2014}" { "placeholder" } else { "" }, "{planting_notes}" }
|
||||
}
|
||||
}
|
||||
@@ -584,7 +585,7 @@ pub fn CultivarDetail(slug: String) -> Element {
|
||||
|
||||
// Card 3: Where to Buy
|
||||
div { class: "detail-card",
|
||||
div { class: "detail-card-header", "Where to Buy" }
|
||||
div { class: "detail-card-header", "{t(l, \"card.where_to_buy\")}" }
|
||||
{
|
||||
let sup_links = suppliers_data.read();
|
||||
let sups = all_suppliers.read();
|
||||
@@ -598,10 +599,10 @@ pub fn CultivarDetail(slug: String) -> Element {
|
||||
table { class: "attr-table",
|
||||
thead {
|
||||
tr {
|
||||
th { "Supplier" }
|
||||
th { "SKU" }
|
||||
th { "Price" }
|
||||
th { "Link" }
|
||||
th { "{t(l, \"field.supplier\")}" }
|
||||
th { "{t(l, \"field.sku\")}" }
|
||||
th { "{t(l, \"field.price\")}" }
|
||||
th { "{t(l, \"field.link\")}" }
|
||||
}
|
||||
}
|
||||
tbody {
|
||||
@@ -625,7 +626,7 @@ pub fn CultivarDetail(slug: String) -> Element {
|
||||
td {
|
||||
if let Some(ref u) = url {
|
||||
if !u.is_empty() {
|
||||
a { href: "{u}", target: "_blank", class: "external-link", "View" }
|
||||
a { href: "{u}", target: "_blank", class: "external-link", "{t(l, \"btn.view\")}" }
|
||||
} else {
|
||||
span { class: "placeholder", "\u{2014}" }
|
||||
}
|
||||
@@ -642,7 +643,7 @@ pub fn CultivarDetail(slug: String) -> Element {
|
||||
}
|
||||
},
|
||||
_ => rsx! {
|
||||
p { class: "placeholder detail-card-empty", "\u{2014} No suppliers linked" }
|
||||
p { class: "placeholder detail-card-empty", "\u{2014} {t(l, \"no_suppliers_linked\")}" }
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -655,7 +656,7 @@ pub fn CultivarDetail(slug: String) -> Element {
|
||||
// Image card
|
||||
if let Some(ref key) = sp_img_key {
|
||||
div { class: "detail-card",
|
||||
div { class: "detail-card-header", "Image" }
|
||||
div { class: "detail-card-header", "{t(l, \"card.image\")}" }
|
||||
div { class: "image-card-body",
|
||||
img { class: "species-image", src: "/img/{key}", alt: "{c.name}" }
|
||||
if let Some(ref img) = sp_primary_img {
|
||||
@@ -673,7 +674,7 @@ pub fn CultivarDetail(slug: String) -> Element {
|
||||
if img.caption.is_some() || img.license.is_some() {
|
||||
" | "
|
||||
}
|
||||
a { href: "{url}", target: "_blank", class: "attribution-link", "Source" }
|
||||
a { href: "{url}", target: "_blank", class: "attribution-link", "{t(l, \"field.source\")}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -683,27 +684,27 @@ pub fn CultivarDetail(slug: String) -> Element {
|
||||
|
||||
// Card 4: Growing Information
|
||||
div { class: "detail-card",
|
||||
div { class: "detail-card-header", "Growing Information" }
|
||||
div { class: "detail-card-header", "{t(l, \"card.growing_info\")}" }
|
||||
table { class: "attr-table",
|
||||
tbody {
|
||||
tr {
|
||||
th { "Growing Time" }
|
||||
th { "{t(l, \"field.growing_time\")}" }
|
||||
td { class: if growing_time == "\u{2014}" { "placeholder" } else { "" }, "{growing_time}" }
|
||||
}
|
||||
tr {
|
||||
th { "Planting Depth" }
|
||||
th { "{t(l, \"field.planting_depth\")}" }
|
||||
td { class: if planting_depth == "\u{2014}" { "placeholder" } else { "" }, "{planting_depth}" }
|
||||
}
|
||||
tr {
|
||||
th { "Row Spacing" }
|
||||
th { "{t(l, \"field.row_spacing\")}" }
|
||||
td { class: if row_spacing == "\u{2014}" { "placeholder" } else { "" }, "{row_spacing}" }
|
||||
}
|
||||
tr {
|
||||
th { "Plant Spacing" }
|
||||
th { "{t(l, \"field.plant_spacing\")}" }
|
||||
td { class: if plant_spacing == "\u{2014}" { "placeholder" } else { "" }, "{plant_spacing}" }
|
||||
}
|
||||
tr {
|
||||
th { "Propagation Methods" }
|
||||
th { "{t(l, \"field.propagation\")}" }
|
||||
td { class: if propagation == "\u{2014}" { "placeholder" } else { "" }, "{propagation}" }
|
||||
}
|
||||
}
|
||||
@@ -712,19 +713,19 @@ pub fn CultivarDetail(slug: String) -> Element {
|
||||
|
||||
// Card 5: Germination
|
||||
div { class: "detail-card",
|
||||
div { class: "detail-card-header", "Germination" }
|
||||
div { class: "detail-card-header", "{t(l, \"card.germination\")}" }
|
||||
table { class: "attr-table",
|
||||
tbody {
|
||||
tr {
|
||||
th { "Days to Germination" }
|
||||
th { "{t(l, \"field.days_to_germ\")}" }
|
||||
td { class: if days_germ == "\u{2014}" { "placeholder" } else { "" }, "{days_germ}" }
|
||||
}
|
||||
tr {
|
||||
th { "Germination Temp" }
|
||||
th { "{t(l, \"field.germ_temp\")}" }
|
||||
td { class: if germ_temp == "\u{2014}" { "placeholder" } else { "" }, "{germ_temp}" }
|
||||
}
|
||||
tr {
|
||||
th { "Light Requirement" }
|
||||
th { "{t(l, \"field.light_req\")}" }
|
||||
if light_req != "\u{2014}" {
|
||||
td { "{light_req}" }
|
||||
} else if let Some(ref fb) = sp_light_fb {
|
||||
@@ -737,15 +738,15 @@ pub fn CultivarDetail(slug: String) -> Element {
|
||||
}
|
||||
}
|
||||
tr {
|
||||
th { "Stratification Required" }
|
||||
th { "{t(l, \"field.stratification\")}" }
|
||||
td { class: if strat_req == "\u{2014}" { "placeholder" } else { "" }, "{strat_req}" }
|
||||
}
|
||||
tr {
|
||||
th { "Stratification Days" }
|
||||
th { "{t(l, \"field.stratification_days\")}" }
|
||||
td { class: if strat_days == "\u{2014}" { "placeholder" } else { "" }, "{strat_days}" }
|
||||
}
|
||||
tr {
|
||||
th { "Scarification Required" }
|
||||
th { "{t(l, \"field.scarification\")}" }
|
||||
td { class: if scar_req == "\u{2014}" { "placeholder" } else { "" }, "{scar_req}" }
|
||||
}
|
||||
}
|
||||
@@ -754,23 +755,23 @@ pub fn CultivarDetail(slug: String) -> Element {
|
||||
|
||||
// Card 6: Climate & Environment
|
||||
div { class: "detail-card",
|
||||
div { class: "detail-card-header", "Climate & Environment" }
|
||||
div { class: "detail-card-header", "{t(l, \"card.climate\")}" }
|
||||
table { class: "attr-table",
|
||||
tbody {
|
||||
tr {
|
||||
th { "Min Temp" }
|
||||
th { "{t(l, \"field.min_temp\")}" }
|
||||
td { class: if min_temp == "\u{2014}" { "placeholder" } else { "" }, "{min_temp}" }
|
||||
}
|
||||
tr {
|
||||
th { "Max Temp" }
|
||||
th { "{t(l, \"field.max_temp\")}" }
|
||||
td { class: if max_temp == "\u{2014}" { "placeholder" } else { "" }, "{max_temp}" }
|
||||
}
|
||||
tr {
|
||||
th { "Humidity" }
|
||||
th { "{t(l, \"field.humidity\")}" }
|
||||
td { class: if humidity == "\u{2014}" { "placeholder" } else { "" }, "{humidity}" }
|
||||
}
|
||||
tr {
|
||||
th { "Light" }
|
||||
th { "{t(l, \"field.light\")}" }
|
||||
if light != "\u{2014}" {
|
||||
td { "{light}" }
|
||||
} else if let Some(ref fb) = sp_light_fb {
|
||||
@@ -783,7 +784,7 @@ pub fn CultivarDetail(slug: String) -> Element {
|
||||
}
|
||||
}
|
||||
tr {
|
||||
th { "Frost Tolerance" }
|
||||
th { "{t(l, \"field.frost_tolerance\")}" }
|
||||
if frost_tol != "\u{2014}" {
|
||||
td { "{frost_tol}" }
|
||||
} else if let Some(ref fb) = sp_frost_fb {
|
||||
@@ -796,16 +797,16 @@ pub fn CultivarDetail(slug: String) -> Element {
|
||||
}
|
||||
}
|
||||
tr {
|
||||
th { "Min Light Hours/Day" }
|
||||
th { "{t(l, \"field.min_light_hours\")}" }
|
||||
td { class: if min_light_h == "\u{2014}" { "placeholder" } else { "" }, "{min_light_h}" }
|
||||
}
|
||||
tr {
|
||||
th { "Optimal Light Hours/Day" }
|
||||
th { "{t(l, \"field.opt_light_hours\")}" }
|
||||
td { class: if opt_light_h == "\u{2014}" { "placeholder" } else { "" }, "{opt_light_h}" }
|
||||
}
|
||||
if let Some(ref fb) = sp_drought_fb {
|
||||
tr {
|
||||
th { "Drought Tolerance" }
|
||||
th { "{t(l, \"field.drought_tolerance\")}" }
|
||||
td {
|
||||
span { class: "estimated-value", "{fb}" }
|
||||
span { class: "estimated-tag", "~ species" }
|
||||
@@ -814,7 +815,7 @@ pub fn CultivarDetail(slug: String) -> Element {
|
||||
}
|
||||
if let Some(ref fb) = sp_usda_fb {
|
||||
tr {
|
||||
th { "USDA Hardiness Zone" }
|
||||
th { "{t(l, \"field.usda_hardiness_zone\")}" }
|
||||
td {
|
||||
span { class: "estimated-value", "{fb}" }
|
||||
span { class: "estimated-tag", "~ species" }
|
||||
@@ -827,7 +828,7 @@ pub fn CultivarDetail(slug: String) -> Element {
|
||||
|
||||
// Card 7: Species Information
|
||||
div { class: "detail-card",
|
||||
div { class: "detail-card-header", "Species Information" }
|
||||
div { class: "detail-card-header", "{t(l, \"card.species_info\")}" }
|
||||
{
|
||||
let sp_read = species_data.read();
|
||||
match &*sp_read {
|
||||
@@ -841,8 +842,8 @@ pub fn CultivarDetail(slug: String) -> Element {
|
||||
let sp_layer = opt_str(&sp.plant_layer);
|
||||
let sp_drought = opt_str(&sp.drought_tolerance);
|
||||
let sp_usda = opt_str(&sp.hardiness_zone_usda);
|
||||
let sp_nfix = opt_bool(sp.nitrogen_fixer);
|
||||
let sp_dynacc = opt_bool(sp.dynamic_accumulator);
|
||||
let sp_nfix = opt_bool_t(sp.nitrogen_fixer, l);
|
||||
let sp_dynacc = opt_bool_t(sp.dynamic_accumulator, l);
|
||||
let sp_food = pick_desc(¤t_lang, &sp.food_uses_de, &sp.food_uses_en, &sp.food_uses);
|
||||
let sp_med = pick_desc(¤t_lang, &sp.medicinal_uses_de, &sp.medicinal_uses_en, &sp.medicinal_uses);
|
||||
let sp_other = pick_desc(¤t_lang, &sp.other_uses_de, &sp.other_uses_en, &sp.other_uses);
|
||||
@@ -852,47 +853,47 @@ pub fn CultivarDetail(slug: String) -> Element {
|
||||
table { class: "attr-table",
|
||||
tbody {
|
||||
tr {
|
||||
th { "Plant Layer" }
|
||||
th { "{t(l, \"field.plant_layer\")}" }
|
||||
td { class: if sp_layer == "\u{2014}" { "placeholder" } else { "" }, "{sp_layer}" }
|
||||
}
|
||||
tr {
|
||||
th { "Drought Tolerance" }
|
||||
th { "{t(l, \"field.drought_tolerance\")}" }
|
||||
td { class: if sp_drought == "\u{2014}" { "placeholder" } else { "" }, "{sp_drought}" }
|
||||
}
|
||||
tr {
|
||||
th { "USDA Zone" }
|
||||
th { "{t(l, \"field.usda_zone\")}" }
|
||||
td { class: if sp_usda == "\u{2014}" { "placeholder" } else { "" }, "{sp_usda}" }
|
||||
}
|
||||
tr {
|
||||
th { "pH Range" }
|
||||
th { "{t(l, \"field.ph_range\")}" }
|
||||
td { class: if ph_range == "\u{2014}" { "placeholder" } else { "" }, "{ph_range}" }
|
||||
}
|
||||
tr {
|
||||
th { "Nitrogen Fixer" }
|
||||
th { "{t(l, \"field.nitrogen_fixer\")}" }
|
||||
td { class: if sp_nfix == "\u{2014}" { "placeholder" } else { "" }, "{sp_nfix}" }
|
||||
}
|
||||
tr {
|
||||
th { "Dynamic Accumulator" }
|
||||
th { "{t(l, \"field.dynamic_acc\")}" }
|
||||
td { class: if sp_dynacc == "\u{2014}" { "placeholder" } else { "" }, "{sp_dynacc}" }
|
||||
}
|
||||
tr {
|
||||
th { "Food Uses" }
|
||||
th { "{t(l, \"field.food_uses\")}" }
|
||||
td { class: if sp_food == "\u{2014}" { "placeholder" } else { "" }, "{sp_food}" }
|
||||
}
|
||||
tr {
|
||||
th { "Medicinal Uses" }
|
||||
th { "{t(l, \"field.medicinal_uses\")}" }
|
||||
td { class: if sp_med == "\u{2014}" { "placeholder" } else { "" }, "{sp_med}" }
|
||||
}
|
||||
tr {
|
||||
th { "Other Uses" }
|
||||
th { "{t(l, \"field.other_uses\")}" }
|
||||
td { class: if sp_other == "\u{2014}" { "placeholder" } else { "" }, "{sp_other}" }
|
||||
}
|
||||
tr {
|
||||
th { "Wildlife Value" }
|
||||
th { "{t(l, \"field.wildlife_value\")}" }
|
||||
td { class: if sp_wildlife == "\u{2014}" { "placeholder" } else { "" }, "{sp_wildlife}" }
|
||||
}
|
||||
tr {
|
||||
th { "Native Range" }
|
||||
th { "{t(l, \"field.native_range\")}" }
|
||||
td { class: if sp_native == "\u{2014}" { "placeholder" } else { "" }, "{sp_native}" }
|
||||
}
|
||||
}
|
||||
@@ -900,7 +901,7 @@ pub fn CultivarDetail(slug: String) -> Element {
|
||||
}
|
||||
},
|
||||
_ => rsx! {
|
||||
p { class: "placeholder detail-card-empty", "Loading species data\u{2026}" }
|
||||
p { class: "placeholder detail-card-empty", "{t(l, \"loading_species_data\")}" }
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -910,9 +911,7 @@ pub fn CultivarDetail(slug: String) -> Element {
|
||||
|
||||
if has_any_fallback {
|
||||
p { class: "species-fallback-note",
|
||||
"Values marked "
|
||||
span { class: "estimated-tag", "~ species" }
|
||||
" are estimated from species-level data."
|
||||
"{t(l, \"estimated_note\")}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user