1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """script to convert a mozilla .dtd UTF-8 localization format to a
23 gettext .po localization file using the po and dtd modules, and the
24 dtd2po convertor class which is in this module
25 You can convert back to .dtd using po2dtd.py"""
26
27 from translate.storage import po
28 from translate.storage import dtd
29 from translate.misc import quote
30 from translate.convert import accesskey as accesskeyfn
31
32
34 """Says if the given entity is likely to contain CSS that should not be
35 translated."""
36 if '.' in entity:
37 prefix, suffix = entity.rsplit('.', 1)
38 if suffix in ["height", "width", "unixWidth", "macWidth", "size"] or suffix.startswith("style"):
39 return True
40 return False
41
42
44
45 - def __init__(self, blankmsgstr=False, duplicatestyle="msgctxt"):
46 self.currentgroup = None
47 self.blankmsgstr = blankmsgstr
48 self.duplicatestyle = duplicatestyle
49
73
75
76 unquoted = dtd.unquotefromdtd(thedtd.definition).replace("\r", "")
77
78
79
80 lines = unquoted.split('\n')
81 while lines and not lines[0].strip():
82 del lines[0]
83 while lines and not lines[-1].strip():
84 del lines[-1]
85
86 if len(lines) > 1:
87 thepo.source = "\n".join([lines[0].rstrip() + ' '] + \
88 [line.strip() + ' ' for line in lines[1:-1]] + \
89 [lines[-1].lstrip()])
90 elif lines:
91 thepo.source = lines[0]
92 else:
93 thepo.source = ""
94 thepo.target = ""
95
97 """converts a dtd unit to a po unit, returns None if empty or not for translation"""
98 if thedtd is None:
99 return None
100 if getattr(thedtd, "entityparameter", None) == "SYSTEM":
101 return None
102 thepo = po.pounit(encoding="UTF-8")
103
104 for commentnum in range(len(thedtd.comments)):
105 commenttype, locnote = thedtd.comments[commentnum]
106
107 if commenttype == 'locnote':
108
109 typeend = quote.findend(locnote, 'LOCALIZATION NOTE')
110
111 idstart = locnote.find('(', typeend)
112 if idstart == -1:
113 continue
114 idend = locnote.find(')', idstart+1)
115 entity = locnote[idstart+1:idend].strip()
116
117 actualnotestart = locnote.find(':', idend+1)
118 actualnoteend = locnote.find('-->', idend)
119 actualnote = locnote[actualnotestart+1:actualnoteend].strip()
120
121 if thedtd.entity == entity:
122
123 if actualnote.startswith("DONT_TRANSLATE"):
124
125 thedtd.entity = ""
126 thedtd.definition = ""
127 del thedtd.comments[commentnum]
128
129 break
130 else:
131
132 thedtd.comments[commentnum] = ("automaticcomment", actualnote)
133
134 self.convertcomments(thedtd, thepo)
135 self.convertstrings(thedtd, thepo)
136 if thepo.isblank() and not thepo.getlocations():
137 return None
138 else:
139 return thepo
140
166
168 """creates self.mixedentities from the dtd file..."""
169 self.mixedentities = {}
170 for entity in thedtdfile.index.keys():
171 for labelsuffix in dtd.labelsuffixes:
172 if entity.endswith(labelsuffix):
173 entitybase = entity[:entity.rfind(labelsuffix)]
174
175
176 for akeytype in dtd.accesskeysuffixes:
177 if (entitybase + akeytype) in thedtdfile.index:
178
179 self.mixedentities[entity] = {}
180 self.mixedentities[entitybase+akeytype] = {}
181
182
184 """converts a dtd unit from thedtdfile to a po unit, handling mixed entities along the way..."""
185
186 if thedtd.entity in self.mixedentities:
187
188
189 alreadymixed = self.mixedentities[thedtd.entity].get(mixbucket, None)
190 if alreadymixed:
191
192 return None
193 elif alreadymixed is None:
194
195 labeldtd, accesskeydtd = None, None
196 labelentity, accesskeyentity = None, None
197 for labelsuffix in dtd.labelsuffixes:
198 if thedtd.entity.endswith(labelsuffix):
199 entitybase = thedtd.entity[:thedtd.entity.rfind(labelsuffix)]
200 for akeytype in dtd.accesskeysuffixes:
201 if (entitybase + akeytype) in thedtdfile.index:
202 labelentity, labeldtd = thedtd.entity, thedtd
203 accesskeyentity = labelentity[:labelentity.rfind(labelsuffix)] + akeytype
204 accesskeydtd = thedtdfile.index[accesskeyentity]
205 break
206 else:
207 for akeytype in dtd.accesskeysuffixes:
208 if thedtd.entity.endswith(akeytype):
209 accesskeyentity, accesskeydtd = thedtd.entity, thedtd
210 for labelsuffix in dtd.labelsuffixes:
211 labelentity = accesskeyentity[:accesskeyentity.rfind(akeytype)] + labelsuffix
212 if labelentity in thedtdfile.index:
213 labeldtd = thedtdfile.index[labelentity]
214 break
215 else:
216 labelentity = None
217 accesskeyentity = None
218 thepo = self.convertmixedunit(labeldtd, accesskeydtd)
219 if thepo is not None:
220 if accesskeyentity is not None:
221 self.mixedentities[accesskeyentity][mixbucket] = True
222 if labelentity is not None:
223 self.mixedentities[labelentity][mixbucket] = True
224 return thepo
225 else:
226
227 if accesskeyentity is not None:
228 self.mixedentities[accesskeyentity][mixbucket] = False
229 if labelentity is not None:
230 self.mixedentities[labelentity][mixbucket] = False
231 return self.convertunit(thedtd)
232
234 thetargetfile = po.pofile()
235 targetheader = thetargetfile.init_headers(charset="UTF-8", encoding="8bit", x_accelerator_marker="&")
236 targetheader.addnote("extracted from %s" % thedtdfile.filename, "developer")
237
238 thedtdfile.makeindex()
239 self.findmixedentities(thedtdfile)
240
241 for thedtd in thedtdfile.units:
242 if thedtd.isnull():
243 continue
244 thepo = self.convertdtdunit(thedtdfile, thedtd)
245 if thepo is not None:
246 thetargetfile.addunit(thepo)
247 thetargetfile.removeduplicates(self.duplicatestyle)
248 return thetargetfile
249
250 - def mergestore(self, origdtdfile, translateddtdfile):
251 thetargetfile = po.pofile()
252 targetheader = thetargetfile.init_headers(charset="UTF-8", encoding="8bit")
253 targetheader.addnote("extracted from %s, %s" % (origdtdfile.filename, translateddtdfile.filename), "developer")
254
255 origdtdfile.makeindex()
256 self.findmixedentities(origdtdfile)
257 translateddtdfile.makeindex()
258 self.findmixedentities(translateddtdfile)
259
260 for origdtd in origdtdfile.units:
261 if origdtd.isnull():
262 continue
263 origpo = self.convertdtdunit(origdtdfile, origdtd, mixbucket="orig")
264 if origdtd.entity in self.mixedentities:
265 mixedentitydict = self.mixedentities[origdtd.entity]
266 if "orig" not in mixedentitydict:
267
268 mixbucket = "orig"
269 del self.mixedentities[origdtd.entity]
270 elif mixedentitydict["orig"]:
271
272 mixbucket = "translate"
273 else:
274
275 mixbucket = "orig"
276 else:
277 mixbucket = "translate"
278 if origpo is None:
279
280 continue
281 if origdtd.entity in translateddtdfile.index:
282 translateddtd = translateddtdfile.index[origdtd.entity]
283 translatedpo = self.convertdtdunit(translateddtdfile, translateddtd, mixbucket=mixbucket)
284 else:
285 translatedpo = None
286 if origpo is not None:
287 if translatedpo is not None and not self.blankmsgstr:
288 origpo.target = translatedpo.source
289 thetargetfile.addunit(origpo)
290 thetargetfile.removeduplicates(self.duplicatestyle)
291 return thetargetfile
292
293
294 -def convertdtd(inputfile, outputfile, templatefile, pot=False, duplicatestyle="msgctxt"):
295 """reads in inputfile and templatefile using dtd, converts using dtd2po, writes to outputfile"""
296 inputstore = dtd.dtdfile(inputfile)
297 convertor = dtd2po(blankmsgstr=pot, duplicatestyle=duplicatestyle)
298 if templatefile is None:
299 outputstore = convertor.convertstore(inputstore)
300 else:
301 templatestore = dtd.dtdfile(templatefile)
302 outputstore = convertor.mergestore(templatestore, inputstore)
303 if outputstore.isempty():
304 return 0
305 outputfile.write(str(outputstore))
306 return 1
307
308
309 -def main(argv=None):
310 from translate.convert import convert
311 formats = {"dtd": ("po", convertdtd), ("dtd", "dtd"): ("po", convertdtd)}
312 parser = convert.ConvertOptionParser(formats, usetemplates=True, usepots=True, description=__doc__)
313 parser.add_duplicates_option()
314 parser.passthrough.append("pot")
315 parser.run(argv)
316
317
318 if __name__ == '__main__':
319 main()
320