It seems the blueprints form parser only accepts complete translatable expressions or literals. I looked through the source code to find this out but was pretty clueless where to look or what to search for.
I also looked through the large plugin blueprint of the Admin plugin itself and did not see any examples of doing this, though the language files do contain placeholders, that presumably are used in other contexts.
Can anyone confirm that this is a limitation, or if you have a workaround? Currently I guess my workaround is to require complete translation of the whole string expression.
(I suspect this problem also applies to plugins BTW.)
Hi @hughbris, dug a bit, and this is indeed a current limitation, specifically here. As an alternative, you can create a custom field or extend/override an existing one.
@hughbris, a simple example of a custom text field (with support for complex translation), seems to work correctly, maybe it will be useful for you.
Field template
path to templates => THEME_TEMPLATES/forms/fields/text_complex_trans/text_complex_trans.html.twig
text_complex_trans.html.twig
{% extends "forms/fields/text/text.html.twig" %}
{# Macro #}
{% macro complex_translation(attribute, translation_vars = [], markdown=false ) %}
{% set field_attribute = markdown ? attribute|markdown(false) : attribute %}
{% set vars = [] %}
{% for value in translation_vars %}
{% set vars = vars|merge([ value|t ]) %}
{% endfor %}
{% set field_attribute = field_attribute|t(vars) %}
{{ field_attribute|e }}
{% endmacro %}
{# Field Label #}
{% if field.label_complex_translation and field.label_translation_vars is not empty and field.label %}
{% set field = field|merge({ label: _self.complex_translation (field.label, field.label_translation_vars, field.markdown|defined(false) ) }) %}
{% endif %}
{# Field Help #}
{% if field.help_complex_translation and field.help_translation_vars is not empty and field.help %}
{% set field = field|merge({ help: _self.complex_translation (field.help, field.help_translation_vars, field.markdown|defined(false) ) }) %}
{% endif %}
Blueprint
example
header.complex_trans:
type: text_complex_trans
# LABEL
label: "THEME_MYTHEME.ADMIN.MYFIELD" #string with 2 variables (example)
label_complex_translation: true
label_translation_vars: [ 'one', 'two' ] #must have the same number of variables as in the label. supports translation
# HELP
help: "THEME_MYTHEME.ADMIN.MYFIELD_HELP" #string with 2 variables (example)
help_complex_translation: true
help_translation_vars: [ 'three', 'four' ] #must have the same number of variables as in the help string. supports translation
That is indeed the right code to override. I’ve noticed before, with similar problems I guess, that these form templates aren’t very well set up for overrides. I may have even raised an issue or submitted a PR about it. (I’ve been doing this so damned long!)
So I think on at least one site, I’ve extended that exact template but it’s unsatisfactory because there are no blocks defined in much of the template file. That means I need to copy, paste, and modify the whole file just to extend the label, and I’ll be doomed to manually check/update it in future form plugin upgrades.
A simple custom field is a great idea, thank you. It would be more robust although my first thought was that it seems like overkill for a label. I’ll have to think about whether bundling that into a theme adds complexity or consequences for the user. I don’t think so. I will try to remember to update this thread when I’ve tried it out.
Thanks for your contribution to the community, Hugh!
I also initially thought that creating a custom field would be unjustifiable if it meant having to override the default form field particle. However, I remembered there was another way, and decided to test it.
I’ve been trying this and having several aha moments which went nowhere. It took me some time to work out that my custom field template is being ignored. Since I have made custom fields in themes before, and comparing to what I did there (was similar to your code), the only difference seemed to be that we’re using blueprints and not user forms. Even if I try to override the field type (display) in the theme, the template isn’t picked up.
I could describe several alternative rabbit holes I went down. They might be useful to outline later.
For now, I’d just like to know: was your code built/tested with a front end form or a theme blueprint?
Yes, I tested this on a Page Blueprint and it seems to work on my end. Did you register/add the template directory to the twig lookup paths in your theme.php file using the onTwigTemplatePaths function? I suspect this is the point that might have been missed.
Here is the relevant part from my_theme.php as an example
public static function getSubscribedEvents()
{
return [
'onThemeInitialized' => ['onThemeInitialized', 0],
];
}
public function onThemeInitialized()
{
// If in an Admin page.
if ($this->isAdmin()) {
$this->enable([
'onTwigTemplatePaths' => ['onTwigTemplatePaths', 0],
]);
}
}
/**
* Add twig paths to theme templates.
*/
public function onTwigTemplatePaths()
{
$twig = $this->grav['twig'];
$twig->twig_paths[] = __DIR__ . '/templates';
}
Note: If your custom form field is needed on the frontend, you must move the onTwigTemplatePaths logic outside the if ($this->isAdmin()) block.
Regarding field overriding for the Admin Panel: if you override an existing core field (i.e., same name, same type, e.g., display), instead of creating a new one (e.g., display_complex_trans), you must use a different approach:
The standard path: user/themes/YOURTHEME/admin/themes/grav/templates/forms/fields/field_name/field_name.html.twig #the path refers to the admin plugin's internal structure
I also fell into a rabbit hole while testing my custom form field. The Twig macro I shared works perfectly in the admin but exhibits incorrect behavior in a frontend form. It seems that translation processing is handled differently in the two contexts.
The most stable solution I have achieved so far, which now works reliably in both cases, is shown below. But I think a more elegant solution could be found.
Updated Twig Macro Example
Current working macro (Max 5 arguments)
{# Macro #}
{% macro complex_translation(attribute='', translation_vars=[], markdown=false ) %}
{% set field_attribute = markdown ? attribute|markdown(false) : attribute %}
{% set args = [] %}
{% for value in translation_vars %}
{% set args = args|merge([ value|t ]) %}
{% endfor %}
{% set count = args|length %}
{% if count <= 5 %}
{% switch count %}
{% case 1 %}
{% set field_attribute = field_attribute|t(args[0]) %}
{% case 2 %}
{% set field_attribute = field_attribute|t(args[0], args[1]) %}
{% case 3 %}
{% set field_attribute = field_attribute|t(args[0], args[1], args[2]) %}
{% case 4 %}
{% set field_attribute = field_attribute|t(args[0], args[1], args[2], args[3]) %}
{% case 5 %}
{% set field_attribute = field_attribute|t(args[0], args[1], args[2], args[3], args[4]) %}
{% default %}
{% set field_attribute = field_attribute|t %}
{% endswitch %}
{{ field_attribute|e }}
{% else %}
{% set error_msg = 'Error: The complex_translation macro received ' ~ count ~ ' variables, but supports a maximum of 5.' %}
<div class="notices red"><p>{{ error_msg|raw }}</p></div>
{% endif %}
{% endmacro %}