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 ]
112
125 """Create a header dictionary with useful defaults.
126
127 pot_creation_date can be None (current date) or a value (datetime or string)
128 po_revision_date can be None (form), False (=pot_creation_date), True (=now),
129 or a value (datetime or string)
130
131 @return: Dictionary with the header items
132 @rtype: dict
133 """
134 if project_id_version is None:
135 project_id_version = "PACKAGE VERSION"
136 if pot_creation_date is None or pot_creation_date == True:
137 pot_creation_date = time.strftime("%Y-%m-%d %H:%M") + tzstring()
138 if isinstance(pot_creation_date, time.struct_time):
139 pot_creation_date = time.strftime("%Y-%m-%d %H:%M", pot_creation_date) + tzstring()
140 if po_revision_date is None:
141 po_revision_date = "YEAR-MO-DA HO:MI+ZONE"
142 elif po_revision_date == False:
143 po_revision_date = pot_creation_date
144 elif po_revision_date == True:
145 po_revision_date = time.strftime("%Y-%m-%d %H:%M") + tzstring()
146 if isinstance(po_revision_date, time.struct_time):
147 po_revision_date = time.strftime("%Y-%m-%d %H:%M", po_revision_date) + tzstring()
148 if last_translator is None:
149 last_translator = "FULL NAME <EMAIL@ADDRESS>"
150 if language_team is None:
151 language_team = "LANGUAGE <LL@li.org>"
152 if mime_version is None:
153 mime_version = "1.0"
154 if report_msgid_bugs_to is None:
155 report_msgid_bugs_to = ""
156
157 defaultargs = dictutils.ordereddict()
158 defaultargs["Project-Id-Version"] = project_id_version
159 defaultargs["Report-Msgid-Bugs-To"] = report_msgid_bugs_to
160 defaultargs["POT-Creation-Date"] = pot_creation_date
161 defaultargs["PO-Revision-Date"] = po_revision_date
162 defaultargs["Last-Translator"] = last_translator
163 defaultargs["Language-Team"] = language_team
164 defaultargs["MIME-Version"] = mime_version
165 defaultargs["Content-Type"] = "text/plain; charset=%s" % charset
166 defaultargs["Content-Transfer-Encoding"] = encoding
167 if plural_forms:
168 defaultargs["Plural-Forms"] = plural_forms
169 defaultargs["X-Generator"] = self.x_generator
170
171 return update(defaultargs, add=True, **kwargs)
172
174 """Returns the header element, or None. Only the first element is allowed
175 to be a header. Note that this could still return an empty header element,
176 if present."""
177 if len(self.units) == 0:
178 return None
179 candidate = self.units[0]
180 if candidate.isheader():
181 return candidate
182 else:
183 return None
184
192
194 """Updates the fields in the PO style header.
195
196 This will create a header if add == True."""
197 header = self.header()
198 if not header:
199 if add:
200 header = self.makeheader(**kwargs)
201
202
203
204
205 header._store = self
206 self.units.insert(0, header)
207 else:
208 headeritems = update(self.parseheader(), add, **kwargs)
209 keys = headeritems.keys()
210 if not "Content-Type" in keys or "charset=CHARSET" in headeritems["Content-Type"]:
211 headeritems["Content-Type"] = "text/plain; charset=UTF-8"
212 if not "Content-Transfer-Encoding" in keys or "ENCODING" in headeritems["Content-Transfer-Encoding"]:
213 headeritems["Content-Transfer-Encoding"] = "8bit"
214 headerString = ""
215 for key, value in headeritems.items():
216 if value is not None:
217 headerString += "%s: %s\n" % (key, value)
218 header.target = headerString
219 header.markfuzzy(False)
220 return header
221
223 """Returns the nplural and plural values from the header."""
224 header = self.parseheader()
225 pluralformvalue = header.get('Plural-Forms', None)
226 if pluralformvalue is None:
227 return None, None
228 nplural = re.findall("nplurals=(.+?);", pluralformvalue)
229 plural = re.findall("plural=(.+?);?$", pluralformvalue)
230 if not nplural or nplural[0] == "INTEGER":
231 nplural = None
232 else:
233 nplural = nplural[0]
234 if not plural or plural[0] == "EXPRESSION":
235 plural = None
236 else:
237 plural = plural[0]
238 return nplural, plural
239
245
247 """Return the target language if specified in the header.
248
249 Some attempt at understanding Poedit's custom headers is done."""
250 header = self.parseheader()
251 if 'X-Poedit-Language' in header:
252 from translate.lang import poedit
253 language = header.get('X-Poedit-Language')
254 country = header.get('X-Poedit-Country')
255 return poedit.isocode(language, country)
256 return header.get('Language')
257
259 """Set the target language in the header.
260
261 This removes any custom Poedit headers if they exist.
262
263 @param lang: the new target language code
264 @type lang: str
265 """
266 if isinstance(lang, basestring) and len(lang) > 1:
267 self.updateheader(add=True, Language=lang, X_Poedit_Language=None, X_Poedit_Country=None)
268
270 """Merges another header with this header.
271
272 This header is assumed to be the template.
273
274 @type otherstore: L{base.TranslationStore}
275 """
276
277 newvalues = otherstore.parseheader()
278 retain = {
279 "Project_Id_Version": newvalues['Project-Id-Version'],
280 "PO_Revision_Date" : newvalues['PO-Revision-Date'],
281 "Last_Translator" : newvalues['Last-Translator'],
282 "Language_Team" : newvalues['Language-Team'],
283 }
284
285 plurals = newvalues.get('Plural-Forms', None)
286 if plurals:
287 retain['Plural-Forms'] = plurals
288 self.updateheader(**retain)
289
291 """Add contribution comments if necessary."""
292 header = self.header()
293 if not header:
294 return
295 prelines = []
296 contriblines = []
297 postlines = []
298 contribexists = False
299 incontrib = False
300 outcontrib = False
301 for line in header.getnotes("translator").split('\n'):
302 line = line.strip()
303 if line == u"FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.":
304 incontrib = True
305 continue
306 if author_re.match(line):
307 incontrib = True
308 contriblines.append(line)
309 continue
310 if line == "" and incontrib:
311 incontrib = False
312 outcontrib = True
313 if incontrib:
314 contriblines.append(line)
315 elif not outcontrib:
316 prelines.append(line)
317 else:
318 postlines.append(line)
319
320 year = time.strftime("%Y")
321 contribexists = False
322 for i in range(len(contriblines)):
323 line = contriblines[i]
324 if name in line and (email is None or email in line):
325 contribexists = True
326 if year in line:
327 break
328 else:
329
330 if line[-1] == '.':
331 line = line[:-1]
332 contriblines[i] = "%s, %s." % (line, year)
333
334 if not contribexists:
335
336 if email:
337 contriblines.append("%s <%s>, %s." % (name, email, year))
338 else:
339 contriblines.append("%s, %s." % (name, year))
340
341 header.removenotes()
342 header.addnote("\n".join(prelines))
343 header.addnote("\n".join(contriblines))
344 header.addnote("\n".join(postlines))
345
347 """Create a header for the given filename.
348
349 Check .makeheaderdict() for information on parameters."""
350 headerpo = self.UnitClass(encoding=self._encoding)
351 headerpo.markfuzzy()
352 headerpo.source = ""
353 headeritems = self.makeheaderdict(**kwargs)
354 headervalue = ""
355 for (key, value) in headeritems.items():
356 headervalue += "%s: %s\n" % (key, value)
357 headerpo.target = headervalue
358 return headerpo
359