Index: pootlefile.py =================================================================== --- pootlefile.py (revision 7756) +++ pootlefile.py (working copy) @@ -230,401 +230,423 @@ assignitems.extend(actionitems) return assignitems -class pootlebase(object): - pass - -class pootlefile(pootlebase): - """this represents a pootle-managed file and its associated files""" - x_generator = "Pootle %s" % __version__.ver - def __init__(self, project=None, pofilename=None): - if pofilename: - self.__class__.__bases__ = (factory.getclass(pofilename),) - super(pootlefile, self).__init__() - self.pofilename = pofilename - if project is None: - from Pootle import projects - self.project = projects.DummyProject(None) - self.checker = None - self.filename = self.pofilename - else: - self.project = project - self.checker = self.project.checker - self.filename = os.path.join(self.project.podir, self.pofilename) - - self.lockedfile = LockedFile(self.filename) - # we delay parsing until it is required - self.pomtime = None - self.assigns = None - - self.pendingfilename = self.filename + os.extsep + "pending" - self.pendingfile = None - self.statistics = statistics.pootlestatistics(self) - self.tmfilename = self.filename + os.extsep + "tm" - # we delay parsing until it is required - self.pomtime = None - self.tracker = timecache.timecache(20*60) - - def parsestring(cls, storestring): - newstore = cls() - newstore.parse(storestring) - return newstore - parsestring = classmethod(parsestring) - - def parsefile(cls, storefile): - """Reads the given file (or opens the given filename) and parses back to an object""" - if isinstance(storefile, basestring): - storefile = open(storefile, "r") - if "r" in getattr(storefile, "mode", "r"): - storestring = storefile.read() - else: - storestring = "" - return cls.parsestring(storestring) - parsefile = classmethod(parsefile) - - def getheaderplural(self): - """returns values for nplural and plural values. It tries to see if the - file has it specified (in a po header or similar).""" - try: - return super(pootlefile, self).getheaderplural() - except AttributeError: - return None, None - - def updateheaderplural(self, *args, **kwargs): - """updates the file header. If there is an updateheader function in the - underlying store it will be delegated there.""" - try: - super(pootlefile, self).updateheaderplural(*args, **kwargs) - except AttributeError: - pass - - def updateheader(self, **kwargs): - """updates the file header. If there is an updateheader function in the - underlying store it will be delegated there.""" - try: - super(pootlefile, self).updateheader(**kwargs) - except AttributeError: - pass - - def readpendingfile(self): - """reads and parses the pending file corresponding to this file""" - if os.path.exists(self.pendingfilename): - inputfile = open(self.pendingfilename, "r") - self.pendingfile = factory.getobject(inputfile, ignore=".pending") - else: - self.pendingfile = po.pofile() - - def savependingfile(self): - """saves changes to disk...""" - output = str(self.pendingfile) - outputfile = open(self.pendingfilename, "w") - outputfile.write(output) - outputfile.close() - - def readtmfile(self): - """reads and parses the tm file corresponding to this file""" - if os.path.exists(self.tmfilename): - tmmtime = statistics.getmodtime(self.tmfilename) - if tmmtime == getattr(self, "tmmtime", None): +def make_class(base_class): + class pootlefile(base_class): + """this represents a pootle-managed file and its associated files""" + x_generator = "Pootle %s" % __version__.ver + def __init__(self, project=None, pofilename=None): + if pofilename: + self.__class__.__bases__ = (factory.getclass(pofilename),) + super(pootlefile, self).__init__() + self.pofilename = pofilename + if project is None: + from Pootle import projects + self.project = projects.DummyProject(None) + self.checker = None + self.filename = self.pofilename + else: + self.project = project + self.checker = self.project.checker + self.filename = os.path.join(self.project.podir, self.pofilename) + + self.lockedfile = LockedFile(self.filename) + # we delay parsing until it is required + self.pomtime = None + self.assigns = None + + self.pendingfilename = self.filename + os.extsep + "pending" + self.pendingfile = None + self.statistics = statistics.pootlestatistics(self) + self.tmfilename = self.filename + os.extsep + "tm" + # we delay parsing until it is required + self.pomtime = None + self.tracker = timecache.timecache(20*60) + + def parsestring(cls, storestring): + newstore = cls() + newstore.parse(storestring) + return newstore + parsestring = classmethod(parsestring) + + def parsefile(cls, storefile): + """Reads the given file (or opens the given filename) and parses back to an object""" + if isinstance(storefile, basestring): + storefile = open(storefile, "r") + if "r" in getattr(storefile, "mode", "r"): + storestring = storefile.read() + else: + storestring = "" + return cls.parsestring(storestring) + parsefile = classmethod(parsefile) + + def getheaderplural(self): + """returns values for nplural and plural values. It tries to see if the + file has it specified (in a po header or similar).""" + try: + return super(pootlefile, self).getheaderplural() + except AttributeError: + return None, None + + def updateheaderplural(self, *args, **kwargs): + """updates the file header. If there is an updateheader function in the + underlying store it will be delegated there.""" + try: + super(pootlefile, self).updateheaderplural(*args, **kwargs) + except AttributeError: + pass + + def updateheader(self, **kwargs): + """updates the file header. If there is an updateheader function in the + underlying store it will be delegated there.""" + try: + super(pootlefile, self).updateheader(**kwargs) + except AttributeError: + pass + + def readpendingfile(self): + """reads and parses the pending file corresponding to this file""" + if os.path.exists(self.pendingfilename): + inputfile = open(self.pendingfilename, "r") + self.pendingfile = factory.getobject(inputfile, ignore=".pending") + else: + self.pendingfile = po.pofile() + + def savependingfile(self): + """saves changes to disk...""" + output = str(self.pendingfile) + outputfile = open(self.pendingfilename, "w") + outputfile.write(output) + outputfile.close() + + def readtmfile(self): + """reads and parses the tm file corresponding to this file""" + if os.path.exists(self.tmfilename): + tmmtime = statistics.getmodtime(self.tmfilename) + if tmmtime == getattr(self, "tmmtime", None): + return + inputfile = open(self.tmfilename, "r") + self.tmmtime, self.tmfile = tmmtime, factory.getobject(inputfile, ignore=".tm") + else: + self.tmfile = po.pofile() + + def getsuggestions(self, item): + """find all the suggestion items submitted for the given item""" + unit = self.getitem(item) + if isinstance(unit, xliff.xliffunit): + return unit.getalttrans() + + locations = unit.getlocations() + self.readpendingfile() + # TODO: review the matching method + suggestpos = [suggestpo for suggestpo in self.pendingfile.units if suggestpo.getlocations() == locations] + return suggestpos + + def addsuggestion(self, item, suggtarget, username): + """adds a new suggestion for the given item""" + unit = self.getitem(item) + if isinstance(unit, xliff.xliffunit): + if isinstance(suggtarget, list) and (len(suggtarget) > 0): + suggtarget = suggtarget[0] + unit.addalttrans(suggtarget, origin=username) + self.statistics.reclassifyunit(item) + self.savepofile() return - inputfile = open(self.tmfilename, "r") - self.tmmtime, self.tmfile = tmmtime, factory.getobject(inputfile, ignore=".tm") - else: - self.tmfile = po.pofile() - - def getsuggestions(self, item): - """find all the suggestion items submitted for the given item""" - unit = self.getitem(item) - if isinstance(unit, xliff.xliffunit): - return unit.getalttrans() - - locations = unit.getlocations() - self.readpendingfile() - # TODO: review the matching method - suggestpos = [suggestpo for suggestpo in self.pendingfile.units if suggestpo.getlocations() == locations] - return suggestpos - - def addsuggestion(self, item, suggtarget, username): - """adds a new suggestion for the given item""" - unit = self.getitem(item) - if isinstance(unit, xliff.xliffunit): - if isinstance(suggtarget, list) and (len(suggtarget) > 0): - suggtarget = suggtarget[0] - unit.addalttrans(suggtarget, origin=username) + + self.readpendingfile() + newpo = unit.copy() + if username is not None: + newpo.msgidcomments.append('"_: suggested by %s\\n"' % username) + newpo.target = suggtarget + newpo.markfuzzy(False) + self.pendingfile.addunit(newpo) + self.savependingfile() self.statistics.reclassifyunit(item) + + def deletesuggestion(self, item, suggitem): + """removes the suggestion from the pending file""" + unit = self.getitem(item) + if hasattr(unit, "xmlelement"): + suggestions = self.getsuggestions(item) + unit.delalttrans(suggestions[suggitem]) + self.savepofile() + else: + self.readpendingfile() + locations = unit.getlocations() + # TODO: remove the suggestion in a less brutal manner + pendingitems = [pendingitem for pendingitem, suggestpo in enumerate(self.pendingfile.units) if suggestpo.getlocations() == locations] + pendingitem = pendingitems[suggitem] + del self.pendingfile.units[pendingitem] + self.savependingfile() + self.statistics.reclassifyunit(item) + + def getsuggester(self, item, suggitem): + """returns who suggested the given item's suggitem if recorded, else None""" + unit = self.getsuggestions(item)[suggitem] + if hasattr(unit, "xmlelement"): + return unit.xmlelement.get("origin") + + for msgidcomment in unit.msgidcomments: + if msgidcomment.find("suggested by ") != -1: + suggestedby = po.unquotefrompo([msgidcomment]).replace("_:", "", 1).replace("suggested by ", "", 1).strip() + return suggestedby + return None + + def gettmsuggestions(self, item): + """find all the tmsuggestion items submitted for the given item""" + self.readtmfile() + unit = self.getitem(item) + locations = unit.getlocations() + # TODO: review the matching method + # Can't simply use the location index, because we want multiple matches + suggestpos = [suggestpo for suggestpo in self.tmfile.units if suggestpo.getlocations() == locations] + return suggestpos + + def track(self, item, message): + """sets the tracker message for the given item""" + self.tracker[item] = message + + def readpofile(self): + """reads and parses the main file""" + # make sure encoding is reset so it is read from the file + self.encoding = None + self.units = [] + pomtime, filecontents = self.lockedfile.getcontents() + # note: we rely on this not resetting the filename, which we set earlier, when given a string + self.parse(filecontents) + self.pomtime = pomtime + + def savepofile(self): + """saves changes to the main file to disk...""" + output = str(self) + self.pomtime = self.lockedfile.writecontents(output) + + def pofreshen(self): + """makes sure we have a freshly parsed pofile""" + try: + if self.pomtime != self.lockedfile.readmodtime(): + self.readpofile() + except OSError, e: + # If this exception is not triggered by a bad + # symlink, then we have a missing file on our hands... + if not os.path.islink(self.filename): + # ...and thus we rescan our files to get rid of the missing filename + self.project.scanpofiles() + else: + print "%s is a broken symlink" % (self.filename,) + + def getoutput(self): + """returns pofile output""" + self.pofreshen() + return super(pootlefile, self).getoutput() + + def updateunit(self, item, newvalues, userprefs, languageprefs): + """updates a translation with a new target value""" + self.pofreshen() + unit = self.getitem(item) + + if newvalues.has_key("target"): + unit.target = newvalues["target"] + if newvalues.has_key("fuzzy"): + unit.markfuzzy(newvalues["fuzzy"]) + if newvalues.has_key("translator_comments"): + unit.removenotes() + if newvalues["translator_comments"]: + unit.addnote(newvalues["translator_comments"]) + + po_revision_date = time.strftime("%Y-%m-%d %H:%M") + tzstring() + headerupdates = {"PO_Revision_Date": po_revision_date, "X_Generator": self.x_generator} + if userprefs: + if getattr(userprefs, "name", None) and getattr(userprefs, "email", None): + headerupdates["Last_Translator"] = "%s <%s>" % (userprefs.name, userprefs.email) + # XXX: If we needed to add a header, the index value in item will be one out after + # adding the header. + # TODO: remove once we force the PO class to always output headers + force_recache = False + if not self.header(): + force_recache = True + self.updateheader(add=True, **headerupdates) + if languageprefs: + nplurals = getattr(languageprefs, "nplurals", None) + pluralequation = getattr(languageprefs, "pluralequation", None) + if nplurals and pluralequation: + self.updateheaderplural(nplurals, pluralequation) self.savepofile() - return - - self.readpendingfile() - newpo = unit.copy() - if username is not None: - newpo.msgidcomments.append('"_: suggested by %s\\n"' % username) - newpo.target = suggtarget - newpo.markfuzzy(False) - self.pendingfile.addunit(newpo) - self.savependingfile() - self.statistics.reclassifyunit(item) - - def deletesuggestion(self, item, suggitem): - """removes the suggestion from the pending file""" - unit = self.getitem(item) - if hasattr(unit, "xmlelement"): - suggestions = self.getsuggestions(item) - unit.delalttrans(suggestions[suggitem]) - self.savepofile() - else: - self.readpendingfile() - locations = unit.getlocations() - # TODO: remove the suggestion in a less brutal manner - pendingitems = [pendingitem for pendingitem, suggestpo in enumerate(self.pendingfile.units) if suggestpo.getlocations() == locations] - pendingitem = pendingitems[suggitem] - del self.pendingfile.units[pendingitem] - self.savependingfile() - self.statistics.reclassifyunit(item) - - def getsuggester(self, item, suggitem): - """returns who suggested the given item's suggitem if recorded, else None""" - unit = self.getsuggestions(item)[suggitem] - if hasattr(unit, "xmlelement"): - return unit.xmlelement.get("origin") - - for msgidcomment in unit.msgidcomments: - if msgidcomment.find("suggested by ") != -1: - suggestedby = po.unquotefrompo([msgidcomment]).replace("_:", "", 1).replace("suggested by ", "", 1).strip() - return suggestedby - return None - - def gettmsuggestions(self, item): - """find all the tmsuggestion items submitted for the given item""" - self.readtmfile() - unit = self.getitem(item) - locations = unit.getlocations() - # TODO: review the matching method - # Can't simply use the location index, because we want multiple matches - suggestpos = [suggestpo for suggestpo in self.tmfile.units if suggestpo.getlocations() == locations] - return suggestpos - - def track(self, item, message): - """sets the tracker message for the given item""" - self.tracker[item] = message - - def readpofile(self): - """reads and parses the main file""" - # make sure encoding is reset so it is read from the file - self.encoding = None - self.units = [] - pomtime, filecontents = self.lockedfile.getcontents() - # note: we rely on this not resetting the filename, which we set earlier, when given a string - self.parse(filecontents) - self.pomtime = pomtime - - def savepofile(self): - """saves changes to the main file to disk...""" - output = str(self) - self.pomtime = self.lockedfile.writecontents(output) - - def pofreshen(self): - """makes sure we have a freshly parsed pofile""" - try: - if self.pomtime != self.lockedfile.readmodtime(): - self.readpofile() - except OSError, e: - # If this exception is not triggered by a bad - # symlink, then we have a missing file on our hands... - if not os.path.islink(self.filename): - # ...and thus we rescan our files to get rid of the missing filename - self.project.scanpofiles() - else: - print "%s is a broken symlink" % (self.filename,) - - def getoutput(self): - """returns pofile output""" - self.pofreshen() - return super(pootlefile, self).getoutput() - - def updateunit(self, item, newvalues, userprefs, languageprefs): - """updates a translation with a new target value""" - self.pofreshen() - unit = self.getitem(item) - - if newvalues.has_key("target"): - unit.target = newvalues["target"] - if newvalues.has_key("fuzzy"): - unit.markfuzzy(newvalues["fuzzy"]) - if newvalues.has_key("translator_comments"): - unit.removenotes() - if newvalues["translator_comments"]: - unit.addnote(newvalues["translator_comments"]) - - po_revision_date = time.strftime("%Y-%m-%d %H:%M") + tzstring() - headerupdates = {"PO_Revision_Date": po_revision_date, "X_Generator": self.x_generator} - if userprefs: - if getattr(userprefs, "name", None) and getattr(userprefs, "email", None): - headerupdates["Last_Translator"] = "%s <%s>" % (userprefs.name, userprefs.email) - # XXX: If we needed to add a header, the index value in item will be one out after - # adding the header. - # TODO: remove once we force the PO class to always output headers - force_recache = False - if not self.header(): - force_recache = True - self.updateheader(add=True, **headerupdates) - if languageprefs: - nplurals = getattr(languageprefs, "nplurals", None) - pluralequation = getattr(languageprefs, "pluralequation", None) - if nplurals and pluralequation: - self.updateheaderplural(nplurals, pluralequation) - self.savepofile() - if force_recache: - self.statistics.purge_totals() - self.statistics.reclassifyunit(item) - - def getitem(self, item): - """Returns a single unit based on the item number.""" - return self.units[self.statistics.getstats()["total"][item]] - - def iteritems(self, search, lastitem=None): - """iterates through the items in this pofile starting after the given lastitem, using the given search""" - # update stats if required - translatables = self.statistics.getstats()["total"] - if lastitem is None: - minitem = 0 - else: - minitem = lastitem + 1 - maxitem = len(translatables) - validitems = range(minitem, maxitem) - if search.assignedto or search.assignedaction: - assignitems = self.getassigns().finditems(search) - validitems = [item for item in validitems if item in assignitems] - # loop through, filtering on matchnames if required - for item in validitems: - if not search.matchnames: - yield item - for name in search.matchnames: - if translatables[item] in self.statistics.getstats()[name]: + if force_recache: + self.statistics.purge_totals() + self.statistics.reclassifyunit(item) + + def getitem(self, item): + """Returns a single unit based on the item number.""" + return self.units[self.statistics.getstats()["total"][item]] + + def iteritems(self, search, lastitem=None): + """iterates through the items in this pofile starting after the given lastitem, using the given search""" + # update stats if required + translatables = self.statistics.getstats()["total"] + if lastitem is None: + minitem = 0 + else: + minitem = lastitem + 1 + maxitem = len(translatables) + validitems = range(minitem, maxitem) + if search.assignedto or search.assignedaction: + assignitems = self.getassigns().finditems(search) + validitems = [item for item in validitems if item in assignitems] + # loop through, filtering on matchnames if required + for item in validitems: + if not search.matchnames: yield item - - def matchitems(self, newfile, uselocations=False): - """matches up corresponding items in this pofile with the given newfile, and returns tuples of matching poitems (None if no match found)""" - if not hasattr(self, "sourceindex"): - self.makeindex() - if not hasattr(newfile, "sourceindex"): - newfile.makeindex() - matches = [] - for newpo in newfile.units: - if newpo.isheader(): - continue - foundid = False - if uselocations: - newlocations = newpo.getlocations() - mergedlocations = [] - for location in newlocations: - if location in mergedlocations: - continue - if location in self.locationindex: - oldpo = self.locationindex[location] - if oldpo is not None: - foundid = True - matches.append((oldpo, newpo)) - mergedlocations.append(location) + for name in search.matchnames: + if translatables[item] in self.statistics.getstats()[name]: + yield item + + def matchitems(self, newfile, uselocations=False): + """matches up corresponding items in this pofile with the given newfile, and returns tuples of matching poitems (None if no match found)""" + if not hasattr(self, "sourceindex"): + self.makeindex() + if not hasattr(newfile, "sourceindex"): + newfile.makeindex() + matches = [] + for newpo in newfile.units: + if newpo.isheader(): + continue + foundid = False + if uselocations: + newlocations = newpo.getlocations() + mergedlocations = [] + for location in newlocations: + if location in mergedlocations: continue - if not foundid: - # We can't use the multistring, because it might contain more than two - # entries in a PO xliff file. Rather use the singular. - source = unicode(newpo.source) - if source in self.sourceindex: - oldpo = self.sourceindex[source] - matches.append((oldpo, newpo)) - else: - matches.append((None, newpo)) - # find items that have been removed - matcheditems = [oldpo for oldpo, newpo in matches if oldpo] - for oldpo in self.units: - if not oldpo in matcheditems: - matches.append((oldpo, None)) - return matches - - def getassigns(self): - if self.assigns is None: - self.assigns = pootleassigns(self) - return self.assigns - - def mergeitem(self, oldpo, newpo, username, suggest=False): - """merges any changes from newpo into oldpo""" - unchanged = oldpo.target == newpo.target - if not suggest and (not oldpo.target or not newpo.target or oldpo.isheader() or newpo.isheader() or unchanged): - oldpo.merge(newpo) - else: - for item in self.statistics.getstats()["total"]: - matchpo = self.units[item] - if matchpo == oldpo: - strings = getattr(newpo.target, "strings", [newpo.target]) - self.addsuggestion(item, strings, username) - return - raise KeyError("Could not find item for merge") - - def mergefile(self, newfile, username, allownewstrings=True, suggestions=False): - """make sure each msgid is unique ; merge comments etc from duplicates into original""" - self.makeindex() - matches = self.matchitems(newfile) - for oldpo, newpo in matches: - if suggestions: - if oldpo and newpo: - self.mergeitem(oldpo, newpo, username, suggest=True) - continue - - if oldpo is None: - if allownewstrings: - if isinstance(newpo, po.pounit): - self.addunit(newpo) + if location in self.locationindex: + oldpo = self.locationindex[location] + if oldpo is not None: + foundid = True + matches.append((oldpo, newpo)) + mergedlocations.append(location) + continue + if not foundid: + # We can't use the multistring, because it might contain more than two + # entries in a PO xliff file. Rather use the singular. + source = unicode(newpo.source) + if source in self.sourceindex: + oldpo = self.sourceindex[source] + matches.append((oldpo, newpo)) else: - self.addunit(self.UnitClass.buildfromunit(newpo)) - elif newpo is None: - # TODO: mark the old one as obsolete - pass + matches.append((None, newpo)) + # find items that have been removed + matcheditems = [oldpo for oldpo, newpo in matches if oldpo] + for oldpo in self.units: + if not oldpo in matcheditems: + matches.append((oldpo, None)) + return matches + + def getassigns(self): + if self.assigns is None: + self.assigns = pootleassigns(self) + return self.assigns + + def mergeitem(self, oldpo, newpo, username, suggest=False): + """merges any changes from newpo into oldpo""" + unchanged = oldpo.target == newpo.target + if not suggest and (not oldpo.target or not newpo.target or oldpo.isheader() or newpo.isheader() or unchanged): + oldpo.merge(newpo) else: - self.mergeitem(oldpo, newpo, username) - # we invariably want to get the ids (source locations) from the newpo - if hasattr(newpo, "sourcecomments"): - oldpo.sourcecomments = newpo.sourcecomments - - if not isinstance(newfile, po.pofile) or suggestions: - #TODO: We don't support updating the header yet. + for item in self.statistics.getstats()["total"]: + matchpo = self.units[item] + if matchpo == oldpo: + strings = getattr(newpo.target, "strings", [newpo.target]) + self.addsuggestion(item, strings, username) + return + raise KeyError("Could not find item for merge") + + def mergefile(self, newfile, username, allownewstrings=True, suggestions=False): + """make sure each msgid is unique ; merge comments etc from duplicates into original""" + self.makeindex() + matches = self.matchitems(newfile) + for oldpo, newpo in matches: + if suggestions: + if oldpo and newpo: + self.mergeitem(oldpo, newpo, username, suggest=True) + continue + + if oldpo is None: + if allownewstrings: + if isinstance(newpo, po.pounit): + self.addunit(newpo) + else: + self.addunit(self.UnitClass.buildfromunit(newpo)) + elif newpo is None: + # TODO: mark the old one as obsolete + pass + else: + self.mergeitem(oldpo, newpo, username) + # we invariably want to get the ids (source locations) from the newpo + if hasattr(newpo, "sourcecomments"): + oldpo.sourcecomments = newpo.sourcecomments + + if not isinstance(newfile, po.pofile) or suggestions: + #TODO: We don't support updating the header yet. + self.savepofile() + # the easiest way to recalculate everything + self.readpofile() + return + + #Let's update selected header entries. Only the ones listed below, and ones + #that are empty in self can be updated. The check in header_order is just + #a basic sanity check so that people don't insert garbage. + updatekeys = ['Content-Type', + 'POT-Creation-Date', + 'Last-Translator', + 'Project-Id-Version', + 'PO-Revision-Date', + 'Language-Team'] + headerstoaccept = {} + ownheader = self.parseheader() + for (key, value) in newfile.parseheader().items(): + if key in updatekeys or (not key in ownheader or not ownheader[key]) and key in po.pofile.header_order: + headerstoaccept[key] = value + self.updateheader(add=True, **headerstoaccept) + + #Now update the comments above the header: + header = self.header() + newheader = newfile.header() + if header is None and not newheader is None: + header = self.UnitClass("", encoding=self.encoding) + header.target = "" + if header: + header._initallcomments(blankall=True) + if newheader: + for i in range(len(header.allcomments)): + header.allcomments[i].extend(newheader.allcomments[i]) + self.savepofile() # the easiest way to recalculate everything self.readpofile() - return + return pootlefile - #Let's update selected header entries. Only the ones listed below, and ones - #that are empty in self can be updated. The check in header_order is just - #a basic sanity check so that people don't insert garbage. - updatekeys = ['Content-Type', - 'POT-Creation-Date', - 'Last-Translator', - 'Project-Id-Version', - 'PO-Revision-Date', - 'Language-Team'] - headerstoaccept = {} - ownheader = self.parseheader() - for (key, value) in newfile.parseheader().items(): - if key in updatekeys or (not key in ownheader or not ownheader[key]) and key in po.pofile.header_order: - headerstoaccept[key] = value - self.updateheader(add=True, **headerstoaccept) - - #Now update the comments above the header: - header = self.header() - newheader = newfile.header() - if header is None and not newheader is None: - header = self.UnitClass("", encoding=self.encoding) - header.target = "" - if header: - header._initallcomments(blankall=True) - if newheader: - for i in range(len(header.allcomments)): - header.allcomments[i].extend(newheader.allcomments[i]) - - self.savepofile() - # the easiest way to recalculate everything - self.readpofile() +_pootlefile_classes = {} +# We want to extend the functionality of translation stores with some +# Pootle-specific functionality, but we still want them to act like +# translation stores. The clean way to do this, is to store a reference +# to a translation store inside a "pootlefile" class and to delegate +# if needed to the store. This was done initially through __getattr__ +# and __setattr__, although it proved to be rather slow (which made +# a difference for large sets of translation files). This is now +# achieved through inheritance. When we have to load a translation file, +# we get hold of its corresponding translation store class. Then we +# see whether there is a class which contains pootlefile functionality +# and which derives from the translation store class. If there isn't +# we invoke make_class to create such a class. Then we return an +# instance of this class to the user. +def pootlefile(project=None, pofilename=None): + po_class = po.pofile + if pofilename != None: + po_class = factory.getclass(pofilename) + if po_class not in _pootlefile_classes: + _pootlefile_classes[po_class] = make_class(po_class) + return _pootlefile_classes[po_class](project, pofilename) + class Search: """an object containing all the searching information""" def __init__(self, dirfilter=None, matchnames=[], assignedto=None, assignedaction=None, searchtext=None):