translations #2

This commit is contained in:
Ondrej Vlach 2023-11-27 01:19:10 +01:00
parent 07c02052fd
commit 6efbfc0e9c
7 changed files with 128 additions and 22 deletions

15
Cargo.lock generated
View File

@ -1288,6 +1288,7 @@ dependencies = [
"fluent-resmgr", "fluent-resmgr",
"log", "log",
"ovlach_data", "ovlach_data",
"phf",
"reqwest", "reqwest",
"rocket", "rocket",
"rocket_dyn_templates", "rocket_dyn_templates",
@ -1410,6 +1411,7 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
dependencies = [ dependencies = [
"phf_macros",
"phf_shared", "phf_shared",
] ]
@ -1433,6 +1435,19 @@ dependencies = [
"rand", "rand",
] ]
[[package]]
name = "phf_macros"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
dependencies = [
"phf_generator",
"phf_shared",
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "phf_shared" name = "phf_shared"
version = "0.11.2" version = "0.11.2"

View File

@ -19,4 +19,5 @@ chrono = "0.4.31"
sha256 = "1.4.0" sha256 = "1.4.0"
fluent-bundle = "0.15.2" fluent-bundle = "0.15.2"
fluent-resmgr = "0.0.6" fluent-resmgr = "0.0.6"
unic-langid = "0.9.1" unic-langid = "0.9.1"
phf = { version = "0.11.2", features = ["macros"] }

View File

@ -1,8 +1,6 @@
use std::collections::HashMap;
use rocket::{*, fairing::AdHoc}; use rocket::{*, fairing::AdHoc};
use rocket_dyn_templates::{Template, tera::Value}; use rocket_dyn_templates::Template;
use ::serde::Deserialize; use ::serde::Deserialize;
use tools::tera::{static_filter, translate_filter, calculate_age, insert_space_every, lang_entity, gravatar_link}; use tools::tera::{static_filter, translate_filter, calculate_age, insert_space_every, lang_entity, gravatar_link};
@ -44,7 +42,10 @@ pub fn rocket_builder() -> Rocket<Build> {
AdHoc::config::<PresentationConfig>() AdHoc::config::<PresentationConfig>()
).attach( ).attach(
AdHoc::config::<CVBackendConfig>() AdHoc::config::<CVBackendConfig>()
).attach(
tools::rocket::RequestTimer
).mount("/", routes![ ).mount("/", routes![
routes::root::index routes::root::index,
routes::root::index_without_lang
]) ])
} }

View File

@ -1,10 +1,10 @@
use log::error; use log::error;
use ovlach_data::cv::cv::CV; use ovlach_data::cv::cv::CV;
use rocket::{get, State, response::status::NotFound, http::Status}; use rocket::{get, State, response::Redirect, http::Status};
use rocket_dyn_templates::{Template, context}; use rocket_dyn_templates::Template;
use serde::Serialize; use serde::Serialize;
use crate::{PresentationConfig, services::cv::fetch_cv_data_from_backend, CVBackendConfig}; use crate::{PresentationConfig, services::cv::fetch_cv_data_from_backend, CVBackendConfig, tools::rocket::RequestLanguage};
#[derive(Serialize, Debug)] #[derive(Serialize, Debug)]
struct RootPage { struct RootPage {
@ -14,14 +14,14 @@ struct RootPage {
lang: String, lang: String,
} }
#[get("/")] #[get("/<language>")]
pub async fn index(presentation_config: &State<PresentationConfig>, cv_config: &State<CVBackendConfig>) -> Result<Template, Status> { pub async fn index(presentation_config: &State<PresentationConfig>, cv_config: &State<CVBackendConfig>, language: RequestLanguage) -> Result<Template, Status> {
let context = match fetch_cv_data_from_backend(cv_config.cv_backend_path.clone()).await { let context = match fetch_cv_data_from_backend(cv_config.cv_backend_path.clone()).await {
Ok(cv) => RootPage { Ok(cv) => RootPage {
static_host: presentation_config.static_route.clone(), static_host: presentation_config.static_route.clone(),
cv, cv,
download_cv_url: "FIXME!".to_string(), download_cv_url: "FIXME!".to_string(),
lang: "en".to_string(), lang: language.language,
}, },
Err(e) => { Err(e) => {
error!("Can't fetch CV data from backend {:?}", e); error!("Can't fetch CV data from backend {:?}", e);
@ -29,7 +29,12 @@ pub async fn index(presentation_config: &State<PresentationConfig>, cv_config: &
} }
}; };
//return Templ;
Ok(Template::render("default", &context)) Ok(Template::render("default", &context))
} }
#[get("/")]
pub fn index_without_lang() -> Redirect {
// Default language is czech (TODO: config)
Redirect::to(format!("{}/{}", "", "cs"))
}

View File

@ -1 +1,2 @@
pub mod tera; pub mod tera;
pub mod rocket;

83
src/tools/rocket.rs Normal file
View File

@ -0,0 +1,83 @@
use std::time::SystemTime;
use log::info;
use rocket::{Request, Response, Data, fairing::{Kind, Info, Fairing}, request::{self, FromRequest, FromParam}, http::Status};
use phf::phf_map;
pub struct RequestLanguage {
pub language: String,
}
static LANG_TO_CODES: phf::Map<&'static str, &'static str> = phf_map! {
"cs" => "cs-CZ",
"en" => "en-US",
};
impl<'r> FromParam<'r> for RequestLanguage {
type Error = &'r str;
fn from_param(param: &'r str) -> Result<Self, Self::Error> {
match LANG_TO_CODES.get(param) {
Some(val) => Ok(RequestLanguage {
language: val.to_string(),
}),
None => Ok(RequestLanguage {
language: LANG_TO_CODES["en"].to_string(),
}),
}
}
}
/// Fairing for timing requests.
pub struct RequestTimer;
/// Value stored in request-local state.
#[derive(Copy, Clone)]
struct TimerStart(Option<SystemTime>);
#[rocket::async_trait]
impl Fairing for RequestTimer {
fn info(&self) -> Info {
Info {
name: "Request Timer",
kind: Kind::Request | Kind::Response
}
}
/// Stores the start time of the request in request-local state.
async fn on_request(&self, request: &mut Request<'_>, _: &mut Data<'_>) {
// Store a `TimerStart` instead of directly storing a `SystemTime`
// to ensure that this usage doesn't conflict with anything else
// that might store a `SystemTime` in request-local cache.
request.local_cache(|| TimerStart(Some(SystemTime::now())));
}
/// Adds a header to the response indicating how long the server took to
/// process the request.
async fn on_response<'r>(&self, req: &'r Request<'_>, res: &mut Response<'r>) {
let start_time = req.local_cache(|| TimerStart(None));
if let Some(Ok(duration)) = start_time.0.map(|st| st.elapsed()) {
let ms = duration.as_secs() * 1000 + duration.subsec_millis() as u64;
res.set_raw_header("X-Response-Time", format!("{} ms", ms));
info!("Response time: {} ms", ms);
}
}
}
/// Request guard used to retrieve the start time of a request.
#[derive(Copy, Clone)]
pub struct StartTime(pub SystemTime);
// Allows a route to access the time a request was initiated.
#[rocket::async_trait]
impl<'r> FromRequest<'r> for StartTime {
type Error = ();
async fn from_request(request: &'r Request<'_>) -> request::Outcome<Self, ()> {
match *request.local_cache(|| TimerStart(None)) {
TimerStart(Some(time)) => request::Outcome::Success(StartTime(time)),
TimerStart(None) => request::Outcome::Error((Status::InternalServerError, ())),
}
}
}

View File

@ -4,9 +4,8 @@ use chrono::{Utc, Datelike};
use log::error; use log::error;
use rocket_dyn_templates::tera::{Value, Error}; use rocket_dyn_templates::tera::{Value, Error};
use ovlach_data::cv::chrono::from_string; use ovlach_data::cv::chrono::from_string;
use sha256::{digest, try_digest}; use sha256::digest;
use fluent_resmgr::resource_manager::ResourceManager; use fluent_resmgr::resource_manager::ResourceManager;
use tokio::sync::broadcast::error;
use unic_langid::LanguageIdentifier; use unic_langid::LanguageIdentifier;
// TODO: tenhle modul je trochu prasacky.. // TODO: tenhle modul je trochu prasacky..
@ -23,9 +22,10 @@ pub fn translate_filter(
value: &Value, value: &Value,
args: &HashMap<String, rocket_dyn_templates::tera::Value> args: &HashMap<String, rocket_dyn_templates::tera::Value>
) -> Result<Value, Error> { ) -> Result<Value, Error> {
// TODO: prepis me!
let mgr = ResourceManager::new("./resources/{locale}/{res_id}".into()); let mgr = ResourceManager::new("./resources/{locale}/{res_id}".into());
let locales = vec![LanguageIdentifier::from_str("en-US").unwrap()]; let lang = args["lang"].as_str().unwrap();
let val_id = vec![value.as_str().unwrap().to_string()]; let locales = vec![LanguageIdentifier::from_str(lang).unwrap()];
let val = value.as_str().unwrap().to_string(); let val = value.as_str().unwrap().to_string();
let bundle = mgr.get_bundle(locales, vec!["ovlach_frontend".to_string()]); let bundle = mgr.get_bundle(locales, vec!["ovlach_frontend".to_string()]);
@ -54,7 +54,7 @@ pub fn translate_filter(
pub fn lang_entity( pub fn lang_entity(
value: &Value, value: &Value,
args: &HashMap<String, rocket_dyn_templates::tera::Value> _: &HashMap<String, rocket_dyn_templates::tera::Value>
) -> Result<Value, Error> { ) -> Result<Value, Error> {
error!("{:?}", value); // TODO: rewrite me!!! error!("{:?}", value); // TODO: rewrite me!!!
return Ok(rocket_dyn_templates::tera::Value::String(format!("{}", value["en"].as_str().unwrap()))); // TODO: fix-me here! return Ok(rocket_dyn_templates::tera::Value::String(format!("{}", value["en"].as_str().unwrap()))); // TODO: fix-me here!
@ -62,7 +62,7 @@ pub fn lang_entity(
pub fn gravatar_link( pub fn gravatar_link(
value: &Value, value: &Value,
args: &HashMap<String, rocket_dyn_templates::tera::Value> _: &HashMap<String, rocket_dyn_templates::tera::Value>
) -> Result<Value, Error> { ) -> Result<Value, Error> {
let val = digest(value.as_str().unwrap()); let val = digest(value.as_str().unwrap());
return Ok(rocket_dyn_templates::tera::Value::String(format!("https://gravatar.com/avatar/{}", val))); // TODO: fix-me here! return Ok(rocket_dyn_templates::tera::Value::String(format!("https://gravatar.com/avatar/{}", val))); // TODO: fix-me here!
@ -72,7 +72,7 @@ pub fn insert_space_every(
value: &Value, value: &Value,
args: &HashMap<String, rocket_dyn_templates::tera::Value> args: &HashMap<String, rocket_dyn_templates::tera::Value>
) -> Result<Value, Error> { ) -> Result<Value, Error> {
let mut input = value.as_u64().unwrap().to_string(); let input = value.as_u64().unwrap().to_string();
let times = args.get("times").unwrap().as_u64().unwrap(); let times = args.get("times").unwrap().as_u64().unwrap();
let mut result = String::new(); let mut result = String::new();
@ -88,7 +88,7 @@ pub fn insert_space_every(
pub fn calculate_age( pub fn calculate_age(
value: &Value, value: &Value,
args: &HashMap<String, rocket_dyn_templates::tera::Value> _: &HashMap<String, rocket_dyn_templates::tera::Value>
) -> Result<Value, Error> { ) -> Result<Value, Error> {
error!("{:?}", value.as_str()); error!("{:?}", value.as_str());
//let s = value.to_string().trim_matches('"'); // TODO: unwrap here! //let s = value.to_string().trim_matches('"'); // TODO: unwrap here!