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
30 default_header = {
31 "Project-Id-Version": "PACKAGE VERSION",
32 "PO-Revision-Date": "YEAR-MO-DA HO:MI+ZONE",
33 "Last-Translator": "FULL NAME <EMAIL@ADDRESS>",
34 "Language-Team": "LANGUAGE <LL@li.org>",
35 "Plural-Forms": "nplurals=INTEGER; plural=EXPRESSION;",
36 }
37
39 """Parses an input string with the definition of a PO header and returns
40 the interpreted values as a dictionary."""
41 headervalues = dictutils.ordereddict()
42 for line in input.split("\n"):
43 if not line or ":" not in line:
44 continue
45 key, value = line.split(":", 1)
46
47 key = str(key.strip())
48 headervalues[key] = value.strip()
49 return headervalues
50
52 """Returns the timezone as a string in the format [+-]0000, eg +0200.
53
54 @rtype: str"""
55 if time.daylight:
56 tzoffset = time.altzone
57 else:
58 tzoffset = time.timezone
59
60 hours, minutes = time.gmtime(abs(tzoffset))[3:5]
61 if tzoffset > 0:
62 hours *= -1
63 tz = str("%+d" % hours).zfill(3) + str(minutes).zfill(2)
64 return tz
65
66 -def update(existing, add=False, **kwargs):
97
98
100 """This class implements functionality for manipulation of po file headers.
101 This class is a mix-in class and useless on its own. It must be used from all
102 classes which represent a po file"""
103
104 x_generator = "Translate Toolkit %s" % __version__.sver
105
106 header_order = [
107 "Project-Id-Version",
108 "Report-Msgid-Bugs-To",
109 "POT-Creation-Date",
110 "PO-Revision-Date",
111 "Last-Translator",
112 "Language-Team",
113 "Language",
114 "MIME-Version",
115 "Content-Type",
116 "Content-Transfer-Encoding",
117 "Plural-Forms",
118 "X-Generator",
119 ]
121 """sets default values for po headers"""
122
123 headerdict = self.makeheaderdict(charset=charset, encoding=encoding, **kwargs)
124 self.updateheader(add=True, **headerdict)
125 return self.header()
126
139 """Create a header dictionary with useful defaults.
140
141 pot_creation_date can be None (current date) or a value (datetime or string)
142 po_revision_date can be None (form), False (=pot_creation_date), True (=now),
143 or a value (datetime or string)
144
145 @return: Dictionary with the header items
146 @rtype: dict
147 """
148 if project_id_version is None:
149 project_id_version = "PACKAGE VERSION"
150 if pot_creation_date is None or pot_creation_date == True:
151 pot_creation_date = time.strftime("%Y-%m-%d %H:%M") + tzstring()
152 if isinstance(pot_creation_date, time.struct_time):
153 pot_creation_date = time.strftime("%Y-%m-%d %H:%M", pot_creation_date) + tzstring()
154 if po_revision_date is None:
155 po_revision_date = "YEAR-MO-DA HO:MI+ZONE"
156 elif po_revision_date == False:
157 po_revision_date = pot_creation_date
158 elif po_revision_date == True:
159 po_revision_date = time.strftime("%Y-%m-%d %H:%M") + tzstring()
160 if isinstance(po_revision_date, time.struct_time):
161 po_revision_date = time.strftime("%Y-%m-%d %H:%M", po_revision_date) + tzstring()
162 if last_translator is None:
163 last_translator = "FULL NAME <EMAIL@ADDRESS>"
164 if language_team is None:
165 language_team = "LANGUAGE <LL@li.org>"
166 if mime_version is None:
167 mime_version = "1.0"
168 if report_msgid_bugs_to is None:
169 report_msgid_bugs_to = ""
170
171 defaultargs = dictutils.ordereddict()
172 defaultargs["Project-Id-Version"] = project_id_version
173 defaultargs["Report-Msgid-Bugs-To"] = report_msgid_bugs_to
174 defaultargs["POT-Creation-Date"] = pot_creation_date
175 defaultargs["PO-Revision-Date"] = po_revision_date
176 defaultargs["Last-Translator"] = last_translator
177 defaultargs["Language-Team"] = language_team
178 defaultargs["MIME-Version"] = mime_version
179 defaultargs["Content-Type"] = "text/plain; charset=%s" % charset
180 defaultargs["Content-Transfer-Encoding"] = encoding
181 if plural_forms:
182 defaultargs["Plural-Forms"] = plural_forms
183 defaultargs["X-Generator"] = self.x_generator
184
185 return update(defaultargs, add=True, **kwargs)
186
188 """Returns the header element, or None. Only the first element is allowed
189 to be a header. Note that this could still return an empty header element,
190 if present."""
191 if len(self.units) == 0:
192 return None
193 candidate = self.units[0]
194 if candidate.isheader():
195 return candidate
196 else:
197 return None
198
206
208 """Updates the fields in the PO style header.
209
210 This will create a header if add == True."""
211 header = self.header()
212 if not header:
213 if add:
214 header = self.makeheader(**kwargs)
215 self._insert_header(header)
216 else:
217 headeritems = update(self.parseheader(), add, **kwargs)
218 keys = headeritems.keys()
219 if not "Content-Type" in keys or "charset=CHARSET" in headeritems["Content-Type"]:
220 headeritems["Content-Type"] = "text/plain; charset=UTF-8"
221 if not "Content-Transfer-Encoding" in keys or "ENCODING" in headeritems["Content-Transfer-Encoding"]:
222 headeritems["Content-Transfer-Encoding"] = "8bit"
223 headerString = ""
224 for key, value in headeritems.items():
225 if value is not None:
226 headerString += "%s: %s\n" % (key, value)
227 header.target = headerString
228 header.markfuzzy(False)
229 return header
230
238
240 """Returns the nplural and plural values from the header."""
241 header = self.parseheader()
242 pluralformvalue = header.get('Plural-Forms', None)
243 if pluralformvalue is None:
244 return None, None
245 nplural = re.findall("nplurals=(.+?);", pluralformvalue)
246 plural = re.findall("plural=(.+?);?$", pluralformvalue)
247 if not nplural or nplural[0] == "INTEGER":
248 nplural = None
249 else:
250 nplural = nplural[0]
251 if not plural or plural[0] == "EXPRESSION":
252 plural = None
253 else:
254 plural = plural[0]
255 return nplural, plural
256
262
284
286 """Set the target language in the header.
287
288 This removes any custom Poedit headers if they exist.
289
290 @param lang: the new target language code
291 @type lang: str
292 """
293 if isinstance(lang, basestring) and len(lang) > 1:
294 self.updateheader(add=True, Language=lang, X_Poedit_Language=None, X_Poedit_Country=None)
295
297 """Merges another header with this header.
298
299 This header is assumed to be the template.
300
301 @type otherstore: L{base.TranslationStore}
302 """
303
304 newvalues = otherstore.parseheader()
305 retain_list = ("Project-Id-Version", "PO-Revision-Date", "Last-Translator",
306 "Language-Team", "Plural-Forms")
307 retain = dict((key, newvalues[key]) for key in retain_list if newvalues.get(key, None) and newvalues[key] != default_header.get(key, None))
308 self.updateheader(**retain)
309
311 """Add contribution comments if necessary."""
312 header = self.header()
313 if not header:
314 return
315 prelines = []
316 contriblines = []
317 postlines = []
318 contribexists = False
319 incontrib = False
320 outcontrib = False
321 for line in header.getnotes("translator").split('\n'):
322 line = line.strip()
323 if line == u"FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.":
324 incontrib = True
325 continue
326 if author_re.match(line):
327 incontrib = True
328 contriblines.append(line)
329 continue
330 if line == "" and incontrib:
331 incontrib = False
332 outcontrib = True
333 if incontrib:
334 contriblines.append(line)
335 elif not outcontrib:
336 prelines.append(line)
337 else:
338 postlines.append(line)
339
340 year = time.strftime("%Y")
341 contribexists = False
342 for i in range(len(contriblines)):
343 line = contriblines[i]
344 if name in line and (email is None or email in line):
345 contribexists = True
346 if year in line:
347 break
348 else:
349
350 if line[-1] == '.':
351 line = line[:-1]
352 contriblines[i] = "%s, %s." % (line, year)
353
354 if not contribexists:
355
356 if email:
357 contriblines.append("%s <%s>, %s." % (name, email, year))
358 else:
359 contriblines.append("%s, %s." % (name, year))
360
361 header.removenotes()
362 header.addnote("\n".join(prelines))
363 header.addnote("\n".join(contriblines))
364 header.addnote("\n".join(postlines))
365
367 """Create a header for the given filename.
368
369 Check .makeheaderdict() for information on parameters."""
370 headerpo = self.UnitClass("", encoding=self._encoding)
371 headerpo.markfuzzy()
372 headeritems = self.makeheaderdict(**kwargs)
373 headervalue = ""
374 for (key, value) in headeritems.items():
375 headervalue += "%s: %s\n" % (key, value)
376 headerpo.target = headervalue
377 return headerpo
378