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:
+105
-101
@@ -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};
|
||||
use crate::i18n::{pick_desc, pick_name, t};
|
||||
|
||||
const STORAGE_KEY_COLS: &str = "herbapi_species_cols";
|
||||
const STORAGE_KEY_PP: &str = "herbapi_species_pp";
|
||||
@@ -93,16 +93,17 @@ pub fn SpeciesList() -> Element {
|
||||
|
||||
let current_page = *page.read();
|
||||
let is_table = *table_view.read();
|
||||
let l = lang.read().clone();
|
||||
|
||||
rsx! {
|
||||
div { class: "page",
|
||||
h1 { "Species" }
|
||||
h1 { "{t(&l, \"page.species\")}" }
|
||||
|
||||
div { class: "table-toolbar",
|
||||
div { class: "search-bar",
|
||||
input {
|
||||
r#type: "text",
|
||||
placeholder: "Search species...",
|
||||
placeholder: "{t(&l, \"search.placeholder_species\")}",
|
||||
value: "{search}",
|
||||
oninput: move |e| {
|
||||
search.set(e.value());
|
||||
@@ -118,7 +119,7 @@ pub fn SpeciesList() -> Element {
|
||||
table_view.set(new_val);
|
||||
let _ = LocalStorage::set(STORAGE_KEY_VIEW, new_val);
|
||||
},
|
||||
if is_table { "Card View" } else { "Table View" }
|
||||
if is_table { "{t(&l, \"btn.card_view\")}" } else { "{t(&l, \"btn.table_view\")}" }
|
||||
}
|
||||
PerPageSelector {
|
||||
per_page: per_page,
|
||||
@@ -131,14 +132,14 @@ pub fn SpeciesList() -> Element {
|
||||
// Filter bar
|
||||
div { class: "filter-bar",
|
||||
div { class: "filter-group",
|
||||
label { "Layer" }
|
||||
label { "{t(&l, \"filter.layer\")}" }
|
||||
select {
|
||||
value: "{filter_layer}",
|
||||
onchange: move |e| {
|
||||
filter_layer.set(e.value());
|
||||
page.set(1);
|
||||
},
|
||||
option { value: "", "All" }
|
||||
option { value: "", "{t(&l, \"filter.all\")}" }
|
||||
option { value: "canopy", "Canopy" }
|
||||
option { value: "understory", "Understory" }
|
||||
option { value: "shrub", "Shrub" }
|
||||
@@ -149,14 +150,14 @@ pub fn SpeciesList() -> Element {
|
||||
}
|
||||
}
|
||||
div { class: "filter-group",
|
||||
label { "Drought Tol." }
|
||||
label { "{t(&l, \"filter.drought_tol\")}" }
|
||||
select {
|
||||
value: "{filter_drought}",
|
||||
onchange: move |e| {
|
||||
filter_drought.set(e.value());
|
||||
page.set(1);
|
||||
},
|
||||
option { value: "", "All" }
|
||||
option { value: "", "{t(&l, \"filter.all\")}" }
|
||||
option { value: "none", "None" }
|
||||
option { value: "low", "Low" }
|
||||
option { value: "moderate", "Moderate" }
|
||||
@@ -174,7 +175,7 @@ pub fn SpeciesList() -> Element {
|
||||
page.set(1);
|
||||
},
|
||||
}
|
||||
"N-Fixer"
|
||||
"{t(&l, \"field.n_fixer\")}"
|
||||
}
|
||||
}
|
||||
div { class: "filter-group filter-checkbox",
|
||||
@@ -187,7 +188,7 @@ pub fn SpeciesList() -> Element {
|
||||
page.set(1);
|
||||
},
|
||||
}
|
||||
"Dyn. Accumulator"
|
||||
"{t(&l, \"field.dyn_accum\")}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -201,7 +202,7 @@ pub fn SpeciesList() -> Element {
|
||||
}
|
||||
|
||||
match &*species.read() {
|
||||
None => rsx! { p { "Loading..." } },
|
||||
None => rsx! { p { "{t(&l, \"loading\")}" } },
|
||||
Some(Err(e)) => rsx! { p { class: "error", "Error: {e}" } },
|
||||
Some(Ok(data)) => {
|
||||
let fmap_read = family_map.read();
|
||||
@@ -212,7 +213,7 @@ pub fn SpeciesList() -> Element {
|
||||
};
|
||||
|
||||
rsx! {
|
||||
p { class: "result-count", "{data.total} species" }
|
||||
p { class: "result-count", "{data.total} {t(&l, \"nav.species\").to_lowercase()}" }
|
||||
|
||||
if is_table {
|
||||
{
|
||||
@@ -223,49 +224,49 @@ pub fn SpeciesList() -> Element {
|
||||
thead {
|
||||
tr {
|
||||
if is_col_visible(&vis, "name_scientific") {
|
||||
th { "Scientific Name" }
|
||||
th { "{t(&l, \"field.scientific_name\")}" }
|
||||
}
|
||||
if is_col_visible(&vis, "common_name") {
|
||||
th { "Common Name" }
|
||||
th { "{t(&l, \"field.common_name\")}" }
|
||||
}
|
||||
if is_col_visible(&vis, "name_de") {
|
||||
th { "German" }
|
||||
th { "{t(&l, \"field.name_de\")}" }
|
||||
}
|
||||
if is_col_visible(&vis, "name_en") {
|
||||
th { "English" }
|
||||
th { "{t(&l, \"field.name_en\")}" }
|
||||
}
|
||||
if is_col_visible(&vis, "family") {
|
||||
th { "Family" }
|
||||
th { "{t(&l, \"field.family\")}" }
|
||||
}
|
||||
if is_col_visible(&vis, "plant_layer") {
|
||||
th { "Layer" }
|
||||
th { "{t(&l, \"field.layer\")}" }
|
||||
}
|
||||
if is_col_visible(&vis, "nitrogen_fixer") {
|
||||
th { "N-Fixer" }
|
||||
th { "{t(&l, \"field.n_fixer\")}" }
|
||||
}
|
||||
if is_col_visible(&vis, "dynamic_accumulator") {
|
||||
th { "Dyn. Accum." }
|
||||
th { "{t(&l, \"field.dyn_accum\")}" }
|
||||
}
|
||||
if is_col_visible(&vis, "food_uses") {
|
||||
th { "Food Uses" }
|
||||
th { "{t(&l, \"field.food_uses\")}" }
|
||||
}
|
||||
if is_col_visible(&vis, "edibility_rating") {
|
||||
th { "Edibility" }
|
||||
th { "{t(&l, \"field.edibility\")}" }
|
||||
}
|
||||
if is_col_visible(&vis, "drought_tolerance") {
|
||||
th { "Drought Tol." }
|
||||
th { "{t(&l, \"field.drought_tol\")}" }
|
||||
}
|
||||
if is_col_visible(&vis, "hardiness_zone_usda") {
|
||||
th { "USDA Zone" }
|
||||
th { "{t(&l, \"field.usda_zone\")}" }
|
||||
}
|
||||
if is_col_visible(&vis, "nectar_value") {
|
||||
th { "Nectar" }
|
||||
th { "{t(&l, \"field.nectar\")}" }
|
||||
}
|
||||
if is_col_visible(&vis, "wild_bee_count") {
|
||||
th { "Wild Bees" }
|
||||
th { "{t(&l, \"field.wild_bees\")}" }
|
||||
}
|
||||
if is_col_visible(&vis, "native_status") {
|
||||
th { "Native Status" }
|
||||
th { "{t(&l, \"field.native_status\")}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -301,7 +302,7 @@ pub fn SpeciesList() -> Element {
|
||||
if is_col_visible(&vis, "nitrogen_fixer") {
|
||||
td {
|
||||
match s.nitrogen_fixer {
|
||||
Some(true) => rsx! { span { class: "badge organic", "Yes" } },
|
||||
Some(true) => rsx! { span { class: "badge organic", "{t(&l, \"yes\")}" } },
|
||||
Some(false) => rsx! { "-" },
|
||||
None => rsx! { "-" },
|
||||
}
|
||||
@@ -310,7 +311,7 @@ pub fn SpeciesList() -> Element {
|
||||
if is_col_visible(&vis, "dynamic_accumulator") {
|
||||
td {
|
||||
match s.dynamic_accumulator {
|
||||
Some(true) => rsx! { span { class: "badge organic", "Yes" } },
|
||||
Some(true) => rsx! { span { class: "badge organic", "{t(&l, \"yes\")}" } },
|
||||
Some(false) => rsx! { "-" },
|
||||
None => rsx! { "-" },
|
||||
}
|
||||
@@ -401,13 +402,13 @@ pub fn SpeciesList() -> 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\")}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -461,11 +462,12 @@ pub fn SpeciesDetail(slug: String) -> Element {
|
||||
rsx! {
|
||||
div { class: "page species-detail",
|
||||
match &*species.read() {
|
||||
None => rsx! { p { "Loading..." } },
|
||||
None => rsx! { p { "{t(&lang.read(), \"loading\")}" } },
|
||||
Some(Err(e)) => rsx! { p { class: "error", "Error: {e}" } },
|
||||
Some(Ok(s)) => {
|
||||
let species_slug = s.slug.clone();
|
||||
let current_lang = lang.read().clone();
|
||||
let l = &*current_lang;
|
||||
|
||||
// Helper closures to format fields
|
||||
let os = |v: &Option<String>| -> String {
|
||||
@@ -476,8 +478,8 @@ pub fn SpeciesDetail(slug: String) -> Element {
|
||||
};
|
||||
let ob = |v: Option<bool>| -> String {
|
||||
match v {
|
||||
Some(true) => "Yes".to_string(),
|
||||
Some(false) => "No".to_string(),
|
||||
Some(true) => t(l, "yes").to_string(),
|
||||
Some(false) => t(l, "no").to_string(),
|
||||
None => "\u{2014}".to_string(),
|
||||
}
|
||||
};
|
||||
@@ -565,27 +567,27 @@ pub fn SpeciesDetail(slug: String) -> Element {
|
||||
|
||||
// Card 1: Species Details
|
||||
div { class: "detail-card",
|
||||
div { class: "detail-card-header", "Species Details" }
|
||||
div { class: "detail-card-header", "{t(l, \"card.species_details\")}" }
|
||||
table { class: "attr-table",
|
||||
tbody {
|
||||
tr {
|
||||
th { "Scientific Name" }
|
||||
th { "{t(l, \"field.scientific_name\")}" }
|
||||
td { em { "{s.name_scientific}" } }
|
||||
}
|
||||
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 == em { "placeholder" } else { "" }, "{name_en}" }
|
||||
}
|
||||
tr {
|
||||
th { "Name DE" }
|
||||
th { "{t(l, \"field.name_de\")}" }
|
||||
td { class: if name_de == em { "placeholder" } else { "" }, "{name_de}" }
|
||||
}
|
||||
tr {
|
||||
th { "Family" }
|
||||
th { "{t(l, \"field.family\")}" }
|
||||
td {
|
||||
if let Some(Some(ref fam)) = *family_data.read() {
|
||||
Link { to: Route::FamilyDetail { slug: fam.slug.clone() },
|
||||
@@ -597,7 +599,7 @@ pub fn SpeciesDetail(slug: String) -> Element {
|
||||
}
|
||||
}
|
||||
tr {
|
||||
th { "Description" }
|
||||
th { "{t(l, \"field.description\")}" }
|
||||
td { class: if desc == em { "placeholder" } else { "" }, "{desc}" }
|
||||
}
|
||||
}
|
||||
@@ -606,23 +608,23 @@ pub fn SpeciesDetail(slug: String) -> Element {
|
||||
|
||||
// Card 2: Uses
|
||||
div { class: "detail-card",
|
||||
div { class: "detail-card-header", "Uses" }
|
||||
div { class: "detail-card-header", "{t(l, \"card.uses\")}" }
|
||||
table { class: "attr-table",
|
||||
tbody {
|
||||
tr {
|
||||
th { "Food Uses" }
|
||||
th { "{t(l, \"field.food_uses\")}" }
|
||||
td { class: if food == em { "placeholder" } else { "" }, "{food}" }
|
||||
}
|
||||
tr {
|
||||
th { "Medicinal Uses" }
|
||||
th { "{t(l, \"field.medicinal_uses\")}" }
|
||||
td { class: if med == em { "placeholder" } else { "" }, "{med}" }
|
||||
}
|
||||
tr {
|
||||
th { "Other Uses" }
|
||||
th { "{t(l, \"field.other_uses\")}" }
|
||||
td { class: if other == em { "placeholder" } else { "" }, "{other}" }
|
||||
}
|
||||
tr {
|
||||
th { "Edibility Rating" }
|
||||
th { "{t(l, \"field.edibility\")}" }
|
||||
td { class: if edibility == em { "placeholder" } else { "" }, "{edibility}" }
|
||||
}
|
||||
}
|
||||
@@ -631,27 +633,27 @@ pub fn SpeciesDetail(slug: String) -> Element {
|
||||
|
||||
// Card 3: Ecology
|
||||
div { class: "detail-card",
|
||||
div { class: "detail-card-header", "Ecology" }
|
||||
div { class: "detail-card-header", "{t(l, \"card.ecology\")}" }
|
||||
table { class: "attr-table",
|
||||
tbody {
|
||||
tr {
|
||||
th { "Plant Layer" }
|
||||
th { "{t(l, \"field.plant_layer\")}" }
|
||||
td { class: if layer == em { "placeholder" } else { "" }, "{layer}" }
|
||||
}
|
||||
tr {
|
||||
th { "Succession Stage" }
|
||||
th { "{t(l, \"field.succession\")}" }
|
||||
td { class: if succession == em { "placeholder" } else { "" }, "{succession}" }
|
||||
}
|
||||
tr {
|
||||
th { "Wildlife Value" }
|
||||
th { "{t(l, \"field.wildlife_value\")}" }
|
||||
td { class: if wildlife == em { "placeholder" } else { "" }, "{wildlife}" }
|
||||
}
|
||||
tr {
|
||||
th { "Native Range" }
|
||||
th { "{t(l, \"field.native_range\")}" }
|
||||
td { class: if native == em { "placeholder" } else { "" }, "{native}" }
|
||||
}
|
||||
tr {
|
||||
th { "Pollination Type" }
|
||||
th { "{t(l, \"field.pollination\")}" }
|
||||
td { class: if pollination == em { "placeholder" } else { "" }, "{pollination}" }
|
||||
}
|
||||
}
|
||||
@@ -665,7 +667,7 @@ pub fn SpeciesDetail(slug: String) -> Element {
|
||||
// Image card
|
||||
if let Some(ref key) = 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: "{s.name_scientific}" }
|
||||
if let Some(ref img) = primary_img {
|
||||
@@ -683,7 +685,7 @@ pub fn SpeciesDetail(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\")}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -693,31 +695,31 @@ pub fn SpeciesDetail(slug: String) -> Element {
|
||||
|
||||
// Card 4: Growing Requirements
|
||||
div { class: "detail-card",
|
||||
div { class: "detail-card-header", "Growing Requirements" }
|
||||
div { class: "detail-card-header", "{t(l, \"card.growing_requirements\")}" }
|
||||
table { class: "attr-table",
|
||||
tbody {
|
||||
tr {
|
||||
th { "Soil Moisture" }
|
||||
th { "{t(l, \"field.soil_moisture\")}" }
|
||||
td { class: if soil_moist == em { "placeholder" } else { "" }, "{soil_moist}" }
|
||||
}
|
||||
tr {
|
||||
th { "pH Range" }
|
||||
th { "{t(l, \"field.ph_range\")}" }
|
||||
td { class: if ph_range == em { "placeholder" } else { "" }, "{ph_range}" }
|
||||
}
|
||||
tr {
|
||||
th { "Drought Tolerance" }
|
||||
th { "{t(l, \"field.drought_tolerance\")}" }
|
||||
td { class: if drought == em { "placeholder" } else { "" }, "{drought}" }
|
||||
}
|
||||
tr {
|
||||
th { "Salt Tolerance" }
|
||||
th { "{t(l, \"field.salt_tolerance\")}" }
|
||||
td { class: if salt == em { "placeholder" } else { "" }, "{salt}" }
|
||||
}
|
||||
tr {
|
||||
th { "USDA Zone" }
|
||||
th { "{t(l, \"field.usda_zone\")}" }
|
||||
td { class: if usda == em { "placeholder" } else { "" }, "{usda}" }
|
||||
}
|
||||
tr {
|
||||
th { "AT Zone" }
|
||||
th { "{t(l, \"field.at_zone\")}" }
|
||||
td { class: if at_zone == em { "placeholder" } else { "" }, "{at_zone}" }
|
||||
}
|
||||
}
|
||||
@@ -726,39 +728,39 @@ pub fn SpeciesDetail(slug: String) -> Element {
|
||||
|
||||
// Card 5: Permaculture
|
||||
div { class: "detail-card",
|
||||
div { class: "detail-card-header", "Permaculture" }
|
||||
div { class: "detail-card-header", "{t(l, \"card.permaculture\")}" }
|
||||
table { class: "attr-table",
|
||||
tbody {
|
||||
tr {
|
||||
th { "Nitrogen Fixer" }
|
||||
th { "{t(l, \"field.nitrogen_fixer\")}" }
|
||||
td { class: if n_fixer == em { "placeholder" } else { "" }, "{n_fixer}" }
|
||||
}
|
||||
tr {
|
||||
th { "Dynamic Accumulator" }
|
||||
th { "{t(l, \"field.dynamic_acc\")}" }
|
||||
td { class: if dyn_acc == em { "placeholder" } else { "" }, "{dyn_acc}" }
|
||||
}
|
||||
tr {
|
||||
th { "Attracts Pollinators" }
|
||||
th { "{t(l, \"field.attracts_pollinators\")}" }
|
||||
td { class: if pollinators == em { "placeholder" } else { "" }, "{pollinators}" }
|
||||
}
|
||||
tr {
|
||||
th { "Attracts Beneficial Insects" }
|
||||
th { "{t(l, \"field.attracts_beneficial\")}" }
|
||||
td { class: if beneficial == em { "placeholder" } else { "" }, "{beneficial}" }
|
||||
}
|
||||
tr {
|
||||
th { "Mulch Plant" }
|
||||
th { "{t(l, \"field.mulch_plant\")}" }
|
||||
td { class: if mulch == em { "placeholder" } else { "" }, "{mulch}" }
|
||||
}
|
||||
tr {
|
||||
th { "Ground Cover Quality" }
|
||||
th { "{t(l, \"field.ground_cover_quality\")}" }
|
||||
td { class: if gc_quality == em { "placeholder" } else { "" }, "{gc_quality}" }
|
||||
}
|
||||
tr {
|
||||
th { "Allelopathic" }
|
||||
th { "{t(l, \"field.allelopathic\")}" }
|
||||
td { class: if allelo == em { "placeholder" } else { "" }, "{allelo}" }
|
||||
}
|
||||
tr {
|
||||
th { "Guild Role" }
|
||||
th { "{t(l, \"field.guild_role\")}" }
|
||||
td { class: if guild == em { "placeholder" } else { "" }, "{guild}" }
|
||||
}
|
||||
}
|
||||
@@ -767,11 +769,11 @@ pub fn SpeciesDetail(slug: String) -> Element {
|
||||
|
||||
// Card 6: Wildlife & Ecology
|
||||
div { class: "detail-card",
|
||||
div { class: "detail-card-header", "Wildlife & Ecology" }
|
||||
div { class: "detail-card-header", "{t(l, \"card.wildlife\")}" }
|
||||
table { class: "attr-table",
|
||||
tbody {
|
||||
tr {
|
||||
th { "Nectar Value" }
|
||||
th { "{t(l, \"field.nectar\")}" }
|
||||
td {
|
||||
match s.nectar_value {
|
||||
Some(v) => rsx! {
|
||||
@@ -788,7 +790,7 @@ pub fn SpeciesDetail(slug: String) -> Element {
|
||||
}
|
||||
}
|
||||
tr {
|
||||
th { "Pollen Value" }
|
||||
th { "{t(l, \"field.pollen\")}" }
|
||||
td {
|
||||
match s.pollen_value {
|
||||
Some(v) => rsx! {
|
||||
@@ -805,69 +807,69 @@ pub fn SpeciesDetail(slug: String) -> Element {
|
||||
}
|
||||
}
|
||||
tr {
|
||||
th { "Wild Bees" }
|
||||
th { "{t(l, \"field.wild_bees\")}" }
|
||||
td {
|
||||
match (s.wild_bee_count, s.wild_bee_specialist_count) {
|
||||
(Some(total), Some(spec)) => rsx! { "{total} species ({spec} specialists)" },
|
||||
(Some(total), None) => rsx! { "{total} species" },
|
||||
(Some(total), Some(spec)) => rsx! { "{total} {t(l, \"species_species\")} ({spec} specialists)" },
|
||||
(Some(total), None) => rsx! { "{total} {t(l, \"species_species\")}" },
|
||||
_ => rsx! { span { class: "placeholder", "\u{2014}" } },
|
||||
}
|
||||
}
|
||||
}
|
||||
tr {
|
||||
th { "Butterflies & Moths" }
|
||||
th { "{t(l, \"field.butterflies\")}" }
|
||||
td {
|
||||
match (s.butterfly_moth_count, s.caterpillar_host_count) {
|
||||
(Some(bm), Some(ch)) => rsx! { "{bm} species ({ch} caterpillar hosts)" },
|
||||
(Some(bm), None) => rsx! { "{bm} species" },
|
||||
(Some(bm), Some(ch)) => rsx! { "{bm} {t(l, \"species_species\")} ({ch} caterpillar hosts)" },
|
||||
(Some(bm), None) => rsx! { "{bm} {t(l, \"species_species\")}" },
|
||||
_ => rsx! { span { class: "placeholder", "\u{2014}" } },
|
||||
}
|
||||
}
|
||||
}
|
||||
if s.caterpillar_specialist_count.is_some() {
|
||||
tr {
|
||||
th { "Caterpillar Specialists" }
|
||||
th { "{t(l, \"field.caterpillar_specialists\")}" }
|
||||
td { "{s.caterpillar_specialist_count.unwrap()}" }
|
||||
}
|
||||
}
|
||||
tr {
|
||||
th { "Hoverflies" }
|
||||
th { "{t(l, \"field.hoverflies\")}" }
|
||||
td {
|
||||
match s.hoverfly_count {
|
||||
Some(v) => rsx! { "{v} species" },
|
||||
Some(v) => rsx! { "{v} {t(l, \"species_species\")}" },
|
||||
None => rsx! { span { class: "placeholder", "\u{2014}" } },
|
||||
}
|
||||
}
|
||||
}
|
||||
tr {
|
||||
th { "Beetles" }
|
||||
th { "{t(l, \"field.beetles\")}" }
|
||||
td {
|
||||
match s.beetle_count {
|
||||
Some(v) => rsx! { "{v} species" },
|
||||
Some(v) => rsx! { "{v} {t(l, \"species_species\")}" },
|
||||
None => rsx! { span { class: "placeholder", "\u{2014}" } },
|
||||
}
|
||||
}
|
||||
}
|
||||
tr {
|
||||
th { "Birds" }
|
||||
th { "{t(l, \"field.birds\")}" }
|
||||
td {
|
||||
match s.bird_count {
|
||||
Some(v) => rsx! { "{v} species" },
|
||||
Some(v) => rsx! { "{v} {t(l, \"species_species\")}" },
|
||||
None => rsx! { span { class: "placeholder", "\u{2014}" } },
|
||||
}
|
||||
}
|
||||
}
|
||||
tr {
|
||||
th { "Mammals" }
|
||||
th { "{t(l, \"field.mammals\")}" }
|
||||
td {
|
||||
match s.mammal_count {
|
||||
Some(v) => rsx! { "{v} species" },
|
||||
Some(v) => rsx! { "{v} {t(l, \"species_species\")}" },
|
||||
None => rsx! { span { class: "placeholder", "\u{2014}" } },
|
||||
}
|
||||
}
|
||||
}
|
||||
tr {
|
||||
th { "Native Status" }
|
||||
th { "{t(l, \"field.native_status\")}" }
|
||||
td {
|
||||
match &s.native_status {
|
||||
Some(ns) if !ns.is_empty() => {
|
||||
@@ -884,7 +886,7 @@ pub fn SpeciesDetail(slug: String) -> Element {
|
||||
}
|
||||
}
|
||||
tr {
|
||||
th { "NaturaDB Tags" }
|
||||
th { "{t(l, \"field.naturadb_tags\")}" }
|
||||
td {
|
||||
match &s.naturadb_tags {
|
||||
Some(tags) if !tags.is_empty() => {
|
||||
@@ -907,11 +909,11 @@ pub fn SpeciesDetail(slug: String) -> Element {
|
||||
|
||||
// Card 7: External Links
|
||||
div { class: "detail-card",
|
||||
div { class: "detail-card-header", "External Links" }
|
||||
div { class: "detail-card-header", "{t(l, \"card.external_links\")}" }
|
||||
table { class: "attr-table",
|
||||
tbody {
|
||||
tr {
|
||||
th { "Wikidata QID" }
|
||||
th { "{t(l, \"field.wikidata_qid\")}" }
|
||||
td {
|
||||
if let Some(ref q) = qid {
|
||||
if !q.is_empty() {
|
||||
@@ -925,7 +927,7 @@ pub fn SpeciesDetail(slug: String) -> Element {
|
||||
}
|
||||
}
|
||||
tr {
|
||||
th { "GBIF ID" }
|
||||
th { "{t(l, \"field.gbif_id\")}" }
|
||||
td {
|
||||
if let Some(ref g) = gbif {
|
||||
if !g.is_empty() {
|
||||
@@ -939,15 +941,15 @@ pub fn SpeciesDetail(slug: String) -> Element {
|
||||
}
|
||||
}
|
||||
tr {
|
||||
th { "EPPO Code" }
|
||||
th { "{t(l, \"field.eppo_code\")}" }
|
||||
td { class: if eppo == em { "placeholder" } else { "" }, "{eppo}" }
|
||||
}
|
||||
tr {
|
||||
th { "PFAF URL" }
|
||||
th { "{t(l, \"field.pfaf_url\")}" }
|
||||
td {
|
||||
if let Some(ref u) = pfaf {
|
||||
if !u.is_empty() {
|
||||
a { href: "{u}", target: "_blank", class: "external-link", "View on PFAF" }
|
||||
a { href: "{u}", target: "_blank", class: "external-link", "{t(l, \"field.view_on_pfaf\")}" }
|
||||
} else {
|
||||
span { class: "placeholder", "\u{2014}" }
|
||||
}
|
||||
@@ -963,7 +965,7 @@ pub fn SpeciesDetail(slug: String) -> Element {
|
||||
}
|
||||
|
||||
// Cultivars for this species (below the two-column layout)
|
||||
h2 { "Cultivars" }
|
||||
h2 { "{t(l, \"card.cultivars\")}" }
|
||||
CultivarListForSpecies { species_slug: species_slug }
|
||||
}
|
||||
},
|
||||
@@ -981,13 +983,15 @@ fn CultivarListForSpecies(species_slug: String) -> Element {
|
||||
async move { api::list_cultivars(1, 100, Some(&s), None).await }
|
||||
});
|
||||
|
||||
let l = lang.read().clone();
|
||||
|
||||
rsx! {
|
||||
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)) => {
|
||||
if data.data.is_empty() {
|
||||
rsx! { p { class: "empty", "No cultivars yet." } }
|
||||
rsx! { p { class: "empty", "{t(&l, \"no_cultivars\")}" } }
|
||||
} else {
|
||||
rsx! {
|
||||
div { class: "card-grid",
|
||||
@@ -1004,7 +1008,7 @@ fn CultivarListForSpecies(species_slug: String) -> Element {
|
||||
p { class: "card-common", "{common}" }
|
||||
}
|
||||
if c.is_organic {
|
||||
span { class: "badge organic", "Organic" }
|
||||
span { class: "badge organic", "{t(&l, \"field.organic\")}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user