Hello, thank you for sharing this tool.
I found a problem related to merge operation, I'll try to describe it in this issue
How to reproduce
test.skl
<?xml version="1.0" encoding="UTF-8"?>
<ROOT>
<NIV1>%%%1%%%
</NIV1>
</ROOT>
test.xlf
<?xml version="1.0" encoding="UTF-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:2.0" xmlns:mtc="urn:oasis:names:tc:xliff:matches:2.0" xmlns:mda="urn:oasis:names:tc:xliff:metadata:2.0" srcLang="fr" version="2.1" trgLang="en-US">
<file original="test.xml" id="1">
<skeleton href="test.skl"/>
<mda:metadata>
<mda:metaGroup category="format">
<mda:meta type="datatype">xml</mda:meta>
</mda:metaGroup>
<mda:metaGroup category="tool">
<mda:meta type="tool-id">OpenXLIFF</mda:meta>
<mda:meta type="tool-name">OpenXLIFF Filters</mda:meta>
<mda:meta type="tool-version">3.15.0 20230913_0710</mda:meta>
</mda:metaGroup>
<mda:metaGroup category="PI">
<mda:meta type="encoding">UTF-8</mda:meta>
</mda:metaGroup>
</mda:metadata>
<unit id="1">
<mda:metadata id="1">
<mda:metaGroup category="attributes" id="ph0">
<mda:meta type="ctype">x-bold</mda:meta>
</mda:metaGroup>
</mda:metadata>
<originalData>
<data id="ph0"><G></data>
<data id="ph1"></G></data>
</originalData>
<ignorable>
<source xml:space="preserve">
<ph id="ph0"/>
</source>
</ignorable>
<segment state="final" id="1-0">
<source xml:space="preserve"> Bonjour. </source>
<target> Hello. </target>
</segment>
<ignorable>
<source xml:space="preserve">
<ph id="ph1"/>
</source>
</ignorable>
<segment state="final" id="1-1">
<source xml:space="preserve"> Ce text devrait être traduit </source>
<target> This text should be translated </target>
</segment>
</unit>
</file>
</xliff>
command
./merge.sh -xliff test.xlf -target result.xml
actual xml result
<?xml version="1.0" encoding="UTF-8"?>
<ROOT>
<NIV1>
<G>
Bonjour.
Hello.
</G>
Ce text devrait être traduit This text should be translated </NIV1>
</ROOT>
expected xml result
<?xml version="1.0" encoding="UTF-8"?>
<ROOT>
<NIV1><G> Hello. </G> This text should be translated </NIV1>
</ROOT>
Investigation
First, I observed that this issue appears because I formatted the xliff file.
If I rollback some changes, the result is correct. Rolled back changes are:
<ignorable>
<source xml:space="preserve">
<ph id="ph0"/>
</source>
</ignorable>
→
<ignorable>
<source xml:space="preserve"><ph id="ph0"/></source>
</ignorable>
(needed as well for ph1
)
Looking into the code, I think I understand the problem.
In https://github.com/rmraya/OpenXLIFF/blob/v3.15.0/src/com/maxprograms/xliff2/FromXliff2.java#L294-L326
We have:
joinedSource.addContent(src.getContent());
joinedTarget.addContent(src.getContent());
At this point joinedSource
and joinedTarget
point to the same content (same java objects per references)
Then l. 326 we have:
src.setContent(harvestContent(joinedSource, tags, attributes));
Which internally calls joinedSource.getContent();
which itself calls Element#mergeText
.
And this is precisely were we have an issue.
When harvesting for the source we call the mergeText
function that mutates some XMLnode
s that are also referenced by the target. After harvesting for the source, the content of target becomes invalid.
To confirm this hypothesis, I simply removed the call to #mergeText
in Element#getContent
and it actually fixes the problem.
Solution
I don't know what would be the best to fix this issue, I think there are different possibilities like:
- Not mutating state in a method that apparently only reads data (we don't expect
#getContent
to mutate the state of Element
)
- I saw that this method was also called in the
equals
method, which can be dangerous as well
- Not sharing data between 2
Element
objets, in which case that would requires to copy XMLNode
s
If you want me to share a PR with a fix proposal, feel free to ask.
By the way, I think it could be very helpful to introduce a pom.xml
in order to have maven dependencies (in which case it would be necessary to publish XMLJava
in maven central)