summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--babel/messages/mofile.py3
-rw-r--r--babel/support.py173
-rw-r--r--babel/tests/support.py148
3 files changed, 323 insertions, 1 deletions
diff --git a/babel/messages/mofile.py b/babel/messages/mofile.py
index 5938402..168613c 100644
--- a/babel/messages/mofile.py
+++ b/babel/messages/mofile.py
@@ -186,7 +186,8 @@ def write_mo(fileobj, catalog, use_fuzzy=False):
else:
msgstr = message.string.encode(catalog.charset)
if message.context:
- msgid = '\x04'.join(message.context.encode(catalog.charset), msgid)
+ msgid = '\x04'.join([message.context.encode(catalog.charset),
+ msgid])
offsets.append((len(ids), len(msgid), len(strs), len(msgstr)))
ids += msgid + '\x00'
strs += msgstr + '\x00'
diff --git a/babel/support.py b/babel/support.py
index ddc0894..20eac91 100644
--- a/babel/support.py
+++ b/babel/support.py
@@ -19,6 +19,7 @@ in applications.
from datetime import date, datetime, time, timedelta
import gettext
+import locale
from babel.core import Locale
from babel.dates import format_date, format_datetime, format_time, \
@@ -404,3 +405,175 @@ class Translations(gettext.GNUTranslations, object):
domain.
"""
return self._domains.get(domain, self).ungettext(singular, plural, num)
+
+ # Most of the downwards code, until it get's included in stdlib, from:
+ # http://bugs.python.org/file10036/gettext-pgettext.patch
+ #
+ # The encoding of a msgctxt and a msgid in a .mo file is
+ # msgctxt + "\x04" + msgid (gettext version >= 0.15)
+ CONTEXT_ENCODING = '%s\x04%s'
+
+ def pgettext(self, context, message):
+ """Look up the `context` and `message` id in the catalog and return the
+ corresponding message string, as an 8-bit string encoded with the
+ catalog's charset encoding, if known. If there is no entry in the
+ catalog for the `message` id and `context` , and a fallback has been
+ set, the look up is forwarded to the fallback's ``pgettext()``
+ method. Otherwise, the `message` id is returned.
+ """
+ ctxt_msg_id = self.CONTEXT_ENCODING % (context, message)
+ missing = object()
+ tmsg = self._catalog.get(ctxt_msg_id, missing)
+ if tmsg is missing:
+ if self._fallback:
+ return self._fallback.pgettext(context, message)
+ return message
+ # Encode the Unicode tmsg back to an 8-bit string, if possible
+ if self._output_charset:
+ return tmsg.encode(self._output_charset)
+ elif self._charset:
+ return tmsg.encode(self._charset)
+ return tmsg
+
+ def lpgettext(self, context, message):
+ """Equivalent to ``pgettext()``, but the translation is returned in the
+ preferred system encoding, if no other encoding was explicitly set with
+ ``bind_textdomain_codeset()``.
+ """
+ ctxt_msg_id = self.CONTEXT_ENCODING % (context, message)
+ missing = object()
+ tmsg = self._catalog.get(ctxt_msg_id, missing)
+ if tmsg is missing:
+ if self._fallback:
+ return self._fallback.lpgettext(context, message)
+ return message
+ if self._output_charset:
+ return tmsg.encode(self._output_charset)
+ return tmsg.encode(locale.getpreferredencoding())
+
+ def npgettext(self, context, singular, plural, num):
+ """Do a plural-forms lookup of a message id. `singular` is used as the
+ message id for purposes of lookup in the catalog, while `num` is used to
+ determine which plural form to use. The returned message string is an
+ 8-bit string encoded with the catalog's charset encoding, if known.
+
+ If the message id for `context` is not found in the catalog, and a
+ fallback is specified, the request is forwarded to the fallback's
+ ``npgettext()`` method. Otherwise, when ``num`` is 1 ``singular`` is
+ returned, and ``plural`` is returned in all other cases.
+ """
+ ctxt_msg_id = self.CONTEXT_ENCODING % (context, singular)
+ try:
+ tmsg = self._catalog[(ctxt_msg_id, self.plural(num))]
+ if self._output_charset:
+ return tmsg.encode(self._output_charset)
+ elif self._charset:
+ return tmsg.encode(self._charset)
+ return tmsg
+ except KeyError:
+ if self._fallback:
+ return self._fallback.npgettext(context, singular, plural, num)
+ if num == 1:
+ return singular
+ else:
+ return plural
+
+ def lnpgettext(self, context, singular, plural, num):
+ """Equivalent to ``npgettext()``, but the translation is returned in the
+ preferred system encoding, if no other encoding was explicitly set with
+ ``bind_textdomain_codeset()``.
+ """
+ ctxt_msg_id = self.CONTEXT_ENCODING % (context, singular)
+ try:
+ tmsg = self._catalog[(ctxt_msg_id, self.plural(num))]
+ if self._output_charset:
+ return tmsg.encode(self._output_charset)
+ return tmsg.encode(locale.getpreferredencoding())
+ except KeyError:
+ if self._fallback:
+ return self._fallback.lnpgettext(context, singular, plural, num)
+ if num == 1:
+ return singular
+ else:
+ return plural
+
+ def upgettext(self, context, message):
+ """Look up the `context` and `message` id in the catalog and return the
+ corresponding message string, as a Unicode string. If there is no entry
+ in the catalog for the `message` id and `context`, and a fallback has
+ been set, the look up is forwarded to the fallback's ``upgettext()``
+ method. Otherwise, the `message` id is returned.
+ """
+ ctxt_message_id = self.CONTEXT_ENCODING % (context, message)
+ missing = object()
+ tmsg = self._catalog.get(ctxt_message_id, missing)
+ if tmsg is missing:
+ if self._fallback:
+ return self._fallback.upgettext(context, message)
+ return unicode(message)
+ return tmsg
+
+ def unpgettext(self, context, singular, plural, num):
+ """Do a plural-forms lookup of a message id. `singular` is used as the
+ message id for purposes of lookup in the catalog, while `num` is used to
+ determine which plural form to use. The returned message string is a
+ Unicode string.
+
+ If the message id for `context` is not found in the catalog, and a
+ fallback is specified, the request is forwarded to the fallback's
+ ``unpgettext()`` method. Otherwise, when `num` is 1 `singular` is
+ returned, and `plural` is returned in all other cases.
+ """
+ ctxt_message_id = self.CONTEXT_ENCODING % (context, singular)
+ try:
+ tmsg = self._catalog[(ctxt_message_id, self.plural(num))]
+ except KeyError:
+ if self._fallback:
+ return self._fallback.unpgettext(context, singular, plural, num)
+ if num == 1:
+ tmsg = unicode(singular)
+ else:
+ tmsg = unicode(plural)
+ return tmsg
+
+ def dpgettext(self, domain, context, message):
+ """Like `pgettext()`, but look the message up in the specified
+ `domain`.
+ """
+ return self._domains.get(domain, self).pgettext(context, message)
+
+ def dupgettext(self, domain, context, message):
+ """Like `upgettext()`, but look the message up in the specified
+ `domain`.
+ """
+ return self._domains.get(domain, self).upgettext(context, message)
+
+ def ldpgettext(self, domain, context, message):
+ """Equivalent to ``dpgettext()``, but the translation is returned in the
+ preferred system encoding, if no other encoding was explicitly set with
+ ``bind_textdomain_codeset()``.
+ """
+ return self._domains.get(domain, self).lpgettext(context, message)
+
+ def dnpgettext(self, domain, context, singular, plural, num):
+ """Like ``npgettext``, but look the message up in the specified
+ `domain`.
+ """
+ return self._domains.get(domain, self).npgettext(context, singular,
+ plural, num)
+
+ def dunpgettext(self, domain, context, singular, plural, num):
+ """Like ``unpgettext``, but look the message up in the specified
+ `domain`.
+ """
+ return self._domains.get(domain, self).unpgettext(context, singular,
+ plural, num)
+
+ def ldnpgettext(self, domain, context, singular, plural, num):
+ """Equivalent to ``dnpgettext()``, but the translation is returned in
+ the preferred system encoding, if no other encoding was explicitly set
+ with ``bind_textdomain_codeset()``.
+ """
+ return self._domains.get(domain, self).lnpgettext(context, singular,
+ plural, num)
+
diff --git a/babel/tests/support.py b/babel/tests/support.py
index 215e8dd..00a1a0e 100644
--- a/babel/tests/support.py
+++ b/babel/tests/support.py
@@ -12,13 +12,161 @@
# history and logs, available at http://babel.edgewall.org/log/.
import doctest
+import os
+from StringIO import StringIO
import unittest
from babel import support
+from babel.messages import Catalog
+from babel.messages.mofile import write_mo
+
+class TranslationsTestCase(unittest.TestCase):
+
+ def setUp(self):
+ # Use a locale which won't fail to run the tests
+ os.environ['LANG'] = 'en_US.UTF-8'
+ messages1 = [
+ ('foo', {'string': 'Voh'}),
+ ('foo', {'string': 'VohCTX', 'context': 'foo'}),
+ (('foo1', 'foos1'), {'string': ('Voh1', 'Vohs1')}),
+ (('foo1', 'foos1'), {'string': ('VohCTX1', 'VohsCTX1'), 'context': 'foo'}),
+ ]
+ messages2 = [
+ ('foo', {'string': 'VohD'}),
+ ('foo', {'string': 'VohCTXD', 'context': 'foo'}),
+ (('foo1', 'foos1'), {'string': ('VohD1', 'VohsD1')}),
+ (('foo1', 'foos1'), {'string': ('VohCTXD1', 'VohsCTXD1'), 'context': 'foo'}),
+ ]
+ catalog1 = Catalog(locale='en_GB', domain='messages')
+ catalog2 = Catalog(locale='en_GB', domain='messages1')
+ for ids, kwargs in messages1:
+ catalog1.add(ids, **kwargs)
+ for ids, kwargs in messages2:
+ catalog2.add(ids, **kwargs)
+ catalog1_fp = StringIO()
+ catalog2_fp = StringIO()
+ write_mo(catalog1_fp, catalog1)
+ catalog1_fp.seek(0)
+ write_mo(catalog2_fp, catalog2)
+ catalog2_fp.seek(0)
+ translations1 = support.Translations(catalog1_fp)
+ translations2 = support.Translations(catalog2_fp, domain='messages1')
+ self.translations = translations1.add(translations2, merge=False)
+
+ def assertEqualTypeToo(self, expected, result):
+ self.assertEqual(expected, result)
+ assert type(expected) == type(result), "instance type's do not " + \
+ "match: %r!=%r" % (type(expected), type(result))
+
+ def test_pgettext(self):
+ self.assertEqualTypeToo('Voh', self.translations.gettext('foo'))
+ self.assertEqualTypeToo('VohCTX', self.translations.pgettext('foo',
+ 'foo'))
+
+ def test_upgettext(self):
+ self.assertEqualTypeToo(u'Voh', self.translations.ugettext('foo'))
+ self.assertEqualTypeToo(u'VohCTX', self.translations.upgettext('foo',
+ 'foo'))
+
+ def test_lpgettext(self):
+ self.assertEqualTypeToo('Voh', self.translations.lgettext('foo'))
+ self.assertEqualTypeToo('VohCTX', self.translations.lpgettext('foo',
+ 'foo'))
+
+ def test_npgettext(self):
+ self.assertEqualTypeToo('Voh1',
+ self.translations.ngettext('foo1', 'foos1', 1))
+ self.assertEqualTypeToo('Vohs1',
+ self.translations.ngettext('foo1', 'foos1', 2))
+ self.assertEqualTypeToo('VohCTX1',
+ self.translations.npgettext('foo', 'foo1',
+ 'foos1', 1))
+ self.assertEqualTypeToo('VohsCTX1',
+ self.translations.npgettext('foo', 'foo1',
+ 'foos1', 2))
+
+ def test_unpgettext(self):
+ self.assertEqualTypeToo(u'Voh1',
+ self.translations.ungettext('foo1', 'foos1', 1))
+ self.assertEqualTypeToo(u'Vohs1',
+ self.translations.ungettext('foo1', 'foos1', 2))
+ self.assertEqualTypeToo(u'VohCTX1',
+ self.translations.unpgettext('foo', 'foo1',
+ 'foos1', 1))
+ self.assertEqualTypeToo(u'VohsCTX1',
+ self.translations.unpgettext('foo', 'foo1',
+ 'foos1', 2))
+
+ def test_lnpgettext(self):
+ self.assertEqualTypeToo('Voh1',
+ self.translations.lngettext('foo1', 'foos1', 1))
+ self.assertEqualTypeToo('Vohs1',
+ self.translations.lngettext('foo1', 'foos1', 2))
+ self.assertEqualTypeToo('VohCTX1',
+ self.translations.lnpgettext('foo', 'foo1',
+ 'foos1', 1))
+ self.assertEqualTypeToo('VohsCTX1',
+ self.translations.lnpgettext('foo', 'foo1',
+ 'foos1', 2))
+
+ def test_dpgettext(self):
+ self.assertEqualTypeToo(
+ 'VohD', self.translations.dgettext('messages1', 'foo'))
+ self.assertEqualTypeToo(
+ 'VohCTXD', self.translations.dpgettext('messages1', 'foo', 'foo'))
+
+ def test_dupgettext(self):
+ self.assertEqualTypeToo(
+ u'VohD', self.translations.dugettext('messages1', 'foo'))
+ self.assertEqualTypeToo(
+ u'VohCTXD', self.translations.dupgettext('messages1', 'foo', 'foo'))
+
+ def test_ldpgettext(self):
+ self.assertEqualTypeToo(
+ 'VohD', self.translations.ldgettext('messages1', 'foo'))
+ self.assertEqualTypeToo(
+ 'VohCTXD', self.translations.ldpgettext('messages1', 'foo', 'foo'))
+
+ def test_dnpgettext(self):
+ self.assertEqualTypeToo(
+ 'VohD1', self.translations.dngettext('messages1', 'foo1', 'foos1', 1))
+ self.assertEqualTypeToo(
+ 'VohsD1', self.translations.dngettext('messages1', 'foo1', 'foos1', 2))
+ self.assertEqualTypeToo(
+ 'VohCTXD1', self.translations.dnpgettext('messages1', 'foo', 'foo1',
+ 'foos1', 1))
+ self.assertEqualTypeToo(
+ 'VohsCTXD1', self.translations.dnpgettext('messages1', 'foo', 'foo1',
+ 'foos1', 2))
+
+ def test_dunpgettext(self):
+ self.assertEqualTypeToo(
+ u'VohD1', self.translations.dungettext('messages1', 'foo1', 'foos1', 1))
+ self.assertEqualTypeToo(
+ u'VohsD1', self.translations.dungettext('messages1', 'foo1', 'foos1', 2))
+ self.assertEqualTypeToo(
+ u'VohCTXD1', self.translations.dunpgettext('messages1', 'foo', 'foo1',
+ 'foos1', 1))
+ self.assertEqualTypeToo(
+ u'VohsCTXD1', self.translations.dunpgettext('messages1', 'foo', 'foo1',
+ 'foos1', 2))
+
+ def test_ldnpgettext(self):
+ self.assertEqualTypeToo(
+ 'VohD1', self.translations.ldngettext('messages1', 'foo1', 'foos1', 1))
+ self.assertEqualTypeToo(
+ 'VohsD1', self.translations.ldngettext('messages1', 'foo1', 'foos1', 2))
+ self.assertEqualTypeToo(
+ 'VohCTXD1', self.translations.ldnpgettext('messages1', 'foo', 'foo1',
+ 'foos1', 1))
+ self.assertEqualTypeToo(
+ 'VohsCTXD1', self.translations.ldnpgettext('messages1', 'foo', 'foo1',
+ 'foos1', 2))
def suite():
suite = unittest.TestSuite()
suite.addTest(doctest.DocTestSuite(support))
+ suite.addTest(unittest.makeSuite(TranslationsTestCase, 'test'))
return suite
if __name__ == '__main__':