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
44 -def update(existing, add=False, **kwargs):
75
76
78 """This class implements functionality for manipulation of po file headers.
79 This class is a mix-in class and useless on its own. It must be used from all
80 classes which represent a po file"""
81
82 x_generator = "Translate Toolkit %s" % __version__.ver
83
84 header_order = [
85 "Project-Id-Version",
86 "Report-Msgid-Bugs-To",
87 "POT-Creation-Date",
88 "PO-Revision-Date",
89 "Last-Translator",
90 "Language-Team",
91 "MIME-Version",
92 "Content-Type",
93 "Content-Transfer-Encoding",
94 "Plural-Forms",
95 "X-Generator",
96 ]
97
99 """Returns the timezone as a string in the format [+-]0000, eg +0200."""
100 if time.daylight:
101 tzoffset = time.altzone
102 else:
103 tzoffset = time.timezone
104
105 hours, minutes = time.gmtime(abs(tzoffset))[3:5]
106 if tzoffset > 0:
107 hours *= -1
108 tz = str("%+d" % hours).zfill(3) + str(minutes).zfill(2)
109 return tz
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") + self.tzstring()
124 if isinstance(pot_creation_date, time.struct_time):
125 pot_creation_date = time.strftime("%Y-%m-%d %H:%M", pot_creation_date) + self.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") + self.tzstring()
132 if isinstance(po_revision_date, time.struct_time):
133 po_revision_date = time.strftime("%Y-%m-%d %H:%M", po_revision_date) + self.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
196
198 """returns the nplural and plural values from the header"""
199 header = self.parseheader()
200 pluralformvalue = header.get('Plural-Forms', None)
201 if pluralformvalue is None:
202 return None, None
203 nplural = re.findall("nplurals=(.+?);", pluralformvalue)
204 plural = re.findall("plural=(.+?);?$", pluralformvalue)
205 if not nplural or nplural[0] == "INTEGER":
206 nplural = None
207 else:
208 nplural = nplural[0]
209 if not plural or plural[0] == "EXPRESSION":
210 plural = None
211 else:
212 plural = plural[0]
213 return nplural, plural
214
220
222 """Merges another header with this header.
223
224 This header is assumed to be the template.
225
226 @type otherstore: L{base.TranslationStore}
227
228 """
229
230 newvalues = otherstore.parseheader()
231 self.updateheader(
232 Project_Id_Version = newvalues['Project-Id-Version'],
233 PO_Revision_Date = newvalues['PO-Revision-Date'],
234 Last_Translator = newvalues['Last-Translator'],
235 Language_Team = newvalues['Language-Team'],
236 Plural_Forms = newvalues['Plural-Forms']
237 )
238
240 """Add contribution comments
241 """
242 header = self.header()
243 if not header:
244 return
245 prelines = []
246 contriblines = []
247 postlines = []
248 contribexists = False
249 incontrib = False
250 outcontrib = False
251 for line in header.getnotes("translator").split('\n'):
252 line = line.strip()
253 if line == u"FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.":
254 incontrib = True
255 continue
256 if author_re.match(line):
257 incontrib = True
258 contriblines.append(line)
259 continue
260 if line == "" and incontrib:
261 incontrib = False
262 outcontrib = True
263 if incontrib:
264 contriblines.append(line)
265 elif not outcontrib:
266 prelines.append(line)
267 else:
268 postlines.append(line)
269
270 year = time.strftime("%Y")
271 contribexists = False
272 for line in contriblines:
273 if name in line and (email is None or email in line):
274 contribexists = True
275 break
276 if not contribexists:
277
278 if email:
279 contriblines.append("%s <%s>, %s" % (name, email, year))
280 else:
281 contriblines.append("%s, %s" % (name, year))
282
283 header.removenotes()
284 header.addnote("\n".join(prelines))
285 header.addnote("\n".join(contriblines))
286 header.addnote("\n".join(postlines))
287