I'm trying to make an extension which adds support for the trans tag. Jinja trans syntax requires you to bind template expressions to new variables via keyword arguments, like:
<p>{% trans user=user.username %}Hello {{ user }}!{% endtrans %}</p>
Now, I'm able to iterate over the arguments to my tag and add them to the context like this:
this.run = function(context, args, body) {
for (key in args) {
context.ctx[key] = args[key];
}
return gettext(body()); # gettext() is defined somewhere else.
}
But it seems I'm getting the global context and polluting this, which leaves my new variables available outside my trans tag (i.e. the user variable will be available everywhere afterwards).
How do I extend only the "local" context used by my tag? It seems the for and (in some cases) set tags do this and I tried looking through the source for this, but I'm unfortunately lost.
That's a great question. It's unfortunately a limitation of the current custom tags API. Basically each scope is defined by a frame object, but you don't have access to that here. I'm not sure what the API would look like. You need to request a new frame somehow, because I don't want to run all custom tags in their own scope (that affects things like set tags, their side effects won't be seen outside of it).
It's hard to come up with a good custom tags API. If you can figure out an API for this, I'd be happy to accept a PR. Sorry the current API kind of stinks.
At least thanks for an honest answer :)
So, internal tags, like for, which also makes new variables available in the scope inside its tags, uses another API than that exposed to custom tags?
I wish I had the time and knowledge to figure out a better API as you suggest, but I don't think that is going to happen any time soon.
Maybe for my own personal use I can live with a polluted global context, just to get things done for my current project. But it makes it sort of embarrassing to share my work on the trans tag then :(
Hi all, guys! Just faced the same kind of problem. I'm creating format.js internationalization helper for nunjucks templater. And one last thing i have to implement — is a custom 'intl' tag, that overrides global intl locale options provided. For ex., you can render whole template, using a 'en' locale, but have one block with 'fr' locale.
Unfortunately, docs about hijacking nunjucks extensions api is very poor, but i've got somehow through sources, and have come to this.
What about adding access to frame stack through some method in ctx, like lookup?
For example:
// inside run function of custom extension:
var frame = context.addFrame();
frame.set('my-scoped-var', 'scoped-value');
return content();
Or just allow raw access to frames stack, so developer can use push/pop and all other frame methods.
var frame = context.frames.push();
+1
+1
+1
Most helpful comment
Hi all, guys! Just faced the same kind of problem. I'm creating format.js internationalization helper for nunjucks templater. And one last thing i have to implement — is a custom 'intl' tag, that overrides global intl locale options provided. For ex., you can render whole template, using a 'en' locale, but have one block with 'fr' locale.
Unfortunately, docs about hijacking nunjucks extensions api is very poor, but i've got somehow through sources, and have come to this.
What about adding access to frame stack through some method in ctx, like lookup?
For example:
Or just allow raw access to frames stack, so developer can use push/pop and all other frame methods.