aboutsummaryrefslogtreecommitdiff
path: root/server/src/routes
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/routes')
-rw-r--r--server/src/routes/ui/browser.rs5
-rw-r--r--server/src/routes/ui/home.rs20
-rw-r--r--server/src/routes/ui/layout.rs24
-rw-r--r--server/src/routes/ui/node.rs30
-rw-r--r--server/src/routes/ui/search.rs18
-rw-r--r--server/src/routes/ui/sort.rs8
-rw-r--r--server/src/routes/ui/stats.rs26
7 files changed, 81 insertions, 50 deletions
diff --git a/server/src/routes/ui/browser.rs b/server/src/routes/ui/browser.rs
index 9cc9d88..96c005d 100644
--- a/server/src/routes/ui/browser.rs
+++ b/server/src/routes/ui/browser.rs
@@ -6,7 +6,7 @@
use super::{
account::session::Session,
error::MyError,
- layout::{trs, trsa, DynLayoutPage, LayoutPage},
+ layout::{trs, DynLayoutPage, LayoutPage},
node::NodeCard,
sort::{filter_and_sort_nodes, NodeFilterSort, NodeFilterSortForm, SortOrder, SortProperty},
};
@@ -15,6 +15,7 @@ use crate::{
routes::{api::AcceptJson, locale::AcceptLanguage},
uri,
};
+use jellybase::locale::tr;
use jellycommon::{api::ApiItemsResponse, Visibility};
use rocket::{get, serde::json::Json, Either, State};
@@ -66,7 +67,7 @@ pub fn r_all_items_filter(
li {@NodeCard { node, udata, lang: &lang }}
}}
p.pagecontrols {
- span.current { @trsa(&lang, "page.curr", &[("cur", &(page + 1).to_string()), ("max", &max_page.to_string())]) " " }
+ span.current { @tr(lang, "page.curr").replace("{cur}", &(page + 1).to_string()).replace("{max}", &max_page.to_string()) " " }
@if page > 0 {
a.prev[href=uri!(r_all_items_filter(Some(page - 1), filter.clone()))] { @trs(&lang, "page.prev") } " "
}
diff --git a/server/src/routes/ui/home.rs b/server/src/routes/ui/home.rs
index 9e4035b..817a301 100644
--- a/server/src/routes/ui/home.rs
+++ b/server/src/routes/ui/home.rs
@@ -43,7 +43,7 @@ pub fn r_home(
let mut categories = Vec::<(String, Vec<_>)>::new();
categories.push((
- tr(lang, "home.bin.continue_watching", &[]).to_string(),
+ tr(lang, "home.bin.continue_watching").to_string(),
items
.iter()
.filter(|(_, u)| matches!(u.watched, WatchedState::Progress(_)))
@@ -51,7 +51,7 @@ pub fn r_home(
.collect(),
));
categories.push((
- tr(lang, "home.bin.watchlist", &[]).to_string(),
+ tr(lang, "home.bin.watchlist").to_string(),
items
.iter()
.filter(|(_, u)| matches!(u.watched, WatchedState::Pending))
@@ -64,7 +64,7 @@ pub fn r_home(
items.sort_by_key(|(n, _)| n.release_date.map(|d| -d).unwrap_or(i64::MAX));
categories.push((
- tr(lang, "home.bin.latest_video", &[]).to_string(),
+ tr(lang, "home.bin.latest_video").to_string(),
items
.iter()
.filter(|(n, _)| matches!(n.kind, NodeKind::Video))
@@ -73,7 +73,7 @@ pub fn r_home(
.collect(),
));
categories.push((
- tr(lang, "home.bin.latest_music", &[]).to_string(),
+ tr(lang, "home.bin.latest_music").to_string(),
items
.iter()
.filter(|(n, _)| matches!(n.kind, NodeKind::Music))
@@ -82,7 +82,7 @@ pub fn r_home(
.collect(),
));
categories.push((
- tr(lang, "home.bin.latest_short_form", &[]).to_string(),
+ tr(lang, "home.bin.latest_short_form").to_string(),
items
.iter()
.filter(|(n, _)| matches!(n.kind, NodeKind::ShortFormVideo))
@@ -99,7 +99,7 @@ pub fn r_home(
});
categories.push((
- tr(lang, "home.bin.max_rating", &[]).to_string(),
+ tr(lang, "home.bin.max_rating").to_string(),
items
.iter()
.take(16)
@@ -116,7 +116,7 @@ pub fn r_home(
});
categories.push((
- tr(lang, "home.bin.daily_random", &[]).to_string(),
+ tr(lang, "home.bin.daily_random").to_string(),
(0..16)
.flat_map(|i| Some(items[cheap_daily_random(i).checked_rem(items.len())?].clone()))
.collect(),
@@ -126,7 +126,7 @@ pub fn r_home(
let mut items = items.clone();
items.retain(|(_, u)| matches!(u.watched, WatchedState::Watched));
categories.push((
- tr(lang, "home.bin.watch_again", &[]).to_string(),
+ tr(lang, "home.bin.watch_again").to_string(),
(0..16)
.flat_map(|i| Some(items[cheap_daily_random(i).checked_rem(items.len())?].clone()))
.collect(),
@@ -135,7 +135,7 @@ pub fn r_home(
items.retain(|(n, _)| matches!(n.kind, NodeKind::Music));
categories.push((
- tr(lang, "home.bin.daily_random_music", &[]).to_string(),
+ tr(lang, "home.bin.daily_random_music").to_string(),
(0..16)
.flat_map(|i| Some(items[cheap_daily_random(i).checked_rem(items.len())?].clone()))
.collect(),
@@ -148,7 +148,7 @@ pub fn r_home(
}))
} else {
Either::Left(LayoutPage {
- title: tr(lang, "home", &[]).to_string(),
+ title: tr(lang, "home").to_string(),
content: markup::new! {
h2 { "Explore " @CONF.brand }
ul.children.hlist {@for (node, udata) in &toplevel {
diff --git a/server/src/routes/ui/layout.rs b/server/src/routes/ui/layout.rs
index d0bb780..5ca07ea 100644
--- a/server/src/routes/ui/layout.rs
+++ b/server/src/routes/ui/layout.rs
@@ -56,11 +56,29 @@ impl RenderAttributeValue for TrString<'_> {
false
}
}
-pub fn trsa<'a>(lang: &Language, key: &str, args: &[(&str, &str)]) -> TrString<'a> {
- TrString(tr(*lang, key, args))
+
+pub fn escape(str: &str) -> String {
+ let mut o = String::with_capacity(str.len());
+ let mut last = 0;
+ for (index, byte) in str.bytes().enumerate() {
+ if let Some(esc) = match byte {
+ b'<' => Some("&lt;"),
+ b'>' => Some("&gt;"),
+ b'&' => Some("&amp;"),
+ b'"' => Some("&quot;"),
+ _ => None,
+ } {
+ o += &str[last..index];
+ o += esc;
+ last = index + 1;
+ }
+ }
+ o += &str[last..];
+ o
}
+
pub fn trs<'a>(lang: &Language, key: &str) -> TrString<'a> {
- TrString(tr(*lang, key, &[]))
+ TrString(tr(*lang, key))
}
markup::define! {
diff --git a/server/src/routes/ui/node.rs b/server/src/routes/ui/node.rs
index e94547c..e6f0605 100644
--- a/server/src/routes/ui/node.rs
+++ b/server/src/routes/ui/node.rs
@@ -9,7 +9,7 @@ use super::{
rocket_uri_macro_r_node_thumbnail,
},
error::MyResult,
- layout::{trs, trsa},
+ layout::trs,
sort::{filter_and_sort_nodes, NodeFilterSort, NodeFilterSortForm, SortOrder, SortProperty},
};
use crate::{
@@ -32,7 +32,7 @@ use crate::{
};
use anyhow::{anyhow, Result};
use chrono::DateTime;
-use jellybase::locale::Language;
+use jellybase::locale::{tr, Language};
use jellycommon::{
api::ApiNodeResponse,
user::{NodeUserData, WatchedState},
@@ -214,7 +214,7 @@ markup::define! {
ul.parents { @for (node, _) in *parents { li {
a.component[href=uri!(r_library_node(&node.slug))] { @node.title }
}}}
- @if node.media.is_some() {
+ @if node.media.is_some() {
a.play[href=&uri!(r_player(&node.slug, PlayerConfig::default()))] { @trs(lang, "node.player_link") }
}
@if !matches!(node.kind, NodeKind::Collection | NodeKind::Channel) {
@@ -373,7 +373,7 @@ markup::define! {
@match udata.watched {
WatchedState::None => {}
WatchedState::Pending => { p.pending { @trs(lang, "prop.watched.pending") } }
- WatchedState::Progress(x) => { p.progress { @trsa(lang, "prop.watched.progress", &[("time", &format_duration(x))]) } }
+ WatchedState::Progress(x) => { p.progress { @tr(**lang, "prop.watched.progress").replace("{time}", &format_duration(x)) } }
WatchedState::Watched => { p.watched { @trs(lang, "prop.watched.watched") } }
}
}
@@ -391,31 +391,31 @@ pub fn aspect_class(kind: NodeKind) -> &'static str {
}
pub fn format_duration(d: f64) -> String {
- format_duration_mode(d, false)
+ format_duration_mode(d, false, Language::English)
}
-pub fn format_duration_long(d: f64) -> String {
- format_duration_mode(d, true)
+pub fn format_duration_long(d: f64, lang: Language) -> String {
+ format_duration_mode(d, true, lang)
}
-fn format_duration_mode(mut d: f64, long_units: bool) -> String {
+fn format_duration_mode(mut d: f64, long_units: bool, lang: Language) -> String {
let mut s = String::new();
let sign = if d > 0. { "" } else { "-" };
d = d.abs();
- for (short, long, k) in [
- ("d", "day", 60. * 60. * 24.),
- ("h", "hour", 60. * 60.),
- ("m", "minute", 60.),
- ("s", "second", 1.),
+ for (short, long, long_pl, k) in [
+ ("d", "time.day", "time.days", 60. * 60. * 24.),
+ ("h", "time.hour", "time.hours", 60. * 60.),
+ ("m", "time.minute", "time.minutes", 60.),
+ ("s", "time.second", "time.seconds", 1.),
] {
let h = (d / k).floor();
d -= h * k;
if h > 0. {
if long_units {
+ let long = tr(lang, if h != 1. { long_pl } else { long });
// TODO breaks if seconds is zero
write!(
s,
- "{}{h} {long}{}{}",
+ "{}{h} {long}{}",
if k != 1. { "" } else { " and " },
- if h != 1. { "s" } else { "" },
if k > 60. { ", " } else { "" },
)
.unwrap();
diff --git a/server/src/routes/ui/search.rs b/server/src/routes/ui/search.rs
index 051dd24..bc84e57 100644
--- a/server/src/routes/ui/search.rs
+++ b/server/src/routes/ui/search.rs
@@ -6,12 +6,12 @@
use super::{
account::session::Session,
error::MyResult,
- layout::{DynLayoutPage, LayoutPage},
+ layout::{trs, DynLayoutPage, LayoutPage},
node::{DatabaseNodeUserDataExt, NodeCard},
};
use crate::routes::{api::AcceptJson, locale::AcceptLanguage};
use anyhow::anyhow;
-use jellybase::database::Database;
+use jellybase::{database::Database, locale::tr};
use jellycommon::{api::ApiSearchResponse, Visibility};
use rocket::{get, serde::json::Json, Either, State};
use std::time::Instant;
@@ -23,9 +23,9 @@ pub async fn r_search<'a>(
aj: AcceptJson,
query: Option<&str>,
page: Option<usize>,
- lang: AcceptLanguage
+ lang: AcceptLanguage,
) -> MyResult<Either<DynLayoutPage<'a>, Json<ApiSearchResponse>>> {
- let AcceptLanguage(lang)=lang;
+ let AcceptLanguage(lang) = lang;
let results = if let Some(query) = query {
let timing = Instant::now();
let (count, ids) = db.search(query, 32, page.unwrap_or_default() * 32)?;
@@ -48,17 +48,17 @@ pub async fn r_search<'a>(
Either::Right(Json(ApiSearchResponse { count, results }))
} else {
Either::Left(LayoutPage {
- title: "Search".to_string(),
+ title: tr(lang, "search.title").to_string(),
class: Some("search"),
content: markup::new! {
- h1 { "Search" }
+ h1 { @trs(&lang, "search.title") }
form[action="", method="GET"] {
- input[type="text", name="query", placeholder="Search Term", value=&query];
+ input[type="text", name="query", placeholder=&*tr(lang, "search.placeholder"), value=&query];
input[type="submit", value="Search"];
}
@if let Some((count, results, search_dur)) = &results {
- h2 { "Results" }
- p.stats { @format!("Found {count} nodes in {search_dur:?}.") }
+ h2 { @trs(&lang, "search.results.title") }
+ p.stats { @tr(lang, "search.results.stats").replace("{count}", &count.to_string()).replace("{dur}", &format!("{search_dur:?}")) }
ul.children {@for (node, udata) in results.iter() {
li { @NodeCard { node, udata, lang: &lang } }
}}
diff --git a/server/src/routes/ui/sort.rs b/server/src/routes/ui/sort.rs
index 4906c43..6d38e11 100644
--- a/server/src/routes/ui/sort.rs
+++ b/server/src/routes/ui/sort.rs
@@ -112,7 +112,7 @@ impl FilterProperty {
),
(
"filter_sort.filter.federation",
- &[(FederationLocal, "Local"), (FederationRemote, "Remote")],
+ &[(FederationLocal, "federation.local"), (FederationRemote, "federation.remote")],
),
(
"filter_sort.filter.watched",
@@ -236,7 +236,7 @@ markup::define! {
.categories {
@for (cname, cat) in FilterProperty::CATS {
.category {
- h3 { @cname }
+ h3 { @trs(lang, cname) }
@for (value, label) in *cat {
label { input[type="checkbox", name="filter_kind", value=value, checked=f.filter_kind.as_ref().map(|k|k.contains(value)).unwrap_or(true)]; @trs(lang, label) } br;
}
@@ -249,7 +249,7 @@ markup::define! {
.categories {
@for (cname, cat) in SortProperty::CATS {
.category {
- h3 { @cname }
+ h3 { @trs(lang, cname) }
@for (value, label) in *cat {
label { input[type="radio", name="sort_by", value=value, checked=Some(value)==f.sort_by.as_ref()]; @trs(lang, label) } br;
}
@@ -260,7 +260,7 @@ markup::define! {
fieldset.sortorder {
legend { "Sort Order" }
@use SortOrder::*;
- @for (value, label) in [(Ascending, "Ascending"), (Descending, "Descending")] {
+ @for (value, label) in [(Ascending, "filter_sort.order.asc"), (Descending, "filter_sort.order.desc")] {
label { input[type="radio", name="sort_order", value=value, checked=Some(value)==f.sort_order]; @trs(lang, label) } br;
}
}
diff --git a/server/src/routes/ui/stats.rs b/server/src/routes/ui/stats.rs
index 07da1d4..b0e0b27 100644
--- a/server/src/routes/ui/stats.rs
+++ b/server/src/routes/ui/stats.rs
@@ -13,14 +13,18 @@ use crate::{
routes::{
api::AcceptJson,
locale::AcceptLanguage,
- ui::{layout::trs, node::{
- format_duration, format_duration_long, format_size, rocket_uri_macro_r_library_node,
- }},
+ ui::{
+ layout::trs,
+ node::{
+ format_duration, format_duration_long, format_size, rocket_uri_macro_r_library_node,
+ },
+ },
},
uri,
};
use jellybase::locale::tr;
use jellycommon::{Node, NodeID, NodeKind, Visibility};
+use markup::raw;
use rocket::{get, serde::json::Json, Either, State};
use serde::Serialize;
use serde_json::{json, Value};
@@ -81,13 +85,21 @@ pub fn r_stats(
})))
} else {
Either::Left(LayoutPage {
- title: tr(lang, "stats.title", &[]).to_string(),
+ title: tr(lang, "stats.title").to_string(),
content: markup::new! {
.page.stats {
h1 { @trs(&lang, "stats.title") }
- p { "There is a total of " b{@all.count} " nodes in the library." }
- p { "The total runtime of the library is " b{@format_duration_long(all.runtime)} ", taking up " b{@format_size(all.size)} " of disk space." }
- p { "An average node has a runtime of " b{@format_duration(all.average_runtime())} " and file size of " b{@format_size(all.average_size() as u64)} "." }
+ p { @raw(tr(lang, "stats.count")
+ .replace("{count}", &format!("<b>{}</b>", all.count))
+ )}
+ p { @raw(tr(lang, "stats.runtime")
+ .replace("{dur}", &format!("<b>{}</b>", format_duration_long(all.runtime, lang)))
+ .replace("{size}", &format!("<b>{}</b>", format_size(all.size)))
+ )}
+ p { @raw(tr(lang, "stats.average")
+ .replace("{dur}", &format!("<b>{}</b>", format_duration(all.average_runtime())))
+ .replace("{size}", &format!("<b>{}</b>", format_size(all.average_size() as u64)))
+ )}
h2 { @trs(&lang, "stats.by_kind.title") }
table.striped {