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
253 """Return the target language if specified in the header.
254
255 Some attempt at understanding Poedit's custom headers is done."""
256 header = self.parseheader()
257 if 'X-Poedit-Language' in header:
258 from translate.lang import poedit
259 language = header.get('X-Poedit-Language')
260 country = header.get('X-Poedit-Country')
261 return poedit.isocode(language, country)
262 return header.get('Language')
263
265 """Set the target language in the header.
266
267 This removes any custom Poedit headers if they exist.
268
269 @param lang: the new target language code
270 @type lang: str
271 """
272 if isinstance(lang, basestring) and len(lang) > 1:
273 self.updateheader(add=True, Language=lang, X_Poedit_Language=None, X_Poedit_Country=None)
274
276 """Merges another header with this header.
277
278 This header is assumed to be the template.
279
280 @type otherstore: L{base.TranslationStore}
281 """
282
283 newvalues = otherstore.parseheader()
284 retain = {
285 "Project_Id_Version": newvalues['Project-Id-Version'],
286 "PO_Revision_Date" : newvalues['PO-Revision-Date'],
287 "Last_Translator" : newvalues['Last-Translator'],
288 "Language_Team" : newvalues['Language-Team'],
289 }
290
291 plurals = newvalues.get('Plural-Forms', None)
292 if plurals:
293 retain['Plural-Forms'] = plurals
294 self.updateheader(**retain)
295
297 """Add contribution comments if necessary."""
298 header = self.header()
299 if not header:
300 return
301 prelines = []
302 contriblines = []
303 postlines = []
304 contribexists = False
305 incontrib = False
306 outcontrib = False
307 for line in header.getnotes("translator").split('\n'):
308 line = line.strip()
309 if line == u"FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.":
310 incontrib = True
311 continue
312 if author_re.match(line):
313 incontrib = True
314 contriblines.append(line)
315 continue
316 if line == "" and incontrib:
317 incontrib = False
318 outcontrib = True
319 if incontrib:
320 contriblines.append(line)
321 elif not outcontrib:
322 prelines.append(line)
323 else:
324 postlines.append(line)
325
326 year = time.strftime("%Y")
327 contribexists = False
328 for i in range(len(contriblines)):
329 line = contriblines[i]
330 if name in line and (email is None or email in line):
331 contribexists = True
332 if year in line:
333 break
334 else:
335
336 if line[-1] == '.':
337 line = line[:-1]
338 contriblines[i] = "%s, %s." % (line, year)
339
340 if not contribexists:
341
342 if email:
343 contriblines.append("%s <%s>, %s." % (name, email, year))
344 else:
345 contriblines.append("%s, %s." % (name, year))
346
347 header.removenotes()
348 header.addnote("\n".join(prelines))
349 header.addnote("\n".join(contriblines))
350 header.addnote("\n".join(postlines))
351
353 """Create a header for the given filename.
354
355 Check .makeheaderdict() for information on parameters."""
356 headerpo = self.UnitClass(encoding=self._encoding)
357 headerpo.markfuzzy()
358 headerpo.source = ""
359 headeritems = self.makeheaderdict(**kwargs)
360 headervalue = ""
361 for (key, value) in headeritems.items():
362 headervalue += "%s: %s\n" % (key, value)
363 headerpo.target = headervalue
364 return headerpo
365