Permalink
Browse files

Fixed #9988 -- Added support for translation contexts. Thanks, Claude…

… Paroz.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14450 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
jezdez committed Nov 4, 2010
1 parent 0659391 commit 83aeb3c768173f48dc295c3194fecd705d1c05ac
@@ -190,7 +190,7 @@ def make_messages(locale=None, domain='django', verbosity='1', all=False,
f.write(src)
finally:
f.close()
cmd = 'xgettext -d %s -L Perl --keyword=gettext_noop --keyword=gettext_lazy --keyword=ngettext_lazy:1,2 --from-code UTF-8 -o - "%s"' % (domain, os.path.join(dirpath, thefile))
cmd = 'xgettext -d %s -L Perl --keyword=gettext_noop --keyword=gettext_lazy --keyword=ngettext_lazy:1,2 --keyword=pgettext:1c,2 --keyword=npgettext:1c,2,3 --from-code UTF-8 -o - "%s"' % (domain, os.path.join(dirpath, thefile))
msgs, errors = _popen(cmd)
if errors:
raise CommandError("errors happened while running xgettext on %s\n%s" % (file, errors))
@@ -225,7 +225,7 @@ def make_messages(locale=None, domain='django', verbosity='1', all=False,
raise SyntaxError(msg)
if verbosity > 1:
sys.stdout.write('processing file %s in %s\n' % (file, dirpath))
cmd = 'xgettext -d %s -L Python --keyword=gettext_noop --keyword=gettext_lazy --keyword=ngettext_lazy:1,2 --keyword=ugettext_noop --keyword=ugettext_lazy --keyword=ungettext_lazy:1,2 --from-code UTF-8 -o - "%s"' % (
cmd = 'xgettext -d %s -L Python --keyword=gettext_noop --keyword=gettext_lazy --keyword=ngettext_lazy:1,2 --keyword=ugettext_noop --keyword=ugettext_lazy --keyword=ungettext_lazy:1,2 --keyword=pgettext:1c,2 --keyword=npgettext:1c,2,3 --keyword=pgettext_lazy:1c,2 --keyword=npgettext_lazy:1c,2,3 --from-code UTF-8 -o - "%s"' % (
domain, os.path.join(dirpath, thefile))
msgs, errors = _popen(cmd)
if errors:
@@ -10,7 +10,8 @@
'get_language', 'get_language_bidi', 'get_date_formats',
'get_partial_date_formats', 'check_for_language', 'to_locale',
'get_language_from_request', 'templatize', 'ugettext', 'ugettext_lazy',
'ungettext', 'deactivate_all']
'ungettext', 'ungettext_lazy', 'pgettext', 'pgettext_lazy',
'npgettext', 'npgettext_lazy', 'deactivate_all']
# Here be dragons, so a short explanation of the logic won't hurt:
# We are trying to solve two problems: (1) access settings, in particular
@@ -63,10 +64,18 @@ def ugettext(message):
def ungettext(singular, plural, number):
return _trans.ungettext(singular, plural, number)
def pgettext(context, message):
return _trans.pgettext(context, message)
def npgettext(context, singular, plural, number):
return _trans.npgettext(context, singular, plural, number)
ngettext_lazy = lazy(ngettext, str)
gettext_lazy = lazy(gettext, str)
ungettext_lazy = lazy(ungettext, unicode)
ugettext_lazy = lazy(ugettext, unicode)
pgettext_lazy = lazy(pgettext, unicode)
npgettext_lazy = lazy(npgettext, unicode)
def activate(language):
return _trans.activate(language)
@@ -15,6 +15,12 @@ def ngettext(singular, plural, number):
def ungettext(singular, plural, number):
return force_unicode(ngettext(singular, plural, number))
def pgettext(context, message):
return ugettext(message)
def npgettext(context, singular, plural, number):
return ungettext(singular, plural, number)
activate = lambda x: None
deactivate = deactivate_all = lambda: None
get_language = lambda: settings.LANGUAGE_CODE
@@ -24,6 +24,9 @@
# file lookups when checking the same locale on repeated requests.
_accepted = {}
# magic gettext number to separate context from message
CONTEXT_SEPARATOR = u"\x04"
# Format of Accept-Language header values. From RFC 2616, section 14.4 and 3.9.
accept_language_re = re.compile(r'''
([A-Za-z]{1,8}(?:-[A-Za-z]{1,8})*|\*) # "en", "en-au", "x-y-z", "*"
@@ -279,6 +282,14 @@ def gettext(message):
def ugettext(message):
return do_translate(message, 'ugettext')
def pgettext(context, message):
result = do_translate(
u"%s%s%s" % (context, CONTEXT_SEPARATOR, message), 'ugettext')
if CONTEXT_SEPARATOR in result:
# Translation not found
result = message
return result
def gettext_noop(message):
"""
Marks strings for translation but doesn't translate them now. This can be
@@ -313,6 +324,15 @@ def ungettext(singular, plural, number):
"""
return do_ntranslate(singular, plural, number, 'ungettext')
def npgettext(context, singular, plural, number):
result = do_ntranslate(u"%s%s%s" % (context, CONTEXT_SEPARATOR, singular),
u"%s%s%s" % (context, CONTEXT_SEPARATOR, plural),
number, 'ungettext')
if CONTEXT_SEPARATOR in result:
# Translation not found
result = do_ntranslate(singular, plural, number, 'ungettext')
return result
def check_for_language(lang_code):
"""
Checks whether there is a global language file for the given language
@@ -68,6 +68,8 @@ def get_formats():
function gettext(msgid) { return msgid; }
function ngettext(singular, plural, count) { return (count == 1) ? singular : plural; }
function gettext_noop(msgid) { return msgid; }
function pgettext(context, msgid) { return msgid; }
function npgettext(context, singular, plural, count) { return (count == 1) ? singular : plural; }
"""
LibHead = """
@@ -98,6 +100,21 @@ def get_formats():
function gettext_noop(msgid) { return msgid; }
function pgettext(context, msgid) {
var value = gettext(context + '\x04' + msgid);
if (value.indexOf('\x04') != -1) {
value = msgid;
}
return value;
}
function npgettext(context, singular, plural, count) {
var value = ngettext(context + '\x04' + singular, context + '\x04' + plural, count);
if (value.indexOf('\x04') != -1) {
value = ngettext(singular, plural, count);
}
return value;
}
"""
LibFormatHead = """
@@ -86,6 +86,14 @@ Users of Python 2.5 and above may now use :ref:`transaction management functions
For more information, see :ref:`transaction-management-functions`.
Contextual markers in translatable strings
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For translation strings with ambiguous meaning, you can now
use the ``pgettext`` function to specify the context of the string.
For more information, see :ref:`contextual-markers`
Everything else
~~~~~~~~~~~~~~~
@@ -193,6 +193,39 @@ cardinality of the elements at play.
``django-admin.py compilemessages`` or a ``KeyError`` Python exception at
runtime.
.. _contextual-markers:
Contextual markers
------------------
.. versionadded:: 1.3
Sometimes words have several meanings, such as ``"May"`` in English, which
refers to a month name and to a verb. To enable translators to translate
these words correctly in different contexts, you can use the
``django.utils.translation.pgettext()`` function, or the
``django.utils.translation.npgettext()`` function if the string needs
pluralization. Both take a context string as the first variable.
In the resulting .po file, the string will then appear as often as there are
different contextual markers for the same string (the context will appear on
the ``msgctxt`` line), allowing the translator to give a different translation
for each of them.
For example::
from django.utils.translation import pgettext
month = pgettext("month name", "May")
will appear in the .po file as:
.. code-block:: po
msgctxt "month name"
msgid "May"
msgstr ""
.. _lazy-translations:
Lazy translation
Binary file not shown.
@@ -20,3 +20,20 @@ msgstr ""
#: models.py:3
msgid "Date/time"
msgstr "Datum/Zeit (LOCALE_PATHS)"
#: models.py:5
msgctxt "month name"
msgid "May"
msgstr "Mai"
#: models.py:7
msgctxt "verb"
msgid "May"
msgstr "Kann"
#: models.py:9
msgctxt "search"
msgid "%d result"
msgid_plural "%d results"
msgstr[0] "%d Resultat"
msgstr[1] "%d Resultate"
@@ -11,7 +11,7 @@
from django.utils.formats import get_format, date_format, time_format, localize, localize_input, iter_format_modules
from django.utils.numberformat import format as nformat
from django.utils.safestring import mark_safe, SafeString, SafeUnicode
from django.utils.translation import ugettext, ugettext_lazy, activate, deactivate, gettext_lazy, to_locale
from django.utils.translation import ugettext, ugettext_lazy, activate, deactivate, gettext_lazy, pgettext, npgettext, to_locale
from django.utils.importlib import import_module
@@ -54,6 +54,22 @@ def test_lazy_pickle(self):
s2 = pickle.loads(pickle.dumps(s1))
self.assertEqual(unicode(s2), "test")
def test_pgettext(self):
# Reset translation catalog to include other/locale/de
self.old_locale_paths = settings.LOCALE_PATHS
settings.LOCALE_PATHS += (os.path.join(os.path.dirname(os.path.abspath(__file__)), 'other', 'locale'),)
from django.utils.translation import trans_real
trans_real._active = {}
trans_real._translations = {}
activate('de')
self.assertEqual(pgettext("unexisting", "May"), u"May")
self.assertEqual(pgettext("month name", "May"), u"Mai")
self.assertEqual(pgettext("verb", "May"), u"Kann")
self.assertEqual(npgettext("search", "%d result", "%d results", 4) % 4, u"4 Resultate")
settings.LOCALE_PATHS = self.old_locale_paths
def test_string_concat(self):
"""
unicode(string_concat(...)) should not raise a TypeError - #4796
Binary file not shown.
@@ -22,3 +22,7 @@ msgstr "il faut le traduire"
msgid "Choose a time"
msgstr "Choisir une heure"
msgctxt "month name"
msgid "May"
msgstr "mai"
@@ -30,6 +30,9 @@ def test_jsi18n(self):
# catalog['this is to be translated'] = 'same_that_trans_txt'
# javascript_quote is used to be able to check unicode strings
self.assertContains(response, javascript_quote(trans_txt), 1)
if lang_code == 'fr':
# Message with context (msgctxt)
self.assertContains(response, "['month name\x04May'] = 'mai';", 1)
class JsI18NTests(TestCase):

0 comments on commit 83aeb3c

Please sign in to comment.