|
2 |
# -*- coding: utf-8 -*- |
2 |
# -*- coding: utf-8 -*- |
3 |
# |
3 |
# |
4 |
# Univention Configuration Registry |
4 |
# Univention Configuration Registry |
5 |
# build UMC module |
5 |
""" |
|
|
6 |
Each module definition contains the following entries: |
7 |
|
8 |
Module: The internal name of the module |
9 |
Python: A directory containing the python module. There must be a subdirectory named like the internal name of the module. |
10 |
Definition: The XML definition of the module |
11 |
Javascript: The directory of the javascript code. In this directory must be a a file called <Module>.js |
12 |
Category: The XML definition of additional categories |
13 |
Icons: A directory containing the icons used by the module. The |
14 |
directory structure must follow the following pattern |
15 |
<weight>x<height>/<icon>.(png|gif) |
16 |
|
17 |
The entry Category is optional. |
18 |
|
19 |
Example: |
20 |
Module: ucr |
21 |
Python: umc/module |
22 |
Definition: umc/ucr.xml |
23 |
Javascript: umc/js |
24 |
Category: umc/categories/ucr.xml |
25 |
Icons: umc/icons |
26 |
""" |
27 |
|
6 |
# |
28 |
# |
7 |
# Copyright 2011-2012 Univention GmbH |
29 |
# Copyright 2011-2012 Univention GmbH |
8 |
# |
30 |
# |
Lines 48-54
JAVASCRIPT = 'Javascript'
|
Link Here
|
---|
|
48 |
CATEGORY = 'Category' |
70 |
CATEGORY = 'Category' |
49 |
ICONS = 'Icons' |
71 |
ICONS = 'Icons' |
50 |
|
72 |
|
51 |
LANGUAGES = ( 'de', ) |
73 |
LANGUAGES = ('de',) |
52 |
|
74 |
|
53 |
PO_HEADER = 'This file is auto-generated by the dh-umc tools and should not be edited!' |
75 |
PO_HEADER = 'This file is auto-generated by the dh-umc tools and should not be edited!' |
54 |
PO_METADATA = { |
76 |
PO_METADATA = { |
|
64 |
'Content-Transfer-Encoding' : '8bit' |
86 |
'Content-Transfer-Encoding' : '8bit' |
65 |
} |
87 |
} |
66 |
|
88 |
|
67 |
"""Each module definition contains the following entries: |
|
|
68 |
|
89 |
|
69 |
Module: The internal name of the module |
90 |
class UMC_Module(dict): |
70 |
Python: A directory containing the python module. There must be a subdirectory named like the internal name of the module. |
91 |
""" |
71 |
Definition: The XML definition of the module |
92 |
Container for UMC module definition. |
72 |
Javascript: The directory of the javascript code. In this directory must be a a file called <Module>.js |
93 |
""" |
73 |
Category: The XML definition of additional categories |
|
|
74 |
Icons: A directory containing the icons used by the module. The |
75 |
directory structure must follow the following pattern |
76 |
<weight>x<height>/<icon>.(png|gif) |
77 |
|
94 |
|
78 |
The entry Category is optional. |
95 |
def __init__(self, *args): |
79 |
|
96 |
dict.__init__(self, *args) |
80 |
Example: |
97 |
for key in (MODULE, PYTHON, JAVASCRIPT, DEFINITION, CATEGORY, ICONS): |
81 |
Module: ucr |
98 |
if key in self and self[key]: |
82 |
Python: umc/module |
99 |
self[key] = self[key][0] |
83 |
Definition: umc/ucr.xml |
|
|
84 |
Javascript: umc/js |
85 |
Category: umc/categories/ucr.xml |
86 |
Icons: umc/icons |
87 |
""" |
88 |
|
89 |
class UMC_Module( dict ): |
90 |
def __init__( self, *args ): |
91 |
dict.__init__( self, *args ) |
92 |
for key in ( MODULE, PYTHON, JAVASCRIPT, DEFINITION, CATEGORY, ICONS ): |
93 |
if key in self and self[ key ]: |
94 |
self[ key ] = self[ key ][ 0 ] |
95 |
|
100 |
|
96 |
@property |
101 |
@property |
97 |
def package( self ): |
102 |
def package(self): |
98 |
return self.get( 'package' ) |
103 |
""" |
|
|
104 |
Return the name of the Debian binary package. |
105 |
""" |
106 |
return self.get('package') |
99 |
|
107 |
|
100 |
@property |
108 |
@property |
101 |
def python_path( self ): |
109 |
def python_path( self ): |
|
|
110 |
""" |
111 |
Return path to Python UMC directory. |
112 |
""" |
102 |
return '%(Python)s/%(Module)s/' % self |
113 |
return '%(Python)s/%(Module)s/' % self |
103 |
|
114 |
|
104 |
@property |
115 |
@property |
105 |
def js_path( self ): |
116 |
def js_path( self ): |
|
|
117 |
""" |
118 |
Return path to JavaScript UMC directory. |
119 |
""" |
106 |
return '%(Javascript)s/' % self |
120 |
return '%(Javascript)s/' % self |
107 |
|
121 |
|
108 |
@property |
122 |
@property |
109 |
def js_module_file( self ): |
123 |
def js_module_file( self ): |
|
|
124 |
""" |
125 |
Return path to main JavaScript file. |
126 |
""" |
110 |
return '%(Javascript)s/%(Module)s.js' % self |
127 |
return '%(Javascript)s/%(Module)s.js' % self |
111 |
|
128 |
|
112 |
def iter_files( self, suffix ): |
129 |
@staticmethod |
113 |
for dirname, dirs, files in os.walk( self.js_path ): |
130 |
def _iter_files(base, suffix): |
|
|
131 |
""" |
132 |
Iterate over all files below base ending with suffix. |
133 |
""" |
134 |
for dirname, dirs, files in os.walk(base): |
114 |
# ignore .svn directories |
135 |
# ignore .svn directories |
115 |
if '.svn' in dirs: |
136 |
if '.svn' in dirs: |
116 |
dirs.remove( '.svn' ) |
137 |
dirs.remove('.svn') |
117 |
# we are only interested in .js files |
138 |
# we are only interested in .js files |
118 |
for ifile in files: |
139 |
for ifile in files: |
119 |
if ifile.endswith( suffix ): |
140 |
if ifile.endswith(suffix): |
120 |
yield os.path.join(dirname, ifile) |
141 |
yield os.path.join(dirname, ifile) |
121 |
|
142 |
|
122 |
@property |
143 |
@property |
123 |
def js_files( self ): |
144 |
def js_files(self): |
124 |
return self.iter_files( '.js' ) |
145 |
""" |
125 |
# for dirname, dirs, files in os.walk( self.js_path ): |
146 |
Iterate over all JavaScript UMC files. |
126 |
# # ignore .svn directories |
147 |
""" |
127 |
# if '.svn' in dirs: |
148 |
return UMC_Module._iter_files(self.js_path, '.js') |
128 |
# dirs.remove( '.svn' ) |
|
|
129 |
# # we are only interested in .js files |
130 |
# for ifile in files: |
131 |
# if ifile.endswith('.js'): |
132 |
# yield os.path.join(dirname, ifile) |
133 |
|
149 |
|
134 |
@property |
150 |
@property |
135 |
def html_files( self ): |
151 |
def html_files(self): |
136 |
return self.iter_files( '.html' ) |
152 |
""" |
137 |
# for dirname, dirs, files in os.walk( self.js_path ): |
153 |
Iterate over all JavaScript HTML files. |
138 |
# # ignore .svn directories |
154 |
""" |
139 |
# if '.svn' in dirs: |
155 |
return UMC_Module._iter_files(self.js_path, '.html') |
140 |
# dirs.remove( '.svn' ) |
|
|
141 |
# # we are only interested in .js files |
142 |
# for ifile in files: |
143 |
# if ifile.endswith('.html'): |
144 |
# yield os.path.join(dirname, ifile) |
145 |
|
156 |
|
146 |
@property |
157 |
@property |
147 |
def module_name( self ): |
158 |
def module_name(self): |
148 |
return self.__getitem__( MODULE ) |
159 |
""" |
|
|
160 |
Return the name of the UMC module. |
161 |
""" |
162 |
return self.__getitem__(MODULE) |
149 |
|
163 |
|
150 |
@property |
164 |
@property |
151 |
def xml_definition( self ): |
165 |
def xml_definition(self): |
152 |
return self.get( DEFINITION ) |
166 |
""" |
|
|
167 |
Return the path to the XML UMC definition. |
168 |
""" |
169 |
return self.get(DEFINITION) |
153 |
|
170 |
|
154 |
@property |
171 |
@property |
155 |
def xml_categories( self ): |
172 |
def xml_categories(self): |
|
|
173 |
""" |
174 |
Return the path to the XML file defining categories. |
175 |
""" |
156 |
if CATEGORY in self: |
176 |
if CATEGORY in self: |
157 |
return self.get( CATEGORY, '' ) |
177 |
return self.get(CATEGORY, '') |
158 |
|
178 |
|
159 |
@property |
179 |
@property |
160 |
def python_files( self ): |
180 |
def python_files(self): |
161 |
for filename in os.listdir( self.python_path ): |
181 |
""" |
162 |
if not filename.endswith( '.py' ): |
182 |
Iterate over all Python UMC files. |
163 |
continue |
183 |
""" |
164 |
yield os.path.join( self.python_path, filename ) |
184 |
return UMC_Module._iter_files(self.python_path, '.py') |
165 |
|
185 |
|
166 |
@property |
186 |
@property |
167 |
def python_po_files( self ): |
187 |
def python_po_files(self): |
|
|
188 |
""" |
189 |
Iterate over all Python UMC message catalogs. |
190 |
""" |
168 |
path = '%(Python)s/%(Module)s/' % self |
191 |
path = '%(Python)s/%(Module)s/' % self |
169 |
for lang in LANGUAGES: |
192 |
for lang in LANGUAGES: |
170 |
yield os.path.join( path, '%s.po' % lang ) |
193 |
yield os.path.join( path, '%s.po' % lang ) |
171 |
|
194 |
|
172 |
@property |
195 |
@property |
173 |
def js_po_files( self ): |
196 |
def js_po_files( self ): |
|
|
197 |
""" |
198 |
Iterate over all JavaScript UMC message catalogs. |
199 |
""" |
174 |
for lang in LANGUAGES: |
200 |
for lang in LANGUAGES: |
175 |
yield os.path.join( self.__getitem__( JAVASCRIPT ), '%s.po' % lang ) |
201 |
yield os.path.join( self.__getitem__( JAVASCRIPT ), '%s.po' % lang ) |
176 |
|
202 |
|
177 |
@property |
203 |
@property |
178 |
def xml_po_files( self ): |
204 |
def xml_po_files(self): |
179 |
if self.xml_definition is None: return |
205 |
""" |
180 |
dirpath = os.path.dirname( self.xml_definition ) |
206 |
Iterate over all XML UMC message catalogs. |
|
|
207 |
""" |
208 |
if self.xml_definition is None: |
209 |
return |
210 |
dirpath = os.path.dirname(self.xml_definition) |
181 |
for lang in LANGUAGES: |
211 |
for lang in LANGUAGES: |
182 |
yield ( lang, os.path.join( dirpath, '%s.po' % lang ) ) |
212 |
yield (lang, os.path.join(dirpath, '%s.po' % lang)) |
183 |
|
213 |
|
184 |
@property |
214 |
@property |
185 |
def icons( self ): |
215 |
def icons(self): |
186 |
return self.get( ICONS ) |
216 |
""" |
|
|
217 |
Return path to UMC icon directory. |
218 |
""" |
219 |
return self.get(ICONS) |
220 |
|
187 |
|
221 |
|
188 |
def read_modules( package, core = False ): |
222 |
def read_modules(package, core=False): |
|
|
223 |
""" |
224 |
Read UMC module definition from debian/<package>.umc-modules. |
225 |
""" |
189 |
modules = [] |
226 |
modules = [] |
190 |
|
227 |
|
191 |
file_umc_module = os.path.join( 'debian/', package + '.umc-modules' ) |
228 |
file_umc_module = os.path.join('debian', package + '.umc-modules') |
192 |
|
229 |
|
193 |
if not os.path.isfile( file_umc_module ): |
230 |
if not os.path.isfile(file_umc_module): |
194 |
return modules |
231 |
return modules |
195 |
|
232 |
|
196 |
f_umc_module = open( file_umc_module, 'r' ) |
233 |
f_umc_module = open(file_umc_module, 'r') |
197 |
|
234 |
|
198 |
for item in dh_ucs.parseRfc822( f_umc_module.read() ): |
235 |
for item in dh_ucs.parseRfc822(f_umc_module.read()): |
199 |
# required fields |
236 |
# required fields |
200 |
if not core: |
237 |
if not core: |
201 |
for required in ( MODULE, PYTHON, DEFINITION, JAVASCRIPT ): |
238 |
for required in (MODULE, PYTHON, DEFINITION, JAVASCRIPT): |
202 |
if not required in item or not item[ required ]: |
239 |
if not required in item or not item[required]: |
203 |
raise AttributeError( 'UMC module definition incomplete. key %s missing' % required ) |
240 |
raise AttributeError('UMC module definition incomplete. key %s missing' % (required,)) |
204 |
|
241 |
|
205 |
# single values |
242 |
# single values |
206 |
item[ 'package' ] = package |
243 |
item['package'] = package |
207 |
module = UMC_Module( item ) |
244 |
module = UMC_Module(item) |
208 |
if core: |
245 |
if core: |
209 |
if module.module_name != 'umc-core' or not module.xml_categories: |
246 |
if module.module_name != 'umc-core' or not module.xml_categories: |
210 |
raise ValueError( 'Module definition does not match core module' ) |
247 |
raise ValueError( 'Module definition does not match core module' ) |
211 |
modules.append( module ) |
248 |
modules.append(module) |
|
|
249 |
|
250 |
f_umc_module.close() |
212 |
|
251 |
|
213 |
return modules |
252 |
return modules |
214 |
|
253 |
|
215 |
def _appendPoEntry( poFile, xmlEntry ): |
|
|
216 |
"""Helper function to access text property of XML elements and to find the |
217 |
corresponding po-entry.""" |
218 |
if xmlEntry != None: |
219 |
poFile.append( polib.POEntry( msgid = xmlEntry.text, msgstr = '' ) ) |
220 |
|
254 |
|
221 |
def module_xml2po( module, po_file, language ): |
255 |
def module_xml2po(module, po_file, language): |
222 |
"""Create a PO file the XML definition of an UMC module""" |
256 |
"""Create a PO file the XML definition of an UMC module""" |
223 |
message_po = '%s/messages.po' % ( os.path.dirname( po_file ) or '.' ) |
257 |
message_pot = '%s/messages.pot' % (os.path.dirname(po_file) or '.',) |
224 |
|
258 |
|
225 |
po = polib.POFile() |
259 |
pot = polib.POFile() |
226 |
po.header = PO_HEADER |
260 |
pot.header = PO_HEADER |
227 |
po.metadata = copy.copy( PO_METADATA ) |
261 |
pot.metadata = copy.copy(PO_METADATA) |
228 |
po.metadata[ 'Project-Id-Version' ] = module.package |
262 |
pot.metadata['Project-Id-Version'] = module.package |
229 |
po.metadata[ 'POT-Creation-Date' ] = formatdate( localtime = True ) |
263 |
pot.metadata['POT-Creation-Date'] = formatdate(localtime=True) |
230 |
po.metadata[ 'Language' ] = language |
264 |
pot.metadata['Language'] = language |
231 |
|
265 |
|
232 |
if module.xml_definition and os.path.isfile( module.xml_definition ): |
266 |
def _append_po_entry(xml_entry): |
233 |
tree = ET.ElementTree( file = module.xml_definition ) |
267 |
"""Helper function to access text property of XML elements and to find the |
234 |
_appendPoEntry( po, tree.find( 'module/name' ) ) |
268 |
corresponding po-entry.""" |
235 |
_appendPoEntry( po, tree.find( 'module/description' ) ) |
269 |
if xml_entry is not None: |
236 |
for flavor in tree.findall( 'module/flavor' ): |
270 |
pot.append(polib.POEntry(msgid=xml_entry.text, msgstr='')) |
237 |
_appendPoEntry( po, flavor.find( 'name' ) ) |
271 |
|
238 |
_appendPoEntry( po, flavor.find( 'description' ) ) |
272 |
if module.xml_definition and os.path.isfile(module.xml_definition): |
239 |
|
273 |
tree = ET.ElementTree(file=module.xml_definition) |
240 |
if module.xml_categories and os.path.isfile( module.xml_categories ): |
274 |
_append_po_entry(tree.find('module/name')) |
241 |
tree = ET.ElementTree( file = module.xml_categories ) |
275 |
_append_po_entry(tree.find('module/description')) |
242 |
for cat in tree.findall( 'categories/category' ): |
276 |
for flavor in tree.findall('module/flavor'): |
243 |
_appendPoEntry( po, cat.find( 'name' ) ) |
277 |
_append_po_entry(flavor.find('name')) |
244 |
|
278 |
_append_po_entry(flavor.find('description')) |
245 |
po.save( message_po ) |
279 |
|
246 |
if os.path.isfile( po_file ): |
280 |
if module.xml_categories and os.path.isfile(module.xml_categories): |
247 |
dh_ucs.doIt( 'msgmerge', '--update', '--sort-output', po_file, message_po ) |
281 |
tree = ET.ElementTree(file=module.xml_categories) |
248 |
if os.path.isfile( message_po ): |
282 |
for cat in tree.findall('categories/category'): |
249 |
os.unlink( message_po ) |
283 |
_append_po_entry(cat.find('name')) |
|
|
284 |
|
285 |
pot.save(message_pot) |
286 |
|
287 |
if os.path.isfile(po_file): |
288 |
dh_ucs.doIt('msgmerge', '--update', '--sort-output', po_file, message_pot) |
289 |
if os.path.isfile(message_pot): |
290 |
os.unlink(message_pot) |
250 |
else: |
291 |
else: |
251 |
dh_ucs.doIt( 'mv', message_po, po_file ) |
292 |
dh_ucs.doIt('mv', message_pot, po_file) |
|
|
293 |
|
252 |
|
294 |
|
253 |
def create_po_file( po_file, package, files, language = 'python' ): |
295 |
def create_po_file(po_file, package, files, language='python'): |
254 |
"""Create a PO file for a defined set of files""" |
296 |
"""Create a PO file for a defined set of files""" |
255 |
message_po = '%s/messages.po' % ( os.path.dirname( po_file ) or '.' ) |
297 |
message_pot = '%s/messages.pot' % (os.path.dirname(po_file) or '.',) |
256 |
|
298 |
|
257 |
if os.path.isfile( message_po ): |
299 |
if os.path.isfile(message_pot): |
258 |
os.unlink( message_po ) |
300 |
os.unlink(message_pot) |
259 |
if isinstance( files, basestring ): |
301 |
if isinstance(files, basestring): |
260 |
files = [ files ] |
302 |
files = [files] |
261 |
dh_ucs.doIt( 'xgettext', '--force-po', '--from-code=UTF-8', '--sort-output', '--package-name=%s' % package, '--msgid-bugs-address=packages@univention.de', '--copyright-holder=Univention GmbH', '--language', language, '-o', message_po, *files ) |
303 |
dh_ucs.doIt('xgettext', |
262 |
po = polib.pofile( message_po ) |
304 |
'--force-po', |
263 |
po.header = PO_HEADER |
305 |
'--from-code=UTF-8', |
264 |
po.metadata[ 'Content-Type' ] = 'text/plain; charset=UTF-8' |
306 |
'--sort-output', |
265 |
po.save() |
307 |
'--package-name=%s' % (package,), |
266 |
if os.path.isfile( po_file ): |
308 |
'--msgid-bugs-address=packages@univention.de', |
267 |
dh_ucs.doIt( 'msgmerge', '--update', '--sort-output', po_file, message_po ) |
309 |
'--copyright-holder=Univention GmbH', |
268 |
if os.path.isfile( message_po ): |
310 |
'--language', language, |
269 |
os.unlink( message_po ) |
311 |
'-o', message_pot, |
|
|
312 |
*files) |
313 |
pot = polib.pofile(message_pot) |
314 |
pot.header = PO_HEADER |
315 |
pot.metadata['Content-Type'] = 'text/plain; charset=UTF-8' |
316 |
pot.save() |
317 |
|
318 |
if os.path.isfile(po_file): |
319 |
dh_ucs.doIt('msgmerge', '--update', '--sort-output', po_file, message_pot) |
320 |
if os.path.isfile(message_pot): |
321 |
os.unlink(message_pot) |
270 |
else: |
322 |
else: |
271 |
dh_ucs.doIt( 'mv', message_po, po_file ) |
323 |
dh_ucs.doIt('mv', message_pot, po_file) |
|
|
324 |
|
272 |
|
325 |
|
273 |
def create_mo_file( po_file ): |
326 |
def create_mo_file( po_file ): |
274 |
dh_ucs.doIt( 'msgfmt', '--check', '--output-file', po_file.replace( '.po', '.mo' ), po_file ) |
327 |
""" |
|
|
328 |
Compile textual message catalog to binary message catalog. |
329 |
""" |
330 |
mo_file = po_file.replace('.po', '.mo') |
331 |
dh_ucs.doIt('msgfmt', '--check', '--output-file', mo_file, po_file) |
332 |
|
275 |
|
333 |
|
276 |
def create_json_file( po_file ): |
334 |
def create_json_file( po_file ): |
277 |
json_file = po_file.replace( '.po', '.json' ) |
335 |
""" |
278 |
json_fd = open( json_file, 'w' ) |
336 |
Compile textual message catalog to JSON message catalog. |
279 |
pofile = polib.pofile( po_file ) |
337 |
""" |
|
|
338 |
json_file = po_file.replace('.po', '.json') |
339 |
pofile = polib.pofile(po_file) |
280 |
data = {} |
340 |
data = {} |
281 |
for entry in pofile: |
341 |
for entry in pofile: |
282 |
data[ entry.msgid ] = entry.msgstr |
342 |
data[entry.msgid] = entry.msgstr |
283 |
|
343 |
|
284 |
json_fd.write( json.dumps( data ) ) |
344 |
json_fd = open(json_file, 'w') |
|
|
345 |
json_fd.write(json.dumps(data)) |
285 |
json_fd.close() |
346 |
json_fd.close() |
286 |
|
|
|