]> git.evergreen-ils.org Git - working/Evergreen.git/blob - build/i18n/scripts/fieldmapper.py
Make unit tests less fragile.
[working/Evergreen.git] / build / i18n / scripts / fieldmapper.py
1 #!/usr/bin/env python
2 # fieldmapper.py
3 """
4 This class enables translation of Evergreen's fieldmapper IDL XML.
5
6 Requires polib from http://polib.googlecode.com
7 """
8 # Copyright 2007 Dan Scott <dscott@laurentian.ca>
9 #
10 # This program is free software; you can redistribute it and/or
11 # modify it under the terms of the GNU General Public License
12 # as published by the Free Software Foundation; either version 2
13 # of the License, or (at your option) any later version.
14 #
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 # GNU General Public License for more details.
19
20 import basel10n
21 import optparse
22 import polib
23 import sys
24 import xml.sax
25 import xml.sax.handler
26
27 class IDL(basel10n.BaseL10N):
28     """
29     This class provides methods for extracting translatable strings from
30     Evergreen's fieldmapper IDL reporter:label attributes, generating a
31     translatable POT file, reading translated PO files, and generating
32     an updated fm_IDL.xml file with the additional language strings.
33     """
34
35     def __init__(self):
36         self.pot = None
37         basel10n.BaseL10N.__init__(self)
38         self.idl = ''
39         self.definitions = []
40
41     def loadpo(self, potfile):
42         """
43         Load a POT file
44         """
45         basel10n.BaseL10N.loadpo(self, potfile)
46
47     def pothead(self):
48         """
49         Initialize POT metadata
50         """
51         basel10n.BaseL10N.pothead(self)
52
53     def savepot(self, outfile):
54         """
55         Write a POT file
56         """
57         basel10n.BaseL10N.savepot(self, outfile)
58
59     def get_strings(self, source):
60         """
61         Extracts translatable strings from the reporter:label attributes
62         in Evergreen's fieldmapper IDL file.
63         """
64         self.pothead()
65
66         locator = xml.sax.xmlreader.Locator()
67         parser = xml.sax.make_parser()
68         handler = IDLHandler()
69         handler.setDocumentLocator(locator)
70         parser.setContentHandler(handler)
71         parser.parse(source)
72
73         for entity in handler.entities:
74             poe = polib.POEntry()
75             poe.occurences = handler.entities[entity]
76             poe.msgid = entity
77             self.pot.append(poe)
78         self.idl = handler.entityized
79
80     def create_entity(self):
81         """
82         Creates an entity definition file based on a translated PO file.
83         """
84         entity = '<!ENTITY %s "%s">'
85         for entry in self.pot:
86             for name in entry.occurences:
87                 if entry.msgstr == '':
88                     # No translation available; use the en-US definition
89                     self.definitions.append(entity % (name[0], entry.msgid))
90                 else:
91                     self.definitions.append(entity % (name[0], entry.msgstr))
92
93 class IDLHandler(xml.sax.handler.ContentHandler):
94     """
95     Parses a fieldmapper IDL file to get at reporter:label and name attributes.
96     Generates a list of entity definitions and their values, as well as an
97     entity-ized version of the fieldmapper IDL.
98     """
99
100     def __init__(self):
101         xml.sax.handler.ContentHandler.__init__(self)
102         self.entities = dict()
103         self.classid = None
104         self.entityized = u"""<?xml version="1.0" encoding="utf-8"?>
105 <!DOCTYPE fieldmapper [
106     <!--#include virtual="/opac/locale/${locale}/fm_IDL.dtd"--> 
107 ]>
108 """
109         self.locator = None
110
111     def setDocumentLocator(self, locator):
112         """
113         Override setDocumentLocator so we can track line numbers
114         """
115         self.locator = locator
116
117     def startElement(self, name, attributes):
118         """
119         Return the reporter:label or name attribute value for each class
120         or field element.
121         """
122         entity = None
123         lineno = self.locator.getLineNumber()
124         if name == 'class':
125             self.classid = attributes['id']
126         if attributes.has_key('reporter:label'):
127             if name == 'class':
128                 entity = "%s.%s.label" % (name, self.classid)
129             elif name == 'field':
130                 entity = "%s.%s.%s.label" % (name, self.classid, \
131                     attributes['name'])
132             label = attributes['reporter:label']
133             if not self.entities.has_key(label):
134                 self.entities[label] = [(str(entity), lineno)]
135             else:
136                 self.entities[label].append((str(entity), lineno))
137
138         # Now we'll render an entity-ized version of this element
139         element = "<%s" % (name)
140         for att in attributes.keys():
141             # Replace reporter:label attribute values with entities
142             if att == 'reporter:label':
143                 element = element + " %s='&%s;'" % (att, entity) 
144             else:
145                 element = element + " %s='%s'" % (att, attributes[att])
146
147         # field and link elements are empty elements
148         if name == 'field' or name == 'link':
149             element = element + " />"
150         else:
151             element = element + ">"
152         self.entityized = self.entityized + element
153
154     def characters(self, content):
155         """
156         Shove character data into the entityized IDL file
157         """
158         self.entityized = self.entityized + content
159
160     def endElement(self, name):
161         """
162         field and link elements are empty elements
163         """
164         if name == 'field' or name == 'link':
165             pass
166         else:
167             self.entityized = self.entityized + "</%s>" % (name)
168
169 def main():
170     """
171     Determine what action to take
172     """
173     opts = optparse.OptionParser()
174     opts.add_option('-p', '--pot', action='store', \
175         help='Create a POT file from the specified fieldmapper IDL file', \
176         metavar='FILE')
177     opts.add_option('-c', '--convert', action='store', \
178         help='Create a fieldmapper FILE that uses entities instead of text ' \
179         'strings for field labels and names', metavar='FILE')
180     opts.add_option('-e', '--entity', action='store', \
181         help='Create an entity definition from a translated PO FILE', \
182         metavar='FILE')
183     opts.add_option('-o', '--output', dest='outfile', \
184         help='Write output to FILE (defaults to STDOUT)', metavar='FILE')
185     (options, args) = opts.parse_args()
186
187     pot = IDL()
188     # Generate a new POT file from the fieldmapper IDL
189     if options.pot:
190         pot.get_strings(options.pot)
191         if options.outfile:
192             pot.savepot(options.outfile)
193         else:
194             sys.stdout.write(pot.pot.__str__())
195     # Generate an entity file from a PO file
196     elif options.entity:
197         pot.loadpo(options.entity)
198         pot.create_entity()
199         if options.outfile:
200             outfile = open(options.outfile, 'w')
201             for entity in pot.definitions: 
202                 outfile.write(entity + "\n")
203         else:
204             for entity in pot.definitions:
205                 print(entity)
206     # Generate an entity-ized fieldmapper IDL file
207     elif options.convert:
208         pot.get_strings(options.convert)
209         if options.outfile:
210             outfile = open(options.outfile, 'w')
211             outfile.write(pot.idl)
212         else:
213             sys.stdout.write(pot.idl)
214     # No options were recognized - print help and bail
215     else:
216         opts.print_help()
217
218 if __name__ == '__main__':
219     main()