browser_automation_cli/
i18n.rs1use std::sync::OnceLock;
7
8static EFFECTIVE_LANG: OnceLock<&'static str> = OnceLock::new();
10
11pub fn normalize_lang(lang: Option<&str>) -> &'static str {
13 match lang.map(|s| s.trim().to_ascii_lowercase()) {
14 Some(ref s) if s.starts_with("pt") => "pt",
15 _ => "en",
16 }
17}
18
19pub fn resolve_lang(cli_lang: Option<&str>) -> &'static str {
21 if let Some(l) = cli_lang {
22 if !l.trim().is_empty() {
23 return normalize_lang(Some(l));
24 }
25 }
26 if let Ok(cfg) = crate::xdg::load_config() {
27 if let Some(ref l) = cfg.lang {
28 if !l.trim().is_empty() {
29 return normalize_lang(Some(l));
30 }
31 }
32 }
33 #[cfg(unix)]
37 {
38 if let Ok(v) = std::env::var("LANG") {
42 if v.to_ascii_lowercase().starts_with("pt") {
43 return "pt";
44 }
45 }
46 if let Ok(v) = std::env::var("LC_ALL") {
47 if v.to_ascii_lowercase().starts_with("pt") {
48 return "pt";
49 }
50 }
51 if let Ok(v) = std::env::var("LC_MESSAGES") {
52 if v.to_ascii_lowercase().starts_with("pt") {
53 return "pt";
54 }
55 }
56 }
57 "en"
58}
59
60pub fn set_effective_lang(lang: &'static str) {
62 let _ = EFFECTIVE_LANG.set(lang);
63}
64
65pub fn effective_lang() -> &'static str {
67 EFFECTIVE_LANG.get().copied().unwrap_or("en")
68}
69
70pub fn suggestion_for(kind: &str, lang: Option<&str>) -> Option<&'static str> {
72 let l = normalize_lang(lang.or(Some(effective_lang())));
73 match (kind, l) {
74 ("usage", "pt") => Some("Confira --help e os argumentos obrigatórios"),
75 ("usage", _) => Some("Check --help and required arguments"),
76 ("broken-pipe", "pt") => {
77 Some("Nao pipe stdout para consumidor fechado; exit 141 e esperado")
78 }
79 ("broken-pipe", _) => Some("Do not pipe stdout to a closed consumer; exit 141 is expected"),
80 ("unavailable", "pt") => {
81 Some("Instale Chrome/Chromium no PATH ou use: browser-automation-cli config set chrome_path <path>")
82 }
83 ("unavailable", _) => {
84 Some("Install Chrome/Chromium on PATH or: browser-automation-cli config set chrome_path <path>")
85 }
86 ("data", "pt") => Some("Verifique robots.txt ou o payload JSON/NDJSON"),
87 ("data", _) => Some("Check robots.txt or the JSON/NDJSON payload"),
88 ("browser", "pt") => Some("Verifique URL e se o Chrome ainda esta vivo no one-shot"),
89 ("browser", _) => Some("Check the URL and whether Chrome stayed alive in this one-shot"),
90 _ => None,
91 }
92}
93
94pub fn suggestion_key(key: &str, lang: Option<&str>) -> &'static str {
96 let l = normalize_lang(lang.or(Some(effective_lang())));
97 match (key, l) {
98 ("vision_required", "pt") => "Passe --experimental-vision na mesma invocação",
99 ("vision_required", _) => "Pass --experimental-vision on the same invocation",
100 ("robots_dual", "pt") => {
101 "Passe as duas flags juntas quando ignorar robots.txt de propósito"
102 }
103 ("robots_dual", _) => "Pass both flags together when you intentionally skip robots.txt",
104 ("category_memory", "pt") => {
105 "Passe --category-memory (heap take/summary/close funcionam sem ops de grafo profundo)"
106 }
107 ("category_memory", _) => {
108 "Pass --category-memory (heap take/summary/close work without deep graph ops)"
109 }
110 ("category_extensions", "pt") => "Passe --category-extensions na mesma invocação",
111 ("category_extensions", _) => "Pass --category-extensions on the same invocation",
112 ("screencast_flag", "pt") => "Passe --experimental-screencast na mesma invocação",
113 ("screencast_flag", _) => "Pass --experimental-screencast on the same invocation",
114 ("webmcp_flag", "pt") => "Passe --category-webmcp na mesma invocação",
115 ("webmcp_flag", _) => "Pass --category-webmcp on the same invocation",
116 ("third_party_flag", "pt") => "Passe --category-third-party na mesma invocação",
117 ("third_party_flag", _) => "Pass --category-third-party on the same invocation",
118 ("capture_network", "pt") => "Passe --capture-network antes de run/net",
119 ("capture_network", _) => "Pass --capture-network before run/net",
120 ("capture_console", "pt") => "Passe --capture-console antes de run/console",
121 ("capture_console", _) => "Pass --capture-console before run/console",
122 ("run_fail_fast", "pt") => {
123 "Corrija o passo com falha; os passos seguintes não foram executados"
124 }
125 ("run_fail_fast", _) => "Fix the failing step; subsequent steps were not executed",
126 ("lighthouse_missing", "pt") => {
127 "Instale lighthouse ou: browser-automation-cli config set lighthouse_path <path>"
128 }
129 ("lighthouse_missing", _) => {
130 "Install lighthouse or: browser-automation-cli config set lighthouse_path <path>"
131 }
132 (_, "pt") => "Confira --help e os argumentos obrigatórios",
133 _ => "Check --help and required arguments",
134 }
135}
136
137pub fn localize_error_suggestion(err: &crate::error::CliError) -> crate::error::CliError {
139 let lang = effective_lang();
140 if lang == "en" {
141 return err.clone();
142 }
143 if err.suggestion().is_none() {
145 if let Some(s) = suggestion_for(err.kind().as_str(), Some(lang)) {
146 let mut out = crate::error::CliError::with_suggestion(err.kind(), err.message(), s);
147 if let Some(d) = err.data() {
148 out = out.with_data(d.clone());
149 }
150 return out;
151 }
152 return err.clone();
153 }
154 let s = err.suggestion().unwrap_or("");
155 let mapped = match s {
157 "Pass --experimental-vision on the same invocation" => {
158 suggestion_key("vision_required", Some(lang))
159 }
160 "Pass both flags together when you intentionally skip robots.txt" => {
161 suggestion_key("robots_dual", Some(lang))
162 }
163 "Pass --category-memory (heap take/summary/close work without deep graph ops)" => {
164 suggestion_key("category_memory", Some(lang))
165 }
166 "Pass --category-extensions on the same invocation" => {
167 suggestion_key("category_extensions", Some(lang))
168 }
169 "Pass --experimental-screencast on the same invocation" => {
170 suggestion_key("screencast_flag", Some(lang))
171 }
172 "Pass --category-webmcp on the same invocation" => {
173 suggestion_key("webmcp_flag", Some(lang))
174 }
175 "Pass --category-third-party on the same invocation" => {
176 suggestion_key("third_party_flag", Some(lang))
177 }
178 "Pass --capture-network before run/net" => suggestion_key("capture_network", Some(lang)),
179 "Pass --capture-console before run/console" => {
180 suggestion_key("capture_console", Some(lang))
181 }
182 "Fix the failing step; subsequent steps were not executed" => {
183 suggestion_key("run_fail_fast", Some(lang))
184 }
185 other if other.contains("lighthouse") && other.contains("npm") => {
186 suggestion_key("lighthouse_missing", Some(lang))
187 }
188 _ => {
189 return err.clone();
190 }
191 };
192 let mut out = crate::error::CliError::with_suggestion(err.kind(), err.message(), mapped);
193 if let Some(d) = err.data() {
194 out = out.with_data(d.clone());
195 }
196 out
197}