Skip to main content

romance_core/addon/
i18n.rs

1use crate::addon::Addon;
2use anyhow::Result;
3use std::path::Path;
4
5pub struct I18nAddon;
6
7impl Addon for I18nAddon {
8    fn name(&self) -> &str {
9        "i18n"
10    }
11
12    fn check_prerequisites(&self, project_root: &Path) -> Result<()> {
13        super::check_romance_project(project_root)
14    }
15
16    fn is_already_installed(&self, project_root: &Path) -> bool {
17        project_root.join("backend/src/i18n.rs").exists()
18    }
19
20    fn install(&self, project_root: &Path) -> Result<()> {
21        install_i18n(project_root)
22    }
23
24    fn uninstall(&self, project_root: &Path) -> Result<()> {
25        use colored::Colorize;
26
27        println!("{}", "Uninstalling i18n (internationalization)...".bold());
28
29        // Delete files
30        if super::remove_file_if_exists(&project_root.join("backend/src/i18n.rs"))? {
31            println!("  {} backend/src/i18n.rs", "delete".red());
32        }
33        if super::remove_file_if_exists(&project_root.join("frontend/src/lib/i18n.ts"))? {
34            println!("  {} frontend/src/lib/i18n.ts", "delete".red());
35        }
36
37        // Delete locales directory
38        let locales_dir = project_root.join("backend/locales");
39        if locales_dir.exists() {
40            std::fs::remove_dir_all(&locales_dir)?;
41            println!("  {} backend/locales/", "delete".red());
42        }
43
44        // Remove mod declaration from main.rs
45        super::remove_mod_from_main(project_root, "i18n")?;
46
47        // Remove locale_middleware from routes/mod.rs
48        super::remove_line_from_file(
49            &project_root.join("backend/src/routes/mod.rs"),
50            "locale_middleware",
51        )?;
52
53        // Remove feature flag
54        super::remove_feature_flag(project_root, "i18n")?;
55
56        // Regenerate AI context
57        crate::ai_context::regenerate(project_root).ok();
58
59        println!();
60        println!(
61            "{}",
62            "i18n (internationalization) uninstalled successfully."
63                .green()
64                .bold()
65        );
66
67        Ok(())
68    }
69}
70
71fn install_i18n(project_root: &Path) -> Result<()> {
72    use crate::template::TemplateEngine;
73    use crate::utils;
74    use colored::Colorize;
75    use tera::Context;
76
77    println!("{}", "Installing i18n (internationalization)...".bold());
78
79    let engine = TemplateEngine::new()?;
80    let ctx = Context::new();
81
82    // Generate backend i18n module
83    let content = engine.render("addon/i18n/i18n.rs.tera", &ctx)?;
84    utils::write_file(&project_root.join("backend/src/i18n.rs"), &content)?;
85    println!("  {} backend/src/i18n.rs", "create".green());
86
87    // Generate English locale file
88    let content = engine.render("addon/i18n/en.json.tera", &ctx)?;
89    utils::write_file(&project_root.join("backend/locales/en.json"), &content)?;
90    println!("  {} backend/locales/en.json", "create".green());
91
92    // Generate Russian locale file
93    let content = engine.render("addon/i18n/ru.json.tera", &ctx)?;
94    utils::write_file(&project_root.join("backend/locales/ru.json"), &content)?;
95    println!("  {} backend/locales/ru.json", "create".green());
96
97    // Generate frontend i18n module
98    let content = engine.render("addon/i18n/i18n_frontend.ts.tera", &ctx)?;
99    utils::write_file(&project_root.join("frontend/src/lib/i18n.ts"), &content)?;
100    println!("  {} frontend/src/lib/i18n.ts", "create".green());
101
102    // Add mod i18n to main.rs
103    super::add_mod_to_main(project_root, "i18n")?;
104
105    // Inject Accept-Language middleware via ROMANCE:MIDDLEWARE marker
106    utils::insert_at_marker(
107        &project_root.join("backend/src/routes/mod.rs"),
108        "// === ROMANCE:MIDDLEWARE ===",
109        "        .layer(axum::middleware::from_fn(crate::i18n::locale_middleware))",
110    )?;
111
112    // Add serde_json dependency if not present (should already be there)
113    crate::generator::auth::insert_cargo_dependency(
114        &project_root.join("backend/Cargo.toml"),
115        &[("serde_json", r#""1""#)],
116    )?;
117
118    // Update romance.toml with i18n feature
119    super::update_feature_flag(project_root, "i18n", true)?;
120
121    println!();
122    println!(
123        "{}",
124        "i18n (internationalization) installed successfully!"
125            .green()
126            .bold()
127    );
128    println!("  Locale files: backend/locales/en.json, backend/locales/ru.json");
129    println!("  Backend usage: i18n::t(\"en\", \"common.success\")");
130    println!("  Frontend usage: import {{ t }} from '@/lib/i18n'");
131    println!("  The Accept-Language middleware extracts locale from request headers.");
132    println!("  Access locale in handlers via: request.extensions().get::<i18n::Locale>()");
133
134    Ok(())
135}