]> git.evergreen-ils.org Git - working/Evergreen.git/blob - build/i18n/scripts/fieldmapper.py
Forward-port r15406 from rel_1_6: Fix occurrences parsing in i18n build
[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 codecs
22 import optparse
23 import polib
24 import sys
25 import xml.sax
26 import xml.sax.handler
27
28 class IDL(basel10n.BaseL10N):
29     """
30     This class provides methods for extracting translatable strings from
31     Evergreen's fieldmapper IDL reporter:label attributes, generating a
32     translatable POT file, reading translated PO files, and generating
33     an updated fm_IDL.xml file with the additional language strings.
34     """
35
36     def __init__(self):
37         self.pot = None
38         basel10n.BaseL10N.__init__(self)
39         self.idl = ''
40         self.definitions = []
41
42     def get_strings(self, source):
43         """
44         Extracts translatable strings from the reporter:label attributes
45         in Evergreen's fieldmapper IDL file.
46         """
47         self.pothead()
48
49         locator = xml.sax.xmlreader.Locator()
50         parser = xml.sax.make_parser()
51         handler = IDLHandler()
52         handler.setDocumentLocator(locator)
53         parser.setContentHandler(handler)
54         parser.parse(source)
55
56         for entity in handler.entities:
57             poe = polib.POEntry()
58             poe.occurrences = handler.entities[entity]
59             poe.msgid = entity
60             self.pot.append(poe)
61         self.idl = handler.entityized
62
63     def create_entity(self):
64         """
65         Creates an entity definition file based on a translated PO file.
66         """
67         entity = '<!ENTITY %s "%s">'
68         for entry in self.pot:
69             for name in entry.occurrences:
70                 if entry.msgstr == '':
71                     # No translation available; use the en-US definition
72                     self.definitions.append(entity % (name[0], entry.msgid))
73                 else:
74                     self.definitions.append(entity % (name[0], entry.msgstr))
75
76 class IDLHandler(xml.sax.handler.ContentHandler):
77     """
78     Parses a fieldmapper IDL file to get at reporter:label and name attributes.
79     Generates a list of entity definitions and their values, as well as an
80     entity-ized version of the fieldmapper IDL.
81     """
82
83     def __init__(self):
84         xml.sax.handler.ContentHandler.__init__(self)
85         self.entities = dict()
86         self.classid = None
87         self.entityized = u"""<?xml version="1.0" encoding="utf-8"?>
88 <!DOCTYPE fieldmapper [
89     <!--#include virtual="/opac/locale/${locale}/fm_IDL.dtd"--> 
90 ]>
91 """
92         self.locator = None
93
94     def setDocumentLocator(self, locator):
95         """
96         Override setDocumentLocator so we can track line numbers
97         """
98         self.locator = locator
99
100     def startElement(self, name, attributes):
101         """
102         Return the reporter:label or name attribute value for each class
103         field, or link element.
104         """
105         entity = None
106         lineno = self.locator.getLineNumber()
107         if name == 'class':
108             self.classid = attributes['id']
109         if attributes.has_key('reporter:label'):
110             if name == 'class':
111                 entity = "%s.%s.label" % (name, self.classid)
112             elif name == 'field':
113                 entity = "%s.%s.%s.label" % (name, self.classid, \
114                     attributes['name'])
115             elif name == 'link':
116                 entity = "%s.%s.%s.label" % (name, self.classid, \
117                     attributes['field'])
118             label = attributes['reporter:label']
119             if not self.entities.has_key(label):
120                 self.entities[label] = [(str(entity), lineno)]
121             else:
122                 self.entities[label].append((str(entity), lineno))
123
124         # Now we'll render an entity-ized version of this element
125         element = "<%s" % (name)
126         for att in attributes.keys():
127             # Replace reporter:label attribute values with entities
128             if att == 'reporter:label':
129                 element = element + " %s='&%s;'" % (att, entity) 
130             else:
131                 element = element + " %s='%s'" % (att, attributes[att])
132
133         # field and link elements are empty elements
134         if name == 'field' or name == 'link':
135             element = element + " />"
136         else:
137             element = element + ">"
138         self.entityized = self.entityized + element
139
140     def characters(self, content):
141         """
142         Shove character data into the entityized IDL file
143         """
144         self.entityized = self.entityized + xml.sax.saxutils.escape(content)
145
146     def endElement(self, name):
147         """
148         field and link elements are empty elements
149         """
150         if name == 'field' or name == 'link':
151             pass
152         else:
153             self.entityized = self.entityized + "</%s>" % (name)
154
155 def main():
156     """
157     Determine what action to take
158     """
159     opts = optparse.OptionParser()
160     opts.add_option('-p', '--pot', action='store', \
161         help='Create a POT file from the specified fieldmapper IDL file', \
162         metavar='FILE')
163     opts.add_option('-c', '--convert', action='store', \
164         help='Create a fieldmapper FILE that uses entities instead of text ' \
165         'strings for field labels and names', metavar='FILE')
166     opts.add_option('-e', '--entity', action='store', \
167         help='Create an entity definition from a translated PO FILE', \
168         metavar='FILE')
169     opts.add_option('-o', '--output', dest='outfile', \
170         help='Write output to FILE (defaults to STDOUT)', metavar='FILE')
171     (options, args) = opts.parse_args()
172
173     pot = IDL()
174     # Generate a new POT file from the fieldmapper IDL
175     if options.pot:
176         pot.get_strings(options.pot)
177         if options.outfile:
178             pot.savepot(options.outfile)
179         else:
180             sys.stdout.write(pot.pot.__str__())
181     # Generate an entity file from a PO file
182     elif options.entity:
183         pot.loadpo(options.entity)
184         pot.create_entity()
185         if options.outfile:
186             outfile = codecs.open(options.outfile, encoding='utf-8', mode='w')
187             for entity in pot.definitions: 
188                 outfile.write(entity + "\n")
189         else:
190             for entity in pot.definitions:
191                 print(entity)
192     # Generate an entity-ized fieldmapper IDL file
193     elif options.convert:
194         pot.get_strings(options.convert)
195         if options.outfile:
196             outfile = codecs.open(options.outfile, encoding='utf-8', mode='w')
197             outfile.write(pot.idl)
198         else:
199             sys.stdout.write(pot.idl)
200     # No options were recognized - print help and bail
201     else:
202         opts.print_help()
203
204 if __name__ == '__main__':
205     main()