summaryrefslogtreecommitdiff
path: root/itstool.in
diff options
context:
space:
mode:
authorShaun McCance <shaunm@gnome.org>2011-04-30 14:56:07 -0400
committerShaun McCance <shaunm@gnome.org>2011-04-30 14:56:07 -0400
commit4aac006a9f0350f3dbd6588cdb4254e8b895ec78 (patch)
treea4151555c7a28e33d35ef137c012fbe7cf329f4c /itstool.in
parent6d60e6e1d14bc6b62e0c43cb0781d562d23dd684 (diff)
downloaditstool-4aac006a9f0350f3dbd6588cdb4254e8b895ec78.tar.gz
Added --version
Diffstat (limited to 'itstool.in')
-rwxr-xr-xitstool.in797
1 files changed, 797 insertions, 0 deletions
diff --git a/itstool.in b/itstool.in
new file mode 100755
index 0000000..69f5640
--- /dev/null
+++ b/itstool.in
@@ -0,0 +1,797 @@
+#!/usr/bin/env python
+
+VERSION="@VERSION@"
+
+import gettext
+import hashlib
+import libxml2
+import optparse
+import os
+import os.path
+import re
+import sys
+import time
+
+NS_ITS = 'http://www.w3.org/2005/11/its'
+NS_ITST = 'http://itstool.org/extensions/'
+NS_BLANK = 'http://itstool.org/extensions/blank/'
+NS_XLINK = 'http://www.w3.org/1999/xlink'
+
+class NoneTranslations:
+ def gettext(self, message):
+ return None
+
+ def lgettext(self, message):
+ return None
+
+ def ngettext(self, msgid1, msgid2, n):
+ return None
+
+ def lngettext(self, msgid1, msgid2, n):
+ return None
+
+ def ugettext(self, message):
+ return None
+
+ def ungettext(self, msgid1, msgid2, n):
+ return None
+
+
+class MessageList (object):
+ def __init__ (self):
+ self._messages = []
+ self._by_node = {}
+ self._has_credits = False
+
+ def add_message (self, message, node):
+ self._messages.append (message)
+ if node is not None:
+ self._by_node[node] = message
+
+ def add_credits(self):
+ if self._has_credits:
+ return
+ msg = Message()
+ msg.set_context('_')
+ msg.add_text('translator-credits')
+ msg.add_comment('Put one translator per line, in the form NAME <EMAIL>, YEAR1, YEAR2')
+ self._messages.append(msg)
+ self._has_credits = True
+
+ def get_message_by_node (self, node):
+ return self._by_node.get(node, None)
+
+ def get_nodes_with_messages (self):
+ return self._by_node.keys()
+
+ def output (self, out):
+ msgs = []
+ msgdict = {}
+ for msg in self._messages:
+ key = (msg.get_context(), msg.get_string())
+ if msgdict.has_key(key):
+ for source in msg.get_sources():
+ msgdict[key].add_source(source)
+ for comment in msg.get_comments():
+ msgdict[key].add_comment(comment)
+ if msg.get_preserve_space():
+ msgdict[key].set_preserve_space()
+ else:
+ msgs.append(msg)
+ msgdict[key] = msg
+ out.write('msgid ""\n')
+ out.write('msgstr ""\n')
+ out.write('"Project-Id-Version: PACKAGE VERSION\\n"\n')
+ out.write('"POT-Creation-Date: %s\\n"\n' % time.strftime("%Y-%m-%d %H:%M%z"))
+ out.write('"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n"\n')
+ out.write('"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n"\n')
+ out.write('"Language-Team: LANGUAGE <LL@li.org>\\n"\n')
+ out.write('"MIME-Version: 1.0\\n"\n')
+ out.write('"Content-Type: text/plain; charset=UTF-8\\n"\n')
+ out.write('"Content-Transfer-Encoding: 8bit\\n"\n')
+ out.write('\n')
+ for msg in msgs:
+ out.write(msg.format())
+ out.write('\n')
+
+
+class Message (object):
+ def __init__ (self):
+ self._message = []
+ self._empty = True
+ self._ctxt = None
+ self._placeholders = []
+ self._sources = []
+ self._comments = []
+ self._preserve = False
+
+ class Placeholder (object):
+ def __init__ (self, node):
+ self.node = node
+ self.name = node.name
+
+ def escape (self, text):
+ return text.replace('\\','\\\\').replace('"', "\\\"").replace("\n","\\n").replace("\t","\\t")
+
+ def add_text (self, text):
+ if len(self._message) == 0 or not(isinstance(self._message[-1], basestring)):
+ self._message.append('')
+ self._message[-1] += text.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
+ if re.sub('\s+', ' ', text).strip() != '':
+ self._empty = False
+
+ def add_placeholder (self, node):
+ holder = Message.Placeholder(node)
+ self._placeholders.append(holder)
+ self._message.append(holder)
+
+ def get_placeholder (self, name):
+ placeholder = 1
+ for holder in self._placeholders:
+ holdername = '%s-%i' % (holder.name, placeholder)
+ if holdername == name:
+ return holder
+ placeholder += 1
+
+ def add_start_tag (self, node):
+ if len(self._message) == 0 or not(isinstance(self._message[-1], basestring)):
+ self._message.append('')
+ self._message[-1] += ('<%s' % node.name)
+ if node.properties is not None:
+ for prop in node.properties:
+ if prop.type == 'attribute':
+ name = prop.name
+ if prop.ns() is not None:
+ name = prop.ns().name + ':' + name
+ atval = prop.content
+ atval = atval.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;')
+ self._message += " %s=\"%s\"" % (name, atval)
+ if node.children is not None:
+ self._message[-1] += '>'
+ else:
+ self._message[-1] += '/>'
+
+ def add_end_tag (self, node):
+ if node.children is not None:
+ if len(self._message) == 0 or not(isinstance(self._message[-1], basestring)):
+ self._message.append('')
+ self._message[-1] += ('</%s>' % node.name)
+
+ def is_empty (self):
+ return self._empty
+
+ def get_context (self):
+ return self._ctxt
+
+ def set_context (self, ctxt):
+ self._ctxt = ctxt
+
+ def add_source (self, source):
+ self._sources.append(source)
+
+ def get_sources (self):
+ return self._sources
+
+ def add_comment (self, comment):
+ self._comments.append(comment)
+
+ def get_comments (self):
+ return self._comments
+
+ def get_string (self):
+ message = ''
+ placeholder = 1
+ for msg in self._message:
+ if isinstance(msg, basestring):
+ message += msg
+ elif isinstance(msg, Message.Placeholder):
+ message += '<_:%s-%i/>' % (msg.name, placeholder)
+ placeholder += 1
+ if not self._preserve:
+ message = re.sub('\s+', ' ', message).strip()
+ return message
+
+ def get_preserve_space (self):
+ return self._preserve
+
+ def set_preserve_space (self, preserve=True):
+ self._preserve = preserve
+
+ def format (self):
+ ret = ''
+ for i in range(len(self._comments)):
+ if i != 0:
+ ret += '#.\n'
+ comment = self._comments[i]
+ while len(comment) > 72:
+ j = comment.rfind(' ', 0, 72)
+ if j == -1:
+ j = comment.find(' ')
+ if j == -1:
+ break
+ ret += '#. %s\n' % comment[:j]
+ comment = comment[j+1:]
+ ret += '#. %s\n' % comment
+ for source in self._sources:
+ ret += '#: %s\n' % source
+ if self._preserve:
+ ret += '#, no-wrap\n'
+ if self._ctxt is not None:
+ ret += 'msgctxt "%s"\n' % self._ctxt
+ message = self.get_string()
+ if self._preserve:
+ ret += 'msgid ""\n'
+ lines = message.split('\n')
+ for line, no in zip(lines, range(len(lines))):
+ if no == len(lines) - 1:
+ ret += '"%s"\n' % self.escape(line)
+ else:
+ ret += '"%s\\n"\n' % self.escape(line)
+ else:
+ ret += 'msgid "%s"\n' % self.escape(message)
+ ret += 'msgstr ""\n'
+ return ret
+
+
+def xml_child_iter (node):
+ child = node.children
+ while child is not None:
+ yield child
+ child = child.next
+
+def xml_is_ns_name (node, ns, name):
+ if node.type != 'element':
+ return False
+ return node.name == name and node.ns() is not None and node.ns().content == ns
+
+
+class Document (object):
+ def __init__ (self, filename, messages):
+ ctxt = libxml2.createFileParserCtxt(filename)
+ ctxt.lineNumbers(1)
+ ctxt.replaceEntities(1)
+ ctxt.parseDocument()
+ self._filename = filename
+ self._doc = ctxt.doc()
+ self._localrules = []
+ def pre_process (node):
+ for child in xml_child_iter(node):
+ if xml_is_ns_name(child, 'http://www.w3.org/2001/XInclude', 'include'):
+ if child.prop('parse') == 'text':
+ child.xincludeProcessTree()
+ elif xml_is_ns_name(child, NS_ITS, 'rules'):
+ if child.hasNsProp('href', NS_XLINK):
+ href = child.nsProp('href', NS_XLINK)
+ href = os.path.join(os.path.dirname(filename), href)
+ hctxt = libxml2.createFileParserCtxt(href)
+ hctxt.replaceEntities(1)
+ hctxt.parseDocument()
+ self._localrules.append(hctxt.doc().getRootElement())
+ else:
+ self._localrules.append(child)
+ pre_process(child)
+ pre_process(self._doc)
+ self._msgs = messages
+ self._its_translate_nodes = {}
+ self._its_within_text_nodes = {}
+ self._its_loc_notes = {}
+ self._itst_preserve_space_nodes = {}
+ self._its_lang = {}
+ self._itst_lang_attr = {}
+ self._itst_credits = None
+ self._itst_externals = []
+
+ def apply_its_rule(self, rule, xpath):
+ if rule.type != 'element':
+ return
+ if xml_is_ns_name(rule, NS_ITS, 'translateRule'):
+ if rule.prop('selector') is not None:
+ for node in xpath.xpathEval(rule.prop('selector')):
+ self._its_translate_nodes[node] = rule.prop('translate')
+ elif xml_is_ns_name(rule, NS_ITS, 'withinTextRule'):
+ if rule.prop('selector') is not None:
+ for node in xpath.xpathEval(rule.prop('selector')):
+ self._its_within_text_nodes[node] = rule.prop('withinText')
+ elif xml_is_ns_name(rule, NS_ITST, 'preserveSpaceRule'):
+ if rule.prop('selector') is not None:
+ for node in xpath.xpathEval(rule.prop('selector')):
+ self._itst_preserve_space_nodes[node] = rule.prop('preserveSpace')
+ elif xml_is_ns_name(rule, NS_ITS, 'locNoteRule'):
+ locnote = None
+ for child in xml_child_iter(rule):
+ if xml_is_ns_name(child, NS_ITS, 'locNote'):
+ locnote = re.sub('\s+', ' ', child.content).strip()
+ break
+ if locnote is None:
+ if rule.hasProp('locNoteRef'):
+ locnote = 'SEE: ' + re.sub('\s+', ' ', rule.prop('locNoteRef')).strip()
+ if rule.prop('selector') is not None:
+ for node in xpath.xpathEval(rule.prop('selector')):
+ if locnote is not None:
+ self._its_loc_notes[node] = locnote
+ else:
+ if rule.hasProp('locNotePointer'):
+ sel = rule.prop('locNotePointer')
+ ref = False
+ elif rule.hasProp('locNoteRefPointer'):
+ sel = rule.prop('locNoteRefPointer')
+ ref = True
+ else:
+ continue
+ try:
+ oldnode = xpath.contextNode()
+ except:
+ oldnode = None
+ xpath.setContextNode(node)
+ for note in xpath.xpathEval(sel):
+ cont = re.sub('\s+', ' ', note.content).strip()
+ if ref:
+ cont = 'SEE: ' + cont
+ self._its_loc_notes[node] = cont
+ break
+ xpath.setContextNode(oldnode)
+ elif xml_is_ns_name(rule, NS_ITS, 'langRule'):
+ if rule.prop('selector') is not None and rule.prop('langPointer') is not None:
+ for node in xpath.xpathEval(rule.prop('selector')):
+ try:
+ oldnode = xpath.contextNode()
+ except:
+ oldnode = None
+ xpath.setContextNode(node)
+ res = xpath.xpathEval(rule.prop('langPointer'))
+ if len(res) > 0:
+ self._its_lang[node] = res[0].content
+ # We need to construct language attributes, not just read
+ # language information. Technically, langPointer could be
+ # any XPath expression. But if it looks like an attribute
+ # accessor, just use the attribute name.
+ if rule.prop('langPointer')[0] == '@':
+ self._itst_lang_attr[node] = rule.prop('langPointer')[1:]
+ xpath.setContextNode(oldnode)
+ elif xml_is_ns_name(rule, NS_ITST, 'credits'):
+ if rule.prop('appendTo') is not None:
+ for node in xpath.xpathEval(rule.prop('appendTo')):
+ self._itst_credits = (node, rule)
+ break
+ elif xml_is_ns_name(rule, NS_ITST, 'externalRefRule'):
+ if rule.prop('selector') is not None and rule.prop('refPointer') is not None:
+ for node in xpath.xpathEval(rule.prop('selector')):
+ try:
+ oldnode = xpath.contextNode()
+ except:
+ oldnode = None
+ xpath.setContextNode(node)
+ res = xpath.xpathEval(rule.prop('refPointer'))
+ if len(res) > 0:
+ self._itst_externals.append((node, res[0].content))
+ xpath.setContextNode(oldnode)
+
+ def apply_its_rules (self):
+ dirs = []
+ ddir = os.getenv('XDG_DATA_HOME', '')
+ if ddir == '':
+ ddir = os.path.join(os.path.expanduser('~'), '.local', 'share')
+ dirs.append(ddir)
+ ddir = os.getenv('XDG_DATA_DIRS', '')
+ if ddir == '':
+ ddir = '/usr/local/share:/usr/share'
+ dirs.extend(ddir.split(':'))
+ ddone = {}
+ for ddir in dirs:
+ itsdir = os.path.join(ddir, 'itstool', 'its')
+ if not os.path.exists(itsdir):
+ continue
+ for dfile in os.listdir(itsdir):
+ if dfile.endswith('.its'):
+ if not ddone.get(dfile, False):
+ self.apply_its_file(os.path.join(itsdir, dfile))
+ ddone[dfile] = True
+ self.apply_local_its_rules()
+
+ def apply_its_file (self, filename):
+ doc = libxml2.parseFile(filename)
+ root = doc.getRootElement()
+ if not xml_is_ns_name(root, NS_ITS, 'rules'):
+ return
+ matched = True
+ for match in xml_child_iter(root):
+ if xml_is_ns_name(match, NS_ITST, 'match'):
+ matched = False
+ xpath = self._doc.xpathNewContext()
+ par = match
+ nss = {}
+ while par is not None:
+ nsdef = par.nsDefs()
+ while nsdef is not None:
+ if nsdef.name is not None:
+ if not nss.has_key(nsdef.name):
+ nss[nsdef.name] = nsdef.content
+ xpath.xpathRegisterNs(nsdef.name, nsdef.content)
+ nsdef = nsdef.next
+ par = par.parent
+ if match.hasProp('selector'):
+ if len(xpath.xpathEval(match.prop('selector'))) > 0:
+ matched = True
+ break
+ if matched == False:
+ return
+ for rule in xml_child_iter(root):
+ xpath = self._doc.xpathNewContext()
+ par = match
+ nss = {}
+ while par is not None:
+ nsdef = par.nsDefs()
+ while nsdef is not None:
+ if nsdef.name is not None:
+ if not nss.has_key(nsdef.name):
+ nss[nsdef.name] = nsdef.content
+ xpath.xpathRegisterNs(nsdef.name, nsdef.content)
+ nsdef = nsdef.next
+ par = par.parent
+ self.apply_its_rule(rule, xpath)
+
+ def apply_local_its_rules (self):
+ for rules in self._localrules:
+ def reg_ns(xpath, node):
+ if node.parent is not None:
+ reg_ns(xpath, node.parent)
+ nsdef = node.nsDefs()
+ while nsdef is not None:
+ if nsdef.name is not None:
+ xpath.xpathRegisterNs(nsdef.name, nsdef.content)
+ nsdef = nsdef.next
+ xpath = self._doc.xpathNewContext()
+ reg_ns(xpath, rules)
+ for rule in xml_child_iter(rules):
+ if rule.nsDefs() is not None:
+ rule_xpath = self._doc.xpathNewContent()
+ reg_ns(rule_xpath, rule)
+ else:
+ rule_xpath = xpath
+ self.apply_its_rule(rule, rule_xpath)
+
+ def _append_credits(self, parent, node, trdata):
+ if xml_is_ns_name(node, NS_ITST, 'for-each'):
+ select = node.prop('select')
+ if select == 'years':
+ for year in trdata[2].split(','):
+ for child in xml_child_iter(node):
+ self._append_credits(parent, child, trdata + (year.strip(),))
+ elif xml_is_ns_name(node, NS_ITST, 'value-of'):
+ select = node.prop('select')
+ val = None
+ if select == 'name':
+ val = trdata[0]
+ elif select == 'email':
+ val = trdata[1]
+ elif select == 'years':
+ val = trdata[2]
+ elif select == 'year' and len(trdata) == 4:
+ val = trdata[3]
+ if val is not None:
+ val = val.encode('utf-8')
+ parent.addContent(val)
+ else:
+ newnode = node.copyNode(2)
+ parent.addChild(newnode)
+ for child in xml_child_iter(node):
+ self._append_credits(newnode, child, trdata)
+
+ def merge_credits(self, translations, language, node):
+ if self._itst_credits is None:
+ return
+ # Dear Python, please implement pgettext.
+ # http://bugs.python.org/issue2504
+ # Sincerely, Shaun
+ trans = translations.ugettext('_\x04translator-credits')
+ if trans is None or trans == 'translator-credits':
+ return
+ regex = re.compile('(.*) \<(.*)\>, (.*)')
+ for credit in trans.split('\n'):
+ match = regex.match(credit)
+ if not match:
+ continue
+ trdata = match.groups()
+ for node in xml_child_iter(self._itst_credits[1]):
+ self._append_credits(self._itst_credits[0], node, trdata)
+
+ def merge_translations(self, translations, language, node=None):
+ is_root = False
+ if node is None:
+ is_root = True
+ self.generate_messages(comments=False)
+ node = self._doc.getRootElement()
+ if node is None or node.type != 'element':
+ return
+ if is_root:
+ self.merge_credits(translations, language, node)
+ msg = self._msgs.get_message_by_node(node)
+ if msg is None:
+ children = [child for child in xml_child_iter(node)]
+ for child in children:
+ self.merge_translations(translations, language, node=child)
+ else:
+ newnode = self.get_translated(node, translations)
+ if newnode != node:
+ node.replaceNode(newnode)
+ if is_root:
+ # Apply language attributes to untranslated nodes. We don't do
+ # this before processing, because then these attributes would
+ # be copied into the new nodes. We apply the attribute without
+ # checking whether it was translated, because any that were will
+ # just be floating around, unattached to a document.
+ for lcnode in self._msgs.get_nodes_with_messages():
+ attr = self._itst_lang_attr.get(lcnode)
+ if attr is None:
+ continue
+ origlang = None
+ lcpar = lcnode
+ while lcpar is not None:
+ origlang = self._its_lang.get(lcpar)
+ if origlang is not None:
+ break
+ lcpar = lcpar.parent
+ if origlang is not None:
+ lcnode.setProp(attr, origlang)
+ # And then set the language attribute on the root node.
+ if language is not None:
+ attr = self._itst_lang_attr.get(node)
+ if attr is not None:
+ node.setProp(attr, language)
+ # Because of the way we create nodes and rewrite the document,
+ # we end up with lots of redundant namespace definitions. We
+ # kill them off in one fell swoop at the end.
+ def fix_node_ns (node, nsdefs):
+ childnsdefs = nsdefs.copy()
+ nsdef = node.nsDefs()
+ while nsdef is not None:
+ nextnsdef = nsdef.next
+ if nsdefs.has_key(nsdef.name) and nsdefs[nsdef.name] == nsdef.content:
+ node.removeNsDef(nsdef.content)
+ else:
+ childnsdefs[nsdef.name] = nsdef.content
+ nsdef = nextnsdef
+ for child in xml_child_iter(node):
+ if child.type == 'element':
+ fix_node_ns(child, childnsdefs)
+ fix_node_ns(node, {})
+
+ def get_translated (self, node, translations):
+ msg = self._msgs.get_message_by_node(node)
+ if msg is None:
+ return node
+ trans = translations.ugettext(msg.get_string())
+ if trans is None:
+ return node
+ nss = {}
+ def reg_ns(node, nss):
+ if node.parent is not None:
+ reg_ns(node.parent, nss)
+ nsdef = node.nsDefs()
+ while nsdef is not None:
+ nss[nsdef.name] = nsdef.content
+ nsdef = nsdef.next
+ reg_ns(node, nss)
+ nss['_'] = NS_BLANK
+ blurb = '<' + node.name
+ for nsname in nss.keys():
+ if nsname is None:
+ blurb += ' xmlns="%s"' % nss[nsname]
+ else:
+ blurb += ' xmlns:%s="%s"' % (nsname, nss[nsname])
+ blurb += '>%s</%s>' % (trans.encode('utf-8'), node.name)
+ ctxt = libxml2.createDocParserCtxt(blurb)
+ ctxt.replaceEntities(0)
+ ctxt.parseDocument()
+ trnode = ctxt.doc().getRootElement()
+ def scan_node(node):
+ for child in xml_child_iter(node):
+ if child.type != 'element':
+ continue
+ if child.ns() is not None and child.ns().content == NS_BLANK:
+ repl = self.get_translated(msg.get_placeholder(child.name).node, translations)
+ child.replaceNode(repl)
+ scan_node(child)
+ scan_node(trnode)
+ retnode = node.copyNode(2)
+ for child in xml_child_iter(trnode):
+ retnode.addChild(child.copyNode(1))
+ return retnode
+
+ def generate_messages(self, comments=True):
+ if self._itst_credits is not None:
+ self._msgs.add_credits()
+ for ext in self._itst_externals:
+ msg = Message()
+ try:
+ fullfile = os.path.join(os.path.dirname(self._filename), ext[1])
+ filefp = open(fullfile)
+ filemd5 = hashlib.md5(filefp.read()).hexdigest()
+ filefp.close()
+ except:
+ filemd5 = '__failed__'
+ txt = "external ref='%s' md5='%s'" % (ext[1], filemd5)
+ msg.set_context('_')
+ msg.add_text(txt)
+ msg.add_source('%s:%i(%s)' % (self._doc.name, ext[0].lineNo(), ext[0].name))
+ msg.add_comment('This is a reference to an external file such as an image or'
+ ' video. When the file changes, the md5 hash will change to'
+ ' let you know you need to update your localized copy. The'
+ ' msgstr is not used at all. Set it to whatever you like'
+ ' once you have updated your copy of the file.')
+ self._msgs.add_message(msg, None)
+ self._in_translatable = True
+ for child in xml_child_iter(self._doc):
+ if child.type == 'element':
+ self.generate_message(child, None, comments=comments)
+ break
+
+ def generate_message (self, node, msg, comments=True):
+ if node.type in ('text', 'cdata') and msg is not None:
+ msg.add_text(node.content)
+ return
+ if node.type != 'element':
+ return
+ translate = self.get_its_translate(node)
+ if translate is None:
+ if self._in_translatable:
+ translate = 'yes'
+ else:
+ translate = 'no'
+ if translate == 'no':
+ if msg is not None:
+ msg.add_placeholder(node)
+ is_unit = False
+ msg = None
+ else:
+ is_unit = msg is None or self.is_translation_unit(node)
+ if is_unit:
+ if msg is not None:
+ msg.add_placeholder(node)
+ msg = Message()
+ if self.get_preserve_space(node):
+ msg.set_preserve_space()
+ msg.add_source('%s:%i(%s/%s)' % (self._doc.name, node.lineNo(), node.parent.name, node.name))
+ else:
+ msg.add_start_tag(node)
+
+ if comments and msg is not None:
+ comment = self.get_its_loc_note(node)
+ if comment is not None:
+ msg.add_comment(comment)
+
+ in_translatable = self._in_translatable
+ self._in_translatable = (translate == 'yes')
+ for child in xml_child_iter(node):
+ self.generate_message(child, msg, comments=comments)
+ self._in_translatable = in_translatable
+
+ if translate:
+ if is_unit and not msg.is_empty():
+ self._msgs.add_message(msg, node)
+ elif msg is not None:
+ msg.add_end_tag(node)
+
+ def is_translation_unit (self, node):
+ return self.get_its_within_text(node) != 'yes'
+
+ def get_preserve_space (self, node):
+ if node.getSpacePreserve() == 1:
+ return True
+ else:
+ while node.type == 'element':
+ if self._itst_preserve_space_nodes.has_key(node):
+ return (self._itst_preserve_space_nodes[node] == 'yes')
+ node = node.parent
+ return False
+
+ def get_its_translate (self, node):
+ if node.hasNsProp('translate', NS_ITS):
+ return node.nsProp('translate', NS_ITS)
+ if xml_is_ns_name(node, NS_ITS, 'span'):
+ if node.hasProp('translate'):
+ return node.prop('translate')
+ if self._its_translate_nodes.has_key(node):
+ return self._its_translate_nodes[node]
+ return None
+
+ def get_its_within_text (self, node):
+ return self._its_within_text_nodes.get(node, 'no')
+
+ def get_its_loc_note (self, node):
+ if node.hasNsProp('locNote', NS_ITS):
+ return re.sub('\s+', ' ', node.nsProp('locNote', NS_ITS)).strip()
+ if node.hasNsProp('locNoteRef', NS_ITS):
+ return 'SEE: ' + re.sub('\s+', ' ', node.nsProp('locNoteRef', NS_ITS)).strip()
+ if xml_is_ns_name(node, NS_ITS, 'span'):
+ if node.hasProp('locNote'):
+ return re.sub('\s+', ' ', node.prop('locNote')).strip()
+ if node.hasProp('locNoteRef'):
+ return 'SEE: ' + re.sub('\s+', ' ', node.prop('locNoteRef')).strip()
+ return self._its_loc_notes.get(node, None)
+
+
+if __name__ == '__main__':
+ options = optparse.OptionParser()
+ options.set_usage('\n itstool [OPTIONS] [XMLFILES]\n itstool -m <MOFILE> [OPTIONS] [XMLFILES]')
+ options.add_option('-i', '--its',
+ action='append',
+ dest='itsfile',
+ metavar='ITS',
+ help='load the ITS rules in the file ITS (can specify multiple times)')
+ options.add_option('-l', '--lang',
+ dest='lang',
+ default=None,
+ metavar='LANGUAGE',
+ help='explicitly set the language code for output file')
+ options.add_option('-m', '--merge',
+ dest='merge',
+ metavar='FILE',
+ help='merge from a PO or MO file FILE and output XML files')
+ options.add_option('-o', '--output',
+ dest='output',
+ default=None,
+ metavar='OUT',
+ help='output PO files to file OUT or XML files in directory OUT')
+ options.add_option('-v', '--version',
+ action='store_true',
+ dest='version',
+ default=False,
+ help='output PO files to file OUT or XML files in directory OUT')
+ (opts, args) = options.parse_args(sys.argv)
+
+ if opts.version:
+ print 'itstool ' + VERSION
+ sys.exit(0)
+
+ if opts.merge is None:
+ messages = MessageList()
+ for filename in args[1:]:
+ doc = Document(filename, messages)
+ doc.apply_its_rules()
+ if opts.itsfile is not None:
+ for itsfile in opts.itsfile:
+ doc.apply_its_file(itsfile)
+ doc.generate_messages()
+ if opts.output is None or opts.output == '-':
+ out = sys.stdout
+ else:
+ try:
+ out = file(opts.output, 'w')
+ except:
+ sys.stderr.write('Error: Cannot write to file %s\n' % opts.output)
+ sys.exit(1)
+ messages.output(out)
+ else:
+ try:
+ translations = gettext.GNUTranslations(open(opts.merge, 'rb'))
+ except:
+ sys.stderr.write('Error: cannot open mo file %s\n' % opts.merge)
+ sys.exit(1)
+ translations.add_fallback(NoneTranslations())
+ if opts.lang is None:
+ opts.lang = os.path.splitext(os.path.basename(opts.merge))[0]
+ if opts.output is None:
+ out = './'
+ elif os.path.isdir(opts.output):
+ out = opts.output
+ elif len(args) == 2:
+ if opts.output == '-':
+ out = sys.stdout
+ else:
+ out = file(opts.output, 'w')
+ else:
+ sys.stderr.write('Error: Non-directory output for multiple files\n')
+ sys.exit(1)
+ for filename in args[1:]:
+ messages = MessageList()
+ doc = Document(filename, messages)
+ doc.apply_its_rules()
+ if opts.itsfile is not None:
+ for itsfile in opts.itsfile:
+ doc.apply_its_file(itsfile)
+ doc.merge_translations(translations, opts.lang)
+ fout = out
+ if isinstance(fout, basestring):
+ fout = file(os.path.join(fout, os.path.basename(filename)), 'w')
+ fout.write(doc._doc.serialize('utf-8'))