i18n: file sizes and measures fill {n} as a left-to-right isolate

This commit is contained in:
aiongg committed 2026-10-24 02:20:40 +00:00
1 parent c465dd3f47
commit 7cb4a73b3c
2 files changed
+13 -4

No files matched your search

+2 -2
View File
@@ -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`
+11 -2
View File
@@ -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", &ltr(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"]);