i18n: the find box, sort and filter builders and password toggle in Chinese
CI / platforms (windows-latest) (push) Failing after 18s
CI / site (push) Successful in 23s
CI / crate (push) Failing after 13m36s
CI / platforms (ubuntu-24.04) (push) Canceled after 0s

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:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-10-14 20:41:47 -04:00
1 parent 79e94f7e24
commit a4bf6965f9
11 files changed
+303 -79

No files matched your search

+49 -28
View File
@@ -149,6 +149,26 @@ impl FindWidget {
}
}
/// The count shown, and whether it reads as a miss.
pub(crate) fn count(
matches: &Result<(Option<usize>, usize), SharedString>,
cx: &App,
) -> (SharedString, bool) {
match matches {
Ok((_, 0)) => (i18n::text(cx, "find.none", &[]), true),
Ok((Some(current), total)) => {
let (at, total) = ((current + 1).to_string(), total.to_string());
let args = [("at", at.as_str()), ("total", total.as_str())];
(i18n::text(cx, "find.at", &args), false)
}
Ok((None, total)) => {
let total = total.to_string();
(i18n::text(cx, "find.found", &[("total", &total)]), false)
}
Err(message) => (message.clone(), true),
}
}
impl RenderOnce for FindWidget {
fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
let theme = cx.theme();
@@ -156,7 +176,7 @@ impl RenderOnce for FindWidget {
let options = self.options;
let toggle = |key: &'static str,
icon: IconName,
words: &'static str,
words: SharedString,
on: bool,
set: fn(&mut FindOptions, bool)| {
let on_options = self.on_options.clone();
@@ -175,16 +195,11 @@ impl RenderOnce for FindWidget {
}
})
};
let (count, missing) = match &self.matches {
Ok((_, 0)) => ("No results".to_string(), true),
Ok((Some(current), total)) => (format!("{} of {total}", current + 1), false),
Ok((None, total)) => (format!("{total} found"), false),
Err(message) => (message.to_string(), true),
};
let (count, missing) = count(&self.matches, cx);
let found = matches!(self.matches, Ok((_, total)) if total > 0);
let icon_button = |key: &'static str,
icon: IconName,
words: &'static str,
words: SharedString,
enabled: bool,
action: Option<OnBool>,
value: bool| {
@@ -230,7 +245,7 @@ impl RenderOnce for FindWidget {
} else {
IconName::ChevronRight
},
"Toggle replace",
i18n::text(cx, "find.fold", &[]),
true,
Some(fold),
!showing,
@@ -246,21 +261,21 @@ impl RenderOnce for FindWidget {
row.child(toggle(
"case",
IconName::CaseSensitive,
"Match case",
i18n::text(cx, "find.case", &[]),
options.case,
|next, on| next.case = on,
))
.child(toggle(
"word",
IconName::WholeWord,
"Whole word",
i18n::text(cx, "find.word", &[]),
options.word,
|next, on| next.word = on,
))
.child(toggle(
"regex",
IconName::Regex,
"Regular expression",
i18n::text(cx, "find.regex", &[]),
options.regex,
|next, on| next.regex = on,
))
@@ -279,7 +294,7 @@ impl RenderOnce for FindWidget {
.child(icon_button(
"previous",
IconName::ArrowUp,
"Previous match",
i18n::text(cx, "find.previous", &[]),
found,
self.on_step.clone(),
false,
@@ -287,7 +302,7 @@ impl RenderOnce for FindWidget {
.child(icon_button(
"next",
IconName::ArrowDown,
"Next match",
i18n::text(cx, "find.next", &[]),
found,
self.on_step.clone(),
true,
@@ -313,22 +328,28 @@ impl RenderOnce for FindWidget {
.child(Input::new(field).size(ControlSize::Sm)),
)
.child(
Button::new((self.id.clone(), "replace"), "Replace")
.variant(ButtonVariant::Ghost)
.size(ControlSize::Sm)
.disabled(!found)
.when_some(one, |button, one| {
button.on_click(move |_, window, cx| one(false, window, cx))
}),
Button::new(
(self.id.clone(), "replace"),
i18n::text(cx, "find.replace", &[]),
)
.variant(ButtonVariant::Ghost)
.size(ControlSize::Sm)
.disabled(!found)
.when_some(one, |button, one| {
button.on_click(move |_, window, cx| one(false, window, cx))
}),
)
.child(
Button::new((self.id.clone(), "all"), "Replace all")
.variant(ButtonVariant::Ghost)
.size(ControlSize::Sm)
.disabled(!found)
.when_some(all, |button, all| {
button.on_click(move |_, window, cx| all(true, window, cx))
}),
Button::new(
(self.id.clone(), "all"),
i18n::text(cx, "find.replace_all", &[]),
)
.variant(ButtonVariant::Ghost)
.size(ControlSize::Sm)
.disabled(!found)
.when_some(all, |button, all| {
button.on_click(move |_, window, cx| all(true, window, cx))
}),
)
}))
}
+40
View File
@@ -0,0 +1,40 @@
//! The find box reads in Chinese under zh-CN.
use gpui::{Entity, IntoElement, Render, TestAppContext, Window, div, prelude::*};
use crate::{forms::TextInput, i18n::I18n, theme::Theme};
struct Host(Entity<TextInput>, Entity<TextInput>);
impl Render for Host {
fn render(&mut self, _: &mut Window, _: &mut gpui::Context<Self>) -> impl IntoElement {
let find = super::super::FindWidget::new("find", &self.0, Some(0), 2)
.replace(&self.1)
.on_options(|_, _, _| {})
.on_step(|_, _, _| {})
.on_toggle_replace(|_, _, _| {})
.on_close(|_, _| {});
div().size_full().child(find)
}
}
#[gpui::test]
fn the_find_box_reads_in_chinese(cx: &mut TestAppContext) {
cx.update(|cx| {
Theme::init(cx);
cx.set_global(I18n::new("zh-CN").catalog("zh-CN", &[]));
});
cx.update(|cx| {
let count = |matches| super::super::find::count(&matches, cx);
assert_eq!(count(Ok((None, 0))), ("无结果".into(), true));
assert_eq!(count(Ok((Some(0), 2))), ("第 1 项,共 2 项".into(), false));
assert_eq!(count(Ok((None, 3))), ("找到 3 项".into(), false));
});
let (_, cx) = cx.add_window_view(|window, cx| {
Host(
cx.new(|cx| TextInput::new(window, cx)),
cx.new(|cx| TextInput::new(window, cx)),
)
});
cx.run_until_parked();
}
+1
View File
@@ -1,3 +1,4 @@
mod chinese;
mod history;
mod stale;