36 import xml.etree.ElementTree
as ET
38 accepted_exts = [
".js",
".html",
".css"]
42 ts_folder = os.path.join(www_folder,
"translations")
45 print(
"Parsing %s..." % (os.path.normpath(filename)))
46 with open(filename, encoding =
'utf-8', mode =
'r')
as file:
48 r"QBT_TR\((([^\)]|\)(?!QBT_TR))+)\)QBT_TR\[CONTEXT=([a-zA-Z_][a-zA-Z0-9_]*)\]")
49 for match
in regex.finditer(file.read()):
50 string = match.group(1)
51 context = match.group(3)
53 if context
not in sources:
54 sources[context] = set()
55 sources[context].add(string)
58 print(
'Processing %s...' % (os.path.normpath(filename)))
61 tree = ET.ElementTree(file = filename)
63 print(
'\tFailed to parse %s!' % (os.path.normpath(filename)))
67 for context
in root.findall(
'context'):
68 context_name = context.find(
'name').text
69 has_context = context_name
in sources
70 if not has_context
and no_obsolete:
74 for message
in context.findall(
'message'):
75 for location
in message.findall(
'location'):
76 message.remove(location)
78 source = message.find(
'source').text
79 translation = message.find(
'translation')
80 if has_context
and source
in sources[context_name]:
81 sources[context_name].remove(source)
83 trtype = translation.attrib.get(
'type')
84 if (trtype ==
'obsolete')
or (trtype ==
'vanished'):
85 del translation.attrib[
'type']
87 if no_obsolete
or (translation.attrib.get(
'type',
'') ==
'unfinished'):
88 context.remove(message)
90 translation.attrib[
'type'] =
'vanished'
96 for source
in sources[context_name]:
97 message = ET.SubElement(context,
'message')
98 ET.SubElement(message,
'source').text = source
99 ET.SubElement(message,
'translation', {
'type':
'unfinished'})
100 del sources[context_name]
103 for context_name
in sources:
104 context = ET.SubElement(root,
'context')
105 ET.SubElement(context,
'name').text = context_name
107 for source
in sources[context_name]:
108 message = ET.SubElement(context,
'message')
109 ET.SubElement(message,
'source').text = source
110 ET.SubElement(message,
'translation', {
'type':
'unfinished'})
115 for context
in root.findall(
'./context'):
116 context.text =
'\n' + indent
118 context.find(
'./name').tail =
'\n' + indent
119 messages = context.findall(
'./message')
120 if len(messages) == 0:
continue
122 for message
in messages:
123 message.text =
'\n' + (indent * 2)
124 message.tail =
'\n' + indent
125 elems = message.findall(
'./')
126 if len(elems) == 0:
continue
129 elem.tail =
'\n' + (indent * 2)
130 elems[-1:][0].tail =
'\n' + indent
131 messages[-1:][0].tail =
'\n'
134 with open(filename, mode =
'wb')
as file:
135 file.write(b
'<?xml version="1.0" encoding="utf-8"?>\n'
137 tree.write(file, encoding =
'utf-8')
139 print(
'\tFailed to write %s!' % (os.path.normpath(filename)))
141 argp = argparse.ArgumentParser(
142 prog =
'tstool.py', description =
'Update qBittorrent WebUI translation files.')
143 argp.add_argument(
'--no-obsolete', dest =
'no_obsolete', action =
'store_true',
144 default = no_obsolete,
145 help =
'remove obsolete messages (default: mark them as obsolete)')
146 argp.add_argument(
'--www-folder', dest =
'www_folder', action =
'store',
147 default = www_folder,
148 help =
'folder with WebUI source files (default: "%s")' % (www_folder))
149 argp.add_argument(
'--ts-folder', dest =
'ts_folder', action =
'store',
151 help =
'folder with WebUI translation files (default: "%s")' % (ts_folder))
153 args = argp.parse_args()
154 no_obsolete = args.no_obsolete
155 www_folder = args.www_folder
156 ts_folder = args.ts_folder
158 print(
"Processing source files...")
161 for root, dirs, files
in os.walk(www_folder):
163 if os.path.splitext(file)[-1]
in accepted_exts:
168 print(
"No source files found!")
171 nstrings = sum(len(sublist)
for sublist
in source_ts)
172 print(
"Found %d strings within %d contexts." % (nstrings, len(source_ts)))
175 print(
"Processing translation files...")
176 for entry
in os.scandir(ts_folder):
177 if (entry.is_file()
and entry.name.startswith(
'webui_')
178 and entry.name.endswith(
".ts")):