]> git.evergreen-ils.org Git - working/Evergreen.git/blob - build/i18n/scripts/fieldmapper.py
Split out localization base class into basel10n.py
[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         self.locator = None
109
110     def setDocumentLocator(self, locator):
111         """
112         Override setDocumentLocator so we can track line numbers
113         """
114         self.locator = locator
115
116     def startElement(self, name, attributes):
117         """
118         Return the reporter:label or name attribute value for each class
119         or field element.
120         """
121         entity = None
122         lineno = self.locator.getLineNumber()
123         if name == 'class':
124             self.classid = attributes['id']
125         if attributes.has_key('reporter:label'):
126             if name == 'class':
127                 entity = "%s.%s.label" % (name, self.classid)
128             elif name == 'field':
129                 entity = "%s.%s.%s.label" % (name, self.classid, \
130                     attributes['name'])
131             label = attributes['reporter:label']
132             if not self.entities.has_key(label):
133                 self.entities[label] = [(lineno, str(entity))]
134             else:
135                 self.entities[label].append((lineno, str(entity)))
136
137         # Now we'll render an entity-ized version of this element
138         element = "<%s" % (name)
139         for att in attributes.keys():
140             # Replace reporter:label attribute values with entities
141             if att == 'reporter:label':
142                 element = element + " %s='&%s;'" % (att, entity) 
143             else:
144                 element = element + " %s='%s'" % (att, attributes[att])
145
146         # field and link elements are empty elements
147         if name == 'field' or name == 'link':
148             element = element + " />"
149         else:
150             element = element + ">"
151         self.entityized = self.entityized + element
152
153     def characters(self, content):
154         """
155         Shove character data into the entityized IDL file
156         """
157         self.entityized = self.entityized + content
158
159     def endElement(self, name):
160         """
161         field and link elements are empty elements
162         """
163         if name == 'field' or name == 'link':
164             pass
165         else:
166             self.entityized = self.entityized + "</%s>" % (name)
167
168 def main():
169     """
170     Determine what action to take
171     """
172     opts = optparse.OptionParser()
173     opts.add_option('-p', '--pot', action='store', \
174         help='Create a POT file from the specified fieldmapper IDL file', \
175         metavar='FILE')
176     opts.add_option('-c', '--convert', action='store', \
177         help='Create a fieldmapper FILE that uses entities instead of text ' \
178         'strings for field labels and names', metavar='FILE')
179     opts.add_option('-e', '--entity', action='store', \
180         help='Create an entity definition from a translated PO FILE', \
181         metavar='FILE')
182     opts.add_option('-o', '--output', dest='outfile', \
183         help='Write output to FILE (defaults to STDOUT)', metavar='FILE')
184     (options, args) = opts.parse_args()
185
186     pot = IDL()
187     # Generate a new POT file from the fieldmapper IDL
188     if options.pot:
189         pot.get_strings(options.pot)
190         if options.outfile:
191             pot.savepot(options.outfile)
192         else:
193             sys.stdout.write(pot.pot.__str__())
194     # Generate an entity file from a PO file
195     elif options.entity:
196         pot.loadpo(options.entity)
197         pot.create_entity()
198         if options.outfile:
199             outfile = open(options.outfile, 'w')
200             for entity in pot.definitions: 
201                 outfile.write(entity)
202         else:
203             for entity in pot.definitions:
204                 print(entity)
205     # Generate an entity-ized fieldmapper IDL file
206     elif options.convert:
207         pot.get_strings(options.convert)
208         if options.outfile:
209             outfile = open(options.outfile)
210             outfile.write(pot.idl)
211         else:
212             sys.stdout.write(pot.idl)
213     # No options were recognized - print help and bail
214     else:
215         opts.print_help()
216
217 if __name__ == '__main__':
218     main()