]> git.evergreen-ils.org Git - working/Evergreen.git/blob - build/i18n/scripts/ils_events.py
Bring the magic of codecs to the i18n script masses
[working/Evergreen.git] / build / i18n / scripts / ils_events.py
1 #!/usr/bin/env python
2 # ils_events.py
3 """
4 This class enables translation of Evergreen's ils_events XML file.
5
6 Requires polib from http://polib.googlecode.com
7
8 Source event definitions are structured as follows:
9 <ils_events>
10     <event code='1' textcode='UNKNOWN'>
11         <desc xml:lang="en-US">Placeholder event.  Used for development only</desc>
12      </event>
13 </ils_events>
14
15 This generates an updated file with the following structure:
16 <ils_events>
17     <event code='1' textcode='UNKNOWN'>
18         <desc xml:lang="en-US">Placeholder event.  Used for development only</desc>
19         <desc xml:lang="fr-CA">Exemple - seulement developpement</desc>
20     </event>
21 </ils_events>
22 """
23 # Copyright 2007 Dan Scott <dscott@laurentian.ca>
24 #
25 # This program is free software; you can redistribute it and/or
26 # modify it under the terms of the GNU General Public License
27 # as published by the Free Software Foundation; either version 2
28 # of the License, or (at your option) any later version.
29 #
30 # This program is distributed in the hope that it will be useful,
31 # but WITHOUT ANY WARRANTY; without even the implied warranty of
32 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
33 # GNU General Public License for more details.
34
35 import basel10n
36 import codecs
37 import optparse
38 import polib
39 import re
40 import sys
41 import xml.sax
42 import xml.sax.handler
43
44 class ILSEvents(basel10n.BaseL10N):
45     """
46     This class provides methods for extracting translatable strings from
47     Evergreen's ils_events XML file, generating a translatable POT file,
48     reading translated PO files, and generating an updated ils_events.xml
49     file with the additional language strings.
50     """
51
52     def __init__(self):
53         self.pot = None
54         basel10n.BaseL10N.__init__(self)
55         self.definitions = []
56         self.locale = None
57
58     def get_strings(self, source):
59         """
60         Extracts translatable strings from the //desc[@lang='en-US'] attributes
61         in Evergreen's ils_events.xml file.
62         """
63         self.pothead()
64
65         locator = xml.sax.xmlreader.Locator()
66         parser = xml.sax.make_parser()
67         handler = ILSEventHandler()
68         handler.setDocumentLocator(locator)
69         parser.setContentHandler(handler)
70         parser.parse(source)
71
72         for entry in handler.events:
73             poe = polib.POEntry()
74             poe.occurrences = handler.events[entry]
75             poe.msgid = entry
76             self.pot.append(poe)
77
78     def create_events(self):
79         """
80         Creates an ILS events XML file based on a translated PO file.
81
82         Each PO entry has one or more file comment with the following structure:
83
84         #: numcode.textcode:lineno
85         """
86
87         event = """    <event code='%d' textcode='%s'>
88         <desc xml:lang='%s'>%s</desc>\n    </event>"""
89
90         # We should generate this in a real XML way, rather than faking it
91         # But we'll fake it for now
92         for entry in self.pot:
93             for name in entry.occurrences:
94                 # regex name here
95                 pat = re.compile(r'(\d+)\.(\w+)').match(name[0])
96                 numcode = pat.group(1)
97                 textcode = pat.group(2)
98
99                 if entry.msgstr == '':
100                     # No translation available; use the en-US definition
101                     self.definitions.append(event % (int(numcode), textcode, self.locale, entry.msgid))
102                 else:
103                     self.definitions.append(event % (int(numcode), textcode, self.locale, entry.msgstr))
104
105 class ILSEventHandler(xml.sax.handler.ContentHandler):
106     """
107     Parses an ils_events.xml file to get at event[@code] attributes and
108     the contained desc[@lang='en-US'] elements.
109
110     Generates a list of events and their English descriptions.
111     """
112
113     def __init__(self):
114         xml.sax.handler.ContentHandler.__init__(self)
115         self.events = dict()
116         self.desc = u''
117         self.en_us_flag = False
118         self.numcode = None
119         self.textcode = None
120         self.locator = None
121
122     def setDocumentLocator(self, locator):
123         """
124         Override setDocumentLocator so we can track line numbers
125         """
126         self.locator = locator
127
128     def startElement(self, name, attributes):
129         """
130         Grab the event code attribute value for each class
131         or field element.
132         """
133         if name == 'event':
134             self.numcode = attributes['code']
135             self.textcode = attributes['textcode']
136         if name == 'desc' and attributes['xml:lang'] == 'en-US':
137             self.en_us_flag = True
138
139     def characters(self, content):
140         """
141         Build the ILS event description
142         """
143         if self.en_us_flag is True and content is not None:
144             self.desc += content
145
146     def endElement(self, name):
147         """
148         Generate the event with the closed description
149         """
150         if name == 'desc' and self.en_us_flag is True:
151             lineno = self.locator.getLineNumber()
152             event = "%d.%s" % (int(self.numcode), self.textcode)
153             if self.events.has_key(self.desc):
154                 self.events[self.desc].append([str(event), lineno])
155             else:
156                 self.events[self.desc] = [[str(event), lineno]]
157
158             # Reset event values
159             self.desc = u''
160             self.en_us_flag = False
161             self.numcode = None
162             self.textcode = None
163
164 def main():
165     """
166     Determine what action to take
167     """
168     opts = optparse.OptionParser()
169     opts.add_option('-p', '--pot', action='store', \
170         help='Create a POT file from the specified ils_events.xml file', \
171         metavar='FILE')
172     opts.add_option('-c', '--create', action='store', \
173         help='Create an ils_events.xml file from a translated PO FILE', \
174         metavar='FILE')
175     opts.add_option('-l', '--locale', action='store', \
176         help='Locale of the ils_events.xml file that will be generated', \
177         metavar='FILE')
178     opts.add_option('-o', '--output', dest='outfile', \
179         help='Write output to FILE (defaults to STDOUT)', metavar='FILE')
180     (options, args) = opts.parse_args()
181
182     pot = ILSEvents()
183
184     # Generate a new POT file from the ils_events.xml file
185     if options.pot:
186         pot.get_strings(options.pot)
187         if options.outfile:
188             pot.savepot(options.outfile)
189         else:
190             sys.stdout.write(pot.pot.__str__())
191
192     # Generate an ils_events.xml file from a PO file
193     elif options.create:
194         if options.locale:
195             pot.locale = options.locale
196         else:
197             opts.error('Must specify an output locale to create an XML file')
198
199         head = """<?xml version="1.0" encoding="utf-8"?>
200 <ils_events>
201         """
202         
203         tail = "</ils_events>"
204
205         pot.loadpo(options.create)
206         pot.create_events()
207         if options.outfile:
208             outfile = codecs.open(options.outfile, encoding='utf-8', mode='w')
209             outfile.write(head)
210             for event in pot.definitions: 
211                 outfile.write(event + "\n")
212             outfile.write(tail)
213         else:
214             print(head)
215             for event in pot.definitions:
216                 print(event)
217             print(tail)
218
219     # No options were recognized - print help and bail
220     else:
221         opts.print_help()
222
223 if __name__ == '__main__':
224     main()