feat: phase 2 — i18n messages EN/FR (--lang, AM_LANG/LANG, catalogue tr/tr_fmt, REPL lang) (closes #74)
This commit is contained in:
@@ -34,7 +34,7 @@ pub fn generate(dir: &Path, force: bool, template: Option<&str>) -> Result<PathB
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
let stack = if ctx.stack.is_empty() {
|
||||
"aucune détectée".to_string()
|
||||
crate::i18n::tr("aucune détectée").to_string()
|
||||
} else {
|
||||
ctx.stack.join(", ")
|
||||
};
|
||||
@@ -82,7 +82,7 @@ pub fn run(app: &App, force: bool, template: Option<&str>) -> Result<i32> {
|
||||
"wrote {} (stack: {})",
|
||||
path.display(),
|
||||
if ctx.stack.is_empty() {
|
||||
"aucune détectée".to_string()
|
||||
crate::i18n::tr("aucune détectée").to_string()
|
||||
} else {
|
||||
ctx.stack.join(", ")
|
||||
}
|
||||
|
||||
@@ -109,16 +109,22 @@ pub fn run(app: &App) -> Result<i32> {
|
||||
let scheduled = schedule_locked(&locked);
|
||||
if !scheduled.is_empty() {
|
||||
println!(
|
||||
"{} {} entrée(s) verrouillée(s) — suppression planifiée au prochain redémarrage",
|
||||
"{} {}",
|
||||
"locked:".yellow(),
|
||||
scheduled.len()
|
||||
crate::tr_fmt!(
|
||||
"{} entrée(s) verrouillée(s) — suppression planifiée au prochain redémarrage",
|
||||
scheduled.len()
|
||||
)
|
||||
);
|
||||
}
|
||||
if scheduled.len() != locked.len() {
|
||||
println!(
|
||||
"{} {} entrée(s) verrouillée(s) — le processus différé les supprimera après la fermeture de am",
|
||||
"{} {}",
|
||||
"deferred:".cyan(),
|
||||
locked.len() - scheduled.len()
|
||||
crate::tr_fmt!(
|
||||
"{} entrée(s) verrouillée(s) — le processus différé les supprimera après la fermeture de am",
|
||||
locked.len() - scheduled.len()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,12 +119,25 @@ pub fn report_md(app: &App, days: u32) -> String {
|
||||
}
|
||||
let sessions = crate::sessions::load(app).map(|s| s.sessions.len()).unwrap_or(0);
|
||||
let top_rows = top(app, None).unwrap_or_default();
|
||||
let mut md = format!(
|
||||
"# Rapport agent-manager — {days} derniers jours\n\n- Lancements : {total_launches}\n- Échecs : {failures}\n- Changements (install/update/uninstall) : {changes}\n- Durée cumulée : {}s\n- Sessions enregistrées : {sessions}\n",
|
||||
duration
|
||||
);
|
||||
// Issue #74: the report follows the active language.
|
||||
let md = if app.lang().is_en() {
|
||||
format!(
|
||||
"# agent-manager report — last {days} days\n\n- Launches: {total_launches}\n- Failures: {failures}\n- Changes (install/update/uninstall): {changes}\n- Cumulative duration: {}s\n- Recorded sessions: {sessions}\n",
|
||||
duration
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"# Rapport agent-manager — {days} derniers jours\n\n- Lancements : {total_launches}\n- Échecs : {failures}\n- Changements (install/update/uninstall) : {changes}\n- Durée cumulée : {}s\n- Sessions enregistrées : {sessions}\n",
|
||||
duration
|
||||
)
|
||||
};
|
||||
let mut md = md;
|
||||
if !top_rows.is_empty() {
|
||||
md.push_str("\n## Top agents\n\n| Agent | Lancements | Durée | Échecs |\n|---|---|---|---|\n");
|
||||
md.push_str(if app.lang().is_en() {
|
||||
"\n## Top agents\n\n| Agent | Launches | Duration | Failures |\n|---|---|---|---|\n"
|
||||
} else {
|
||||
"\n## Top agents\n\n| Agent | Lancements | Durée | Échecs |\n|---|---|---|---|\n"
|
||||
});
|
||||
for r in &top_rows {
|
||||
md.push_str(&format!(
|
||||
"| {} | {} | {}s | {} |\n",
|
||||
@@ -137,8 +150,15 @@ pub fn report_md(app: &App, days: u32) -> String {
|
||||
if !rows.is_empty() {
|
||||
let t = crate::costs::totals(&rows);
|
||||
md.push_str(&format!(
|
||||
"\n## Coûts estimés\n\n- Sessions facturées : {}\n- Tokens : {} in / {} out\n- Coût estimé : ${:.4}\n",
|
||||
t.sessions, t.tokens_in, t.tokens_out, t.cost_usd
|
||||
"{}\n\n- {}: {}\n- {}: {} in / {} out\n- {}: ${:.4}\n",
|
||||
if app.lang().is_en() { "\n## Estimated costs" } else { "\n## Coûts estimés" },
|
||||
if app.lang().is_en() { "Billed sessions" } else { "Sessions facturées" },
|
||||
t.sessions,
|
||||
if app.lang().is_en() { "Tokens" } else { "Tokens" },
|
||||
t.tokens_in,
|
||||
t.tokens_out,
|
||||
if app.lang().is_en() { "Estimated cost" } else { "Coût estimé" },
|
||||
t.cost_usd
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,13 +111,18 @@ pub fn run(app: &App, query: &str) -> Result<i32> {
|
||||
));
|
||||
} else {
|
||||
app.log.info(&format!(
|
||||
"no agent matches '{query}' — vouliez-vous dire : {} ?",
|
||||
"{} — {} : {} ?",
|
||||
crate::tr_fmt!("no agent matches '{}'", query),
|
||||
crate::i18n::tr("vouliez-vous dire"),
|
||||
suggestions.join(", ")
|
||||
));
|
||||
}
|
||||
return Ok(0);
|
||||
}
|
||||
println!("suggestions for \"{query}\":");
|
||||
println!(
|
||||
"{} \"{query}\":",
|
||||
crate::i18n::tr("suggestions for")
|
||||
);
|
||||
let mut table = crate::output::Table::new(vec!["AGENT", "CATEGORY", "WHY"]);
|
||||
for r in &rows {
|
||||
table.row(vec![
|
||||
|
||||
+34
-10
@@ -435,7 +435,11 @@ pub fn render(app: &App, random: bool) -> String {
|
||||
};
|
||||
if random {
|
||||
let (title, body) = random_tip();
|
||||
return format!("💡 {} — {}\n", p.acc(title), body);
|
||||
return format!(
|
||||
"💡 {} — {}\n",
|
||||
p.acc(crate::i18n::tr_in(app.lang(), title)),
|
||||
crate::i18n::tr_in(app.lang(), body)
|
||||
);
|
||||
}
|
||||
let mut out = String::new();
|
||||
out.push_str(&header(app, &p));
|
||||
@@ -444,7 +448,7 @@ pub fn render(app: &App, random: bool) -> String {
|
||||
out.push_str(§ion_rule(app, &p, s.icon, s.title));
|
||||
out.push('\n');
|
||||
for e in s.entries {
|
||||
out.push_str(&entry(&p, e));
|
||||
out.push_str(&entry(&p, app.lang(), e));
|
||||
}
|
||||
out.push('\n');
|
||||
}
|
||||
@@ -453,11 +457,18 @@ pub fn render(app: &App, random: bool) -> String {
|
||||
let (title, body) = daily_tip();
|
||||
out.push_str(§ion_rule(app, &p, "💡", "Le saviez-vous ?"));
|
||||
out.push('\n');
|
||||
out.push_str(&format!(" {} — {}\n", p.acc(title), body));
|
||||
out.push_str(&format!(
|
||||
" {} — {}\n",
|
||||
p.acc(crate::i18n::tr_in(app.lang(), title)),
|
||||
crate::i18n::tr_in(app.lang(), body)
|
||||
));
|
||||
out.push('\n');
|
||||
out.push_str(&format!(
|
||||
" {}\n",
|
||||
p.dim("am help <commande> pour le détail · am tip --random pour une astuce isolée · dans le shell am, tapez simplement 'tip'")
|
||||
p.dim(crate::i18n::tr_in(
|
||||
app.lang(),
|
||||
"am help <commande> pour le détail · am tip --random pour une astuce isolée · dans le shell am, tapez simplement 'tip'"
|
||||
))
|
||||
));
|
||||
out
|
||||
}
|
||||
@@ -528,17 +539,26 @@ fn header(app: &App, p: &Painter) -> String {
|
||||
/// Section separator: " 🚀 Lancer & piloter ────────".
|
||||
fn section_rule(app: &App, p: &Painter, icon: &str, title: &str) -> String {
|
||||
let w = page_width(app);
|
||||
let label = format!(" {icon} {title} ");
|
||||
let label = format!(" {icon} {} ", crate::i18n::tr_in(app.lang(), title));
|
||||
let dashes = "─".repeat(w.saturating_sub(label.chars().count()).max(1));
|
||||
format!("{}{}", p.hdr(&label), p.frame(&dashes))
|
||||
}
|
||||
|
||||
/// One cheat-sheet entry: usage line, options, example.
|
||||
fn entry(p: &Painter, e: &TipEntry) -> String {
|
||||
fn entry(p: &Painter, lang: crate::i18n::Lang, e: &TipEntry) -> String {
|
||||
let mut out = String::new();
|
||||
out.push_str(&format!(" {} {} {}\n", p.inf("▸"), p.acc(e.usage), e.about));
|
||||
out.push_str(&format!(
|
||||
" {} {} {}\n",
|
||||
p.inf("▸"),
|
||||
p.acc(e.usage),
|
||||
crate::i18n::tr_in(lang, e.about)
|
||||
));
|
||||
for (flag, desc) in e.options {
|
||||
out.push_str(&format!(" {} {}\n", p.dim(flag), desc));
|
||||
out.push_str(&format!(
|
||||
" {} {}\n",
|
||||
p.dim(flag),
|
||||
crate::i18n::tr_in(lang, desc)
|
||||
));
|
||||
}
|
||||
if !e.example.is_empty() {
|
||||
out.push_str(&format!(" {} {}\n", p.dim("❯"), p.val(e.example)));
|
||||
@@ -593,8 +613,12 @@ fn most_used_section(app: &App, p: &Painter) -> String {
|
||||
out.push('\n');
|
||||
if top.is_empty() {
|
||||
out.push_str(&format!(
|
||||
" {} — lancez le shell am et tapez quelques commandes : vos habitudes apparaîtront ici.\n",
|
||||
p.dim("aucune commande enregistrée")
|
||||
" {} — {}\n",
|
||||
p.dim(crate::i18n::tr_in(app.lang(), "aucune commande enregistrée")),
|
||||
crate::i18n::tr_in(
|
||||
app.lang(),
|
||||
"lancez le shell am et tapez quelques commandes : vos habitudes apparaîtront ici"
|
||||
)
|
||||
));
|
||||
return out;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user