A Perl transpiler that converts Jinja2 templates to Template Toolkit 2 (TT2) syntax.
Source: https://github.com/lucianofedericopereira/jinja2tt2
Description
Jinja2 is deeply integrated with Python, making a direct port impractical. However, since TT2 and Jinja2 share similar concepts and syntax patterns, this transpiler performs a mechanical translation between the two template languages.
Why TT2?
TT2 and Jinja2 share:
- Variable interpolation:
{{ var }}maps to[% var %] - Control structures:
{% if %}/{% for %}map to[% IF %]/[% FOREACH %] - Filters:
{{ name|upper }}maps to[% name | upper %] - Includes, blocks, and inheritance (conceptually similar)
- Expression grammar close enough to map mechanically
Installation
No external dependencies beyond core Perl 5.20+.
git clone https://github.com/lucianofedericopereira/jinja2tt2
cd jinja2tt2
Usage
Command Line
# Transpile a file to stdout
./bin/jinja2tt2 template.j2
# Transpile with output to file
./bin/jinja2tt2 template.j2 -o template.tt
# Transpile in-place (creates .tt file)
./bin/jinja2tt2 -i template.j2
# From stdin
echo '{{ name|upper }}' | ./bin/jinja2tt2
# Debug mode (shows tokens and AST)
./bin/jinja2tt2 --debug template.j2
Programmatic Usage
use Jinja2::TT2;
my $transpiler = Jinja2::TT2->new();
# From string
my $tt2 = $transpiler->transpile('{{ user.name|upper }}');
# Result: [% user.name.upper %]
# From file
my $tt2 = $transpiler->transpile_file('template.j2');
Supported Constructs
Variables
{{ foo }} → [% foo %]
{{ user.name }} → [% user.name %]
{{ items[0] }} → [% items.0 %]
Filters
{{ name|upper }} → [% name.upper %]
{{ name|lower|trim }} → [% name.lower.trim %]
{{ items|join(", ") }} → [% items.join(', ') %]
{{ name|default("Guest") }} → [% (name || 'Guest') %]
Conditionals
{% if user %} → [% IF user %]
{% elif admin %} → [% ELSIF admin %]
{% else %} → [% ELSE %]
{% endif %} → [% END %]
Loops
{% for item in items %} → [% FOREACH item IN items %]
{{ loop.index }} → [% loop.count %]
{{ loop.first }} → [% loop.first %]
{{ loop.last }} → [% loop.last %]
{% endfor %} → [% END %]
Blocks and Macros
{% block content %} → [% BLOCK content %]
{% endblock %} → [% END %]
{% macro btn(text) %} → [% MACRO btn(text) BLOCK %]
{% endmacro %} → [% END %]
Comments
{# This is a comment #} → [%# This is a comment %]
Whitespace Control
{{- name -}} → [%- name -%]
{%- if x -%} → [%- IF x -%]
Other Constructs
-
{% include "file.html" %}→[% INCLUDE file.html %] -
{% set x = 42 %}→[% x = 42 %] - Ternary:
{{ x if cond else y }}→[% (cond ? x : y) %] - Boolean literals:
true/false→1/0
Filter Mapping
| Jinja2 | TT2 Equivalent |
|---|---|
upper |
.upper |
lower |
.lower |
trim |
.trim |
first |
.first |
last |
.last |
length |
.size |
join |
.join |
reverse |
.reverse |
sort |
.sort |
escape / e
|
`\ |
{% raw %}default
|
`\ |
{% raw %}replace
|
.replace |
Some filters require TT2 plugins (e.g., tojson needs Template::Plugin::JSON).
Loop Variable Mapping
| Jinja2 | TT2 |
|---|---|
loop.index |
loop.count |
loop.index0 |
loop.index |
loop.first |
loop.first |
loop.last |
loop.last |
loop.length |
loop.size |
Limitations
-
Template inheritance (
{% extends %}) requires manual adjustment for TT2'sWRAPPERpattern - Autoescape is not directly supported in TT2
- Some filters need custom TT2 plugins or vmethods
- Complex Python expressions may need review
Running Tests
prove -l t/
Architecture
- Tokenizer: Splits Jinja2 source into tokens (text, variables, statements, comments)
- Parser: Builds an Abstract Syntax Tree (AST) from the token stream
- Emitter: Walks the AST and generates equivalent TT2 code
Credits
- Luciano Federico Pereira - Author
License
This is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) version 2.1 as published by the Free Software Foundation.

Top comments (0)