Add a new comand convert to convert between outputt script types

This commit is contained in:
2025-12-30 11:18:26 +08:00
parent 7670570a88
commit f58ffd290b
5 changed files with 496 additions and 1 deletions

View File

@@ -8,7 +8,7 @@
//! △ LLM message
//! ● Translated message
//! ```
use crate::types::Message;
use crate::types::*;
use anyhow::Result;
/// A parser for the M3T format.
@@ -183,6 +183,50 @@ impl<'a> M3tParser<'a> {
}
Ok(messages)
}
pub fn parse_as_extend(&mut self) -> Result<Vec<ExtendedMessage>> {
let mut messages = Vec::new();
let mut name = None;
let mut llm = None;
let mut source = None;
while let Some(line) = self.next_line() {
if line.is_empty() {
continue;
}
// Remove zero-width space characters
let line = line.trim().trim_matches('\u{200b}');
if line.starts_with("") {
let line = line[3..].trim();
if line.starts_with("NAME:") {
name = Some(line[5..].trim().to_string());
} else {
source = Some(line.to_string());
}
} else if line.starts_with("") {
let line = line[3..].trim();
llm = Some(line.to_string());
} else if line.starts_with("") {
let message = line[3..].trim();
let source = match source.take() {
Some(s) => s,
None => {
return Err(anyhow::anyhow!(
"Missing original message before translated message at line {}",
self.line
));
}
};
let m = ExtendedMessage {
name: name.take(),
source,
translated: message.to_string(),
llm: llm.take(),
};
messages.push(m);
}
}
Ok(messages)
}
}
/// A dumper for the M3T format.
@@ -205,6 +249,25 @@ impl M3tDumper {
}
result
}
/// Dumps the extended messages in M3T format.
pub fn dump_extended(messages: &[ExtendedMessage]) -> String {
let mut result = String::new();
for message in messages {
if let Some(name) = &message.name {
result.push_str(&format!("○ NAME: {}\n\n", name));
}
result.push_str(&format!("{}\n", message.source.replace("\n", "\\n")));
if let Some(llm) = &message.llm {
result.push_str(&format!("{}\n", llm.replace("\n", "\\n")));
}
result.push_str(&format!(
"{}\n\n",
message.translated.replace("\n", "\\n")
));
}
result
}
}
#[test]

View File

@@ -258,6 +258,67 @@ impl PoDumper {
result
}
pub fn dump_extended(
mut self,
entries: &[ExtendedMessage],
encoding: Encoding,
) -> Result<String> {
self.add_entry(PoEntry {
comments: vec![
Comment::Translator(String::from("Generated by msg-tool")),
Comment::Flag(vec![String::from("fuzzy")]),
],
msgctxt: None,
msgid: String::new(),
msgid_plural: None,
msgstr: MsgStr::Single(Self::gen_start_str(encoding)),
});
let mut added = HashSet::new();
let mut added_messages: HashMap<(&String, &Option<String>), usize> = HashMap::new();
for entry in entries {
let count = added_messages
.get(&(&entry.source, &entry.name))
.map(|&s| s)
.unwrap_or(0);
let inadded = added.contains(&entry.source);
let mut comments = Vec::new();
if let Some(name) = &entry.name {
comments.push(Comment::Translator(format!("NAME: {}", name)));
}
if let Some(llm) = &entry.llm {
comments.push(Comment::Translator(format!(
"LLM: {}",
llm.replace("\n", "\\n")
)));
}
self.add_entry(PoEntry {
comments,
msgctxt: if count > 0 || inadded {
Some(format!(
"{}{}",
entry.name.as_ref().map(|s| s.as_str()).unwrap_or(""),
count
))
} else {
None
},
msgid: entry.source.clone(),
msgid_plural: None,
msgstr: MsgStr::Single(entry.translated.clone()),
});
added_messages.insert((&entry.source, &entry.name), count + 1);
if !inadded {
added.insert(&entry.source);
}
}
let mut result = String::new();
for line in &self.entries {
result.push_str(&line.dump()?);
result.push('\n');
}
Ok(result)
}
pub fn dump(mut self, entries: &[Message], encoding: Encoding) -> Result<String> {
self.add_entry(PoEntry {
comments: vec![
@@ -708,6 +769,42 @@ impl<'a> PoParser<'a> {
}
Ok(messages)
}
pub fn parse_as_extend(&mut self) -> Result<Vec<ExtendedMessage>> {
let mut messages = Vec::new();
for (i, entry) in self.parse_entries()?.into_iter().enumerate() {
if entry.msgid.is_empty() && i == 0 {
// This is the header entry, skip it
continue;
}
let mut name = None;
let mut llm = None;
for comment in &entry.comments {
if let Comment::Translator(s) = comment {
let s = s.trim();
if s.starts_with("NAME:") {
name = Some(s[5..].trim().to_string());
} else if s.starts_with("LLM:") {
llm = Some(s[4..].trim().replace("\\n", "\n"));
}
}
}
let message = match entry.msgstr {
MsgStr::Single(s) => s,
MsgStr::Plural(_) => {
return Err(anyhow!("Plural msgstr not supported in this context"));
}
};
let m = ExtendedMessage {
name: name,
source: entry.msgid,
translated: message,
llm: llm,
};
messages.push(m);
}
Ok(messages)
}
}
// --- Unit Tests ---