2 # -*- coding: utf-8 -*-
4 # TSTool - script for update qBittorrent WebUI translation files
5 # Copyright (C) 2018 Vladimir Golovnev <glassez@yandex.ru>
7 # This program is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU General Public License
9 # as published by the Free Software Foundation; either version 2
10 # of the License, or (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 # In addition, as a special exception, the copyright holders give permission to
22 # link this program with the OpenSSL project's "OpenSSL" library (or with
23 # modified versions of it that use the same license as the "OpenSSL" library),
24 # and distribute the linked executables. You must obey the GNU General Public
25 # License in all respects for all of the code used other than "OpenSSL". If you
26 # modify file(s), you may extend this exception to your version of the file(s),
27 # but you are not obligated to do so. If you do not wish to do so, delete this
28 # exception statement from your version.
36 import xml
.etree
.ElementTree
as ET
38 accepted_exts
= [".js", ".html", ".css"]
42 ts_folder
= os
.path
.join(www_folder
, "translations")
45 def parseSource(filename
, sources
):
46 print("Parsing %s..." % (os
.path
.normpath(filename
)))
47 with
open(filename
, encoding
='utf-8', mode
='r') as file:
49 r
"QBT_TR\((([^\)]|\)(?!QBT_TR))+)\)QBT_TR\[CONTEXT=([a-zA-Z_][a-zA-Z0-9_]*)\]")
50 for match
in regex
.finditer(file.read()):
51 string
= match
.group(1)
52 context
= match
.group(3)
54 if context
not in sources
:
55 sources
[context
] = set()
56 sources
[context
].add(string
)
59 def processTranslation(filename
, sources
):
60 print('Processing %s...' % (os
.path
.normpath(filename
)))
63 tree
= ET
.ElementTree(file=filename
)
65 print('\tFailed to parse %s!' % (os
.path
.normpath(filename
)))
69 for context
in root
.findall('context'):
70 context_name
= context
.find('name').text
71 has_context
= context_name
in sources
72 if not has_context
and no_obsolete
:
76 for message
in context
.findall('message'):
77 for location
in message
.findall('location'):
78 message
.remove(location
)
80 source
= message
.find('source').text
81 translation
= message
.find('translation')
82 if has_context
and source
in sources
[context_name
]:
83 sources
[context_name
].remove(source
)
85 trtype
= translation
.attrib
.get('type')
86 if (trtype
== 'obsolete') or (trtype
== 'vanished'):
87 del translation
.attrib
['type'] # i.e. finished
89 if no_obsolete
or (translation
.attrib
.get('type', '') == 'unfinished'):
90 context
.remove(message
)
92 translation
.attrib
['type'] = 'vanished'
97 # add new messages for current context
98 for source
in sources
[context_name
]:
99 message
= ET
.SubElement(context
, 'message')
100 ET
.SubElement(message
, 'source').text
= source
101 ET
.SubElement(message
, 'translation', {'type': 'unfinished'})
102 del sources
[context_name
]
104 # add messages for new contexts
105 for context_name
in sources
:
106 context
= ET
.SubElement(root
, 'context')
107 ET
.SubElement(context
, 'name').text
= context_name
109 for source
in sources
[context_name
]:
110 message
= ET
.SubElement(context
, 'message')
111 ET
.SubElement(message
, 'source').text
= source
112 ET
.SubElement(message
, 'translation', {'type': 'unfinished'})
114 # prettify output xml
117 for context
in root
.findall('./context'):
118 context
.text
= '\n' + indent
120 context
.find('./name').tail
= '\n' + indent
121 messages
= context
.findall('./message')
122 if len(messages
) == 0:
125 for message
in messages
:
126 message
.text
= '\n' + (indent
* 2)
127 message
.tail
= '\n' + indent
128 elems
= message
.findall('./')
133 elem
.tail
= '\n' + (indent
* 2)
134 elems
[-1:][0].tail
= '\n' + indent
135 messages
[-1:][0].tail
= '\n'
138 with
open(filename
, mode
='wb') as file:
139 file.write(b
'<?xml version="1.0" encoding="utf-8"?>\n'
141 tree
.write(file, encoding
='utf-8')
143 print('\tFailed to write %s!' % (os
.path
.normpath(filename
)))
146 argp
= argparse
.ArgumentParser(
147 prog
='tstool.py', description
='Update qBittorrent WebUI translation files.')
148 argp
.add_argument('--no-obsolete', dest
='no_obsolete', action
='store_true',
150 help='remove obsolete messages (default: mark them as obsolete)')
151 argp
.add_argument('--www-folder', dest
='www_folder', action
='store',
153 help='folder with WebUI source files (default: "%s")' % (www_folder
))
154 argp
.add_argument('--ts-folder', dest
='ts_folder', action
='store',
156 help='folder with WebUI translation files (default: "%s")' % (ts_folder
))
158 args
= argp
.parse_args()
159 no_obsolete
= args
.no_obsolete
160 www_folder
= args
.www_folder
161 ts_folder
= args
.ts_folder
163 print("Processing source files...")
166 for root
, dirs
, files
in os
.walk(www_folder
):
168 if os
.path
.splitext(file)[-1] in accepted_exts
:
169 parseSource(os
.path
.join(root
, file), source_ts
)
173 print("No source files found!")
176 nstrings
= sum(len(sublist
) for sublist
in source_ts
)
177 print("Found %d strings within %d contexts." % (nstrings
, len(source_ts
)))
180 print("Processing translation files...")
181 for entry
in os
.scandir(ts_folder
):
182 if (entry
.is_file() and entry
.name
.startswith('webui_') and entry
.name
.endswith(".ts")):
183 processTranslation(entry
.path
, copy
.deepcopy(source_ts
))