mirror of
https://github.com/lifegpc/msg-tool.git
synced 2026-06-07 21:38:58 +08:00
Add a new comand convert to convert between outputt script types
This commit is contained in:
@@ -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]
|
||||
|
||||
@@ -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 ---
|
||||
|
||||
Reference in New Issue
Block a user