1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """class that handles all header functions for a header in a po file"""
23
24 from translate.misc import dictutils
25 from translate import __version__
26 import re
27 import time
28
29 author_re = re.compile(r".*<\S+@\S+>.*\d{4,4}")
30
32 """Parses an input string with the definition of a PO header and returns
33 the interpreted values as a dictionary"""
34 headervalues = dictutils.ordereddict()
35 for line in input.split("\n"):
36 if not line or ":" not in line:
37 continue
38 key, value = line.split(":", 1)
39
40 key = str(key.strip())
41 headervalues[key] = value.strip()
42 return headervalues
43
45 """Returns the timezone as a string in the format [+-]0000, eg +0200."""
46 if time.daylight:
47 tzoffset = time.altzone
48 else:
49 tzoffset = time.timezone
50
51 hours, minutes = time.gmtime(abs(tzoffset))[3:5]
52 if tzoffset > 0:
53 hours *= -1
54 tz = str("%+d" % hours).zfill(3) + str(minutes).zfill(2)
55 return tz
56
57 -def update(existing, add=False, **kwargs):
88
89
91 """This class implements functionality for manipulation of po file headers.
92 This class is a mix-in class and useless on its own. It must be used from all
93 classes which represent a po file"""
94
95 x_generator = "Translate Toolkit %s" % __version__.ver
96
97 header_order = [
98 "Project-Id-Version",
99 "Report-Msgid-Bugs-To",
100 "POT-Creation-Date",
101 "PO-Revision-Date",
102 "Last-Translator",
103 "Language-Team",
104 "MIME-Version",
105 "Content-Type",
106 "Content-Transfer-Encoding",
107 "Plural-Forms",
108 "X-Generator",
109 ]
110
111
113 """create a header for the given filename. arguments are specially handled, kwargs added as key: value
114 pot_creation_date can be None (current date) or a value (datetime or string)
115 po_revision_date can be None (form), False (=pot_creation_date), True (=now), or a value (datetime or string)
116
117 @return: Dictionary with the header items
118 @rtype: dict
119 """
120 if project_id_version is None:
121 project_id_version = "PACKAGE VERSION"
122 if pot_creation_date is None or pot_creation_date == True:
123 pot_creation_date = time.strftime("%Y-%m-%d %H:%M") + tzstring()
124 if isinstance(pot_creation_date, time.struct_time):
125 pot_creation_date = time.strftime("%Y-%m-%d %H:%M", pot_creation_date) + tzstring()
126 if po_revision_date is None:
127 po_revision_date = "YEAR-MO-DA HO:MI+ZONE"
128 elif po_revision_date == False:
129 po_revision_date = pot_creation_date
130 elif po_revision_date == True:
131 po_revision_date = time.strftime("%Y-%m-%d %H:%M") + tzstring()
132 if isinstance(po_revision_date, time.struct_time):
133 po_revision_date = time.strftime("%Y-%m-%d %H:%M", po_revision_date) + tzstring()
134 if last_translator is None:
135 last_translator = "FULL NAME <EMAIL@ADDRESS>"
136 if language_team is None:
137 language_team = "LANGUAGE <LL@li.org>"
138 if mime_version is None:
139 mime_version = "1.0"
140 if report_msgid_bugs_to is None:
141 report_msgid_bugs_to = ""
142
143 defaultargs = dictutils.ordereddict()
144 defaultargs["Project-Id-Version"] = project_id_version
145 defaultargs["Report-Msgid-Bugs-To"] = report_msgid_bugs_to
146 defaultargs["POT-Creation-Date"] = pot_creation_date
147 defaultargs["PO-Revision-Date"] = po_revision_date
148 defaultargs["Last-Translator"] = last_translator
149 defaultargs["Language-Team"] = language_team
150 defaultargs["MIME-Version"] = mime_version
151 defaultargs["Content-Type"] = "text/plain; charset=%s" % charset
152 defaultargs["Content-Transfer-Encoding"] = encoding
153 if plural_forms:
154 defaultargs["Plural-Forms"] = plural_forms
155 defaultargs["X-Generator"] = self.x_generator
156
157 return update(defaultargs, add=True, **kwargs)
158
160 """Returns the header element, or None. Only the first element is allowed
161 to be a header. Note that this could still return an empty header element,
162 if present."""
163 if len(self.units) == 0:
164 return None
165 candidate = self.units[0]
166 if candidate.isheader():
167 return candidate
168 else:
169 return None
170
178
180 """Updates the fields in the PO style header.
181 This will create a header if add == True"""
182 header = self.header()
183 if not header:
184
185 if add and callable(getattr(self, "makeheader", None)):
186 header = self.makeheader(**kwargs)
187 self.units.insert(0, header)
188 else:
189 headeritems = update(self.parseheader(), add, **kwargs)
190 keys = headeritems.keys()
191 if not "Content-Type" in keys or "charset=CHARSET" in headeritems["Content-Type"]:
192 headeritems["Content-Type"] = "text/plain; charset=UTF-8"
193 if not "Content-Transfer-Encoding" in keys or "ENCODING" in headeritems["Content-Transfer-Encoding"]:
194 headeritems["Content-Transfer-Encoding"] = "8bit"
195 headerString = ""
196 for key, value in headeritems.items():
197 headerString += "%s: %s\n" % (key, value)
198 header.target = headerString
199 header.markfuzzy(False)
200 return header
201
203 """returns the nplural and plural values from the header"""
204 header = self.parseheader()
205 pluralformvalue = header.get('Plural-Forms', None)
206 if pluralformvalue is None:
207 return None, None
208 nplural = re.findall("nplurals=(.+?);", pluralformvalue)
209 plural = re.findall("plural=(.+?);?$", pluralformvalue)
210 if not nplural or nplural[0] == "INTEGER":
211 nplural = None
212 else:
213 nplural = nplural[0]
214 if not plural or plural[0] == "EXPRESSION":
215 plural = None
216 else:
217 plural = plural[0]
218 return nplural, plural
219
225
227 """Merges another header with this header.
228
229 This header is assumed to be the template.
230
231 @type otherstore: L{base.TranslationStore}
232
233 """
234
235 newvalues = otherstore.parseheader()
236 retain = {
237 "Project_Id_Version": newvalues['Project-Id-Version'],
238 "PO_Revision_Date" : newvalues['PO-Revision-Date'],
239 "Last_Translator" : newvalues['Last-Translator'],
240 "Language_Team" : newvalues['Language-Team'],
241 }
242
243 plurals = newvalues.get('Plural-Forms', None)
244 if plurals:
245 retain['Plural-Forms'] = plurals
246 self.updateheader(**retain)
247
249 """Add contribution comments
250 """
251 header = self.header()
252 if not header:
253 return
254 prelines = []
255 contriblines = []
256 postlines = []
257 contribexists = False
258 incontrib = False
259 outcontrib = False
260 for line in header.getnotes("translator").split('\n'):
261 line = line.strip()
262 if line == u"FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.":
263 incontrib = True
264 continue
265 if author_re.match(line):
266 incontrib = True
267 contriblines.append(line)
268 continue
269 if line == "" and incontrib:
270 incontrib = False
271 outcontrib = True
272 if incontrib:
273 contriblines.append(line)
274 elif not outcontrib:
275 prelines.append(line)
276 else:
277 postlines.append(line)
278
279 year = time.strftime("%Y")
280 contribexists = False
281 for line in contriblines:
282 if name in line and (email is None or email in line):
283 contribexists = True
284 break
285 if not contribexists:
286
287 if email:
288 contriblines.append("%s <%s>, %s" % (name, email, year))
289 else:
290 contriblines.append("%s, %s" % (name, year))
291
292 header.removenotes()
293 header.addnote("\n".join(prelines))
294 header.addnote("\n".join(contriblines))
295 header.addnote("\n".join(postlines))
296