Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
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...
commit 83aeb3c768173f48dc295c3194fecd705d1c05ac 1 parent 0659391
@jezdez jezdez authored
View
4 django/core/management/commands/makemessages.py
@@ -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:
View
11 django/utils/translation/__init__.py
@@ -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)
View
6 django/utils/translation/trans_null.py
@@ -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
View
20 django/utils/translation/trans_real.py
@@ -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
View
17 django/views/i18n.py
@@ -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 = """
View
8 docs/releases/1.3.txt
@@ -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
~~~~~~~~~~~~~~~
View
33 docs/topics/i18n/internationalization.txt
@@ -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
View
BIN  tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.mo
Binary file not shown
View
17 tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.po
@@ -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"
View
18 tests/regressiontests/i18n/tests.py
@@ -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
View
BIN  tests/regressiontests/views/locale/fr/LC_MESSAGES/djangojs.mo
Binary file not shown
View
4 tests/regressiontests/views/locale/fr/LC_MESSAGES/djangojs.po
@@ -22,3 +22,7 @@ msgstr "il faut le traduire"
msgid "Choose a time"
msgstr "Choisir une heure"
+
+msgctxt "month name"
+msgid "May"
+msgstr "mai"
View
3  tests/regressiontests/views/tests/i18n.py
@@ -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):
Please sign in to comment.
Something went wrong with that request. Please try again.