~ubuntu-branches/ubuntu/trusty/python-babel/trusty

« back to all changes in this revision

Viewing changes to babel/support.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short
  • Date: 2013-10-28 10:11:31 UTC
  • mfrom: (4.1.2 sid)
  • Revision ID: package-import@ubuntu.com-20131028101131-zwbmm8sc29iemmlr
Tags: 1.3-2ubuntu1
* Merge from Debian unstable.  Remaining changes:
  - debian/rules: Run the testsuite during builds.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# -*- coding: utf-8 -*-
2
 
#
3
 
# Copyright (C) 2007 Edgewall Software
4
 
# All rights reserved.
5
 
#
6
 
# This software is licensed as described in the file COPYING, which
7
 
# you should have received as part of this distribution. The terms
8
 
# are also available at http://babel.edgewall.org/wiki/License.
9
 
#
10
 
# This software consists of voluntary contributions made by many
11
 
# individuals. For the exact contribution history, see the revision
12
 
# history and logs, available at http://babel.edgewall.org/log/.
13
 
 
14
 
"""Several classes and functions that help with integrating and using Babel
15
 
in applications.
16
 
 
17
 
.. note: the code in this module is not used by Babel itself
18
 
"""
19
 
 
20
 
from datetime import date, datetime, time
 
2
"""
 
3
    babel.support
 
4
    ~~~~~~~~~~~~~
 
5
 
 
6
    Several classes and functions that help with integrating and using Babel
 
7
    in applications.
 
8
 
 
9
    .. note: the code in this module is not used by Babel itself
 
10
 
 
11
    :copyright: (c) 2013 by the Babel Team.
 
12
    :license: BSD, see LICENSE for more details.
 
13
"""
 
14
 
21
15
import gettext
 
16
import locale
22
17
 
23
18
from babel.core import Locale
24
 
from babel.dates import format_date, format_datetime, format_time, LC_TIME
 
19
from babel.dates import format_date, format_datetime, format_time, \
 
20
     format_timedelta
25
21
from babel.numbers import format_number, format_decimal, format_currency, \
26
 
                          format_percent, format_scientific, LC_NUMERIC
27
 
from babel.util import set, UTC
28
 
 
29
 
__all__ = ['Format', 'LazyProxy', 'Translations']
30
 
__docformat__ = 'restructuredtext en'
 
22
     format_percent, format_scientific
 
23
from babel._compat import PY2, text_type, text_to_native
31
24
 
32
25
 
33
26
class Format(object):
34
27
    """Wrapper class providing the various date and number formatting functions
35
28
    bound to a specific locale and time-zone.
36
 
    
 
29
 
 
30
    >>> from babel.util import UTC
 
31
    >>> from datetime import date
37
32
    >>> fmt = Format('en_US', UTC)
38
33
    >>> fmt.date(date(2007, 4, 1))
39
34
    u'Apr 1, 2007'
43
38
 
44
39
    def __init__(self, locale, tzinfo=None):
45
40
        """Initialize the formatter.
46
 
        
 
41
 
47
42
        :param locale: the locale identifier or `Locale` instance
48
43
        :param tzinfo: the time-zone info (a `tzinfo` instance or `None`)
49
44
        """
52
47
 
53
48
    def date(self, date=None, format='medium'):
54
49
        """Return a date formatted according to the given pattern.
55
 
        
 
50
 
 
51
        >>> from datetime import date
56
52
        >>> fmt = Format('en_US')
57
53
        >>> fmt.date(date(2007, 4, 1))
58
54
        u'Apr 1, 2007'
59
 
        
60
 
        :see: `babel.dates.format_date`
61
55
        """
62
56
        return format_date(date, format, locale=self.locale)
63
57
 
64
58
    def datetime(self, datetime=None, format='medium'):
65
59
        """Return a date and time formatted according to the given pattern.
66
 
        
 
60
 
 
61
        >>> from datetime import datetime
67
62
        >>> from pytz import timezone
68
63
        >>> fmt = Format('en_US', tzinfo=timezone('US/Eastern'))
69
64
        >>> fmt.datetime(datetime(2007, 4, 1, 15, 30))
70
 
        u'Apr 1, 2007 11:30:00 AM'
71
 
        
72
 
        :see: `babel.dates.format_datetime`
 
65
        u'Apr 1, 2007, 11:30:00 AM'
73
66
        """
74
67
        return format_datetime(datetime, format, tzinfo=self.tzinfo,
75
68
                               locale=self.locale)
76
69
 
77
70
    def time(self, time=None, format='medium'):
78
71
        """Return a time formatted according to the given pattern.
79
 
        
 
72
 
 
73
        >>> from datetime import datetime
80
74
        >>> from pytz import timezone
81
75
        >>> fmt = Format('en_US', tzinfo=timezone('US/Eastern'))
82
76
        >>> fmt.time(datetime(2007, 4, 1, 15, 30))
83
77
        u'11:30:00 AM'
84
 
        
85
 
        :see: `babel.dates.format_time`
86
78
        """
87
79
        return format_time(time, format, tzinfo=self.tzinfo, locale=self.locale)
88
80
 
 
81
    def timedelta(self, delta, granularity='second', threshold=.85,
 
82
                  format='medium', add_direction=False):
 
83
        """Return a time delta according to the rules of the given locale.
 
84
 
 
85
        >>> from datetime import timedelta
 
86
        >>> fmt = Format('en_US')
 
87
        >>> fmt.timedelta(timedelta(weeks=11))
 
88
        u'3 months'
 
89
        """
 
90
        return format_timedelta(delta, granularity=granularity,
 
91
                                threshold=threshold,
 
92
                                format=format, add_direction=add_direction,
 
93
                                locale=self.locale)
 
94
 
89
95
    def number(self, number):
90
96
        """Return an integer number formatted for the locale.
91
 
        
 
97
 
92
98
        >>> fmt = Format('en_US')
93
99
        >>> fmt.number(1099)
94
100
        u'1,099'
95
 
        
96
 
        :see: `babel.numbers.format_number`
97
101
        """
98
102
        return format_number(number, locale=self.locale)
99
103
 
100
104
    def decimal(self, number, format=None):
101
105
        """Return a decimal number formatted for the locale.
102
 
        
 
106
 
103
107
        >>> fmt = Format('en_US')
104
108
        >>> fmt.decimal(1.2345)
105
109
        u'1.234'
106
 
        
107
 
        :see: `babel.numbers.format_decimal`
108
110
        """
109
111
        return format_decimal(number, format, locale=self.locale)
110
112
 
111
113
    def currency(self, number, currency):
112
114
        """Return a number in the given currency formatted for the locale.
113
 
        
114
 
        :see: `babel.numbers.format_currency`
115
115
        """
116
116
        return format_currency(number, currency, locale=self.locale)
117
117
 
118
118
    def percent(self, number, format=None):
119
119
        """Return a number formatted as percentage for the locale.
120
 
        
 
120
 
121
121
        >>> fmt = Format('en_US')
122
122
        >>> fmt.percent(0.34)
123
123
        u'34%'
124
 
        
125
 
        :see: `babel.numbers.format_percent`
126
124
        """
127
125
        return format_percent(number, format, locale=self.locale)
128
126
 
129
127
    def scientific(self, number):
130
128
        """Return a number formatted using scientific notation for the locale.
131
 
        
132
 
        :see: `babel.numbers.format_scientific`
133
129
        """
134
130
        return format_scientific(number, locale=self.locale)
135
131
 
137
133
class LazyProxy(object):
138
134
    """Class for proxy objects that delegate to a specified function to evaluate
139
135
    the actual object.
140
 
    
 
136
 
141
137
    >>> def greeting(name='world'):
142
138
    ...     return 'Hello, %s!' % name
143
139
    >>> lazy_greeting = LazyProxy(greeting, name='Joe')
147
143
    u'  Hello, Joe!'
148
144
    >>> u'(%s)' % lazy_greeting
149
145
    u'(Hello, Joe!)'
150
 
    
 
146
 
151
147
    This can be used, for example, to implement lazy translation functions that
152
148
    delay the actual translation until the string is actually used. The
153
149
    rationale for such behavior is that the locale of the user may not always
154
150
    be available. In web applications, you only know the locale when processing
155
151
    a request.
156
 
    
 
152
 
157
153
    The proxy implementation attempts to be as complete as possible, so that
158
154
    the lazy objects should mostly work as expected, for example for sorting:
159
 
    
 
155
 
160
156
    >>> greetings = [
161
157
    ...     LazyProxy(greeting, 'world'),
162
158
    ...     LazyProxy(greeting, 'Joe'),
169
165
    Hello, universe!
170
166
    Hello, world!
171
167
    """
172
 
    __slots__ = ['_func', '_args', '_kwargs', '_value']
 
168
    __slots__ = ['_func', '_args', '_kwargs', '_value', '_is_cache_enabled']
173
169
 
174
170
    def __init__(self, func, *args, **kwargs):
 
171
        is_cache_enabled = kwargs.pop('enable_cache', True)
175
172
        # Avoid triggering our own __setattr__ implementation
176
173
        object.__setattr__(self, '_func', func)
177
174
        object.__setattr__(self, '_args', args)
178
175
        object.__setattr__(self, '_kwargs', kwargs)
 
176
        object.__setattr__(self, '_is_cache_enabled', is_cache_enabled)
179
177
        object.__setattr__(self, '_value', None)
180
178
 
 
179
    @property
181
180
    def value(self):
182
181
        if self._value is None:
183
182
            value = self._func(*self._args, **self._kwargs)
 
183
            if not self._is_cache_enabled:
 
184
                return value
184
185
            object.__setattr__(self, '_value', value)
185
186
        return self._value
186
 
    value = property(value)
187
187
 
188
188
    def __contains__(self, key):
189
189
        return key in self.value
263
263
    def __setitem__(self, key, value):
264
264
        self.value[key] = value
265
265
 
266
 
    
267
 
class Translations(gettext.GNUTranslations, object):
 
266
 
 
267
class NullTranslations(gettext.NullTranslations, object):
 
268
 
 
269
    DEFAULT_DOMAIN = None
 
270
 
 
271
    def __init__(self, fp=None):
 
272
        """Initialize a simple translations class which is not backed by a
 
273
        real catalog. Behaves similar to gettext.NullTranslations but also
 
274
        offers Babel's on *gettext methods (e.g. 'dgettext()').
 
275
 
 
276
        :param fp: a file-like object (ignored in this class)
 
277
        """
 
278
        # These attributes are set by gettext.NullTranslations when a catalog
 
279
        # is parsed (fp != None). Ensure that they are always present because
 
280
        # some *gettext methods (including '.gettext()') rely on the attributes.
 
281
        self._catalog = {}
 
282
        self.plural = lambda n: int(n != 1)
 
283
        super(NullTranslations, self).__init__(fp=fp)
 
284
        self.files = filter(None, [getattr(fp, 'name', None)])
 
285
        self.domain = self.DEFAULT_DOMAIN
 
286
        self._domains = {}
 
287
 
 
288
    def dgettext(self, domain, message):
 
289
        """Like ``gettext()``, but look the message up in the specified
 
290
        domain.
 
291
        """
 
292
        return self._domains.get(domain, self).gettext(message)
 
293
 
 
294
    def ldgettext(self, domain, message):
 
295
        """Like ``lgettext()``, but look the message up in the specified
 
296
        domain.
 
297
        """
 
298
        return self._domains.get(domain, self).lgettext(message)
 
299
 
 
300
    def udgettext(self, domain, message):
 
301
        """Like ``ugettext()``, but look the message up in the specified
 
302
        domain.
 
303
        """
 
304
        return self._domains.get(domain, self).ugettext(message)
 
305
    # backward compatibility with 0.9
 
306
    dugettext = udgettext
 
307
 
 
308
    def dngettext(self, domain, singular, plural, num):
 
309
        """Like ``ngettext()``, but look the message up in the specified
 
310
        domain.
 
311
        """
 
312
        return self._domains.get(domain, self).ngettext(singular, plural, num)
 
313
 
 
314
    def ldngettext(self, domain, singular, plural, num):
 
315
        """Like ``lngettext()``, but look the message up in the specified
 
316
        domain.
 
317
        """
 
318
        return self._domains.get(domain, self).lngettext(singular, plural, num)
 
319
 
 
320
    def udngettext(self, domain, singular, plural, num):
 
321
        """Like ``ungettext()`` but look the message up in the specified
 
322
        domain.
 
323
        """
 
324
        return self._domains.get(domain, self).ungettext(singular, plural, num)
 
325
    # backward compatibility with 0.9
 
326
    dungettext  = udngettext
 
327
 
 
328
    # Most of the downwards code, until it get's included in stdlib, from:
 
329
    #    http://bugs.python.org/file10036/gettext-pgettext.patch
 
330
    #
 
331
    # The encoding of a msgctxt and a msgid in a .mo file is
 
332
    # msgctxt + "\x04" + msgid (gettext version >= 0.15)
 
333
    CONTEXT_ENCODING = '%s\x04%s'
 
334
 
 
335
    def pgettext(self, context, message):
 
336
        """Look up the `context` and `message` id in the catalog and return the
 
337
        corresponding message string, as an 8-bit string encoded with the
 
338
        catalog's charset encoding, if known.  If there is no entry in the
 
339
        catalog for the `message` id and `context` , and a fallback has been
 
340
        set, the look up is forwarded to the fallback's ``pgettext()``
 
341
        method. Otherwise, the `message` id is returned.
 
342
        """
 
343
        ctxt_msg_id = self.CONTEXT_ENCODING % (context, message)
 
344
        missing = object()
 
345
        tmsg = self._catalog.get(ctxt_msg_id, missing)
 
346
        if tmsg is missing:
 
347
            if self._fallback:
 
348
                return self._fallback.pgettext(context, message)
 
349
            return message
 
350
        # Encode the Unicode tmsg back to an 8-bit string, if possible
 
351
        if self._output_charset:
 
352
            return text_to_native(tmsg, self._output_charset)
 
353
        elif self._charset:
 
354
            return text_to_native(tmsg, self._charset)
 
355
        return tmsg
 
356
 
 
357
    def lpgettext(self, context, message):
 
358
        """Equivalent to ``pgettext()``, but the translation is returned in the
 
359
        preferred system encoding, if no other encoding was explicitly set with
 
360
        ``bind_textdomain_codeset()``.
 
361
        """
 
362
        ctxt_msg_id = self.CONTEXT_ENCODING % (context, message)
 
363
        missing = object()
 
364
        tmsg = self._catalog.get(ctxt_msg_id, missing)
 
365
        if tmsg is missing:
 
366
            if self._fallback:
 
367
                return self._fallback.lpgettext(context, message)
 
368
            return message
 
369
        if self._output_charset:
 
370
            return tmsg.encode(self._output_charset)
 
371
        return tmsg.encode(locale.getpreferredencoding())
 
372
 
 
373
    def npgettext(self, context, singular, plural, num):
 
374
        """Do a plural-forms lookup of a message id.  `singular` is used as the
 
375
        message id for purposes of lookup in the catalog, while `num` is used to
 
376
        determine which plural form to use.  The returned message string is an
 
377
        8-bit string encoded with the catalog's charset encoding, if known.
 
378
 
 
379
        If the message id for `context` is not found in the catalog, and a
 
380
        fallback is specified, the request is forwarded to the fallback's
 
381
        ``npgettext()`` method.  Otherwise, when ``num`` is 1 ``singular`` is
 
382
        returned, and ``plural`` is returned in all other cases.
 
383
        """
 
384
        ctxt_msg_id = self.CONTEXT_ENCODING % (context, singular)
 
385
        try:
 
386
            tmsg = self._catalog[(ctxt_msg_id, self.plural(num))]
 
387
            if self._output_charset:
 
388
                return text_to_native(tmsg, self._output_charset)
 
389
            elif self._charset:
 
390
                return text_to_native(tmsg, self._charset)
 
391
            return tmsg
 
392
        except KeyError:
 
393
            if self._fallback:
 
394
                return self._fallback.npgettext(context, singular, plural, num)
 
395
            if num == 1:
 
396
                return singular
 
397
            else:
 
398
                return plural
 
399
 
 
400
    def lnpgettext(self, context, singular, plural, num):
 
401
        """Equivalent to ``npgettext()``, but the translation is returned in the
 
402
        preferred system encoding, if no other encoding was explicitly set with
 
403
        ``bind_textdomain_codeset()``.
 
404
        """
 
405
        ctxt_msg_id = self.CONTEXT_ENCODING % (context, singular)
 
406
        try:
 
407
            tmsg = self._catalog[(ctxt_msg_id, self.plural(num))]
 
408
            if self._output_charset:
 
409
                return tmsg.encode(self._output_charset)
 
410
            return tmsg.encode(locale.getpreferredencoding())
 
411
        except KeyError:
 
412
            if self._fallback:
 
413
                return self._fallback.lnpgettext(context, singular, plural, num)
 
414
            if num == 1:
 
415
                return singular
 
416
            else:
 
417
                return plural
 
418
 
 
419
    def upgettext(self, context, message):
 
420
        """Look up the `context` and `message` id in the catalog and return the
 
421
        corresponding message string, as a Unicode string.  If there is no entry
 
422
        in the catalog for the `message` id and `context`, and a fallback has
 
423
        been set, the look up is forwarded to the fallback's ``upgettext()``
 
424
        method.  Otherwise, the `message` id is returned.
 
425
        """
 
426
        ctxt_message_id = self.CONTEXT_ENCODING % (context, message)
 
427
        missing = object()
 
428
        tmsg = self._catalog.get(ctxt_message_id, missing)
 
429
        if tmsg is missing:
 
430
            if self._fallback:
 
431
                return self._fallback.upgettext(context, message)
 
432
            return text_type(message)
 
433
        return tmsg
 
434
 
 
435
    def unpgettext(self, context, singular, plural, num):
 
436
        """Do a plural-forms lookup of a message id.  `singular` is used as the
 
437
        message id for purposes of lookup in the catalog, while `num` is used to
 
438
        determine which plural form to use.  The returned message string is a
 
439
        Unicode string.
 
440
 
 
441
        If the message id for `context` is not found in the catalog, and a
 
442
        fallback is specified, the request is forwarded to the fallback's
 
443
        ``unpgettext()`` method.  Otherwise, when `num` is 1 `singular` is
 
444
        returned, and `plural` is returned in all other cases.
 
445
        """
 
446
        ctxt_message_id = self.CONTEXT_ENCODING % (context, singular)
 
447
        try:
 
448
            tmsg = self._catalog[(ctxt_message_id, self.plural(num))]
 
449
        except KeyError:
 
450
            if self._fallback:
 
451
                return self._fallback.unpgettext(context, singular, plural, num)
 
452
            if num == 1:
 
453
                tmsg = text_type(singular)
 
454
            else:
 
455
                tmsg = text_type(plural)
 
456
        return tmsg
 
457
 
 
458
    def dpgettext(self, domain, context, message):
 
459
        """Like `pgettext()`, but look the message up in the specified
 
460
        `domain`.
 
461
        """
 
462
        return self._domains.get(domain, self).pgettext(context, message)
 
463
 
 
464
    def udpgettext(self, domain, context, message):
 
465
        """Like `upgettext()`, but look the message up in the specified
 
466
        `domain`.
 
467
        """
 
468
        return self._domains.get(domain, self).upgettext(context, message)
 
469
    # backward compatibility with 0.9
 
470
    dupgettext = udpgettext
 
471
 
 
472
    def ldpgettext(self, domain, context, message):
 
473
        """Equivalent to ``dpgettext()``, but the translation is returned in the
 
474
        preferred system encoding, if no other encoding was explicitly set with
 
475
        ``bind_textdomain_codeset()``.
 
476
        """
 
477
        return self._domains.get(domain, self).lpgettext(context, message)
 
478
 
 
479
    def dnpgettext(self, domain, context, singular, plural, num):
 
480
        """Like ``npgettext``, but look the message up in the specified
 
481
        `domain`.
 
482
        """
 
483
        return self._domains.get(domain, self).npgettext(context, singular,
 
484
                                                         plural, num)
 
485
 
 
486
    def udnpgettext(self, domain, context, singular, plural, num):
 
487
        """Like ``unpgettext``, but look the message up in the specified
 
488
        `domain`.
 
489
        """
 
490
        return self._domains.get(domain, self).unpgettext(context, singular,
 
491
                                                          plural, num)
 
492
    # backward compatibility with 0.9
 
493
    dunpgettext = udnpgettext
 
494
 
 
495
    def ldnpgettext(self, domain, context, singular, plural, num):
 
496
        """Equivalent to ``dnpgettext()``, but the translation is returned in
 
497
        the preferred system encoding, if no other encoding was explicitly set
 
498
        with ``bind_textdomain_codeset()``.
 
499
        """
 
500
        return self._domains.get(domain, self).lnpgettext(context, singular,
 
501
                                                          plural, num)
 
502
 
 
503
    if not PY2:
 
504
        ugettext = gettext.NullTranslations.gettext
 
505
        ungettext = gettext.NullTranslations.ngettext
 
506
 
 
507
 
 
508
class Translations(NullTranslations, gettext.GNUTranslations):
268
509
    """An extended translation catalog class."""
269
510
 
270
511
    DEFAULT_DOMAIN = 'messages'
271
512
 
272
 
    def __init__(self, fileobj=None, domain=DEFAULT_DOMAIN):
 
513
    def __init__(self, fp=None, domain=None):
273
514
        """Initialize the translations catalog.
274
515
 
275
 
        :param fileobj: the file-like object the translation should be read
276
 
                        from
 
516
        :param fp: the file-like object the translation should be read from
 
517
        :param domain: the message domain (default: 'messages')
277
518
        """
278
 
        gettext.GNUTranslations.__init__(self, fp=fileobj)
279
 
        self.files = filter(None, [getattr(fileobj, 'name', None)])
280
 
        self.domain = domain
281
 
        self._domains = {}
282
 
 
283
 
    def load(cls, dirname=None, locales=None, domain=DEFAULT_DOMAIN):
 
519
        super(Translations, self).__init__(fp=fp)
 
520
        self.domain = domain or self.DEFAULT_DOMAIN
 
521
 
 
522
    if not PY2:
 
523
        ugettext = gettext.GNUTranslations.gettext
 
524
        ungettext = gettext.GNUTranslations.ngettext
 
525
 
 
526
    @classmethod
 
527
    def load(cls, dirname=None, locales=None, domain=None):
284
528
        """Load translations from the given directory.
285
529
 
286
530
        :param dirname: the directory containing the ``MO`` files
287
531
        :param locales: the list of locales in order of preference (items in
288
532
                        this list can be either `Locale` objects or locale
289
533
                        strings)
290
 
        :param domain: the message domain
291
 
        :return: the loaded catalog, or a ``NullTranslations`` instance if no
292
 
                 matching translations were found
293
 
        :rtype: `Translations`
 
534
        :param domain: the message domain (default: 'messages')
294
535
        """
295
536
        if locales is not None:
296
537
            if not isinstance(locales, (list, tuple)):
300
541
            domain = cls.DEFAULT_DOMAIN
301
542
        filename = gettext.find(domain, dirname, locales)
302
543
        if not filename:
303
 
            return gettext.NullTranslations()
304
 
        return cls(fileobj=open(filename, 'rb'), domain=domain)
305
 
    load = classmethod(load)
 
544
            return NullTranslations()
 
545
        with open(filename, 'rb') as fp:
 
546
            return cls(fp=fp, domain=domain)
306
547
 
307
548
    def __repr__(self):
308
549
        return '<%s: "%s">' % (type(self).__name__,
320
561
        :param merge: whether translations for message domains that have
321
562
                      already been added should be merged with the existing
322
563
                      translations
323
 
        :return: the `Translations` instance (``self``) so that `merge` calls
324
 
                 can be easily chained
325
 
        :rtype: `Translations`
326
564
        """
327
565
        domain = getattr(translations, 'domain', self.DEFAULT_DOMAIN)
328
566
        if merge and domain == self.domain:
345
583
 
346
584
        :param translations: the `Translations` instance with the messages to
347
585
                             merge
348
 
        :return: the `Translations` instance (``self``) so that `merge` calls
349
 
                 can be easily chained
350
 
        :rtype: `Translations`
351
586
        """
352
587
        if isinstance(translations, gettext.GNUTranslations):
353
588
            self._catalog.update(translations._catalog)
355
590
                self.files.extend(translations.files)
356
591
 
357
592
        return self
358
 
 
359
 
    def dgettext(self, domain, message):
360
 
        """Like ``gettext()``, but look the message up in the specified
361
 
        domain.
362
 
        """
363
 
        return self._domains.get(domain, self).gettext(message)
364
 
    
365
 
    def ldgettext(self, domain, message):
366
 
        """Like ``lgettext()``, but look the message up in the specified 
367
 
        domain.
368
 
        """ 
369
 
        return self._domains.get(domain, self).lgettext(message)
370
 
    
371
 
    def dugettext(self, domain, message):
372
 
        """Like ``ugettext()``, but look the message up in the specified
373
 
        domain.
374
 
        """
375
 
        return self._domains.get(domain, self).ugettext(message)
376
 
    
377
 
    def dngettext(self, domain, singular, plural, num):
378
 
        """Like ``ngettext()``, but look the message up in the specified
379
 
        domain.
380
 
        """
381
 
        return self._domains.get(domain, self).ngettext(singular, plural, num)
382
 
    
383
 
    def ldngettext(self, domain, singular, plural, num):
384
 
        """Like ``lngettext()``, but look the message up in the specified
385
 
        domain.
386
 
        """
387
 
        return self._domains.get(domain, self).lngettext(singular, plural, num)
388
 
    
389
 
    def dungettext(self, domain, singular, plural, num):
390
 
        """Like ``ungettext()`` but look the message up in the specified
391
 
        domain.
392
 
        """
393
 
        return self._domains.get(domain, self).ungettext(singular, plural, num)