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 update(existing, add=False, **kwargs):
45 """Update an existing header dictionary with the values in kwargs, adding new values 46 only if add is true. 47 48 @return: Updated dictionary of header entries 49 @rtype: dict 50 """ 51 headerargs = dictutils.ordereddict() 52 fixedargs = dictutils.cidict() 53 for key, value in kwargs.items(): 54 key = key.replace("_", "-") 55 if key.islower(): 56 key = key.title() 57 fixedargs[key] = value 58 removed = [] 59 for key in poheader.header_order: 60 if existing.has_key(key): 61 if key in fixedargs: 62 headerargs[key] = fixedargs.pop(key) 63 else: 64 headerargs[key] = existing[key] 65 removed.append(key) 66 elif add and fixedargs.has_key(key): 67 headerargs[key] = fixedargs.pop(key) 68 for key, value in existing.iteritems(): 69 if not key in removed: 70 headerargs[key] = value 71 if add: 72 for key in fixedargs: 73 headerargs[key] = fixedargs[key] 74 return headerargs
75 76
77 -class poheader:
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
98 - def tzstring(self):
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
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") + 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
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 headerString = "" 191 for key, value in headeritems.items(): 192 headerString += "%s: %s\n" % (key, value) 193 header.target = headerString 194 header.markfuzzy(False) # TODO: check why we do this? 195 return header
196
197 - def getheaderplural(self):
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
215 - def updateheaderplural(self, nplurals, plural):
216 """update the Plural-Form PO header""" 217 if isinstance(nplurals, basestring): 218 nplurals = int(nplurals) 219 self.updateheader( Plural_Forms = "nplurals=%d; plural=%s;" % (nplurals, plural) )
220
221 - def mergeheaders(self, otherstore):
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
239 - def updatecontributor(self, name, email=None):
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 # Add a new contributor 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