aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock187
-rw-r--r--Cargo.toml2
-rw-r--r--TODO.md9
-rw-r--r--src/i18n.rs120
-rw-r--r--src/main.rs17
-rw-r--r--src/templates.rs2
-rw-r--r--static/style.css7
-rw-r--r--templates/base.html17
8 files changed, 346 insertions, 15 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 2c56a4a..3d2dd4d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -18,6 +18,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
+name = "aead"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
+dependencies = [
+ "crypto-common",
+ "generic-array",
+]
+
+[[package]]
+name = "aes"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2"
+dependencies = [
+ "cfg-if",
+ "cipher",
+ "cpufeatures",
+]
+
+[[package]]
+name = "aes-gcm"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
+dependencies = [
+ "aead",
+ "aes",
+ "cipher",
+ "ctr",
+ "ghash",
+ "subtle",
+]
+
+[[package]]
name = "aho-corasick"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -303,6 +338,34 @@ dependencies = [
]
[[package]]
+name = "cipher"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
+dependencies = [
+ "crypto-common",
+ "inout",
+]
+
+[[package]]
+name = "cookie"
+version = "0.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24"
+dependencies = [
+ "aes-gcm",
+ "base64",
+ "hkdf",
+ "hmac",
+ "percent-encoding",
+ "rand",
+ "sha2",
+ "subtle",
+ "time",
+ "version_check",
+]
+
+[[package]]
name = "core-foundation"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -343,10 +406,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
+ "rand_core",
"typenum",
]
[[package]]
+name = "ctr"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
+dependencies = [
+ "cipher",
+]
+
+[[package]]
name = "darling"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -452,6 +525,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
+ "subtle",
]
[[package]]
@@ -724,6 +798,16 @@ dependencies = [
]
[[package]]
+name = "ghash"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40"
+dependencies = [
+ "opaque-debug",
+ "polyval",
+]
+
+[[package]]
name = "gimli"
version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -747,7 +831,7 @@ dependencies = [
"futures-sink",
"futures-util",
"http 0.2.11",
- "indexmap",
+ "indexmap 2.1.0",
"slab",
"tokio",
"tokio-util",
@@ -766,7 +850,7 @@ dependencies = [
"futures-sink",
"futures-util",
"http 1.0.0",
- "indexmap",
+ "indexmap 2.1.0",
"slab",
"tokio",
"tokio-util",
@@ -775,6 +859,12 @@ dependencies = [
[[package]]
name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
+name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
@@ -825,6 +915,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
+name = "hkdf"
+version = "0.12.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
+dependencies = [
+ "hmac",
+]
+
+[[package]]
+name = "hmac"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
+dependencies = [
+ "digest",
+]
+
+[[package]]
name = "http"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1016,12 +1124,31 @@ dependencies = [
[[package]]
name = "indexmap"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
+dependencies = [
+ "autocfg",
+ "hashbrown 0.12.3",
+]
+
+[[package]]
+name = "indexmap"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
dependencies = [
"equivalent",
- "hashbrown",
+ "hashbrown 0.14.3",
+]
+
+[[package]]
+name = "inout"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
+dependencies = [
+ "generic-array",
]
[[package]]
@@ -1321,6 +1448,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
+name = "opaque-debug"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
+
+[[package]]
name = "openssl"
version = "0.10.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1454,7 +1587,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38a712ff53e257d60d3d22936c51cafa606552129d55539c8a400de44eff676d"
dependencies = [
"async-trait",
+ "base64",
"bytes",
+ "chrono",
+ "cookie",
"fluent",
"fluent-langneg",
"fluent-syntax",
@@ -1473,6 +1609,8 @@ dependencies = [
"percent-encoding",
"pin-project-lite",
"poem-derive",
+ "priority-queue",
+ "rand",
"regex",
"rfc7239",
"rust-embed",
@@ -1482,6 +1620,7 @@ dependencies = [
"smallvec",
"sync_wrapper",
"thiserror",
+ "time",
"tokio",
"tokio-util",
"tracing",
@@ -1502,6 +1641,18 @@ dependencies = [
]
[[package]]
+name = "polyval"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "opaque-debug",
+ "universal-hash",
+]
+
+[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1514,6 +1665,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
+name = "priority-queue"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0bda9164fe05bc9225752d54aae413343c36f684380005398a6a8fde95fe785"
+dependencies = [
+ "autocfg",
+ "indexmap 1.9.3",
+]
+
+[[package]]
name = "proc-macro-crate"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1988,6 +2149,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
+name = "subtle"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
+
+[[package]]
name = "sval"
version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2331,7 +2498,7 @@ version = "0.19.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
dependencies = [
- "indexmap",
+ "indexmap 2.1.0",
"serde",
"serde_spanned",
"toml_datetime",
@@ -2344,7 +2511,7 @@ version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338"
dependencies = [
- "indexmap",
+ "indexmap 2.1.0",
"toml_datetime",
"winnow",
]
@@ -2534,6 +2701,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
[[package]]
+name = "universal-hash"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
+dependencies = [
+ "crypto-common",
+ "subtle",
+]
+
+[[package]]
name = "url"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 7be8c9d..83814b7 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", "i18n"] }
+poem = { version = "2.0.0", features = ["embed", "i18n", "session"] }
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/TODO.md b/TODO.md
index 2b1d327..18b793d 100644
--- a/TODO.md
+++ b/TODO.md
@@ -9,8 +9,8 @@
[x] make font smaller
[x] fix blos.sm badge
[x] fix update font
-[ ] localisation
- [ ] landing page for blos.sm
+[x] localisation
+[x] site translations
[ ] opengraph
[ ] more badges
[ ] harbor valorant
@@ -19,7 +19,8 @@
[ ] links i like
[ ] badges i created
[ ] skinnymoji url
-[ ] site translations
+[ ] clean up css
+[ ] move routes to own file
[ ] credits
[ ] latest video from garbageh channel widget
[ ] homepage revamp
@@ -36,5 +37,5 @@
badges to steal:
[x] keith
[x] webb
+[ ] bunny ring
[ ] puff place
-[ ] shibao blog
diff --git a/src/i18n.rs b/src/i18n.rs
new file mode 100644
index 0000000..964aaa3
--- /dev/null
+++ b/src/i18n.rs
@@ -0,0 +1,120 @@
+use std::str::FromStr;
+
+use poem::{
+ error::I18NError,
+ http::header,
+ i18n::{unic_langid::LanguageIdentifier, I18NArgs, I18NResources},
+ session::Session,
+ Endpoint, FromRequest, Middleware, Request, RequestBody,
+};
+use serde::Deserialize;
+
+#[derive(Deserialize)]
+struct Params {
+ lang: String,
+}
+
+pub async fn set_language<E: Endpoint>(next: E, mut req: Request) -> poem::Result<E::Output> {
+ if let Ok(params) = req.params::<Params>() {
+ let session = req
+ .extensions()
+ .get::<Session>()
+ .expect("To use the `set_language` middleware, the `Session` data is required.");
+ session.set("lang", params.lang);
+ println!("{:?}", session.get::<String>("lang"))
+ }
+ next.call(req).await
+}
+
+pub struct Locale(poem::i18n::I18NBundle);
+
+impl Locale {
+ /// Gets the text with arguments.
+ ///
+ /// See also: [`I18NBundle::text_with_args`](I18NBundle::text_with_args)
+ pub fn text_with_args<'a>(
+ &self,
+ id: impl AsRef<str>,
+ args: impl Into<I18NArgs<'a>>,
+ ) -> Result<String, I18NError> {
+ self.0.text_with_args(id, args)
+ }
+
+ /// Gets the text.
+ ///
+ /// See also: [`I18NBundle::text`](I18NBundle::text)
+ pub fn text(&self, id: impl AsRef<str>) -> Result<String, I18NError> {
+ self.0.text(id)
+ }
+}
+
+#[poem::async_trait]
+impl<'a> FromRequest<'a> for Locale {
+ async fn from_request(req: &'a Request, body: &mut RequestBody) -> poem::Result<Self> {
+ let session = req
+ .extensions()
+ .get::<Session>()
+ .expect("To use the `Locale` extractor, the `Session` data is required.");
+ let resources = req
+ .extensions()
+ .get::<I18NResources>()
+ .expect("To use the `Locale` extractor, the `I18NResources` data is required.");
+
+ let mut lang_id = None;
+ if let Some(lang) = session.get::<String>("lang") {
+ lang_id = Some(lang);
+ };
+
+ if let Some(lang_id) = lang_id {
+ if let Ok(lang_id) = LanguageIdentifier::from_str(&lang_id) {
+ return Ok(Self(resources.negotiate_languages(&[&lang_id])));
+ }
+ };
+
+ let accept_languages = req
+ .headers()
+ .get(header::ACCEPT_LANGUAGE)
+ .and_then(|value| value.to_str().ok())
+ .map(parse_accept_languages)
+ .unwrap_or_default();
+
+ Ok(Self(resources.negotiate_languages(&accept_languages)))
+ }
+}
+
+fn parse_accept_languages(value: &str) -> Vec<LanguageIdentifier> {
+ let mut languages = Vec::new();
+
+ for s in value.split(',').map(str::trim) {
+ if let Some(res) = parse_language(s) {
+ languages.push(res);
+ }
+ }
+
+ languages.sort_by(|(_, a), (_, b)| b.cmp(a));
+ languages
+ .into_iter()
+ .map(|(language, _)| language)
+ .collect()
+}
+
+fn parse_language(value: &str) -> Option<(LanguageIdentifier, u16)> {
+ let mut parts = value.split(';');
+ let name = parts.next()?.trim();
+ let quality = match parts.next() {
+ Some(quality) => parse_quality(quality).unwrap_or_default(),
+ None => 1000,
+ };
+ let language = LanguageIdentifier::from_str(name).ok()?;
+ Some((language, quality))
+}
+
+fn parse_quality(value: &str) -> Option<u16> {
+ let mut parts = value.split('=');
+ let name = parts.next()?.trim();
+ if name != "q" {
+ return None;
+ }
+ let q = parts.next()?.trim().parse::<f32>().ok()?;
+ Some((q.clamp(0.0, 1.0) * 1000.0) as u16)
+}
diff --git a/src/main.rs b/src/main.rs
index d810159..8e31fee 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -4,6 +4,7 @@ mod article;
mod atom;
mod blog;
mod error;
+mod i18n;
mod live;
mod poetry;
mod posts;
@@ -14,9 +15,12 @@ mod utils;
use std::{collections::HashSet, time::Duration};
+use i18n::set_language;
use poem::http::StatusCode;
use poem::i18n::unic_langid::langid;
-use poem::i18n::{I18NResources, Locale};
+use poem::i18n::I18NResources;
+use poem::session::{CookieConfig, CookieSession, ServerSession};
+use poem::web::cookie::{CookieKey, SameSite};
use poem::{
endpoint::EmbeddedFilesEndpoint,
get, handler,
@@ -36,6 +40,7 @@ use tracing_subscriber::FmtSubscriber;
use crate::article::Article;
use crate::blog::Blogpost;
+use crate::i18n::Locale;
use crate::poetry::Poem;
use crate::posts::Post;
@@ -208,6 +213,14 @@ async fn main() -> std::result::Result<(), std::io::Error> {
.build()
.unwrap();
+ let session = CookieSession::new(
+ // CookieConfig::private(CookieKey::generate())
+ // .name("blossom")
+ // .domain("blos.sm")
+ // .same_site(SameSite::Strict),
+ CookieConfig::default(),
+ );
+
let blossom = Route::new()
.at("/", get(home))
.at("/blog", get(get_blog))
@@ -220,7 +233,9 @@ async fn main() -> std::result::Result<(), std::io::Error> {
.nest("/static/", EmbeddedFilesEndpoint::<Static>::new())
.catch_all_error(custom_error)
.data(resources)
+ .around(set_language)
.with(Tracing)
+ .with(session)
.with(AddData::new(
reqwest::Client::builder()
.connect_timeout(Duration::from_secs(1))
diff --git a/src/templates.rs b/src/templates.rs
index 2ab9c41..66462eb 100644
--- a/src/templates.rs
+++ b/src/templates.rs
@@ -2,9 +2,9 @@ use std::collections::HashSet;
use askama::Template;
use poem::http::StatusCode;
-use poem::i18n::Locale;
use rand::{thread_rng, Rng};
+use crate::i18n::Locale;
use crate::poetry;
use crate::posts::Post;
use crate::{blog, scrobbles::NowPlayingData};
diff --git a/static/style.css b/static/style.css
index df6921d..b777549 100644
--- a/static/style.css
+++ b/static/style.css
@@ -554,6 +554,13 @@ pre code {
border: none;
}
+#lang-select {
+ background: #311f20;
+ float: right;
+ padding: 0.5vw 1vw;
+ margin-left: 4vw;
+}
+
/* branches */
.branch {
diff --git a/templates/base.html b/templates/base.html
index 428ce40..366d42a 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -29,8 +29,19 @@
<div id="page">
- {% block header %}
- {% endblock header %}
+ <div>
+ <div id="lang-select" class="panel">
+ <a href="?lang=de">de</a>
+ <a href="?lang=en">en</a>
+ <a href="?lang=fr">fr</a>
+ <a href="?lang=ja">ja</a>
+ <a href="?lang=pt">pt</a>
+ <a href="?lang=zh">zh</a>
+ </div>
+
+ {% block header %}
+ {% endblock header %}
+ </div>
<nav class="panel">
<ul id="nav">
@@ -89,4 +100,4 @@
</div>
</body>
-</html> \ No newline at end of file
+</html>