Beispiel #1
0
 def serialize(self, out):
     """Write the XML document to a file."""
     root = self.document.getroot()
     reindent(root, indent="    ", skip=['TS'])
     doctype = self.document.docinfo.doctype
     # Iterate over empty tags without children and force empty text
     # This will prevent self-closing tags in pretty_print mode
     # Qt Linguist does self-close the "location" elements though
     for e in root.xpath("//*[not(./node()) and not(text()) and not(name() = 'location')]"):
         e.text = ""
     # For conformance with Qt output, write XML declaration with double quotes
     out.write(str('<?xml version="1.0" encoding="utf-8"?>\n').encode('utf-8'))
     # For conformance with Qt output, post-process etree.tostring output,
     # replacing ' with &apos; and " with &quot; in text elements
     treestring = etree.tostring(root, doctype=doctype, pretty_print=True,
                                 xml_declaration=False, encoding='utf-8')
     pos = 0
     while pos >= 0:
         nextpos = treestring.find(b'<', pos)
         out.write(
             treestring[pos:nextpos].replace(b"'", b"&apos;").replace(b'"', b"&quot;")
         )
         pos = nextpos
         nextpos = treestring.find(b'>', pos)
         out.write(treestring[pos:nextpos])
         pos = nextpos
     out.write(treestring[pos:])
Beispiel #2
0
 def serialize(self, out=None):
     """Converts to a string containing the file's XML"""
     root = self.document.getroot()
     if self.XMLdoublequotes:
         out.write(b'<?xml version="1.0" encoding="utf-8"?>\n')
     if self.XMLindent:
         reindent(root, **self.XMLindent)
     if 1:
         treestring = etree.tostring(
             self.document,
             pretty_print=not self.XMLindent,
             xml_declaration=not self.XMLdoublequotes,
             encoding=self.encoding,
             doctype=self.XMLdoctype,
         )
         treestring = self.serialize_hook(treestring)
         out.write(treestring)
         return
     self.document.write(
         out,
         pretty_print=not self.XMLindent,
         xml_declaration=not self.XMLdoublequotes,
         encoding="utf-8",
         doctype=self.XMLdoctype,
     )
Beispiel #3
0
 def serialize(self, out):
     """Write the XML document to a file."""
     root = self.document.getroot()
     reindent(root, indent="    ", skip=['TS'])
     doctype = self.document.docinfo.doctype
     # Iterate over empty tags without children and force empty text
     # This will prevent self-closing tags in pretty_print mode
     # Qt Linguist does self-close the "location" elements though
     for e in root.xpath("//*[not(./node()) and not(text()) and not(name() = 'location')]"):
         e.text = ""
     # For conformance with Qt output, write XML declaration with double quotes
     out.write(str('<?xml version="1.0" encoding="utf-8"?>\n').encode('utf-8'))
     # For conformance with Qt output, post-process etree.tostring output,
     # replacing ' with &apos; and " with &quot; in text elements
     treestring = etree.tostring(root, doctype=doctype, pretty_print=True,
                                 xml_declaration=False, encoding='utf-8')
     pos = 0
     while pos >= 0:
         nextpos = treestring.find(b'<', pos)
         out.write(
             treestring[pos:nextpos].replace(b"'", b"&apos;").replace(b'"', b"&quot;")
         )
         pos = nextpos
         nextpos = treestring.find(b'>', pos)
         out.write(treestring[pos:nextpos])
         pos = nextpos
     out.write(treestring[pos:])
Beispiel #4
0
 def serialize(self, out=None):
     """Converts to a string containing the file's XML"""
     out.write(b'<?xml version="1.0" encoding="utf-8"?>\n')
     reindent(self.document.getroot(), indent="    ", max_level=3)
     self.document.write(out,
                         pretty_print=False,
                         xml_declaration=False,
                         encoding='utf-8')
Beispiel #5
0
 def serialize(self, out=None):
     root = self.document.getroot()
     reindent(root, indent="  ", max_level=4)
     # Use same header as Visual Studio
     out.write(b'<?xml version="1.0" encoding="utf-8"?>\n')
     content = etree.tostring(root, pretty_print=False, xml_declaration=False,
                              encoding='utf-8')
     # Additional space on empty tags same as Visual Studio
     out.write(content.replace(b'/>', b' />'))
Beispiel #6
0
 def serialize(self, out=None):
     """Converts to a string containing the file's XML"""
     out.write(b'<?xml version="1.0" encoding="utf-8"?>\n')
     reindent(self.document.getroot(),
              indent="    ",
              leaves=('string', 'item'))
     self.document.write(out,
                         pretty_print=False,
                         xml_declaration=False,
                         encoding='utf-8',
                         doctype=self._doctype)
    def test_indent_tab(self):
        """Test that using a tab for indent yields a consistent result."""
        xmlsource = self._xmlfromstring('<root><str key="test">Test</str></root>')
        reindent(xmlsource, indent="\t")
        actual = self._xmltostring(xmlsource)
        expected = b'''<?xml version='1.0' encoding='utf-8'?>
<root>
\t<str key="test">Test</str>
</root>
'''
        assert actual == expected
Beispiel #8
0
    def test_indent_four_spaces(self):
        """Test that using 4 spaces for indent yields a consistent result."""
        xmlsource = self._xmlfromstring(
            '<root><str key="test">Test</str></root>')
        reindent(xmlsource, indent="    ")
        actual = self._xmltostring(xmlsource)
        expected = b"""<?xml version='1.0' encoding='utf-8'?>
<root>
    <str key="test">Test</str>
</root>
"""
        assert actual == expected
Beispiel #9
0
    def reindent(self):
        """Reindents the backing document to be consistent."""
        # no elements? nothing to do.
        if not (len(self.root)):
            pass

        if self.indent_chars is None:
            # indent None means: linearize
            self.root.text = None
            for child in self.root:
                child.tail = None
        else:
            reindent(self.root, indent=self.indent_chars)

        if self.trailing_eol:
            # ensure trailing EOL for VCS
            self.root.tail = "\n"
def main():
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "--vpn",
        required=True,
        dest="vpn_path",
        help="Path to VPN project repository",
    )
    parser.add_argument(
        "--l10n",
        required=True,
        dest="l10n_path",
        help="Path to folder including subfolders for all locales",
    )
    parser.add_argument(
        "--lib",
        required=False,
        default="",
        dest="lib_path",
        help="Path to qt libraries",
    )
    args = parser.parse_args()

    source_file = os.path.join(args.vpn_path, "src", "src.pro")
    exe_path = os.path.join(args.lib_path, "lupdate")
    os.system(f"{exe_path} {source_file} -ts")

    source_ts_file = os.path.join(args.vpn_path, "translations", "mozillavpn_en.ts")
    output_xliff_file = os.path.join(args.l10n_path, "en", "mozillavpn.xliff")

    # Update English XLIFF file
    print(f"Updating {output_xliff_file}")
    exe_path = os.path.join(args.lib_path, "lconvert")
    os.system(f"{exe_path} -if ts -i {source_ts_file} -of xlf -o {output_xliff_file}")

    # Clean up the new XLIFF file
    NS = {"x": "urn:oasis:names:tc:xliff:document:1.2"}
    tree = etree.parse(output_xliff_file)
    root = tree.getroot()
    objectify.deannotate(root, cleanup_namespaces=True)

    # Remove targets (i.e. translations) if present, since this is the reference
    # locale
    for target in root.xpath("//x:target", namespaces=NS):
        if target is not None:
            target.getparent().remove(target)

    # Remove the xml:space attribute in the <source>, since it's not used when
    # exporting back to .ts
    for source in root.xpath("//x:source", namespaces=NS):
        attrib_name = "{http://www.w3.org/XML/1998/namespace}space"
        source.attrib.pop(attrib_name)

    # Change QT <extracomment> elements into <notes>
    for extracomment in root.xpath("//x:extracomment", namespaces=NS):
        extracomment.tag = "note"

    # Remove all <context-group> elements
    for context_group in root.xpath("//x:context-group", namespaces=NS):
        context_group.getparent().remove(context_group)

    # Replace the existing locale file with the new XML content
    with open(output_xliff_file, "w") as fp:
        # Fix identation of XML file
        reindent(root)
        xliff_content = etree.tostring(
            tree, encoding="UTF-8", xml_declaration=True, pretty_print=True
        )
        fp.write(xliff_content.decode("utf-8"))
Beispiel #11
0
 def serialize(self, out):
     self.removedefaultfile()
     reindent(self.document.getroot(), indent="  ", max_level=4)
     super(xlifffile, self).serialize(out)
Beispiel #12
0
def main():
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "--reference",
        required=True,
        dest="reference_locale",
        help="Reference locale code",
    )
    parser.add_argument(
        "--path",
        required=True,
        dest="base_folder",
        help="Path to folder including subfolders for all locales",
    )
    parser.add_argument(
        "--xliff",
        required=True,
        dest="xliff_filename",
        help="Name of the XLIFF file to process",
    )
    parser.add_argument(
        "--nofile",
        required=False,
        dest="ignore_file",
        action="store_true",
        help=
        "Ignore the 'file' element when storing existing translations, only use IDs and source string",
    )
    parser.add_argument("locales", nargs="*", help="Locales to process")
    args = parser.parse_args()

    reference_locale = args.reference_locale
    excluded_locales = [reference_locale]

    # Get a list of files to update (absolute paths)
    base_folder = os.path.realpath(args.base_folder)

    reference_file_path = os.path.join(base_folder, reference_locale,
                                       args.xliff_filename)
    if not os.path.isfile(reference_file_path):
        sys.exit(
            f"Requested reference file doesn't exist: {reference_file_path}")

    file_paths = []
    if not args.locales:
        for xliff_path in glob(base_folder + "/*/" + args.xliff_filename):
            parts = xliff_path.split(os.sep)
            if not parts[-2] in excluded_locales:
                file_paths.append(xliff_path)
    else:
        for locale in args.locales:
            if locale in excluded_locales:
                print(
                    f"Requested locale is in the list of excluded locales: {locale}"
                )
                continue

            if os.path.isdir(locale):
                file_paths.append(
                    os.path.join(base_folder, locale, args.xliff_filename))
            else:
                print(f"Requested locale doesn't exist: {locale}")

    if not file_paths:
        sys.exit("No locales updated.")
    else:
        file_paths.sort()

    # Read reference XML file
    try:
        reference_tree = etree.parse(reference_file_path)
    except Exception as e:
        sys.exit(f"ERROR: Can't parse reference {reference_locale} file\n{e}")

    for file_path in file_paths:
        print(f"Updating {file_path}")

        # Make a copy of the reference tree and root
        reference_tree_copy = deepcopy(reference_tree)
        reference_root_copy = reference_tree_copy.getroot()

        # Read localized XML file
        try:
            locale_tree = etree.parse(file_path)
            locale_root = locale_tree.getroot()
        except Exception as e:
            print(f"ERROR: Can't parse {file_path}")
            print(e)
            continue
        """
        Using locale folder as locale code for the target-language attribute.
        This can be use to map a locale code to a different one.
        Structure: "locale folder" -> "locale code"
        """
        locale_code = file_path.split(os.sep)[-2]
        locale_mapping = {}
        locale_code = locale_mapping.get(locale_code, locale_code)
        """
         Store existing localizations in a dictionary.
         The key for each translation is a combination of the <file> "original"
         attribute, the <trans-unit> "id", and the <source> text.
         This allows to invalidate a translation if the source string
         changed without using a new ID.
        """
        translations = {}
        for trans_node in locale_root.xpath("//x:trans-unit", namespaces=NS):
            for child in trans_node.xpath("./x:target", namespaces=NS):
                file_name = trans_node.getparent().getparent().get("original")
                source_string = trans_node.xpath("./x:source",
                                                 namespaces=NS)[0].text
                if args.ignore_file:
                    string_id = f"{trans_node.get('id')}:{hash(source_string)}"
                else:
                    string_id = (
                        f"{file_name}:{trans_node.get('id')}:{hash(source_string)}"
                    )
                translations[string_id] = child.text

        # Inject available translations in the reference XML
        for trans_node in reference_root_copy.xpath("//x:trans-unit",
                                                    namespaces=NS):
            # Add xml:space="preserve" to all trans-units, to avoid conflict
            # with Pontoon
            attrib_name = "{http://www.w3.org/XML/1998/namespace}space"
            xml_space = trans_node.get(attrib_name)
            if xml_space is None:
                trans_node.set(attrib_name, "preserve")

            file_name = trans_node.getparent().getparent().get("original")
            source_string = trans_node.xpath("./x:source",
                                             namespaces=NS)[0].text
            original_id = trans_node.get("id")
            if args.ignore_file:
                string_id = f"{original_id}:{hash(source_string)}"
            else:
                string_id = f"{file_name}:{original_id}:{hash(source_string)}"
            updated = False
            translated = string_id in translations
            for child in trans_node.xpath("./x:target", namespaces=NS):
                if translated:
                    child.text = translations[string_id]
                else:
                    # No translation available, remove the target
                    child.getparent().remove(child)
                updated = True

            if translated and not updated:
                # Translation is available, but the refence XLIFF has no target.
                # Create a target node and insert it after source.
                child = etree.Element("target")
                child.text = translations[string_id]
                trans_node.insert(1, child)

        # Update target-language where defined, replace underscores with
        # hyphens if necessary (e.g. en_GB => en-GB).
        for file_node in reference_root_copy.xpath("//x:file", namespaces=NS):
            if file_node.get("target-language"):
                file_node.set("target-language", locale_code.replace("_", "-"))

        # Replace the existing locale file with the new XML content
        with open(file_path, "w") as fp:
            # Fix identation of XML file
            reindent(reference_root_copy)
            xliff_content = etree.tostring(
                reference_tree_copy,
                encoding="UTF-8",
                xml_declaration=True,
                pretty_print=True,
            )
            fp.write(xliff_content.decode("utf-8"))
Beispiel #13
0
 def serialize(self, out=None):
     reindent(self.document.getroot(), indent="  ", max_level=4)
     super(RESXFile, self).serialize(out)
Beispiel #14
0
 def serialize(self, out):
     self.removedefaultfile()
     reindent(self.document.getroot(), indent="  ", max_level=4)
     super(xlifffile, self).serialize(out)
Beispiel #15
0
 def serialize(self, out=None):
     reindent(self.document.getroot(), indent="  ", max_level=4)
     super(RESXFile, self).serialize(out)