roblox-slang 2.0.4

Type-safe internationalization for Roblox experiences
Documentation
use super::CoverageInfo;
use crate::parser::Translation;
use std::collections::{HashMap, HashSet};

/// Generate coverage report for all locales
pub fn generate_coverage_report(
    translations: &[Translation],
    base_locale: &str,
    supported_locales: &[String],
) -> HashMap<String, CoverageInfo> {
    // Get all keys from base locale
    let base_keys: HashSet<String> = translations
        .iter()
        .filter(|t| t.locale == base_locale)
        .map(|t| t.key.clone())
        .collect();

    let total_keys = base_keys.len();
    let mut coverage: HashMap<String, CoverageInfo> = HashMap::new();

    for locale in supported_locales {
        // Get keys for this locale
        let locale_keys: HashSet<String> = translations
            .iter()
            .filter(|t| t.locale == locale.as_str())
            .map(|t| t.key.clone())
            .collect();

        let translated_keys = locale_keys.len();

        // Find missing keys
        let missing: Vec<String> = base_keys.difference(&locale_keys).cloned().collect();

        let coverage_percent = if total_keys > 0 {
            (translated_keys as f64 / total_keys as f64) * 100.0
        } else {
            100.0
        };

        coverage.insert(
            locale.clone(),
            CoverageInfo {
                total_keys,
                translated_keys,
                missing_keys: missing,
                coverage_percent,
            },
        );
    }

    coverage
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_generate_coverage_report() {
        let translations = vec![
            Translation {
                key: "ui.button".to_string(),
                value: "Buy".to_string(),
                locale: "en".to_string(),
                context: None,
            },
            Translation {
                key: "ui.label".to_string(),
                value: "Welcome".to_string(),
                locale: "en".to_string(),
                context: None,
            },
            Translation {
                key: "ui.button".to_string(),
                value: "Beli".to_string(),
                locale: "id".to_string(),
                context: None,
            },
        ];

        let supported_locales = vec!["en".to_string(), "id".to_string()];
        let coverage = generate_coverage_report(&translations, "en", &supported_locales);

        assert_eq!(coverage.len(), 2);

        // English should be 100%
        assert_eq!(coverage["en"].coverage_percent, 100.0);
        assert_eq!(coverage["en"].translated_keys, 2);
        assert_eq!(coverage["en"].missing_keys.len(), 0);

        // Indonesian should be 50%
        assert_eq!(coverage["id"].coverage_percent, 50.0);
        assert_eq!(coverage["id"].translated_keys, 1);
        assert_eq!(coverage["id"].missing_keys.len(), 1);
    }
}