i18n: the find box, sort and filter builders and password toggle in Chinese
The find counts, toggles, replace buttons, the builders' add, join and direction labels, the filter tests and the password tooltip read the builtin catalog. Test::label becomes Test::key, the choice's value; its label is the catalog's. The shipped-copy scan checks button, segment and match-arm text on every match, and zh-CN render tests cover both. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
79e94f7e24
commit
a4bf6965f9
11 files changed
+303
-79
No files matched your search
+45
-31
@@ -152,7 +152,7 @@ impl RenderOnce for FilterBuilder {
|
||||
let emit = Rc::new(emit);
|
||||
let tests: Vec<Choice> = Test::ALL
|
||||
.iter()
|
||||
.map(|test| Choice::new(test.label(), test.label()))
|
||||
.map(|test| Choice::new(test.key(), i18n::text(cx, test.key(), &[])))
|
||||
.collect();
|
||||
let theme = cx.theme();
|
||||
let (rules, any, id) = (self.rules.clone(), self.any, self.id.clone());
|
||||
@@ -187,13 +187,13 @@ impl RenderOnce for FilterBuilder {
|
||||
.child(
|
||||
div().w(theme.label_width() * 0.75).child(
|
||||
Select::new((id.clone(), format!("test-{ix}")), tests.clone())
|
||||
.selected(rule.test.label())
|
||||
.selected(rule.test.key())
|
||||
.size(ControlSize::Sm)
|
||||
.on_change(move |label, window, cx| {
|
||||
let mut next = for_test.clone();
|
||||
next[ix].test = *Test::ALL
|
||||
.iter()
|
||||
.find(|test| test.label() == label.as_ref())
|
||||
.find(|test| test.key() == label.as_ref())
|
||||
.expect("a listed test");
|
||||
test(next, any, window, cx)
|
||||
}),
|
||||
@@ -224,18 +224,21 @@ impl RenderOnce for FilterBuilder {
|
||||
.items_center()
|
||||
.justify_between()
|
||||
.child(
|
||||
Button::new((self.id.clone(), "add"), "Add rule")
|
||||
.icon(IconName::Plus)
|
||||
.variant(ButtonVariant::Ghost)
|
||||
.size(ControlSize::Sm)
|
||||
.on_click(move |_, window, cx| {
|
||||
let mut next = to_add.clone();
|
||||
next.push(FilterRule {
|
||||
column: first.clone(),
|
||||
..FilterRule::default()
|
||||
});
|
||||
add(next, any, window, cx)
|
||||
}),
|
||||
Button::new(
|
||||
(self.id.clone(), "add"),
|
||||
i18n::text(cx, "builder.add_rule", &[]),
|
||||
)
|
||||
.icon(IconName::Plus)
|
||||
.variant(ButtonVariant::Ghost)
|
||||
.size(ControlSize::Sm)
|
||||
.on_click(move |_, window, cx| {
|
||||
let mut next = to_add.clone();
|
||||
next.push(FilterRule {
|
||||
column: first.clone(),
|
||||
..FilterRule::default()
|
||||
});
|
||||
add(next, any, window, cx)
|
||||
}),
|
||||
)
|
||||
.when(self.rules.len() > 1, |footer| {
|
||||
footer.child(
|
||||
@@ -243,8 +246,8 @@ impl RenderOnce for FilterBuilder {
|
||||
(self.id.clone(), "join"),
|
||||
if any { "any" } else { "all" },
|
||||
)
|
||||
.segment("all", "Match all", None)
|
||||
.segment("any", "Match any", None)
|
||||
.segment("all", i18n::text(cx, "builder.all", &[]), None)
|
||||
.segment("any", i18n::text(cx, "builder.any", &[]), None)
|
||||
.size(ControlSize::Sm)
|
||||
.on_change(move |value, window, cx| {
|
||||
join(to_join.clone(), value.as_ref() == "any", window, cx)
|
||||
@@ -365,8 +368,16 @@ impl RenderOnce for SortBuilder {
|
||||
(id.clone(), format!("sort-way-{ix}")),
|
||||
if sort.rising { "up" } else { "down" },
|
||||
)
|
||||
.segment("up", "Ascending", Some(IconName::ArrowUp))
|
||||
.segment("down", "Descending", Some(IconName::ArrowDown))
|
||||
.segment(
|
||||
"up",
|
||||
i18n::text(cx, "builder.up", &[]),
|
||||
Some(IconName::ArrowUp),
|
||||
)
|
||||
.segment(
|
||||
"down",
|
||||
i18n::text(cx, "builder.down", &[]),
|
||||
Some(IconName::ArrowDown),
|
||||
)
|
||||
.size(ControlSize::Sm)
|
||||
.on_change(move |way, window, cx| {
|
||||
let mut next = for_flip.clone();
|
||||
@@ -395,18 +406,21 @@ impl RenderOnce for SortBuilder {
|
||||
.gap_1()
|
||||
.child(list)
|
||||
.children(unused.map(|column| {
|
||||
Button::new((self.id.clone(), "add"), "Add sort")
|
||||
.icon(IconName::Plus)
|
||||
.variant(ButtonVariant::Ghost)
|
||||
.size(ControlSize::Sm)
|
||||
.on_click(move |_, window, cx| {
|
||||
let mut next = add.1.clone();
|
||||
next.push(SortKey {
|
||||
column: column.clone(),
|
||||
rising: true,
|
||||
});
|
||||
(add.0)(&next, window, cx)
|
||||
})
|
||||
Button::new(
|
||||
(self.id.clone(), "add"),
|
||||
i18n::text(cx, "builder.add_sort", &[]),
|
||||
)
|
||||
.icon(IconName::Plus)
|
||||
.variant(ButtonVariant::Ghost)
|
||||
.size(ControlSize::Sm)
|
||||
.on_click(move |_, window, cx| {
|
||||
let mut next = add.1.clone();
|
||||
next.push(SortKey {
|
||||
column: column.clone(),
|
||||
rising: true,
|
||||
});
|
||||
(add.0)(&next, window, cx)
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
+8
-7
@@ -26,14 +26,15 @@ impl Test {
|
||||
Test::Empty,
|
||||
];
|
||||
|
||||
pub fn label(self) -> &'static str {
|
||||
/// Its catalog key, which also names it.
|
||||
pub fn key(self) -> &'static str {
|
||||
match self {
|
||||
Self::Contains => "contains",
|
||||
Self::Equals => "is",
|
||||
Self::NotEquals => "is not",
|
||||
Self::Above => "is above",
|
||||
Self::Below => "is below",
|
||||
Self::Empty => "is empty",
|
||||
Self::Contains => "builder.test.contains",
|
||||
Self::Equals => "builder.test.equals",
|
||||
Self::NotEquals => "builder.test.not_equals",
|
||||
Self::Above => "builder.test.above",
|
||||
Self::Below => "builder.test.below",
|
||||
Self::Empty => "builder.test.empty",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
//! The sort and filter builders read in Chinese under zh-CN.
|
||||
|
||||
use gpui::{IntoElement, Render, TestAppContext, Window, div, prelude::*};
|
||||
|
||||
use crate::{
|
||||
i18n::{self, I18n},
|
||||
tables::{FilterBuilder, FilterRule, SortBuilder, SortKey, Test},
|
||||
theme::Theme,
|
||||
};
|
||||
|
||||
struct Builders;
|
||||
|
||||
impl Render for Builders {
|
||||
fn render(&mut self, _: &mut Window, _: &mut gpui::Context<Self>) -> impl IntoElement {
|
||||
let columns = [("a", "第一列"), ("b", "第二列")];
|
||||
let rule = FilterRule {
|
||||
column: "a".into(),
|
||||
..FilterRule::default()
|
||||
};
|
||||
let key = SortKey {
|
||||
column: "a".into(),
|
||||
rising: true,
|
||||
};
|
||||
div()
|
||||
.size_full()
|
||||
.child(FilterBuilder::new("filter", columns).rules([rule.clone(), rule], true))
|
||||
.child(SortBuilder::new("sort", columns).keys([key]))
|
||||
}
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn the_builders_read_in_chinese(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| {
|
||||
Theme::init(cx);
|
||||
cx.set_global(I18n::new("zh-CN").catalog("zh-CN", &[]));
|
||||
});
|
||||
cx.update(|cx| {
|
||||
for test in Test::ALL {
|
||||
let shown = i18n::text(cx, test.key(), &[]);
|
||||
assert!(
|
||||
!shown.chars().any(|ch| ch.is_ascii_alphabetic()),
|
||||
"{test:?}: {shown}"
|
||||
);
|
||||
}
|
||||
for key in [
|
||||
"builder.add_rule",
|
||||
"builder.all",
|
||||
"builder.any",
|
||||
"builder.add_sort",
|
||||
"builder.up",
|
||||
"builder.down",
|
||||
] {
|
||||
let shown = i18n::text(cx, key, &[]);
|
||||
assert!(
|
||||
!shown.chars().any(|ch| ch.is_ascii_alphabetic()),
|
||||
"{key}: {shown}"
|
||||
);
|
||||
}
|
||||
});
|
||||
let (_, cx) = cx.add_window_view(|_, _| Builders);
|
||||
cx.run_until_parked();
|
||||
}
|
||||
@@ -9,6 +9,7 @@ use gpui::{
|
||||
Styled, TestAppContext, VisualTestContext, Window, div, point, px,
|
||||
};
|
||||
|
||||
mod chinese;
|
||||
mod clipboard;
|
||||
mod columns;
|
||||
mod stale;
|
||||
|
||||
Reference in new issue
Block a user