[IMP] npybabel.py: comment w/ rationale for not using iterparse() in qweb extraction
[odoo/odoo.git] / npybabel.py
1 #!/usr/bin/env python
2 # EASY-INSTALL-ENTRY-SCRIPT: 'Babel==0.9.6','console_scripts','pybabel'
3 __requires__ = 'Babel==0.9.6'
4 import sys
5 from pkg_resources import load_entry_point
6 import re
7 import json
8 from lxml import etree as elt
9 from babel.messages import extract
10
11 if __name__ == '__main__':
12     sys.exit(
13         load_entry_point('Babel==0.9.6', 'console_scripts', 'pybabel')()
14     )
15
16 XMLJS_EXPR = re.compile(r"""(?:\_t *\( *((?:"(?:[^"\\]|\\.)*")|(?:'(?:[^'\\]|\\.)*')) *\))""")
17
18 TRANSLATION_FLAG_COMMENT = "openerp-web"
19
20 def extract_xmljs(fileobj, keywords, comment_tags, options):
21     """Extract messages from Javascript code embedded into XML documents.
22     This complements the ``extract_javascript`` extractor which works
23     only on pure .js files, and the``extract_qweb`` extractor, which only
24     extracts XML text.
25
26     :param fileobj: the file-like object the messages should be extracted
27                     from
28     :param keywords: a list of keywords (i.e. function names) that should
29                      be recognized as translation functions
30     :param comment_tags: a list of translator tags to search for and
31                          include in the results
32     :param options: a dictionary of additional options (optional)
33     :return: an iterator over ``(lineno, funcname, message, comments)``
34              tuples
35     :rtype: ``iterator``
36     """
37     content = fileobj.read()
38     found = XMLJS_EXPR.finditer(content)
39     index = 0
40     line_nbr = 0
41     for f in found:
42         msg = f.group(1)
43         msg = json.loads(msg)
44         while index < f.start():
45             if content[index] == "\n":
46                 line_nbr += 1
47             index += 1
48         yield (line_nbr, None, msg, [TRANSLATION_FLAG_COMMENT])
49
50 def extract_qweb(fileobj, keywords, comment_tags, options):
51     """Extract messages from qweb template files.
52     :param fileobj: the file-like object the messages should be extracted
53                     from
54     :param keywords: a list of keywords (i.e. function names) that should
55                      be recognized as translation functions
56     :param comment_tags: a list of translator tags to search for and
57                          include in the results
58     :param options: a dictionary of additional options (optional)
59     :return: an iterator over ``(lineno, funcname, message, comments)``
60              tuples
61     :rtype: ``iterator``
62     """
63     result = []
64     def handle_text(text, lineno):
65         text = (text or "").strip()
66         if len(text) > 1: # Avoid mono-char tokens like ':' ',' etc.
67             result.append((lineno, None, text, [TRANSLATION_FLAG_COMMENT]))
68
69     # not using elementTree.iterparse because we need to skip sub-trees in case
70     # the ancestor element had a reason to be skipped
71     def iter_elements(current_element):
72         for el in current_element:
73             if "t-js" not in el.attrib and \
74                     not ("t-jquery" in el.attrib and "t-operation" not in el.attrib) and \
75                     not ("t-translation" in el.attrib and el.attrib["t-translation"].strip() == "off"):
76                 handle_text(el.text, el.sourceline)
77                 iter_elements(el)
78             handle_text(el.tail, el.sourceline)
79
80     tree = elt.parse(fileobj)
81     iter_elements(tree.getroot())
82
83     return result
84
85 def extract_javascript(fileobj, keywords, comment_tags, options):
86     """Extract messages from Javascript source files. This extractor delegates
87     to babel's buit-in javascript extractor, but adds a special comment
88     used as a flag to identify web translations. 
89
90     :param fileobj: the file-like object the messages should be extracted
91                     from
92     :param keywords: a list of keywords (i.e. function names) that should
93                      be recognized as translation functions
94     :param comment_tags: a list of translator tags to search for and
95                          include in the results
96     :param options: a dictionary of additional options (optional)
97     :return: an iterator over ``(lineno, funcname, message, comments)``
98              tuples
99     :rtype: ``iterator``
100     """
101     for (message_lineno, funcname, messages, comments) in \
102         extract.extract_javascript(fileobj, keywords, comment_tags, options):
103         comments.append(TRANSLATION_FLAG_COMMENT)
104         yield (message_lineno, funcname, messages, comments)