Fixed #30585 -- Added {% translate %} and {% blocktranslate %} template tags.
This commit is contained in:
parent
70d95682b1
commit
d291c72bf2
|
@ -98,7 +98,8 @@ class TranslateNode(Node):
|
|||
class BlockTranslateNode(Node):
|
||||
|
||||
def __init__(self, extra_context, singular, plural=None, countervar=None,
|
||||
counter=None, message_context=None, trimmed=False, asvar=None):
|
||||
counter=None, message_context=None, trimmed=False, asvar=None,
|
||||
tag_name='blocktranslate'):
|
||||
self.extra_context = extra_context
|
||||
self.singular = singular
|
||||
self.plural = plural
|
||||
|
@ -107,6 +108,7 @@ class BlockTranslateNode(Node):
|
|||
self.message_context = message_context
|
||||
self.trimmed = trimmed
|
||||
self.asvar = asvar
|
||||
self.tag_name = tag_name
|
||||
|
||||
def render_token_list(self, tokens):
|
||||
result = []
|
||||
|
@ -163,8 +165,8 @@ class BlockTranslateNode(Node):
|
|||
if nested:
|
||||
# Either string is malformed, or it's a bug
|
||||
raise TemplateSyntaxError(
|
||||
"'blocktrans' is unable to format string returned by gettext: %r using %r"
|
||||
% (result, data)
|
||||
'%r is unable to format string returned by gettext: %r '
|
||||
'using %r' % (self.tag_name, result, data)
|
||||
)
|
||||
with translation.override(None):
|
||||
result = self.render(context, nested=True)
|
||||
|
@ -313,6 +315,7 @@ def do_get_current_language_bidi(parser, token):
|
|||
return GetCurrentLanguageBidiNode(args[2])
|
||||
|
||||
|
||||
@register.tag("translate")
|
||||
@register.tag("trans")
|
||||
def do_translate(parser, token):
|
||||
"""
|
||||
|
@ -406,6 +409,7 @@ def do_translate(parser, token):
|
|||
return TranslateNode(message_string, noop, asvar, message_context)
|
||||
|
||||
|
||||
@register.tag("blocktranslate")
|
||||
@register.tag("blocktrans")
|
||||
def do_block_translate(parser, token):
|
||||
"""
|
||||
|
@ -513,19 +517,20 @@ def do_block_translate(parser, token):
|
|||
break
|
||||
if countervar and counter:
|
||||
if token.contents.strip() != 'plural':
|
||||
raise TemplateSyntaxError("'blocktrans' doesn't allow other block tags inside it")
|
||||
raise TemplateSyntaxError("%r doesn't allow other block tags inside it" % bits[0])
|
||||
while parser.tokens:
|
||||
token = parser.next_token()
|
||||
if token.token_type in (TokenType.VAR, TokenType.TEXT):
|
||||
plural.append(token)
|
||||
else:
|
||||
break
|
||||
if token.contents.strip() != 'endblocktrans':
|
||||
raise TemplateSyntaxError("'blocktrans' doesn't allow other block tags (seen %r) inside it" % token.contents)
|
||||
end_tag_name = 'end%s' % bits[0]
|
||||
if token.contents.strip() != end_tag_name:
|
||||
raise TemplateSyntaxError("%r doesn't allow other block tags (seen %r) inside it" % (bits[0], token.contents))
|
||||
|
||||
return BlockTranslateNode(extra_context, singular, plural, countervar,
|
||||
counter, message_context, trimmed=trimmed,
|
||||
asvar=asvar)
|
||||
asvar=asvar, tag_name=bits[0])
|
||||
|
||||
|
||||
@register.tag
|
||||
|
|
|
@ -19,15 +19,15 @@ def blankout(src, char):
|
|||
|
||||
context_re = _lazy_re_compile(r"""^\s+.*context\s+((?:"[^"]*?")|(?:'[^']*?'))\s*""")
|
||||
inline_re = _lazy_re_compile(
|
||||
# Match the trans 'some text' part
|
||||
r"""^\s*trans\s+((?:"[^"]*?")|(?:'[^']*?'))"""
|
||||
# Match the trans/translate 'some text' part.
|
||||
r"""^\s*trans(?:late)?\s+((?:"[^"]*?")|(?:'[^']*?'))"""
|
||||
# Match and ignore optional filters
|
||||
r"""(?:\s*\|\s*[^\s:]+(?::(?:[^\s'":]+|(?:"[^"]*?")|(?:'[^']*?')))?)*"""
|
||||
# Match the optional context part
|
||||
r"""(\s+.*context\s+((?:"[^"]*?")|(?:'[^']*?')))?\s*"""
|
||||
)
|
||||
block_re = _lazy_re_compile(r"""^\s*blocktrans(\s+.*context\s+((?:"[^"]*?")|(?:'[^']*?')))?(?:\s+|$)""")
|
||||
endblock_re = _lazy_re_compile(r"""^\s*endblocktrans$""")
|
||||
block_re = _lazy_re_compile(r"""^\s*blocktrans(?:late)?(\s+.*context\s+((?:"[^"]*?")|(?:'[^']*?')))?(?:\s+|$)""")
|
||||
endblock_re = _lazy_re_compile(r"""^\s*endblocktrans(?:late)?$""")
|
||||
plural_re = _lazy_re_compile(r"""^\s*plural$""")
|
||||
constant_re = _lazy_re_compile(r"""_\(((?:".*?")|(?:'.*?'))\)""")
|
||||
|
||||
|
|
|
@ -2695,14 +2695,14 @@ Therefore here is our new ``change_form.html`` :
|
|||
{% load i18n admin_urls %}
|
||||
{% block object-tools-items %}
|
||||
<li>
|
||||
<a href="{% url opts|admin_urlname:'history' original.pk|admin_urlquote %}" class="historylink">{% trans "History" %}</a>
|
||||
<a href="{% url opts|admin_urlname:'history' original.pk|admin_urlquote %}" class="historylink">{% translate "History" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="mylink/" class="historylink">My Link</a>
|
||||
</li>
|
||||
{% if has_absolute_url %}
|
||||
<li>
|
||||
<a href="{% url 'admin:view_on_site' content_type_id original.pk %}" class="viewsitelink">{% trans "View on site" %}</a>
|
||||
<a href="{% url 'admin:view_on_site' content_type_id original.pk %}" class="viewsitelink">{% translate "View on site" %}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -814,10 +814,10 @@ This would display as "It is the 4th of September".
|
|||
|
||||
You can also use the syntax ``{% now "Y" as current_year %}`` to store the
|
||||
output (as a string) inside a variable. This is useful if you want to use
|
||||
``{% now %}`` inside a template tag like :ttag:`blocktrans` for example::
|
||||
``{% now %}`` inside a template tag like :ttag:`blocktranslate` for example::
|
||||
|
||||
{% now "Y" as current_year %}
|
||||
{% blocktrans %}Copyright {{ current_year }}{% endblocktrans %}
|
||||
{% blocktranslate %}Copyright {{ current_year }}{% endblocktranslate %}
|
||||
|
||||
.. templatetag:: regroup
|
||||
|
||||
|
@ -1200,10 +1200,10 @@ image in the above example will be 88 pixels wide
|
|||
(because 175/200 = .875; .875 * 100 = 87.5 which is rounded up to 88).
|
||||
|
||||
In some cases you might want to capture the result of ``widthratio`` in a
|
||||
variable. It can be useful, for instance, in a :ttag:`blocktrans` like this::
|
||||
variable. It can be useful, for instance, in a :ttag:`blocktranslate` like this::
|
||||
|
||||
{% widthratio this_value max_value max_width as width %}
|
||||
{% blocktrans %}The width is: {{ width }}{% endblocktrans %}
|
||||
{% blocktranslate %}The width is: {{ width }}{% endblocktranslate %}
|
||||
|
||||
.. templatetag:: with
|
||||
|
||||
|
@ -2023,7 +2023,7 @@ Example::
|
|||
|
||||
You have {{ num_cherries }} cherr{{ num_cherries|pluralize:"y,ies" }}.
|
||||
|
||||
.. note:: Use :ttag:`blocktrans` to pluralize translated strings.
|
||||
.. note:: Use :ttag:`blocktranslate` to pluralize translated strings.
|
||||
|
||||
.. templatefilter:: pprint
|
||||
|
||||
|
|
|
@ -414,7 +414,7 @@ Here are some tips for working with inheritance:
|
|||
tag ``as`` syntax can't be used inside the block. For example, this template
|
||||
doesn't render anything::
|
||||
|
||||
{% trans "Title" as title %}
|
||||
{% translate "Title" as title %}
|
||||
{% block content %}{{ title }}{% endblock %}
|
||||
|
||||
* For extra readability, you can optionally give a *name* to your
|
||||
|
|
|
@ -260,7 +260,10 @@ Signals
|
|||
Templates
|
||||
~~~~~~~~~
|
||||
|
||||
* ...
|
||||
* The renamed :ttag:`translate` and :ttag:`blocktranslate` template tags are
|
||||
introduced for internationalization in template code. The older :ttag:`trans`
|
||||
and :ttag:`blocktrans` template tags aliases continue to work, and will be
|
||||
retained for the foreseeable future.
|
||||
|
||||
Tests
|
||||
~~~~~
|
||||
|
|
|
@ -682,7 +682,7 @@ templates to achieve the same result:
|
|||
{% get_current_language as LANGUAGE_CODE %}
|
||||
|
||||
{% cache 600 welcome LANGUAGE_CODE %}
|
||||
{% trans "Welcome to example.com" %}
|
||||
{% translate "Welcome to example.com" %}
|
||||
{% endcache %}
|
||||
|
||||
The cache timeout can be a template variable, as long as the template variable
|
||||
|
|
|
@ -328,8 +328,8 @@ will appear in the ``.po`` file as:
|
|||
msgid "May"
|
||||
msgstr ""
|
||||
|
||||
Contextual markers are also supported by the :ttag:`trans` and
|
||||
:ttag:`blocktrans` template tags.
|
||||
Contextual markers are also supported by the :ttag:`translate` and
|
||||
:ttag:`blocktranslate` template tags.
|
||||
|
||||
.. _lazy-translations:
|
||||
|
||||
|
@ -575,21 +575,22 @@ have already loaded the ``i18n`` tag.
|
|||
unchanged.
|
||||
|
||||
.. templatetag:: trans
|
||||
.. templatetag:: translate
|
||||
|
||||
``trans`` template tag
|
||||
----------------------
|
||||
``translate`` template tag
|
||||
--------------------------
|
||||
|
||||
The ``{% trans %}`` template tag translates either a constant string
|
||||
The ``{% translate %}`` template tag translates either a constant string
|
||||
(enclosed in single or double quotes) or variable content::
|
||||
|
||||
<title>{% trans "This is the title." %}</title>
|
||||
<title>{% trans myvar %}</title>
|
||||
<title>{% translate "This is the title." %}</title>
|
||||
<title>{% translate myvar %}</title>
|
||||
|
||||
If the ``noop`` option is present, variable lookup still takes place but the
|
||||
translation is skipped. This is useful when "stubbing out" content that will
|
||||
require translation in the future::
|
||||
|
||||
<title>{% trans "myvar" noop %}</title>
|
||||
<title>{% translate "myvar" noop %}</title>
|
||||
|
||||
Internally, inline translations use an
|
||||
:func:`~django.utils.translation.gettext` call.
|
||||
|
@ -598,15 +599,14 @@ In case a template var (``myvar`` above) is passed to the tag, the tag will
|
|||
first resolve such variable to a string at run-time and then look up that
|
||||
string in the message catalogs.
|
||||
|
||||
It's not possible to mix a template variable inside a string within ``{% trans
|
||||
%}``. If your translations require strings with variables (placeholders), use
|
||||
:ttag:`{% blocktrans %}<blocktrans>` instead.
|
||||
|
||||
It's not possible to mix a template variable inside a string within
|
||||
``{% translate %}``. If your translations require strings with variables
|
||||
(placeholders), use :ttag:`{% blocktranslate %}<blocktranslate>` instead.
|
||||
|
||||
If you'd like to retrieve a translated string without displaying it, you can
|
||||
use the following syntax::
|
||||
|
||||
{% trans "This is the title" as the_title %}
|
||||
{% translate "This is the title" as the_title %}
|
||||
|
||||
<title>{{ the_title }}</title>
|
||||
<meta name="description" content="{{ the_title }}">
|
||||
|
@ -615,12 +615,12 @@ In practice you'll use this to get a string you can use in multiple places in a
|
|||
template or so you can use the output as an argument for other template tags or
|
||||
filters::
|
||||
|
||||
{% trans "starting point" as start %}
|
||||
{% trans "end point" as end %}
|
||||
{% trans "La Grande Boucle" as race %}
|
||||
{% translate "starting point" as start %}
|
||||
{% translate "end point" as end %}
|
||||
{% translate "La Grande Boucle" as race %}
|
||||
|
||||
<h1>
|
||||
<a href="/" title="{% blocktrans %}Back to '{{ race }}' homepage{% endblocktrans %}">{{ race }}</a>
|
||||
<a href="/" title="{% blocktranslate %}Back to '{{ race }}' homepage{% endblocktranslate %}">{{ race }}</a>
|
||||
</h1>
|
||||
<p>
|
||||
{% for stage in tour_stages %}
|
||||
|
@ -628,50 +628,56 @@ filters::
|
|||
{% endfor %}
|
||||
</p>
|
||||
|
||||
``{% trans %}`` also supports :ref:`contextual markers<contextual-markers>`
|
||||
``{% translate %}`` also supports :ref:`contextual markers<contextual-markers>`
|
||||
using the ``context`` keyword:
|
||||
|
||||
.. code-block:: html+django
|
||||
|
||||
{% trans "May" context "month name" %}
|
||||
{% translate "May" context "month name" %}
|
||||
|
||||
.. versionchanged:: 3.1
|
||||
|
||||
The ``trans`` tag was renamed to ``translate``. The ``trans``
|
||||
tag is still supported as an alias for backwards compatibility.
|
||||
|
||||
.. templatetag:: blocktrans
|
||||
.. templatetag:: blocktranslate
|
||||
|
||||
``blocktrans`` template tag
|
||||
---------------------------
|
||||
``blocktranslate`` template tag
|
||||
-------------------------------
|
||||
|
||||
Contrarily to the :ttag:`trans` tag, the ``blocktrans`` tag allows you to mark
|
||||
complex sentences consisting of literals and variable content for translation
|
||||
by making use of placeholders::
|
||||
Contrarily to the :ttag:`translate` tag, the ``blocktranslate`` tag allows you
|
||||
to mark complex sentences consisting of literals and variable content for
|
||||
translation by making use of placeholders::
|
||||
|
||||
{% blocktrans %}This string will have {{ value }} inside.{% endblocktrans %}
|
||||
{% blocktranslate %}This string will have {{ value }} inside.{% endblocktranslate %}
|
||||
|
||||
To translate a template expression -- say, accessing object attributes or
|
||||
using template filters -- you need to bind the expression to a local variable
|
||||
for use within the translation block. Examples::
|
||||
|
||||
{% blocktrans with amount=article.price %}
|
||||
{% blocktranslate with amount=article.price %}
|
||||
That will cost $ {{ amount }}.
|
||||
{% endblocktrans %}
|
||||
{% endblocktranslate %}
|
||||
|
||||
{% blocktrans with myvar=value|filter %}
|
||||
{% blocktranslate with myvar=value|filter %}
|
||||
This will have {{ myvar }} inside.
|
||||
{% endblocktrans %}
|
||||
{% endblocktranslate %}
|
||||
|
||||
You can use multiple expressions inside a single ``blocktrans`` tag::
|
||||
You can use multiple expressions inside a single ``blocktranslate`` tag::
|
||||
|
||||
{% blocktrans with book_t=book|title author_t=author|title %}
|
||||
{% blocktranslate with book_t=book|title author_t=author|title %}
|
||||
This is {{ book_t }} by {{ author_t }}
|
||||
{% endblocktrans %}
|
||||
{% endblocktranslate %}
|
||||
|
||||
.. note:: The previous more verbose format is still supported:
|
||||
``{% blocktrans with book|title as book_t and author|title as author_t %}``
|
||||
``{% blocktranslate with book|title as book_t and author|title as author_t %}``
|
||||
|
||||
Other block tags (for example ``{% for %}`` or ``{% if %}``) are not allowed
|
||||
inside a ``blocktrans`` tag.
|
||||
inside a ``blocktranslate`` tag.
|
||||
|
||||
If resolving one of the block arguments fails, ``blocktrans`` will fall back to
|
||||
the default language by deactivating the currently active language
|
||||
If resolving one of the block arguments fails, ``blocktranslate`` will fall
|
||||
back to the default language by deactivating the currently active language
|
||||
temporarily with the :func:`~django.utils.translation.deactivate_all`
|
||||
function.
|
||||
|
||||
|
@ -681,43 +687,43 @@ This tag also provides for pluralization. To use it:
|
|||
be the one used to select the right plural form.
|
||||
|
||||
* Specify both the singular and plural forms separating them with the
|
||||
``{% plural %}`` tag within the ``{% blocktrans %}`` and
|
||||
``{% endblocktrans %}`` tags.
|
||||
``{% plural %}`` tag within the ``{% blocktranslate %}`` and
|
||||
``{% endblocktranslate %}`` tags.
|
||||
|
||||
An example::
|
||||
|
||||
{% blocktrans count counter=list|length %}
|
||||
{% blocktranslate count counter=list|length %}
|
||||
There is only one {{ name }} object.
|
||||
{% plural %}
|
||||
There are {{ counter }} {{ name }} objects.
|
||||
{% endblocktrans %}
|
||||
{% endblocktranslate %}
|
||||
|
||||
A more complex example::
|
||||
|
||||
{% blocktrans with amount=article.price count years=i.length %}
|
||||
{% blocktranslate with amount=article.price count years=i.length %}
|
||||
That will cost $ {{ amount }} per year.
|
||||
{% plural %}
|
||||
That will cost $ {{ amount }} per {{ years }} years.
|
||||
{% endblocktrans %}
|
||||
{% endblocktranslate %}
|
||||
|
||||
When you use both the pluralization feature and bind values to local variables
|
||||
in addition to the counter value, keep in mind that the ``blocktrans``
|
||||
in addition to the counter value, keep in mind that the ``blocktranslate``
|
||||
construct is internally converted to an ``ngettext`` call. This means the
|
||||
same :ref:`notes regarding ngettext variables <pluralization-var-notes>`
|
||||
apply.
|
||||
|
||||
Reverse URL lookups cannot be carried out within the ``blocktrans`` and should
|
||||
be retrieved (and stored) beforehand::
|
||||
Reverse URL lookups cannot be carried out within the ``blocktranslate`` and
|
||||
should be retrieved (and stored) beforehand::
|
||||
|
||||
{% url 'path.to.view' arg arg2 as the_url %}
|
||||
{% blocktrans %}
|
||||
{% blocktranslate %}
|
||||
This is a URL: {{ the_url }}
|
||||
{% endblocktrans %}
|
||||
{% endblocktranslate %}
|
||||
|
||||
If you'd like to retrieve a translated string without displaying it, you can
|
||||
use the following syntax::
|
||||
|
||||
{% blocktrans asvar the_title %}The title is {{ title }}.{% endblocktrans %}
|
||||
{% blocktranslate asvar the_title %}The title is {{ title }}.{% endblocktranslate %}
|
||||
<title>{{ the_title }}</title>
|
||||
<meta name="description" content="{{ the_title }}">
|
||||
|
||||
|
@ -725,32 +731,38 @@ In practice you'll use this to get a string you can use in multiple places in a
|
|||
template or so you can use the output as an argument for other template tags or
|
||||
filters.
|
||||
|
||||
``{% blocktrans %}`` also supports :ref:`contextual
|
||||
``{% blocktranslate %}`` also supports :ref:`contextual
|
||||
markers<contextual-markers>` using the ``context`` keyword:
|
||||
|
||||
.. code-block:: html+django
|
||||
|
||||
{% blocktrans with name=user.username context "greeting" %}Hi {{ name }}{% endblocktrans %}
|
||||
{% blocktranslate with name=user.username context "greeting" %}Hi {{ name }}{% endblocktranslate %}
|
||||
|
||||
Another feature ``{% blocktrans %}`` supports is the ``trimmed`` option. This
|
||||
option will remove newline characters from the beginning and the end of the
|
||||
content of the ``{% blocktrans %}`` tag, replace any whitespace at the beginning
|
||||
and end of a line and merge all lines into one using a space character to
|
||||
separate them. This is quite useful for indenting the content of a ``{%
|
||||
blocktrans %}`` tag without having the indentation characters end up in the
|
||||
corresponding entry in the PO file, which makes the translation process easier.
|
||||
Another feature ``{% blocktranslate %}`` supports is the ``trimmed`` option.
|
||||
This option will remove newline characters from the beginning and the end of
|
||||
the content of the ``{% blocktranslate %}`` tag, replace any whitespace at the
|
||||
beginning and end of a line and merge all lines into one using a space
|
||||
character to separate them. This is quite useful for indenting the content of a
|
||||
``{% blocktranslate %}`` tag without having the indentation characters end up
|
||||
in the corresponding entry in the PO file, which makes the translation process
|
||||
easier.
|
||||
|
||||
For instance, the following ``{% blocktrans %}`` tag::
|
||||
For instance, the following ``{% blocktranslate %}`` tag::
|
||||
|
||||
{% blocktrans trimmed %}
|
||||
{% blocktranslate trimmed %}
|
||||
First sentence.
|
||||
Second paragraph.
|
||||
{% endblocktrans %}
|
||||
{% endblocktranslate %}
|
||||
|
||||
will result in the entry ``"First sentence. Second paragraph."`` in the PO file,
|
||||
compared to ``"\n First sentence.\n Second sentence.\n"``, if the ``trimmed``
|
||||
option had not been specified.
|
||||
|
||||
.. versionchanged:: 3.1
|
||||
|
||||
The ``blocktrans`` tag was renamed to ``blocktranslate``. The ``blocktrans``
|
||||
tag is still supported as an alias for backwards compatibility.
|
||||
|
||||
String literals passed to tags and filters
|
||||
------------------------------------------
|
||||
|
||||
|
@ -782,21 +794,21 @@ tag:
|
|||
.. code-block:: html+django
|
||||
|
||||
{% comment %}Translators: View verb{% endcomment %}
|
||||
{% trans "View" %}
|
||||
{% translate "View" %}
|
||||
|
||||
{% comment %}Translators: Short intro blurb{% endcomment %}
|
||||
<p>{% blocktrans %}A multiline translatable
|
||||
literal.{% endblocktrans %}</p>
|
||||
<p>{% blocktranslate %}A multiline translatable
|
||||
literal.{% endblocktranslate %}</p>
|
||||
|
||||
or with the ``{#`` ... ``#}`` :ref:`one-line comment constructs <template-comments>`:
|
||||
|
||||
.. code-block:: html+django
|
||||
|
||||
{# Translators: Label of a button that triggers search #}
|
||||
<button type="submit">{% trans "Go" %}</button>
|
||||
<button type="submit">{% translate "Go" %}</button>
|
||||
|
||||
{# Translators: This is a text of the base template #}
|
||||
{% blocktrans %}Ambiguous translatable block of text{% endblocktrans %}
|
||||
{% blocktranslate %}Ambiguous translatable block of text{% endblocktranslate %}
|
||||
|
||||
.. note:: Just for completeness, these are the corresponding fragments of the
|
||||
resulting ``.po`` file:
|
||||
|
@ -841,12 +853,12 @@ If you want to select a language within a template, you can use the
|
|||
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||
<p>{% trans "Welcome to our page" %}</p>
|
||||
<p>{% translate "Welcome to our page" %}</p>
|
||||
|
||||
{% language 'en' %}
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||
<p>{% trans "Welcome to our page" %}</p>
|
||||
<p>{% translate "Welcome to our page" %}</p>
|
||||
{% endlanguage %}
|
||||
|
||||
While the first occurrence of "Welcome to our page" uses the current language,
|
||||
|
@ -1450,7 +1462,7 @@ template tag. It enables the given language in the enclosed template section:
|
|||
|
||||
{% get_available_languages as languages %}
|
||||
|
||||
{% trans "View this category in:" %}
|
||||
{% translate "View this category in:" %}
|
||||
{% for lang_code, lang_name in languages %}
|
||||
{% language lang_code %}
|
||||
<a href="{% url 'category' slug=category.slug %}">{{ lang_name }}</a>
|
||||
|
|
|
@ -5,4 +5,4 @@ shouldn't create a .po file with duplicate `Plural-Forms` headers
|
|||
{% endcomment %}
|
||||
{% blocktrans count number=3 %}{{ number }} Bar{% plural %}{{ number }} Bars{% endblocktrans %}
|
||||
|
||||
{% trans 'First `trans`, then `blocktrans` with a plural' %}
|
||||
{% translate 'First `translate`, then `blocktranslate` with a plural' %}
|
||||
|
|
|
@ -85,9 +85,11 @@ continued here.{% endcomment %}
|
|||
{% blocktrans context 'Special blocktrans context wrapped in single quotes' %}Translatable literal with context wrapped in single quotes{% endblocktrans %}
|
||||
{% blocktrans context "Special blocktrans context wrapped in double quotes" %}Translatable literal with context wrapped in double quotes{% endblocktrans %}
|
||||
|
||||
{% blocktranslate %}blocktranslate text{% endblocktranslate %}
|
||||
{% translate "translate text" %}
|
||||
|
||||
{# BasicExtractorTests.test_blocktrans_trimmed #}
|
||||
{% blocktrans %}
|
||||
{# BasicExtractorTests.test_blocktranslate_trimmed #}
|
||||
{% blocktranslate %}
|
||||
Text with a few
|
||||
line breaks.
|
||||
{% endblocktrans %}
|
||||
|
@ -98,10 +100,10 @@ continued here.{% endcomment %}
|
|||
{% endblocktrans %}
|
||||
{% trans "Get my line number" %}
|
||||
|
||||
{% blocktrans trimmed count counter=mylist|length %}
|
||||
First `trans`, then `blocktrans` with a plural
|
||||
{% blocktranslate trimmed count counter=mylist|length %}
|
||||
First `translate`, then `blocktranslate` with a plural
|
||||
{% plural %}
|
||||
Plural for a `trans` and `blocktrans` collision case
|
||||
{% endblocktrans %}
|
||||
Plural for a `translate` and `blocktranslate` collision case
|
||||
{% endblocktranslate %}
|
||||
|
||||
{% trans "Non-breaking space :" %}
|
||||
|
|
|
@ -8,12 +8,13 @@ by using catalogs created from management commands.
|
|||
|
||||
Example:
|
||||
|
||||
The string "Two %% Three %%%" renders differently using trans and blocktrans.
|
||||
This issue is difficult to debug, it could be a problem with extraction,
|
||||
interpolation, or both.
|
||||
The string "Two %% Three %%%" renders differently using translate and
|
||||
blocktranslate. This issue is difficult to debug, it could be a problem with
|
||||
extraction, interpolation, or both.
|
||||
|
||||
How this script helps:
|
||||
* Add {% trans "Two %% Three %%%" %} and blocktrans equivalent to templates.
|
||||
* Add {% translate "Two %% Three %%%" %} and blocktranslate equivalent to
|
||||
templates.
|
||||
* Run this script.
|
||||
* Test extraction - verify the new msgid in sampleproject's django.po.
|
||||
* Add a translation to sampleproject's django.po.
|
||||
|
|
|
@ -182,7 +182,7 @@ class BasicExtractorTests(ExtractorTests):
|
|||
po_contents = fp.read()
|
||||
self.assertMsgId("Non-breaking space\u00a0:", po_contents)
|
||||
|
||||
def test_blocktrans_trimmed(self):
|
||||
def test_blocktranslate_trimmed(self):
|
||||
management.call_command('makemessages', locale=[LOCALE], verbosity=0)
|
||||
self.assertTrue(os.path.exists(self.PO_FILE))
|
||||
with open(self.PO_FILE) as fp:
|
||||
|
@ -256,6 +256,10 @@ class BasicExtractorTests(ExtractorTests):
|
|||
self.assertIn('msgctxt "Special blocktrans context #4"', po_contents)
|
||||
self.assertMsgId("Translatable literal #8d %(a)s", po_contents)
|
||||
|
||||
# {% translate %} and {% blocktranslate %}
|
||||
self.assertMsgId('translate text', po_contents)
|
||||
self.assertMsgId('blocktranslate text', po_contents)
|
||||
|
||||
def test_context_in_single_quotes(self):
|
||||
management.call_command('makemessages', locale=[LOCALE], verbosity=0)
|
||||
self.assertTrue(os.path.exists(self.PO_FILE))
|
||||
|
@ -528,7 +532,7 @@ class CopyPluralFormsExtractorTests(ExtractorTests):
|
|||
found = re.findall(r'^(?P<value>"Plural-Forms.+?\\n")\s*$', po_contents, re.MULTILINE | re.DOTALL)
|
||||
self.assertEqual(1, len(found))
|
||||
|
||||
def test_trans_and_plural_blocktrans_collision(self):
|
||||
def test_translate_and_plural_blocktranslate_collision(self):
|
||||
"""
|
||||
Ensures a correct workaround for the gettext bug when handling a literal
|
||||
found inside a {% trans %} tag and also in another file inside a
|
||||
|
@ -539,8 +543,8 @@ class CopyPluralFormsExtractorTests(ExtractorTests):
|
|||
with open(self.PO_FILE) as fp:
|
||||
po_contents = fp.read()
|
||||
self.assertNotIn("#-#-#-#-# django.pot (PACKAGE VERSION) #-#-#-#-#\\n", po_contents)
|
||||
self.assertMsgId('First `trans`, then `blocktrans` with a plural', po_contents)
|
||||
self.assertMsgIdPlural('Plural for a `trans` and `blocktrans` collision case', po_contents)
|
||||
self.assertMsgId('First `translate`, then `blocktranslate` with a plural', po_contents)
|
||||
self.assertMsgIdPlural('Plural for a `translate` and `blocktranslate` collision case', po_contents)
|
||||
|
||||
|
||||
class NoWrapExtractorTests(ExtractorTests):
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import inspect
|
||||
import os
|
||||
from functools import partial, wraps
|
||||
|
||||
from asgiref.local import Local
|
||||
|
||||
|
@ -8,10 +10,39 @@ from django.utils import translation
|
|||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import trans_real
|
||||
|
||||
from ...utils import setup
|
||||
from ...utils import setup as base_setup
|
||||
from .base import MultipleLocaleActivationTestCase, extended_locale_paths, here
|
||||
|
||||
|
||||
def setup(templates, *args, **kwargs):
|
||||
blocktrans_setup = base_setup(templates, *args, **kwargs)
|
||||
blocktranslate_setup = base_setup({
|
||||
name: template.replace(
|
||||
'{% blocktrans ', '{% blocktranslate '
|
||||
).replace(
|
||||
'{% endblocktrans %}', '{% endblocktranslate %}'
|
||||
)
|
||||
for name, template in templates.items()
|
||||
})
|
||||
|
||||
tags = {
|
||||
'blocktrans': blocktrans_setup,
|
||||
'blocktranslate': blocktranslate_setup,
|
||||
}
|
||||
|
||||
def decorator(func):
|
||||
@wraps(func)
|
||||
def inner(self, *args):
|
||||
signature = inspect.signature(func)
|
||||
for tag_name, setup_func in tags.items():
|
||||
if 'tag_name' in signature.parameters:
|
||||
setup_func(partial(func, tag_name=tag_name))(self)
|
||||
else:
|
||||
setup_func(func)(self)
|
||||
return inner
|
||||
return decorator
|
||||
|
||||
|
||||
class I18nBlockTransTagTests(SimpleTestCase):
|
||||
libraries = {'i18n': 'django.templatetags.i18n'}
|
||||
|
||||
|
@ -76,8 +107,8 @@ class I18nBlockTransTagTests(SimpleTestCase):
|
|||
'{% blocktrans with berta=anton|escape %}{{ berta }}{% endblocktrans %}'})
|
||||
def test_i18n17(self):
|
||||
"""
|
||||
Escaping inside blocktrans and trans works as if it was directly in the
|
||||
template.
|
||||
Escaping inside blocktranslate and translate works as if it was
|
||||
directly in the template.
|
||||
"""
|
||||
output = self.engine.render_to_string('i18n17', {'anton': 'α & β'})
|
||||
self.assertEqual(output, 'α & β')
|
||||
|
@ -224,8 +255,8 @@ class I18nBlockTransTagTests(SimpleTestCase):
|
|||
self.assertEqual(output, '>Error: Seite nicht gefunden<')
|
||||
|
||||
@setup({'template': '{% load i18n %}{% blocktrans asvar %}Yes{% endblocktrans %}'})
|
||||
def test_blocktrans_syntax_error_missing_assignment(self):
|
||||
msg = "No argument provided to the 'blocktrans' tag for the asvar option."
|
||||
def test_blocktrans_syntax_error_missing_assignment(self, tag_name):
|
||||
msg = "No argument provided to the '{}' tag for the asvar option.".format(tag_name)
|
||||
with self.assertRaisesMessage(TemplateSyntaxError, msg):
|
||||
self.engine.render_to_string('template')
|
||||
|
||||
|
@ -235,14 +266,14 @@ class I18nBlockTransTagTests(SimpleTestCase):
|
|||
self.assertEqual(output, '%s')
|
||||
|
||||
@setup({'template': '{% load i18n %}{% blocktrans %}{% block b %} {% endblock %}{% endblocktrans %}'})
|
||||
def test_with_block(self):
|
||||
msg = "'blocktrans' doesn't allow other block tags (seen 'block b') inside it"
|
||||
def test_with_block(self, tag_name):
|
||||
msg = "'{}' doesn't allow other block tags (seen 'block b') inside it".format(tag_name)
|
||||
with self.assertRaisesMessage(TemplateSyntaxError, msg):
|
||||
self.engine.render_to_string('template')
|
||||
|
||||
@setup({'template': '{% load i18n %}{% blocktrans %}{% for b in [1, 2, 3] %} {% endfor %}{% endblocktrans %}'})
|
||||
def test_with_for(self):
|
||||
msg = "'blocktrans' doesn't allow other block tags (seen 'for b in [1, 2, 3]') inside it"
|
||||
def test_with_for(self, tag_name):
|
||||
msg = "'{}' doesn't allow other block tags (seen 'for b in [1, 2, 3]') inside it".format(tag_name)
|
||||
with self.assertRaisesMessage(TemplateSyntaxError, msg):
|
||||
self.engine.render_to_string('template')
|
||||
|
||||
|
@ -252,14 +283,14 @@ class I18nBlockTransTagTests(SimpleTestCase):
|
|||
self.engine.render_to_string('template', {'foo': 'bar'})
|
||||
|
||||
@setup({'template': '{% load i18n %}{% blocktrans with %}{% endblocktrans %}'})
|
||||
def test_no_args_with(self):
|
||||
msg = '"with" in \'blocktrans\' tag needs at least one keyword argument.'
|
||||
def test_no_args_with(self, tag_name):
|
||||
msg = '"with" in \'{}\' tag needs at least one keyword argument.'.format(tag_name)
|
||||
with self.assertRaisesMessage(TemplateSyntaxError, msg):
|
||||
self.engine.render_to_string('template')
|
||||
|
||||
@setup({'template': '{% load i18n %}{% blocktrans count a %}{% endblocktrans %}'})
|
||||
def test_count(self):
|
||||
msg = '"count" in \'blocktrans\' tag expected exactly one keyword argument.'
|
||||
def test_count(self, tag_name):
|
||||
msg = '"count" in \'{}\' tag expected exactly one keyword argument.'.format(tag_name)
|
||||
with self.assertRaisesMessage(TemplateSyntaxError, msg):
|
||||
self.engine.render_to_string('template', {'a': [1, 2, 3]})
|
||||
|
||||
|
@ -268,13 +299,25 @@ class I18nBlockTransTagTests(SimpleTestCase):
|
|||
'There is {{ count }} object. {% block a %} {% endblock %}'
|
||||
'{% endblocktrans %}'
|
||||
)})
|
||||
def test_plural_bad_syntax(self):
|
||||
msg = "'blocktrans' doesn't allow other block tags inside it"
|
||||
def test_plural_bad_syntax(self, tag_name):
|
||||
msg = "'{}' doesn't allow other block tags inside it".format(tag_name)
|
||||
with self.assertRaisesMessage(TemplateSyntaxError, msg):
|
||||
self.engine.render_to_string('template', {'var': [1, 2, 3]})
|
||||
|
||||
|
||||
class TranslationBlockTransTagTests(SimpleTestCase):
|
||||
tag_name = 'blocktrans'
|
||||
|
||||
def get_template(self, template_string):
|
||||
return Template(
|
||||
template_string.replace(
|
||||
'{{% blocktrans ',
|
||||
'{{% {}'.format(self.tag_name)
|
||||
).replace(
|
||||
'{{% endblocktrans %}}',
|
||||
'{{% end{} %}}'.format(self.tag_name)
|
||||
)
|
||||
)
|
||||
|
||||
@override_settings(LOCALE_PATHS=extended_locale_paths)
|
||||
def test_template_tags_pgettext(self):
|
||||
|
@ -283,54 +326,58 @@ class TranslationBlockTransTagTests(SimpleTestCase):
|
|||
trans_real._translations = {}
|
||||
with translation.override('de'):
|
||||
# Nonexistent context
|
||||
t = Template('{% load i18n %}{% blocktrans context "nonexistent" %}May{% endblocktrans %}')
|
||||
t = self.get_template('{% load i18n %}{% blocktrans context "nonexistent" %}May{% endblocktrans %}')
|
||||
rendered = t.render(Context())
|
||||
self.assertEqual(rendered, 'May')
|
||||
|
||||
# Existing context... using a literal
|
||||
t = Template('{% load i18n %}{% blocktrans context "month name" %}May{% endblocktrans %}')
|
||||
t = self.get_template('{% load i18n %}{% blocktrans context "month name" %}May{% endblocktrans %}')
|
||||
rendered = t.render(Context())
|
||||
self.assertEqual(rendered, 'Mai')
|
||||
t = Template('{% load i18n %}{% blocktrans context "verb" %}May{% endblocktrans %}')
|
||||
t = self.get_template('{% load i18n %}{% blocktrans context "verb" %}May{% endblocktrans %}')
|
||||
rendered = t.render(Context())
|
||||
self.assertEqual(rendered, 'Kann')
|
||||
|
||||
# Using a variable
|
||||
t = Template('{% load i18n %}{% blocktrans context message_context %}May{% endblocktrans %}')
|
||||
t = self.get_template('{% load i18n %}{% blocktrans context message_context %}May{% endblocktrans %}')
|
||||
rendered = t.render(Context({'message_context': 'month name'}))
|
||||
self.assertEqual(rendered, 'Mai')
|
||||
t = Template('{% load i18n %}{% blocktrans context message_context %}May{% endblocktrans %}')
|
||||
t = self.get_template('{% load i18n %}{% blocktrans context message_context %}May{% endblocktrans %}')
|
||||
rendered = t.render(Context({'message_context': 'verb'}))
|
||||
self.assertEqual(rendered, 'Kann')
|
||||
|
||||
# Using a filter
|
||||
t = Template('{% load i18n %}{% blocktrans context message_context|lower %}May{% endblocktrans %}')
|
||||
t = self.get_template(
|
||||
'{% load i18n %}{% blocktrans context message_context|lower %}May{% endblocktrans %}'
|
||||
)
|
||||
rendered = t.render(Context({'message_context': 'MONTH NAME'}))
|
||||
self.assertEqual(rendered, 'Mai')
|
||||
t = Template('{% load i18n %}{% blocktrans context message_context|lower %}May{% endblocktrans %}')
|
||||
t = self.get_template(
|
||||
'{% load i18n %}{% blocktrans context message_context|lower %}May{% endblocktrans %}'
|
||||
)
|
||||
rendered = t.render(Context({'message_context': 'VERB'}))
|
||||
self.assertEqual(rendered, 'Kann')
|
||||
|
||||
# Using 'count'
|
||||
t = Template(
|
||||
t = self.get_template(
|
||||
'{% load i18n %}{% blocktrans count number=1 context "super search" %}'
|
||||
'{{ number }} super result{% plural %}{{ number }} super results{% endblocktrans %}'
|
||||
)
|
||||
rendered = t.render(Context())
|
||||
self.assertEqual(rendered, '1 Super-Ergebnis')
|
||||
t = Template(
|
||||
t = self.get_template(
|
||||
'{% load i18n %}{% blocktrans count number=2 context "super search" %}{{ number }}'
|
||||
' super result{% plural %}{{ number }} super results{% endblocktrans %}'
|
||||
)
|
||||
rendered = t.render(Context())
|
||||
self.assertEqual(rendered, '2 Super-Ergebnisse')
|
||||
t = Template(
|
||||
t = self.get_template(
|
||||
'{% load i18n %}{% blocktrans context "other super search" count number=1 %}'
|
||||
'{{ number }} super result{% plural %}{{ number }} super results{% endblocktrans %}'
|
||||
)
|
||||
rendered = t.render(Context())
|
||||
self.assertEqual(rendered, '1 anderen Super-Ergebnis')
|
||||
t = Template(
|
||||
t = self.get_template(
|
||||
'{% load i18n %}{% blocktrans context "other super search" count number=2 %}'
|
||||
'{{ number }} super result{% plural %}{{ number }} super results{% endblocktrans %}'
|
||||
)
|
||||
|
@ -338,13 +385,13 @@ class TranslationBlockTransTagTests(SimpleTestCase):
|
|||
self.assertEqual(rendered, '2 andere Super-Ergebnisse')
|
||||
|
||||
# Using 'with'
|
||||
t = Template(
|
||||
t = self.get_template(
|
||||
'{% load i18n %}{% blocktrans with num_comments=5 context "comment count" %}'
|
||||
'There are {{ num_comments }} comments{% endblocktrans %}'
|
||||
)
|
||||
rendered = t.render(Context())
|
||||
self.assertEqual(rendered, 'Es gibt 5 Kommentare')
|
||||
t = Template(
|
||||
t = self.get_template(
|
||||
'{% load i18n %}{% blocktrans with num_comments=5 context "other comment count" %}'
|
||||
'There are {{ num_comments }} comments{% endblocktrans %}'
|
||||
)
|
||||
|
@ -352,19 +399,19 @@ class TranslationBlockTransTagTests(SimpleTestCase):
|
|||
self.assertEqual(rendered, 'Andere: Es gibt 5 Kommentare')
|
||||
|
||||
# Using trimmed
|
||||
t = Template(
|
||||
t = self.get_template(
|
||||
'{% load i18n %}{% blocktrans trimmed %}\n\nThere\n\t are 5 '
|
||||
'\n\n comments\n{% endblocktrans %}'
|
||||
)
|
||||
rendered = t.render(Context())
|
||||
self.assertEqual(rendered, 'There are 5 comments')
|
||||
t = Template(
|
||||
t = self.get_template(
|
||||
'{% load i18n %}{% blocktrans with num_comments=5 context "comment count" trimmed %}\n\n'
|
||||
'There are \t\n \t {{ num_comments }} comments\n\n{% endblocktrans %}'
|
||||
)
|
||||
rendered = t.render(Context())
|
||||
self.assertEqual(rendered, 'Es gibt 5 Kommentare')
|
||||
t = Template(
|
||||
t = self.get_template(
|
||||
'{% load i18n %}{% blocktrans context "other super search" count number=2 trimmed %}\n'
|
||||
'{{ number }} super \n result{% plural %}{{ number }} super results{% endblocktrans %}'
|
||||
)
|
||||
|
@ -374,12 +421,14 @@ class TranslationBlockTransTagTests(SimpleTestCase):
|
|||
# Misuses
|
||||
msg = "Unknown argument for 'blocktrans' tag: %r."
|
||||
with self.assertRaisesMessage(TemplateSyntaxError, msg % 'month="May"'):
|
||||
Template('{% load i18n %}{% blocktrans context with month="May" %}{{ month }}{% endblocktrans %}')
|
||||
self.get_template(
|
||||
'{% load i18n %}{% blocktrans context with month="May" %}{{ month }}{% endblocktrans %}'
|
||||
)
|
||||
msg = '"context" in %r tag expected exactly one argument.' % 'blocktrans'
|
||||
with self.assertRaisesMessage(TemplateSyntaxError, msg):
|
||||
Template('{% load i18n %}{% blocktrans context %}{% endblocktrans %}')
|
||||
self.get_template('{% load i18n %}{% blocktrans context %}{% endblocktrans %}')
|
||||
with self.assertRaisesMessage(TemplateSyntaxError, msg):
|
||||
Template(
|
||||
self.get_template(
|
||||
'{% load i18n %}{% blocktrans count number=2 context %}'
|
||||
'{{ number }} super result{% plural %}{{ number }}'
|
||||
' super results{% endblocktrans %}'
|
||||
|
@ -409,7 +458,23 @@ class TranslationBlockTransTagTests(SimpleTestCase):
|
|||
self.assertEqual(rendered, 'My other name is James.')
|
||||
|
||||
|
||||
class TranslationBlockTranslationTagTests(TranslationBlockTransTagTests):
|
||||
tag_name = 'blocktranslation'
|
||||
|
||||
|
||||
class MultipleLocaleActivationBlockTransTests(MultipleLocaleActivationTestCase):
|
||||
tag_name = 'blocktrans'
|
||||
|
||||
def get_template(self, template_string):
|
||||
return Template(
|
||||
template_string.replace(
|
||||
'{{% blocktrans ',
|
||||
'{{% {}'.format(self.tag_name)
|
||||
).replace(
|
||||
'{{% endblocktrans %}}',
|
||||
'{{% end{} %}}'.format(self.tag_name)
|
||||
)
|
||||
)
|
||||
|
||||
def test_single_locale_activation(self):
|
||||
"""
|
||||
|
@ -418,35 +483,51 @@ class MultipleLocaleActivationBlockTransTests(MultipleLocaleActivationTestCase):
|
|||
"""
|
||||
with translation.override('fr'):
|
||||
self.assertEqual(
|
||||
Template("{% load i18n %}{% blocktrans %}Yes{% endblocktrans %}").render(Context({})),
|
||||
self.get_template("{% load i18n %}{% blocktrans %}Yes{% endblocktrans %}").render(Context({})),
|
||||
'Oui'
|
||||
)
|
||||
|
||||
def test_multiple_locale_btrans(self):
|
||||
with translation.override('de'):
|
||||
t = Template("{% load i18n %}{% blocktrans %}No{% endblocktrans %}")
|
||||
t = self.get_template("{% load i18n %}{% blocktrans %}No{% endblocktrans %}")
|
||||
with translation.override(self._old_language), translation.override('nl'):
|
||||
self.assertEqual(t.render(Context({})), 'Nee')
|
||||
|
||||
def test_multiple_locale_deactivate_btrans(self):
|
||||
with translation.override('de', deactivate=True):
|
||||
t = Template("{% load i18n %}{% blocktrans %}No{% endblocktrans %}")
|
||||
t = self.get_template("{% load i18n %}{% blocktrans %}No{% endblocktrans %}")
|
||||
with translation.override('nl'):
|
||||
self.assertEqual(t.render(Context({})), 'Nee')
|
||||
|
||||
def test_multiple_locale_direct_switch_btrans(self):
|
||||
with translation.override('de'):
|
||||
t = Template("{% load i18n %}{% blocktrans %}No{% endblocktrans %}")
|
||||
t = self.get_template("{% load i18n %}{% blocktrans %}No{% endblocktrans %}")
|
||||
with translation.override('nl'):
|
||||
self.assertEqual(t.render(Context({})), 'Nee')
|
||||
|
||||
|
||||
class MultipleLocaleActivationBlockTranslationTests(MultipleLocaleActivationBlockTransTests):
|
||||
tag_name = 'blocktranslation'
|
||||
|
||||
|
||||
class MiscTests(SimpleTestCase):
|
||||
tag_name = 'blocktranslate'
|
||||
|
||||
def get_template(self, template_string):
|
||||
return Template(
|
||||
template_string.replace(
|
||||
'{{% blocktrans ',
|
||||
'{{% {}'.format(self.tag_name)
|
||||
).replace(
|
||||
'{{% endblocktrans %}}',
|
||||
'{{% end{} %}}'.format(self.tag_name)
|
||||
)
|
||||
)
|
||||
|
||||
@override_settings(LOCALE_PATHS=extended_locale_paths)
|
||||
def test_percent_in_translatable_block(self):
|
||||
t_sing = Template("{% load i18n %}{% blocktrans %}The result was {{ percent }}%{% endblocktrans %}")
|
||||
t_plur = Template(
|
||||
t_sing = self.get_template("{% load i18n %}{% blocktrans %}The result was {{ percent }}%{% endblocktrans %}")
|
||||
t_plur = self.get_template(
|
||||
"{% load i18n %}{% blocktrans count num as number %}"
|
||||
"{{ percent }}% represents {{ num }} object{% plural %}"
|
||||
"{{ percent }}% represents {{ num }} objects{% endblocktrans %}"
|
||||
|
@ -457,13 +538,15 @@ class MiscTests(SimpleTestCase):
|
|||
self.assertEqual(t_plur.render(Context({'percent': 42, 'num': 4})), '42% stellt 4 Objekte dar')
|
||||
|
||||
@override_settings(LOCALE_PATHS=extended_locale_paths)
|
||||
def test_percent_formatting_in_blocktrans(self):
|
||||
def test_percent_formatting_in_blocktranslate(self):
|
||||
"""
|
||||
Python's %-formatting is properly escaped in blocktrans, singular, or
|
||||
plural.
|
||||
Python's %-formatting is properly escaped in blocktranslate, singular,
|
||||
or plural.
|
||||
"""
|
||||
t_sing = Template("{% load i18n %}{% blocktrans %}There are %(num_comments)s comments{% endblocktrans %}")
|
||||
t_plur = Template(
|
||||
t_sing = self.get_template(
|
||||
"{% load i18n %}{% blocktrans %}There are %(num_comments)s comments{% endblocktrans %}"
|
||||
)
|
||||
t_plur = self.get_template(
|
||||
"{% load i18n %}{% blocktrans count num as number %}"
|
||||
"%(percent)s% represents {{ num }} object{% plural %}"
|
||||
"%(percent)s% represents {{ num }} objects{% endblocktrans %}"
|
||||
|
@ -473,3 +556,7 @@ class MiscTests(SimpleTestCase):
|
|||
self.assertEqual(t_sing.render(Context({'num_comments': 42})), 'There are %(num_comments)s comments')
|
||||
self.assertEqual(t_plur.render(Context({'percent': 42, 'num': 1})), '%(percent)s% represents 1 object')
|
||||
self.assertEqual(t_plur.render(Context({'percent': 42, 'num': 4})), '%(percent)s% represents 4 objects')
|
||||
|
||||
|
||||
class MiscBlockTranslationTests(MiscTests):
|
||||
tag_name = 'blocktrans'
|
|
@ -1,3 +1,6 @@
|
|||
import inspect
|
||||
from functools import partial, wraps
|
||||
|
||||
from asgiref.local import Local
|
||||
|
||||
from django.template import Context, Template, TemplateSyntaxError
|
||||
|
@ -7,10 +10,35 @@ from django.utils import translation
|
|||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import trans_real
|
||||
|
||||
from ...utils import setup
|
||||
from ...utils import setup as base_setup
|
||||
from .base import MultipleLocaleActivationTestCase, extended_locale_paths
|
||||
|
||||
|
||||
def setup(templates, *args, **kwargs):
|
||||
trans_setup = base_setup(templates, *args, **kwargs)
|
||||
translate_setup = base_setup({
|
||||
name: template.replace('{% trans ', '{% translate ')
|
||||
for name, template in templates.items()
|
||||
})
|
||||
|
||||
tags = {
|
||||
'trans': trans_setup,
|
||||
'translate': translate_setup,
|
||||
}
|
||||
|
||||
def decorator(func):
|
||||
@wraps(func)
|
||||
def inner(self, *args):
|
||||
signature = inspect.signature(func)
|
||||
for tag_name, setup_func in tags.items():
|
||||
if 'tag_name' in signature.parameters:
|
||||
setup_func(partial(func, tag_name=tag_name))(self)
|
||||
else:
|
||||
setup_func(func)(self)
|
||||
return inner
|
||||
return decorator
|
||||
|
||||
|
||||
class I18nTransTagTests(SimpleTestCase):
|
||||
libraries = {'i18n': 'django.templatetags.i18n'}
|
||||
|
||||
|
@ -84,38 +112,38 @@ class I18nTransTagTests(SimpleTestCase):
|
|||
self.assertEqual(output, 'Page not found')
|
||||
|
||||
@setup({'template': '{% load i18n %}{% trans %}A}'})
|
||||
def test_syntax_error_no_arguments(self):
|
||||
msg = "'trans' takes at least one argument"
|
||||
def test_syntax_error_no_arguments(self, tag_name):
|
||||
msg = "'{}' takes at least one argument".format(tag_name)
|
||||
with self.assertRaisesMessage(TemplateSyntaxError, msg):
|
||||
self.engine.render_to_string('template')
|
||||
|
||||
@setup({'template': '{% load i18n %}{% trans "Yes" badoption %}'})
|
||||
def test_syntax_error_bad_option(self):
|
||||
msg = "Unknown argument for 'trans' tag: 'badoption'"
|
||||
def test_syntax_error_bad_option(self, tag_name):
|
||||
msg = "Unknown argument for '{}' tag: 'badoption'".format(tag_name)
|
||||
with self.assertRaisesMessage(TemplateSyntaxError, msg):
|
||||
self.engine.render_to_string('template')
|
||||
|
||||
@setup({'template': '{% load i18n %}{% trans "Yes" as %}'})
|
||||
def test_syntax_error_missing_assignment(self):
|
||||
msg = "No argument provided to the 'trans' tag for the as option."
|
||||
def test_syntax_error_missing_assignment(self, tag_name):
|
||||
msg = "No argument provided to the '{}' tag for the as option.".format(tag_name)
|
||||
with self.assertRaisesMessage(TemplateSyntaxError, msg):
|
||||
self.engine.render_to_string('template')
|
||||
|
||||
@setup({'template': '{% load i18n %}{% trans "Yes" as var context %}'})
|
||||
def test_syntax_error_missing_context(self):
|
||||
msg = "No argument provided to the 'trans' tag for the context option."
|
||||
def test_syntax_error_missing_context(self, tag_name):
|
||||
msg = "No argument provided to the '{}' tag for the context option.".format(tag_name)
|
||||
with self.assertRaisesMessage(TemplateSyntaxError, msg):
|
||||
self.engine.render_to_string('template')
|
||||
|
||||
@setup({'template': '{% load i18n %}{% trans "Yes" context as var %}'})
|
||||
def test_syntax_error_context_as(self):
|
||||
msg = "Invalid argument 'as' provided to the 'trans' tag for the context option"
|
||||
def test_syntax_error_context_as(self, tag_name):
|
||||
msg = "Invalid argument 'as' provided to the '{}' tag for the context option".format(tag_name)
|
||||
with self.assertRaisesMessage(TemplateSyntaxError, msg):
|
||||
self.engine.render_to_string('template')
|
||||
|
||||
@setup({'template': '{% load i18n %}{% trans "Yes" context noop %}'})
|
||||
def test_syntax_error_context_noop(self):
|
||||
msg = "Invalid argument 'noop' provided to the 'trans' tag for the context option"
|
||||
def test_syntax_error_context_noop(self, tag_name):
|
||||
msg = "Invalid argument 'noop' provided to the '{}' tag for the context option".format(tag_name)
|
||||
with self.assertRaisesMessage(TemplateSyntaxError, msg):
|
||||
self.engine.render_to_string('template')
|
||||
|
||||
|
@ -132,6 +160,15 @@ class I18nTransTagTests(SimpleTestCase):
|
|||
|
||||
|
||||
class TranslationTransTagTests(SimpleTestCase):
|
||||
tag_name = 'trans'
|
||||
|
||||
def get_template(self, template_string):
|
||||
return Template(
|
||||
template_string.replace(
|
||||
'{{% trans ',
|
||||
'{{% {}'.format(self.tag_name)
|
||||
)
|
||||
)
|
||||
|
||||
@override_settings(LOCALE_PATHS=extended_locale_paths)
|
||||
def test_template_tags_pgettext(self):
|
||||
|
@ -140,44 +177,57 @@ class TranslationTransTagTests(SimpleTestCase):
|
|||
trans_real._translations = {}
|
||||
with translation.override('de'):
|
||||
# Nonexistent context...
|
||||
t = Template('{% load i18n %}{% trans "May" context "nonexistent" %}')
|
||||
t = self.get_template('{% load i18n %}{% trans "May" context "nonexistent" %}')
|
||||
rendered = t.render(Context())
|
||||
self.assertEqual(rendered, 'May')
|
||||
|
||||
# Existing context... using a literal
|
||||
t = Template('{% load i18n %}{% trans "May" context "month name" %}')
|
||||
t = self.get_template('{% load i18n %}{% trans "May" context "month name" %}')
|
||||
rendered = t.render(Context())
|
||||
self.assertEqual(rendered, 'Mai')
|
||||
t = Template('{% load i18n %}{% trans "May" context "verb" %}')
|
||||
t = self.get_template('{% load i18n %}{% trans "May" context "verb" %}')
|
||||
rendered = t.render(Context())
|
||||
self.assertEqual(rendered, 'Kann')
|
||||
|
||||
# Using a variable
|
||||
t = Template('{% load i18n %}{% trans "May" context message_context %}')
|
||||
t = self.get_template('{% load i18n %}{% trans "May" context message_context %}')
|
||||
rendered = t.render(Context({'message_context': 'month name'}))
|
||||
self.assertEqual(rendered, 'Mai')
|
||||
t = Template('{% load i18n %}{% trans "May" context message_context %}')
|
||||
t = self.get_template('{% load i18n %}{% trans "May" context message_context %}')
|
||||
rendered = t.render(Context({'message_context': 'verb'}))
|
||||
self.assertEqual(rendered, 'Kann')
|
||||
|
||||
# Using a filter
|
||||
t = Template('{% load i18n %}{% trans "May" context message_context|lower %}')
|
||||
t = self.get_template('{% load i18n %}{% trans "May" context message_context|lower %}')
|
||||
rendered = t.render(Context({'message_context': 'MONTH NAME'}))
|
||||
self.assertEqual(rendered, 'Mai')
|
||||
t = Template('{% load i18n %}{% trans "May" context message_context|lower %}')
|
||||
t = self.get_template('{% load i18n %}{% trans "May" context message_context|lower %}')
|
||||
rendered = t.render(Context({'message_context': 'VERB'}))
|
||||
self.assertEqual(rendered, 'Kann')
|
||||
|
||||
# Using 'as'
|
||||
t = Template('{% load i18n %}{% trans "May" context "month name" as var %}Value: {{ var }}')
|
||||
t = self.get_template('{% load i18n %}{% trans "May" context "month name" as var %}Value: {{ var }}')
|
||||
rendered = t.render(Context())
|
||||
self.assertEqual(rendered, 'Value: Mai')
|
||||
t = Template('{% load i18n %}{% trans "May" as var context "verb" %}Value: {{ var }}')
|
||||
t = self.get_template('{% load i18n %}{% trans "May" as var context "verb" %}Value: {{ var }}')
|
||||
rendered = t.render(Context())
|
||||
self.assertEqual(rendered, 'Value: Kann')
|
||||
|
||||
|
||||
class TranslationTranslateTagTests(TranslationTransTagTests):
|
||||
tag_name = 'translate'
|
||||
|
||||
|
||||
class MultipleLocaleActivationTransTagTests(MultipleLocaleActivationTestCase):
|
||||
tag_name = 'trans'
|
||||
|
||||
def get_template(self, template_string):
|
||||
return Template(
|
||||
template_string.replace(
|
||||
'{{% trans ',
|
||||
'{{% {}'.format(self.tag_name)
|
||||
)
|
||||
)
|
||||
|
||||
def test_single_locale_activation(self):
|
||||
"""
|
||||
|
@ -185,27 +235,34 @@ class MultipleLocaleActivationTransTagTests(MultipleLocaleActivationTestCase):
|
|||
constructs.
|
||||
"""
|
||||
with translation.override('fr'):
|
||||
self.assertEqual(Template("{% load i18n %}{% trans 'Yes' %}").render(Context({})), 'Oui')
|
||||
self.assertEqual(
|
||||
self.get_template("{% load i18n %}{% trans 'Yes' %}").render(Context({})),
|
||||
'Oui'
|
||||
)
|
||||
|
||||
def test_multiple_locale_trans(self):
|
||||
with translation.override('de'):
|
||||
t = Template("{% load i18n %}{% trans 'No' %}")
|
||||
t = self.get_template("{% load i18n %}{% trans 'No' %}")
|
||||
with translation.override(self._old_language), translation.override('nl'):
|
||||
self.assertEqual(t.render(Context({})), 'Nee')
|
||||
|
||||
def test_multiple_locale_deactivate_trans(self):
|
||||
with translation.override('de', deactivate=True):
|
||||
t = Template("{% load i18n %}{% trans 'No' %}")
|
||||
t = self.get_template("{% load i18n %}{% trans 'No' %}")
|
||||
with translation.override('nl'):
|
||||
self.assertEqual(t.render(Context({})), 'Nee')
|
||||
|
||||
def test_multiple_locale_direct_switch_trans(self):
|
||||
with translation.override('de'):
|
||||
t = Template("{% load i18n %}{% trans 'No' %}")
|
||||
t = self.get_template("{% load i18n %}{% trans 'No' %}")
|
||||
with translation.override('nl'):
|
||||
self.assertEqual(t.render(Context({})), 'Nee')
|
||||
|
||||
|
||||
class MultipleLocaleActivationTranslateTagTests(MultipleLocaleActivationTransTagTests):
|
||||
tag_name = 'translate'
|
||||
|
||||
|
||||
class LocalizeNodeTests(SimpleTestCase):
|
||||
def test_repr(self):
|
||||
node = LocalizeNode(nodelist=[], use_l10n=True)
|
Loading…
Reference in New Issue