Twig: How to concat and translate strings? - php

I tried the answers of this question, but it's not working. It's weird and I cannot reproduce the behavior:
I cleared the cache
Concat works (it output's the right string)
The concated string as a variable is not translateable
The concated string pasted as a string is translateable
Edit: obj2arr casts the object to an array, to make it iterable. prepareForTwig is just using trim(), etc. - the string is outputted correctly.
Edit 2: {% set transVar = (key|prepareForTwig) %} (without prefix) doesn't work as well.
yml:
# Resources/translations/messages.en.yml
my:
keywords:
keyword1: K1
keyword2: K2
# ...
twig:
{# my.twig.html #}
{% for key, value in data|obj2arr %}
{% set transVar = 'my.keywords.' ~ (key|prepareForTwig)) %}
{{ transVar }}<br/> {# output, e.g.: my.keywords.keyword1 #}
{{ transVar|trans}}<br/> {# output, e.g.: my.keywords.keyword1 #}
{{ 'my.keywords.keyword1'|trans }} {# output: K1 #}
{% endfor %}
EDIT:
CustomTwigExtension.php
class CustomTwigExtension extends \Twig_Extension
{
public function getFilters()
{
return array(
new \Twig_SimpleFilter('obj2arr', array($this, 'obj2arrFilter')),
new \Twig_SimpleFilter('prepareForTwig', array($this, 'prepareForTwigFilter')),
);
}
public function obj2arrFilter($obj)
{
return (array)$obj;
}
public function prepareForTwigFilter($str) {
$str = trim($str);
$str = strtolower($str);
$str = substr($str, 2, strlen($str)); // obj2arr() prefixes "*"
return $str;
}
public function getName()
{
return 'custom_twig_extension';
}
}
Thanks in advance!

Others examples didn't seem to work in my case, this is what I ended up using:
{{ "my.prefix.#{ myVariable }.my.postfix" | trans }}
The translation string has to be between double quotes.

Every answer of outputting concated translationstrings was right, example:
{% 'my.prefix.' ~ extendByKeyword | trans }}
The problem was a weird generated space:
(array) $obj added * (there are two spaces after the star) as a prefix to the key-variable.
I used substr() to get rid of the * (star + 1 space), but didn't notice/expect the second space.
After comparing the strings with strlen(), I found the cause.
Thanks to #Artamiel and #CarlosGranados for your help.

Related

Twig merge for json with string of numbers as key

I'm trying to merge hashes with Twig to output JSON.
My problem is that some of my keys are using strings of numbers and twig converts it to integers.
My code :
{% set rows = {} %}
{% for key, val in row %}
{% set rows = rows|merge({ (key) : val }) %}
{% endfor %}
{{ { 'report': { 'metric': metric, 'rows': rows, 'tot': tot, 'min': min, 'max': max } }|json_encode|raw }}
Which outputs
{"report":{"metric":"sessions","rows":["5","4","4","3","7","4","4"],"tot":"31","min":"0","max":"7"}}
I also tried replacing my keys with number_format, but since I'm stripping all non-numeric characters, the output is the same.
{% set rows = {} %}
{% for key, val in row %}
{% set rows = rows|merge({ (key)|number_format(0,'','') : val }) %}
{% endfor %}
{{ { 'report': { 'metric': metric, 'rows': rows, 'tot': tot, 'min': min, 'max': max } }|json_encode|raw }}
The result expected goes like this:
{"report":{"metric":"sessions","rows":{"20180423":"5","20180424":"4","20180425":"4","20180426":"3","20180427":"7","20180428":"4","20180429":"4"},"tot":"31","min":"0","max":"7"}}
Is there any way I can prevent Twig from changing my keys to integers?
Found this post, but it doesn't work for me since my keys are strings of numbers.
key value being replaced by 'key' when using merge() in twig
Twig's merge filter relies on PHP's array_merge function and the doc says :
Values in the input array with numeric keys will be renumbered with
incrementing keys starting from zero in the result array.
And string containing only numbers are considered numeric.
Solution:
The simplest solution would be to change the keys format from "20180423" to "2018-04-23" which would make it non-numeric.
If you really need to keep your numeric keys, you could create a custom filter to merge arrays the way you want:
namespace App\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
class AppExtension extends AbstractExtension
{
public function getFilters()
{
return array(
new TwigFilter('mymerge', array($this, 'merge')),
);
}
public function merge($baseArray, $arrayToMerge)
{
foreach ($arrayToMerge as $key => $value) {
$baseArray[$key] = $value;
}
return $baseArray;
}
}
Then
{% set test = {"1234": "2", "2345": "3"} %}
{% set rows = {"test": "1"} %}
{% set rows = rows|mymerge(test) %}
{{ { 'report': { 'rows': rows } }|json_encode|raw }}
Would output
{"report":{"rows":{"test":"1","1234":"2","2345":"3"}}}

Display split results in Symfony2 twig

I have a function in my Symfony controller which returns the following:
test+testString1+test2
TestController.php
public function getResultAction($fileName) {
$string1="test";
$string2="testString1";
$string3="test2";
$response = $string1."+".$string2."+".$string3;
return new Response($response);
}
In my twig I've rendered the function in my controller:
test.html.twig
{% set test %}
{{ render(controller('TestBundle:Test:getResult')) }}|split('+', 4)
{% endset %}
{{ test[0] }}
I'm using twig split filter so that I can display test, testString1 and test2 individually. But then whenever I attempt to display test[0], I receive the following error:
Impossible to access a key "0" on an object of class "Twig_Markup" that does not implement ArrayAccess interface in TestBundle:Test:test.html.twig
What's wrong with what I'm doing? I hope you could help me with this. Thanks
You miss the split filter inside the double brace as follow:
{% set test %}
{{ render(controller('TestBundle:Test:getResult')) |split('+', 4) }}
{% endset %}
Hovenever seems same problem with the set tag. From the doc:
The set tag can also be used to 'capture' chunks of text
Try with:
{%
set test=render(controller('TestBundle:Test:getResult'))|split('+', 4)
%}
Hope this help

Symfony2 translation yaml array and twig loop

I am trying to have twig convert an array from the translation file
// messages.en.yml
termsAndConditions:
title: Terms and Conditions
paragraph:
- Paragraph text...blah blah...1
- Paragraph text...blah blah...2
- Paragraph text...blah blah...3
- Paragraph text...blah blah...n
// termsandconditions.html.twig
// tried...
{% for i in range(1,termsAndConditions.paragraph|length) -%}
<p>{% trans %}termsAndConditions.paragraph. {{ i }}{% endtrans %}</p>
{%- endfor %}
// and tried...
{% for i in range(1,termsAndConditions.paragraph|length) -%}
<p>{{ 'termsAndConditions.paragraph.' ~ i ~ |trans }}</p>
{%- endfor %}
You need to use pairs of keys: values to get things to work:
// messages.en.yml
termsAndConditions:
title: Terms and Conditions
paragraph:
1: Paragraph text...blah blah...1
2: Paragraph text...blah blah...2
3: Paragraph text...blah blah...3
4: Paragraph text...blah blah...n
Also and due to the fact that you want to use a variable for your translation, access to the translation this way {{('termsAndConditions.paragraph.'~i)|trans }}.
I've hardcoded 4 instead of termsAndConditions.paragraph|length. Not really sure if you can access that from a twig template...
// termsandconditions.html.twig
{% for i in range(1,4) -%}
<p>{{('termsAndConditions.paragraph.'~i)|trans}}</p>
{%- endfor %}
It should work. Hope it helps.
UPDATE
termsAndConditions.paragraph|length makes no sense unless you've defined in the template the variable or you've injected the variable through the controller.
Solutions
Solution 1. In your controller, access the yaml and get the number of translations, then pass it to the template and that's it.
Solution 2. Create a service and inject the translator service to it. In the controller create a method that calculates the number of elements of a particular key. Injecting the translator service is better than reading the yaml directly as it caches the catalogue when it reads it.
Solution 3. Same as 2 but creating a twig filter. I'm going to go for this one as it seems kind of fun.
Solution 3
Let's start by creating the Extension:
namespace Acme\DemoBundle\Twig\Extension;
class TransLengthExtension extends \Twig_Extension
{
private $translator;
public function __construct($translator) {
$this->translator = $translator;
}
public function getFilters()
{
return array(
new \Twig_SimpleFilter('translength', array($this, 'translengthFilter')),
);
}
public function translengthFilter($id, $domain = null)
{
if (null === $domain) {
$domain = 'messages';
}
$i = 0;
$g = $this->translator->getMessages();
foreach($g["messages"] as $key => $message) {
if(substr( $key, 0, strlen($id) ) === $id ) {
$i++;
}
}
return $i;
}
public function getName()
{
return 'acme_extension';
}
}
As you can see above, it calculates the number of $id occurrences in the translation catalogue. Translator takes care of locale, loading the appropiate catalogue and so on. It also caches results which is really good in terms of performance.
Register the service injecting the translator and registering as a twig extension:
services:
acme.twig.acme_extension:
class: Acme\DemoBundle\Twig\Extension\TransLengthExtension
arguments:
- "#translator"
tags:
- { name: twig.extension }
Now, in your template:
{% set end = 'termsAndConditions.paragraph'|translength %}
{% for i in range(1,end) -%}
<p>{{('termsAndConditions.paragraph.'~i)|trans}}</p>
{%- endfor %}
Hope it helps.
There's a twig filter for this - keys
{% for keyname in yml_tree_nodes|keys %}

How to add a delimiter in twig to a string?

i have time in format HHMM as string and would like to add " : " after hours. is there a simple filter to split string after two characters and add a delimiter?
You can split by position like this
{% set bar = "aabbcc"|split('', 2) %}
{# bar contains ['aa', 'bb', 'cc'] #}
As described in the doc
So you can do something like:
{% set bar = "1203"|split('', 2) %}
{# bar contains ['12', '03'] #}
now let's do:
{{ bar[0]~":"~bar[1] }}
this will output:
12:03
Better if you build a macro or a TWIG extension with the function
Hope this help

Symfony2 Array to string conversion, set empty value

I`am rendering a twig template in symfony2 and its possible to get a variable with value of array type but i am trying to preview the variable and this error pops:
Array to string conversion ...
So my qustion is not how to print out the array, but is it possible in twig to disable this error, so when an array comes and it cannot be outputed, to set an empty value.
In config.yml, under twig i have set
strict_variables: false
But this dose not include printing arrays.
EDIT:
In controller I pass this array to twig, for example:
'data' => array(
'description' => array(
'desc1',
'desc2'
)
);
In twig I am trying to print {{ data['description'] }} and I got the following error:
Array to string conversion ...
This is the normal error that must pop, but I need if I try to print this a no error to pop but instead I need to print nothing(empty value), same way like trying to print an non existing variable with twig set with
strict_variables: false
ANSWER:
Make a custom function:
public function getFunctions()
{
return array(
'to_string' => new \Twig_Function_Method($this, 'toString')
);
}
public function toString($data) {
if(is_array($data)) {
return '';
}
return $data;
}
And in twig template:
{{ to_string(data['description']) }}
You have two desc: desc1 and desc2, what do you want to print? If both you have to use this notation (for instance)
{{ data['description'][desc1] }} - {{ data['description'][desc2] }}
More in general:
{% for desc in data['description'] %}
{{ desc }}
{% endfor %}
EDIT
Ok, now I got it (from your comment):
{% if data['description'] is iterable %}
//echo here an empty string
{% else %}
{{ data['description'] }}
{% endif %}

Categories