1import re 2 3from jinja2.exceptions import TemplateSyntaxError 4from jinja2.ext import Extension 5from jinja2.lexer import count_newlines 6from jinja2.lexer import Token 7 8 9_outside_re = re.compile(r"\\?(gettext|_)\(") 10_inside_re = re.compile(r"\\?[()]") 11 12 13class InlineGettext(Extension): 14 """This extension implements support for inline gettext blocks:: 15 16 <h1>_(Welcome)</h1> 17 <p>_(This is a paragraph)</p> 18 19 Requires the i18n extension to be loaded and configured. 20 """ 21 22 def filter_stream(self, stream): 23 paren_stack = 0 24 25 for token in stream: 26 if token.type != "data": 27 yield token 28 continue 29 30 pos = 0 31 lineno = token.lineno 32 33 while 1: 34 if not paren_stack: 35 match = _outside_re.search(token.value, pos) 36 else: 37 match = _inside_re.search(token.value, pos) 38 if match is None: 39 break 40 new_pos = match.start() 41 if new_pos > pos: 42 preval = token.value[pos:new_pos] 43 yield Token(lineno, "data", preval) 44 lineno += count_newlines(preval) 45 gtok = match.group() 46 if gtok[0] == "\\": 47 yield Token(lineno, "data", gtok[1:]) 48 elif not paren_stack: 49 yield Token(lineno, "block_begin", None) 50 yield Token(lineno, "name", "trans") 51 yield Token(lineno, "block_end", None) 52 paren_stack = 1 53 else: 54 if gtok == "(" or paren_stack > 1: 55 yield Token(lineno, "data", gtok) 56 paren_stack += -1 if gtok == ")" else 1 57 if not paren_stack: 58 yield Token(lineno, "block_begin", None) 59 yield Token(lineno, "name", "endtrans") 60 yield Token(lineno, "block_end", None) 61 pos = match.end() 62 63 if pos < len(token.value): 64 yield Token(lineno, "data", token.value[pos:]) 65 66 if paren_stack: 67 raise TemplateSyntaxError( 68 "unclosed gettext expression", 69 token.lineno, 70 stream.name, 71 stream.filename, 72 ) 73