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 ' and " with " 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"'").replace(b'"', b""") ) pos = nextpos nextpos = treestring.find(b'>', pos) out.write(treestring[pos:nextpos]) pos = nextpos out.write(treestring[pos:])
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, )
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')
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' />'))
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
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
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"))
def serialize(self, out): self.removedefaultfile() reindent(self.document.getroot(), indent=" ", max_level=4) super(xlifffile, self).serialize(out)
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"))
def serialize(self, out=None): reindent(self.document.getroot(), indent=" ", max_level=4) super(RESXFile, self).serialize(out)