diff options
| author | 2024-02-12 01:39:00 +0000 | |
|---|---|---|
| committer | 2024-02-12 01:39:00 +0000 | |
| commit | 2b8623fde242d379c66e4c532f7cad0dbb2198aa (patch) | |
| tree | d875588258b57998a1e089a17124e21e9632059b | |
| parent | 82ef62654f58eb6212fad1beb3adb2c1c979fb56 (diff) | |
| download | blossom-2b8623fde242d379c66e4c532f7cad0dbb2198aa.tar.gz blossom-2b8623fde242d379c66e4c532f7cad0dbb2198aa.tar.bz2 blossom-2b8623fde242d379c66e4c532f7cad0dbb2198aa.zip | |
add language select widget
Diffstat (limited to '')
| -rw-r--r-- | Cargo.lock | 187 | ||||
| -rw-r--r-- | Cargo.toml | 2 | ||||
| -rw-r--r-- | TODO.md | 9 | ||||
| -rw-r--r-- | src/i18n.rs | 120 | ||||
| -rw-r--r-- | src/main.rs | 17 | ||||
| -rw-r--r-- | src/templates.rs | 2 | ||||
| -rw-r--r-- | static/style.css | 7 | ||||
| -rw-r--r-- | templates/base.html | 17 | 
8 files changed, 346 insertions, 15 deletions
| @@ -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" @@ -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" @@ -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> | 
