Skip to content

Allow access to Source object from TwigFunction and TwigFilter #4627

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
SnowySailor opened this issue Apr 8, 2025 · 3 comments
Open

Allow access to Source object from TwigFunction and TwigFilter #4627

SnowySailor opened this issue Apr 8, 2025 · 3 comments

Comments

@SnowySailor
Copy link

SnowySailor commented Apr 8, 2025

We are currently working on doing translations for our website which makes heavy use of Twig templates, and we have a need for using our own custom translation logic that relies on knowing the current template that the translation is being done from.

With the current version of Twig, we are able to access the name of the "base" template that gets rendered (by passing it into the context at render time), but we aren't able to access the name of the templates that are included or extended if the translate filter is called from them.

Here is a very basic example:

templates/_wrapper.tpl

<html>
    <head>
        <title>{{ "title"|translate }}</title>
    </head>
    <body>
        {% block body %}
    </body>
</html>

templates/main.tpl

{% extends '_wrapper.tpl' %}

{% block body %}
<p>{{ "helloWorld"|translate }}</p>
{% endblock %}

index.php

MyTwigWrapper::render('templates/main.tpl');

TranslateExtension.php

// ......
new TwigFilter(
    'translate',
    fn (array $context, string $text) => $this->translate($context, $text),
    ['needs_context' => true]
)
// ......

In this situation, we could use the $context to pull the templates/main.tpl template name (it's passed in via our twig wrapper function), however we run into an issue when we hit the templates/_wrapper.tpl's call to "title"|translate because we only have the templates/main.tpl file name in our $context and we don't know that the translate filter is being called from a different template.

As a solution, we would like to add the Source object as an optional parameter (needs_source) for Filters and Functions. This Source object has information about the templated that initiated the call to the filter/function, and we can use it to extract the name of the template for use in our translation logic. This would be done in the same way as it's done with needs_context, needs_env, etc. An additional parameter (the Source object) would be added onto the end of the function call and would be made available to the custom filter/function.

Perhaps there is a better way to approach this, however. If so, please let me know because I haven't been able to figure it out. If we can go forward with adding needs_source I'm happy to implement that. I've got a version working with a slightly older version of Twig locally, so applying it to the current version shouldn't be much trouble. Since it would be a completely new parameter that's false by default it shouldn't have an effect on existing code and should be a completely non-breaking change.

@SnowySailor
Copy link
Author

SnowySailor@4830dc9

I made the changes on my fork and pulled them into our repo and it seems to be working. I'll have to do some more rigorous testing and see about writing some unit tests, but I'm able to access the Source object by adding needs_source now.

@stof
Copy link
Member

stof commented Apr 8, 2025

You could already implement such callable today using a custom node class (which allows to customize how the callable call is compiled).

I'm not sure we want to have this needs_source option in Twig core. Making the behavior of a callable depend on where it is called from can make the behavior of that callable very confusing (depending on what the logic does) and is quite uncommon in programming languages.

@SnowySailor
Copy link
Author

@stof

I'm not sure we want to have this needs_source option in Twig core. Making the behavior of a callable depend on where it is called from can make the behavior of that callable very confusing (depending on what the logic does) and is quite uncommon in programming languages.

Totally understand that it's not a typical thing that someone would want to do. The use case is using a single json translation file per template instead of maintaining a single giant file which is much more prone to merge conflicts and bloat. This means we need to load each json file per-template, which requires access to the template name/path (hence the Source object).

You could already implement such callable today using a custom node class (which allows to customize how the callable call is compiled).

Wasn't aware of this. Do you have an example that could help me get started? I'm not getting any real results on Google when I'm looking this up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants