use std::collections::HashMap; use std::env; use std::fs; use std::path::Path; fn main() { let out_dir = env::var("OUT_DIR").unwrap(); let dest_path = Path::new(&out_dir).join("locales.rs"); // Read the YAML file let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); let repo_root = Path::new(&manifest_dir).parent().unwrap().parent().unwrap(); let yaml_path = repo_root.join("resources/locales/i18n.yml"); let yaml_content = fs::read_to_string(&yaml_path).unwrap_or_else(|_| panic!("Failed to read {yaml_path:?}")); let locales = parse_yaml(&yaml_content); let generated_code = generate_locale_code(&locales); fs::write(&dest_path, generated_code).expect("Failed to write generated locales."); println!("cargo:rerun-if-changed={}", yaml_path.display()); } fn parse_yaml(content: &str) -> HashMap> { let mut result: HashMap> = HashMap::new(); let mut current_key = None; for line in content.lines() { if let Some((key, value)) = line.split_once(':') { let is_translation = key .as_bytes() .first() .is_some_and(|&b| b.is_ascii_whitespace()); let key = key.trim(); if !key.starts_with('#') && !key.is_empty() { if !is_translation { current_key = result.entry(key.replace('.', "_")).or_default().into(); } else { current_key .as_mut() .unwrap() .insert(key.to_string(), value.trim().trim_matches('"').to_string()); } } } } result } fn generate_locale_code(locales: &HashMap>) -> String { let mut code = String::new(); code.push_str("#[derive(Debug, Clone)]\n"); code.push_str("pub struct Locale {\n"); for key in locales.keys() { code.push_str(&format!(" pub {}: &'static str,\n", key)); } code.push_str("}\n\n"); let mut languages = std::collections::HashSet::new(); for translations in locales.values() { for lang in translations.keys() { languages.insert(lang.clone()); } } for lang in &languages { code.push_str(&format!( "pub static {}_LOCALES: Locale = Locale {{\n", lang.to_uppercase() )); for (key, translations) in locales { let value = translations .get(lang) .unwrap_or_else(|| panic!("Missing: {}", key)); code.push_str(&format!(" {key}: {value:?},\n")); } code.push_str("};\n\n"); } code.push_str("pub fn locale(name: &str) -> Option<&'static Locale> {\n"); code.push_str(" hashify::tiny_map!(name.as_bytes(),\n"); for lang in &languages { code.push_str(&format!( " \"{}\" => &{}_LOCALES,\n", lang, lang.to_uppercase() )); } code.push_str(" )\n"); code.push_str("}\n"); code }