Follow-up to #2981: a missing Chinese effect row draws English instead of failing the build, the font probe sees effect and category characters, coverage totals recounted

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon ThomasandClaude Opus 5.5 committed 2026-11-03 09:52:40 -05:00
1 parent 3ae9c8dbc2
commit d16c720e59
3 files changed
+31 -40

No files matched your search

+20 -31
View File
@@ -105,6 +105,13 @@ pub(crate) fn name(id: &str, english: &str) -> String {
catalog(current()).and_then(|c| c.row(id)).map_or_else(|| english.to_string(), |(_, translated)| translated.to_string())
}
/// The characters of `language`'s effect names and category labels, so the interface's font probe
/// registers a face for each of them as it does for the panel catalog's.
#[cfg(not(target_arch = "wasm32"))]
pub(super) fn characters(language: &str) -> impl Iterator<Item = char> {
catalog(language).into_iter().flat_map(|c| c.rows.iter().map(|(_, _, t)| *t).chain(c.categories.iter().map(|(_, t)| *t))).flat_map(str::chars)
}
/// The number of effect-name rows `language` translates, for the row-count report; 0 when it has
/// no catalog or when it is English.
#[cfg(test)]
@@ -2248,8 +2255,8 @@ mod tests {
}
/// The languages whose tables are complete: one row per effect in the registry and one row
/// per category. A translator moves a language here from [`PARTIAL`] when its table is done;
/// the completeness tests then fail if a new effect or category arrives without a row.
/// per category when they were written. A new effect without a row draws its English name (as
/// in every language), so it does not block other work; the rows are guarded like [`PARTIAL`]'s.
const COMPLETE: &[(&str, &[Row])] = &[("zh-hans", SIMPLIFIED_CHINESE), ("zh-hant", TRADITIONAL_CHINESE)];
/// The languages whose tables are being filled in: guarded row by row.
@@ -2276,35 +2283,6 @@ mod tests {
}
}
/// A complete language translates every effect the registry has and labels every category, so
/// a new effect or category cannot ship in English to a Chinese interface.
#[test]
fn a_complete_language_covers_the_registry() {
let registry = effects::registry();
let categories = effects::categories();
for (language, rows) in COMPLETE {
for spec in registry {
assert!(
rows.iter().any(|(id, _, _)| *id == spec.id),
"{language} has no row for {} ({}): a complete table covers every effect",
spec.name,
spec.id
);
}
let labels = match *language {
"zh-hans" => SIMPLIFIED_CHINESE_CATEGORIES,
"zh-hant" => TRADITIONAL_CHINESE_CATEGORIES,
other => panic!("{other} is complete but has no category table in this test"),
};
for source in &categories {
assert!(
labels.iter().any(|(english, _)| english == source),
"{language} has no category label for {source}: a complete table covers every category"
);
}
}
}
/// Every category row of every language names a category that exists, else the generated
/// Effect menu and Effects & Presets would carry a label no folder has.
#[test]
@@ -2420,6 +2398,17 @@ mod category_name_tests {
}
}
/// The interface's font probe sees the effect names and category labels, not only the panel
/// catalog: `鑲` appears only in Flange & Chorus (`鑲邊與合聲`), and a sparse Han face missing
/// it would otherwise draw a box.
#[test]
fn the_font_probe_sees_effect_and_category_characters() {
let chars = crate::i18n::catalog_characters("zh-hant");
assert!(chars.contains(&'鑲'), "an effect name's character is probed");
let (_, label) = TRADITIONAL_CHINESE_CATEGORIES.first().copied().unwrap_or_default();
assert!(label.chars().all(|ch| chars.contains(&ch)), "{label}: a category label's characters are probed");
}
/// The same for Traditional Chinese: one repeated label would hide a category behind another
/// in the Effect menu.
#[test]
+5 -3
View File
@@ -145,11 +145,13 @@ fn string_literal(rest: &str) -> Option<String> {
None
}
/// Characters required by the fixed panel catalog, for system-font coverage selection.
/// Sorted and deduplicated so fallback registration is deterministic.
/// Characters required by the fixed panel catalog and the effect names and categories, for
/// system-font coverage selection. Sorted and deduplicated so fallback registration is
/// deterministic.
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn catalog_characters(language: &str) -> Vec<char> {
catalog(language).into_iter().flat_map(|c| c.values()).flat_map(|s| s.chars()).collect::<std::collections::BTreeSet<_>>().into_iter().collect()
let panels = catalog(language).into_iter().flat_map(|c| c.values()).flat_map(|s| s.chars());
panels.chain(super::effects::characters(language)).collect::<std::collections::BTreeSet<_>>().into_iter().collect()
}
/// Translate one interface string: the current language's row for `text`, or `text` itself.
+6 -6
View File
@@ -69,7 +69,7 @@ and recounted again at this tree after #1774 by counting catalog rows (`const` t
`crates/ui-egui/src/i18n.rs`, `i18n/ui.rs` and `i18n/effects.rs`: 630 menu rows, 998
panel/dialog sources in Simplified Chinese, 951 in Traditional Chinese and 996 in Japanese). The
denominator, **≈ 4,200 user-visible strings**, is a rounded estimate: the 630 fixed menu entries,
the 998 panel/dialog sources, 307 effect names, ≈ 1,650 distinct effect parameter names and
the 998 panel/dialog sources, 308 effect names, ≈ 1,650 distinct effect parameter names and
≈ 620 other unwrapped interface strings (Home screen literals, engine-built tooltips, error and
status messages). It counts distinct strings, not catalog rows; engine messages are only partly
counted, so the true percentages are slightly lower.
@@ -93,18 +93,18 @@ Other languages EffectCraft ships (6):
| Language | Code | UI strings translated | Status | To `full` |
|---|---|---|---|---|
| Traditional Chinese | `zh-hant` | 1,910 (≈ 45%): menus 630 rows, panels 951, effect names 308 + 21 categories | partial; effect names complete, parameter names and engine text still in English | 3–5 h + review |
| Traditional Chinese | `zh-hant` | 1,889 (≈ 45%): menus 630 rows, panels 951, effect names 308 (+ 21 categories, not counted, as for the other languages) | partial; effect names complete, parameter names and engine text still in English | 3–5 h + review |
| Ukrainian | `uk` | 1,153 (≈ 29%): menus 629 rows, panels 524 | partial | 6–9 h + review |
| Polish | `pl` | 1,325 (≈ 34%): menus 630 rows, panels 695 | partial; unwrapped UI, effects and engine text still in English; AI-drafted, native review welcome | Remaining strings + native review |
| Czech | `cs` | 1,147 (≈ 29%): menus 629 rows, panels 518 | Existing catalog entries covered; unwrapped UI, effects and engine text still in English; AI-assisted, not yet native-speaker proofread | Remaining strings + independent native review |
| Georgian | `ka` | 1,514 (≈ 39%): menus 629 rows, panels 885 (the Simplified Chinese catalog's 694 plus 191 Georgian-only rows: form dialogs, context menus, column headers), effect names 308 + 21 categories; native-speaker reviewed | partial | 5–8 h |
| Georgian | `ka` | 1,822 (≈ 43%): menus 629 rows, panels 885 (the Simplified Chinese catalog's 694 plus 191 Georgian-only rows: form dialogs, context menus, column headers), effect names 308 + 21 categories; native-speaker reviewed | partial | 5–8 h |
| Swedish | `sv` | menus 630 rows, panels 723, effect names 308 and categories 21; added after the measurement above | Settings, Edit menu and Match System (`sv-*` locales) offer it; parameter and engine text still in English; not yet native-speaker reviewed | Remaining strings + native review |
Russian (`ru`) has a menu catalog (630 rows, ≈ 15%); After Effects also ships Italian, which EffectCraft does not (8–12 h).
**Overall: ≈ 24%**, the average coverage of After Effects' 10 non-English languages
(Simplified Chinese 46%, Traditional Chinese 38%, Japanese 46%, Brazilian Portuguese 34%,
Spanish 29%, French, German and Russian 16%, Korean and Italian 0%; 241 / 10, was ≈ 21% on
**Overall: ≈ 25%**, the average coverage of After Effects' 10 non-English languages
(Simplified Chinese 46%, Traditional Chinese 45%, Japanese 46%, Brazilian Portuguese 34%,
Spanish 29%, French, German and Russian 16%, Korean and Italian 0%; 248 / 10, was ≈ 24% before #2037's effect names and ≈ 21% on
22 October; Ukrainian (≈ 29%) and Czech (≈ 29%) are EffectCraft-only and stay out of that
average). This is the localization row in [target-app-parity.md](target-app-parity.md#by-dimension).