use std::{ fs::File, io::{BufReader, Read, Write}, path::Path, }; use icu_locid::{langid, LanguageIdentifier}; use xml::{attribute::OwnedAttribute, reader, writer, EmitterConfig, EventReader, EventWriter}; use crate::{Bundle, Message, ReadError, WriteError}; pub fn export_file( bundle: &Bundle, locale: LanguageIdentifier, path: &Path, ) -> Result<(), WriteError> { let mut file = File::create(path).unwrap(); export_fh(bundle, locale, &mut file) } pub fn export_fh(bundle: &Bundle, locale: LanguageIdentifier, fh: W) -> Result<(), WriteError> where W: Write, { let mut writer = EmitterConfig::new().perform_indent(true).create_writer(fh); writer .write( writer::XmlEvent::start_element("xliff") .attr("xmlns", "urn:oasis:names:tc:xliff:document:2.0") .attr("version", "2.0") .attr("srcLang", &format!("{}", locale)), ) .unwrap(); writer .write(writer::XmlEvent::start_element("file").attr("id", "main")) .unwrap(); for (key, message) in bundle.message_iter() { write_message(&mut writer, key, message, &locale); } writer.write(writer::XmlEvent::end_element()).unwrap(); writer.write(writer::XmlEvent::end_element()).unwrap(); Ok(()) } pub fn import_file(bundle: &mut Bundle, path: &Path) -> Result<(), ReadError> { let file = File::open(path).unwrap(); let file = BufReader::new(file); import_reader(bundle, file) } pub fn import_reader(bundle: &mut Bundle, fh: R) -> Result<(), ReadError> where R: Read, { let parser = EventReader::new(fh); let mut locale: LanguageIdentifier = langid!("en"); let mut current_key = None; let mut current_text: Option = None; let mut in_target = false; for event in parser { match event { Ok(reader::XmlEvent::StartElement { name, attributes, .. }) => match name.local_name.as_ref() { "xliff" => { locale = find_attribute(&attributes, "trgLang") .unwrap() .parse::() .unwrap(); } "unit" => current_key = find_attribute(&attributes, "id"), "target" => in_target = true, _ => println!("name: {}", name), }, Ok(reader::XmlEvent::EndElement { name }) => match name.local_name.as_ref() { "unit" => { if let Some(key) = current_key { let message = bundle.message(key); let variant = message.variant_mut(locale.clone()); if let Some(ref text) = current_text { variant.set_content(text.clone()); } } current_key = None; } "target" => in_target = false, _ => {} }, Ok(reader::XmlEvent::Characters(data)) => { if in_target { current_text = Some(data) } } Err(e) => { eprintln!("error: {e}"); break; } _ => {} } } Ok(()) } fn write_message( writer: &mut EventWriter, key: &str, message: &Message, locale: &LanguageIdentifier, ) where T: std::io::Write, { [ writer::XmlEvent::start_element("unit") .attr("id", key) .into(), writer::XmlEvent::start_element("notes").into(), writer::XmlEvent::start_element("note").into(), writer::XmlEvent::characters(message.description()), writer::XmlEvent::end_element().into(), writer::XmlEvent::end_element().into(), writer::XmlEvent::start_element("segment").into(), writer::XmlEvent::start_element("source").into(), writer::XmlEvent::characters(message.variant(locale).unwrap().content()), writer::XmlEvent::end_element().into(), writer::XmlEvent::end_element().into(), writer::XmlEvent::end_element().into(), ] .into_iter() .for_each(|elem: writer::XmlEvent| writer.write(elem).unwrap()); } fn find_attribute(attrs: &Vec, name: &str) -> Option { for f in attrs { if name == f.name.local_name { return Some(f.value.clone()); } } None }