From 83aeb3c768173f48dc295c3194fecd705d1c05ac Mon Sep 17 00:00:00 2001 From: Jannis Leidel Date: Thu, 4 Nov 2010 10:48:27 +0000 Subject: [PATCH] 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 --- .../core/management/commands/makemessages.py | 4 +-- django/utils/translation/__init__.py | 11 +++++- django/utils/translation/trans_null.py | 6 ++++ django/utils/translation/trans_real.py | 20 +++++++++++ django/views/i18n.py | 17 +++++++++ docs/releases/1.3.txt | 8 +++++ docs/topics/i18n/internationalization.txt | 33 ++++++++++++++++++ .../other/locale/de/LC_MESSAGES/django.mo | Bin 469 -> 611 bytes .../other/locale/de/LC_MESSAGES/django.po | 17 +++++++++ tests/regressiontests/i18n/tests.py | 18 +++++++++- .../views/locale/fr/LC_MESSAGES/djangojs.mo | Bin 484 -> 519 bytes .../views/locale/fr/LC_MESSAGES/djangojs.po | 4 +++ tests/regressiontests/views/tests/i18n.py | 3 ++ 13 files changed, 137 insertions(+), 4 deletions(-) diff --git a/django/core/management/commands/makemessages.py b/django/core/management/commands/makemessages.py index 955a822e0c..34c9b8ce91 100644 --- a/django/core/management/commands/makemessages.py +++ b/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: diff --git a/django/utils/translation/__init__.py b/django/utils/translation/__init__.py index 4477c291f4..0e1b4f8d67 100644 --- a/django/utils/translation/__init__.py +++ b/django/utils/translation/__init__.py @@ -10,7 +10,8 @@ __all__ = ['gettext', 'gettext_noop', 'gettext_lazy', 'ngettext', '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) diff --git a/django/utils/translation/trans_null.py b/django/utils/translation/trans_null.py index 8a075806ec..a8bebad942 100644 --- a/django/utils/translation/trans_null.py +++ b/django/utils/translation/trans_null.py @@ -15,6 +15,12 @@ ngettext_lazy = ngettext 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 diff --git a/django/utils/translation/trans_real.py b/django/utils/translation/trans_real.py index fe34b6ab0f..8486fdf8f4 100644 --- a/django/utils/translation/trans_real.py +++ b/django/utils/translation/trans_real.py @@ -24,6 +24,9 @@ _default = None # 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 diff --git a/django/views/i18n.py b/django/views/i18n.py index 2078649e3d..133c42f05b 100644 --- a/django/views/i18n.py +++ b/django/views/i18n.py @@ -68,6 +68,8 @@ NullSource = """ 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 @@ function ngettext(singular, plural, count) { 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 = """ diff --git a/docs/releases/1.3.txt b/docs/releases/1.3.txt index b371f6dc64..7e843d90fd 100644 --- a/docs/releases/1.3.txt +++ b/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 ~~~~~~~~~~~~~~~ diff --git a/docs/topics/i18n/internationalization.txt b/docs/topics/i18n/internationalization.txt index 5fc347c89d..e4ed85ce3f 100644 --- a/docs/topics/i18n/internationalization.txt +++ b/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 diff --git a/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.mo b/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.mo index 2bc9343aa31850042669f03865cb890cc73e35d3..b662e9392da234d2b1a618dcc20ea5d84b0da19c 100644 GIT binary patch delta 243 zcmcc0{FtTwo)F7a1|VPsVi_QI0b+I_&H-W&=m26)AnpWWJ|Lb9#L_^#2#7g=cpnhQ zGcqt72hx&2tj@&1zzn1vfwTmWb_LQbKspXc1C=oVDKG$u0oA#KIUvg!ToOxC^-D5y zQyFse^GY%l@)C1XS$q>K8H!UAi;^=~R8tg+Qj1G-N*Lg*VurHRq9l-_jZxK%O1_Di VAh{s0szk6l7(120J25Yh0RZ(vED`_! delta 99 zcmaFNa+Nvio)F7a1|VPpVi_RT0b*7lwgF-g2moSEAPxlLct!?>Xdo>K#JhoPAOh=tJ0kJ6~1H*D4 zEeOPSfox_V{T@gQ1L@B|8i*KJCI-s!<>u#=WGLh%=BBdvCRT3DOJZcsP0XBpo>3G4 D4`&ow delta 114 zcmZo?dBPlcPl#nI0}wC+u?!HK05K~N`v5TrBmgll5GMk$1Q6!~u_+@1LkEx+1mb-_ qwlI*s0HlG4fnj2z+{V5{Mv=@Mg|x)d5`~;pg_5Ggl+w(iR0aS5$`O