[rkward-cvs] SF.net SVN: rkward-code:[4981] trunk/rkward/scripts/extract_plugin_messages .py
tfry at users.sf.net
tfry at users.sf.net
Fri Oct 31 13:40:25 UTC 2014
Revision: 4981
http://sourceforge.net/p/rkward/code/4981
Author: tfry
Date: 2014-10-31 13:40:23 +0000 (Fri, 31 Oct 2014)
Log Message:
-----------
Initial support for extracting messages from JS (files, only, so far, but should be easy enough to extend to inlined code)
Modified Paths:
--------------
trunk/rkward/scripts/extract_plugin_messages.py
Modified: trunk/rkward/scripts/extract_plugin_messages.py
===================================================================
--- trunk/rkward/scripts/extract_plugin_messages.py 2014-10-30 13:17:17 UTC (rev 4980)
+++ trunk/rkward/scripts/extract_plugin_messages.py 2014-10-31 13:40:23 UTC (rev 4981)
@@ -123,9 +123,14 @@
else:
outfile.write ("i18n (" + quote (node.getAttribute ("label")) + ");\n")
if (node.hasAttribute ("file")):
- if (node.tagName != "code"):
- # TODO: handle .js files
- handleSubFile (node.getAttribute ("file"), node.tagName == "component")
+ filename = node.getAttribute ("file")
+ if (filename.endswith (".js")):
+ filename = os.path.join (os.path.dirname (infile["infile"]), filename)
+ jsfile = codecs.open (filename, 'r', 'utf-8')
+ handleJSChunk (jsfile.read (), filename, 0, getFileCaption (None, infile["caption"]))
+ jsfile.close ()
+ else:
+ handleSubFile (filename, node.tagName == "component")
if (node.tagName in text_containers):
textchunks = getFullText (node).split ("\n\n")
for chunk in textchunks:
@@ -138,17 +143,20 @@
for child in node.childNodes:
handleNode (child)
-# Try to determine a caption for the file (will be used as context comment)
-def getFileCaption (docelem):
- elems = docelem.getElementsByTagName ("title")
- if (elems.length):
- return normalize (getFullText (elems.item (0)))
- elems = docelem.getElementsByTagName ("dialog")
- if (elems.length):
- return elems.item (0).getAttribute ("label")
- elems = docelem.getElementsByTagName ("wizard")
- if (elems.length):
- return elems.item (0).getAttribute ("label")
+# Try to determine a caption for the file (will be used as context comment). If none is found in this file use "Loaded from loaded_from"
+def getFileCaption (docelem, loaded_from):
+ if (not docelem is None):
+ elems = docelem.getElementsByTagName ("title")
+ if (elems.length):
+ return normalize (getFullText (elems.item (0)))
+ elems = docelem.getElementsByTagName ("dialog")
+ if (elems.length):
+ return elems.item (0).getAttribute ("label")
+ elems = docelem.getElementsByTagName ("wizard")
+ if (elems.length):
+ return elems.item (0).getAttribute ("label")
+ if (loaded_from != ""):
+ return "Loaded from " + loaded_from
return ""
# Gather labels of elements with given id (so <setting id="xyz">text</setting> elements can be labelled)
@@ -161,6 +169,120 @@
ret.update (getElementLabelsRecursive (ce))
return ret
+# It really is sort of lame to have to parse JS and extract i18n-calls, when xgettext could do it. But that would not
+# - allow us to add info on which plugin this belongs to
+# - list the i18n strings from the JS file in sequence with the i18n strings from the XML parts of the same plugin
+# - give decent file context for inlines JS script code
+class JSParseBuffer:
+ def __init__ (self, content):
+ self.buf = content
+ self.comment = ""
+ self.nline = 0
+ self.nchar = 0
+ def atEof (self):
+ return (self.nchar >= len (self.buf))
+ def currentChar (self):
+ if (self.atEof ()):
+ return ""
+ return (self.buf[self.nchar])
+ def advance (self, n=1):
+ for step in range (n):
+ self.nchar += 1
+ if (self.nchar >= (len (self.buf) - 1)):
+ return False
+ if (self.buf[self.nchar] == "\n"):
+ self.nline += 1
+ return True
+ def skipWhitespace (self):
+ while (self.buf[self.nchar].isspace ()):
+ if (not self.advance ()):
+ break
+ def seekUntil (self, needle):
+ fromchar = self.nchar
+ while (not self.startswith (needle)):
+ if (not self.advance ()):
+ break
+ return self.buf[fromchar:self.nchar]
+ def seek_line_comment_end (self):
+ comment = ""
+ while True:
+ comment += self.seekUntil ("\n")
+ self.skipWhitespace ()
+ if (self.startswith ("//")):
+ self.advance (2)
+ else:
+ break
+ return comment
+ # TODO: handle includes, somehow
+ def nibble_until (self, string, skip_over_parentheses=False):
+ fromchar = self.nchar
+ while (not self.startswith (string)):
+ if (self.atEof ()):
+ break
+ if (self.startswith ('\\')):
+ self.advance (2)
+ continue
+ if (self.buf[self.nchar] in ['"', '\'', '`']):
+ ochar = self.buf[self.nchar]
+ if (self.advance ()):
+ self.nibble_until (ochar)
+ elif (self.startswith ("/*")):
+ self.comment = self.seekUntil ("*/")
+ elif (self.startswith ("//")):
+ self.comment = self.seek_line_comment_end ()
+ elif (skip_over_parentheses and (self.startswith ("("))): # skip over nested parentheses
+ self.advance ()
+ self.nibble_until (")", True)
+ elif (not self.buf[self.nchar].isspace ()):
+ self.comment = ""
+ if (not self.advance ()):
+ break
+ return (self.buf[fromchar:self.nchar])
+ def startswith (self, string):
+ return self.buf.startswith (string, self.nchar)
+
+def handleJSChunk (buf, filename, offset, caption):
+ global outfile
+
+ jsbuf = JSParseBuffer (buf)
+ while (True):
+ call = ""
+ junk = jsbuf.nibble_until ("i18n")
+ if (jsbuf.atEof ()):
+ break
+ # skip over somethingelsei18n identifiers
+ if (jsbuf.nchar > 0 and jsbuf.buf[jsbuf.nchar-1].isalnum ()):
+ jsbuf.advance ()
+ continue
+ # skip over i18nsomethingelse identifiers
+ jsbuf.advance (4) # i18n
+ if (jsbuf.startswith ("cp")):
+ call = "i18ncp"
+ jsbuf.advance (2)
+ elif (jsbuf.startswith ("c")):
+ call = "i18nc"
+ jsbuf.advance (1)
+ elif (jsbuf.startswith ("p")):
+ call = "i18np"
+ jsbuf.advance (1)
+ else:
+ call = "i18n"
+ nextchar = jsbuf.currentChar ()
+ if (not (nextchar.isspace () or nextchar == '(')):
+ continue
+ comment = normalize (jsbuf.comment)
+ line = jsbuf.nline
+ call += jsbuf.nibble_until ("(") + jsbuf.nibble_until (")", True) + ";"
+ text = "/* "
+ if (comment.lower ().startswith ("i18n:") or comment.lower ().startswith ("translators:")):
+ text += "i18n: " + comment + "\n"
+ text += "i18n: file: " + filename
+ if (offset >= 0):
+ text += ":" + str (offset + line + 1)
+ text += "\ni18n: ectx: " + caption + " */"
+ text += call
+ outfile.write (text)
+
# When we encounter a "file"-attribute, we generally dive right into parsing that file, i.e. we do depth first
# Advantage is that strings in all files belonging to one plugin will be in direct succession in the .pot file
# The exception is if the referenced file declares an own (different) po_id. In this case it will be handled, later.
@@ -181,9 +303,7 @@
oldinfile = copy.deepcopy (infile)
infile["infile"] = filename
infile["file_prefix"] = xmldoc.documentElement.getAttribute ("base_prefix")
- infile["caption"] = getFileCaption (xmldoc.documentElement)
- if ((infile["caption"] == "") and (oldinfile["caption"] != "")):
- infile["caption"] = "Loaded from " + oldinfile["caption"]
+ infile["caption"] = getFileCaption (xmldoc.documentElement, oldinfile["caption"])
if (fetch_ids):
infile["id_labels"] = getElementLabelsRecursive (xmldoc.documentElement)
handleNode (xmldoc.documentElement)
More information about the rkward-tracker
mailing list