2

I'm using the https://api.drupal.org/InlineTemplate to render a string with a variable, and that variable is itself another theme function.

My reading of the documentation is that this is supported; based on the fact that the #context variable can be a string or render array.

#context: (array) The variables to substitute into the Twig template. Each variable may be a string or a render array.

However, my implementation is not working:

    $my_subtemplate = [
      '#type' => 'inline_template',
      '#template' => '{% trans %}my {{ foo }} here{% endtrans %}',
      '#context' => ['foo' => 'bar'],
    ];

    $my_string_with_embedded_themable = [
      '#type' => 'inline_template',
      '#template' => '{% trans %}Print this string and this subtemplate: {{ my_subtemplate }}.{% endtrans %}',
      '#context' => [
        'my_subtemplate' => $my_subtemplate,
      ],
    ];

The generated output is:

Print this string and this subtemplate: .

I've confirmed that my_subtemplate generates the correct output on its own:

my bar here

But when its placed into a variable of another inline template, it does not render.

4
  • 1
    Even if this worked it would not be a valid use case for interface translation, which is supposed to be used for string constants and not for strings after variable placeholders got replaced. And in inline templates defined in PHP you don't need Twig methods to translate because you can use t().
    – 4uk4
    Commented Dec 29, 2019 at 18:56
  • I get your point that you could use t(), but otherwise, this comment ^ doesn't make sense. The example in the core documentation linked above uses the trans Twig function, and the trans Twig functions itself supports variables as placeholders, for position purposes. IMHO the bug here is that the trans Twig function doesn't support render array variable expansion, while other Twig functions do, eg spaceless. Commented Dec 29, 2019 at 19:32
  • Regarding position purposes, I mean 'Hello {{name}}' might be translated in another language by putting the name first, so the translation in some other language would be '{{name}} ASDF' (where ASDF is the translation of Hello). Commented Dec 29, 2019 at 19:37
  • To make it work I would use t() instead of renderRoot. Depending on where you place this code it is bad for caching and attachments or can cause a nested renderRoot error.
    – 4uk4
    Commented Dec 29, 2019 at 20:23

2 Answers 2

4

The issue not in the inline_template but the issue comes from {% trans %} if you place a render array variable into {% trans %}{% endtrans %} you will get the following error and it will not rendered:

Warning: htmlspecialchars() expects parameter 1 to be string, array given in Drupal\Component\Utility\Html::escape()

So you have two options:

1) get the variable out of {% trans %}{% endtrans %} like the following:

    $my_subtemplate = [
      '#type'     => 'inline_template',
      '#template' => '{% trans %} my {{ foo }} here {% endtrans %}',
      '#context'  => [
        'foo' => 'bar'
      ],
    ];
    $my_string_with_embedded_themable = [
      '#type'     => 'inline_template',
      '#template' => '{% trans %} Print this string and this subtemplate: {% endtrans %} {{ my_subtemplate }} .',
      '#context'  => [
        'my_subtemplate' => $my_subtemplate,
      ],
    ];

2) Pre-render the renderable array variable to a string first.

    $my_subtemplate = [
      '#type'     => 'inline_template',
      '#template' => '{% trans %} my {{ foo }} here {% endtrans %}',
      '#context'  => [
        'foo' => 'bar'
      ],
    ];

    $renderer = \Drupal::service('renderer'); 
    $my_subtemplate = $renderer->renderRoot($my_subtemplate); // this fixes it

    $my_string_with_embedded_themable = [
      '#type'     => 'inline_template',
      '#template' => '{% trans %} Print this string and this subtemplate: {{ my_subtemplate }}.{% endtrans %}',
      '#context'  => [
        'my_subtemplate' => $my_subtemplate,
      ],
    ];
2
  • 1
    wow, that is very interesting. Thanks for the quick response. I've added a comment to the documentation (link in OP) and expanded your answer to provide the other alternative -- to render the renderable to string first. Commented Dec 29, 2019 at 18:22
  • 1
    You are welcome, i'm glad that helped, great i read your comment in documentation, good job!
    – berramou
    Commented Dec 29, 2019 at 18:25
0

I like to also use inline_template, as a way to escape from the limitations of the drupal render arrays.

I would solve it with a single include to have templates as proper twig templates and not mixed with the logic (MVC approach + better syntax highlighting):

$my_string_with_embedded_themable = [
  '#type' => 'inline_template',
  '#template' => "{% include '@myTheme/path/my-template-file.html.twig' %}",
  '#context' => [
     'foo' => 'bar',
     'my_subtemplate' = 'my_subtemplate'
  ]
]

And then 'web/themes/myTheme/templates/path/my-template-file.html.twig':

<p>
{% trans %}Print this string and this subtemplate:{% endtrans %}
{% include '@myTheme/' ~ my_subtemplate %}.
</p>

The above example does not really require the include in the include, but the real approach is probably more complex anyway.

so 'web/themes/myTheme/templates/' + my_subtemplate:

{% trans %} my {{ foo }} here {% endtrans %}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.