mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-14 19:27:05 -04:00
Merge pull request #490 from cjcwwww/i18n/panel-helper-strings
This commit is contained in:
10 files changed
+197
-39
No files matched your search
@@ -129,7 +129,7 @@ const SIMPLIFIED_CHINESE: &[(&str, &str)] = &[
|
||||
("Casts Shadows", "投射阴影"),
|
||||
("Change Renderer…", "更改渲染器…"),
|
||||
("Changing a shortcut creates a custom preset; the default stays as it is.", "修改快捷键会创建自定义预设;默认预设保持不变。"),
|
||||
("Channel", "色版"),
|
||||
("Channel", "通道"),
|
||||
("Channel:", "通道:"),
|
||||
("Chapter:", "章节:"),
|
||||
("Choose...", "选择..."),
|
||||
@@ -285,7 +285,7 @@ const SIMPLIFIED_CHINESE: &[(&str, &str)] = &[
|
||||
("More ArtCraft apps", "更多 ArtCraft 应用"),
|
||||
("More tutorials", "更多教程"),
|
||||
("Motion Blur", "运动模糊"),
|
||||
("Motion Blur: {}", "动态模糊:{}"),
|
||||
("Motion Blur: {}", "运动模糊:{}"),
|
||||
("Motion Sketch: drag to record", "动态草图:拖动以记录"),
|
||||
("Move Down", "下移"),
|
||||
("Move Up", "上移"),
|
||||
@@ -490,6 +490,64 @@ const SIMPLIFIED_CHINESE: &[(&str, &str)] = &[
|
||||
("⌘/Ctrl-drag a pin to record its motion", "⌘/Ctrl 拖动定位点可记录其运动"),
|
||||
("▣ {}", "▣ {}"),
|
||||
("⚠ {} is used by:", "⚠ {} 已被以下项目使用:"),
|
||||
// Strings a panel used to hand to a helper as a bare literal: the helper draws them (the
|
||||
// viewer's toolbar tooltips and popup items, the solid dialog's checkbox), so the lookup has
|
||||
// to reach the catalog from inside the helper. Kept at the end so the catalogs stay parallel.
|
||||
("3D Reference Axes", "3D 参考轴"),
|
||||
("Affect all layers that use this solid", "影响使用该纯色的所有图层"),
|
||||
("Choose grid and guide options", "选择网格和参考线选项"),
|
||||
("Colorize", "着色"),
|
||||
("Custom", "自定义"),
|
||||
("Custom...", "自定义…"),
|
||||
("Fit", "适合"),
|
||||
("Fit up to 100%", "适合(最大 100%)"),
|
||||
("Full", "完整"),
|
||||
("Half", "一半"),
|
||||
("Only this layer uses this solid.", "仅此图层使用该纯色。"),
|
||||
("Proportional Grid", "比例网格"),
|
||||
("Quarter", "四分之一"),
|
||||
("Region of Interest", "目标区域"),
|
||||
("Reset Exposure", "重置曝光"),
|
||||
("Show Channel and Color Management Settings", "显示通道和色彩管理设置"),
|
||||
("Show Snapshot (hold)", "显示快照(按住)"),
|
||||
("Take Snapshot", "拍摄快照"),
|
||||
("Third", "三分之一"),
|
||||
("Toggle Mask and Shape Path Visibility", "切换蒙版与形状路径可见性"),
|
||||
("Toggle Transparency Grid", "切换透明网格"),
|
||||
// Technical terms and proper nouns: the project settings, the import and export forms, the
|
||||
// viewer's channel and Fast Previews popups, the keyframe dialogs and the About window. The
|
||||
// terms that are the same in Chinese (Display P3, HDTV (Rec. 709), RGB, Alpha, H.264, ProRes…)
|
||||
// need no row: a missing row falls back to the English source, which is the decision.
|
||||
("16 bits per channel", "每通道 16 位"),
|
||||
("32 bits per channel (float)", "每通道 32 位(浮点)"),
|
||||
("8 bits per channel", "每通道 8 位"),
|
||||
("Adaptive Resolution", "自适应分辨率"),
|
||||
("Adobe-style built-in", "Adobe 风格内置"),
|
||||
("Blue", "蓝色"),
|
||||
("Current Settings", "当前设置"),
|
||||
("Disable Per-character 3D", "停用逐字 3D 化"),
|
||||
("Draft", "草图"),
|
||||
("Enable Per-character 3D", "启用逐字 3D 化"),
|
||||
("Fast Draft", "快速草图"),
|
||||
("Fast Previews (Draft 3D, Faster Effects)", "快速预览(3D 草图、更快效果)"),
|
||||
("Green", "绿色"),
|
||||
("Import Image Sequence", "导入图像序列"),
|
||||
("Import PDF / Illustrator File", "导入 PDF / Illustrator 文件"),
|
||||
("Import Photoshop File", "导入 Photoshop 文件"),
|
||||
("Interpret Unlabeled Alpha As", "将未标记的 Alpha 解释为"),
|
||||
("Join the ArtCraft Discord", "加入 ArtCraft Discord"),
|
||||
("Mercury GPU Acceleration ({})", "Mercury GPU 加速({})"),
|
||||
("Mercury Software Only", "仅 Mercury 软件"),
|
||||
("OCIO (built-in ACES config)", "OCIO(内置 ACES 配置)"),
|
||||
("Off (Final Quality)", "关闭(最终质量)"),
|
||||
("OpenGL (compatibility)", "OpenGL(兼容)"),
|
||||
("Opus Bitrate: {} kbps", "Opus 比特率:{} kbps"),
|
||||
("RGB Straight", "RGB 直接"),
|
||||
("Red", "红色"),
|
||||
("Tone map", "色调映射"),
|
||||
("Use In-Window Menu Bar on macOS", "在 macOS 上使用窗口内菜单栏"),
|
||||
("Wireframe", "线框"),
|
||||
("sRGB IEC61966-2.1 (default)", "sRGB IEC61966-2.1(默认)"),
|
||||
];
|
||||
|
||||
const TRADITIONAL_CHINESE: &[(&str, &str)] = &[
|
||||
@@ -907,6 +965,61 @@ const TRADITIONAL_CHINESE: &[(&str, &str)] = &[
|
||||
("⌘/Ctrl-drag a pin to record its motion", "⌘/Ctrl 拖曳釘選點可記錄其運動"),
|
||||
("▣ {}", "▣ {}"),
|
||||
("⚠ {} is used by:", "⚠ {} 已被以下專案使用:"),
|
||||
// The same rows as the Simplified catalog (the tests require both lists to match). The
|
||||
// Traditional wording is the translator's call and is left as the English source for now.
|
||||
("3D Reference Axes", "3D Reference Axes"),
|
||||
("Affect all layers that use this solid", "Affect all layers that use this solid"),
|
||||
("Choose grid and guide options", "Choose grid and guide options"),
|
||||
("Colorize", "Colorize"),
|
||||
("Custom", "Custom"),
|
||||
("Custom...", "Custom..."),
|
||||
("Fit", "Fit"),
|
||||
("Fit up to 100%", "Fit up to 100%"),
|
||||
("Full", "Full"),
|
||||
("Half", "Half"),
|
||||
("Only this layer uses this solid.", "Only this layer uses this solid."),
|
||||
("Proportional Grid", "Proportional Grid"),
|
||||
("Quarter", "Quarter"),
|
||||
("Region of Interest", "Region of Interest"),
|
||||
("Reset Exposure", "Reset Exposure"),
|
||||
("Show Channel and Color Management Settings", "Show Channel and Color Management Settings"),
|
||||
("Show Snapshot (hold)", "Show Snapshot (hold)"),
|
||||
("Take Snapshot", "Take Snapshot"),
|
||||
("Third", "Third"),
|
||||
("Toggle Mask and Shape Path Visibility", "Toggle Mask and Shape Path Visibility"),
|
||||
("Toggle Transparency Grid", "Toggle Transparency Grid"),
|
||||
// The same rows as the Simplified catalog (the tests require both lists to match). The
|
||||
// Traditional wording is the translator's call and stays the English source until then.
|
||||
("16 bits per channel", "16 bits per channel"),
|
||||
("32 bits per channel (float)", "32 bits per channel (float)"),
|
||||
("8 bits per channel", "8 bits per channel"),
|
||||
("Adaptive Resolution", "Adaptive Resolution"),
|
||||
("Adobe-style built-in", "Adobe-style built-in"),
|
||||
("Blue", "Blue"),
|
||||
("Current Settings", "Current Settings"),
|
||||
("Disable Per-character 3D", "Disable Per-character 3D"),
|
||||
("Draft", "Draft"),
|
||||
("Enable Per-character 3D", "Enable Per-character 3D"),
|
||||
("Fast Draft", "Fast Draft"),
|
||||
("Fast Previews (Draft 3D, Faster Effects)", "Fast Previews (Draft 3D, Faster Effects)"),
|
||||
("Green", "Green"),
|
||||
("Import Image Sequence", "Import Image Sequence"),
|
||||
("Import PDF / Illustrator File", "Import PDF / Illustrator File"),
|
||||
("Import Photoshop File", "Import Photoshop File"),
|
||||
("Interpret Unlabeled Alpha As", "Interpret Unlabeled Alpha As"),
|
||||
("Join the ArtCraft Discord", "Join the ArtCraft Discord"),
|
||||
("Mercury GPU Acceleration ({})", "Mercury GPU Acceleration ({})"),
|
||||
("Mercury Software Only", "Mercury Software Only"),
|
||||
("OCIO (built-in ACES config)", "OCIO (built-in ACES config)"),
|
||||
("Off (Final Quality)", "Off (Final Quality)"),
|
||||
("OpenGL (compatibility)", "OpenGL (compatibility)"),
|
||||
("Opus Bitrate: {} kbps", "Opus Bitrate: {} kbps"),
|
||||
("RGB Straight", "RGB Straight"),
|
||||
("Red", "Red"),
|
||||
("Tone map", "Tone map"),
|
||||
("Use In-Window Menu Bar on macOS", "Use In-Window Menu Bar on macOS"),
|
||||
("Wireframe", "Wireframe"),
|
||||
("sRGB IEC61966-2.1 (default)", "sRGB IEC61966-2.1 (default)"),
|
||||
];
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -965,6 +1078,12 @@ mod tests {
|
||||
("panels/viewer.rs", include_str!("../panels/viewer.rs")),
|
||||
("panels/viewer_tools.rs", include_str!("../panels/viewer_tools.rs")),
|
||||
("panels/viewers.rs", include_str!("../panels/viewers.rs")),
|
||||
// Text the interface draws but does not own: the settings pages and the viewer's channel
|
||||
// and Fast Previews lists come from the engine as data, so their source strings live here.
|
||||
// Both files pass the literal scan below unchanged (they hold no drawn literals).
|
||||
("engine settings pages", include_str!("../../../engine/src/prefs.rs")),
|
||||
("engine viewer channels", include_str!("../../../engine/src/viewer.rs")),
|
||||
("engine viewer commands", include_str!("../../../engine/src/commands/viewer_cmds.rs")),
|
||||
];
|
||||
|
||||
/// Call shapes that hand a string literal straight to the user.
|
||||
@@ -976,6 +1095,7 @@ mod tests {
|
||||
".heading(\"",
|
||||
".monospace(\"",
|
||||
".on_hover_text(\"",
|
||||
".on_disabled_hover_text(\"",
|
||||
".hint_text(\"",
|
||||
".text(\"",
|
||||
".prefix(\"",
|
||||
@@ -1039,19 +1159,44 @@ mod tests {
|
||||
}
|
||||
|
||||
/// Every row is asked for by a converted panel: a leftover row means the panel went back to its
|
||||
/// English literal.
|
||||
/// English literal. A string a helper draws for the panel (a toolbar tooltip, a popup item, a
|
||||
/// checkbox label) is looked up inside that helper, so its English source only appears as a
|
||||
/// literal there - `helper_labels_are_translated` keeps those helpers honest.
|
||||
#[test]
|
||||
fn every_row_is_used_by_a_converted_panel() {
|
||||
for (en, _) in catalogs() {
|
||||
let plain = format!("tr({en:?})");
|
||||
let formatted = format!("tr_args({en:?}");
|
||||
let bare = format!("{en:?}");
|
||||
assert!(
|
||||
CONVERTED.iter().any(|(_, src)| src.contains(&plain) || src.contains(&formatted)),
|
||||
CONVERTED.iter().any(|(_, src)| src.contains(&plain) || src.contains(&formatted) || src.contains(&bare)),
|
||||
"{en:?} is in the catalog but no converted panel asks for it"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A label that a helper takes as an argument is drawn by the helper, so the helper has to ask
|
||||
/// the catalog: `Checkbox::new` and `RadioButton::new` are not among the scanned call shapes
|
||||
/// (the label is their second argument), which let the solid dialog's checkbox ship in English.
|
||||
#[test]
|
||||
fn helper_labels_are_translated() {
|
||||
for (path, source) in CONVERTED {
|
||||
for marker in ["Checkbox::new(", "RadioButton::new("] {
|
||||
let mut from = 0;
|
||||
while let Some(at) = source[from..].find(marker) {
|
||||
let at = from + at;
|
||||
let rest = &source[at + marker.len()..];
|
||||
let line = &rest[..rest.find('\n').unwrap_or(rest.len())];
|
||||
if let Some(q) = line.find('"') {
|
||||
let before = line[..q].trim_end();
|
||||
assert!(before.ends_with("tr("), "{path}: the `{marker}` label `{}` is not in the catalog", &line[q..]);
|
||||
}
|
||||
from = at + marker.len();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A newly hard-coded interface string fails here instead of shipping in English to every
|
||||
/// language. It is a heuristic - it looks at the call shapes the panels use - and it ignores
|
||||
/// one-character literals, which are punctuation or a key name rather than a sentence.
|
||||
|
||||
@@ -340,11 +340,13 @@ pub fn show(ui: &mut egui::Ui, d: &mut CompDraft, t: &Tokens, auto: &mut Registr
|
||||
Tab::Renderer => {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(tr("Renderer:"));
|
||||
let r =
|
||||
egui::ComboBox::from_id_salt("comp-renderer").selected_text(if d.advanced_3d { "Advanced 3D" } else { "Classic 3D" }).show_ui(ui, |ui| {
|
||||
let r = egui::ComboBox::from_id_salt("comp-renderer").selected_text(tr(if d.advanced_3d { "Advanced 3D" } else { "Classic 3D" })).show_ui(
|
||||
ui,
|
||||
|ui| {
|
||||
ui.selectable_value(&mut d.advanced_3d, false, tr("Classic 3D"));
|
||||
ui.selectable_value(&mut d.advanced_3d, true, tr("Advanced 3D"));
|
||||
});
|
||||
},
|
||||
);
|
||||
auto.add("dialog.comp.renderer", r.response.rect, "Renderer");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -158,7 +158,8 @@ pub(crate) fn modal(ctx: &egui::Context, title: &str, size: egui::Vec2, t: &Toke
|
||||
.show(ctx, |ui| {
|
||||
ui.set_width(size.x - 36.0);
|
||||
ui.set_min_height(size.y - 36.0);
|
||||
ui.label(egui::RichText::new(title).font(Tokens::semibold(15.0)).color(t.tab_text_active));
|
||||
// The id stays keyed on the untranslated source, so switching language cannot move it.
|
||||
ui.label(egui::RichText::new(tr(title)).font(Tokens::semibold(15.0)).color(t.tab_text_active));
|
||||
ui.add_space(10.0);
|
||||
egui::ScrollArea::both().max_height((available.y - 84.0).max(40.0)).auto_shrink([false, true]).show(ui, body);
|
||||
});
|
||||
@@ -291,7 +292,7 @@ fn about_main(app: &mut EffectcraftApp, ui: &mut egui::Ui, t: &Tokens, cmd: &mut
|
||||
);
|
||||
let foreground = if discord { Color32::WHITE } else { t.text };
|
||||
icons::paint(p, Rect::from_center_size(pos2(r.min.x + 18.0, r.center().y), vec2(15.0, 15.0)), icon, foreground);
|
||||
p.text(pos2(r.min.x + 36.0, r.center().y), Align2::LEFT_CENTER, label, Tokens::medium(12.5), foreground);
|
||||
p.text(pos2(r.min.x + 36.0, r.center().y), Align2::LEFT_CENTER, tr(label), Tokens::medium(12.5), foreground);
|
||||
p.text(
|
||||
pos2(r.max.x - 12.0, r.center().y),
|
||||
Align2::RIGHT_CENTER,
|
||||
@@ -418,8 +419,8 @@ fn solid(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) {
|
||||
if editing.is_some() {
|
||||
ui.add_space(8.0);
|
||||
let r = ui
|
||||
.add_enabled(shared, egui::Checkbox::new(&mut affect_all, "Affect all layers that use this solid"))
|
||||
.on_disabled_hover_text("Only this layer uses this solid.");
|
||||
.add_enabled(shared, egui::Checkbox::new(&mut affect_all, tr("Affect all layers that use this solid")))
|
||||
.on_disabled_hover_text(tr("Only this layer uses this solid."));
|
||||
auto.add("dialog.solid.affectAll", r.rect, "Affect all layers that use this solid");
|
||||
if shared && !affect_all {
|
||||
ui.label(egui::RichText::new(tr("This change will create a new solid for this layer.")).color(t.text_dim).size(11.0));
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//! Keyboard Shortcuts: `shortcut_editor`). Every form runs an engine command with the collected parameters, so whatever a
|
||||
//! dialog does an agent can do with one `engine.execute`.
|
||||
|
||||
use crate::i18n::tr;
|
||||
use crate::i18n::{tr, tr_args};
|
||||
use egui::{Color32, vec2};
|
||||
use serde_json::{Map, Value, json};
|
||||
|
||||
@@ -487,9 +487,11 @@ pub fn open_form(app: &mut EffectcraftApp, id: &str, p: &Value) -> bool {
|
||||
}
|
||||
"file.projectSettings" if p.as_object().is_none_or(|m| m.is_empty()) => {
|
||||
let st = &s.project.settings;
|
||||
// The catalogue row is `Mercury GPU Acceleration ({})`, so the value goes through
|
||||
// tr_args: wrapping the assembled string in tr() would look up the backend's name.
|
||||
let gpu_label = match &s.accel {
|
||||
Some(a) => format!("Mercury GPU Acceleration ({})", a.name()),
|
||||
None => "Mercury GPU Acceleration (no GPU: software)".to_string(),
|
||||
Some(a) => tr_args("Mercury GPU Acceleration ({})", &[&a.name()]),
|
||||
None => tr("Mercury GPU Acceleration (no GPU: software)").to_string(),
|
||||
};
|
||||
let depth = match st.bit_depth.label() {
|
||||
l if l.starts_with("16") => 1,
|
||||
@@ -765,7 +767,7 @@ pub fn show_form(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) {
|
||||
super::dialogs::modal(ctx, &f.title.clone(), vec2(480.0, h), t, |ui| {
|
||||
egui::Grid::new("form-grid").num_columns(2).spacing([14.0, 10.0]).show(ui, |ui| {
|
||||
for (i, fl) in f.fields.iter_mut().enumerate() {
|
||||
ui.label(&fl.label);
|
||||
ui.label(tr(&fl.label));
|
||||
let r = match &mut fl.kind {
|
||||
FieldKind::Number { value, speed } => {
|
||||
let dec = if *speed < 0.01 { 5 } else { 3 };
|
||||
@@ -801,7 +803,7 @@ pub fn show_form(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) {
|
||||
FieldKind::Note(text) => {
|
||||
ui.scope(|ui| {
|
||||
ui.set_width(220.0);
|
||||
ui.add(egui::Label::new(egui::RichText::new(text.as_str()).small()).wrap())
|
||||
ui.add(egui::Label::new(egui::RichText::new(tr(text.as_str())).small()).wrap())
|
||||
})
|
||||
.inner
|
||||
.rect
|
||||
@@ -810,11 +812,11 @@ pub fn show_form(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) {
|
||||
FieldKind::Choice { options, sel } => {
|
||||
let cur = options.get(*sel).map(|o| o.0.clone()).unwrap_or_default();
|
||||
egui::ComboBox::from_id_salt(("form", fl.key.as_str()))
|
||||
.selected_text(cur)
|
||||
.selected_text(tr(&cur))
|
||||
.width(240.0)
|
||||
.show_ui(ui, |ui| {
|
||||
for (i, (l, _)) in options.iter().enumerate() {
|
||||
ui.selectable_value(sel, i, l);
|
||||
ui.selectable_value(sel, i, tr(l));
|
||||
}
|
||||
})
|
||||
.response
|
||||
@@ -888,7 +890,7 @@ pub fn show_info(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) {
|
||||
let (title, body) = app.dialog_state.info.clone();
|
||||
let mut close = false;
|
||||
super::dialogs::modal(ctx, &title, vec2(440.0, 160.0), t, |ui| {
|
||||
ui.label(body);
|
||||
ui.label(tr(&body));
|
||||
ui.add_space(16.0);
|
||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||
if ui.add(egui::Button::new(egui::RichText::new(tr(" OK ")).color(Color32::WHITE)).fill(t.accent)).clicked() {
|
||||
|
||||
@@ -190,9 +190,10 @@ pub fn velocity(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) {
|
||||
|
||||
fn combo(ui: &mut egui::Ui, app: &mut EffectcraftApp, id: &str, sel: &mut usize, opts: &[&str], enabled: bool) {
|
||||
ui.add_enabled_ui(enabled, |ui| {
|
||||
let r = egui::ComboBox::from_id_salt(id).width(200.0).selected_text(opts[*sel]).show_ui(ui, |ui| {
|
||||
// The options are the English source strings; the automation id and label stay English.
|
||||
let r = egui::ComboBox::from_id_salt(id).width(200.0).selected_text(tr(opts[*sel])).show_ui(ui, |ui| {
|
||||
for (i, o) in opts.iter().enumerate() {
|
||||
ui.selectable_value(sel, i, *o);
|
||||
ui.selectable_value(sel, i, tr(o));
|
||||
}
|
||||
});
|
||||
app.auto.add(id, r.response.rect, id);
|
||||
|
||||
@@ -15,6 +15,7 @@ use egui::{Align2, Color32, Rect, Sense, Stroke, pos2, vec2};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::dock::PanelKind;
|
||||
use crate::i18n::tr;
|
||||
use crate::icons::{self, Icon};
|
||||
use crate::theme::Tokens;
|
||||
use crate::{EffectcraftApp, widgets};
|
||||
@@ -120,7 +121,8 @@ pub fn show(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) {
|
||||
}
|
||||
let per_char = layer.props.prop("text/perChar3d").is_some_and(|q| q.value.as_bool());
|
||||
let entries = animator_entries(per_char);
|
||||
let labels: Vec<String> = entries.iter().map(|(l, _)| l.clone()).collect();
|
||||
// The entries are the English source strings; the menu draws them translated.
|
||||
let labels: Vec<String> = entries.iter().map(|(l, _)| tr(l).to_string()).collect();
|
||||
if let Some(i) = widgets::popup_menu(ui, pop, br.left_bottom(), &labels, None) {
|
||||
match entries[i].1 {
|
||||
"" => {}
|
||||
|
||||
@@ -183,7 +183,7 @@ fn output_menu(it: &RenderQueueItem, available: &[OutputFormat], templates: &Ren
|
||||
}
|
||||
v.push(("-".into(), Value::Null));
|
||||
for kbps in [24u32, 32, 64, 96, 128, 192, 256] {
|
||||
v.push((mark(o.opus_bitrate_kbps == kbps, &format!("Opus Bitrate: {kbps} kbps")), json!({"audioBitrate": kbps})));
|
||||
v.push((mark(o.opus_bitrate_kbps == kbps, &tr_args("Opus Bitrate: {} kbps", &[&kbps])), json!({"audioBitrate": kbps})));
|
||||
}
|
||||
for (a, k) in [(OpusApplication::Audio, "audio"), (OpusApplication::Voip, "voice")] {
|
||||
v.push((mark(o.opus_application == a, &tr_args("Opus Tuning: {}", &[&(a.label())])), json!({"opusApplication": k})));
|
||||
|
||||
@@ -50,14 +50,14 @@ fn page_ui(app: &mut EffectcraftApp, ui: &mut egui::Ui, page: &Page, cur: &Value
|
||||
match *item {
|
||||
Item::Section(title) => {
|
||||
ui.add_space(6.0);
|
||||
ui.label(RichText::new(title).font(Tokens::semibold(12.5)).color(t.tab_text_active));
|
||||
ui.label(RichText::new(tr(title)).font(Tokens::semibold(12.5)).color(t.tab_text_active));
|
||||
ui.separator();
|
||||
}
|
||||
Item::Note(text) => {
|
||||
ui.label(RichText::new(text).color(t.text_dim));
|
||||
ui.label(RichText::new(tr(text)).color(t.text_dim));
|
||||
}
|
||||
Item::Button { label, command, params } => {
|
||||
let r = ui.button(label);
|
||||
let r = ui.button(tr(label));
|
||||
reg(app, &format!("settings.button.{}", label.trim_end_matches('.')), &r, label);
|
||||
if r.clicked() {
|
||||
acts.push(Act::Run(command.into(), serde_json::from_str(params).unwrap_or(json!({}))));
|
||||
@@ -96,7 +96,7 @@ fn page_ui(app: &mut EffectcraftApp, ui: &mut egui::Ui, page: &Page, cur: &Value
|
||||
match kind {
|
||||
Kind::Bool => {
|
||||
let mut b = v.as_bool().unwrap_or(false);
|
||||
let r = ui.checkbox(&mut b, label);
|
||||
let r = ui.checkbox(&mut b, tr(label));
|
||||
if r.changed() {
|
||||
acts.push(Act::Set(key.into(), json!(b)));
|
||||
}
|
||||
@@ -104,7 +104,7 @@ fn page_ui(app: &mut EffectcraftApp, ui: &mut egui::Ui, page: &Page, cur: &Value
|
||||
}
|
||||
_ => {
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_sized(vec2(label_w, 18.0), egui::Label::new(format!("{label}:")).truncate());
|
||||
ui.add_sized(vec2(label_w, 18.0), egui::Label::new(format!("{}:", tr(label))).truncate());
|
||||
let r = match kind {
|
||||
Kind::Int(lo, hi, unit) => {
|
||||
let mut x = v.as_i64().unwrap_or(0);
|
||||
@@ -146,11 +146,11 @@ fn page_ui(app: &mut EffectcraftApp, ui: &mut egui::Ui, page: &Page, cur: &Value
|
||||
let curv = v.as_str().map_or_else(|| v.to_string(), str::to_string);
|
||||
let shown = opts.iter().find(|o| o.1 == curv).map(|o| o.0).unwrap_or(curv.as_str()).to_string();
|
||||
egui::ComboBox::from_id_salt(("settings", key))
|
||||
.selected_text(shown)
|
||||
.selected_text(tr(&shown))
|
||||
.width(220.0)
|
||||
.show_ui(ui, |ui| {
|
||||
for (l, val) in opts {
|
||||
if ui.selectable_label(*val == curv, *l).clicked() {
|
||||
if ui.selectable_label(*val == curv, tr(l)).clicked() {
|
||||
acts.push(Act::Set(key.into(), json!(val)));
|
||||
}
|
||||
}
|
||||
@@ -449,7 +449,7 @@ pub fn show(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) {
|
||||
ui.painter().text(
|
||||
r.left_center() + vec2(10.0, 0.0),
|
||||
egui::Align2::LEFT_CENTER,
|
||||
p.title,
|
||||
tr(p.title),
|
||||
Tokens::ui(12.5),
|
||||
if sel { Color32::WHITE } else { t.text },
|
||||
);
|
||||
@@ -466,7 +466,7 @@ pub fn show(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) {
|
||||
// dialog scrolled sideways a little (#284).
|
||||
ui.set_width(ui.available_width());
|
||||
let p = &all[idx];
|
||||
ui.label(RichText::new(p.title).font(Tokens::semibold(14.0)).color(t.tab_text_active));
|
||||
ui.label(RichText::new(tr(p.title)).font(Tokens::semibold(14.0)).color(t.tab_text_active));
|
||||
ui.add_space(6.0);
|
||||
egui::ScrollArea::vertical().max_height(body_h - 30.0).auto_shrink([false, false]).show(ui, |ui| {
|
||||
page_ui(app, ui, p, &cur, t, &mut acts);
|
||||
|
||||
@@ -14,6 +14,7 @@ use effectcraft_engine::time::Tick;
|
||||
use egui::{Align2, Color32, Pos2, Rect, Sense, Stroke, StrokeKind, pos2, vec2};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::i18n::tr;
|
||||
use crate::icons::{self, Icon};
|
||||
use crate::theme::Tokens;
|
||||
use crate::{EffectcraftApp, widgets};
|
||||
@@ -3280,7 +3281,7 @@ fn text_anim_popups(
|
||||
let mut opts: Vec<String> = Vec::new();
|
||||
let mut acts: Vec<(String, serde_json::Value)> = Vec::new();
|
||||
if key == "animate" {
|
||||
opts.push(if per_char { "Disable Per-character 3D" } else { "Enable Per-character 3D" }.into());
|
||||
opts.push(tr(if per_char { "Disable Per-character 3D" } else { "Enable Per-character 3D" }).to_string());
|
||||
acts.push(("layer.enablePerChar3D".into(), json!({"layer": layer.id.0, "enabled": !per_char})));
|
||||
opts.push("-".into());
|
||||
acts.push((String::new(), json!(null)));
|
||||
|
||||
@@ -486,7 +486,9 @@ fn popup(app: &mut EffectcraftApp, ui: &mut egui::Ui, id: &str, anchor: Rect, it
|
||||
ui.separator();
|
||||
continue;
|
||||
}
|
||||
let r = ui.selectable_label(*on, label.as_str());
|
||||
// The item is the English source: the drawn text is looked up in the catalog, while
|
||||
// the automation label stays English for scripts and tests.
|
||||
let r = ui.selectable_label(*on, crate::i18n::tr(label));
|
||||
app.auto.add(&format!("viewer.{auto}.{i}"), r.rect, label);
|
||||
if r.clicked() {
|
||||
chosen = Some(i);
|
||||
@@ -520,10 +522,10 @@ pub(crate) fn resolution_label(res: Resolution, scale: f64) -> String {
|
||||
} else {
|
||||
"Quarter"
|
||||
};
|
||||
format!("({r})")
|
||||
format!("({})", crate::i18n::tr(r))
|
||||
}
|
||||
Resolution::Custom(n) => format!("Custom ({n})"),
|
||||
r => r.label().to_string(),
|
||||
Resolution::Custom(n) => format!("{} ({n})", crate::i18n::tr("Custom")),
|
||||
r => crate::i18n::tr(r.label()).to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -602,7 +604,9 @@ pub(crate) fn bottom_bar(app: &mut EffectcraftApp, ui: &mut egui::Ui, bar: Rect,
|
||||
|
||||
let tog = |ui: &mut egui::Ui, auto: &mut crate::automation::Registry, x: &mut f32, icon: Icon, on: bool, id: &str, tip: &str| -> (bool, Rect) {
|
||||
let r = Rect::from_min_size(pos2(*x, cy - 11.0), vec2(22.0, 22.0));
|
||||
let resp = widgets::icon_button(ui, r, icon, on, &t, egui::Id::new(("vw-btn", id))).on_hover_text(tip);
|
||||
// The tip is the English source: the tooltip is looked up in the catalog, while the
|
||||
// automation label stays English for scripts and tests.
|
||||
let resp = widgets::icon_button(ui, r, icon, on, &t, egui::Id::new(("vw-btn", id))).on_hover_text(crate::i18n::tr(tip));
|
||||
auto.add(&format!("viewer.{id}"), r, tip);
|
||||
*x += 24.0;
|
||||
(resp.clicked(), r)
|
||||
@@ -632,7 +636,7 @@ pub(crate) fn bottom_bar(app: &mut EffectcraftApp, ui: &mut egui::Ui, bar: Rect,
|
||||
}
|
||||
let v = &app.ui.viewer;
|
||||
let items = vec![
|
||||
("Title/Action Safe".to_string(), v.safe_margins),
|
||||
("Title/action safe".to_string(), v.safe_margins),
|
||||
("Proportional Grid".to_string(), v.proportional_grid),
|
||||
("Grid".to_string(), v.grid),
|
||||
("Guides".to_string(), v.guides),
|
||||
|
||||
Reference in new issue
Block a user