Skip to main content
 首页 » 编程设计

python之扩展 Jinja 的 {% trans %} 以使用 JavaScript 变量

2025年05月04日33开发

我想扩展 trans 的行为,而不是将变量呈现为来自上下文的值,而是呈现为 html(不使用上下文)。我的目标是能够通过 JavaScript 在客户端填充这些变量。

Jinja 似乎不允许进行大量此类自定义,或者我无法找到合适的钩子(Hook)。

这是我想要实现的目标:

{% etrans name=username %} 
My name is {{ name }} 
{% endetrans %} 

这应该呈现为:

My name is <span id='#username'></span> 

当然,我可以只使用普通的 {% trans %} 指令并将我的 html 代码传递给 template.render(html_code_params),但这需要在模板和我想避免的呈现代码中定义它们。

到目前为止,这是我得到的(不多)允许新的 etrans 标签和使用 InternationalizationExtension 必须提供的任何好东西的能力。

from jinja2.ext import InternationalizationExtension 
from jinja2.runtime import concat 
 
 
class JavaScriptVariableExtension(InternationalizationExtension): 
    tagname = 'etrans' 
    tags = set([tagname]) 
 
    def _parse_block(self, parser, allow_pluralize): 
        """Parse until the next block tag with a given name. 
 
        Copy from InternationalizationExtension, as this uses hardcoded 
        `name:endtrans` instead of relying on tag name 
        """ 
        referenced = [] 
        buf = [] 
        while 1: 
            if parser.stream.current.type == 'data': 
                buf.append(parser.stream.current.value.replace('%', '%%')) 
                next(parser.stream) 
            elif parser.stream.current.type == 'variable_begin': 
                next(parser.stream) 
                name = parser.stream.expect('name').value 
                referenced.append(name) 
                buf.append('%%(%s)s' % name) 
                parser.stream.expect('variable_end') 
            elif parser.stream.current.type == 'block_begin': 
                next(parser.stream) 
                # can't use hardcoded "endtrans" 
                # if parser.stream.current.test('name:endtrans'): 
                if parser.stream.current.test('name:end%s' % self.tagname): 
                    break 
                elif parser.stream.current.test('name:pluralize'): 
                    if allow_pluralize: 
                        break 
                    parser.fail('a translatable section can have only one ' 
                                'pluralize section') 
                parser.fail('control structures in translatable sections are ' 
                            'not allowed') 
            elif parser.stream.eos: 
                parser.fail('unclosed translation block') 
            else: 
                assert False, 'internal parser error' 
 
        return referenced, concat(buf) 
 
 
i18n_extended = JavaScriptVariableExtension 

我不介意重载更多的方法(尽管上述方法的原因应该在上游得到修复)。

单步执行代码是一次非常有趣的冒险。但是,我遇到了障碍,如果有人可以提供一些建议,我很感兴趣。

我看到的问题是,在编译期间,函数 context.resolve() 被嵌入到编译代码中。 jinja2.jinja2.compiler.CodeGenerator 不允许在这里进行任何不同的处理(如果我错了请纠正我)。理想情况下,我会定义另一个节点(用于变量)并且该节点将处理它在编译期间的处理方式,但我不明白这是怎么可能的。我可能过于关注这个作为解决方案,所以也许有人可以提供替代方案。

请您参考如下方法:

正如@Garrett 的评论所建议的那样,一个更简单的解决方案是将一个函数传递给对变量进行插值的模板渲染器。就我而言,我的目标客户端框架是 Angular,但这也适用于您想在 {% trans %} 环境中使用的任何 JS 变量。以下是构建 block :

def text_to_javascript(string): 
    # modify as needed...  
    return "<span>{{ %s }}</span>" % string 
 
def render(): 
    tmpl = jinja_env.get_template(template_filename) 
    return tmpl.render({'js': text_to_javascript}) 

这就是我在模板文件中使用它的方式:

{% trans username=js('user.name') %} 
  My name is {{ username }} 
{% endtrans %} 

在 Angular Controller 中,变量 user 绑定(bind)到 $scope,如下所示:

$scope.user = {'name': 'Bugs Bunny'}