diff options
-rw-r--r-- | Cargo.lock | 36 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | resources/en/base.ftl | 9 | ||||
-rw-r--r-- | resources/en/contact.ftl | 9 | ||||
-rw-r--r-- | resources/en/home.ftl | 13 | ||||
-rw-r--r-- | src/error.rs | 1 | ||||
-rw-r--r-- | src/main.rs | 54 | ||||
-rw-r--r-- | src/templates.rs | 22 | ||||
-rw-r--r-- | templates/base.html | 2 | ||||
-rw-r--r-- | templates/blogpost-panel.html | 5 | ||||
-rw-r--r-- | templates/contact.html | 21 | ||||
-rw-r--r-- | templates/filtertags.html | 2 | ||||
-rw-r--r-- | templates/home.html | 17 | ||||
-rw-r--r-- | templates/latestposts.html | 7 | ||||
-rw-r--r-- | templates/poem-panel.html | 5 | ||||
-rw-r--r-- | templates/random-poem.html | 5 |
16 files changed, 163 insertions, 47 deletions
@@ -1455,6 +1455,9 @@ checksum = "38a712ff53e257d60d3d22936c51cafa606552129d55539c8a400de44eff676d" dependencies = [ "async-trait", "bytes", + "fluent", + "fluent-langneg", + "fluent-syntax", "futures-util", "headers", "hex", @@ -1462,6 +1465,7 @@ dependencies = [ "http-body-util", "hyper 1.1.0", "hyper-util", + "intl-memoizer", "mime", "mime_guess", "nix", @@ -1481,6 +1485,7 @@ dependencies = [ "tokio", "tokio-util", "tracing", + "unic-langid", "wildmatch", ] @@ -1519,6 +1524,12 @@ dependencies = [ ] [[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] name = "proc-macro2" version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2438,6 +2449,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "238722e6d794ed130f91f4ea33e01fcff4f188d92337a21297892521c72df516" dependencies = [ "unic-langid-impl", + "unic-langid-macros", ] [[package]] @@ -2450,6 +2462,30 @@ dependencies = [ ] [[package]] +name = "unic-langid-macros" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c854cefb82ff2816410ce606acbad1b3af065140907b29be9229040752b83ec" +dependencies = [ + "proc-macro-hack", + "tinystr", + "unic-langid-impl", + "unic-langid-macros-impl", +] + +[[package]] +name = "unic-langid-macros-impl" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea2a4c80deb4fb3ca51f66b5e2dd91e3642bbce52234bcf22e41668281208e4" +dependencies = [ + "proc-macro-hack", + "quote 1.0.35", + "syn 2.0.48", + "unic-langid-impl", +] + +[[package]] name = "unicase" version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -22,7 +22,7 @@ toml = "0.7.4" tokio-stream = { version = "0.1.14", features = ["fs"] } atom_syndication = "0.12.1" fluent = "0.16.0" -poem = { version = "2.0.0", features = ["embed"] } +poem = { version = "2.0.0", features = ["embed", "i18n"] } askama = { git = "https://bunny.garden/forks/askama.git", features = ["with-poem"] } askama_poem = { git = "https://bunny.garden/forks/askama.git" } rust-embed = "8.2.0" diff --git a/resources/en/base.ftl b/resources/en/base.ftl new file mode 100644 index 0000000..9285cd2 --- /dev/null +++ b/resources/en/base.ftl @@ -0,0 +1,9 @@ +title = celeste's hard drive +title-blog = celeste's blog +title-poetry = celeste's poetry +branch = cherry blossom branch +latest-blogposts = latest blogposts +random-poem = random poem +filter-by-tags = filter by tags +permalink = permalink +untitled = untitled diff --git a/resources/en/contact.ftl b/resources/en/contact.ftl new file mode 100644 index 0000000..4b9f2d2 --- /dev/null +++ b/resources/en/contact.ftl @@ -0,0 +1,9 @@ +title-contact = celeste's contact details +name = celeste 🌸 +pronouns = pronouns: <span class="p-x-pronoun-nominative">she</span>/<span class="p-x-pronoun-accusative">her</span> +email = email +jabber = jabber +scrobbles = scrobbles +bandcamp-fan = bandcamp fan +languages = languages (in rough skill order): english, portuguese, french, japanese, german, chinese, latin, toki pona, korean +programming = programming: rust, erlang, go, haskell, webdev, lisp diff --git a/resources/en/home.ftl b/resources/en/home.ftl new file mode 100644 index 0000000..24702ee --- /dev/null +++ b/resources/en/home.ftl @@ -0,0 +1,13 @@ +latest-update = latest update: added poetry! +now-playing = now playing + +# welcome panel +welcome = + <h2>hallo i am celeste welcome 2 my site 🌟</h2> + <p>this is where i do the posting</p> + <p>i wish u a wonderful day</p> + <br> + <img src="/static/quinntyping.png"> + <p>perpetually under construction</p> + <img style="border: 0;" src="/static/barraconstruction.gif"> + diff --git a/src/error.rs b/src/error.rs index 611ce79..adaf77f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -56,6 +56,7 @@ impl ResponseError for BlossomError { Self: std::error::Error + Send + Sync + 'static, { templates::Error { + title: self.to_string(), status: self.status(), message: self.to_string(), } diff --git a/src/main.rs b/src/main.rs index 2f1a00b..de7bc73 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,6 +15,7 @@ mod utils; use std::{collections::HashSet, time::Duration}; use poem::http::StatusCode; +use poem::i18n::{I18NResources, Locale}; use poem::{ endpoint::EmbeddedFilesEndpoint, get, handler, @@ -44,7 +45,7 @@ type Result<T> = std::result::Result<T, BlossomError>; struct Static; #[handler] -async fn home(Data(reqwest): Data<&reqwest::Client>) -> templates::Home { +async fn home(Data(reqwest): Data<&reqwest::Client>, locale: Locale) -> templates::Home { let listenbrainz_client = listenbrainz::raw::Client::new(); let (live, listenbrainz, blogposts, poems) = tokio::join!( live::get_live_status(reqwest), @@ -59,20 +60,27 @@ async fn home(Data(reqwest): Data<&reqwest::Client>) -> templates::Home { let poems = poems.unwrap_or_default(); let poem = poems.choose(&mut rand::thread_rng()).cloned(); templates::Home { + title: locale.text("title").unwrap(), is_live, listenbrainz, blogposts, poem, + locale, } } // #[get("/blog/<blogpost>")] #[handler] -async fn blogpost(Path(blogpost): Path<String>) -> Result<templates::Blogpost> { +async fn blogpost(Path(blogpost): Path<String>, locale: Locale) -> Result<templates::Blogpost> { let blogpost = Blogpost::get_article(&blogpost).await?; Ok(templates::Blogpost { + title: blogpost + .subject() + .unwrap_or(locale.text("untitled").unwrap().as_str()) + .to_owned(), blogpost, filter_tags: HashSet::new(), + locale, }) } @@ -83,7 +91,10 @@ struct FilterTags { // #[get("/blog?<filter>")] #[handler] -async fn get_blog(filter_tags: Option<Query<FilterTags>>) -> Result<templates::Blog> { +async fn get_blog( + filter_tags: Option<Query<FilterTags>>, + locale: Locale, +) -> Result<templates::Blog> { let mut blogposts = Blogpost::get_articles().await?; let tags: Vec<String> = posts::Post::get_tags(&blogposts) .into_iter() @@ -95,12 +106,15 @@ async fn get_blog(filter_tags: Option<Query<FilterTags>>) -> Result<templates::B blogposts = posts::Post::filter_by_tags(blogposts, &filter_hashset); } Ok(templates::Blog { + title: locale.text("title-blog").unwrap(), blogposts, tags, filter_tags: filter_hashset, + locale, }) } +// TODO: localize feed #[handler] async fn feed() -> Result<Response> { let blogposts: Vec<Box<dyn Post + Send + Sync>> = Blogpost::get_articles() @@ -132,8 +146,11 @@ async fn feed() -> Result<Response> { } #[handler] -async fn contact() -> templates::Contact { - templates::Contact +async fn contact(locale: Locale) -> templates::Contact { + templates::Contact { + title: locale.text("title-contact").unwrap(), + locale, + } } #[handler] @@ -142,21 +159,34 @@ async fn plants() -> Result<()> { } #[handler] -async fn get_poem(Path(poem): Path<String>) -> Result<templates::Poem> { +async fn get_poem(Path(poem): Path<String>, locale: Locale) -> Result<templates::Poem> { let poem = Poem::get_article(&poem).await?; - Ok(templates::Poem { poem, jiggle: 4 }) + Ok(templates::Poem { + title: (&poem.title) + .clone() + .unwrap_or(locale.text("untitled").unwrap()), + poem, + jiggle: 4, + locale, + }) } #[handler] -async fn get_poetry() -> Result<templates::Poetry> { +async fn get_poetry(locale: Locale) -> Result<templates::Poetry> { let mut poems = Poem::get_articles().await?; poems.sort_by_key(|poem| poem.created_at); poems.reverse(); - Ok(templates::Poetry { poems, jiggle: 16 }) + Ok(templates::Poetry { + title: locale.text("title-poetry").unwrap(), + poems, + jiggle: 16, + locale, + }) } async fn custom_error(err: poem::Error) -> impl IntoResponse { templates::Error { + title: err.to_string(), status: err.status(), message: err.to_string(), } @@ -171,6 +201,11 @@ async fn main() -> std::result::Result<(), std::io::Error> { // let mut skinny_data = mastodon_async::Data::default(); // skinny_data.base = Cow::from("https://skinnyver.se"); + let resources = I18NResources::builder() + .add_path("./resources") + .build() + .unwrap(); + let blossom = Route::new() .at("/", get(home)) .at("/blog", get(get_blog)) @@ -182,6 +217,7 @@ async fn main() -> std::result::Result<(), std::io::Error> { .at("/plants", get(plants)) .nest("/static/", EmbeddedFilesEndpoint::<Static>::new()) .catch_all_error(custom_error) + .data(resources) .with(Tracing) .with(AddData::new( reqwest::Client::builder() diff --git a/src/templates.rs b/src/templates.rs index 4bcb64b..2ab9c41 100644 --- a/src/templates.rs +++ b/src/templates.rs @@ -2,6 +2,7 @@ use std::collections::HashSet; use askama::Template; use poem::http::StatusCode; +use poem::i18n::Locale; use rand::{thread_rng, Rng}; use crate::poetry; @@ -17,55 +18,66 @@ mod filters { } #[derive(Template)] -#[template(path = "base.html")] -struct Base; - -#[derive(Template)] #[template(path = "home.html")] pub struct Home { + pub title: String, pub is_live: bool, pub listenbrainz: NowPlayingData, pub blogposts: Vec<blog::Blogpost>, pub poem: Option<poetry::Poem>, + pub locale: Locale, } #[derive(Template)] #[template(path = "blogpost.html")] pub struct Blogpost { + pub title: String, pub blogpost: blog::Blogpost, pub filter_tags: HashSet<String>, + pub locale: Locale, } // filtertags, blogpost-panel #[derive(Template)] #[template(path = "blog.html")] pub struct Blog { + pub title: String, pub blogposts: Vec<blog::Blogpost>, pub tags: Vec<String>, pub filter_tags: HashSet<String>, + pub locale: Locale, } #[derive(Template)] #[template(path = "poem.html")] pub struct Poem { + pub title: String, pub poem: poetry::Poem, pub jiggle: isize, + pub locale: Locale, } #[derive(Template)] #[template(path = "poetry.html")] pub struct Poetry { + pub title: String, pub poems: Vec<poetry::Poem>, pub jiggle: isize, + pub locale: Locale, } #[derive(Template)] #[template(path = "contact.html")] -pub struct Contact; +pub struct Contact { + pub title: String, + pub locale: Locale, +} #[derive(Template)] #[template(path = "error.html")] pub struct Error { + pub title: String, pub status: StatusCode, pub message: String, + // TODO: localize error page } diff --git a/templates/base.html b/templates/base.html index c1dbfad..428ce40 100644 --- a/templates/base.html +++ b/templates/base.html @@ -7,7 +7,7 @@ <link rel="stylesheet" href="/static/style.css"> <link rel="stylesheet" href="/static/fonts.css"> <link rel="icon" type="image/png" href="/static/favicon.png" /> - <title>celeste's hard drive</title> + <title>{{ title }}</title> </head> <body> diff --git a/templates/blogpost-panel.html b/templates/blogpost-panel.html index 45117b6..2871407 100644 --- a/templates/blogpost-panel.html +++ b/templates/blogpost-panel.html @@ -1,6 +1,7 @@ <div class="panel content blogpost"> - <h1 class="title">{{ blogpost.subject().unwrap_or("untitled") }}</h1> - <h2 class="created-at">{{ blogpost.published_at() }} <a href="/blog/{{ blogpost.file_name() }}">permalink</a></h2> + <h1 class="title">{{ blogpost.subject().unwrap_or(locale.text("untitled").unwrap().as_str()) }}</h1> + <h2 class="created-at">{{ blogpost.published_at() }} <a href="/blog/{{ blogpost.file_name() }}">{{ + locale.text("permalink").unwrap() }}</a></h2> <div class="tags">{% for tag in blogpost.tags() %}{% let tag = tag.to_string() %}<a class="tag {% if filter_tags.contains(tag) %}active{% endif %}" href="/blog?filter={{ tag }}">{{ tag }}</a>{% diff --git a/templates/contact.html b/templates/contact.html index 4302e88..d6534fd 100644 --- a/templates/contact.html +++ b/templates/contact.html @@ -6,7 +6,7 @@ <header> <div class="panel" id="title"> - <h1>celeste 🌸</h1> + <h1>{{ locale.text("name").unwrap() }}</h1> </div> </header> @@ -15,21 +15,22 @@ {% block content %} <div class="content panel h-card" rel="author"> - <h1 class="p-name">cel</h1> + <h1 class="p-name">{{ locale.text("name").unwrap() }}</h1> <ul> <li> - <h3>pronouns: <span class="p-x-pronoun-nominative">she</span>/<span class="p-x-pronoun-accusative">her</span></h3> + <h3>{{ locale.text("pronouns").unwrap()|safe }}</h3> </li> - <li>email: <a class="u-email" href="mailto:cel@blos.sm">cel@blos.sm</a></li> - <li>jabber: <a class="u-impp" href="xmpp:cel@blos.sm?message">cel@blos.sm</a></li> - <li>scrobbles: <a class="u-url" href="https://listenbrainz.org/user/celblossom">listenbrainz/celblossom</a></li> - <li>bandcamp fan: <a class="u-url" href="https://bandcamp.com/celblossom">bandcamp/celblossom</a></li> + <li>{{ locale.text("email").unwrap() }}: <a class="u-email" href="mailto:cel@blos.sm">cel@blos.sm</a></li> + <li>{{ locale.text("jabber").unwrap() }}: <a class="u-impp" href="xmpp:cel@blos.sm?message">cel@blos.sm</a></li> + <li>{{ locale.text("scrobbles").unwrap() }}: <a class="u-url" + href="https://listenbrainz.org/user/celblossom">listenbrainz/celblossom</a></li> + <li>{{ locale.text("bandcamp-fan").unwrap() }}: <a class="u-url" + href="https://bandcamp.com/celblossom">bandcamp/celblossom</a></li> <br> - <li><span class="p-note">languages (in rough skill order): english, portuguese, french, japanese, german, chinese, - latin, toki pona, korean</span></li> + <li><span class="p-note">{{ locale.text("languages").unwrap() }}</span></li> </li> - <li><span class="p-note">programming: rust, erlang, go, haskell, webdev, lisp</span></li> + <li><span class="p-note">{{ locale.text("programming").unwrap() }}</span></li> </ul> </div> diff --git a/templates/filtertags.html b/templates/filtertags.html index 55c802f..4902dd4 100644 --- a/templates/filtertags.html +++ b/templates/filtertags.html @@ -1,5 +1,5 @@ <div class="panel" id="filter-tags"> - <h2>filter by tags</h2> + <h2>{{ locale.text("filter-by-tags").unwrap() }}</h2> <div class="tags"> {% for tag in tags.clone() %}{% let contains_tag = filter_tags.contains(tag) %}<a class="{% if contains_tag %}active{% endif %}" diff --git a/templates/home.html b/templates/home.html index 7bbed9e..3e34cae 100644 --- a/templates/home.html +++ b/templates/home.html @@ -6,7 +6,7 @@ <header> <div class="panel" id="title"> - <h1>celeste 🌸</h1> + <h1>{{ locale.text("name").unwrap() }}</h1> </div> {% if is_live %} @@ -16,7 +16,8 @@ {% endif %} {% if listenbrainz.is_scrobbling %} - <a href="https://listenbrainz.org/user/celblossom" class="panel" id="scrobble">now playing: {{ + <a href="https://listenbrainz.org/user/celblossom" class="panel" id="scrobble">{{ locale.text("now-playing").unwrap() + }}: {{ listenbrainz.song.clone().unwrap_or_default() }} - {{ listenbrainz.artist.clone().unwrap_or_default() }}</a> {% endif %} @@ -28,13 +29,7 @@ {% block content %} <div class="panel content"> - <h2>hallo i am celeste welcome 2 my site 🌟</h2> - <p>this is where i do the posting</p> - <p>i wish u a wonderful day</p> - <br> - <img src="/static/quinntyping.png"> - <p>perpetually under construction</p> - <img style="border: 0;" src="/static/barraconstruction.gif"> + {{ locale.text("welcome").unwrap()|safe }} </div> {% endblock content %} @@ -44,10 +39,10 @@ <aside> <div class="panel" style="background-color: #b52f6a; z-index: -1; font-family: 'Terminal Grotesque'; font-size: 2em"> - latest update: added poetry!</div> + {{ locale.text("latest-update").unwrap() }}</div> {% include "latestposts.html" %} {% include "random-poem.html" %} </aside> -{% endblock aside %}
\ No newline at end of file +{% endblock aside %} diff --git a/templates/latestposts.html b/templates/latestposts.html index e2dfa9f..b2ed2c5 100644 --- a/templates/latestposts.html +++ b/templates/latestposts.html @@ -1,10 +1,11 @@ <div class="panel" id="posts"> - <h2>latest posts <a class="small-badge" href="/feed"><img class="small-badge" src="/static/atombadge.png" - alt="atom newsfeed"></a></h2> + <h2>{{ locale.text("latest-blogposts").unwrap() }} <a class="small-badge" href="/feed"><img class="small-badge" + src="/static/atombadge.png" alt="atom newsfeed"></a></h2> <table id="post-list"> {% for blogpost in blogposts %} <tr> - <td><a class="post-entry" href="/blog/{{ blogpost.file_name() }}">{{ blogpost.subject().unwrap_or("untitled") + <td><a class="post-entry" href="/blog/{{ blogpost.file_name() }}">{{ + blogpost.subject().unwrap_or(locale.text("untitled").unwrap().as_str()) }}<span class="post-date">{{ blogpost.published_at()|mytruncate(10) }}</span></a></td> </tr> diff --git a/templates/poem-panel.html b/templates/poem-panel.html index 3359578..e1fb016 100644 --- a/templates/poem-panel.html +++ b/templates/poem-panel.html @@ -2,9 +2,10 @@ style="top: {{ self::thread_rng().gen_range(-jiggle..=jiggle) }}vw; left: {{ self::thread_rng().gen_range(-4..=4) }}vw" class="panel content poem"> {% if let Some(title) = poem.title %}<h1 class="title">{{ title }}</h1>{% endif %} - <h2 class="created-at">{{ poem.created_at.date_naive() }} <a href="/poetry/{{ poem.file_name }}">permalink</a> + <h2 class="created-at">{{ poem.created_at.date_naive() }} <a href="/poetry/{{ poem.file_name }}">{{ + locale.text("permalink").unwrap() }}</a> </h2> <div class="poem-content"> {{ poem.content|safe }} </div> -</div> +</div>
\ No newline at end of file diff --git a/templates/random-poem.html b/templates/random-poem.html index bdfbaff..f3531d8 100644 --- a/templates/random-poem.html +++ b/templates/random-poem.html @@ -1,8 +1,9 @@ {% if let Some(poem) = poem %} <div class="panel" id="random-poem"> - <h2>random poem</h2> + <h2>{{ locale.text("random-poem").unwrap() }}</h2> <a href="/poetry/{{ poem.file_name }}"> - <h3>{% if let Some(title) = poem.title %}{{ title }}{% else %}permalink{% endif %}</h3> + <h3>{% if let Some(title) = poem.title %}{{ title }}{% else %}{{ locale.text("permalink").unwrap() }}{% endif %} + </h3> </a> <div class="poem-content"> {{ poem.content|safe }} |