cjpais/Handy · error
English translations must exist
Error message
English translations must exist
What it means
get_tray_translations resolves a locale through TRANSLATIONS — a static HashMap code-generated at compile time by build.rs from the frontend locale files, with English as the designed single source of truth. Lookup order is exact match, then a script/region fallback (zh-Hant->zh-TW, yue->zh-TW, etc.), then the literal "en" entry. The .expect("English translations must exist") fires only when that final invariant is broken: src/i18n/locales/en/translation.json was deleted, renamed, or failed to generate into OUT_DIR/tray_translations.rs.
Source
Thrown at src-tauri/src/tray_i18n.rs:53
.iter()
.any(|region| subtags.contains(region));
let exact_match = TRANSLATIONS
.iter()
.find_map(|(code, strings)| code.eq_ignore_ascii_case(&normalized).then_some(strings));
let fallback = match language {
"zh" if is_hant || (!is_hans && is_traditional_region) => "zh-TW",
// Cantonese uses Traditional Chinese unless explicitly tagged as Hans.
"yue" if is_hans => "zh",
"yue" => "zh-TW",
_ => language,
};
exact_match
.or_else(|| TRANSLATIONS.get(fallback))
.or_else(|| TRANSLATIONS.get("en"))
.cloned()
.expect("English translations must exist")
}
#[cfg(test)]
mod tests {
use super::{get_tray_translations, TRANSLATIONS};
#[test]
fn resolves_locale_fallbacks() {
for (locale, expected) in [
("zh-Hant-TW", "zh-TW"),
("zh-Hant-HK", "zh-TW"),
("zh-HK", "zh-TW"),
("zh-MO", "zh-TW"),
("ZH-TW", "zh-TW"),
("zh_Hant_TW", "zh-TW"),
("zh-Hans-CN", "zh"),
("yue-Hant-HK", "zh-TW"),
("yue-Hans-CN", "zh"),View on GitHub (pinned to c89b7bf389)
Solutions
- Restore src/i18n/locales/en/translation.json with the complete "tray" key set — it is the source for the generated TrayStrings struct and the "en" map entry
- Clean build artifacts (cargo clean) so build.rs regenerates OUT_DIR/tray_translations.rs from the current locale tree
- Extend the existing tests module in tray_i18n.rs with a test asserting TRANSLATIONS.get("en").is_some() so CI fails before runtime
- If the locales layout changed, update the glob/discovery logic in build.rs to match
Example fix
// src-tauri/src/tray_i18n.rs — add to the existing #[cfg(test)] mod tests
#[test]
fn english_fallback_entry_exists() {
assert!(
TRANSLATIONS.get("en").is_some(),
"build.rs must generate the \"en\" entry; check src/i18n/locales/en/translation.json"
);
}
// before: deleting locales/en/ makes get_tray_translations panic for ANY locale
// after: the unit test fails at `cargo test` time instead of the app panicking at runtime Defensive patterns
Strategy: validation
Validate before calling
// src-tauri/src/tray_i18n.rs tests module
#[test]
fn english_fallback_entry_exists() {
assert!(TRANSLATIONS.get("en").is_some(),
"build.rs must emit the \"en\" entry; check src/i18n/locales/en/translation.json");
} Prevention
- Treat en/translation.json as load-bearing build input: deleting or renaming it breaks the generated TRANSLATIONS map
- Run cargo clean after changing the locales directory layout so build.rs regenerates OUT_DIR
- Add CI tests for fallback invariants (en present) whenever a static map has a documented last-resort entry
When it happens
Trigger: Deleting or renaming en/translation.json (or its "tray" section) so build.rs emits a TRANSLATIONS map without the "en" key; a malformed en JSON that makes build.rs skip the entry; a stale OUT_DIR after moving locale directories; then any call to get_tray_translations panics for every locale.
Common situations: Refactoring the i18n pipeline; changing the locales directory layout without updating build.rs globbing; CI caches serving a stale generated file; deliberately trying to make English a fallback-only build.
Related errors
- Failed to create resampler
- failed to create quit item
- failed to create separator
- failed to create model submenu
- failed to create model item
AI-assisted analysis of cjpais/Handy@c89b7bf389 (2026-08-17).
Data as JSON: /api/errors/de8a3a35ffcbb074.
Report an issue: GitHub.