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

  1. 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
  2. Clean build artifacts (cargo clean) so build.rs regenerates OUT_DIR/tray_translations.rs from the current locale tree
  3. Extend the existing tests module in tray_i18n.rs with a test asserting TRANSLATIONS.get("en").is_some() so CI fails before runtime
  4. 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

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


AI-assisted analysis of cjpais/Handy@c89b7bf389 (2026-08-17). Data as JSON: /api/errors/de8a3a35ffcbb074. Report an issue: GitHub.