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):
 
     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

+ 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*""")
 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"""_\(((?:".*?")|(?:'.*?'))\)""")
 

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

@@ -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 %}

+ 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
 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
 

+ 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
   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

+ 4 - 1
docs/releases/3.1.txt

@@ -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
 ~~~~~

+ 1 - 1
docs/topics/cache.txt

@@ -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

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

@@ -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>

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

@@ -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' %}

+ 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 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 :" %}

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

@@ -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.

+ 8 - 4
tests/i18n/test_extraction.py

@@ -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):

+ 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
+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, 'α &amp; β')
@@ -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'

+ 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 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)