Python: синтаксический анализ XML (xliff) файла, включая заголовки - PullRequest
0 голосов
/ 10 мая 2019

Я пытаюсь проанализировать файл XML (точнее, файл перевода XLIFF) и преобразовать его в (немного другой) формат TMX.

Мой исходный файл XLIFF выглядит так:

<?xml version="1.0" encoding="UTF-8"?>
<xliff version="1.0">
  <file origin="Some/Folder/proj/SomeFile.strings" source-language="en" target-language="hr" datatype="strings" product="Product BlahBlah" product-version="3.9.12" build-num="1" x-train="Blurt">
    <header>
      <count-group name="SomeFile.strings">
        <count count-type="total" unit="word">2</count>
      </count-group>
    </header>
    <body>
      <trans-unit id="8.text" restype="string" resname=""><source>End</source><target match-quality="80" match-description="_predecessor(22) _path(0) _file(15) datatype(5) id(17) restype(6) resname(4) _reserved(11) _one-word-threshold(-25)" state="signed-off" x-match-attributes="preserved-stable" state-qualifier="exact-match" x-leverage-path="predecessor-ice">Kraj</target><note>This is a note</note></trans-unit>
    </body>
  </file>
  <file origin="Some/Folder/proj/SomeOtherFile.strings" source-language="en" target-language="hr" datatype="strings" product="Product BlahBlah2" product-version="3.12.56" build-num="1" x-train="Blurt2">
    <header>
      <count-group name="SomeOtherFile.strings">
        <count count-type="total" unit="word">4</count>
      </count-group>
    </header>
    <body>
      <trans-unit id="14.accessibilityLabel" restype="string" resname=""><source>return to project list</source><target match-quality="80" match-description="_predecessor(22) _path(0) _file(15) datatype(5) id(17) restype(6) resname(4) _reserved(11)" state="signed-off" x-match-attributes="preserved-stable" state-qualifier="exact-match" x-leverage-path="predecessor-ice">povratak na popis projekata</target><note>This is again a note</note></trans-unit>
    </body>
  </file>

  (and more <file> elements continue... some with many more <trans-unit> </trans-unit> elements than these above)

  </xliff>

То, что я собираюсь сделать, это немного изменить и упростить их, чтобы получить вышеперечисленное в следующем формате:

<tu>
    <prop type="FileSource">SomeFile.strings</prop>
    <tuv xml:lang="en">
        <seg>End</seg>
    </tuv>
    <tuv xml:lang="hr">
        <prop type="Note">This is a note</prop>
        <seg>Kraj</seg>
    </tuv>
</tu>
<tu>
    <prop type="FileSource">SomeOtherFile.strings</prop>
    <tuv xml:lang="en">
        <seg>return to project list</seg>
    </tuv>
    <tuv xml:lang="hr">
        <prop type="Note">This is again a note</prop></prop>
        <seg>povratak na popis projekata</seg>
    </tuv>
</tu>

Обратите внимание, что исходный файл XLIFF может иметь несколько <file origin ...> частей, каждая из которых содержит множество элементов <trans-unit ...> (которые являются фактическими строками из этого файла ...)

Мне удалось закодировать часть, которая дает мне части «Исходная» и «Целевая», все в порядке, но мне все еще нужны части из элементов «происхождение файла» ... где определены языки (т.е. «исходный язык» и «целевой язык», которые я затем напишу как <tuv xml:lang="en"> и <tuv xml:lang="hr"> для каждой строки), и где я могу найти соответствующую ссылку на файл строк (то есть «SomeFile.strings» и « SomeOtherFile.strings ", который будет использоваться как <prop type="FileSource">SomeFile.strings</prop>).

В настоящее время у меня есть следующий код Python, который прекрасно извлекает необходимые элементы «source» и «target»:

#!/usr/bin/env python3
#

import sys

from lxml import etree

if len(sys.argv) < 2:
    print('Wrong number of arguments:\n => You need to provide a filename for processing!')
    exit()

file = sys.argv[1]

tree = etree.iterparse(file)
for action, elem in tree:
    if elem.tag == "source":
        print("<TransUnit>")
        print("\t<Source>" + elem.text  + "</Source>")
    elif elem.tag == "target":
        print("\t<Target>" + elem.text + "</Target>")
    elif elem.tag == "note":
        if elem.text is not None:
            print("\t<Note>" + elem.text + "</Note>")
            print("</TransUnit>")
        else: 
            print("</TransUnit>")
    else:
        next

Теперь, как мне также извлечь «исходный язык» (т. Е. Значение «en»), «целевой язык» (т. Е. Значение «hr») и ссылку на файл (т. Е. «SomeFile.strings») из элементы "origin origin ...." в исходном файле XLIFF?

Кроме того, мне нужно сохранить (запомните) эту ссылку на файл, т. Е .:

<prop type="FileSource">SomeOtherFile.strings</prop>
  • для всех единиц перевода (<tu>), которые принадлежат этому файлу (их может быть много, в отличие от приведенного выше примера, где в каждом «файле» есть только один

Так, например, у меня будет:

<tu>
    <prop type="FileSource">SomeFile.strings</prop>
    <tuv xml:lang="en">
        <seg>End</seg>
    </tuv>
    <tuv xml:lang="hr">
        <prop type="Note">This is a note</prop>
        <seg>Kraj</seg>
    </tuv>
</tu>
<tu>
    <prop type="FileSource">SomeFile.strings</prop>
    <tuv xml:lang="en">
        <seg>Start</seg>
    </tuv>
    <tuv xml:lang="hr">
        <prop type="Note">This is a note</prop>
        <seg>Početak</seg>
    </tuv>
</tu>
  • , где каждый элемент <tu> имеет элемент <prop type="FileSource">, показывающий, из какого файла он пришел ...

Я был бы более чем признателен за любую помощь в этом отношении ...

1 Ответ

0 голосов
/ 11 мая 2019

Хех, как это часто бывает, я нашел подходящее решение после еще нескольких копаний ... Возможно, мой вопрос был излишне сложным, в то время как проблема на самом деле заключалась в определении правильного корневого элемента (элементов) и правильной адресации (и нацеливании) детей и внуков.

В любом случае, другой поток stackoverflow поставил меня на правильный путь, поэтому подходящее мне решение теперь выглядит так:

#!/usr/bin/env python3
#

import sys
import os

from lxml import etree

if len(sys.argv) < 2:
    print('Wrong number of arguments:\n => You need to provide a filename for processing!')
    exit()

file = sys.argv[1]

tree = etree.parse(file)
root = tree.getroot()

print("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE tmx SYSTEM \"tmx14.dtd\">\n<tmx version=\"1.4\">")
print("\n<header srclang=\"en\" creationtool=\"XLIFF to TMX\" datatype=\"unknown\" adminlang=\"en\" segtype=\"sentence\" creationtoolversion=\"1.0\">")
print("</header>\n<body>")

for element in root:
    FileOrigin = (os.path.basename(element.attrib['origin']))
    Product = element.attrib['product']
    Source = element.attrib['source-language']
    Target =  element.attrib['target-language']
    # now the children
    for all_tags in element.findall('.//'):
        if all_tags.tag == "source":
            # replacing some troublesome and unnecessary codes
            srctxt = all_tags.text
            srctxt = srctxt.replace('^n', ' ')
            srctxt = srctxt.replace('^b', ' ')
            print("<tu>")
            print("\t<prop type=\"FileSource\">" + FileOrigin + "</prop>")
            print("\t<tuv xml:lang=\"" + Source + "\">")
            print("\t\t<seg>" + srctxt + "</seg>")
        elif all_tags.tag == "target":
            # replacing the same troublesome and unnecessary codes
            targtxt = all_tags.text
            targtxt = targtxt.replace('^n', ' ')
            targtxt = targtxt.replace('^b', ' ')
            print("\t<tuv xml:lang=\"" + Target + "\">")
            print("\t\t<seg>" + targtxt + "</seg>")
        elif all_tags.tag == "note":
            if all_tags.text is not None:
                print("\t\t<prop type=\"Note\">" + all_tags.text.replace('^n', ' ') + "</prop>")
                print("</tu>")
            else: 
                print("</tu>")
        else:
            next
print("</body>\n</tmx>")

Вероятно, немного приберутся и добавят еще несколько наворотов, но в целом это решает мою первоначальную проблему. Возможно, это могло бы помочь другим, пытающимся сделать некоторый синтаксический анализ xliff ...

...