diff roundup/scripts/roundup_gettext.py @ 8080:d1c29284ccd9

feat: issue2551287 - roundup-gettext extracts strings from detectors/extensions Enhance roundup_gettext.py to extract strings from detectors/extensions. If the polib module is available, roundup-gettext will extract translatable strings from the tracker's Python code. If polib is missing, it will print a warning. Marcus did most of the work, I had to do a python 2-> conversion of pygettext.py.
author John Rouillard <rouilj@ieee.org>
date Sat, 13 Jul 2024 18:27:11 -0400
parents 32a5a54536b5
children 2943140f5286
line wrap: on
line diff
--- a/roundup/scripts/roundup_gettext.py	Sat Jul 13 10:22:55 2024 -0400
+++ b/roundup/scripts/roundup_gettext.py	Sat Jul 13 18:27:11 2024 -0400
@@ -2,12 +2,12 @@
 #
 # Copyright 2004 Richard Jones (richard@mechanicalcat.net)
 
-"""Extract translatable strings from tracker templates"""
+"""Extract translatable strings from tracker templates and detectors/extensions"""
 
 from __future__ import print_function
 import os
 import sys
-
+import tempfile
 
 # --- patch sys.path to make sure 'import roundup' finds correct version
 import os.path as osp
@@ -23,6 +23,38 @@
 
 from roundup.i18n import _
 from roundup.cgi.TAL import talgettext
+from roundup.pygettext import make_escapes, TokenEater, tokenize
+
+try:
+    import polib
+except ImportError:
+    print(_("\nExtracting translatable strings from html templates.\n"
+            "Because the 'polib' module is missing, unable to extract\n"
+            "translations from detectors or extensions.\n"
+            "The 'polib' module can be installed with pip.\n"))
+    polib = None
+
+# from pygettext's main():
+class Options:
+    # constants
+    GNU = 1
+    SOLARIS = 2
+    # defaults
+    extractall = 0 # FIXME: currently this option has no effect at all.
+    escape = 0
+    keywords = ["_", "gettext", "ngettext", "ugettext"]
+    outpath = ''
+    outfile = ''
+    writelocations = 1
+    locationstyle = GNU
+    verbose = 0
+    width = 10
+    excludefilename = ''
+    docstrings = 0
+    nodocstrings = {}
+    toexclude = [] # TODO we should exclude all strings already found in some template
+
+tokeneater_options = Options()
 
 # name of message template file.
 # i don't think this will ever need to be changed, but still...
@@ -62,6 +94,48 @@
     # run
     talgettext.main()
 
+    if not polib:
+        return
+
+    # we have now everything from the templates in the TEMPLATE_FILE
+    # now we search in home/detectors and home/extensions *.py files for
+    # tokeneater_options.keywords
+    # this is partly assembled from pygettext's main()
+    make_escapes(not tokeneater_options.escape)
+
+    pyfiles = []
+    for source in ["detectors", "extensions"] :
+        for root, dirs, files in os.walk (os.path.join ("..", source)) :
+            pyfiles.extend ([os.path.join (root, f) for f in files if f.endswith (".py")])
+
+    eater = TokenEater (tokeneater_options)
+
+    for filename in pyfiles :
+        eater.set_filename (filename)
+        with open (filename, "r") as f:
+            try:
+                for token in tokenize.generate_tokens(f.readline):
+                    eater(*token)
+            except tokenize.TokenError as e:
+                print('%s: %s, line %d, column %d' % (
+                    e[0], filename, e[1][0], e[1][1]), file=sys.stderr)
+    
+    with tempfile.NamedTemporaryFile("w") as tf :
+        eater.write(tf)
+        tf.seek (0)
+        p1 = polib.pofile(TEMPLATE_FILE)
+        p2 = polib.pofile(tf.name)
+
+        p2_msg_ids = set([e.msgid for e in p2])
+        for e in p1 :
+            if e.msgid in p2_msg_ids :
+                p2_e = p2.find (e.msgid)
+                e.occurrences.extend (p2_e.occurrences)
+                p2_msg_ids.remove (e.msgid)
+
+        for msgid in p2_msg_ids :
+            p1.append (p2.find (msgid))
+        p1.save ()
 
 if __name__ == "__main__":
     run()

Roundup Issue Tracker: http://roundup-tracker.org/