Package translate :: Package storage :: Module poheader
[hide private]
[frames] | no frames]

Source Code for Module translate.storage.poheader

  1  #!/usr/bin/env python 
  2  # -*- coding: utf-8 -*- 
  3  #  
  4  # Copyright 2002-2006 Zuza Software Foundation 
  5  #  
  6  # This file is part of translate. 
  7  # 
  8  # translate is free software; you can redistribute it and/or modify 
  9  # it under the terms of the GNU General Public License as published by 
 10  # the Free Software Foundation; either version 2 of the License, or 
 11  # (at your option) any later version. 
 12  #  
 13  # translate is distributed in the hope that it will be useful, 
 14  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 15  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 16  # GNU General Public License for more details. 
 17  # 
 18  # You should have received a copy of the GNU General Public License 
 19  # along with translate; if not, write to the Free Software 
 20  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 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   
31 -def parseheaderstring(input):
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 #We don't want unicode keys 40 key = str(key.strip()) 41 headervalues[key] = value.strip() 42 return headervalues
43
44 -def tzstring():
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):
58 """Update an existing header dictionary with the values in kwargs, adding new values 59 only if add is true. 60 61 @return: Updated dictionary of header entries 62 @rtype: dict 63 """ 64 headerargs = dictutils.ordereddict() 65 fixedargs = dictutils.cidict() 66 for key, value in kwargs.items(): 67 key = key.replace("_", "-") 68 if key.islower(): 69 key = key.title() 70 fixedargs[key] = value 71 removed = [] 72 for key in poheader.header_order: 73 if existing.has_key(key): 74 if key in fixedargs: 75 headerargs[key] = fixedargs.pop(key) 76 else: 77 headerargs[key] = existing[key] 78 removed.append(key) 79 elif add and fixedargs.has_key(key): 80 headerargs[key] = fixedargs.pop(key) 81 for key, value in existing.iteritems(): 82 if not key in removed: 83 headerargs[key] = value 84 if add: 85 for key in fixedargs: 86 headerargs[key] = fixedargs[key] 87 return headerargs
88 89
90 -class poheader(object):
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
112 - def makeheaderdict(self, charset="CHARSET", encoding="ENCODING", project_id_version=None, pot_creation_date=None, po_revision_date=None, last_translator=None, language_team=None, mime_version=None, plural_forms=None, report_msgid_bugs_to=None, **kwargs):
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
159 - def header(self):
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
171 - def parseheader(self):
172 """Parses the PO header and returns 173 the interpreted values as a dictionary""" 174 header = self.header() 175 if not header: 176 return {} 177 return parseheaderstring(header.target)
178
179 - def updateheader(self, add=False, **kwargs):
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 # FIXME: does not work for xliff files yet 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) # TODO: check why we do this? 200 return header
201
202 - def getheaderplural(self):
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
220 - def updateheaderplural(self, nplurals, plural):
221 """update the Plural-Form PO header""" 222 if isinstance(nplurals, basestring): 223 nplurals = int(nplurals) 224 self.updateheader( Plural_Forms = "nplurals=%d; plural=%s;" % (nplurals, plural) )
225
226 - def mergeheaders(self, otherstore):
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 # not necessarily there: 243 plurals = newvalues.get('Plural-Forms', None) 244 if plurals: 245 retain['Plural-Forms'] = plurals 246 self.updateheader(**retain)
247
248 - def updatecontributor(self, name, email=None):
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 # Add a new contributor 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