You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-08-07 17:03:01 +03:00
i18n: utilities to format short dates and times
This commit is contained in:
151
Cargo.lock
generated
151
Cargo.lock
generated
@@ -820,6 +820,16 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "calendrical_calculations"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8dfe3bc6a50b4667fafdb6d9cf26731c5418c457e317d8166c972014facf9a5d"
|
||||
dependencies = [
|
||||
"core_maths",
|
||||
"displaydoc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "camino"
|
||||
version = "1.1.6"
|
||||
@@ -1075,6 +1085,15 @@ version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
||||
|
||||
[[package]]
|
||||
name = "core_maths"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3b02505ccb8c50b0aa21ace0fc08c3e53adebd4e58caa18a36152803c7709a3"
|
||||
dependencies = [
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.12"
|
||||
@@ -2293,6 +2312,79 @@ dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_calendar"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7eb932a690c92f87955e923106181ee0d5682e688ff37fb5c7b296e1fe806edb"
|
||||
dependencies = [
|
||||
"calendrical_calculations",
|
||||
"displaydoc",
|
||||
"icu_calendar_data",
|
||||
"icu_locid",
|
||||
"icu_locid_transform",
|
||||
"icu_provider",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_calendar_data"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22aec7d032735d9acb256eeef72adcac43c3b7572f19b51576a63d664b524ca2"
|
||||
|
||||
[[package]]
|
||||
name = "icu_datetime"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1508c7ed627cc0b031c81203eb98f34433e24b32b39d5b2c0238e4962a00957d"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"either",
|
||||
"fixed_decimal",
|
||||
"icu_calendar",
|
||||
"icu_datetime_data",
|
||||
"icu_decimal",
|
||||
"icu_locid",
|
||||
"icu_locid_transform",
|
||||
"icu_plurals",
|
||||
"icu_provider",
|
||||
"icu_timezone",
|
||||
"smallvec",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_datetime_data"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6abc569cb4ee80b30707566f05c5c9ed4bed765f91ce41e7f5a37c5e6a75b3f"
|
||||
|
||||
[[package]]
|
||||
name = "icu_decimal"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcf994f9ed8061c17bb313f28fba6cffc736f0a16c7fab827efc9b73fd3f7778"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"fixed_decimal",
|
||||
"icu_decimal_data",
|
||||
"icu_locid",
|
||||
"icu_locid_transform",
|
||||
"icu_provider",
|
||||
"writeable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_decimal_data"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df2de3548316b697c70f30dec1395c9212db09df1d86a27624ee24872b71326c"
|
||||
|
||||
[[package]]
|
||||
name = "icu_list"
|
||||
version = "1.4.0"
|
||||
@@ -2408,6 +2500,51 @@ dependencies = [
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_relativetime"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47825312a5eb0790bad7b718fa8d41a8ea1e0ba597b4f7bb84bcfe97d7fc5aba"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"fixed_decimal",
|
||||
"icu_decimal",
|
||||
"icu_locid_transform",
|
||||
"icu_plurals",
|
||||
"icu_provider",
|
||||
"icu_relativetime_data",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_relativetime_data"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05b55cc15ea8981fbba78e9347d0c4003d4490c85f76e9adc7f270290046cae8"
|
||||
|
||||
[[package]]
|
||||
name = "icu_timezone"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b35aabe571a7c653c0f543ff1512b8a1b2ad481cfa24b3d25115298d2ff3b50f"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_calendar",
|
||||
"icu_locid",
|
||||
"icu_provider",
|
||||
"icu_timezone_data",
|
||||
"tinystr",
|
||||
"zerotrie",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_timezone_data"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ceee21e181cce2ab44e95923da6b3418df75369f570df82264c29c51ca398d4"
|
||||
|
||||
[[package]]
|
||||
name = "id-arena"
|
||||
version = "2.2.1"
|
||||
@@ -3072,12 +3209,15 @@ name = "mas-i18n"
|
||||
version = "0.7.0"
|
||||
dependencies = [
|
||||
"camino",
|
||||
"icu_calendar",
|
||||
"icu_datetime",
|
||||
"icu_list",
|
||||
"icu_locid",
|
||||
"icu_locid_transform",
|
||||
"icu_plurals",
|
||||
"icu_provider",
|
||||
"icu_provider_adapters",
|
||||
"icu_relativetime",
|
||||
"pad",
|
||||
"pest",
|
||||
"pest_derive",
|
||||
@@ -7149,6 +7289,17 @@ version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
|
||||
|
||||
[[package]]
|
||||
name = "zerotrie"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0594125a0574fb93059c92c588ab209cc036a23d1baeb3410fa9181bea551a0"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.10.1"
|
||||
|
@@ -13,12 +13,15 @@ workspace = true
|
||||
|
||||
[dependencies]
|
||||
camino.workspace = true
|
||||
icu_calendar = { version = "1.4.0", features = ["compiled_data", "std"] }
|
||||
icu_datetime = { version = "1.4.0", features = ["compiled_data", "std"] }
|
||||
icu_list = { version = "1.4.0", features = ["compiled_data", "std"] }
|
||||
icu_locid = { version = "1.4.0", features = ["std",] }
|
||||
icu_locid_transform = { version = "1.4.0", features = ["compiled_data", "std"] }
|
||||
icu_plurals = { version = "1.4.0", features = ["compiled_data", "std"] }
|
||||
icu_provider = { version = "1.4.0", features = ["std", "sync"] }
|
||||
icu_provider_adapters = { version = "1.4.0", features = ["std"] }
|
||||
icu_relativetime = { version = "0.1.4", features = ["compiled_data", "std"] }
|
||||
pad = "0.1.6"
|
||||
pest = "2.7.6"
|
||||
pest_derive = "2.7.6"
|
||||
|
@@ -16,6 +16,8 @@ pub mod sprintf;
|
||||
pub mod translations;
|
||||
mod translator;
|
||||
|
||||
pub use icu_calendar;
|
||||
pub use icu_datetime;
|
||||
pub use icu_locid::locale;
|
||||
pub use icu_provider::DataLocale;
|
||||
|
||||
|
@@ -24,6 +24,7 @@ use icu_provider::{
|
||||
DataRequest, DataRequestMetadata,
|
||||
};
|
||||
use icu_provider_adapters::fallback::LocaleFallbackProvider;
|
||||
use icu_relativetime::{options::Numeric, RelativeTimeFormatter, RelativeTimeFormatterOptions};
|
||||
use thiserror::Error;
|
||||
use writeable::Writeable;
|
||||
|
||||
@@ -298,6 +299,58 @@ impl Translator {
|
||||
Ok(list)
|
||||
}
|
||||
|
||||
/// Format a relative date
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `locale` - The locale to use.
|
||||
/// * `days` - The number of days to format, where 0 = today, 1 = tomorrow,
|
||||
/// -1 = yesterday, etc.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the requested locale is not found.
|
||||
pub fn relative_date(
|
||||
&self,
|
||||
locale: &DataLocale,
|
||||
days: i64,
|
||||
) -> Result<String, icu_relativetime::RelativeTimeError> {
|
||||
// TODO: this is not using the fallbacker
|
||||
let formatter = RelativeTimeFormatter::try_new_long_day(
|
||||
locale,
|
||||
RelativeTimeFormatterOptions {
|
||||
numeric: Numeric::Auto,
|
||||
},
|
||||
)?;
|
||||
|
||||
let date = formatter.format(days.into());
|
||||
Ok(date.write_to_string().into_owned())
|
||||
}
|
||||
|
||||
/// Format time
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `locale` - The locale to use.
|
||||
/// * `time` - The time to format.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the requested locale is not found.
|
||||
pub fn short_time<T: icu_datetime::input::IsoTimeInput>(
|
||||
&self,
|
||||
locale: &DataLocale,
|
||||
time: &T,
|
||||
) -> Result<String, icu_datetime::DateTimeError> {
|
||||
// TODO: this is not using the fallbacker
|
||||
let formatter = icu_datetime::TimeFormatter::try_new_with_length(
|
||||
locale,
|
||||
icu_datetime::options::length::Time::Short,
|
||||
)?;
|
||||
|
||||
Ok(formatter.format_to_string(time))
|
||||
}
|
||||
|
||||
/// Get a list of available locales.
|
||||
#[must_use]
|
||||
pub fn available_locales(&self) -> Vec<&DataLocale> {
|
||||
|
@@ -325,6 +325,93 @@ impl Object for TranslateFunc {
|
||||
|
||||
Ok(Value::from_safe_string(buf))
|
||||
}
|
||||
|
||||
fn call_method(&self, _state: &State, name: &str, args: &[Value]) -> Result<Value, Error> {
|
||||
match name {
|
||||
"relative_date" => {
|
||||
let (date,): (String,) = from_args(args)?;
|
||||
let date: chrono::DateTime<chrono::Utc> = date.parse().map_err(|e| {
|
||||
Error::new(
|
||||
ErrorKind::InvalidOperation,
|
||||
"Invalid date while calling function `relative_date`",
|
||||
)
|
||||
.with_source(e)
|
||||
})?;
|
||||
|
||||
// TODO: grab the clock somewhere
|
||||
#[allow(clippy::disallowed_methods)]
|
||||
let now = chrono::Utc::now();
|
||||
|
||||
let diff = (date - now).num_days();
|
||||
|
||||
Ok(Value::from(
|
||||
self.translator
|
||||
.relative_date(&self.lang, diff)
|
||||
.map_err(|_e| {
|
||||
Error::new(
|
||||
ErrorKind::InvalidOperation,
|
||||
"Failed to format relative date",
|
||||
)
|
||||
})?,
|
||||
))
|
||||
}
|
||||
|
||||
"short_time" => {
|
||||
let (date,): (String,) = from_args(args)?;
|
||||
let date: chrono::DateTime<chrono::Utc> = date.parse().map_err(|e| {
|
||||
Error::new(
|
||||
ErrorKind::InvalidOperation,
|
||||
"Invalid date while calling function `time`",
|
||||
)
|
||||
.with_source(e)
|
||||
})?;
|
||||
|
||||
// TODO: we should use the user's timezone here
|
||||
let time = date.time();
|
||||
|
||||
Ok(Value::from(
|
||||
self.translator
|
||||
.short_time(&self.lang, &TimeAdapter(time))
|
||||
.map_err(|_e| {
|
||||
Error::new(ErrorKind::InvalidOperation, "Failed to format time")
|
||||
})?,
|
||||
))
|
||||
}
|
||||
|
||||
_ => Err(Error::new(
|
||||
ErrorKind::InvalidOperation,
|
||||
"Invalid method on include_asset",
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An adapter to make a [`Timelike`] implement [`IsoTimeInput`]
|
||||
///
|
||||
/// [`Timelike`]: chrono::Timelike
|
||||
/// [`IsoTimeInput`]: mas_i18n::icu_datetime::input::IsoTimeInput
|
||||
struct TimeAdapter<T>(T);
|
||||
|
||||
impl<T: chrono::Timelike> mas_i18n::icu_datetime::input::IsoTimeInput for TimeAdapter<T> {
|
||||
fn hour(&self) -> Option<mas_i18n::icu_calendar::types::IsoHour> {
|
||||
let hour: usize = chrono::Timelike::hour(&self.0).try_into().ok()?;
|
||||
hour.try_into().ok()
|
||||
}
|
||||
|
||||
fn minute(&self) -> Option<mas_i18n::icu_calendar::types::IsoMinute> {
|
||||
let minute: usize = chrono::Timelike::minute(&self.0).try_into().ok()?;
|
||||
minute.try_into().ok()
|
||||
}
|
||||
|
||||
fn second(&self) -> Option<mas_i18n::icu_calendar::types::IsoSecond> {
|
||||
let second: usize = chrono::Timelike::second(&self.0).try_into().ok()?;
|
||||
second.try_into().ok()
|
||||
}
|
||||
|
||||
fn nanosecond(&self) -> Option<mas_i18n::icu_calendar::types::NanoSecond> {
|
||||
let nanosecond: usize = chrono::Timelike::nanosecond(&self.0).try_into().ok()?;
|
||||
nanosecond.try_into().ok()
|
||||
}
|
||||
}
|
||||
|
||||
struct IncludeAsset {
|
||||
|
Reference in New Issue
Block a user