使用Okapi XLIFF 2.0 lib生成CDATA段的问题求助
Okapi XLIFF2 写入器CDATA转义问题解决
问题背景
使用okapi-lib-xliff2:1.44.0生成.xlf文件时,需要为部分元素添加CDATA段,这符合XLIFF 2.0规范要求,但当前写入器会将CDATA标签及内容中的内联代码全部转义,导致输出不符合预期。未找到该库处理CDATA的相关文档,也不清楚需要传递什么参数让写入器正确生成CDATA。
代码示例
try (XLIFFWriter writer = new XLIFFWriter()) { writer.setUseIndentation(true); writer.create( new File("cdata.xlf"), Locale.US.toString(), Locale.FRANCE.toString()); StartFileData fileElementAttribute = new StartFileData(null); String originalFile = "with_cdata.xlf"; fileElementAttribute.setId("1"); fileElementAttribute.setOriginal(originalFile); writer.writeStartFile(fileElementAttribute); Unit unit = new Unit("1"); ExtAttributes additionalAttributes = new ExtAttributes(); additionalAttributes.setAttribute(new ExtAttribute(QName.valueOf("xml:space"), "preserve")); unit.setExtAttributes(additionalAttributes); String segmentId = "test-key-1"; unit.setName(segmentId); unit.setCanResegment(false); Segment segment = unit.appendSegment(); segment.setCanResegment(false); segment.setSource(new CDATAEncoder("UTF-8", "\n").encode("<b>Hello</b>", EncoderContext.TEXT)); segment.setTarget(new CDATAEncoder("UTF-8", "\n").encode("<b>Bonjour</b>", EncoderContext.TEXT)); Note originalComment = new Note(); originalComment.setCategory("engineer-comment"); originalComment.setText(new CDATAEncoder("UTF-8", "\n").encode("This is translation for <b>Hello</b>", EncoderContext.TEXT)); unit.addNote(originalComment); Metadata unitMetadata = new Metadata(); MetaGroup metaGroup = new MetaGroup(); metaGroup.setCategory("unitMetadata"); Meta meta = new Meta("key-1"); meta.setData(new CDATAEncoder("UTF-8", "\n").encode("This is translation for <b>Hello</b>", EncoderContext.TEXT)); metaGroup.add(meta); unitMetadata.addGroup(metaGroup); unit.setMetadata(unitMetadata); writer.writeUnit(unit); }
当前输出
<?xml version="1.0"?> <xliff xmlns="urn:oasis:names:tc:xliff:document:2.0" version="2.0" srcLang="en_US" trgLang="fr_FR"> <file id="1" original="with_cdata.xlf"> <unit id="1" canResegment="no" name="test-key-1" xml:space="preserve"> <mda:metadata xmlns:mda="urn:oasis:names:tc:xliff:metadata:2.0"> <mda:metaGroup category="unitMetadata"> <mda:meta type="key-1"><![CDATA[This is translation for <b>Hello</b>]]></mda:meta> </mda:metaGroup> </mda:metadata> <notes> <note category="engineer-comment"><![CDATA[This is translation for <b>Hello</b>]]></note> </notes> <segment> <source><![CDATA[<b>Hello</b>]]></source> <target><![CDATA[<b>Bonjour</b>]]></target> </segment> </unit> </file> </xliff>
期望输出
<?xml version="1.0"?> <xliff xmlns="urn:oasis:names:tc:xliff:document:2.0" version="2.0" srcLang="en_US" trgLang="fr_FR"> <file id="1" original="with_cdata.xlf"> <unit id="1" canResegment="no" name="test-key-1" xml:space="preserve"> <mda:metadata xmlns:mda="urn:oasis:names:tc:xliff:metadata:2.0"> <mda:metaGroup category="unitMetadata"> <mda:meta type="key-1"><![CDATA[This is translation for <b>Hello</b>]]></mda:meta> </mda:metaGroup> </mda:metadata> <notes> <note category="engineer-comment"><![CDATA[This is translation for <b>Hello</b>]]></note> </notes> <segment> <source><![CDATA[<b>Hello</b>]]></source> <target><![CDATA[<b>Bonjour</b>]]></target> </segment> </unit> </file> </xliff>
解决方案
问题出在手动使用CDATAEncoder生成的CDATA标签会被XLIFFWriter再次转义。Okapi的XLIFF2写入器本身支持CDATA,不需要手动编码,直接传入原始内容后,通过自定义XML输出配置标记内容需要用CDATA包裹即可。
修改后的代码示例
import java.io.File; import java.util.Arrays; import java.util.Set; import java.util.HashSet; import java.util.Locale; import javax.xml.stream.XMLStreamWriter; import net.sf.okapi.lib.xliff2.writer.XLIFFWriter; import net.sf.okapi.lib.xliff2.core.StartFileData; import net.sf.okapi.lib.xliff2.core.Unit; import net.sf.okapi.lib.xliff2.core.ExtAttributes; import net.sf.okapi.lib.xliff2.core.ExtAttribute; import javax.xml.namespace.QName; import net.sf.okapi.lib.xliff2.core.Segment; import net.sf.okapi.lib.xliff2.core.Note; import net.sf.okapi.lib.xliff2.metadata.Metadata; import net.sf.okapi.lib.xliff2.metadata.MetaGroup; import net.sf.okapi.lib.xliff2.metadata.Meta; try (XLIFFWriter writer = new XLIFFWriter()) { writer.setUseIndentation(true); // 配置需要输出CDATA的元素列表 Set<String> cdataElements = new HashSet<>(Arrays.asList("source", "target", "note", "meta")); XMLStreamWriter xmlWriter = writer.getXMLStreamWriter(); // 包装XML写入器,自动处理CDATA CDATAEnabledXMLWriter cdataWriter = new CDATAEnabledXMLWriter(xmlWriter, cdataElements); writer.setXMLStreamWriter(cdataWriter); writer.create( new File("cdata.xlf"), Locale.US.toString(), Locale.FRANCE.toString()); StartFileData fileElementAttribute = new StartFileData(null); String originalFile = "with_cdata.xlf"; fileElementAttribute.setId("1"); fileElementAttribute.setOriginal(originalFile); writer.writeStartFile(fileElementAttribute); Unit unit = new Unit("1"); ExtAttributes additionalAttributes = new ExtAttributes(); additionalAttributes.setAttribute(new ExtAttribute(QName.valueOf("xml:space"), "preserve")); unit.setExtAttributes(additionalAttributes); String segmentId = "test-key-1"; unit.setName(segmentId); unit.setCanResegment(false); Segment segment = unit.appendSegment(); segment.setCanResegment(false); // 直接传入原始内容,无需手动编码CDATA segment.setSource("<b>Hello</b>"); segment.setTarget("<b>Bonjour</b>"); Note originalComment = new Note(); originalComment.setCategory("engineer-comment"); originalComment.setText("This is translation for <b>Hello</b>"); unit.addNote(originalComment); Metadata unitMetadata = new Metadata(); MetaGroup metaGroup = new MetaGroup(); metaGroup.setCategory("unitMetadata"); Meta meta = new Meta("key-1"); meta.setData("This is translation for <b>Hello</b>"); metaGroup.add(meta); unitMetadata.addGroup(metaGroup); unit.setMetadata(unitMetadata); writer.writeUnit(unit); }
配套CDATA写入器包装类
import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import java.util.Set; public class CDATAEnabledXMLWriter implements XMLStreamWriter { private final XMLStreamWriter delegate; private final Set<String> cdataElements; private String currentElement; public CDATAEnabledXMLWriter(XMLStreamWriter delegate, Set<String> cdataElements) { this.delegate = delegate; this.cdataElements = cdataElements; } @Override public void writeStartElement(String localName) throws XMLStreamException { this.currentElement = localName; delegate.writeStartElement(localName); } @Override public void writeStartElement(String namespaceURI, String localName) throws XMLStreamException { this.currentElement = localName; delegate.writeStartElement(namespaceURI, localName); } @Override public void writeStartElement(String prefix, String localName, String namespaceURI) throws XMLStreamException { this.currentElement = localName; delegate.writeStartElement(prefix, localName, namespaceURI); } @Override public void writeCharacters(String text) throws XMLStreamException { if (cdataElements.contains(currentElement)) { delegate.writeCData(text); } else { delegate.writeCharacters(text); } } @Override public void writeEndElement() throws XMLStreamException { delegate.writeEndElement(); this.currentElement = null; } // 以下为委托给原始XMLStreamWriter的其他方法 @Override public void writeNamespace(String prefix, String namespaceURI) throws XMLStreamException { delegate.writeNamespace(prefix, namespaceURI); } @Override public void writeDefaultNamespace(String namespaceURI) throws XMLStreamException { delegate.writeDefaultNamespace(namespaceURI); } @Override public void writeEmptyElement(String localName) throws XMLStreamException { delegate.writeEmptyElement(localName); } @Override public void writeEmptyElement(String namespaceURI, String localName) throws XMLStreamException { delegate.writeEmptyElement(namespaceURI, localName); } @Override public void writeEmptyElement(String prefix, String localName, String namespaceURI) throws XMLStreamException { delegate.writeEmptyElement(prefix, localName, namespaceURI); } @Override public void writeEndDocument() throws XMLStreamException { delegate.writeEndDocument(); } @Override public void close() throws XMLStreamException { delegate.close(); } @Override public void flush() throws XMLStreamException { delegate.flush(); } @Override public void writeAttribute(String localName, String value) throws XMLStreamException { delegate.writeAttribute(localName, value); } @Override public void writeAttribute(String prefix, String namespaceURI, String localName, String value) throws XMLStreamException { delegate.writeAttribute(prefix, namespaceURI, localName, value); } @Override public void writeAttribute(String namespaceURI, String localName, String value) throws XMLStreamException { delegate.writeAttribute(namespaceURI, localName, value); } @Override public void writeComment(String data) throws XMLStreamException { delegate.writeComment(data); } @Override public void writeProcessingInstruction(String target) throws XMLStreamException { delegate.writeProcessingInstruction(target); } @Override public void writeProcessingInstruction(String target, String data) throws XMLStreamException { delegate.writeProcessingInstruction(target, data); } @Override public void writeCData(String data) throws XMLStreamException { delegate.writeCData(data); } @Override public void writeDTD(String dtd) throws XMLStreamException { delegate.writeDTD(dtd); } @Override public void writeEntityRef(String name) throws XMLStreamException { delegate.writeEntityRef(name); } @Override public void writeStartDocument() throws XMLStreamException { delegate.writeStartDocument(); } @Override public void writeStartDocument(String version) throws XMLStreamException { delegate.writeStartDocument(version); } @Override public void writeStartDocument(String encoding, String version) throws XMLStreamException { delegate.writeStartDocument(encoding, version); } @Override public String getPrefix(String uri) throws XMLStreamException { return delegate.getPrefix(uri); } @Override public void setPrefix(String prefix, String uri) throws XMLStreamException { delegate.setPrefix(prefix, uri); } @Override public void setDefaultNamespace(String uri) throws XMLStreamException { delegate.setDefaultNamespace(uri); } @Override public void setNamespaceContext(javax.xml.stream.NamespaceContext context) throws XMLStreamException { delegate.setNamespaceContext(context); } @Override public javax.xml.stream.NamespaceContext getNamespaceContext() { return delegate.getNamespaceContext(); } @Override public Object getProperty(String name) throws IllegalArgumentException { return delegate.getProperty(name); } }
该方案通过包装XMLStreamWriter,自动识别需要输出CDATA的元素,将内容直接以CDATA形式写入,避免了手动编码导致的二次转义问题。
内容的提问来源于stack exchange,提问作者mahsa
相关产品推荐
相关产品推荐

