I am trying to have my twig templates translated using the Symfony Translator Component and .mo files. I used the i18n extention before but wanted a more reliable approach since locale handling for translations on Windows is a nightmare.
These class functions prepare the translation and template:
/**
* Constructor.
*
* #param string $template_dir
* #param string $locale
* #param string $locale_path
*/
public function __construct($template_dir, $locale, $locale_path)
{
$loader = new Twig_Loader_Filesystem($template_dir);
$this->parser = new TemplateNameParser();
$this->template = new \Twig_Environment($loader);
$this->translator = new Translator($locale);
$this->translator->addLoader('mo', new \Symfony\Component\Translation\Loader\MoFileLoader());
$this->translator->addResource('mo', $locale_path, $locale);
$this->template->addExtension(new TranslationExtension($this->translator));
}
/**
* Render template.
*/
public function render($name,$parameters=[]) {
return $this->template->loadTemplate($name,$parameters)->render();
}
Then i have this template:
<h1>{% trans 'Hello World!' %}</h1>
which throws this error:
Uncaught Twig_Error_Syntax: Unexpected token. Twig was looking for the
"with", "from", or "into" keyword.
Which i get because i am not adding the Twig_Extensions_Extension_I18n extension to twig environment. If i do that, the texts in trans functions are not translated because i am not using the filter as i should. For it to work i need to use the trans filter like so: {{ 'Some text'|trans }}.
Is there a way to make translation work with {% trans 'Some text' %} instead of {{ 'Some text'|trans }}? For example, can i add a custom trans function somewhere in the chain?
Note: I know that {% trans %}Some text{% endtrans %} works, but all my templates already use this syntax {% trans 'Some text' %}, and i would like to prevent having to rewrite everything.
The problem seems to stem from incompatible twig and symfony translator versions. But i am not sure tbh.
In my case, i solved the problem long-term by writing a simple script to replace the incorrect syntax with the correct one in each template file.
foreach (glob("your_template_path/*/*/*.twig") as $filename) {
$content = file_get_contents($filename);
$content = preg_replace_callback('/{% trans "[\s\S]+?" %}/',function($matches) {
$text = str_replace(['{% trans','%}','"'],'',$matches[0]);
return '{% trans %}'.trim($text).'{% endtrans %}';
},$content);
$content = preg_replace_callback('/{% trans \'[\s\S]+?\' %}/',function($matches) {
$text = str_replace(['{% trans','%}',"'"],'',$matches[0]);
return '{% trans %}'.trim($text).'{% endtrans %}';
},$content);
file_put_contents($filename,$content);
}
Maybe that helps someone.
Try this
"{% trans %}Hello World{% endtrans %}!"
Related
I'm trying to generate EasyAdmin3 url inside my template with some params, but for some reason they are not present in a controller.
Twig template:
xxx
yyy
Error with missing EA context:
zzz
Controller:
/**
* #Route("/admin/something/{id}", name="rounte_name")
*/
public function xyz($id = null, Request $request, AdminContext $context)
{
dd($_GET['id'], $request->request->all(), $context->getRequest()->request->all());
...
}
The $_GET['id'] works, but request and context are empty [].
Any idea how to generate route by name with params?
Thanks
I don't think you need the ea_url() helper function if you are just generating regular named routes in twig. You should be able to use the path() twig extension provided by Symfony.
{{ path(route_name, route_parameters = [], relative = false) }}
If you are trying to create a link to an EasyAdmin controller action, then you can use the ea_url() helper, but you have to specify the controller and action as well. Try something like:
{% set url = ea_url()
.setController('App\\Controller\\Admin\\FoobarCrudController')
.setAction('customFooAction')
.setEntityId(entity.instance.id)
.set('myParam', 'baz') %}
Custom Foobar Action
Then in your controller, everything should be available as per usual via the $context variable...
public function customFooAction(AdminContext $context)
{
$entity = $context->getEntity()->getInstance();
$myParam = $context->getRequest()->get('myParam');
}
This works for me, I hope it helps. 🙂
How can I pass custom options directly to the Twig template of a custom Symfony 3.4 FormType?
Context:
I have created a custom FormType which also uses a custom Twig template to render its form widget. The build-in options (e.g. translation_domain, lable_attr, etc.) are directly accessible in the template. How can I achieve the same for my custom options?
// MyCustomType.php
class MyCustomType extends AbstractType {
public function getBlockPrefix() {
return 'my_custom_field';
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults([
'custom_option' => 'customOptionValue',
'translation_domain' => 'customTranslationDomain',
]);
$resolver->setAllowedTypes('custom_option', ['string']);
}
}
// form_widgets.html.twig
{% block my_custom_field_widget %}
{{Â dump() }}
{{ translation_domain }}
{#{ custom_option }#}
{% endblock %}
Of course this form widget is not very useful, it only dumps the available variables and the content of translation_domain which is directly accessible. Adding {{ custom_option }} will lead to an error, since this option/variable is not available. Neither directly nor via form.vars.custom_option.
Only when setting the option as form var, it can be accessed in the template:
// MyCustomType.php
class MyCustomType extends AbstractType {
...
public function buildView(FormView $view, FormInterface $form, array $options) {
$view->vars['custom_option'] = $options['custom_option'];
}
}
// form_widgets.html.twig
{% block my_custom_field_widget %}
// fails
{{ custom_option }}
// works
{{ form.vars.custom_option }}
{% endblock %}
So, all built-in options are available in form_vars but can also be accessed directly (e.g. as form.vars.translation_domain' AND as translation_domain`).
How do I achieve the same for my custom options?
Of course accessing the option via form.vars... is no problem. However, I wonder if this is the correct solution since the build-in types are accessible directly. Are these just shortcuts (how are they added?) to the form.vars... or is this some other configuration?
I want to implement translation in Symfony2 using csvFileLoader. In config.yml translation field is enabled and locale set to fr.
I have written following code in controller.
$file = //file path eg: messages.fr.csv;
$loader = new CsvFileLoader($file);
$loader->setCsvControl(',');
$x = $loader->load($file, 'fr');
$translator = new Translator('fr',new MessageSelector());
$translator->addLoader('csv', $loader);
$translator->addResource('csv', $file, 'fr','messages');
$translator->trans('Symfony is great');
Above code works fine. If I use trans tag in twig then text is not translated. Even I add twig extentions:-
$loader = new \Twig_Loader_Filesystem("path to twig template file");
$twig = new \Twig_Environment($loader);
$twig->addExtension(new TranslationExtension($translator));
And Code in witten in twig file
{% trans %}Symfony2 is great{% endtrans %}
Above text is not translated in twig. I tried it using .xlf file then it works but for .csv file translation is not working.
Need solution for above mentioned issue.
Controller :
/**
* #Route("{_locale}/translate1")
*/
public function showTwoAction(Request $request)
{
$delimiter = ";";
$enclosure = '"';
$escape = '\\';
$file = __DIR__.'/file_'.$request->getLocale().'.csv';
$translator = new Translator($request->getLocale(), new MessageSelector());
$translator->addLoader('csv', new CsvFileLoader());
$translator->addResource('csv', $file, $request->getLocale());
$catalogue = $translator->getCatalogue($request->getLocale());
$messages = $catalogue->all();
while ($catalogue = $catalogue->getFallbackCatalogue())
{
$messages = array_replace_recursive($catalogue->all(), $messages);
}
return $this->render("PRIYACoreTranslateBundle:Default:translate.html.twig",$messages);
}
Twig :
{{ messages.Hi }}
{{ messages.Hello }}
Translation CSV Files
=> file_en.csv
"Hi";"Hi"
"Hello";"Hello"
=> file_fr.csv
"Hi";"salut"
"Hello";"Bonjour"
Hope it helps.
I have this controller where \Exception is raised (I haven't figured out which SF2 Exception to use yet) upon certain condition. Here is it:
<?php
namespace My\AppBundle\Controller;
use ....
class MyController extends Controller
{
const EXCEPTION_MESSAGE = <<<EOF
My <b>HTML</b>
<br/>
<small>Small phrase</small>
EOF;
public function indexAction()
{
// my logic
if(in_array($data, $array))
throw new \Exception(self::EXCEPTION_MESSAGE);
// the rest of my logic
return ....
}
}
And in app/Resources/TwigBundle/views/Exception/error.html.twig
{% extends '::base.html.twig' %}
{% block body %}
<h2>Error</h2>
<p>{{ exception.message }}</p>
{% endblock %}
The problem is HTML is not rendered when seeing the error page in prod environement.
I tried {{ exception.message|raw }} and also setting autoescape to false as per this answer but it seems to have no effect.
How can I do to make HTML works when displaying the \Exception message in Twig?
Where ever in the code you catch the exception is where it is needed to be added to the array you pass to twig. for example
$vars = [];
try {
$a->indexAction();
//fill $vars
} catch (Exception $e) {
$vars['exception'] = $e;
}
//pass $vars to twig
I want to have a URL that returns a simple JSON object. I am trying to use Twig to generate the JSON object:
{
"urls": [
{% for child in page.root %}
"{{ child.url }}"{% if not loop.last %},{% endif %}
{% endfor %}
]
}
The carriage returns will not remain in place though, and I keep getting a result that looks like this:
{'urls':['../ants/','../brick-report/','../the-pollution-intervention/','../barclay/','../broken-advertising/','../aldat-n-densom/','../thisisart/','../there-she-goes-again/']}
which Jquery will not parse with it's ajax or getJSON methods. It's totally ignoring this JSON. How might I convince Twig to put the right whitespace in place? I've looked at the manual and it only seems concerned with NOT inserting whitespace.
This works for me (twig template):
var parsedJSON = JSON.parse('{{ ['one', 'two', 'three']|json_encode|e('js') }}');
And this:
console.log(parsedJSON);
outputs:
Array ["one", "two", "three"]
in browser's console.
Twig has a filter for this.
json_encode, it uses PHP json_encode function.
for your case:
{{ {'urls': page.root}|json_encode }}
will output
{"urls":["..\/ants\/","..\/brick-report\/","..\/the-pollution-intervention\/","..\/barclay\/","..\/broken-advertising\/","..\/aldat-n-densom\/","..\/thisisart\/","..\/there-she-goes-again\/"]}
the code is tested and works. For more information take a look at the Twig Documentation for json_encode.
Generally it would make more sense to make controller return json directly, by returning JsonRespnse object
But if you really need to output JSON in Twig and assign it to variable, you can also use:
let foo = {{ bar|json_encode|raw }}
Don't use Twig to generate your json response.
In your controller, use:
return new Response(json_encode($var));
Sample:
public function sampleAction()
{
$urls = array('../test', '../something', '../sample');
return new Response(json_encode($var));
}
If URLs are generated from Symfony2 routes, you can use:
public function sampleAction()
{
$urls = array(
$this->generateUrl('my_test'),
$this->generateUrl('my_something'),
$this->generateUrl('my_sample'),
);
return new Response(json_encode($var));
}
Try wrapping your template in an autoescape block:
{% autoescape 'js' %}
{ "href": "{{ my_url }}" }
{% endautoescape%}
Thats easy if you extend twig.
First, create a class that will contain the extension:
<?php
namespace Acme\DemoBundle\Twig\Extension;
use Symfony\Component\DependencyInjection\ContainerInterface;
use \Twig_Extension;
class VarsExtension extends Twig_Extension
{
protected $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function getName()
{
return 'some.extension';
}
public function getFilters() {
return array(
'json_decode' => new \Twig_Filter_Method($this, 'jsonDecode'),
);
}
public function jsonDecode($str) {
return json_decode($str);
}
}
Basically the $.getJson() method requires json but ther is a string
so you can use $.get() to get the response and use the parser to parse the string to JSON
$.get("ffff",function(data){
// user parser
JSON.parse();
});