cvsdist 2b5159
#!/usr/bin/python
cvsdist 2b5159
## Copyright (C) 2001 Red Hat, Inc.
cvsdist 2b5159
## Copyright (C) 2001 Trond Eivind Glomsrød <teg@redhat.com>
cvsdist 2b5159
cvsdist 6b9110
## v0.2 - 2001-08-21
cvsdist 6b9110
cvsdist 2b5159
## This program is free software; you can redistribute it and/or modify
cvsdist 2b5159
## it under the terms of the GNU General Public License as published by
cvsdist 2b5159
## the Free Software Foundation; either version 2 of the License, or
cvsdist 2b5159
## (at your option) any later version.
cvsdist 2b5159
cvsdist 2b5159
## This program is distributed in the hope that it will be useful,
cvsdist 2b5159
## but WITHOUT ANY WARRANTY; without even the implied warranty of
cvsdist 2b5159
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
cvsdist 2b5159
## GNU General Public License for more details.
cvsdist 2b5159
cvsdist 2b5159
## You should have received a copy of the GNU General Public License
cvsdist 2b5159
## along with this program; if not, write to the Free Software
cvsdist 2b5159
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
cvsdist 2b5159
cvsdist 2b5159
"""
cvsdist 2b5159
A msghack replacement
cvsdist 2b5159
"""
cvsdist 2b5159
cvsdist 2b5159
import string
cvsdist 2b5159
import sys
cvsdist 2b5159
cvsdist 2b5159
class GTMessage:
cvsdist 2b5159
    """
cvsdist 2b5159
    A class containing a message, its msgid and various references pointing at it
cvsdist 2b5159
    """
cvsdist 2b5159
cvsdist 2b5159
    def __init__(self,id=None,message=None,refs=[]):
cvsdist 2b5159
        """
cvsdist 2b5159
        The constructor for the GTMessage class
cvsdist 2b5159
        @self The object instance
cvsdist 2b5159
        @message The message
cvsdist 2b5159
        @id The messageid associated with the object
cvsdist 2b5159
        """
cvsdist 2b5159
        self._message=string.strip(message)
cvsdist 2b5159
        self._id=string.strip(id)
cvsdist 2b5159
        self._refs=[]
cvsdist 2b5159
        for ref in refs:
cvsdist 2b5159
            self._refs.append(ref)
cvsdist 2b5159
cvsdist 2b5159
    def __str__(self):
cvsdist 2b5159
        """
cvsdist 2b5159
        Return a string representation of the object
cvsdist 2b5159
        @self The object instance
cvsdist 2b5159
        """
cvsdist 2b5159
        res=""
cvsdist 2b5159
        for ref in self._refs:
cvsdist 2b5159
            res=res+ref+"\n"
cvsdist 6b9110
        res=res+"msgid %s\nmsgstr %s\n" % (self._id,self._message)
cvsdist 2b5159
        return res
cvsdist 2b5159
cvsdist 2b5159
    def invertedStrings(self):
cvsdist 2b5159
        """
cvsdist 6b9110
        Returns a string representation, but with msgid and msgstr inverted.
cvsdist 6b9110
        Note: Don't invert the "" string
cvsdist 2b5159
        @self The object instance
cvsdist 2b5159
        """
cvsdist 2b5159
        res=""
cvsdist 2b5159
        for ref in self._refs:
cvsdist 2b5159
            res=res+ref+"\n"
cvsdist 6b9110
        if not self._id=="\"\"":
cvsdist 6b9110
            res=res+"msgid %s\nmsgstr %s\n" % (self._message,self._id)
cvsdist 6b9110
        else:
cvsdist 6b9110
            res=res+"msgid %s\nmsgstr %s\n" % (self._id,self._message)
cvsdist 2b5159
        return res
cvsdist 2b5159
cvsdist 2b5159
    def emptyMsgStrings(self):
cvsdist 2b5159
        """
cvsdist 2b5159
        Return a string representation of the object, but leave the msgstr
cvsdist 2b5159
        empty - create a pot file from a po file
cvsdist 6b9110
        Note: Won't remove the "" string
cvsdist 2b5159
        @self The object instance
cvsdist 2b5159
        """
cvsdist 2b5159
        res=""
cvsdist 2b5159
        for ref in self._refs:
cvsdist 2b5159
            res=res+ref+"\n"
cvsdist 6b9110
        if not self._id=="\"\"":
cvsdist 6b9110
            res=res+"msgid %s\nmsgstr \"\"\n" % (self._id)
cvsdist 6b9110
        else:
cvsdist 6b9110
            res=res+"msgid %s\nmsgstr %s\n" % (self._id,self._message)
cvsdist 2b5159
        return res
cvsdist 2b5159
        
cvsdist 31ff37
    def compareMessage(self,msg):
cvsdist 31ff37
        """
cvsdist 31ff37
        Return  if the messages have identical msgids, 0 otherwise
cvsdist 31ff37
        @self The object instance
cvsdist 31ff37
        @msg The message to compare to
cvsdist 31ff37
        """
cvsdist 31ff37
cvsdist 31ff37
        if self._id == msg._id:
cvsdist 31ff37
            return 1
cvsdist 31ff37
        return 0
cvsdist 31ff37
        
cvsdist 31ff37
cvsdist 2b5159
class GTMasterMessage:
cvsdist 2b5159
    """
cvsdist 2b5159
    A class containing a message, its msgid and various references pointing at it
cvsdist 2b5159
    The difference between GTMessage and GTMasterMessage is that this class
cvsdist 2b5159
    can do less operations, but is able to store multiple msgstrs with identifiers
cvsdist 2b5159
    (usually language, like 'msgst(no)'
cvsdist 2b5159
    """
cvsdist 2b5159
cvsdist 2b5159
    def __init__(self,id=None,refs=[]):
cvsdist 2b5159
        """
cvsdist 2b5159
        The constructor for the GTMessage class
cvsdist 2b5159
        @self The object instance
cvsdist 2b5159
        @id The messageid associated with the object
cvsdist 2b5159
        """
cvsdist 2b5159
        self._id=id
cvsdist 2b5159
        self._refs=[]
cvsdist 2b5159
        self._messages=[]
cvsdist 2b5159
        for ref in refs:
cvsdist 2b5159
            self._refs.append(ref)
cvsdist 2b5159
cvsdist 2b5159
    def addMessage(self,message,identifier):
cvsdist 2b5159
        """
cvsdist 2b5159
        Add a new message and identifier to the GTMasterMessage object
cvsdist 2b5159
        @self The object instance
cvsdist 2b5159
        @message The message to append
cvsdist 2b5159
        @identifier The identifier of the message
cvsdist 2b5159
        """
cvsdist 2b5159
        self._messages.append((identifier,message))
cvsdist 2b5159
cvsdist 2b5159
    def __str__(self):
cvsdist 2b5159
        """
cvsdist 2b5159
        Return a string representation of the object
cvsdist 2b5159
        @self The object instance
cvsdist 2b5159
        """
cvsdist 2b5159
        res=""
cvsdist 2b5159
        for ref in self._refs:
cvsdist 2b5159
            res=res+ref+"\n"
cvsdist 6b9110
        res=res+"msgid %s\n" % self._id
cvsdist 2b5159
        for message in self._messages:
cvsdist 6b9110
            res=res+"msgstr(%s) %s\n" %(message[0],message[1])
cvsdist 2b5159
        res=res+"\n"
cvsdist 2b5159
        return res
cvsdist 2b5159
cvsdist 2b5159
class GTFile:
cvsdist 2b5159
    """
cvsdist 2b5159
    A class containing the GTMessages contained in a file
cvsdist 2b5159
    """
cvsdist 2b5159
cvsdist 2b5159
    def __init__(self,filename):
cvsdist 2b5159
        """
cvsdist 2b5159
        The constructor of the GTMFile class
cvsdist 2b5159
        @self The object instance
cvsdist 2b5159
        @filename The  file to initialize from
cvsdist 2b5159
        """
cvsdist 2b5159
        self._filename=filename
cvsdist 2b5159
        self._messages=[]
cvsdist 2b5159
        self.readFile(filename)
cvsdist 2b5159
cvsdist 2b5159
    def __str__(self):
cvsdist 2b5159
        """
cvsdist 2b5159
        Return a string representation of the object
cvsdist 2b5159
        @self The object instance
cvsdist 2b5159
        """
cvsdist 2b5159
        res=""
cvsdist 2b5159
        for message in self._messages:
cvsdist 2b5159
            res=res+str(message)+"\n"
cvsdist 2b5159
        return res
cvsdist 2b5159
cvsdist 2b5159
    def invertedStrings(self):
cvsdist 2b5159
        """
cvsdist 2b5159
        Return a string representation of the object, with msgid and msgstr
cvsdist 6b9110
        swapped. Will remove duplicates...
cvsdist 2b5159
        @self The object instance
cvsdist 2b5159
        """
cvsdist 6b9110
cvsdist 6b9110
        msght={}
cvsdist 6b9110
        msgar=[]
cvsdist 6b9110
cvsdist 2b5159
        for message in self._messages:
cvsdist 6b9110
            if message._id=='""' and len(msgar)==0:
cvsdist 6b9110
                msgar.append(GTMessage(message._id,message._message,message._refs))
cvsdist 6b9110
                continue
cvsdist 6b9110
            msg=GTMessage(message._message,message._id,message._refs)
cvsdist 6b9110
            if not msght.has_key(msg._id):
cvsdist 6b9110
                msght[msg._id]=msg
cvsdist 6b9110
                msgar.append(msg)
cvsdist 6b9110
            else:
cvsdist 6b9110
                msg2=msght[msg._id]
cvsdist 6b9110
                for ref in msg._refs:
cvsdist 6b9110
                    msg2._refs.append(ref)
cvsdist 6b9110
        res=""
cvsdist 6b9110
        for message in msgar:
cvsdist 6b9110
            res=res+str(message)+"\n"
cvsdist 2b5159
        return res
cvsdist 2b5159
cvsdist 31ff37
    def msgidDupes(self):
cvsdist 31ff37
        """
cvsdist 6b9110
        Search for duplicates in the msgids.
cvsdist 31ff37
        @self The object instance
cvsdist 31ff37
        """
cvsdist 31ff37
        msgids={}
cvsdist 31ff37
        res=""
cvsdist 31ff37
        for message in self._messages:
cvsdist 31ff37
            msgid=message._id
cvsdist 31ff37
            if msgids.has_key(msgid):
cvsdist 31ff37
                res=res+"Duplicate: %s\n" % (msgid)
cvsdist 31ff37
            else:
cvsdist 31ff37
                msgids[msgid]=1
cvsdist 31ff37
        return res
cvsdist 31ff37
cvsdist 2b5159
    def getMsgstr(self,msgid):
cvsdist 2b5159
        """
cvsdist 2b5159
        Return the msgstr matching the given id. 'None' if missing
cvsdist 2b5159
        @self The object instance
cvsdist 2b5159
        @msgid The msgid key
cvsdist 2b5159
        """
cvsdist 2b5159
cvsdist 2b5159
        for message in self._messages:
cvsdist 2b5159
            if msgid == message._id:
cvsdist 2b5159
                return message._message
cvsdist 2b5159
        return None
cvsdist 2b5159
cvsdist 2b5159
    def emptyMsgStrings(self):
cvsdist 2b5159
        """
cvsdist 2b5159
        Return a string representation of the object, but leave the msgstr
cvsdist 2b5159
        empty - create a pot file from a po file
cvsdist 2b5159
        @self The object instance
cvsdist 2b5159
        """
cvsdist 2b5159
        
cvsdist 2b5159
        res=""
cvsdist 2b5159
        for message in self._messages:
cvsdist 2b5159
            res=res+message.emptyMsgStrings()+"\n"
cvsdist 2b5159
        return res
cvsdist 2b5159
cvsdist 31ff37
            
cvsdist 31ff37
    def append(self,B):
cvsdist 31ff37
        """
cvsdist 31ff37
        Append entries from dictionary B which aren't
cvsdist 31ff37
        already present in this dictionary
cvsdist 31ff37
        @self The object instance
cvsdist 31ff37
        @B the dictionary to append messages from
cvsdist 31ff37
        """
cvsdist 31ff37
cvsdist 31ff37
        for message in B._messages:
cvsdist 31ff37
            if not self.getMsgstr(message._id):
cvsdist 31ff37
                self._messages.append(message)
cvsdist 31ff37
                
cvsdist 31ff37
cvsdist 2b5159
    def readFile(self,filename):
cvsdist 2b5159
        """
cvsdist 2b5159
        Read the contents of a file into the GTFile object
cvsdist 2b5159
        @self The object instance
cvsdist 2b5159
        @filename The name of the file to read
cvsdist 2b5159
        """
cvsdist 2b5159
        
cvsdist 2b5159
        file=open(filename,"r")
cvsdist 2b5159
        msgid=""
cvsdist 2b5159
        msgstr=""
cvsdist 2b5159
        refs=[]
cvsdist 2b5159
        lines=[]
cvsdist 2b5159
        inmsgid=0
cvsdist 2b5159
        inmsgstr=0
cvsdist 2b5159
        templines=file.readlines()
cvsdist 2b5159
        for line in templines:
cvsdist 2b5159
            lines.append(string.strip(line))
cvsdist 2b5159
        for line in lines:
cvsdist 2b5159
            pos=string.find(line,'"')
cvsdist 2b5159
            pos2=string.rfind(line,'"')
cvsdist 2b5159
            if line and line[0]=="#":
cvsdist 2b5159
                refs.append(string.strip(line))
cvsdist 2b5159
            if inmsgstr==0 and line[:6]=="msgstr":
cvsdist 2b5159
                msgstr=""
cvsdist 2b5159
                inmsgstr=1
cvsdist 2b5159
                inmsgid=0
cvsdist 2b5159
            if inmsgstr==1:
cvsdist 2b5159
                if pos==-1:
cvsdist 2b5159
                    inmsgstr=0
cvsdist 6b9110
                    #Handle entries with and without "" consistently
cvsdist 6b9110
                    if msgid[:2]=='""' and len(msgid)>4: 
cvsdist 6b9110
                        msgid=msgid[2:]
cvsdist 6b9110
                    if msgstr[:2]=='""' and len(msgstr)>4: 
cvsdist 6b9110
                        msgstr=msgstr[2:]
cvsdist 2b5159
                    message=GTMessage(msgid,msgstr,refs)
cvsdist 2b5159
                    self._messages.append(message)
cvsdist 2b5159
                    msgstr=""
cvsdist 2b5159
                    msgid=""
cvsdist 2b5159
                    refs=[]
cvsdist 2b5159
                else:
cvsdist 6b9110
                    msgstr=msgstr+line[pos:pos2+1]+"\n"
cvsdist 2b5159
            if inmsgid==0 and line[:5]=="msgid":
cvsdist 2b5159
                msgid=""
cvsdist 2b5159
                inmsgid=1
cvsdist 2b5159
            if inmsgid==1:
cvsdist 2b5159
                if pos==-1:
cvsdist 2b5159
                    inmsgid=0
cvsdist 2b5159
                else:
cvsdist 6b9110
                    msgid=msgid+line[pos:pos2+1]+"\n"
cvsdist 2b5159
        if msgstr and msgid:
cvsdist 6b9110
            message=GTMessage(msgid,msgstr,refs)
cvsdist 2b5159
            self._messages.append(message)
cvsdist 2b5159
cvsdist 2b5159
cvsdist 2b5159
class GTMaster:
cvsdist 2b5159
    """
cvsdist 2b5159
    A class containing a master catalogue of gettext dictionaries
cvsdist 2b5159
    """
cvsdist 2b5159
cvsdist 2b5159
    def __init__(self,dicts):
cvsdist 2b5159
        """
cvsdist 2b5159
        The constructor for the GTMaster class
cvsdist 2b5159
        @self The object instance
cvsdist 2b5159
        @dicts An array of dictionaries to merge
cvsdist 2b5159
        """
cvsdist 2b5159
        self._messages=[]
cvsdist 2b5159
        self.createMaster(dicts)
cvsdist 2b5159
cvsdist 2b5159
    def createMaster(self,dicts):
cvsdist 2b5159
        """
cvsdist 2b5159
        Create the master catalogue
cvsdist 2b5159
        @self The object instance
cvsdist 2b5159
        @dicts An array of dictionaries to merge
cvsdist 2b5159
        """
cvsdist 2b5159
cvsdist 2b5159
        self._master=dicts[0]
cvsdist 2b5159
        self._dicts=dicts[1:]
cvsdist 2b5159
cvsdist 2b5159
        for message in self._master._messages:
cvsdist 2b5159
            gtm=GTMasterMessage(message._id,message._refs)
cvsdist 2b5159
            gtm.addMessage(message._message,self._master._filename[:-3])
cvsdist 2b5159
            for dict in self._dicts:
cvsdist 2b5159
                res=dict.getMsgstr(message._id)
cvsdist 2b5159
                if(res):
cvsdist 2b5159
                    gtm.addMessage(res,dict._filename[:-3])
cvsdist 2b5159
            self._messages.append(gtm)
cvsdist 2b5159
cvsdist 2b5159
    def __str__(self):
cvsdist 2b5159
        """
cvsdist 2b5159
        Return a string representation of the object
cvsdist 2b5159
        @self The object instance
cvsdist 2b5159
        """
cvsdist 2b5159
        res=""
cvsdist 2b5159
        for message in self._messages:
cvsdist 2b5159
            res=res+str(message)+"\n"
cvsdist 2b5159
        return res
cvsdist 31ff37
cvsdist 2b5159
cvsdist 2b5159
if __name__=="__main__":
cvsdist 31ff37
    output=None
cvsdist 31ff37
    res=None
cvsdist 31ff37
    if("-o") in sys.argv:
cvsdist 31ff37
        output=sys.argv[sys.argv.index("-o")+1]
cvsdist 31ff37
        sys.argv.remove("-o")
cvsdist 31ff37
        sys.argv.remove(output)
cvsdist 2b5159
    if("--invert") in sys.argv:
cvsdist 2b5159
        file=sys.argv[sys.argv.index("--invert")+1]
cvsdist 2b5159
        gtf=GTFile(file)
cvsdist 31ff37
        res1=gtf.msgidDupes()
cvsdist 31ff37
        if res1:
cvsdist 31ff37
            sys.stderr.write(res1)
cvsdist 31ff37
            sys.exit(1)
cvsdist 31ff37
        res=str(gtf.invertedStrings())
cvsdist 31ff37
    elif("--empty") in sys.argv:
cvsdist 2b5159
        file=sys.argv[sys.argv.index("--empty")+1]
cvsdist 2b5159
        gtf=GTFile(file)
cvsdist 31ff37
        res=str(gtf.emptyMsgStrings())
cvsdist 31ff37
    elif("--master") in sys.argv:
cvsdist 2b5159
        loc=sys.argv.index("--master")+1
cvsdist 2b5159
        gtfs=[]
cvsdist 2b5159
        for file in sys.argv[loc:]:
cvsdist 2b5159
            gtfs.append(GTFile(file))
cvsdist 2b5159
        master=GTMaster(gtfs)
cvsdist 31ff37
        res=str(master)
cvsdist 31ff37
    elif("--append") in sys.argv:
cvsdist 31ff37
        file=sys.argv[sys.argv.index("--append")+1]
cvsdist 31ff37
        file2=sys.argv[sys.argv.index("--append")+2]
cvsdist 31ff37
        gtf=GTFile(file)
cvsdist 31ff37
        gtf2=GTFile(file2)
cvsdist 31ff37
        gtf.append(gtf2)
cvsdist 31ff37
        res=str(gtf)
cvsdist 31ff37
    else:
cvsdist b3c85c
        #print "Not implemented: "+str(sys.argv)
cvsdist b3c85c
        print "\
cvsdist b3c85c
Usage: ", str(sys.argv[0])," [OPTION] file.po [ref.po]\n\
cvsdist b3c85c
This program can be used to alter .po files in ways no sane mind would think about.\n\
cvsdist b3c85c
      -o                result will be written to FILE\n\
cvsdist b3c85c
      --invert          invert a po file by switching msgid and msgstr\n\
cvsdist b3c85c
      --master          join any number of files in a master-formatted catalog\n\
cvsdist b3c85c
      --empty           empty the contents of the .po file, creating a .pot\n\
cvsdist b3c85c
      --append          append entries from ref.po that don't exist in file.po\n\
cvsdist b3c85c
\n\
cvsdist b3c85c
Note: It is just a replacement of msghack for backward support.\n\
cvsdist b3c85c
"
cvsdist 31ff37
        sys.exit(1)
cvsdist 31ff37
    if not output:
cvsdist 31ff37
        print res
cvsdist 31ff37
    else:
cvsdist 31ff37
        file=open(output,"w")
cvsdist 31ff37
        file.write(res)
cvsdist 31ff37
    sys.exit(0)