diff --git a/crates/i18n/README.md b/crates/i18n/README.md index 160cb26b..e151cdc4 100644 --- a/crates/i18n/README.md +++ b/crates/i18n/README.md @@ -51,8 +51,8 @@ The app keeps its `tl!` macro, its language setting (`"auto"` → `I18N.system_l placeholders, filled in one pass. - **Isolation:** when a message or a value inserted into it has a right-to-left letter (bidi class R or AL), or a value has a bidi control (LRE, RLE, PDF, LRO, RLO, LRI, RLI, FSI, PDI, - LRM, RLM, ALM), `fmt` wraps each inserted value in a first-strong isolate (FSI…PDI), and `trn` - wraps `{n}` in a left-to-right one (LRI…PDI), so a Latin file name in an Arabic sentence, a + LRM, RLM, ALM), `fmt` wraps each inserted value in a first-strong isolate (FSI…PDI), and `trn`, + `file_size` and `measure` wrap `{n}` in a left-to-right one (LRI…PDI), so a Latin file name in an Arabic sentence, a Hebrew name in an English one, or an ASCII file name with a right-to-left override can't reorder the words around it. Embeddings, overrides and isolates a value opens are closed inside its isolate, and a stray PDF or PDI in it is dropped. Wrap numbers, paths and codes in `ltr` diff --git a/crates/i18n/src/size.rs b/crates/i18n/src/size.rs index be724bde..db25ddec 100644 --- a/crates/i18n/src/size.rs +++ b/crates/i18n/src/size.rs @@ -5,7 +5,7 @@ //! spacing (`{n} Mo`, `{n} %`). They are looked up with [`I18n::get`], never the plain row: a //! plain `B` row means bold. A byte count in words is the `@plural` row `{n} byte|{n} bytes`. -use crate::{I18n, Lang, fmt}; +use crate::{I18n, Lang, fmt, ltr}; /// The context unit rows are in. const CONTEXT: &str = "unit"; @@ -174,7 +174,7 @@ impl I18n { } fn unit_text(&self, lang: Lang, template: &str, n: &str) -> String { - fmt(self.get(lang, CONTEXT, template).unwrap_or(template), &[("n", n)]) + fmt(self.get(lang, CONTEXT, template).unwrap_or(template), &[("n", <r(n))]) } } @@ -234,6 +234,15 @@ mod tests { assert_eq!(en(100, FileSize::BINARY.smallest(SizeUnit::Kilo).unit_decimals(SizeUnit::Kilo, 0)), "1 KB"); } + #[test] + fn numbers_are_left_to_right_isolates_in_rtl_units() { + let (i18n, problems) = I18n::new(&[("ar", "unit\t{n} KB\t{n} ك.ب\n", "")], &["ar"]); + assert!(problems.is_empty(), "{problems:?}"); + let ar = i18n.from_code("ar").unwrap(); + assert_eq!(i18n.file_size(ar, 1536, FileSize::BINARY), "\u{2066}1.5\u{2069} ك.ب"); + assert_eq!(en(1536, FileSize::BINARY), "1.5 KB", "no marks in left-to-right text"); + } + #[test] fn unit_names_are_catalog_rows() { let (i18n, problems) = I18n::new(E, &["fr"]);