I want to add some HTML to Symfony 2 Translation. This way I will know which phrases in my application are translated and which not.
I found in "Symfony\Component\Translation\Translator.php" function "trans". Now i want to add something in function return, for example "< /br>":
/**
* {#inheritdoc}
*
* #api
*/
public function trans($id, array $parameters = array(), $domain = null, $locale = null)
{
if (null === $locale) {
$locale = $this->getLocale();
} else {
$this->assertValidLocale($locale);
}
if (null === $domain) {
$domain = 'messages';
}
if (!isset($this->catalogues[$locale])) {
$this->loadCatalogue($locale);
}
return strtr($this->catalogues[$locale]->get((string) $id, $domain)."</br>", $parameters);
}
The issue is that when I run my application I'm getting for example "Tag< / b r>" (I have add spaces because in normal way it doesn't show here. HTML doesn't interprate this as HTML code but as a string. Is there any way to achieve what I want ? Maybe it is but in the other way ?
This happens because you have the Twig Escaper extension active. That extension adds automatic output escaping to Twig, it defines the autoescape tag and the raw filter.
So I think the best option you got here is to define a new twig extension to let you translate your html strings without having to repeat myvar|raw each time.
To see how it is possible to create a new Twig extension please check the docs here.
Use the same extension when escaping for JS and there should be no need to use anything else especially in your PHP controllers. That's because the escaping is done at the Twig level. Just remember to declare your new Twig filter as safe to avoid automatic escaping again:
$filter = new Twig_SimpleFilter('nl2br', 'nl2br', array('is_safe' => array('html')));
If you need to do some extra processing with the requested data so that you can track what strings are being requested and what not then just declare a new service as a proxy to the Symfony translation one. Your Twig extension can use the same service. This way you can converge all the requests to one single service.
Here a few useful links for you:
http://twig.sensiolabs.org/doc/api.html#escaper-extension
http://twig.sensiolabs.org/doc/advanced.html#automatic-escaping
I'll suggest you simply to use Markdown in your translation.
Then you can parse your translated message with a Markdown parser.
Example in Twig: 'my.message'|trans|markdown (I suppose you have a Markdown filter, there is KnpMarkdownBundle)
MY SOLUTION:
I have achieved what I wanted in few steps:
Override Translator.php and change translator.class in parameters.yml
public function trans($id, $parameters, $domain, $locale)
{
$return = parent::trans($id, $parameters, $domain, $locale);
return "".$return.'');
}
Set translation class in your css.
Set autoescape option to false in parameters.yml
twig:
autoescape: false
Related
I am starting to add multi-language to a site that uses Laravel for frontend.
However, due to lack of resources at the moment, I am translating the strings as I go. It will be a while before whole site is translated.
The only challenge is that if a text is not translated what get displayed is the key. I would like to specify a default/fallback string for such.
e.g.
{{ trans('site.'.$name) }}
If I pass 'Business' as $name and there' no translation for 'Business' in site.php lang file I end up with site.Business on the frontend. This messes up everything. In the worst case, if there's no site.Business, Laravel should output Business.
Even better it should provide an option for default/fallback string.
Is this possible?
On a side note is there a free translation for common words? This will save time having to translate everything myself.
Thanks.
The fallback language is what you should be using. See the docs
You may also configure a "fallback language", which will be used when
the active language does not contain a given language line. Like the
default language, the fallback language is also configured in the
app/config/app.php configuration file:
'fallback_locale' => 'en',
It will surely take you just as much time writing in a fallback inline as it would simply writing in the fallback in a parallel translation file as you write in the translation key. The time spent thinking of an alternate way versus just doing it is going to be negligible in the end.
If you really want an inline fallback, then you need to create a new helper method that does something different. So prepare yourself for some home brewed awesomeness.
Let's create a new function that we can use in any view. I will use the method described by Joseph Sibler. Create a file called helpers.php inside app. Then add this to your composer.json in the autoload object under a files array as "app/helpers.php". Not sure what I mean? See his answer. After adding, run composer dump-autoload.
Now, let's add a trans_fb() method that will take all the parameters of the trans() method, but with a fallback as well. I will define this method such that the first two arguments are required, (the key, and the fallback).
If Laravel cannot find the translation key (it searches in resources/lang/en/auth.php for example auth.failed as a key) it will use the fallback instead, and pass any other optional arguments for the original method.
<?php
if (! function_exists('trans_fb')) {
/**
* Translate the given message with a fallback string if none exists.
*
* #param string $id
* #param string $fallback
* #param array $parameters
* #param string $domain
* #param string $locale
* #return \Symfony\Component\Translation\TranslatorInterface|string
*/
function trans_fb($id, $fallback, $parameters = [], $domain = 'messages', $locale = null)
{
return ($id === ($translation = trans($id, $parameters, $domain, $locale))) ? $fallback : $translation;
}
}
You can then use this in a template like so:
{{ trans_fb("i.love.laravel", "I love Laravel!") }}
I came up with this solution which uses the same parameters as trans() method and when key is not translated, it returns the translated string using the application 'fallback_locale' from app.php.
I still wonder why it's not implemented natively in Laravel and Laravel just returnes untranslated key.
if (!function_exists('trans_fb')) {
function trans_fb(string $key, array $replace = [], ?string $locale = null)
{
$translation = trans($key, $replace, $locale);
if ($key === $translation) {
return trans($key, $replace, config('app.fallback_locale'));
}
return $translation;
}
}
Hi I have a form element error and it keeps breaking it when I wrap it in the translation function. I am using gettext for the translation.
I understand that if I have it set in the registry and Zend_Form it should pick it up automatically but how does poedit see it if at all?
My Bootstrap (Relevant Part):
// Set the instance of Zend_Translate in the registry
$registry->set('Zend_Translate', $translate);
// Set an instance of Zend Translate object for validators
Zend_Form::setDefaultTranslator($translate);
My Error Form:
public function formErrors(Zend_Form $form)
{
$registry = Zend_Registry::getInstance();
$translate = $registry->get('Zend_Translate');
$form->setTranslator($translate);
if ($form->getMessages()) {
$error = '<p class="errorBox">Error text here</p>';
$error->setTranslator($translate);
$error->getView()->translate($error);
return $error;
}
return '';
}
Note:
$this->translate('string to translate'); or $this->getView()->translate('string');
works everywhere else but not here
The usage within a form which I used on the last project was
$this->getTranslator()->translate('text to translate')
This was only used minorly as we had auto-detect resource paths so we did not need to call it. The way you want to use it does seem slightly different. I would recommend adding the extra call after getView() though as it could be the solution.
Using the Zend Framework and the url method for the view:
$this->url(array('field1' => this, 'field2' => 'is', 'field3' => 'my example'), 'route_name');
Where route_name is the name of the url route and each field# is retrieved from the database.
I noticed that by default it changes spaces in Controller/Action names into plus sign so that what looked like:
www.example.com/this is my example
to
www.example.com/this+is+my+example
I would like to change the separatoer from + to - to have something like
www.example.com/this-is-my-example
I know that another thread: How to change the separation character of Zend Url?
as documented a way to do it which I tried without success.
A thorough explanation on how to do it would be much appreciated.
EDIT2: I know where the problem lies if anyone is interested, it comes from the way the url is assemble, it uses urlencode which converts all non-alphanumeric characters expect - and _ and the spaces as +, there is no way to override that than replace the character create the url manually (as Maxime suggested) or create a custom url function replacing the characters (as suggest by aporat)...
Thanks!
If you really want to do that, you can extend the stock Zend_View_Helper_Url view helper and add your url logic into your view helper.
<?php
namespace Application\View\Helper;
class MyUrl extends \Zend_View_Helper_Url
{
/**
* Generates an url given the name of a route.
*
* #access public
*
* #param array $urlOptions Options passed to the assemble method of the Route object.
* #param mixed $name The name of a Route to use. If null it will use the current Route
* #param bool $reset Whether or not to reset the route defaults with those provided
* #return string Url for the link href attribute.
*/
public function myUrl(array $urlOptions = array(), $name = null, $reset = false, $encode = true)
{
return str_replace('+', '-', parent::url($urlOptions, $name, $reset, $encode));
}
}
and then just load your new view helper and you're good to go:
$helper = new \Application\View\Helper\MyUrl;
$this->view->registerHelper($helper, 'myUrl');
Unfortunately, you can't set anything before calling the url(...) function to achieve what you want to do. The reason is that when the URL is assembled, it uses the php urlencode(...) function.
That said, you still have many options:
1) You simply don't use the url(...) function and create your URLs manually. (Best option)
2) You create a new helper that acts like url(...) but add extra changes to the function to achieve what you want to do.
3) You take the output of the url(...) function and do a str_replace to change + with -. (I DO NOT recommend that option)
Personally, I create all my URLs manually to avoid this kind of problem.
I'm guessing its just not done since I can't find any reference to this anywhere, even people asking without response. Although I'm hoping Symfony calls it something else.
How can I get Symfony to auto render views/{controller}/{action}.html.php
No, it's not possible in standard edition of Symfony. However, routing is just one of its components. So if you really want, you can create your own component and use it instead.
You have two options:
1) Use SensioFrameworkExtraBundle - it allows to use #Template annotation. (It's included in SE).
2) Write your own method. I found it annoying to write #Template and any annotations in controllers every time so I added this method in the base controller (it's only an example, examine before using it in production):
public function view(array $parameters = array(), Response $response = null, $extension = '')
{
$extension = !empty($extension) ? $extension : $this->templateExtension;
$view = ViewTemplateResolver::resolve($this->get('request')->get('_controller'), get_called_class());
return $this->render($view . '.' . $extension, $parameters, $response);
}
class ViewTemplateResolver
{
public static function resolve($controller, $class)
{
$action = preg_replace('/(.*?:|Action$)/', '', $controller);
if (preg_match('~(\w+)\\\\(\w+Bundle).*?(\w+(?=Controller$))~', $class, $name)) {
return implode(':', array($name[1] . $name[2], $name[3], $action));
}
}
}
Now in the controller we can do: return $this->view();
When you do routing with annotations instead of yaml, you can add a #Template() to your action method and it's rendering the default template as requested by you.
To do this, change your routing to annotations:
AcmeDemoBundle:
resource: "#PAcmeDemoBundle/Controller/"
type: annotation
prefix: /
Within your controllers, add this for each action:
/**
* #Route("/index", name="demo_index")
* #Template()
*/
Actually I don't know if there is a way to get this behaviour when not using annotations. But as there seems to be logic for this, there might be one.
#meze's answers are both more desirable than out of the box behaviour.
However I think the SensioFrameworkExtraBundle he pointed me to has given me the clue that I needed to achieve this without replacing my own route.
That is to hook into the kernel view event.
Its purpose is specifically stated as:
The purpose of the event is to allow some other return value to be converted into a Response.
I'm assuming then that it can be used to convert a null return from the controller action to a response.
Using the standard MVC set up in Zend Framework, I want to be able to display pages that have anchors throughout. Right now I'm just adding a meaningless parameter with the '#anchor' that I want inside the .phtml file.
<?= $this->url(array(
'controller'=>'my.controller',
'action'=>'my.action',
'anchor'=>'#myanchor'
));
This sets the URL to look like /my.controller/my.action/anchor/#myanchor
Is there a better way to accomplish this? After navigation to the anchor link, the extra item parameter gets set in the user's URL which is something I would rather not happen.
one of possibilities is to override url helper, or to create a new one.
class My_View_Helper_Url extends Zend_View_Helper_Url
{
public function url(array $urlOptions = array(), $name = null, $reset = false, $encode = true)
{
if (isset($urlOptions['anchor']) && !empty($urlOptions['anchor']))
{
$anchor = $urlOptions['anchor'];
unset($urlOptions['anchor']);
}
else
{
$anchor = '';
}
return parent::url($urlOptions, $name, $reset, $encode).$anchor;
}
}
this helper override url helper, problem is, that you can't use parameter called 'anchor', because it will be changed into anchor in url.
you will call it as in your's example
<?= $this->url(array(
'controller'=>'my.controller',
'action'=>'my.action',
'anchor'=>'#myanchor'
));
I hope it helps
There are multiple ways you could go about implementing a fragment id into your URLs. Below are some options, along with some pros and cons for each.
Direct Add
You could simply add the "#$fragment_id" after your url() call. Inelegant, but simple. If you don't use page anchors much (i.e. One or two pages only), this is the way to go.
Write a custom url() helper
You could write a custom version of url() appending an optional 5th argument for the fragment id:
class My_View_Helper_Url extends Zend_View_Helper_Url
{
public function url(array $urlOptions = array(), $name = null,
$reset = false, $encode = true,
$fragment_id = null)
{
$uri = parent::url($urlOptions, $name, $reset, $encode);
if(!is_null($fragment_id)) {
$uri .= "#$fragment_id";
}
return $uri;
}
}
This way, anchor (and anchor/fragment id) information is kept strictly withing the realm of the View. This is good for general use, but can get a little unwieldy for the default route. Also, this is still a little too hard-coded for some uses.
Write a custom Route class (Extreme)
As a third option, you could write a custom version of the Zend_Controller_Router_Route class(es), specifically the assemble($data, $reset, $encode) method (the match($path) method ignores fragment ids by default).
Using this method can be quite tricky, but very useful, especially if use is only limited to specific routes (this method can be used to base the fragment id off of any variable).
Caveat
Certain considerations must be taken into account when using fragment ids. For example, query strings have to precede the fragment id in the uri, otherwise, the query string ignored by PHP. However, most ZF applications tend to avoid use of query strings, so it may not be an issue.
The url view helper accepts a 'fragment' key for the third option:
url('[route]',array([params]),array('fragment'=>'anchor'));
this will automatically end the url with #anchor.
-Thanks to Exlord
I think the Extreme method of writing a custom route class is better because other helper will have the same behavior (like the redirector action helper).