Switch over to relying on the y10n crate

This is still just local but looking promising for segmenting the localization
work I need to do
This commit is contained in:
R Tyler Croy 2021-07-05 09:09:34 -07:00
parent 5337e8d7ca
commit 75fb8cf760
No known key found for this signature in database
GPG Key ID: E5C92681BEF6CEA2
6 changed files with 39 additions and 143 deletions

15
Cargo.lock generated
View File

@ -804,16 +804,15 @@ dependencies = [
"dotenv",
"glob",
"handlebars",
"lazy_static",
"log",
"pretty_env_logger",
"regex",
"rust-embed",
"serde",
"serde_json",
"serde_qs",
"serde_yaml",
"tide",
"y10n",
]
[[package]]
@ -2034,6 +2033,18 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "y10n"
version = "0.1.0"
dependencies = [
"glob",
"lazy_static",
"log",
"regex",
"serde",
"serde_yaml",
]
[[package]]
name = "yaml-rust"
version = "0.4.5"

View File

@ -12,10 +12,8 @@ dotenv = "*"
glob = "0"
# For rendering simple HTTP views
handlebars = { version = "4", features = ["dir_source"] }
lazy_static = "1"
log = "*"
pretty_env_logger = "0.3"
regex = "1"
# Used for embedding templates
rust-embed = "5"
@ -28,3 +26,6 @@ serde_yaml = "0.8"
# Web framework
tide = "*"
# Localization support
y10n = { "path" = "../y10n" }

View File

@ -34,9 +34,9 @@ pub mod v1 {
Ok(path) => {
projects.push(
serde_yaml::from_reader(File::open(path).expect("Failed to open path"))
.expect("Failed to load")
.expect("Failed to load"),
);
},
}
Err(e) => println!("{:?}", e),
}
}
@ -46,17 +46,13 @@ pub mod v1 {
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum ScmType {
Git {
url: String,
},
Git { url: String },
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum TriggerType {
Manual,
Cron {
schedule: String,
},
Cron { schedule: String },
}
#[cfg(test)]

View File

@ -1,81 +0,0 @@
/**
* The i18n module provides some simple internationalization support
* in a way that is compatible with handlebars rendering
*/
use log::*;
pub fn parse_languages(header: &str) -> Vec<Language> {
trace!("Parsing languages from: {}", header);
let mut results = vec![];
for part in header.split(",") {
if let Ok(language) = Language::from(part) {
results.push(language);
}
}
results
}
#[derive(Clone, Debug)]
pub struct Language {
pub code: String,
region: Option<String>,
quality: f64,
}
lazy_static! {
static ref LANG_REGEX: regex::Regex = regex::Regex::new(r"(?P<code>\w+)-?(?P<region>\w+)?(;q=(?P<quality>([0-9]*[.])?[0-9]+)?)?").unwrap();
}
impl Language {
fn from(segment: &str) -> Result<Language, Error> {
if let Some(captures) = LANG_REGEX.captures(segment) {
println!("caps: {:?}", captures);
Ok(Language {
code: captures.name("code").map_or("unknown".to_string(), |c| c.as_str().to_string()),
region: captures.name("region").map_or(None, |c| Some(c.as_str().to_string())),
quality: captures.name("quality").map_or(1.0, |c| c.as_str().parse().unwrap_or(0.0)),
})
}
else {
Err(Error::Generic)
}
}
}
#[derive(Clone, Debug)]
enum Error {
Generic,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn language_from_segment() {
let lang = Language::from("en-US");
assert!(lang.is_ok());
let lang = lang.unwrap();
assert_eq!("en", lang.code);
assert_eq!(Some("US".to_string()), lang.region);
assert_eq!(1.0, lang.quality);
}
#[test]
fn parse_langs_simple() {
let header = "en-US,en;q=0.5";
let langs = parse_languages(&header);
assert_eq!(langs.len(), 2);
}
#[test]
fn parse_langs_multi() {
let header = "en-US,en;q=0.7,de-DE;q=0.3";
let langs = parse_languages(&header);
assert_eq!(langs.len(), 3);
let de = langs.get(2).unwrap();
assert_eq!("de", de.code);
assert_eq!(0.3, de.quality);
}
}

View File

@ -3,13 +3,10 @@
*/
#[macro_use]
extern crate serde_json;
#[macro_use]
extern crate lazy_static;
use log::*;
mod dao;
mod i18n;
mod state;
#[async_std::main]
@ -36,7 +33,7 @@ async fn main() -> Result<(), std::io::Error> {
Some(l) => l.as_str(),
None => "en",
};
let langs = crate::i18n::parse_languages(lang);
let langs = y10n::parse_accept_language(lang);
info!("Lang: {:?}", langs);
req.state().render("index", &langs, Some(data)).await

View File

@ -1,14 +1,12 @@
use async_std::sync::{Arc, RwLock};
use handlebars::Handlebars;
use log::*;
use std::fs::File;
use std::path::Path;
use crate::i18n::Language;
use y10n::*;
#[derive(Clone, Debug)]
pub struct AppState<'a> {
hb: Arc<RwLock<Handlebars<'a>>>,
y10n: Arc<RwLock<Y10n>>,
}
impl AppState<'_> {
@ -17,8 +15,11 @@ impl AppState<'_> {
// This ensures that we get errors rather than empty strings for bad values
hb.set_strict_mode(true);
let y10n = Y10n::from_glob("i18n/**/*.yml");
Self {
hb: Arc::new(RwLock::new(hb)),
y10n: Arc::new(RwLock::new(y10n)),
}
}
@ -44,24 +45,17 @@ impl AppState<'_> {
}
if let Some(data) = &data {
if ! data.is_object() {
warn!("The render function was called without a map, this can lead to funny results");
if !data.is_object() {
warn!(
"The render function was called without a map, this can lead to funny results"
);
}
}
let mut i18n = serde_yaml::Value::Null;
// merge in the langages from lowest priority
for lang in langs.iter().rev() {
let filename = format!("i18n/{}.yml", lang.code);
if ! Path::exists(Path::new(&filename)) {
continue;
}
let y10n = self.y10n.read().await;
let localize = y10n.localize(&langs);
let mut mustache = json!({ "t": localize });
let t: serde_yaml::Value = serde_yaml::from_reader(File::open(filename)?)?;
merge_yaml(&mut i18n, t);
}
let mut mustache = json!({"t" : i18n});
merge(&mut mustache, data.unwrap_or(serde_json::Value::Null));
let hb = self.hb.read().await;
@ -70,11 +64,11 @@ impl AppState<'_> {
let mut body = tide::Body::from_string(view);
body.set_mime("text/html");
Ok(body)
},
}
Err(e) => {
error!("Failed to render {}: {:?}", name, e);
Err(e.into())
},
}
}
}
}
@ -85,11 +79,10 @@ fn merge(a: &mut serde_json::Value, b: serde_json::Value) {
let a = a.as_object_mut().unwrap();
for (k, v) in b {
if v.is_array() && a.contains_key(&k) && a.get(&k).as_ref().unwrap().is_array() {
let mut _a = a.get(&k).unwrap().as_array().unwrap().to_owned();
_a.append(&mut v.as_array().unwrap().to_owned());
a[&k] = serde_json::Value::from(_a);
}
else{
let mut _a = a.get(&k).unwrap().as_array().unwrap().to_owned();
_a.append(&mut v.as_array().unwrap().to_owned());
a[&k] = serde_json::Value::from(_a);
} else {
merge(a.entry(k).or_insert(serde_json::Value::Null), v);
}
}
@ -97,24 +90,3 @@ fn merge(a: &mut serde_json::Value, b: serde_json::Value) {
(a, b) => *a = b,
}
}
fn merge_yaml(a: &mut serde_yaml::Value, b: serde_yaml::Value) {
match (a, b) {
(a @ &mut serde_yaml::Value::Mapping(_), serde_yaml::Value::Mapping(b)) => {
let a = a.as_mapping_mut().unwrap();
for (k, v) in b {
if v.is_sequence() && a.contains_key(&k) && a[&k].is_sequence() {
let mut _b = a.get(&k).unwrap().as_sequence().unwrap().to_owned();
_b.append(&mut v.as_sequence().unwrap().to_owned());
a[&k] = serde_yaml::Value::from(_b);
continue;
}
if !a.contains_key(&k) {a.insert(k.to_owned(), v.to_owned());}
else { merge_yaml(&mut a[&k], v); }
}
}
(a, b) => *a = b,
}
}