sysd_manager_translating/
lib.rs1use constcat::concat;
2use log::info;
3use std::{
4 collections::HashSet,
5 fs::{self, read_to_string},
6 io,
7 path::PathBuf,
8 process::Command,
9};
10
11use crate::error::TransError;
12pub mod error;
13
14pub const MAIN_PROG: &str = "sysd-manager";
15pub const PO_DIR: &str = "./po";
16pub const DESKTOP_DIR: &str = "./data/applications";
17pub const DESKTOP_FILE: &str = "io.github.plrigaux.sysd-manager.desktop";
18pub const DESKTOP_FILE_PATH: &str = concat!(DESKTOP_DIR, "/", DESKTOP_FILE);
19
20pub const METAINFO_DIR: &str = "./data/metainfo";
21pub const METAINFO_FILE: &str = "io.github.plrigaux.sysd-manager.metainfo.xml";
22pub const METAINFO_FILE_PATH: &str = concat!(METAINFO_DIR, "/", METAINFO_FILE);
23
24pub fn xgettext(potfiles_file_path: &str, output_pot_file: &str) {
27 let mut command = Command::new("xgettext");
28
29 for preset in glib_preset() {
30 command.arg(preset);
31 }
32
33 let output = command
34 .arg(format!("--files-from={potfiles_file_path}"))
35 .arg(format!("--output={output_pot_file}"))
36 .arg("--verbose")
37 .output()
38 .unwrap();
39
40 display_output("XGETTEXT", output);
41}
42
43fn display_output(id: &str, output: std::process::Output) {
44 println!("{id}: {:?}", output.status);
45 println!("{id}: {}", String::from_utf8_lossy(&output.stdout));
46 if !output.status.success() {
47 eprintln!("{id}: {}", String::from_utf8_lossy(&output.stderr));
48 }
49}
50
51pub fn msginit(input_pot_file: &str, output_file: &str, lang: &str) {
54 let mut command = Command::new("msginit");
55
56 let output = command
57 .arg(format!("--input={input_pot_file}"))
58 .arg(format!("--output-file={output_file}"))
59 .arg(format!("--locale={lang}"))
60 .output()
61 .expect("command msginit ok");
62
63 display_output("MSGINIT", output);
64}
65
66pub fn msgmerge(input_pot_file: &str, output_file: &str) {
72 let mut command = Command::new("msgmerge");
73
74 let output = command
75 .arg("-o")
76 .arg(output_file)
77 .arg(output_file)
78 .arg(input_pot_file)
79 .arg("--verbose")
80 .output()
81 .unwrap();
82
83 display_output("MSGMERGE", output);
84}
85
86pub fn generate_mo() -> Result<(), TransError> {
87 for path in fs::read_dir(PO_DIR)?
88 .filter_map(|r| r.ok())
89 .filter_map(|dir_entry| {
90 let p = dir_entry.path();
91 if p.extension().is_some_and(|this_ext| this_ext == "po") {
92 Some(p)
93 } else {
94 None
95 }
96 })
97 {
98 info!("PO file {:?} lang {:?}", path, path.file_stem());
99
100 if let Some(po_file) = path.to_str() {
101 if let Some(lang) = path.file_stem().and_then(|s| s.to_str()) {
102 msgfmt(po_file, lang, MAIN_PROG)?;
103 }
104 }
105 }
106
107 Ok(())
108}
109
110pub fn lingas_from_files() -> Result<HashSet<String>, TransError> {
111 let linguas: HashSet<_> = fs::read_dir(PO_DIR)?
112 .filter_map(|r| r.ok())
113 .filter_map(|dir_entry| {
114 let p = dir_entry.path();
115 if p.extension().is_some_and(|this_ext| this_ext == "po") {
116 Some(p)
117 } else {
118 None
119 }
120 })
121 .filter_map(|path| {
122 path.file_stem()
123 .and_then(|s| s.to_str())
124 .map(|s| s.to_string())
125 })
126 .collect();
127
128 Ok(linguas)
129}
130
131pub fn lingas_from_lingua_file() -> Result<HashSet<String>, TransError> {
132 let mut linguas_file = PathBuf::from(PO_DIR);
133 linguas_file.push("LINGUAS");
134
135 let mut linguas = HashSet::new();
136
137 for line in read_to_string(linguas_file)?
138 .lines()
139 .map(|s| s.trim())
140 .filter(|s| !s.starts_with("#"))
141 {
142 for lang in line.split_whitespace() {
143 linguas.insert(lang.to_owned());
144 }
145 }
146
147 Ok(linguas)
148}
149
150pub fn generate_desktop() -> Result<(), TransError> {
151 let out_dir = "target/locale";
152 fs::create_dir_all(out_dir)?;
153 let out_file = format!("{}/{}", out_dir, DESKTOP_FILE);
154
155 let mut command = Command::new("msgfmt");
156 let output = command
157 .arg("--check")
158 .arg("--statistics")
159 .arg("--verbose")
160 .arg("--desktop")
161 .arg(format!("--template={}", DESKTOP_FILE_PATH))
162 .arg("-d")
163 .arg(PO_DIR)
164 .arg("-o")
165 .arg(out_file)
166 .output()?;
167
168 display_output("MSGFMT", output);
169
170 Ok(())
171}
172
173pub fn generate_metainfo() -> Result<(), TransError> {
174 let out_dir = "target/locale";
175 fs::create_dir_all(out_dir)?;
176 let out_file = format!("{}/{}", out_dir, METAINFO_FILE);
177
178 let mut command = Command::new("msgfmt");
179 let output = command
180 .arg("--check")
181 .arg("--statistics")
182 .arg("--verbose")
183 .arg("--xml")
184 .arg(format!("--template={}", METAINFO_FILE_PATH))
185 .arg("-d")
186 .arg(PO_DIR)
187 .arg("-o")
188 .arg(out_file)
189 .output()?;
190
191 display_output("MSGFMT", output);
192
193 Ok(())
194}
195
196pub fn msgfmt(po_file: &str, lang: &str, domain_name: &str) -> io::Result<()> {
200 let mut command = Command::new("msgfmt");
201
202 let out_dir = format!("target/locale/{lang}/LC_MESSAGES");
203
204 fs::create_dir_all(&out_dir)?;
205
206 let output = command
207 .arg("--check")
208 .arg("--statistics")
209 .arg("--verbose")
210 .arg("-o")
211 .arg(format!("{out_dir}/{domain_name}.mo"))
212 .arg(po_file)
213 .output()
214 .unwrap();
215
216 display_output("MSGFMT", output);
217
218 Ok(())
219}
220
221fn glib_preset() -> Vec<&'static str> {
222 let v = vec![
223 "--from-code=UTF-8",
224 "--add-comments",
225 "--keyword=_",
227 "--keyword=N_",
228 "--keyword=C_:1c,2",
229 "--keyword=NC_:1c,2",
230 "--keyword=g_dcgettext:2",
231 "--keyword=g_dngettext:2,3",
232 "--keyword=g_dpgettext2:2c,3",
233 "--flag=N_:1:pass-c-format",
234 "--flag=C_:2:pass-c-format",
235 "--flag=NC_:2:pass-c-format",
236 "--flag=g_dngettext:2:pass-c-format",
237 "--flag=g_strdup_printf:1:c-format",
238 "--flag=g_string_printf:2:c-format",
239 "--flag=g_string_append_printf:2:c-format",
240 "--flag=g_error_new:3:c-format",
241 "--flag=g_set_error:4:c-format",
242 "--flag=g_markup_printf_escaped:1:c-format",
243 "--flag=g_log:3:c-format",
244 "--flag=g_print:1:c-format",
245 "--flag=g_printerr:1:c-format",
246 "--flag=g_printf:1:c-format",
247 "--flag=g_fprintf:2:c-format",
248 "--flag=g_sprintf:2:c-format",
249 "--flag=g_snprintf:3:c-format",
250 ];
251 v
252}