diff --git a/apps/ely-doc/src/chrome.rs b/apps/ely-doc/src/chrome.rs index a6c3113..c849bf6 100644 --- a/apps/ely-doc/src/chrome.rs +++ b/apps/ely-doc/src/chrome.rs @@ -24,17 +24,18 @@ impl Docs { let Some(counts) = self.active_tab().and_then(|tab| tab.extra.counts) else { return status.when_some(zoom, |status, zoom| status.right(zoom)); }; - let (words, characters) = (counts.words.to_string(), counts.characters.to_string()); + let count = |key, n: u64| ely_ui::i18n::counted(cx, key, n, &[("n", &n.to_string())]); let label = match counts.selected_words { - 0 => Self::text( - cx, - "doc.count.words", - &[("words", &words), ("characters", &characters)], - ), - selected => Self::text( + 0 => { + let words = count("doc.count.word", counts.words); + let characters = count("doc.count.character", counts.characters); + Self::text(cx, "doc.count.words", &[("words", &words), ("characters", &characters)]) + } + selected => ely_ui::i18n::counted( cx, "doc.count.selected", - &[("selected", &selected.to_string()), ("words", &words)], + counts.words, + &[("selected", &selected.to_string()), ("words", &counts.words.to_string())], ), }; status.right(StatusBarItem::new("doc-count").label(label)).when_some(zoom, |status, zoom| status.right(zoom)) diff --git a/apps/ely-doc/src/merge/run.rs b/apps/ely-doc/src/merge/run.rs index bb0f720..62c6f94 100644 --- a/apps/ely-doc/src/merge/run.rs +++ b/apps/ely-doc/src/merge/run.rs @@ -260,9 +260,8 @@ impl Docs { let folder = folder.display().to_string(); let text = match written { Ok(count) => { - let count = count.to_string(); - let text = - Docs::text(cx, "doc.merge.done", &[("n", &count), ("folder", &folder)]); + let n = count.to_string(); + let text = ely_ui::i18n::counted(cx, "doc.merge.done", count as u64, &[("n", &n), ("folder", &folder)]); return docs.toast(text, Severity::Success, cx); } Err((n, error)) => Docs::text( diff --git a/apps/ely-pdf/src/ask.rs b/apps/ely-pdf/src/ask.rs index 52a23bc..dbfa1b4 100644 --- a/apps/ely-pdf/src/ask.rs +++ b/apps/ely-pdf/src/ask.rs @@ -183,7 +183,7 @@ impl Viewer { written.map(|()| targets.len()) }, move |viewer, done, _, cx| match done { - Ok(count) => viewer.toast_output(Self::text(cx, "pdf.split.done", &[("count", &count.to_string())]), opened.clone(), cx), + Ok(count) => viewer.toast_output(ely_ui::i18n::counted(cx, "pdf.split.done", count as u64, &[("count", &count.to_string())]), opened.clone(), cx), Err(error) => viewer.failed_write(&stem, &error, cx), }, cx, @@ -205,8 +205,7 @@ impl Viewer { }; let card = match self.ask.as_ref()? { Ask::Crop { pages, margins, .. } => { - let count = pages.len().to_string(); - let title = Self::text(cx, "pdf.crop.title", &[("count", &count)]); + let title = ely_ui::i18n::counted(cx, "pdf.crop.title", pages.len() as u64, &[("count", &pages.len().to_string())]); let sides = ["top", "right", "bottom", "left"]; let rows = sides.iter().enumerate().fold(DescriptionList::new(), |rows, (at, side)| { let name = label(&format!("pdf.crop.{side}")); diff --git a/apps/ely-pdf/src/ocr.rs b/apps/ely-pdf/src/ocr.rs index 008785f..d9050de 100644 --- a/apps/ely-pdf/src/ocr.rs +++ b/apps/ely-pdf/src/ocr.rs @@ -161,7 +161,7 @@ impl Viewer { cx.notify(); let pages: Vec<(usize, Vec)> = run.read.into_iter().filter(|(_, lines)| replace || !lines.is_empty()).collect(); let (read, skipped) = (pages.len().to_string(), run.skipped.to_string()); - let summary = Self::text(cx, "pdf.ocr.done", &[("read", &read), ("skipped", &skipped)]); + let summary = ely_ui::i18n::counted(cx, "pdf.ocr.done", pages.len() as u64, &[("read", &read), ("skipped", &skipped)]); let Some(view) = self.tab(tab).map(|tab| tab.view.clone()).filter(|_| !pages.is_empty()) else { return self.toast(summary, Severity::Info, cx); }; diff --git a/apps/ely-pdf/src/output.rs b/apps/ely-pdf/src/output.rs index 49b40ee..7b548d7 100644 --- a/apps/ely-pdf/src/output.rs +++ b/apps/ely-pdf/src/output.rs @@ -177,7 +177,7 @@ impl Viewer { }, move |viewer, done, _, cx| match done { Ok(count) => { - let text = Self::text(cx, "pdf.export.images_done", &[("count", &count.to_string())]); + let text = ely_ui::i18n::counted(cx, "pdf.export.images_done", count as u64, &[("count", &count.to_string())]); match folder.clone() { Some(folder) => viewer.toast_output(text, folder, cx), None => viewer.toast(text, Severity::Success, cx), diff --git a/apps/ely-pdf/src/redact.rs b/apps/ely-pdf/src/redact.rs index 53d3225..c9bcfec 100644 --- a/apps/ely-pdf/src/redact.rs +++ b/apps/ely-pdf/src/redact.rs @@ -185,7 +185,7 @@ impl Viewer { }, move |viewer, done, _, cx| match done { Ok(0) => viewer.toast_output(Self::text(cx, "pdf.clean.done", &[("name", &shown)]), opened.clone(), cx), - Ok(n) => viewer.toast_output(Self::text(cx, "pdf.redact.done", &[("name", &shown), ("count", &n.to_string())]), opened.clone(), cx), + Ok(n) => viewer.toast_output(ely_ui::i18n::counted(cx, "pdf.redact.done", n as u64, &[("name", &shown), ("count", &n.to_string())]), opened.clone(), cx), Err(error) => viewer.failed(&error, cx), }, cx, @@ -200,8 +200,8 @@ impl Viewer { let (title, body) = match removal { Removal::Clean { reach, what, .. } => ("pdf.clean.title", self.clean_rows(reach, *what, &this, cx)), Removal::Redact { regions, .. } => { - let count = regions.len().to_string(); - ("pdf.redact.title", div().child(Self::text(cx, "pdf.redact.count", &[("count", &count)])).into_any_element()) + let count = ely_ui::i18n::counted(cx, "pdf.redact.count", regions.len() as u64, &[("count", ®ions.len().to_string())]); + ("pdf.redact.title", div().child(count).into_any_element()) } }; let title = Self::text(cx, title, &[("name", &name)]); diff --git a/apps/ely-show/src/output.rs b/apps/ely-show/src/output.rs index 3fb3816..89bd761 100644 --- a/apps/ely-show/src/output.rs +++ b/apps/ely-show/src/output.rs @@ -292,8 +292,8 @@ impl Deck { }, move |deck, written, _, cx| match written { Ok(count) => { - let count = count.to_string(); - let text = Deck::text(cx, "show.output.pictures_done", &[("n", &count), ("folder", &shown)]); + let n = count.to_string(); + let text = ely_ui::i18n::counted(cx, "show.output.pictures_done", count as u64, &[("n", &n), ("folder", &shown)]); deck.toast_output(text, opened.clone(), cx); } Err(error) if error.kind() == io::ErrorKind::AlreadyExists => { diff --git a/apps/ely-table/src/data.rs b/apps/ely-table/src/data.rs index e7497ee..b9203f1 100644 --- a/apps/ely-table/src/data.rs +++ b/apps/ely-table/src/data.rs @@ -311,7 +311,7 @@ impl Book { move |book, picked, _, window, cx| { let (c1, _, c2, _) = picked.area.expect("a data area"); if c1 != c2 { - return book.warn_key("table.columns.one", cx); + return book.warn_key("table.columns.same", cx); } let other = cx.new(|cx| TextInput::new(window, cx)); book.dialog = Some(Dialog::Columns { diff --git a/apps/ely-table/src/sheets/dialogs.rs b/apps/ely-table/src/sheets/dialogs.rs index 2149076..52d7a5c 100644 --- a/apps/ely-table/src/sheets/dialogs.rs +++ b/apps/ely-table/src/sheets/dialogs.rs @@ -114,19 +114,15 @@ pub(crate) fn delete( let mut detail = if references.formulas + references.names == 0 { Book::text(cx, "table.delete.alone", &[]) } else { - Book::text( - cx, - "table.delete.refs", - &[ - ("formulas", &references.formulas.to_string()), - ("first", &references.first), - ("names", &references.names.to_string()), - ], - ) + let count = |key, n: u64| ely_ui::i18n::counted(cx, key, n, &[("n", &n.to_string())]); + let formulas = count("table.delete.formulas", references.formulas); + let names = count("table.delete.names", references.names); + let args = [("formulas", formulas.as_ref()), ("first", &references.first), ("names", &names)]; + Book::text(cx, "table.delete.refs", &args) }; if references.dynamic > 0 { let count = references.dynamic.to_string(); - let dynamic = Book::text(cx, "table.delete.dynamic", &[("count", &count)]); + let dynamic = ely_ui::i18n::counted(cx, "table.delete.dynamic", references.dynamic, &[("count", &count)]); detail = format!("{detail} {dynamic}").into(); } let label = Book::text(cx, "table.delete.go", &[]); diff --git a/apps/ely-tree/src/asks_places.rs b/apps/ely-tree/src/asks_places.rs index c6077fe..3fd3fe6 100644 --- a/apps/ely-tree/src/asks_places.rs +++ b/apps/ely-tree/src/asks_places.rs @@ -95,9 +95,10 @@ impl Tree { Ask::Force { repo, plan } => { let lost = match plan.lost.len() { 0 => label("tree.force.nothing"), - n => text( + n => ely_ui::i18n::counted( cx, "tree.force.lost", + n as u64, &[("n", &n.to_string()), ("commits", &plan.lost.join("\n"))], ), }; diff --git a/apps/ely-tree/src/pane.rs b/apps/ely-tree/src/pane.rs index a50a1f3..72231a5 100644 --- a/apps/ely-tree/src/pane.rs +++ b/apps/ely-tree/src/pane.rs @@ -101,8 +101,8 @@ impl Tree { }; let lines = (!shown.picked.is_empty() && self.partial(shown)).then(|| { let this = this.clone(); - let n = shown.picked.len().to_string(); - Button::new("tree-lines", text(cx, key, &[("n", &n)])) + let n = shown.picked.len(); + Button::new("tree-lines", ely_ui::i18n::counted(cx, key, n as u64, &[("n", &n.to_string())])) .variant(ButtonVariant::Primary) .size(ControlSize::Sm) .on_click(move |_, _, cx| { diff --git a/apps/ely-tree/src/recovering.rs b/apps/ely-tree/src/recovering.rs index 306bdd7..eb8ebd7 100644 --- a/apps/ely-tree/src/recovering.rs +++ b/apps/ely-tree/src/recovering.rs @@ -205,9 +205,10 @@ impl Tree { let short = |oid: &str| oid[..oid.len().min(7)].to_string(); let left = match plan.left.len() { 0 => label("tree.move.nothing"), - n => text( + n => ely_ui::i18n::counted( cx, "tree.move.left", + n as u64, &[("n", &n.to_string()), ("commits", &plan.left.join("\n"))], ), }; diff --git a/apps/ely-tree/src/view.rs b/apps/ely-tree/src/view.rs index a7c0180..cee2f5e 100644 --- a/apps/ely-tree/src/view.rs +++ b/apps/ely-tree/src/view.rs @@ -213,7 +213,7 @@ impl Tree { 0 => (IconName::CircleCheck, text(cx, "tree.clean", &[])), n => ( IconName::GitBranch, - text(cx, "tree.changed", &[("n", &n.to_string())]), + ely_ui::i18n::counted(cx, "tree.changed", n as u64, &[("n", &n.to_string())]), ), }; EmptyState::new(("tree-summary", opened.id), icon, title).into_any_element() diff --git a/crates/ely-ui/src/i18n.rs b/crates/ely-ui/src/i18n.rs index 647a73e..b4e2a59 100644 --- a/crates/ely-ui/src/i18n.rs +++ b/crates/ely-ui/src/i18n.rs @@ -134,6 +134,16 @@ pub fn install(cx: &mut App) { let tag = locale_tag(cx); let locale = resolve(&tag); log::info!("i18n: locale {tag} -> {locale}"); + let (en, zh) = catalogs(); + cx.set_global( + I18n::new(locale) + .catalog("en-US", &en) + .catalog("zh-CN", &zh), + ); +} + +/// The English and Chinese catalogs, every module's messages. +fn catalogs() -> (Vec<(&'static str, &'static str)>, Vec<(&'static str, &'static str)>) { let en: Vec<_> = EN .iter() .chain(office::EN) @@ -182,11 +192,7 @@ pub fn install(cx: &mut App) { .chain(settings::ZH_CN) .copied() .collect(); - cx.set_global( - I18n::new(locale) - .catalog("en-US", &en) - .catalog("zh-CN", &zh), - ); + (en, zh) } /// `ELY_LOCALE`, then the language setting, then the system's. @@ -203,9 +209,41 @@ pub fn text(cx: &App, key: &str, args: &[(&str, &str)]) -> SharedString { cx.global::().text(key, args) } +/// The message for `n` things: `key.one` when one, else `key`. +pub fn counted(cx: &App, key: &str, n: u64, args: &[(&str, &str)]) -> SharedString { + text(cx, &count_key(key, n), args) +} + +fn count_key(key: &str, n: u64) -> String { + match n { + 1 => format!("{key}.one"), + _ => key.to_string(), + } +} + #[cfg(test)] mod tests { - use super::resolve; + use super::{catalogs, count_key, resolve}; + use std::collections::BTreeSet; + + #[test] + fn one_thing_takes_the_singular() { + assert_eq!(count_key("tree.changed", 1), "tree.changed.one"); + for n in [0, 2, 21] { + assert_eq!(count_key("tree.changed", n), "tree.changed", "{n}"); + } + } + + #[test] + fn both_catalogs_hold_the_same_keys_and_singulars() { + let (en, zh) = catalogs(); + let keys = |list: &[(&str, &str)]| list.iter().map(|(key, _)| key.to_string()).collect::>(); + let (en, zh) = (keys(&en), keys(&zh)); + assert_eq!(en.symmetric_difference(&zh).collect::>(), Vec::<&String>::new()); + for key in en.iter().filter_map(|key| key.strip_suffix(".one")) { + assert!(en.contains(key), "{key}.one has no plural"); + } + } #[test] fn zh_variants_pick_zh_cn_others_pick_en() { diff --git a/crates/ely-ui/src/i18n/data.rs b/crates/ely-ui/src/i18n/data.rs index 1fb03f6..cb68be3 100644 --- a/crates/ely-ui/src/i18n/data.rs +++ b/crates/ely-ui/src/i18n/data.rs @@ -70,7 +70,7 @@ pub(super) const EN: &[(&str, &str)] = &[ ("table.columns.space", "Space"), ("table.columns.other", "Other"), ("table.columns.go", "Split"), - ("table.columns.one", "Select cells in one column."), + ("table.columns.same", "Select cells in one column."), ("table.filter.title", "Column {column}"), ("table.filter.up", "Sort A to Z"), ("table.filter.down", "Sort Z to A"), @@ -230,7 +230,7 @@ pub(super) const ZH_CN: &[(&str, &str)] = &[ ("table.columns.space", "空格"), ("table.columns.other", "其他"), ("table.columns.go", "分列"), - ("table.columns.one", "请选同一列中的单元格。"), + ("table.columns.same", "请选同一列中的单元格。"), ("table.filter.title", "{column} 列"), ("table.filter.up", "升序排序"), ("table.filter.down", "降序排序"), diff --git a/crates/ely-ui/src/i18n/pdf.rs b/crates/ely-ui/src/i18n/pdf.rs index 058f2a8..72f3951 100644 --- a/crates/ely-ui/src/i18n/pdf.rs +++ b/crates/ely-ui/src/i18n/pdf.rs @@ -36,6 +36,7 @@ pub(super) const EN: &[(&str, &str)] = &[ ("pdf.side.marks", "Bookmarks"), ("pdf.page.label", "Page {n}"), ("pdf.crop.title", "Crop {count} pages"), + ("pdf.crop.title.one", "Crop 1 page"), ("pdf.crop.top", "Top"), ("pdf.crop.right", "Right"), ("pdf.crop.bottom", "Bottom"), @@ -45,6 +46,7 @@ pub(super) const EN: &[(&str, &str)] = &[ ("pdf.split.every", "Pages per file"), ("pdf.split.go", "Choose Folder…"), ("pdf.split.done", "{count} files written"), + ("pdf.split.done.one", "1 file written"), ("pdf.extract.taken", "{name} is open; choose another name"), ("pdf.extract.done", "Pages written to {name}"), ("pdf.font.original", "The document's font"), @@ -167,6 +169,7 @@ pub(super) const EN: &[(&str, &str)] = &[ "pdf.ocr.done", "Recognized {read} pages; skipped {skipped}.", ), + ("pdf.ocr.done.one", "Recognized 1 page; skipped {skipped}."), ("pdf.mark.add", "Add Bookmark"), ("pdf.mark.rename", "Rename Bookmark"), ("pdf.mark.delete", "Delete Bookmark"), @@ -195,11 +198,13 @@ pub(super) const EN: &[(&str, &str)] = &[ "pdf.redact.count", "{count} marked regions are removed for good.", ), + ("pdf.redact.count.one", "1 marked region is removed for good."), ("pdf.redact.tail", "redacted"), ( "pdf.redact.done", "Saved {name}: {count} regions removed and checked.", ), + ("pdf.redact.done.one", "Saved {name}: 1 region removed and checked."), ("pdf.clean.open", "Clean Document…"), ("pdf.clean.title", "Clean {name}"), ("pdf.clean.meta", "Properties and metadata ({count})"), @@ -254,6 +259,7 @@ pub(super) const ZH_CN: &[(&str, &str)] = &[ ("pdf.side.marks", "书签"), ("pdf.page.label", "第 {n} 页"), ("pdf.crop.title", "裁剪 {count} 页"), + ("pdf.crop.title.one", "裁剪 1 页"), ("pdf.crop.top", "上"), ("pdf.crop.right", "右"), ("pdf.crop.bottom", "下"), @@ -263,6 +269,7 @@ pub(super) const ZH_CN: &[(&str, &str)] = &[ ("pdf.split.every", "每个文件的页数"), ("pdf.split.go", "选择文件夹…"), ("pdf.split.done", "已写入 {count} 个文件"), + ("pdf.split.done.one", "已写入 1 个文件"), ("pdf.extract.taken", "{name} 已打开,请换一个名称"), ("pdf.extract.done", "页面已写入 {name}"), ("pdf.font.original", "文档自带字体"), @@ -370,6 +377,7 @@ pub(super) const ZH_CN: &[(&str, &str)] = &[ ("pdf.ocr.replace", "重新识别,替换 Ely 此前的文字层"), ("pdf.ocr.reading", "正在识别第 {n} 页,共 {count} 页"), ("pdf.ocr.done", "识别了 {read} 页,跳过 {skipped} 页。"), + ("pdf.ocr.done.one", "识别了 1 页,跳过 {skipped} 页。"), ("pdf.mark.add", "添加书签"), ("pdf.mark.rename", "重命名书签"), ("pdf.mark.delete", "删除书签"), @@ -392,11 +400,13 @@ pub(super) const ZH_CN: &[(&str, &str)] = &[ ("pdf.redact.title", "删除 {name} 中标记的内容"), ("pdf.redact.none", "请先标记要删除的文字或区域。"), ("pdf.redact.count", "将彻底删除 {count} 处标记。"), + ("pdf.redact.count.one", "将彻底删除 1 处标记。"), ("pdf.redact.tail", "已删除"), ( "pdf.redact.done", "已保存 {name}:删除并核对了 {count} 处。", ), + ("pdf.redact.done.one", "已保存 {name}:删除并核对了 1 处。"), ("pdf.clean.open", "清理文档…"), ("pdf.clean.title", "清理 {name}"), ("pdf.clean.meta", "属性与元数据({count})"), diff --git a/crates/ely-ui/src/i18n/play.rs b/crates/ely-ui/src/i18n/play.rs index ca882ec..0e72c03 100644 --- a/crates/ely-ui/src/i18n/play.rs +++ b/crates/ely-ui/src/i18n/play.rs @@ -44,6 +44,7 @@ pub(super) const EN: &[(&str, &str)] = &[ "show.output.pictures_done", "Wrote {n} pictures to {folder}", ), + ("show.output.pictures_done.one", "Wrote 1 picture to {folder}"), ("show.output.failed", "Output failed: {error}"), ("show.check.menu", "Check Presentation…"), ("show.check.title", "Presentation Check"), @@ -97,6 +98,7 @@ pub(super) const ZH_CN: &[(&str, &str)] = &[ ("show.output.taken", "{name} 已存在,未写入任何文件"), ("show.output.done", "已导出 {name}"), ("show.output.pictures_done", "已写入 {n} 张图片到 {folder}"), + ("show.output.pictures_done.one", "已写入 1 张图片到 {folder}"), ("show.output.failed", "输出失败:{error}"), ("show.check.menu", "检查演示…"), ("show.check.title", "演示检查"), diff --git a/crates/ely-ui/src/i18n/secure.rs b/crates/ely-ui/src/i18n/secure.rs index 2dc9b4d..435bd14 100644 --- a/crates/ely-ui/src/i18n/secure.rs +++ b/crates/ely-ui/src/i18n/secure.rs @@ -126,6 +126,7 @@ pub(super) const EN: &[(&str, &str)] = &[ ("pdf.export.tail", "optimized"), ("pdf.export.done", "Saved {name}."), ("pdf.export.images_done", "Saved {count} page images."), + ("pdf.export.images_done.one", "Saved 1 page image."), ( "pdf.export.optimized_done", "Saved {name}: {before} → {after}.", @@ -246,6 +247,7 @@ pub(super) const ZH_CN: &[(&str, &str)] = &[ ("pdf.export.tail", "已优化"), ("pdf.export.done", "已保存 {name}。"), ("pdf.export.images_done", "已保存 {count} 张页面图片。"), + ("pdf.export.images_done.one", "已保存 1 张页面图片。"), ( "pdf.export.optimized_done", "已保存 {name}:{before} → {after}。", diff --git a/crates/ely-ui/src/i18n/table.rs b/crates/ely-ui/src/i18n/table.rs index bc839ac..0f8776f 100644 --- a/crates/ely-ui/src/i18n/table.rs +++ b/crates/ely-ui/src/i18n/table.rs @@ -33,12 +33,17 @@ pub(super) const EN: &[(&str, &str)] = &[ ), ( "table.delete.refs", - "{formulas} formulas on other sheets, first {first}, and {names} named ranges refer to it; they will show #REF!.", + "{formulas} on other sheets, first {first}, and {names} refer to it; they will show #REF!.", ), + ("table.delete.formulas", "{n} formulas"), + ("table.delete.formulas.one", "1 formula"), + ("table.delete.names", "{n} named ranges"), + ("table.delete.names.one", "1 named range"), ( "table.delete.dynamic", "{count} formulas or names use INDIRECT; what they point to is known only when they run, so they may break too.", ), + ("table.delete.dynamic.one", "1 formula or name uses INDIRECT; what it points to is known only when it runs, so it may break too."), ("table.delete.go", "Delete"), ("table.cells.rows_above", "Insert Rows Above"), ("table.cells.rows_below", "Insert Rows Below"), @@ -119,12 +124,17 @@ pub(super) const ZH_CN: &[(&str, &str)] = &[ ), ( "table.delete.refs", - "其他工作表上有 {formulas} 个公式(第一个在 {first})和 {names} 个命名区域引用它;删除后它们显示 #REF!。", + "其他工作表上有 {formulas}(第一个在 {first})和 {names}引用它;删除后它们显示 #REF!。", ), + ("table.delete.formulas", "{n} 个公式"), + ("table.delete.formulas.one", "1 个公式"), + ("table.delete.names", "{n} 个命名区域"), + ("table.delete.names.one", "1 个命名区域"), ( "table.delete.dynamic", "另有 {count} 个公式或名称用 INDIRECT,指向何处要运行时才知道,删除后也可能出错。", ), + ("table.delete.dynamic.one", "另有 1 个公式或名称用 INDIRECT,指向何处要运行时才知道,删除后也可能出错。"), ("table.delete.go", "删除"), ("table.cells.rows_above", "在上方插入行"), ("table.cells.rows_below", "在下方插入行"), diff --git a/crates/ely-ui/src/i18n/tools.rs b/crates/ely-ui/src/i18n/tools.rs index 757a14e..b805b64 100644 --- a/crates/ely-ui/src/i18n/tools.rs +++ b/crates/ely-ui/src/i18n/tools.rs @@ -2,8 +2,13 @@ pub(super) const EN: &[(&str, &str)] = &[ ("menu.tools", "Tools"), - ("doc.count.words", "{words} words, {characters} characters"), + ("doc.count.words", "{words}, {characters}"), ("doc.count.selected", "{selected} of {words} words"), + ("doc.count.selected.one", "{selected} of 1 word"), + ("doc.count.word", "{n} words"), + ("doc.count.word.one", "1 word"), + ("doc.count.character", "{n} characters"), + ("doc.count.character.one", "1 character"), ("office.find.open", "Find and Replace"), ("office.find.stale", "Edited. Enter counts again."), ("doc.spell.ignore_all", "Ignore All"), @@ -50,6 +55,7 @@ pub(super) const EN: &[(&str, &str)] = &[ ("doc.merge.unreadable", "{name} cannot be read: {error}"), ("doc.merge.exists", "{name} exists. Nothing was written."), ("doc.merge.done", "Wrote {n} files to {folder}"), + ("doc.merge.done.one", "Wrote 1 file to {folder}"), ( "doc.merge.failed", "The merge stopped at record {n}: {error}", @@ -58,8 +64,13 @@ pub(super) const EN: &[(&str, &str)] = &[ pub(super) const ZH_CN: &[(&str, &str)] = &[ ("menu.tools", "工具"), - ("doc.count.words", "{words} 字,{characters} 字符"), + ("doc.count.words", "{words},{characters}"), ("doc.count.selected", "已选 {selected} / {words} 字"), + ("doc.count.selected.one", "已选 {selected} / 1 字"), + ("doc.count.word", "{n} 字"), + ("doc.count.word.one", "1 字"), + ("doc.count.character", "{n} 字符"), + ("doc.count.character.one", "1 字符"), ("office.find.open", "查找和替换"), ("office.find.stale", "已改动,回车重新计数"), ("doc.spell.ignore_all", "全部忽略"), @@ -103,5 +114,6 @@ pub(super) const ZH_CN: &[(&str, &str)] = &[ ("doc.merge.unreadable", "无法读取 {name}:{error}"), ("doc.merge.exists", "{name} 已存在,未写入任何文件。"), ("doc.merge.done", "已写入 {n} 个文件到 {folder}"), + ("doc.merge.done.one", "已写入 1 个文件到 {folder}"), ("doc.merge.failed", "合并在第 {n} 条停止:{error}"), ]; diff --git a/crates/ely-ui/src/i18n/tree.rs b/crates/ely-ui/src/i18n/tree.rs index 81627a0..9fba88c 100644 --- a/crates/ely-ui/src/i18n/tree.rs +++ b/crates/ely-ui/src/i18n/tree.rs @@ -17,6 +17,7 @@ pub(super) const EN: &[(&str, &str)] = &[ ("tree.clone.busy", "A clone is already running."), ("tree.clean", "Nothing to commit"), ("tree.changed", "{n} files changed"), + ("tree.changed.one", "1 file changed"), ("tree.no_git", "Git is not available"), ("tree.unreadable", "This repository could not be read"), ("tree.branch.detached", "Detached at {oid}"), @@ -62,7 +63,9 @@ pub(super) const EN: &[(&str, &str)] = &[ ("tree.layout.unified", "Unified"), ("tree.layout.split", "Side by Side"), ("tree.lines.stage", "Stage {n} Lines"), + ("tree.lines.stage.one", "Stage 1 Line"), ("tree.lines.unstage", "Unstage {n} Lines"), + ("tree.lines.unstage.one", "Unstage 1 Line"), ("tree.hunk.stage", "Stage Hunk"), ("tree.hunk.unstage", "Unstage Hunk"), ("tree.open_file", "Open File"), @@ -144,6 +147,7 @@ pub(super) const ZH_CN: &[(&str, &str)] = &[ ("tree.clone.busy", "已有克隆正在进行。"), ("tree.clean", "没有要提交的改动"), ("tree.changed", "{n} 个文件有改动"), + ("tree.changed.one", "1 个文件有改动"), ("tree.no_git", "Git 不可用"), ("tree.unreadable", "无法读取此仓库"), ("tree.branch.detached", "游离于 {oid}"), @@ -183,7 +187,9 @@ pub(super) const ZH_CN: &[(&str, &str)] = &[ ("tree.layout.unified", "行内"), ("tree.layout.split", "并排"), ("tree.lines.stage", "暂存 {n} 行"), + ("tree.lines.stage.one", "暂存 1 行"), ("tree.lines.unstage", "取消暂存 {n} 行"), + ("tree.lines.unstage.one", "取消暂存 1 行"), ("tree.hunk.stage", "暂存此块"), ("tree.hunk.unstage", "取消暂存此块"), ("tree.open_file", "打开文件"), diff --git a/crates/ely-ui/src/i18n/tree_places.rs b/crates/ely-ui/src/i18n/tree_places.rs index 1317582..66fc8aa 100644 --- a/crates/ely-ui/src/i18n/tree_places.rs +++ b/crates/ely-ui/src/i18n/tree_places.rs @@ -46,6 +46,7 @@ pub(super) const EN: &[(&str, &str)] = &[ "tree.force.lost", "These {n} commits on the remote would be overwritten:\n{commits}", ), + ("tree.force.lost.one", "This commit on the remote would be overwritten:\n{commits}"), ("tree.stash.open", "Stash Changes…"), ("tree.stash.title", "Stash changes"), ("tree.stash.message", "Message"), @@ -131,6 +132,7 @@ pub(super) const EN: &[(&str, &str)] = &[ "tree.move.left", "These {n} commits leave it; the reflog still finds them:\n{commits}", ), + ("tree.move.left.one", "This commit leaves it; the reflog still finds it:\n{commits}"), ("tree.clean.open", "Clean Untracked Files…"), ("tree.clean.title", "Clean untracked files"), ("tree.clean.ignored", "Include ignored files"), @@ -186,6 +188,7 @@ pub(super) const ZH_CN: &[(&str, &str)] = &[ ), ("tree.force.nothing", "远端没有本分支缺少的提交。"), ("tree.force.lost", "远端这 {n} 个提交会被覆盖:\n{commits}"), + ("tree.force.lost.one", "远端这 1 个提交会被覆盖:\n{commits}"), ("tree.stash.open", "储藏改动…"), ("tree.stash.title", "储藏改动"), ("tree.stash.message", "说明"), @@ -265,6 +268,7 @@ pub(super) const ZH_CN: &[(&str, &str)] = &[ "tree.move.left", "这 {n} 个提交将离开该分支,引用日志里仍可找回:\n{commits}", ), + ("tree.move.left.one", "这 1 个提交将离开该分支,引用日志里仍可找回:\n{commits}"), ("tree.clean.open", "清理未跟踪的文件…"), ("tree.clean.title", "清理未跟踪的文件"), ("tree.clean.ignored", "包括被忽略的文件"),