From 88fa09755ff31b39b0454f0da58bc41a90263e89 Mon Sep 17 00:00:00 2001 From: cel 🌸 Date: Sat, 10 Feb 2024 06:54:25 +0000 Subject: add initial localisation --- Cargo.lock | 36 +++++++++++++++++++++++++++++ Cargo.toml | 2 +- resources/en/base.ftl | 9 ++++++++ resources/en/contact.ftl | 9 ++++++++ resources/en/home.ftl | 13 +++++++++++ src/error.rs | 1 + src/main.rs | 54 +++++++++++++++++++++++++++++++++++-------- src/templates.rs | 22 ++++++++++++++---- templates/base.html | 2 +- templates/blogpost-panel.html | 5 ++-- templates/contact.html | 21 +++++++++-------- templates/filtertags.html | 2 +- templates/home.html | 17 +++++--------- templates/latestposts.html | 7 +++--- templates/poem-panel.html | 5 ++-- templates/random-poem.html | 5 ++-- 16 files changed, 163 insertions(+), 47 deletions(-) create mode 100644 resources/en/base.ftl create mode 100644 resources/en/contact.ftl create mode 100644 resources/en/home.ftl diff --git a/Cargo.lock b/Cargo.lock index 3fff1c8..2c56a4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", ] @@ -1518,6 +1523,12 @@ dependencies = [ "toml_edit 0.20.2", ] +[[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" @@ -2438,6 +2449,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "238722e6d794ed130f91f4ea33e01fcff4f188d92337a21297892521c72df516" dependencies = [ "unic-langid-impl", + "unic-langid-macros", ] [[package]] @@ -2449,6 +2461,30 @@ dependencies = [ "tinystr", ] +[[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" diff --git a/Cargo.toml b/Cargo.toml index 3147ef5..7be8c9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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: she/her +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 = +

hallo i am celeste welcome 2 my site 🌟

+

this is where i do the posting

+

i wish u a wonderful day

+
+ +

perpetually under construction

+ + 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 = std::result::Result; 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/")] #[handler] -async fn blogpost(Path(blogpost): Path) -> Result { +async fn blogpost(Path(blogpost): Path, locale: Locale) -> Result { 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?")] #[handler] -async fn get_blog(filter_tags: Option>) -> Result { +async fn get_blog( + filter_tags: Option>, + locale: Locale, +) -> Result { let mut blogposts = Blogpost::get_articles().await?; let tags: Vec = posts::Post::get_tags(&blogposts) .into_iter() @@ -95,12 +106,15 @@ async fn get_blog(filter_tags: Option>) -> Result Result { let blogposts: Vec> = Blogpost::get_articles() @@ -132,8 +146,11 @@ async fn feed() -> Result { } #[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) -> Result { +async fn get_poem(Path(poem): Path, locale: Locale) -> Result { 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 { +async fn get_poetry(locale: Locale) -> Result { 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::::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; @@ -16,56 +17,67 @@ 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, pub poem: Option, + pub locale: Locale, } #[derive(Template)] #[template(path = "blogpost.html")] pub struct Blogpost { + pub title: String, pub blogpost: blog::Blogpost, pub filter_tags: HashSet, + pub locale: Locale, } // filtertags, blogpost-panel #[derive(Template)] #[template(path = "blog.html")] pub struct Blog { + pub title: String, pub blogposts: Vec, pub tags: Vec, pub filter_tags: HashSet, + 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, 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 @@ - celeste's hard drive + {{ title }} 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 @@
-

{{ blogpost.subject().unwrap_or("untitled") }}

-

{{ blogpost.published_at() }} permalink

+

{{ blogpost.subject().unwrap_or(locale.text("untitled").unwrap().as_str()) }}

+

{{ blogpost.published_at() }} {{ + locale.text("permalink").unwrap() }}

{% for tag in blogpost.tags() %}{% let tag = tag.to_string() %}{{ tag }}{% 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 @@
-

celeste 🌸

+

{{ locale.text("name").unwrap() }}

@@ -15,21 +15,22 @@ {% block content %} 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 @@
-

filter by tags

+

{{ locale.text("filter-by-tags").unwrap() }}

{% for tag in tags.clone() %}{% let contains_tag = filter_tags.contains(tag) %}
-

celeste 🌸

+

{{ locale.text("name").unwrap() }}

{% if is_live %} @@ -16,7 +16,8 @@ {% endif %} {% if listenbrainz.is_scrobbling %} -
now playing: {{ + {{ locale.text("now-playing").unwrap() + }}: {{ listenbrainz.song.clone().unwrap_or_default() }} - {{ listenbrainz.artist.clone().unwrap_or_default() }} {% endif %} @@ -28,13 +29,7 @@ {% block content %}
-

hallo i am celeste welcome 2 my site 🌟

-

this is where i do the posting

-

i wish u a wonderful day

-
- -

perpetually under construction

- + {{ locale.text("welcome").unwrap()|safe }}
{% endblock content %} @@ -44,10 +39,10 @@
{% include "latestposts.html" %} {% include "random-poem.html" %} -{% 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 @@
-

latest posts atom newsfeed

+

{{ locale.text("latest-blogposts").unwrap() }} atom newsfeed

{% for blogpost in blogposts %} - 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 %}

{{ title }}

{% endif %} -

{{ poem.created_at.date_naive() }} permalink +

{{ poem.created_at.date_naive() }} {{ + locale.text("permalink").unwrap() }}

{{ poem.content|safe }}
- + \ 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 %}
{{ blogpost.subject().unwrap_or("untitled") + {{ + blogpost.subject().unwrap_or(locale.text("untitled").unwrap().as_str()) }}