i18n: a locale per app, catalogs of messages, mirrored stacks
i18n::I18n is a gpui global holding the shown locale and one catalog of
messages per locale. text fills each {name} from its arguments; a
missing key, argument or catalog fails, and every catalog holds the
first one's keys. Locale writes numbers with its separators and dates
in CLDR's short numeric form; Hebrew replaces Arabic, whose Egyptian
digits Ely does not write. Direction::row mirrors a stack and
Direction::align sets text against the reading side. The gallery's
chapter 40 shows the messages in seven locales, every locale's number
and date, and a file list that mirrors under Hebrew.
This commit is contained in:
1 parent
17e3e19fc9
commit
ae5f2334f5
9 files changed
+616
-4
No files matched your search
@@ -0,0 +1,106 @@
|
||||
use gpui::Styled;
|
||||
use jiff::{Timestamp, tz::TimeZone};
|
||||
|
||||
use crate::typography::format::{self, Separators};
|
||||
|
||||
/// Which way a line of text runs.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Direction {
|
||||
Ltr,
|
||||
Rtl,
|
||||
}
|
||||
|
||||
impl Direction {
|
||||
/// A flex row that starts at the reading side.
|
||||
pub fn row<E: Styled>(self, element: E) -> E {
|
||||
match self {
|
||||
Direction::Ltr => element.flex().flex_row(),
|
||||
Direction::Rtl => element.flex().flex_row_reverse(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Text set against the reading side.
|
||||
pub fn align<E: Styled>(self, element: E) -> E {
|
||||
match self {
|
||||
Direction::Ltr => element.text_left(),
|
||||
Direction::Rtl => element.text_right(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A language as it writes numbers, dates and lines.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct Locale {
|
||||
pub tag: &'static str,
|
||||
pub name: &'static str,
|
||||
pub separators: Separators,
|
||||
/// A numeric date, as `strftime` reads it.
|
||||
pub date: &'static str,
|
||||
pub direction: Direction,
|
||||
}
|
||||
|
||||
const fn locale(
|
||||
tag: &'static str,
|
||||
name: &'static str,
|
||||
(group, decimal): (char, char),
|
||||
date: &'static str,
|
||||
direction: Direction,
|
||||
) -> Locale {
|
||||
Locale {
|
||||
tag,
|
||||
name,
|
||||
separators: Separators { group, decimal },
|
||||
date,
|
||||
direction,
|
||||
}
|
||||
}
|
||||
|
||||
/// The locales Ely formats for, with CLDR's short numeric dates.
|
||||
pub const LOCALES: [Locale; 7] = [
|
||||
locale(
|
||||
"en-US",
|
||||
"English (US)",
|
||||
(',', '.'),
|
||||
"%-m/%-d/%Y",
|
||||
Direction::Ltr,
|
||||
),
|
||||
locale(
|
||||
"en-GB",
|
||||
"English (UK)",
|
||||
(',', '.'),
|
||||
"%d/%m/%Y",
|
||||
Direction::Ltr,
|
||||
),
|
||||
locale("de-DE", "Deutsch", ('.', ','), "%d.%m.%Y", Direction::Ltr),
|
||||
locale(
|
||||
"fr-FR",
|
||||
"Français",
|
||||
('\u{202f}', ','),
|
||||
"%d/%m/%Y",
|
||||
Direction::Ltr,
|
||||
),
|
||||
locale("ja-JP", "日本語", (',', '.'), "%Y/%m/%d", Direction::Ltr),
|
||||
locale("zh-CN", "中文", (',', '.'), "%Y/%-m/%-d", Direction::Ltr),
|
||||
locale("he-IL", "עברית", (',', '.'), "%-d.%-m.%Y", Direction::Rtl),
|
||||
];
|
||||
|
||||
impl Locale {
|
||||
/// The locale with this tag; fails on one Ely does not list.
|
||||
pub fn of(tag: &str) -> Locale {
|
||||
*LOCALES
|
||||
.iter()
|
||||
.find(|locale| locale.tag == tag)
|
||||
.unwrap_or_else(|| panic!("no locale {tag:?}"))
|
||||
}
|
||||
|
||||
/// `value` with this locale's separators.
|
||||
pub fn number(self, value: f64, decimals: usize) -> String {
|
||||
format::number(value, decimals, self.separators)
|
||||
}
|
||||
|
||||
/// The day `at` falls on in `zone`, written as this locale writes dates.
|
||||
pub fn date(self, at: Timestamp, zone: &TimeZone) -> String {
|
||||
format::datetime(at, zone, self.date)
|
||||
.unwrap_or_else(|error| panic!("{}'s date pattern: {error}", self.tag))
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user