]> git.evergreen-ils.org Git - Evergreen.git/blob - build/i18n/scripts/ils_events.py
0efb37d6bd80a2fcccd30d11447a852e02ce540b
[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 optparse
37 import polib
38 import re
39 import sys
40 import xml.sax
41 import xml.sax.handler
42
43 class ILSEvents(basel10n.BaseL10N):
44     """
45     This class provides methods for extracting translatable strings from
46     Evergreen's ils_events XML file, generating a translatable POT file,
47     reading translated PO files, and generating an updated ils_events.xml
48     file with the additional language strings.
49     """
50
51     def __init__(self):
52         self.pot = None
53         basel10n.BaseL10N.__init__(self)
54         self.definitions = []
55         self.locale = None
56
57     def get_strings(self, source):
58         """
59         Extracts translatable strings from the //desc[@lang='en-US'] attributes
60         in Evergreen's ils_events.xml file.
61         """
62         self.pothead()
63
64         locator = xml.sax.xmlreader.Locator()
65         parser = xml.sax.make_parser()
66         handler = ILSEventHandler()
67         handler.setDocumentLocator(locator)
68         parser.setContentHandler(handler)
69         parser.parse(source)
70
71         for entry in handler.events:
72             poe = polib.POEntry()
73             poe.occurrences = handler.events[entry]
74             poe.msgid = entry
75             self.pot.append(poe)
76
77     def create_events(self):
78         """
79         Creates an ILS events XML file based on a translated PO file.
80
81         Each PO entry has one or more file comment with the following structure:
82
83         #: numcode.textcode:lineno
84         """
85
86         event = """    <event code='%d' textcode='%s'>
87         <desc xml:lang='%s'>%s</desc>\n    </event>"""
88
89         # We should generate this in a real XML way, rather than faking it
90         # But we'll fake it for now
91         for entry in self.pot:
92             for name in entry.occurrences:
93                 # regex name here
94                 pat = re.compile(r'(\d+)\.(\w+)').match(name[0])
95                 numcode = pat.group(1)
96                 textcode = pat.group(2)
97
98                 if entry.msgstr == '':
99                     # No translation available; use the en-US definition
100                     self.definitions.append(event % (int(numcode), textcode, self.locale, entry.msgid))
101                 else:
102                     self.definitions.append(event % (int(numcode), textcode, self.locale, entry.msgstr))
103
104 class ILSEventHandler(xml.sax.handler.ContentHandler):
105     """
106     Parses an ils_events.xml file to get at event[@code] attributes and
107     the contained desc[@lang='en-US'] elements.
108
109     Generates a list of events and their English descriptions.
110     """
111
112     def __init__(self):
113         xml.sax.handler.ContentHandler.__init__(self)
114         self.events = dict()
115         self.desc = u''
116         self.en_us_flag = False
117         self.numcode = None
118         self.textcode = None
119         self.locator = None
120
121     def setDocumentLocator(self, locator):
122         """
123         Override setDocumentLocator so we can track line numbers
124         """
125         self.locator = locator
126
127     def startElement(self, name, attributes):
128         """
129         Grab the event code attribute value for each class
130         or field element.
131         """
132         if name == 'event':
133             self.numcode = attributes['code']
134             self.textcode = attributes['textcode']
135         if name == 'desc' and attributes['xml:lang'] == 'en-US':
136             self.en_us_flag = True
137
138     def characters(self, content):
139         """
140         Build the ILS event description
141         """
142         if self.en_us_flag is True and content is not None:
143             self.desc += content
144
145     def endElement(self, name):
146         """
147         Generate the event with the closed description
148         """
149         if name == 'desc' and self.en_us_flag is True:
150             lineno = self.locator.getLineNumber()
151             event = "%d.%s" % (int(self.numcode), self.textcode)
152             if self.events.has_key(self.desc):
153                 self.events[self.desc].append([str(event), lineno])
154             else:
155                 self.events[self.desc] = [[str(event), lineno]]
156
157             # Reset event values
158             self.desc = u''
159             self.en_us_flag = False
160             self.numcode = None
161             self.textcode = None
162
163 def main():
164     """
165     Determine what action to take
166     """
167     opts = optparse.OptionParser()
168     opts.add_option('-p', '--pot', action='store', \
169         help='Create a POT file from the specified ils_events.xml file', \
170         metavar='FILE')
171     opts.add_option('-c', '--create', action='store', \
172         help='Create an ils_events.xml file from a translated PO FILE', \
173         metavar='FILE')
174     opts.add_option('-l', '--locale', action='store', \
175         help='Locale of the ils_events.xml file that will be generated', \
176         metavar='FILE')
177     opts.add_option('-o', '--output', dest='outfile', \
178         help='Write output to FILE (defaults to STDOUT)', metavar='FILE')
179     (options, args) = opts.parse_args()
180
181     pot = ILSEvents()
182
183     # Generate a new POT file from the ils_events.xml file
184     if options.pot:
185         pot.get_strings(options.pot)
186         if options.outfile:
187             pot.savepot(options.outfile)
188         else:
189             sys.stdout.write(pot.pot.__str__())
190
191     # Generate an ils_events.xml file from a PO file
192     elif options.create:
193         if options.locale:
194             pot.locale = options.locale
195         else:
196             opts.error('Must specify an output locale to create an XML file')
197
198         head = """<?xml version="1.0" encoding="utf-8"?>
199 <ils_events>
200         """
201         
202         tail = "</ils_events>"
203
204         pot.loadpo(options.create)
205         pot.create_events()
206         if options.outfile:
207             outfile = open(options.outfile, 'w')
208             outfile.write(head)
209             for event in pot.definitions: 
210                 outfile.write(event + "\n")
211             outfile.write(tail)
212         else:
213             print(head)
214             for event in pot.definitions:
215                 print(event)
216             print(tail)
217
218     # No options were recognized - print help and bail
219     else:
220         opts.print_help()
221
222 if __name__ == '__main__':
223     main()