Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Fixed #10004 and #12320 -- Enabled the makemessages management comman…
…d to collect comments for translators that start with the "Translators" keyword. Thanks for the report and patches, martinb and Claude Paroz.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14595 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information
jezdez committed Nov 17, 2010
1 parent d7ad02f commit 17b329a
Show file tree
Hide file tree
Showing 9 changed files with 98 additions and 12 deletions.
9 changes: 6 additions & 3 deletions django/core/management/commands/makemessages.py
Expand Up @@ -196,7 +196,7 @@ def make_messages(locale=None, domain='django', verbosity='1', all=False,
'xgettext -d %s -L Perl %s --keyword=gettext_noop ' 'xgettext -d %s -L Perl %s --keyword=gettext_noop '
'--keyword=gettext_lazy --keyword=ngettext_lazy:1,2 ' '--keyword=gettext_lazy --keyword=ngettext_lazy:1,2 '
'--keyword=pgettext:1c,2 --keyword=npgettext:1c,2,3 ' '--keyword=pgettext:1c,2 --keyword=npgettext:1c,2,3 '
'--from-code UTF-8 -o - "%s"' % ( '--from-code UTF-8 --add-comments=Translators -o - "%s"' % (
domain, wrap, os.path.join(dirpath, thefile) domain, wrap, os.path.join(dirpath, thefile)
) )
) )
Expand Down Expand Up @@ -240,8 +240,9 @@ def make_messages(locale=None, domain='django', verbosity='1', all=False,
'--keyword=ugettext_noop --keyword=ugettext_lazy ' '--keyword=ugettext_noop --keyword=ugettext_lazy '
'--keyword=ungettext_lazy:1,2 --keyword=pgettext:1c,2 ' '--keyword=ungettext_lazy:1,2 --keyword=pgettext:1c,2 '
'--keyword=npgettext:1c,2,3 --keyword=pgettext_lazy:1c,2 ' '--keyword=npgettext:1c,2,3 --keyword=pgettext_lazy:1c,2 '
'--keyword=npgettext_lazy:1c,2,3 --from-code UTF-8 -o - ' '--keyword=npgettext_lazy:1c,2,3 --from-code UTF-8 '
'"%s"' % (domain, wrap, os.path.join(dirpath, thefile)) '--add-comments=Translators -o - "%s"' % (
domain, wrap, os.path.join(dirpath, thefile))
) )
msgs, errors = _popen(cmd) msgs, errors = _popen(cmd)
if errors: if errors:
Expand Down Expand Up @@ -282,6 +283,8 @@ def make_messages(locale=None, domain='django', verbosity='1', all=False,
raise CommandError("errors happened while running msgmerge\n%s" % errors) raise CommandError("errors happened while running msgmerge\n%s" % errors)
elif not invoked_for_django: elif not invoked_for_django:
msgs = copy_plural_forms(msgs, locale, domain, verbosity) msgs = copy_plural_forms(msgs, locale, domain, verbosity)
msgs = msgs.replace(
"#. #-#-#-#-# %s.pot (PACKAGE VERSION) #-#-#-#-#\n" % domain, "")
f = open(pofile, 'wb') f = open(pofile, 'wb')
try: try:
f.write(msgs) f.write(msgs)
Expand Down
6 changes: 5 additions & 1 deletion django/template/__init__.py
Expand Up @@ -82,6 +82,7 @@
VARIABLE_TAG_END = '}}' VARIABLE_TAG_END = '}}'
COMMENT_TAG_START = '{#' COMMENT_TAG_START = '{#'
COMMENT_TAG_END = '#}' COMMENT_TAG_END = '#}'
TRANSLATOR_COMMENT_MARK = 'Translators'
SINGLE_BRACE_START = '{' SINGLE_BRACE_START = '{'
SINGLE_BRACE_END = '}' SINGLE_BRACE_END = '}'


Expand Down Expand Up @@ -237,7 +238,10 @@ def create_token(self, token_string, in_tag):
elif token_string.startswith(BLOCK_TAG_START): elif token_string.startswith(BLOCK_TAG_START):
token = Token(TOKEN_BLOCK, token_string[len(BLOCK_TAG_START):-len(BLOCK_TAG_END)].strip()) token = Token(TOKEN_BLOCK, token_string[len(BLOCK_TAG_START):-len(BLOCK_TAG_END)].strip())
elif token_string.startswith(COMMENT_TAG_START): elif token_string.startswith(COMMENT_TAG_START):
token = Token(TOKEN_COMMENT, '') content = ''
if token_string.find(TRANSLATOR_COMMENT_MARK):
content = token_string[len(COMMENT_TAG_START):-len(COMMENT_TAG_END)].strip()
token = Token(TOKEN_COMMENT, content)
else: else:
token = Token(TOKEN_TEXT, token_string) token = Token(TOKEN_TEXT, token_string)
return token return token
Expand Down
17 changes: 15 additions & 2 deletions django/utils/translation/trans_real.py
Expand Up @@ -427,14 +427,23 @@ def templatize(src):
does so by translating the Django translation tags into standard gettext does so by translating the Django translation tags into standard gettext
function invocations. function invocations.
""" """
from django.template import Lexer, TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK from django.template import Lexer, TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK, TOKEN_COMMENT
out = StringIO() out = StringIO()
intrans = False intrans = False
inplural = False inplural = False
singular = [] singular = []
plural = [] plural = []
incomment = False
comment = []
for t in Lexer(src, None).tokenize(): for t in Lexer(src, None).tokenize():
if intrans: if incomment:
if t.token_type == TOKEN_BLOCK and t.contents == 'endcomment':
out.write(' # %s' % ''.join(comment))
incomment = False
comment = []
else:
comment.append(t.contents)
elif intrans:
if t.token_type == TOKEN_BLOCK: if t.token_type == TOKEN_BLOCK:
endbmatch = endblock_re.match(t.contents) endbmatch = endblock_re.match(t.contents)
pluralmatch = plural_re.match(t.contents) pluralmatch = plural_re.match(t.contents)
Expand Down Expand Up @@ -488,6 +497,8 @@ def templatize(src):
elif cmatches: elif cmatches:
for cmatch in cmatches: for cmatch in cmatches:
out.write(' _(%s) ' % cmatch) out.write(' _(%s) ' % cmatch)
elif t.contents == 'comment':
incomment = True
else: else:
out.write(blankout(t.contents, 'B')) out.write(blankout(t.contents, 'B'))
elif t.token_type == TOKEN_VAR: elif t.token_type == TOKEN_VAR:
Expand All @@ -500,6 +511,8 @@ def templatize(src):
out.write(' %s ' % p.split(':',1)[1]) out.write(' %s ' % p.split(':',1)[1])
else: else:
out.write(blankout(p, 'F')) out.write(blankout(p, 'F'))
elif t.token_type == TOKEN_COMMENT:
out.write(' # %s' % t.contents)
else: else:
out.write(blankout(t.contents, 'X')) out.write(blankout(t.contents, 'X'))
return out.getvalue() return out.getvalue()
Expand Down
16 changes: 16 additions & 0 deletions docs/releases/1.3-alpha-2.txt
Expand Up @@ -39,6 +39,22 @@ See the :doc:`reference documentation of the app </ref/contrib/staticfiles>`
for more details or learn how to :doc:`manage static files for more details or learn how to :doc:`manage static files
</howto/static-files>`. </howto/static-files>`.


Translation comments
~~~~~~~~~~~~~~~~~~~~

If you would like to give translators hints about a translatable string, you
can add a comment prefixed with the ``Translators`` keyword on the line
preceding the string, e.g.::

def my_view(request):
# Translators: This message appears on the home page only
output = ugettext("Welcome to my site.")

The comment will appear in the resulting .po file and should also be
displayed by most translation tools.

For more information, see :ref:`translator-comments`.

Backwards-incompatible changes in 1.3 alpha 2 Backwards-incompatible changes in 1.3 alpha 2
============================================= =============================================


Expand Down
10 changes: 7 additions & 3 deletions docs/releases/1.3.txt
Expand Up @@ -121,13 +121,17 @@ value, protect, or do nothing.
For more information, see the :attr:`~django.db.models.ForeignKey.on_delete` For more information, see the :attr:`~django.db.models.ForeignKey.on_delete`
documentation. documentation.


Contextual markers in translatable strings Contextual markers and comments for translatable strings
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


For translation strings with ambiguous meaning, you can now For translation strings with ambiguous meaning, you can now
use the ``pgettext`` function to specify the context of the string. use the ``pgettext`` function to specify the context of the string.


For more information, see :ref:`contextual-markers` And if you just want to add some information for translators, you
can also add special translator comments in the source.

For more information, see :ref:`contextual-markers` and
:ref:`translator-comments`.


Everything else Everything else
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
Expand Down
24 changes: 24 additions & 0 deletions docs/topics/i18n/internationalization.txt
Expand Up @@ -100,6 +100,30 @@ instead of positional interpolation (e.g., ``%s`` or ``%d``) whenever you
have more than a single parameter. If you used positional interpolation, have more than a single parameter. If you used positional interpolation,
translations wouldn't be able to reorder placeholder text. translations wouldn't be able to reorder placeholder text.


.. _translator-comments:

Comments for translators
------------------------

.. versionadded:: 1.3

If you would like to give translators hints about a translatable string, you
can add a comment prefixed with the ``Translators`` keyword on the line
preceding the string, e.g.::

def my_view(request):
# Translators: This message appears on the home page only
output = ugettext("Welcome to my site.")

This also works in templates with the :ttag:`comment` tag:

.. code-block:: django+html

{% comment %}Translators: This is a text of the base template {% endcomment %}

The comment will then appear in the resulting .po file and should also be
displayed by most translation tools.

Marking strings as no-op Marking strings as no-op
------------------------ ------------------------


Expand Down
8 changes: 8 additions & 0 deletions tests/regressiontests/i18n/commands/__init__.py
@@ -0,0 +1,8 @@
from django.utils.translation import ugettext as _

# Translators: This comment should be extracted
dummy1 = _("This is a translatable string.")

# This comment should not be extracted
dummy2 = _("This is another translatable string.")

13 changes: 12 additions & 1 deletion tests/regressiontests/i18n/commands/extraction.py
Expand Up @@ -38,7 +38,18 @@ def assertNotMsgId(self, msgid, s, use_quotes=True):
return self.assert_(not re.search('^msgid %s' % msgid, s, re.MULTILINE)) return self.assert_(not re.search('^msgid %s' % msgid, s, re.MULTILINE))




class TemplateExtractorTests(ExtractorTests): class BasicExtractorTests(ExtractorTests):

def test_comments_extractor(self):
os.chdir(self.test_dir)
management.call_command('makemessages', locale=LOCALE, verbosity=0)
self.assert_(os.path.exists(self.PO_FILE))
po_contents = open(self.PO_FILE, 'r').read()
self.assert_('#. Translators: This comment should be extracted' in po_contents)
self.assert_('This comment should not be extracted' not in po_contents)
# Comments in templates
self.assert_('#. Translators: Django template comment for translators' in po_contents)
self.assert_('#. Translators: Django comment block for translators' in po_contents)


def test_templatize(self): def test_templatize(self):
os.chdir(self.test_dir) os.chdir(self.test_dir)
Expand Down
7 changes: 5 additions & 2 deletions tests/regressiontests/i18n/commands/templates/test.html
@@ -1,5 +1,8 @@
{% load i18n %} {% load i18n %}
{% comment %}Translators: Django comment block for translators {% endcomment %}
{% trans "This literal should be included." %} {% trans "This literal should be included." %}
{% trans "This literal should also be included wrapped or not wrapped depending on the use of the --no-wrap option." %} {% trans "This literal should also be included wrapped or not wrapped depending on the use of the --no-wrap option." %}
{% blocktrans %}I think that 100% is more that 50% of anything.{% endblocktrans %}
{% blocktrans with 'txt' as obj %}I think that 100% is more that 50% of {{ obj }}.{% endblocktrans %} {# Translators: Django template comment for translators #}
<p>{% blocktrans %}I think that 100% is more that 50% of anything.{% endblocktrans %}</p>
{% blocktrans with 'txt' as obj %}I think that 100% is more that 50% of {{ obj }}.{% endblocktrans %}

0 comments on commit 17b329a

Please sign in to comment.