Themes built on the Twig templating engine are now deprecated and require migration to Vitrin to ensure continued support and compatibility with Zid.
.twig and .html files to .jinja.| Twig | Jinja | Notes |
|---|---|---|
raw | safe | Marks string as safe HTML so jinja doesn't double-escape. |
json_encode() | tojson | Useful for embedding data in <script>. |
filter | select / reject | Jinja uses tests/expressions: items | selectattr('x', 'equalto', 1) etc. |
url_encode | urlencode | Percent-encodes strings. |
In many cases, function-style helpers in Twig become filters in Jinja.
| Twig (function/helper) | Jinja (filter) | Notes |
|---|---|---|
asset_url | asset_url | Same name (environment-provided). |
assetUrl | asset_url | Switch to snake_case. |
strReplace(haystack, search, replace) | haystack | replace(search, replace) | Jinja replace takes (old, new). |
| Name | What it does | Typical usage |
|---|---|---|
today() | Current date in the request’s timezone, preformatted | {{ today() }} |
now() | Current time in the request’s timezone | {{ now() }} |
format_datetime | DateTime formatter with tz/locale already set | {{ format_datetime(order.created_at) }} |
format_time | Time formatter with tz/locale already set | {{ format_time(order.created_at) }} |
url_for | Application route builder | {{ url_for('list_products') }} |
{# Current date (uses prebound fmt) #}
{{ today() }} {# e.g., 2025/08/20 #}
{# Current time #}
{{ now() }} {# e.g., 17:05 #}
{# Format a specific datetime with defaults (tz/locale are prebound) #}
{{ format_datetime(order.created_at) }}
{# Choose a predefined width: "full" | "long" | "medium" | "short" #}
{{ format_datetime(order.created_at, fmt="long") }}
{# Override the format string if needed #}
{{ format_datetime(order.created_at, fmt="%Y-%m-%d %H:%M") }}
{# Format only the time portion #}
{{ format_time(order.created_at) }}datetime_/time_ is omitted (None), the helpers use “now” in the request’s timezone.fmt accepts "full" | "long" | "medium" | "short" or a custom format string.tz and locale are already supplied via partial application; override only when you truly need to.| Twig Global / Helper | Jinja / App Equivalent | Example |
|---|---|---|
requestAdd(query: dict) | session.url.include_query_params(**params) | session.url.include_query_params(page=2) |
requestGet | session.query_params.get() | session.query_params.get('q') |
requestUri | session.url.path | /products/123 |
requestInputs | session.url.params | Dict-like of current query. |
urlWithQuery | session.url.include_query_params(**params) | — |
rangeN | Python range(n) | range(5) → 0..4 |
rangeNWithStep | Python range(start, stop, step) | range(0, 10, 2) |
tDate | — (no direct equivalent) | Use Babel date/time formatters. |
| — | url_for(name, *, localize=True, query_params=None, **path_params) | Returns app route: url_for('list_products') |
url_foris provided by your app. Usequery_paramsto append query strings.
| Concept | Twig (old) | Jinja (new) |
|---|---|---|
| Items | response.data | response.results |
| Total count | response.total | response.count |
| Current page | response.current_page | response.page |
| Page size / per page | response.per_page | response.page_size |
| Total pages | response.last_page | response.pages_count |
| Task | Twig | Jinja | Notes |
|---|---|---|---|
| Default value | var ?? 'x' | var or 'x' or var | default('x') | Short alias: | d('x') |
| Force default on falsy | — | default('x', true) | Treats None,'' ,0,[],False as “missing”. |
| Null literal | null | None | — |
{{ title or 'Untitled' }}
{{ title | default('Untitled') }}
{{ count | default(0, true) }}| Twig | Jinja |
|---|---|
{% include 'x' with {a: 1} %} | {% with a=1 %}{% include 'x' %}{% endwith %} |
locals.hello_world_("Hello world"){{ _("Hello %(name)s", name=user.name) }}
{{ ngettext("%(n)s item", "%(n)s items", n, n=n) }}{% schema %}...{% endschema %} to a separate {}.schema.json file with the same base filename..jinja template.locals.* usages with _('English text') using strings from en.json..po file and migrate Arabic strings from ar.json into that PO._("Welcome, {}").format(name)_("Welcome, %(name)s", name=name)When including or extending vitrin-managed templates, prefix with vitrin:{template}.
| Old (Twig-like) | New (Jinja) |
|---|---|
{{ template_for_product_badge }} | {% include 'vitrin:products/badge.jinja' %} |
{{ template_for_product_variants_list }} | {% include 'vitrin:products/variants_list.jinja' %} |
{{ template_for_product_variants_dropdown }} | {% include 'vitrin:products/variants_dropdown.jinja' %} |
{{ template_for_product_payments_widget }} | {% include 'vitrin:products/payment_widgets.jinja' %} |
{{ template_for_product_custom_input_fields }} | {% include 'vitrin:products/custom_input_fields' %} |
{{ account_profile_template_content|safe }} | {% include 'vitrin:account/profile.jinja' %} |
{{ template_for_mazeed_badge }} | {% include 'vitrin:shared/mazeed_badge.jinja' %} |
{{ template_for_cart_products_list }} | {% include 'vitrin:cart/products_list.jinja' %} |
strReplace:{# Twig-ish #}
{{ strReplace(title, 'old', 'new') }}
{# Jinja #}
{{ title | replace('old', 'new') }}{{ session.url.include_query_params(page=page+1) }}{{ url_for('list_products') }}
{{ url_for('product_details', path_params={'id': product.id}) }}{{ format_datetime(order.created_at, format='medium', locale=session.lang) }}
{{ format_time(order.created_at, format='short') }}select/reject rely on tests/expressions (e.g., selectattr, rejectattr, equalto).%(name)s) over string concatenation for translatable messages.