Natim
Natim

Reputation: 18122

Django how to mark_safe a link but not the arguments in a gettext translation

Let say we have a message like this :

            messages.add_message(request, messages.SUCCESS,
                _('Document <a href="%(url)s">%(doc_type_name)s %(name)s (%(fname)s)</a> created.') % {
                    'doc_type_name': conditional_escape(document.doc_type.name),
                    'name': conditional_escape(document.title),
                    'fname': conditional_escape(document.name),
                    'url': document.get_absolute_url()
                })

Here it will work only if we display the message with {{ message|safe }} but we don't want that since if there is some code in %(name) it will be executed too.

If I use:

            messages.add_message(request, messages.SUCCESS,
                mark_safe(_('Document <a href="%(url)s">%(doc_type_name)s %(name)s (%(fname)s)</a> created.') % {
                    'doc_type_name': conditional_escape(document.doc_type.name),
                    'name': conditional_escape(document.title),
                    'fname': conditional_escape(document.name),
                    'url': document.get_absolute_url()
                }))

The mark_safe doesn't work.

I read a solution over there : https://stackoverflow.com/a/12600388/186202

But it is the reverse that I need here:

_('Document %s created.') % mark_safe('')

And as soon as it goes through the ugettext function it is not safe anymore.

How should I do?

Upvotes: 2

Views: 3350

Answers (2)

Julien Kieffer
Julien Kieffer

Reputation: 1145

Previous response are correct: you should avoid mixing python and html as much as possible.

To solve your issue:

from django.utils import six  # Python 3 compatibility
from django.utils.functional import lazy
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
mark_safe_lazy = lazy(mark_safe, six.text_type)

then:

lazy_string = mark_safe_lazy(_("<p>My <strong>string!</strong></p>"))

Found in django documentation: https://docs.djangoproject.com/en/1.9/topics/i18n/translation/#s-other-uses-of-lazy-in-delayed-translations

Upvotes: 2

Marat
Marat

Reputation: 15738

You are trying to mix view and logic by placing HTML inside Python code. Well, sometimes you just have to do this but it is not the case.

mark_safe() returns SafeString object which is treated by Django templates specially. If SafeString evaluated by ugettext or % you will get string again, it is an expected behaviour. You can not mark safe only formatting string, either complete output with doc name/title etc or everything is not safe. Ie, it will not work this way.

You can put HTML into template and use render_to_string(), and probably it is the best option.

Are document title, name and doc_type.name set by user? If not, you can skip mark_safe and document using HTML in document properties as feature.

Upvotes: 4

Related Questions