1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 """class that handles all header functions for a header in a po file"""
22
23 from translate.misc import dictutils
24 from translate import __version__
25 import re
26 import time
27
28 author_re = re.compile(r".*<\S+@\S+>.*\d{4,4}")
29
31 """Parses an input string with the definition of a PO header and returns
32 the interpreted values as a dictionary."""
33 headervalues = dictutils.ordereddict()
34 for line in input.split("\n"):
35 if not line or ":" not in line:
36 continue
37 key, value = line.split(":", 1)
38
39 key = str(key.strip())
40 headervalues[key] = value.strip()
41 return headervalues
42
44 """Returns the timezone as a string in the format [+-]0000, eg +0200.
45
46 @rtype: str"""
47 if time.daylight:
48 tzoffset = time.altzone
49 else:
50 tzoffset = time.timezone
51
52 hours, minutes = time.gmtime(abs(tzoffset))[3:5]
53 if tzoffset > 0:
54 hours *= -1
55 tz = str("%+d" % hours).zfill(3) + str(minutes).zfill(2)
56 return tz
57
58 -def update(existing, add=False, **kwargs):
89
90
92 """This class implements functionality for manipulation of po file headers.
93 This class is a mix-in class and useless on its own. It must be used from all
94 classes which represent a po file"""
95
96 x_generator = "Translate Toolkit %s" % __version__.sver
97
98 header_order = [
99 "Project-Id-Version",
100 "Report-Msgid-Bugs-To",
101 "POT-Creation-Date",
102 "PO-Revision-Date",
103 "Last-Translator",
104 "Language-Team",
105 "Language",
106 "MIME-Version",
107 "Content-Type",
108 "Content-Transfer-Encoding",
109 "Plural-Forms",
110 "X-Generator",
111 ]
113 """sets default values for po headers"""
114
115 headerdict = self.makeheaderdict(charset=charset, encoding=encoding, **kwargs)
116 self.updateheader(add=True, **headerdict)
117 return self.header()
118
131 """Create a header dictionary with useful defaults.
132
133 pot_creation_date can be None (current date) or a value (datetime or string)
134 po_revision_date can be None (form), False (=pot_creation_date), True (=now),
135 or a value (datetime or string)
136
137 @return: Dictionary with the header items
138 @rtype: dict
139 """
140 if project_id_version is None:
141 project_id_version = "PACKAGE VERSION"
142 if pot_creation_date is None or pot_creation_date == True:
143 pot_creation_date = time.strftime("%Y-%m-%d %H:%M") + tzstring()
144 if isinstance(pot_creation_date, time.struct_time):
145 pot_creation_date = time.strftime("%Y-%m-%d %H:%M", pot_creation_date) + tzstring()
146 if po_revision_date is None:
147 po_revision_date = "YEAR-MO-DA HO:MI+ZONE"
148 elif po_revision_date == False:
149 po_revision_date = pot_creation_date
150 elif po_revision_date == True:
151 po_revision_date = time.strftime("%Y-%m-%d %H:%M") + tzstring()
152 if isinstance(po_revision_date, time.struct_time):
153 po_revision_date = time.strftime("%Y-%m-%d %H:%M", po_revision_date) + tzstring()
154 if last_translator is None:
155 last_translator = "FULL NAME <EMAIL@ADDRESS>"
156 if language_team is None:
157 language_team = "LANGUAGE <LL@li.org>"
158 if mime_version is None:
159 mime_version = "1.0"
160 if report_msgid_bugs_to is None:
161 report_msgid_bugs_to = ""
162
163 defaultargs = dictutils.ordereddict()
164 defaultargs["Project-Id-Version"] = project_id_version
165 defaultargs["Report-Msgid-Bugs-To"] = report_msgid_bugs_to
166 defaultargs["POT-Creation-Date"] = pot_creation_date
167 defaultargs["PO-Revision-Date"] = po_revision_date
168 defaultargs["Last-Translator"] = last_translator
169 defaultargs["Language-Team"] = language_team
170 defaultargs["MIME-Version"] = mime_version
171 defaultargs["Content-Type"] = "text/plain; charset=%s" % charset
172 defaultargs["Content-Transfer-Encoding"] = encoding
173 if plural_forms:
174 defaultargs["Plural-Forms"] = plural_forms
175 defaultargs["X-Generator"] = self.x_generator
176
177 return update(defaultargs, add=True, **kwargs)
178
180 """Returns the header element, or None. Only the first element is allowed
181 to be a header. Note that this could still return an empty header element,
182 if present."""
183 if len(self.units) == 0:
184 return None
185 candidate = self.units[0]
186 if candidate.isheader():
187 return candidate
188 else:
189 return None
190
198
200 """Updates the fields in the PO style header.
201
202 This will create a header if add == True."""
203 header = self.header()
204 if not header:
205 if add:
206 header = self.makeheader(**kwargs)
207
208
209
210
211 header._store = self
212 self.units.insert(0, header)
213 else:
214 headeritems = update(self.parseheader(), add, **kwargs)
215 keys = headeritems.keys()
216 if not "Content-Type" in keys or "charset=CHARSET" in headeritems["Content-Type"]:
217 headeritems["Content-Type"] = "text/plain; charset=UTF-8"
218 if not "Content-Transfer-Encoding" in keys or "ENCODING" in headeritems["Content-Transfer-Encoding"]:
219 headeritems["Content-Transfer-Encoding"] = "8bit"
220 headerString = ""
221 for key, value in headeritems.items():
222 if value is not None:
223 headerString += "%s: %s\n" % (key, value)
224 header.target = headerString
225 header.markfuzzy(False)
226 return header
227
229 """Returns the nplural and plural values from the header."""
230 header = self.parseheader()
231 pluralformvalue = header.get('Plural-Forms', None)
232 if pluralformvalue is None:
233 return None, None
234 nplural = re.findall("nplurals=(.+?);", pluralformvalue)
235 plural = re.findall("plural=(.+?);?$", pluralformvalue)
236 if not nplural or nplural[0] == "INTEGER":
237 nplural = None
238 else:
239 nplural = nplural[0]
240 if not plural or plural[0] == "EXPRESSION":
241 plural = None
242 else:
243 plural = plural[0]
244 return nplural, plural
245
251
273
275 """Set the target language in the header.
276
277 This removes any custom Poedit headers if they exist.
278
279 @param lang: the new target language code
280 @type lang: str
281 """
282 if isinstance(lang, basestring) and len(lang) > 1:
283 self.updateheader(add=True, Language=lang, X_Poedit_Language=None, X_Poedit_Country=None)
284
286 """Merges another header with this header.
287
288 This header is assumed to be the template.
289
290 @type otherstore: L{base.TranslationStore}
291 """
292
293 newvalues = otherstore.parseheader()
294 retain = {
295 "Project_Id_Version": newvalues['Project-Id-Version'],
296 "PO_Revision_Date" : newvalues['PO-Revision-Date'],
297 "Last_Translator" : newvalues['Last-Translator'],
298 "Language_Team" : newvalues['Language-Team'],
299 }
300
301 plurals = newvalues.get('Plural-Forms', None)
302 if plurals:
303 retain['Plural-Forms'] = plurals
304 self.updateheader(**retain)
305
307 """Add contribution comments if necessary."""
308 header = self.header()
309 if not header:
310 return
311 prelines = []
312 contriblines = []
313 postlines = []
314 contribexists = False
315 incontrib = False
316 outcontrib = False
317 for line in header.getnotes("translator").split('\n'):
318 line = line.strip()
319 if line == u"FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.":
320 incontrib = True
321 continue
322 if author_re.match(line):
323 incontrib = True
324 contriblines.append(line)
325 continue
326 if line == "" and incontrib:
327 incontrib = False
328 outcontrib = True
329 if incontrib:
330 contriblines.append(line)
331 elif not outcontrib:
332 prelines.append(line)
333 else:
334 postlines.append(line)
335
336 year = time.strftime("%Y")
337 contribexists = False
338 for i in range(len(contriblines)):
339 line = contriblines[i]
340 if name in line and (email is None or email in line):
341 contribexists = True
342 if year in line:
343 break
344 else:
345
346 if line[-1] == '.':
347 line = line[:-1]
348 contriblines[i] = "%s, %s." % (line, year)
349
350 if not contribexists:
351
352 if email:
353 contriblines.append("%s <%s>, %s." % (name, email, year))
354 else:
355 contriblines.append("%s, %s." % (name, year))
356
357 header.removenotes()
358 header.addnote("\n".join(prelines))
359 header.addnote("\n".join(contriblines))
360 header.addnote("\n".join(postlines))
361
363 """Create a header for the given filename.
364
365 Check .makeheaderdict() for information on parameters."""
366 headerpo = self.UnitClass(encoding=self._encoding)
367 headerpo.markfuzzy()
368 headerpo.source = ""
369 headeritems = self.makeheaderdict(**kwargs)
370 headervalue = ""
371 for (key, value) in headeritems.items():
372 headervalue += "%s: %s\n" % (key, value)
373 headerpo.target = headervalue
374 return headerpo
375