mirror of
https://github.com/getzola/zola
synced 2024-11-13 23:57:06 +00:00
Add num_format
filter (#1460)
* Add `num_format` filter for displaying formatted numbers
* Register the filter
* Update docs
* Make `locale` argument required
* Revert "Make `locale` argument required"
This reverts commit 9cdbf28591
.
* Pull the default locale from the site config
* Add note about defaults to the docs
* Add missing borrow
This commit is contained in:
parent
0a7692ad85
commit
93900fb623
5 changed files with 123 additions and 3 deletions
28
Cargo.lock
generated
28
Cargo.lock
generated
|
@ -57,6 +57,15 @@ version = "0.1.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "70033777eb8b5124a81a1889416543dddef2de240019b674c81285a2635a7e1e"
|
checksum = "70033777eb8b5124a81a1889416543dddef2de240019b674c81285a2635a7e1e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arrayvec"
|
||||||
|
version = "0.4.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9"
|
||||||
|
dependencies = [
|
||||||
|
"nodrop",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayvec"
|
name = "arrayvec"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
|
@ -1163,7 +1172,7 @@ version = "0.7.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe"
|
checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec 0.5.2",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"ryu",
|
"ryu",
|
||||||
|
@ -1533,6 +1542,12 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nodrop"
|
||||||
|
version = "0.1.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nom"
|
name = "nom"
|
||||||
version = "5.1.2"
|
version = "5.1.2"
|
||||||
|
@ -1627,6 +1642,16 @@ dependencies = [
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-format"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bafe4179722c2894288ee77a9f044f02811c86af699344c498b0840c698a2465"
|
||||||
|
dependencies = [
|
||||||
|
"arrayvec 0.4.12",
|
||||||
|
"itoa",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-integer"
|
name = "num-integer"
|
||||||
version = "0.1.44"
|
version = "0.1.44"
|
||||||
|
@ -2571,6 +2596,7 @@ dependencies = [
|
||||||
"library",
|
"library",
|
||||||
"mockito",
|
"mockito",
|
||||||
"nom-bibtex",
|
"nom-bibtex",
|
||||||
|
"num-format",
|
||||||
"rendering",
|
"rendering",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
|
@ -12,6 +12,10 @@ pub fn register_early_global_fns(site: &mut Site) -> TeraResult<()> {
|
||||||
site.permalinks.clone(),
|
site.permalinks.clone(),
|
||||||
)?,
|
)?,
|
||||||
);
|
);
|
||||||
|
site.tera.register_filter(
|
||||||
|
"num_format",
|
||||||
|
filters::NumFormatFilter::new(&site.config.default_language),
|
||||||
|
);
|
||||||
|
|
||||||
site.tera.register_function(
|
site.tera.register_function(
|
||||||
"get_url",
|
"get_url",
|
||||||
|
|
|
@ -16,6 +16,7 @@ serde_derive = "1"
|
||||||
sha2 = "0.9"
|
sha2 = "0.9"
|
||||||
url = "2"
|
url = "2"
|
||||||
nom-bibtex = "0.3"
|
nom-bibtex = "0.3"
|
||||||
|
num-format = "0.4"
|
||||||
|
|
||||||
errors = { path = "../errors" }
|
errors = { path = "../errors" }
|
||||||
utils = { path = "../utils" }
|
utils = { path = "../utils" }
|
||||||
|
|
|
@ -6,7 +6,10 @@ use std::path::PathBuf;
|
||||||
use base64::{decode, encode};
|
use base64::{decode, encode};
|
||||||
use config::Config;
|
use config::Config;
|
||||||
use rendering::{render_content, RenderContext};
|
use rendering::{render_content, RenderContext};
|
||||||
use tera::{to_value, try_get_value, Filter as TeraFilter, Result as TeraResult, Tera, Value};
|
use tera::{
|
||||||
|
to_value, try_get_value, Error as TeraError, Filter as TeraFilter, Result as TeraResult, Tera,
|
||||||
|
Value,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::load_tera;
|
use crate::load_tera;
|
||||||
|
|
||||||
|
@ -72,13 +75,43 @@ pub fn base64_decode<S: BuildHasher>(
|
||||||
Ok(to_value(&String::from_utf8(decode(s.as_bytes()).unwrap()).unwrap()).unwrap())
|
Ok(to_value(&String::from_utf8(decode(s.as_bytes()).unwrap()).unwrap()).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct NumFormatFilter {
|
||||||
|
default_language: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NumFormatFilter {
|
||||||
|
pub fn new<S: Into<String>>(default_language: S) -> Self {
|
||||||
|
Self { default_language: default_language.into() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TeraFilter for NumFormatFilter {
|
||||||
|
fn filter(&self, value: &Value, args: &HashMap<String, Value>) -> TeraResult<Value> {
|
||||||
|
use num_format::{Locale, ToFormattedString};
|
||||||
|
|
||||||
|
let num = try_get_value!("num_format", "value", i64, value);
|
||||||
|
let locale = match args.get("locale") {
|
||||||
|
Some(locale) => try_get_value!("num_format", "locale", String, locale),
|
||||||
|
None => self.default_language.clone(),
|
||||||
|
};
|
||||||
|
let locale = Locale::from_name(&locale).map_err(|_| {
|
||||||
|
TeraError::msg(format!(
|
||||||
|
"Filter `num_format` was called with an invalid `locale` argument: `{}`.",
|
||||||
|
locale
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
Ok(to_value(num.to_formatted_string(&locale)).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::{collections::HashMap, path::PathBuf};
|
use std::{collections::HashMap, path::PathBuf};
|
||||||
|
|
||||||
use tera::{to_value, Filter};
|
use tera::{to_value, Filter};
|
||||||
|
|
||||||
use super::{base64_decode, base64_encode, MarkdownFilter};
|
use super::{base64_decode, base64_encode, MarkdownFilter, NumFormatFilter};
|
||||||
use config::Config;
|
use config::Config;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -201,4 +234,44 @@ mod tests {
|
||||||
assert_eq!(result.unwrap(), to_value(expected).unwrap());
|
assert_eq!(result.unwrap(), to_value(expected).unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn num_format_filter() {
|
||||||
|
let tests = vec![
|
||||||
|
(100, "100"),
|
||||||
|
(1_000, "1,000"),
|
||||||
|
(10_000, "10,000"),
|
||||||
|
(100_000, "100,000"),
|
||||||
|
(1_000_000, "1,000,000"),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (input, expected) in tests {
|
||||||
|
let args = HashMap::new();
|
||||||
|
let result = NumFormatFilter::new("en").filter(&to_value(input).unwrap(), &args);
|
||||||
|
let result = dbg!(result);
|
||||||
|
assert!(result.is_ok());
|
||||||
|
assert_eq!(result.unwrap(), to_value(expected).unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn num_format_filter_with_locale() {
|
||||||
|
let tests = vec![
|
||||||
|
("en", 1_000_000, "1,000,000"),
|
||||||
|
("en-IN", 1_000_000, "10,00,000"),
|
||||||
|
// Note:
|
||||||
|
// U+202F is the "NARROW NO-BREAK SPACE" code point.
|
||||||
|
// When displayed to the screen, it looks like a space.
|
||||||
|
("fr", 1_000_000, "1\u{202f}000\u{202f}000"),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (locale, input, expected) in tests {
|
||||||
|
let mut args = HashMap::new();
|
||||||
|
args.insert("locale".to_string(), to_value(locale).unwrap());
|
||||||
|
let result = NumFormatFilter::new("en").filter(&to_value(input).unwrap(), &args);
|
||||||
|
let result = dbg!(result);
|
||||||
|
assert!(result.is_ok());
|
||||||
|
assert_eq!(result.unwrap(), to_value(expected).unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,6 +81,22 @@ Encode the variable to base64.
|
||||||
### base64_decode
|
### base64_decode
|
||||||
Decode the variable from base64.
|
Decode the variable from base64.
|
||||||
|
|
||||||
|
### num_format
|
||||||
|
Format a number into its string representation.
|
||||||
|
|
||||||
|
```jinja2
|
||||||
|
{{ 1000000 | num_format }}
|
||||||
|
<!-- 1,000,000 -->
|
||||||
|
```
|
||||||
|
|
||||||
|
By default this will format the number using the locale set by `config.default_language` in config.toml.
|
||||||
|
|
||||||
|
To format a number for a specific locale, you can use the `locale` argument and pass the name of the desired locale:
|
||||||
|
|
||||||
|
```jinja2
|
||||||
|
{{ 1000000 | num_format(locale="en-IN") }}
|
||||||
|
<!-- 10,00,000 -->
|
||||||
|
```
|
||||||
|
|
||||||
## Built-in functions
|
## Built-in functions
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue