Files
effectcraft/crates
FeiLiuEM 383ff2d487 i18n: the panel tabs, and the labels the call-shape scan cannot see
Two English surfaces the translation work so far could not reach.

The dock's tab titles: `PanelKind::title()` is drawn by `dock_ui.rs`'s `tab_titles`, which listed only
the tabs that name a comp, a layer or a script and let every other one fall back to `title()`'s English
- so every panel tab (`Timeline`, `Preview`, `Properties`, `Effects & Presets`, ...) stayed English. The
Window menu already carries a (`window.panel`, title) row in every catalog, so the dock reads that pair
and falls back to the panel catalog for `Markers` (the one panel only it names); `History` and
`ScriptUI Panel` have no row anywhere and keep their English names. `app.auto`'s labels and the dock's
serialized state keep the English title, which the control channel needs.

The scan's blind shapes: `converted_panels_have_no_unwrapped_literals` only sees a literal that follows
a call token, so a label passed to a helper, the third argument of `Painter::text`, a `(key, label, ...)`
table, a `let` binding and a `match` arm were all invisible. The two that can be checked without
guessing are added (`helper_and_painter_labels_are_translated`), with the same single-character
exemption the call scan makes, and the 25 sites they find now ask the catalog (`Apply`, `Analyze`,
`Cancel`, `Create Camera`, `Add Point`, `Remove Point`, `Clear`, `Render`, `Stop`, `Save Current as
Template...`, `New Composition From Footage`, the three search fields, `(no composition)`,
`per second`, `Waveform`, `steps`), with 15 rows added to the Simplified and Traditional catalogs in
the conventions those tables already use (Render 算圖, Apply 套用, Add Point 添加點).

The `uic!` labels and the Keyboard Shortcuts dialog are a different shape and are still to decide
(#1451).

Verified on Linux: cargo fmt --all --check, cargo test -p effectcraft-ui-egui (every suite passes),
cargo clippy -p effectcraft-ui-egui --all-targets -- -D warnings clean. Reverting the dock's catalog
lookup fails `the_dock_draws_every_panel_it_can_in_the_interface_language`; unwrapping a helper or a
painter label fails `helper_and_painter_labels_are_translated` ("panels/project.rs:434: the interface
text \"Search project\" that widgets::search_field draws is not in the catalog").

Also answers #1451's question about languages whose rows have no Simplified row to be a subset
of: six labels the Composition Settings dialog draws from a `(label, value)` table, a tab array and
a `match` arm (`Square Pixels`, `Basic`, `Advanced`, `3D Renderer`, `Black`, `Composition Settings`,
plus `White` from the same `color_name`) had no row anywhere, so every interface drew them in
English; they have Simplified and Traditional rows now, in the existing menu wording. The seventh,
`Lock Aspect Ratio`, is only an automation name - the drawn label is `Lock Aspect Ratio to {}`,
which already had rows, and `comp_settings.rs` built it with `format!` where `dialogs.rs` already
used `tr_args`, so that one is wired now too.

Review fixes (CodeRabbit): the scan now runs over the whole file with line comments blanked, so a
`.text(` call split across lines is still one argument list - which found five more labels the
per-line loop could not see (`No effects. Drag one from Effects & Presets.`, the colour-ring hint in
`fx_editors.rs`, the Learn blurb, the Script Console hint, the Templates blurb, plus
`Building waveform…` from the review), all wired with rows. And the New Composition title is back to
the English source: `modal_fit` keys the modal id and the drag offset on the title (its own comment
says the id stays on the untranslated source) and translates the heading itself, so `tr(..)` there
would have moved the dialog when the interface language changed.

And the argument list is bounded by the call's closing parenthesis: `args_of` used to flush a
trailing argument at end-of-string, so a last argument without a trailing comma carried the `)` and
the source after it, which made the check skip it as "not a literal". Reverting that one line is a
two-line test (a literal last argument in `header.rs` is caught now and was silent before).

The label scan also understands the two string forms a label can be written in: a plain string ends
at the first unescaped quote, a raw string (`r"…"`, `r#"…"#`) at a quote followed by as many `#` as
opened it, and `literal_text` decodes the argument before the exemptions run - so an escaped or raw
label is checked like any other instead of being skipped.

Two more scan fixes from review: `without_comments` tracks the escape state rather than the previous
character, so a string ending in a backslash (`"a\\"`) closes where it should; and the scanned range
ends at the test *module* - the first `#[cfg(test)]` followed by a `mod` - rather than at the first
`#[cfg(test)]`, because `dialogs.rs` keeps a test-only helper above its test module and production
code between them was not scanned.

And call tokens are looked for in a code-only mask: comments blanked, the contents of plain and raw
strings blanked too, with the quotes kept and every byte offset preserved (a blanked character
becomes as many spaces as its UTF-8 length, and a debug assertion pins the mask length). The argument
list is read from the original source at that offset, so a sample string that quotes a call cannot be
mistaken for one. Building the mask from chars instead of bytes shifted every offset after the first
non-ASCII character, which the debug assertion now catches.

The dock's display path has its own test now: `tab_titles` is called with `general.language` set to
Chinese and the labels a window shows are asserted (`Timeline` 时间轴 ...), plus `History` keeping its
English tab and the English interface keeping all of them. Reverting the dock to `PanelKind::title()`
fails it with "Timeline is not drawn in Chinese", which the catalog-only test could not see.
2026-10-29 19:35:09 +08:00
..
2026-10-26 21:54:30 +00:00
2026-10-28 06:09:21 +00:00
2026-10-28 06:09:21 +00:00