mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-26 10:47:11 -06:00
M14.i18n: translate Czech labels at UI render sites
Localize native/web file filters, diagnostic labels, numbered presets and contributor tooltips with English fallback and Czech plural forms. Fix gradient adjective agreement and disambiguate Japanese printer marks in PDF/Print choices and summaries. Keep file extensions and names intact. Add Czech catalog rows and regression coverage; update localization evidence. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
committed by
bflatastic
co-authored by
Claude Opus 5.5
parent
25c60fd0f5
commit
e3dec2e87b
@@ -562,8 +562,12 @@ pub fn status_bar(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||
ui.label(
|
||||
egui::RichText::new(format!(
|
||||
"render {:.1} ms · ui {:.1} ms · {:.0} fps",
|
||||
app.perf.render_ms, app.perf.frame_ms, app.perf.fps
|
||||
"{} {:.1} ms · {} {:.1} ms · {:.0} fps",
|
||||
crate::i18n::tr_ctx(crate::i18n::current(), "diagnostics", "render"),
|
||||
app.perf.render_ms,
|
||||
crate::i18n::tr_ctx(crate::i18n::current(), "diagnostics", "ui"),
|
||||
app.perf.frame_ms,
|
||||
app.perf.fps
|
||||
))
|
||||
.size(10.5)
|
||||
.color(t.text_disabled),
|
||||
|
||||
@@ -87,11 +87,14 @@ impl SortKey {
|
||||
SortKey::LastCommit,
|
||||
];
|
||||
|
||||
/// Menu label and table header, in the UI language (the short count headers stay as they are).
|
||||
/// Menu label and table header, in the UI language (the symbolic count headers stay as they are).
|
||||
pub fn label(self) -> (&'static str, &'static str) {
|
||||
match self {
|
||||
SortKey::Name => (tl!("Name (A–Z)"), tl!("Name")),
|
||||
SortKey::Prs => (tl!("Merged PRs"), "PRs"),
|
||||
SortKey::Prs => {
|
||||
let header = "PRs";
|
||||
(tl!("Merged PRs"), tl!(header))
|
||||
}
|
||||
SortKey::Commits => (tl!("Commits"), tl!("Commits")),
|
||||
SortKey::LinesAdded => (tl!("Lines added"), "+LOC"),
|
||||
SortKey::LinesDeleted => (tl!("Lines deleted"), "−LOC"),
|
||||
@@ -125,18 +128,24 @@ impl Contributor {
|
||||
|
||||
/// One line with everything we know, for tooltips.
|
||||
pub fn summary(&self) -> String {
|
||||
format!(
|
||||
"@{}: {} PRs, {} commits, +{} / −{} lines (Δ {}), +{} / −{} binary assets, {} – {}",
|
||||
self.login,
|
||||
self.prs,
|
||||
self.commits,
|
||||
group(self.lines_added),
|
||||
group(self.lines_deleted),
|
||||
signed(self.lines_delta()),
|
||||
self.binary_added,
|
||||
self.binary_deleted,
|
||||
day(self.first_commit),
|
||||
day(self.last_commit),
|
||||
let lang = crate::i18n::current();
|
||||
let template = "@{login}: {prs}, {commits}, +{added} / −{deleted} lines (Δ {delta}), +{binary_added} / −{binary_deleted} binary assets, {first} – {last}";
|
||||
let prs = crate::i18n::trn(lang, self.prs, "{n} PR", "{n} PRs");
|
||||
let commits = crate::i18n::trn(lang, self.commits, "{n} commit", "{n} commits");
|
||||
crate::i18n::fmt(
|
||||
tl!(template),
|
||||
&[
|
||||
("login", self.login),
|
||||
("prs", &prs),
|
||||
("commits", &commits),
|
||||
("added", &group(self.lines_added)),
|
||||
("deleted", &group(self.lines_deleted)),
|
||||
("delta", &signed(self.lines_delta())),
|
||||
("binary_added", &self.binary_added.to_string()),
|
||||
("binary_deleted", &self.binary_deleted.to_string()),
|
||||
("first", day(self.first_commit)),
|
||||
("last", day(self.last_commit)),
|
||||
],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,7 +304,9 @@ fn details(app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog, b: &mut
|
||||
let saving = !d.bool(SAVING);
|
||||
d.fields.insert(SAVING.into(), json!(saving));
|
||||
if saving && d.str("presetName").is_empty() {
|
||||
d.fields.insert("presetName".into(), json!(format!("Preset {}", app.session.prefs.new_doc_presets.len() + 1)));
|
||||
let template = "Preset {number}";
|
||||
let name = crate::i18n::fmt(tl!(template), &[("number", &(app.session.prefs.new_doc_presets.len() + 1).to_string())]);
|
||||
d.fields.insert("presetName".into(), json!(name));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -272,13 +272,19 @@ pub(super) fn choice<T: Choice>(d: &Dialog, path: &str) -> Option<T> {
|
||||
/// A dropdown of the choices `T` (options for which `enabled` is false are greyed).
|
||||
pub(super) fn pick<T: Choice>(ui: &mut egui::Ui, d: &mut Dialog, path: &str, width: f32, enabled: impl Fn(T) -> bool) -> bool {
|
||||
let current = choice_index::<T>(d, path);
|
||||
let label = current.and_then(|i| T::LABELS.get(i)).copied().unwrap_or_default();
|
||||
let chosen = widgets::dropdown_with(ui, path, label, T::LABELS, width, |i| T::ALL.get(i).is_some_and(|c| enabled(*c)));
|
||||
let labels: Vec<_> = T::LABELS.iter().map(|label| choice_label(crate::i18n::current(), path, label)).collect();
|
||||
let label = current.and_then(|i| labels.get(i)).copied().unwrap_or_default();
|
||||
let chosen = widgets::dropdown_with(ui, path, label, &labels, width, |i| T::ALL.get(i).is_some_and(|c| enabled(*c)));
|
||||
let Some(id) = chosen.and_then(|i| T::IDS.get(i)) else { return false };
|
||||
set(d, path, json!(id));
|
||||
true
|
||||
}
|
||||
|
||||
/// Printer mark styles use adjectives, separately from interface language names.
|
||||
pub(super) fn choice_label<'a>(lang: crate::i18n::Lang, path: &str, label: &'a str) -> &'a str {
|
||||
if path == "marks.kind" { crate::i18n::tr_ctx(lang, "mark-style", label) } else { crate::i18n::tr(lang, label) }
|
||||
}
|
||||
|
||||
pub(super) fn number(ui: &mut egui::Ui, d: &mut Dialog, path: &str, suffix: &str, enabled: bool) {
|
||||
let v = get(d, path).as_f64().unwrap_or(0.0);
|
||||
ui.add_enabled_ui(enabled, |ui| {
|
||||
@@ -785,6 +791,7 @@ pub(super) fn option_rows<'a>(ui: &mut egui::Ui, changed: impl IntoIterator<Item
|
||||
for c in changed {
|
||||
let value = match &c["value"] {
|
||||
Value::Bool(b) => if *b { tl!("On") } else { tl!("Off") }.to_string(),
|
||||
Value::String(s) if c["option"] == "marks.kind" => choice_label(crate::i18n::current(), "marks.kind", s).to_string(),
|
||||
Value::String(s) => s.clone(),
|
||||
other => other.to_string(),
|
||||
};
|
||||
|
||||
@@ -296,3 +296,11 @@ fn simplify_dialog_ok_keeps_exactly_one_undo_with_or_without_preview() {
|
||||
assert!(app.session.doc().unwrap().selection.anchors.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn printer_mark_labels_are_distinct_from_language_names() {
|
||||
let cs = crate::i18n::Lang::from_code("cs").unwrap();
|
||||
assert_eq!(save_pdf::choice_label(cs, "marks.kind", "Japanese"), "Japonské");
|
||||
assert_eq!(save_pdf::choice_label(cs, "language", "Japanese"), "Japonština");
|
||||
assert_eq!(save_pdf::choice_label(crate::i18n::Lang::EN, "marks.kind", "Japanese"), "Japanese");
|
||||
}
|
||||
|
||||
@@ -1651,7 +1651,7 @@ lighting Light Světlo
|
||||
Small Thumbnail View Malé miniatury
|
||||
Swatch Libraries Menu Nabídka knihoven vzorníku
|
||||
User Library Uživatelská knihovna
|
||||
{kind} Gradient Přechod: {kind}
|
||||
{kind} Gradient {kind} přechod
|
||||
{kind}, {model} {kind}, {model}
|
||||
No symbols in this document V tomto dokumentu nejsou žádné symboly
|
||||
Select art and click New Symbol to make one. Vyberte kresbu a symbol vytvořte tlačítkem Nový symbol.
|
||||
@@ -4472,3 +4472,24 @@ effect Regular Pravidelný
|
||||
Artistic Umělecké
|
||||
Film Grain… Filmové zrno…
|
||||
Highlight Area Oblast světel
|
||||
|
||||
# Czech render-site follow-up (AI drafts; native review requested).
|
||||
file-filter All readable files Všechny čitelné soubory
|
||||
file-filter All placeable files Všechny soubory pro umístění
|
||||
file-filter Swatch libraries Knihovny vzorníků
|
||||
file-filter Flattener presets Přednastavení sloučení průhlednosti
|
||||
file-filter PDF presets Přednastavení PDF
|
||||
file-filter Print presets Přednastavení tisku
|
||||
file-filter SVG Compressed Komprimované SVG
|
||||
file-filter PDF-compatible .ai .ai kompatibilní s PDF
|
||||
file-filter VectorCraft Template Šablona VectorCraft
|
||||
file-filter Templates Šablony
|
||||
diagnostics render vykreslení
|
||||
diagnostics ui rozhraní
|
||||
Preset {number} Přednastavení {number}
|
||||
PRs PR
|
||||
@{login}: {prs}, {commits}, +{added} / −{deleted} lines (Δ {delta}), +{binary_added} / −{binary_deleted} binary assets, {first} – {last} @{login}: {prs}, {commits}, řádky +{added} / −{deleted} (Δ {delta}), binární soubory +{binary_added} / −{binary_deleted}, {first} – {last}
|
||||
@plural {n} PR|{n} PRs {n} PR|{n} PR|{n} PR
|
||||
@plural {n} commit|{n} commits {n} commit|{n} commity|{n} commitů
|
||||
gradient-kind Radial Kruhový
|
||||
mark-style Japanese Japonské
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1120,3 +1120,38 @@ fn as_template(s: &str) -> String {
|
||||
out.push_str(rest);
|
||||
out
|
||||
}
|
||||
|
||||
/// Render-site labels keep English fallback, placeholders and Czech grammar independent of
|
||||
/// the plain language-name key. No global language changes: other UI tests may run alongside.
|
||||
#[test]
|
||||
fn czech_render_site_follow_up() {
|
||||
for (source, expected) in [
|
||||
("All readable files", "Všechny čitelné soubory"),
|
||||
("All placeable files", "Všechny soubory pro umístění"),
|
||||
("SVG Compressed", "Komprimované SVG"),
|
||||
("PDF-compatible .ai", ".ai kompatibilní s PDF"),
|
||||
("VectorCraft Template", "Šablona VectorCraft"),
|
||||
] {
|
||||
assert_eq!(tr_ctx(cs(), "file-filter", source), expected);
|
||||
assert_eq!(tr_ctx(Lang::EN, "file-filter", source), source);
|
||||
}
|
||||
for source in ["SVG", "PNG", "VectorCraft", "unregistered filter"] {
|
||||
assert_eq!(tr_ctx(cs(), "file-filter", source), source);
|
||||
}
|
||||
assert_eq!(tr_ctx(cs(), "diagnostics", "render"), "vykreslení");
|
||||
assert_eq!(tr_ctx(cs(), "diagnostics", "ui"), "rozhraní");
|
||||
assert_eq!(fmt(tr(cs(), "Preset {number}"), &[("number", "12")]), "Přednastavení 12");
|
||||
assert_eq!(fmt(tr(Lang::EN, "Preset {number}"), &[("number", "12")]), "Preset 12");
|
||||
for (kind, expected) in [("Linear", "Lineární přechod"), ("Radial", "Kruhový přechod"), ("Freeform", "Volný přechod")] {
|
||||
assert_eq!(fmt(tr(cs(), "{kind} Gradient"), &[("kind", tr_ctx(cs(), "gradient-kind", kind))]), expected);
|
||||
assert_eq!(fmt(tr(Lang::EN, "{kind} Gradient"), &[("kind", tr_ctx(Lang::EN, "gradient-kind", kind))]), format!("{kind} Gradient"));
|
||||
}
|
||||
assert_eq!(tr(cs(), "Japanese"), "Japonština");
|
||||
assert_eq!(tr_ctx(cs(), "mark-style", "Japanese"), "Japonské");
|
||||
assert_eq!(tr_ctx(Lang::EN, "mark-style", "Japanese"), "Japanese");
|
||||
assert_eq!(tr(cs(), "PRs"), "PR");
|
||||
for (n, expected) in [(0, "0 commitů"), (1, "1 commit"), (2, "2 commity"), (4, "4 commity"), (5, "5 commitů")] {
|
||||
assert_eq!(trn(cs(), n, "{n} commit", "{n} commits"), expected);
|
||||
assert_eq!(trn(cs(), n, "{n} PR", "{n} PRs"), format!("{n} PR"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,7 +241,10 @@ pub(crate) fn describe(paint: &Paint, global: bool, spot: bool) -> String {
|
||||
};
|
||||
crate::i18n::fmt(tl!("{kind}, {model}"), &[("kind", kind), ("model", color.model_name())])
|
||||
}
|
||||
Paint::Gradient(g) => crate::i18n::fmt(tl!("{kind} Gradient"), &[("kind", tl!(g.gradient.kind.label()))]),
|
||||
Paint::Gradient(g) => crate::i18n::fmt(
|
||||
tl!("{kind} Gradient"),
|
||||
&[("kind", crate::i18n::tr_ctx(crate::i18n::current(), "gradient-kind", g.gradient.kind.label()))],
|
||||
),
|
||||
Paint::Pattern { .. } => tl!("Pattern").into(),
|
||||
Paint::None => tl!("None").into(),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user