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:
parent
5337e8d7ca
commit
75fb8cf760
|
@ -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"
|
||||
|
|
|
@ -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" }
|
||||
|
|
12
src/dao.rs
12
src/dao.rs
|
@ -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)]
|
||||
|
|
81
src/i18n.rs
81
src/i18n.rs
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
64
src/state.rs
64
src/state.rs
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue