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
This commit is contained in:
parent
0659391baf
commit
83aeb3c768
|
@ -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 @@ __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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 = """
|
||||
|
|
|
@ -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.test import TestCase
|
|||
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 @@ class TranslationTests(TestCase):
|
|||
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 @@ class I18NTests(TestCase):
|
|||
# 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):
|
||||
|
|
Loading…
Reference in New Issue