Browse Source

Fixed #30585 -- Added {% translate %} and {% blocktranslate %} template tags.

Mike Hansen 5 years ago
parent
commit
d291c72bf2

+ 12 - 7
django/templatetags/i18n.py

@@ -98,7 +98,8 @@ class TranslateNode(Node):
 class BlockTranslateNode(Node):
 class BlockTranslateNode(Node):
 
 
     def __init__(self, extra_context, singular, plural=None, countervar=None,
     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.extra_context = extra_context
         self.singular = singular
         self.singular = singular
         self.plural = plural
         self.plural = plural
@@ -107,6 +108,7 @@ class BlockTranslateNode(Node):
         self.message_context = message_context
         self.message_context = message_context
         self.trimmed = trimmed
         self.trimmed = trimmed
         self.asvar = asvar
         self.asvar = asvar
+        self.tag_name = tag_name
 
 
     def render_token_list(self, tokens):
     def render_token_list(self, tokens):
         result = []
         result = []
@@ -163,8 +165,8 @@ class BlockTranslateNode(Node):
             if nested:
             if nested:
                 # Either string is malformed, or it's a bug
                 # Either string is malformed, or it's a bug
                 raise TemplateSyntaxError(
                 raise TemplateSyntaxError(
-                    "'blocktrans' is unable to format string returned by gettext: %r using %r"
+                    '%r is unable to format string returned by gettext: %r '
-                    % (result, data)
+                    'using %r' % (self.tag_name, result, data)
                 )
                 )
             with translation.override(None):
             with translation.override(None):
                 result = self.render(context, nested=True)
                 result = self.render(context, nested=True)
@@ -313,6 +315,7 @@ def do_get_current_language_bidi(parser, token):
     return GetCurrentLanguageBidiNode(args[2])
     return GetCurrentLanguageBidiNode(args[2])
 
 
 
 
+@register.tag("translate")
 @register.tag("trans")
 @register.tag("trans")
 def do_translate(parser, token):
 def do_translate(parser, token):
     """
     """
@@ -406,6 +409,7 @@ def do_translate(parser, token):
     return TranslateNode(message_string, noop, asvar, message_context)
     return TranslateNode(message_string, noop, asvar, message_context)
 
 
 
 
+@register.tag("blocktranslate")
 @register.tag("blocktrans")
 @register.tag("blocktrans")
 def do_block_translate(parser, token):
 def do_block_translate(parser, token):
     """
     """
@@ -513,19 +517,20 @@ def do_block_translate(parser, token):
             break
             break
     if countervar and counter:
     if countervar and counter:
         if token.contents.strip() != 'plural':
         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:
         while parser.tokens:
             token = parser.next_token()
             token = parser.next_token()
             if token.token_type in (TokenType.VAR, TokenType.TEXT):
             if token.token_type in (TokenType.VAR, TokenType.TEXT):
                 plural.append(token)
                 plural.append(token)
             else:
             else:
                 break
                 break
-    if token.contents.strip() != 'endblocktrans':
+    end_tag_name = 'end%s' % bits[0]
-        raise TemplateSyntaxError("'blocktrans' doesn't allow other block tags (seen %r) inside it" % token.contents)
+    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,
     return BlockTranslateNode(extra_context, singular, plural, countervar,
                               counter, message_context, trimmed=trimmed,
                               counter, message_context, trimmed=trimmed,
-                              asvar=asvar)
+                              asvar=asvar, tag_name=bits[0])
 
 
 
 
 @register.tag
 @register.tag

+ 4 - 4
django/utils/translation/template.py

@@ -19,15 +19,15 @@ def blankout(src, char):
 
 
 context_re = _lazy_re_compile(r"""^\s+.*context\s+((?:"[^"]*?")|(?:'[^']*?'))\s*""")
 context_re = _lazy_re_compile(r"""^\s+.*context\s+((?:"[^"]*?")|(?:'[^']*?'))\s*""")
 inline_re = _lazy_re_compile(
 inline_re = _lazy_re_compile(
-    # Match the trans 'some text' part
+    # Match the trans/translate 'some text' part.
-    r"""^\s*trans\s+((?:"[^"]*?")|(?:'[^']*?'))"""
+    r"""^\s*trans(?:late)?\s+((?:"[^"]*?")|(?:'[^']*?'))"""
     # Match and ignore optional filters
     # Match and ignore optional filters
     r"""(?:\s*\|\s*[^\s:]+(?::(?:[^\s'":]+|(?:"[^"]*?")|(?:'[^']*?')))?)*"""
     r"""(?:\s*\|\s*[^\s:]+(?::(?:[^\s'":]+|(?:"[^"]*?")|(?:'[^']*?')))?)*"""
     # Match the optional context part
     # Match the optional context part
     r"""(\s+.*context\s+((?:"[^"]*?")|(?:'[^']*?')))?\s*"""
     r"""(\s+.*context\s+((?:"[^"]*?")|(?:'[^']*?')))?\s*"""
 )
 )
-block_re = _lazy_re_compile(r"""^\s*blocktrans(\s+.*context\s+((?:"[^"]*?")|(?:'[^']*?')))?(?:\s+|$)""")
+block_re = _lazy_re_compile(r"""^\s*blocktrans(?:late)?(\s+.*context\s+((?:"[^"]*?")|(?:'[^']*?')))?(?:\s+|$)""")
-endblock_re = _lazy_re_compile(r"""^\s*endblocktrans$""")
+endblock_re = _lazy_re_compile(r"""^\s*endblocktrans(?:late)?$""")
 plural_re = _lazy_re_compile(r"""^\s*plural$""")
 plural_re = _lazy_re_compile(r"""^\s*plural$""")
 constant_re = _lazy_re_compile(r"""_\(((?:".*?")|(?:'.*?'))\)""")
 constant_re = _lazy_re_compile(r"""_\(((?:".*?")|(?:'.*?'))\)""")
 
 

+ 2 - 2
docs/ref/contrib/admin/index.txt

@@ -2695,14 +2695,14 @@ Therefore here is our new ``change_form.html`` :
     {% load i18n admin_urls %}
     {% load i18n admin_urls %}
     {% block object-tools-items %}
     {% block object-tools-items %}
         <li>
         <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>
         <li>
         <li>
             <a href="mylink/" class="historylink">My Link</a>
             <a href="mylink/" class="historylink">My Link</a>
         </li>
         </li>
         {% if has_absolute_url %}
         {% if has_absolute_url %}
             <li>
             <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>
             </li>
         {% endif %}
         {% endif %}
     {% endblock %}
     {% endblock %}

+ 5 - 5
docs/ref/templates/builtins.txt

@@ -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
 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
 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 %}
     {% now "Y" as current_year %}
-    {% blocktrans %}Copyright {{ current_year }}{% endblocktrans %}
+    {% blocktranslate %}Copyright {{ current_year }}{% endblocktranslate %}
 
 
 .. templatetag:: regroup
 .. 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).
 (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
 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 %}
     {% widthratio this_value max_value max_width as width %}
-    {% blocktrans %}The width is: {{ width }}{% endblocktrans %}
+    {% blocktranslate %}The width is: {{ width }}{% endblocktranslate %}
 
 
 .. templatetag:: with
 .. templatetag:: with
 
 
@@ -2023,7 +2023,7 @@ Example::
 
 
     You have {{ num_cherries }} cherr{{ num_cherries|pluralize:"y,ies" }}.
     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
 .. templatefilter:: pprint
 
 

+ 1 - 1
docs/ref/templates/language.txt

@@ -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
   tag ``as`` syntax can't be used inside the block. For example, this template
   doesn't render anything::
   doesn't render anything::
 
 
-      {% trans "Title" as title %}
+      {% translate "Title" as title %}
       {% block content %}{{ title }}{% endblock %}
       {% block content %}{{ title }}{% endblock %}
 
 
 * For extra readability, you can optionally give a *name* to your
 * For extra readability, you can optionally give a *name* to your

+ 4 - 1
docs/releases/3.1.txt

@@ -260,7 +260,10 @@ Signals
 Templates
 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
 Tests
 ~~~~~
 ~~~~~

+ 1 - 1
docs/topics/cache.txt

@@ -682,7 +682,7 @@ templates to achieve the same result:
     {% get_current_language as LANGUAGE_CODE %}
     {% get_current_language as LANGUAGE_CODE %}
 
 
     {% cache 600 welcome LANGUAGE_CODE %}
     {% cache 600 welcome LANGUAGE_CODE %}
-        {% trans "Welcome to example.com" %}
+        {% translate "Welcome to example.com" %}
     {% endcache %}
     {% endcache %}
 
 
 The cache timeout can be a template variable, as long as the template variable
 The cache timeout can be a template variable, as long as the template variable

+ 80 - 68
docs/topics/i18n/translation.txt

@@ -328,8 +328,8 @@ will appear in the ``.po`` file as:
     msgid "May"
     msgid "May"
     msgstr ""
     msgstr ""
 
 
-Contextual markers are also supported by the :ttag:`trans` and
+Contextual markers are also supported by the :ttag:`translate` and
-:ttag:`blocktrans` template tags.
+:ttag:`blocktranslate` template tags.
 
 
 .. _lazy-translations:
 .. _lazy-translations:
 
 
@@ -575,21 +575,22 @@ have already loaded the ``i18n`` tag.
     unchanged.
     unchanged.
 
 
 .. templatetag:: trans
 .. 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::
 (enclosed in single or double quotes) or variable content::
 
 
-    <title>{% trans "This is the title." %}</title>
+    <title>{% translate "This is the title." %}</title>
-    <title>{% trans myvar %}</title>
+    <title>{% translate myvar %}</title>
 
 
 If the ``noop`` option is present, variable lookup still takes place but the
 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
 translation is skipped. This is useful when "stubbing out" content that will
 require translation in the future::
 require translation in the future::
 
 
-    <title>{% trans "myvar" noop %}</title>
+    <title>{% translate "myvar" noop %}</title>
 
 
 Internally, inline translations use an
 Internally, inline translations use an
 :func:`~django.utils.translation.gettext` call.
 :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
 first resolve such variable to a string at run-time and then look up that
 string in the message catalogs.
 string in the message catalogs.
 
 
-It's not possible to mix a template variable inside a string within ``{% trans
+It's not possible to mix a template variable inside a string within
-%}``. If your translations require strings with variables (placeholders), use
+``{% translate %}``. If your translations require strings with variables
-:ttag:`{% blocktrans %}<blocktrans>` instead.
+(placeholders), use :ttag:`{% blocktranslate %}<blocktranslate>` instead.
-
 
 
 If you'd like to retrieve a translated string without displaying it, you can
 If you'd like to retrieve a translated string without displaying it, you can
 use the following syntax::
 use the following syntax::
 
 
-    {% trans "This is the title" as the_title %}
+    {% translate "This is the title" as the_title %}
 
 
     <title>{{ the_title }}</title>
     <title>{{ the_title }}</title>
     <meta name="description" content="{{ the_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
 template or so you can use the output as an argument for other template tags or
 filters::
 filters::
 
 
-    {% trans "starting point" as start %}
+    {% translate "starting point" as start %}
-    {% trans "end point" as end %}
+    {% translate "end point" as end %}
-    {% trans "La Grande Boucle" as race %}
+    {% translate "La Grande Boucle" as race %}
 
 
     <h1>
     <h1>
-      <a href="/" title="{% blocktrans %}Back to '{{ race }}' homepage{% endblocktrans %}">{{ race }}</a>
+      <a href="/" title="{% blocktranslate %}Back to '{{ race }}' homepage{% endblocktranslate %}">{{ race }}</a>
     </h1>
     </h1>
     <p>
     <p>
     {% for stage in tour_stages %}
     {% for stage in tour_stages %}
@@ -628,50 +628,56 @@ filters::
     {% endfor %}
     {% endfor %}
     </p>
     </p>
 
 
-``{% trans %}`` also supports :ref:`contextual markers<contextual-markers>`
+``{% translate %}`` also supports :ref:`contextual markers<contextual-markers>`
 using the ``context`` keyword:
 using the ``context`` keyword:
 
 
 .. code-block:: html+django
 .. 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:: blocktrans
+.. templatetag:: blocktranslate
 
 
-``blocktrans`` template tag
+``blocktranslate`` template tag
----------------------------
+-------------------------------
 
 
-Contrarily to the :ttag:`trans` tag, the ``blocktrans`` tag allows you to mark
+Contrarily to the :ttag:`translate` tag, the ``blocktranslate`` tag allows you
-complex sentences consisting of literals and variable content for translation
+to mark complex sentences consisting of literals and variable content for
-by making use of placeholders::
+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
 To translate a template expression -- say, accessing object attributes or
 using template filters -- you need to bind the expression to a local variable
 using template filters -- you need to bind the expression to a local variable
 for use within the translation block. Examples::
 for use within the translation block. Examples::
 
 
-    {% blocktrans with amount=article.price %}
+    {% blocktranslate with amount=article.price %}
     That will cost $ {{ amount }}.
     That will cost $ {{ amount }}.
-    {% endblocktrans %}
+    {% endblocktranslate %}
 
 
-    {% blocktrans with myvar=value|filter %}
+    {% blocktranslate with myvar=value|filter %}
     This will have {{ myvar }} inside.
     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 }}
     This is {{ book_t }} by {{ author_t }}
-    {% endblocktrans %}
+    {% endblocktranslate %}
 
 
 .. note:: The previous more verbose format is still supported:
 .. 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
 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
+If resolving one of the block arguments fails, ``blocktranslate`` will fall
-the default language by deactivating the currently active language
+back to the default language by deactivating the currently active language
 temporarily with the :func:`~django.utils.translation.deactivate_all`
 temporarily with the :func:`~django.utils.translation.deactivate_all`
 function.
 function.
 
 
@@ -681,43 +687,43 @@ This tag also provides for pluralization. To use it:
   be the one used to select the right plural form.
   be the one used to select the right plural form.
 
 
 * Specify both the singular and plural forms separating them with the
 * Specify both the singular and plural forms separating them with the
-  ``{% plural %}`` tag within the ``{% blocktrans %}`` and
+  ``{% plural %}`` tag within the ``{% blocktranslate %}`` and
-  ``{% endblocktrans %}`` tags.
+  ``{% endblocktranslate %}`` tags.
 
 
 An example::
 An example::
 
 
-    {% blocktrans count counter=list|length %}
+    {% blocktranslate count counter=list|length %}
     There is only one {{ name }} object.
     There is only one {{ name }} object.
     {% plural %}
     {% plural %}
     There are {{ counter }} {{ name }} objects.
     There are {{ counter }} {{ name }} objects.
-    {% endblocktrans %}
+    {% endblocktranslate %}
 
 
 A more complex example::
 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.
     That will cost $ {{ amount }} per year.
     {% plural %}
     {% plural %}
     That will cost $ {{ amount }} per {{ years }} years.
     That will cost $ {{ amount }} per {{ years }} years.
-    {% endblocktrans %}
+    {% endblocktranslate %}
 
 
 When you use both the pluralization feature and bind values to local variables
 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
 construct is internally converted to an ``ngettext`` call. This means the
 same :ref:`notes regarding ngettext variables <pluralization-var-notes>`
 same :ref:`notes regarding ngettext variables <pluralization-var-notes>`
 apply.
 apply.
 
 
-Reverse URL lookups cannot be carried out within the ``blocktrans`` and should
+Reverse URL lookups cannot be carried out within the ``blocktranslate`` and
-be retrieved (and stored) beforehand::
+should be retrieved (and stored) beforehand::
 
 
     {% url 'path.to.view' arg arg2 as the_url %}
     {% url 'path.to.view' arg arg2 as the_url %}
-    {% blocktrans %}
+    {% blocktranslate %}
     This is a URL: {{ the_url }}
     This is a URL: {{ the_url }}
-    {% endblocktrans %}
+    {% endblocktranslate %}
 
 
 If you'd like to retrieve a translated string without displaying it, you can
 If you'd like to retrieve a translated string without displaying it, you can
 use the following syntax::
 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>
     <title>{{ the_title }}</title>
     <meta name="description" content="{{ the_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
 template or so you can use the output as an argument for other template tags or
 filters.
 filters.
 
 
-``{% blocktrans %}`` also supports :ref:`contextual
+``{% blocktranslate %}`` also supports :ref:`contextual
 markers<contextual-markers>` using the ``context`` keyword:
 markers<contextual-markers>` using the ``context`` keyword:
 
 
 .. code-block:: html+django
 .. 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
+Another feature ``{% blocktranslate %}`` supports is the ``trimmed`` option.
-option will remove newline characters from the beginning and the end of the
+This option will remove newline characters from the beginning and the end of
-content of the ``{% blocktrans %}`` tag, replace any whitespace at the beginning
+the content of the ``{% blocktranslate %}`` tag, replace any whitespace at the
-and end of a line and merge all lines into one using a space character to
+beginning and end of a line and merge all lines into one using a space
-separate them. This is quite useful for indenting the content of a ``{%
+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
+``{% blocktranslate %}`` tag without having the indentation characters end up
-corresponding entry in the PO file, which makes the translation process easier.
+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.
       First sentence.
       Second paragraph.
       Second paragraph.
-    {% endblocktrans %}
+    {% endblocktranslate %}
 
 
 will result in the entry ``"First sentence. Second paragraph."`` in the PO file,
 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``
 compared to ``"\n  First sentence.\n  Second sentence.\n"``, if the ``trimmed``
 option had not been specified.
 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
 String literals passed to tags and filters
 ------------------------------------------
 ------------------------------------------
 
 
@@ -782,21 +794,21 @@ tag:
 .. code-block:: html+django
 .. code-block:: html+django
 
 
     {% comment %}Translators: View verb{% endcomment %}
     {% comment %}Translators: View verb{% endcomment %}
-    {% trans "View" %}
+    {% translate "View" %}
 
 
     {% comment %}Translators: Short intro blurb{% endcomment %}
     {% comment %}Translators: Short intro blurb{% endcomment %}
-    <p>{% blocktrans %}A multiline translatable
+    <p>{% blocktranslate %}A multiline translatable
-    literal.{% endblocktrans %}</p>
+    literal.{% endblocktranslate %}</p>
 
 
 or with the ``{#`` ... ``#}`` :ref:`one-line comment constructs <template-comments>`:
 or with the ``{#`` ... ``#}`` :ref:`one-line comment constructs <template-comments>`:
 
 
 .. code-block:: html+django
 .. code-block:: html+django
 
 
     {# Translators: Label of a button that triggers search #}
     {# 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 #}
     {# 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
 .. note:: Just for completeness, these are the corresponding fragments of the
     resulting ``.po`` file:
     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 %}
     {% get_current_language as LANGUAGE_CODE %}
     <!-- Current language: {{ LANGUAGE_CODE }} -->
     <!-- Current language: {{ LANGUAGE_CODE }} -->
-    <p>{% trans "Welcome to our page" %}</p>
+    <p>{% translate "Welcome to our page" %}</p>
 
 
     {% language 'en' %}
     {% language 'en' %}
         {% get_current_language as LANGUAGE_CODE %}
         {% get_current_language as LANGUAGE_CODE %}
         <!-- Current language: {{ LANGUAGE_CODE }} -->
         <!-- Current language: {{ LANGUAGE_CODE }} -->
-        <p>{% trans "Welcome to our page" %}</p>
+        <p>{% translate "Welcome to our page" %}</p>
     {% endlanguage %}
     {% endlanguage %}
 
 
 While the first occurrence of "Welcome to our page" uses the current language,
 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 %}
     {% get_available_languages as languages %}
 
 
-    {% trans "View this category in:" %}
+    {% translate "View this category in:" %}
     {% for lang_code, lang_name in languages %}
     {% for lang_code, lang_name in languages %}
         {% language lang_code %}
         {% language lang_code %}
         <a href="{% url 'category' slug=category.slug %}">{{ lang_name }}</a>
         <a href="{% url 'category' slug=category.slug %}">{{ lang_name }}</a>

+ 1 - 1
tests/i18n/commands/templates/plural.djtpl

@@ -5,4 +5,4 @@ shouldn't create a .po file with duplicate `Plural-Forms` headers
 {% endcomment %}
 {% endcomment %}
 {% blocktrans count number=3 %}{{ number }} Bar{% plural %}{{ number }} Bars{% endblocktrans %}
 {% 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' %}

+ 8 - 6
tests/i18n/commands/templates/test.html

@@ -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 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 %}
 {% 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 #}
+{# BasicExtractorTests.test_blocktranslate_trimmed #}
-{% blocktrans %}
+{% blocktranslate %}
   Text with a few
   Text with a few
   line breaks.
   line breaks.
 {% endblocktrans %}
 {% endblocktrans %}
@@ -98,10 +100,10 @@ continued here.{% endcomment %}
 {% endblocktrans %}
 {% endblocktrans %}
 {% trans "Get my line number" %}
 {% trans "Get my line number" %}
 
 
-{% blocktrans trimmed count counter=mylist|length %}
+{% blocktranslate trimmed count counter=mylist|length %}
-First `trans`, then `blocktrans` with a plural
+First `translate`, then `blocktranslate` with a plural
 {% plural %}
 {% plural %}
-Plural for a `trans` and `blocktrans` collision case
+Plural for a `translate` and `blocktranslate` collision case
-{% endblocktrans %}
+{% endblocktranslate %}
 
 
 {% trans "Non-breaking space :" %}
 {% trans "Non-breaking space :" %}

+ 5 - 4
tests/i18n/sampleproject/update_catalogs.py

@@ -8,12 +8,13 @@ by using catalogs created from management commands.
 
 
 Example:
 Example:
 
 
-The string "Two %% Three %%%" renders differently using trans and blocktrans.
+The string "Two %% Three %%%" renders differently using translate and
-This issue is difficult to debug, it could be a problem with extraction,
+blocktranslate. This issue is difficult to debug, it could be a problem with
-interpolation, or both.
+extraction, interpolation, or both.
 
 
 How this script helps:
 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.
  * Run this script.
  * Test extraction - verify the new msgid in sampleproject's django.po.
  * Test extraction - verify the new msgid in sampleproject's django.po.
  * Add a translation to sampleproject's django.po.
  * Add a translation to sampleproject's django.po.

+ 8 - 4
tests/i18n/test_extraction.py

@@ -182,7 +182,7 @@ class BasicExtractorTests(ExtractorTests):
             po_contents = fp.read()
             po_contents = fp.read()
             self.assertMsgId("Non-breaking space\u00a0:", po_contents)
             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)
         management.call_command('makemessages', locale=[LOCALE], verbosity=0)
         self.assertTrue(os.path.exists(self.PO_FILE))
         self.assertTrue(os.path.exists(self.PO_FILE))
         with open(self.PO_FILE) as fp:
         with open(self.PO_FILE) as fp:
@@ -256,6 +256,10 @@ class BasicExtractorTests(ExtractorTests):
             self.assertIn('msgctxt "Special blocktrans context #4"', po_contents)
             self.assertIn('msgctxt "Special blocktrans context #4"', po_contents)
             self.assertMsgId("Translatable literal #8d %(a)s", 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):
     def test_context_in_single_quotes(self):
         management.call_command('makemessages', locale=[LOCALE], verbosity=0)
         management.call_command('makemessages', locale=[LOCALE], verbosity=0)
         self.assertTrue(os.path.exists(self.PO_FILE))
         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)
             found = re.findall(r'^(?P<value>"Plural-Forms.+?\\n")\s*$', po_contents, re.MULTILINE | re.DOTALL)
             self.assertEqual(1, len(found))
             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
         Ensures a correct workaround for the gettext bug when handling a literal
         found inside a {% trans %} tag and also in another file inside a
         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:
         with open(self.PO_FILE) as fp:
             po_contents = fp.read()
             po_contents = fp.read()
             self.assertNotIn("#-#-#-#-#  django.pot (PACKAGE VERSION)  #-#-#-#-#\\n", po_contents)
             self.assertNotIn("#-#-#-#-#  django.pot (PACKAGE VERSION)  #-#-#-#-#\\n", po_contents)
-            self.assertMsgId('First `trans`, then `blocktrans` with a plural', po_contents)
+            self.assertMsgId('First `translate`, then `blocktranslate` with a plural', po_contents)
-            self.assertMsgIdPlural('Plural for a `trans` and `blocktrans` collision case', po_contents)
+            self.assertMsgIdPlural('Plural for a `translate` and `blocktranslate` collision case', po_contents)
 
 
 
 
 class NoWrapExtractorTests(ExtractorTests):
 class NoWrapExtractorTests(ExtractorTests):

+ 132 - 45
tests/template_tests/syntax_tests/i18n/test_blocktrans.py → tests/template_tests/syntax_tests/i18n/test_blocktranslate.py

@@ -1,4 +1,6 @@
+import inspect
 import os
 import os
+from functools import partial, wraps
 
 
 from asgiref.local import Local
 from asgiref.local import Local
 
 
@@ -8,10 +10,39 @@ from django.utils import translation
 from django.utils.safestring import mark_safe
 from django.utils.safestring import mark_safe
 from django.utils.translation import trans_real
 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
 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):
 class I18nBlockTransTagTests(SimpleTestCase):
     libraries = {'i18n': 'django.templatetags.i18n'}
     libraries = {'i18n': 'django.templatetags.i18n'}
 
 
@@ -76,8 +107,8 @@ class I18nBlockTransTagTests(SimpleTestCase):
                       '{% blocktrans with berta=anton|escape %}{{ berta }}{% endblocktrans %}'})
                       '{% blocktrans with berta=anton|escape %}{{ berta }}{% endblocktrans %}'})
     def test_i18n17(self):
     def test_i18n17(self):
         """
         """
-        Escaping inside blocktrans and trans works as if it was directly in the
+        Escaping inside blocktranslate and translate works as if it was
-        template.
+        directly in the template.
         """
         """
         output = self.engine.render_to_string('i18n17', {'anton': 'α & β'})
         output = self.engine.render_to_string('i18n17', {'anton': 'α & β'})
         self.assertEqual(output, 'α &amp; β')
         self.assertEqual(output, 'α &amp; β')
@@ -224,8 +255,8 @@ class I18nBlockTransTagTests(SimpleTestCase):
         self.assertEqual(output, '>Error: Seite nicht gefunden<')
         self.assertEqual(output, '>Error: Seite nicht gefunden<')
 
 
     @setup({'template': '{% load i18n %}{% blocktrans asvar %}Yes{% endblocktrans %}'})
     @setup({'template': '{% load i18n %}{% blocktrans asvar %}Yes{% endblocktrans %}'})
-    def test_blocktrans_syntax_error_missing_assignment(self):
+    def test_blocktrans_syntax_error_missing_assignment(self, tag_name):
-        msg = "No argument provided to the 'blocktrans' tag for the asvar option."
+        msg = "No argument provided to the '{}' tag for the asvar option.".format(tag_name)
         with self.assertRaisesMessage(TemplateSyntaxError, msg):
         with self.assertRaisesMessage(TemplateSyntaxError, msg):
             self.engine.render_to_string('template')
             self.engine.render_to_string('template')
 
 
@@ -235,14 +266,14 @@ class I18nBlockTransTagTests(SimpleTestCase):
         self.assertEqual(output, '%s')
         self.assertEqual(output, '%s')
 
 
     @setup({'template': '{% load i18n %}{% blocktrans %}{% block b %} {% endblock %}{% endblocktrans %}'})
     @setup({'template': '{% load i18n %}{% blocktrans %}{% block b %} {% endblock %}{% endblocktrans %}'})
-    def test_with_block(self):
+    def test_with_block(self, tag_name):
-        msg = "'blocktrans' doesn't allow other block tags (seen 'block b') inside it"
+        msg = "'{}' doesn't allow other block tags (seen 'block b') inside it".format(tag_name)
         with self.assertRaisesMessage(TemplateSyntaxError, msg):
         with self.assertRaisesMessage(TemplateSyntaxError, msg):
             self.engine.render_to_string('template')
             self.engine.render_to_string('template')
 
 
     @setup({'template': '{% load i18n %}{% blocktrans %}{% for b in [1, 2, 3] %} {% endfor %}{% endblocktrans %}'})
     @setup({'template': '{% load i18n %}{% blocktrans %}{% for b in [1, 2, 3] %} {% endfor %}{% endblocktrans %}'})
-    def test_with_for(self):
+    def test_with_for(self, tag_name):
-        msg = "'blocktrans' doesn't allow other block tags (seen 'for b in [1, 2, 3]') inside it"
+        msg = "'{}' doesn't allow other block tags (seen 'for b in [1, 2, 3]') inside it".format(tag_name)
         with self.assertRaisesMessage(TemplateSyntaxError, msg):
         with self.assertRaisesMessage(TemplateSyntaxError, msg):
             self.engine.render_to_string('template')
             self.engine.render_to_string('template')
 
 
@@ -252,14 +283,14 @@ class I18nBlockTransTagTests(SimpleTestCase):
             self.engine.render_to_string('template', {'foo': 'bar'})
             self.engine.render_to_string('template', {'foo': 'bar'})
 
 
     @setup({'template': '{% load i18n %}{% blocktrans with %}{% endblocktrans %}'})
     @setup({'template': '{% load i18n %}{% blocktrans with %}{% endblocktrans %}'})
-    def test_no_args_with(self):
+    def test_no_args_with(self, tag_name):
-        msg = '"with" in \'blocktrans\' tag needs at least one keyword argument.'
+        msg = '"with" in \'{}\' tag needs at least one keyword argument.'.format(tag_name)
         with self.assertRaisesMessage(TemplateSyntaxError, msg):
         with self.assertRaisesMessage(TemplateSyntaxError, msg):
             self.engine.render_to_string('template')
             self.engine.render_to_string('template')
 
 
     @setup({'template': '{% load i18n %}{% blocktrans count a %}{% endblocktrans %}'})
     @setup({'template': '{% load i18n %}{% blocktrans count a %}{% endblocktrans %}'})
-    def test_count(self):
+    def test_count(self, tag_name):
-        msg = '"count" in \'blocktrans\' tag expected exactly one keyword argument.'
+        msg = '"count" in \'{}\' tag expected exactly one keyword argument.'.format(tag_name)
         with self.assertRaisesMessage(TemplateSyntaxError, msg):
         with self.assertRaisesMessage(TemplateSyntaxError, msg):
             self.engine.render_to_string('template', {'a': [1, 2, 3]})
             self.engine.render_to_string('template', {'a': [1, 2, 3]})
 
 
@@ -268,13 +299,25 @@ class I18nBlockTransTagTests(SimpleTestCase):
         'There is {{ count }} object. {% block a %} {% endblock %}'
         'There is {{ count }} object. {% block a %} {% endblock %}'
         '{% endblocktrans %}'
         '{% endblocktrans %}'
     )})
     )})
-    def test_plural_bad_syntax(self):
+    def test_plural_bad_syntax(self, tag_name):
-        msg = "'blocktrans' doesn't allow other block tags inside it"
+        msg = "'{}' doesn't allow other block tags inside it".format(tag_name)
         with self.assertRaisesMessage(TemplateSyntaxError, msg):
         with self.assertRaisesMessage(TemplateSyntaxError, msg):
             self.engine.render_to_string('template', {'var': [1, 2, 3]})
             self.engine.render_to_string('template', {'var': [1, 2, 3]})
 
 
 
 
 class TranslationBlockTransTagTests(SimpleTestCase):
 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)
     @override_settings(LOCALE_PATHS=extended_locale_paths)
     def test_template_tags_pgettext(self):
     def test_template_tags_pgettext(self):
@@ -283,54 +326,58 @@ class TranslationBlockTransTagTests(SimpleTestCase):
         trans_real._translations = {}
         trans_real._translations = {}
         with translation.override('de'):
         with translation.override('de'):
             # Nonexistent context
             # 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())
             rendered = t.render(Context())
             self.assertEqual(rendered, 'May')
             self.assertEqual(rendered, 'May')
 
 
             # Existing context...  using a literal
             # 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())
             rendered = t.render(Context())
             self.assertEqual(rendered, 'Mai')
             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())
             rendered = t.render(Context())
             self.assertEqual(rendered, 'Kann')
             self.assertEqual(rendered, 'Kann')
 
 
             # Using a variable
             # 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'}))
             rendered = t.render(Context({'message_context': 'month name'}))
             self.assertEqual(rendered, 'Mai')
             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'}))
             rendered = t.render(Context({'message_context': 'verb'}))
             self.assertEqual(rendered, 'Kann')
             self.assertEqual(rendered, 'Kann')
 
 
             # Using a filter
             # 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'}))
             rendered = t.render(Context({'message_context': 'MONTH NAME'}))
             self.assertEqual(rendered, 'Mai')
             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'}))
             rendered = t.render(Context({'message_context': 'VERB'}))
             self.assertEqual(rendered, 'Kann')
             self.assertEqual(rendered, 'Kann')
 
 
             # Using 'count'
             # Using 'count'
-            t = Template(
+            t = self.get_template(
                 '{% load i18n %}{% blocktrans count number=1 context "super search" %}'
                 '{% load i18n %}{% blocktrans count number=1 context "super search" %}'
                 '{{ number }} super result{% plural %}{{ number }} super results{% endblocktrans %}'
                 '{{ number }} super result{% plural %}{{ number }} super results{% endblocktrans %}'
             )
             )
             rendered = t.render(Context())
             rendered = t.render(Context())
             self.assertEqual(rendered, '1 Super-Ergebnis')
             self.assertEqual(rendered, '1 Super-Ergebnis')
-            t = Template(
+            t = self.get_template(
                 '{% load i18n %}{% blocktrans count number=2 context "super search" %}{{ number }}'
                 '{% load i18n %}{% blocktrans count number=2 context "super search" %}{{ number }}'
                 ' super result{% plural %}{{ number }} super results{% endblocktrans %}'
                 ' super result{% plural %}{{ number }} super results{% endblocktrans %}'
             )
             )
             rendered = t.render(Context())
             rendered = t.render(Context())
             self.assertEqual(rendered, '2 Super-Ergebnisse')
             self.assertEqual(rendered, '2 Super-Ergebnisse')
-            t = Template(
+            t = self.get_template(
                 '{% load i18n %}{% blocktrans context "other super search" count number=1 %}'
                 '{% load i18n %}{% blocktrans context "other super search" count number=1 %}'
                 '{{ number }} super result{% plural %}{{ number }} super results{% endblocktrans %}'
                 '{{ number }} super result{% plural %}{{ number }} super results{% endblocktrans %}'
             )
             )
             rendered = t.render(Context())
             rendered = t.render(Context())
             self.assertEqual(rendered, '1 anderen Super-Ergebnis')
             self.assertEqual(rendered, '1 anderen Super-Ergebnis')
-            t = Template(
+            t = self.get_template(
                 '{% load i18n %}{% blocktrans context "other super search" count number=2 %}'
                 '{% load i18n %}{% blocktrans context "other super search" count number=2 %}'
                 '{{ number }} super result{% plural %}{{ number }} super results{% endblocktrans %}'
                 '{{ number }} super result{% plural %}{{ number }} super results{% endblocktrans %}'
             )
             )
@@ -338,13 +385,13 @@ class TranslationBlockTransTagTests(SimpleTestCase):
             self.assertEqual(rendered, '2 andere Super-Ergebnisse')
             self.assertEqual(rendered, '2 andere Super-Ergebnisse')
 
 
             # Using 'with'
             # Using 'with'
-            t = Template(
+            t = self.get_template(
                 '{% load i18n %}{% blocktrans with num_comments=5 context "comment count" %}'
                 '{% load i18n %}{% blocktrans with num_comments=5 context "comment count" %}'
                 'There are {{ num_comments }} comments{% endblocktrans %}'
                 'There are {{ num_comments }} comments{% endblocktrans %}'
             )
             )
             rendered = t.render(Context())
             rendered = t.render(Context())
             self.assertEqual(rendered, 'Es gibt 5 Kommentare')
             self.assertEqual(rendered, 'Es gibt 5 Kommentare')
-            t = Template(
+            t = self.get_template(
                 '{% load i18n %}{% blocktrans with num_comments=5 context "other comment count" %}'
                 '{% load i18n %}{% blocktrans with num_comments=5 context "other comment count" %}'
                 'There are {{ num_comments }} comments{% endblocktrans %}'
                 'There are {{ num_comments }} comments{% endblocktrans %}'
             )
             )
@@ -352,19 +399,19 @@ class TranslationBlockTransTagTests(SimpleTestCase):
             self.assertEqual(rendered, 'Andere: Es gibt 5 Kommentare')
             self.assertEqual(rendered, 'Andere: Es gibt 5 Kommentare')
 
 
             # Using trimmed
             # Using trimmed
-            t = Template(
+            t = self.get_template(
                 '{% load i18n %}{% blocktrans trimmed %}\n\nThere\n\t are 5  '
                 '{% load i18n %}{% blocktrans trimmed %}\n\nThere\n\t are 5  '
                 '\n\n   comments\n{% endblocktrans %}'
                 '\n\n   comments\n{% endblocktrans %}'
             )
             )
             rendered = t.render(Context())
             rendered = t.render(Context())
             self.assertEqual(rendered, 'There are 5 comments')
             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'
                 '{% load i18n %}{% blocktrans with num_comments=5 context "comment count" trimmed %}\n\n'
                 'There are  \t\n  \t {{ num_comments }} comments\n\n{% endblocktrans %}'
                 'There are  \t\n  \t {{ num_comments }} comments\n\n{% endblocktrans %}'
             )
             )
             rendered = t.render(Context())
             rendered = t.render(Context())
             self.assertEqual(rendered, 'Es gibt 5 Kommentare')
             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'
                 '{% load i18n %}{% blocktrans context "other super search" count number=2 trimmed %}\n'
                 '{{ number }} super \n result{% plural %}{{ number }} super results{% endblocktrans %}'
                 '{{ number }} super \n result{% plural %}{{ number }} super results{% endblocktrans %}'
             )
             )
@@ -374,12 +421,14 @@ class TranslationBlockTransTagTests(SimpleTestCase):
             # Misuses
             # Misuses
             msg = "Unknown argument for 'blocktrans' tag: %r."
             msg = "Unknown argument for 'blocktrans' tag: %r."
             with self.assertRaisesMessage(TemplateSyntaxError, msg % 'month="May"'):
             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'
             msg = '"context" in %r tag expected exactly one argument.' % 'blocktrans'
             with self.assertRaisesMessage(TemplateSyntaxError, msg):
             with self.assertRaisesMessage(TemplateSyntaxError, msg):
-                Template('{% load i18n %}{% blocktrans context %}{% endblocktrans %}')
+                self.get_template('{% load i18n %}{% blocktrans context %}{% endblocktrans %}')
             with self.assertRaisesMessage(TemplateSyntaxError, msg):
             with self.assertRaisesMessage(TemplateSyntaxError, msg):
-                Template(
+                self.get_template(
                     '{% load i18n %}{% blocktrans count number=2 context %}'
                     '{% load i18n %}{% blocktrans count number=2 context %}'
                     '{{ number }} super result{% plural %}{{ number }}'
                     '{{ number }} super result{% plural %}{{ number }}'
                     ' super results{% endblocktrans %}'
                     ' super results{% endblocktrans %}'
@@ -409,7 +458,23 @@ class TranslationBlockTransTagTests(SimpleTestCase):
             self.assertEqual(rendered, 'My other name is James.')
             self.assertEqual(rendered, 'My other name is James.')
 
 
 
 
+class TranslationBlockTranslationTagTests(TranslationBlockTransTagTests):
+    tag_name = 'blocktranslation'
+
+
 class MultipleLocaleActivationBlockTransTests(MultipleLocaleActivationTestCase):
 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):
     def test_single_locale_activation(self):
         """
         """
@@ -418,35 +483,51 @@ class MultipleLocaleActivationBlockTransTests(MultipleLocaleActivationTestCase):
         """
         """
         with translation.override('fr'):
         with translation.override('fr'):
             self.assertEqual(
             self.assertEqual(
-                Template("{% load i18n %}{% blocktrans %}Yes{% endblocktrans %}").render(Context({})),
+                self.get_template("{% load i18n %}{% blocktrans %}Yes{% endblocktrans %}").render(Context({})),
                 'Oui'
                 'Oui'
             )
             )
 
 
     def test_multiple_locale_btrans(self):
     def test_multiple_locale_btrans(self):
         with translation.override('de'):
         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'):
         with translation.override(self._old_language), translation.override('nl'):
             self.assertEqual(t.render(Context({})), 'Nee')
             self.assertEqual(t.render(Context({})), 'Nee')
 
 
     def test_multiple_locale_deactivate_btrans(self):
     def test_multiple_locale_deactivate_btrans(self):
         with translation.override('de', deactivate=True):
         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'):
         with translation.override('nl'):
             self.assertEqual(t.render(Context({})), 'Nee')
             self.assertEqual(t.render(Context({})), 'Nee')
 
 
     def test_multiple_locale_direct_switch_btrans(self):
     def test_multiple_locale_direct_switch_btrans(self):
         with translation.override('de'):
         with translation.override('de'):
-            t = Template("{% load i18n %}{% blocktrans %}No{% endblocktrans %}")
+            t = self.get_template("{% load i18n %}{% blocktrans %}No{% endblocktrans %}")
         with translation.override('nl'):
         with translation.override('nl'):
             self.assertEqual(t.render(Context({})), 'Nee')
             self.assertEqual(t.render(Context({})), 'Nee')
 
 
 
 
+class MultipleLocaleActivationBlockTranslationTests(MultipleLocaleActivationBlockTransTests):
+    tag_name = 'blocktranslation'
+
+
 class MiscTests(SimpleTestCase):
 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)
     @override_settings(LOCALE_PATHS=extended_locale_paths)
     def test_percent_in_translatable_block(self):
     def test_percent_in_translatable_block(self):
-        t_sing = Template("{% load i18n %}{% blocktrans %}The result was {{ percent }}%{% endblocktrans %}")
+        t_sing = self.get_template("{% load i18n %}{% blocktrans %}The result was {{ percent }}%{% endblocktrans %}")
-        t_plur = Template(
+        t_plur = self.get_template(
             "{% load i18n %}{% blocktrans count num as number %}"
             "{% load i18n %}{% blocktrans count num as number %}"
             "{{ percent }}% represents {{ num }} object{% plural %}"
             "{{ percent }}% represents {{ num }} object{% plural %}"
             "{{ percent }}% represents {{ num }} objects{% endblocktrans %}"
             "{{ 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')
             self.assertEqual(t_plur.render(Context({'percent': 42, 'num': 4})), '42% stellt 4 Objekte dar')
 
 
     @override_settings(LOCALE_PATHS=extended_locale_paths)
     @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
+        Python's %-formatting is properly escaped in blocktranslate, singular,
-        plural.
+        or plural.
         """
         """
-        t_sing = Template("{% load i18n %}{% blocktrans %}There are %(num_comments)s comments{% endblocktrans %}")
+        t_sing = self.get_template(
-        t_plur = Template(
+            "{% load i18n %}{% blocktrans %}There are %(num_comments)s comments{% endblocktrans %}"
+        )
+        t_plur = self.get_template(
             "{% load i18n %}{% blocktrans count num as number %}"
             "{% load i18n %}{% blocktrans count num as number %}"
             "%(percent)s% represents {{ num }} object{% plural %}"
             "%(percent)s% represents {{ num }} object{% plural %}"
             "%(percent)s% represents {{ num }} objects{% endblocktrans %}"
             "%(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_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': 1})), '%(percent)s% represents 1 object')
             self.assertEqual(t_plur.render(Context({'percent': 42, 'num': 4})), '%(percent)s% represents 4 objects')
             self.assertEqual(t_plur.render(Context({'percent': 42, 'num': 4})), '%(percent)s% represents 4 objects')
+
+
+class MiscBlockTranslationTests(MiscTests):
+    tag_name = 'blocktrans'

+ 83 - 26
tests/template_tests/syntax_tests/i18n/test_trans.py → tests/template_tests/syntax_tests/i18n/test_translate.py

@@ -1,3 +1,6 @@
+import inspect
+from functools import partial, wraps
+
 from asgiref.local import Local
 from asgiref.local import Local
 
 
 from django.template import Context, Template, TemplateSyntaxError
 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.safestring import mark_safe
 from django.utils.translation import trans_real
 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
 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):
 class I18nTransTagTests(SimpleTestCase):
     libraries = {'i18n': 'django.templatetags.i18n'}
     libraries = {'i18n': 'django.templatetags.i18n'}
 
 
@@ -84,38 +112,38 @@ class I18nTransTagTests(SimpleTestCase):
         self.assertEqual(output, 'Page not found')
         self.assertEqual(output, 'Page not found')
 
 
     @setup({'template': '{% load i18n %}{% trans %}A}'})
     @setup({'template': '{% load i18n %}{% trans %}A}'})
-    def test_syntax_error_no_arguments(self):
+    def test_syntax_error_no_arguments(self, tag_name):
-        msg = "'trans' takes at least one argument"
+        msg = "'{}' takes at least one argument".format(tag_name)
         with self.assertRaisesMessage(TemplateSyntaxError, msg):
         with self.assertRaisesMessage(TemplateSyntaxError, msg):
             self.engine.render_to_string('template')
             self.engine.render_to_string('template')
 
 
     @setup({'template': '{% load i18n %}{% trans "Yes" badoption %}'})
     @setup({'template': '{% load i18n %}{% trans "Yes" badoption %}'})
-    def test_syntax_error_bad_option(self):
+    def test_syntax_error_bad_option(self, tag_name):
-        msg = "Unknown argument for 'trans' tag: 'badoption'"
+        msg = "Unknown argument for '{}' tag: 'badoption'".format(tag_name)
         with self.assertRaisesMessage(TemplateSyntaxError, msg):
         with self.assertRaisesMessage(TemplateSyntaxError, msg):
             self.engine.render_to_string('template')
             self.engine.render_to_string('template')
 
 
     @setup({'template': '{% load i18n %}{% trans "Yes" as %}'})
     @setup({'template': '{% load i18n %}{% trans "Yes" as %}'})
-    def test_syntax_error_missing_assignment(self):
+    def test_syntax_error_missing_assignment(self, tag_name):
-        msg = "No argument provided to the 'trans' tag for the as option."
+        msg = "No argument provided to the '{}' tag for the as option.".format(tag_name)
         with self.assertRaisesMessage(TemplateSyntaxError, msg):
         with self.assertRaisesMessage(TemplateSyntaxError, msg):
             self.engine.render_to_string('template')
             self.engine.render_to_string('template')
 
 
     @setup({'template': '{% load i18n %}{% trans "Yes" as var context %}'})
     @setup({'template': '{% load i18n %}{% trans "Yes" as var context %}'})
-    def test_syntax_error_missing_context(self):
+    def test_syntax_error_missing_context(self, tag_name):
-        msg = "No argument provided to the 'trans' tag for the context option."
+        msg = "No argument provided to the '{}' tag for the context option.".format(tag_name)
         with self.assertRaisesMessage(TemplateSyntaxError, msg):
         with self.assertRaisesMessage(TemplateSyntaxError, msg):
             self.engine.render_to_string('template')
             self.engine.render_to_string('template')
 
 
     @setup({'template': '{% load i18n %}{% trans "Yes" context as var %}'})
     @setup({'template': '{% load i18n %}{% trans "Yes" context as var %}'})
-    def test_syntax_error_context_as(self):
+    def test_syntax_error_context_as(self, tag_name):
-        msg = "Invalid argument 'as' provided to the 'trans' tag for the context option"
+        msg = "Invalid argument 'as' provided to the '{}' tag for the context option".format(tag_name)
         with self.assertRaisesMessage(TemplateSyntaxError, msg):
         with self.assertRaisesMessage(TemplateSyntaxError, msg):
             self.engine.render_to_string('template')
             self.engine.render_to_string('template')
 
 
     @setup({'template': '{% load i18n %}{% trans "Yes" context noop %}'})
     @setup({'template': '{% load i18n %}{% trans "Yes" context noop %}'})
-    def test_syntax_error_context_noop(self):
+    def test_syntax_error_context_noop(self, tag_name):
-        msg = "Invalid argument 'noop' provided to the 'trans' tag for the context option"
+        msg = "Invalid argument 'noop' provided to the '{}' tag for the context option".format(tag_name)
         with self.assertRaisesMessage(TemplateSyntaxError, msg):
         with self.assertRaisesMessage(TemplateSyntaxError, msg):
             self.engine.render_to_string('template')
             self.engine.render_to_string('template')
 
 
@@ -132,6 +160,15 @@ class I18nTransTagTests(SimpleTestCase):
 
 
 
 
 class TranslationTransTagTests(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)
     @override_settings(LOCALE_PATHS=extended_locale_paths)
     def test_template_tags_pgettext(self):
     def test_template_tags_pgettext(self):
@@ -140,44 +177,57 @@ class TranslationTransTagTests(SimpleTestCase):
         trans_real._translations = {}
         trans_real._translations = {}
         with translation.override('de'):
         with translation.override('de'):
             # Nonexistent context...
             # Nonexistent context...
-            t = Template('{% load i18n %}{% trans "May" context "nonexistent" %}')
+            t = self.get_template('{% load i18n %}{% trans "May" context "nonexistent" %}')
             rendered = t.render(Context())
             rendered = t.render(Context())
             self.assertEqual(rendered, 'May')
             self.assertEqual(rendered, 'May')
 
 
             # Existing context... using a literal
             # 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())
             rendered = t.render(Context())
             self.assertEqual(rendered, 'Mai')
             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())
             rendered = t.render(Context())
             self.assertEqual(rendered, 'Kann')
             self.assertEqual(rendered, 'Kann')
 
 
             # Using a variable
             # 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'}))
             rendered = t.render(Context({'message_context': 'month name'}))
             self.assertEqual(rendered, 'Mai')
             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'}))
             rendered = t.render(Context({'message_context': 'verb'}))
             self.assertEqual(rendered, 'Kann')
             self.assertEqual(rendered, 'Kann')
 
 
             # Using a filter
             # 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'}))
             rendered = t.render(Context({'message_context': 'MONTH NAME'}))
             self.assertEqual(rendered, 'Mai')
             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'}))
             rendered = t.render(Context({'message_context': 'VERB'}))
             self.assertEqual(rendered, 'Kann')
             self.assertEqual(rendered, 'Kann')
 
 
             # Using 'as'
             # 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())
             rendered = t.render(Context())
             self.assertEqual(rendered, 'Value: Mai')
             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())
             rendered = t.render(Context())
             self.assertEqual(rendered, 'Value: Kann')
             self.assertEqual(rendered, 'Value: Kann')
 
 
 
 
+class TranslationTranslateTagTests(TranslationTransTagTests):
+    tag_name = 'translate'
+
+
 class MultipleLocaleActivationTransTagTests(MultipleLocaleActivationTestCase):
 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):
     def test_single_locale_activation(self):
         """
         """
@@ -185,27 +235,34 @@ class MultipleLocaleActivationTransTagTests(MultipleLocaleActivationTestCase):
         constructs.
         constructs.
         """
         """
         with translation.override('fr'):
         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):
     def test_multiple_locale_trans(self):
         with translation.override('de'):
         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'):
         with translation.override(self._old_language), translation.override('nl'):
             self.assertEqual(t.render(Context({})), 'Nee')
             self.assertEqual(t.render(Context({})), 'Nee')
 
 
     def test_multiple_locale_deactivate_trans(self):
     def test_multiple_locale_deactivate_trans(self):
         with translation.override('de', deactivate=True):
         with translation.override('de', deactivate=True):
-            t = Template("{% load i18n %}{% trans 'No' %}")
+            t = self.get_template("{% load i18n %}{% trans 'No' %}")
         with translation.override('nl'):
         with translation.override('nl'):
             self.assertEqual(t.render(Context({})), 'Nee')
             self.assertEqual(t.render(Context({})), 'Nee')
 
 
     def test_multiple_locale_direct_switch_trans(self):
     def test_multiple_locale_direct_switch_trans(self):
         with translation.override('de'):
         with translation.override('de'):
-            t = Template("{% load i18n %}{% trans 'No' %}")
+            t = self.get_template("{% load i18n %}{% trans 'No' %}")
         with translation.override('nl'):
         with translation.override('nl'):
             self.assertEqual(t.render(Context({})), 'Nee')
             self.assertEqual(t.render(Context({})), 'Nee')
 
 
 
 
+class MultipleLocaleActivationTranslateTagTests(MultipleLocaleActivationTransTagTests):
+    tag_name = 'translate'
+
+
 class LocalizeNodeTests(SimpleTestCase):
 class LocalizeNodeTests(SimpleTestCase):
     def test_repr(self):
     def test_repr(self):
         node = LocalizeNode(nodelist=[], use_l10n=True)
         node = LocalizeNode(nodelist=[], use_l10n=True)