mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-01-26 16:16:31 -06:00
Finish deferred translation mechanism
This commit is contained in:
parent
a0a94a9a04
commit
c350f84134
@ -881,7 +881,7 @@ from crud import Create, Retrieve, Update, Delete, Search
|
||||
from parameters import DefaultFrom, Bool, Flag, Int, Float, Bytes, Str, Password,List
|
||||
from parameters import BytesEnum, StrEnum, AccessTime, File
|
||||
from errors import SkipPluginModule
|
||||
from text import _, gettext, ngettext
|
||||
from text import _, ngettext, GettextFactory, NGettextFactory
|
||||
|
||||
# We can't import the python uuid since it includes ctypes which makes
|
||||
# httpd throw up when run in in mod_python due to SELinux issues
|
||||
|
@ -73,14 +73,14 @@ web-UI. The *label* should start with an initial capital. For example:
|
||||
... label=_('Last name'),
|
||||
... )
|
||||
>>> sn.label
|
||||
Gettext('Last name')
|
||||
Gettext('Last name', domain='ipa', localedir=None)
|
||||
|
||||
The *doc* is a longer description of the parameter. It's used on the CLI when
|
||||
displaying the help information for a command, and as extra instruction for a
|
||||
form input on the web-UI. By default the *doc* is the same as the *label*:
|
||||
|
||||
>>> sn.doc
|
||||
Gettext('Last name')
|
||||
Gettext('Last name', domain='ipa', localedir=None)
|
||||
|
||||
But you can override this with the *doc* kwarg. Like the *label*, the *doc*
|
||||
should also start with an initial capital and should not end with any
|
||||
@ -92,7 +92,7 @@ punctuation. For example:
|
||||
... doc=_("The user's last name"),
|
||||
... )
|
||||
>>> sn.doc
|
||||
Gettext("The user's last name")
|
||||
Gettext("The user's last name", domain='ipa', localedir=None)
|
||||
|
||||
Demonstration aside, you should always provide at least the *label* so the
|
||||
various UIs are translatable. Only provide the *doc* if the parameter needs
|
||||
|
@ -52,10 +52,11 @@ def destroy_context():
|
||||
"""
|
||||
Delete all attributes on thread-local `request.context`.
|
||||
"""
|
||||
# need to use .items(), 'cos value.disconnect modifies the dict
|
||||
for (name, value) in context.__dict__.items():
|
||||
# need to use .values(), 'cos value.disconnect modifies the dict
|
||||
for value in context.__dict__.values():
|
||||
if isinstance(value, Connection):
|
||||
value.disconnect()
|
||||
context.__dict__.clear()
|
||||
|
||||
|
||||
def ugettext(message):
|
||||
|
455
ipalib/text.py
455
ipalib/text.py
@ -18,73 +18,480 @@
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
"""
|
||||
Thread-local lazy gettext service.
|
||||
Defers gettext translation till request time.
|
||||
|
||||
TODO: This aren't hooked up into gettext yet, they currently just provide
|
||||
placeholders for the rest of the code.
|
||||
IPA presents some tricky gettext challenges. On the one hand, most translatable
|
||||
message are defined as class attributes on the plugins, which means these get
|
||||
evaluated at module-load time. But on the other hand, each request to the
|
||||
server can be in a different locale, so the actual translation must not occur
|
||||
till request time.
|
||||
|
||||
The `text` module provides a mechanism for for deferred gettext translation. It
|
||||
was designed to:
|
||||
|
||||
1. Allow translatable strings to be marked with the usual ``_()`` and
|
||||
``ngettext()`` functions so that standard tools like xgettext can still
|
||||
be used
|
||||
|
||||
2. Allow programmers to mark strings in a natural way without burdening them
|
||||
with details of the deferred translation mechanism
|
||||
|
||||
A typical plugin will use the deferred translation like this:
|
||||
|
||||
>>> from ipalib import Command, _, ngettext
|
||||
>>> class my_plugin(Command):
|
||||
... my_string = _('Hello, %(name)s.')
|
||||
... my_plural = ngettext('%(count)d goose', '%(count)d geese', 0)
|
||||
...
|
||||
|
||||
With normal gettext usage, the *my_string* and *my_plural* message would be
|
||||
translated at module-load-time when your ``my_plugin`` class is defined. This
|
||||
would mean that all message are translated in the locale of the server rather
|
||||
than the locale of the request.
|
||||
|
||||
However, the ``_()`` function above is actually a `GettextFactory` instance,
|
||||
which when called returns a `Gettext` instance. A `Gettext` instance stores the
|
||||
message to be translated, and the gettext domain and localedir, but it doesn't
|
||||
perform the translation till `Gettext.__unicode__()` is called. For example:
|
||||
|
||||
>>> my_plugin.my_string
|
||||
Gettext('Hello, %(name)s.', domain='ipa', localedir=None)
|
||||
>>> unicode(my_plugin.my_string)
|
||||
u'Hello, %(name)s.'
|
||||
|
||||
Translation can also be performed via the `Gettext.__mod__()` convenience
|
||||
method. For example, these two are equivalent:
|
||||
|
||||
>>> my_plugin.my_string % dict(name='Joe')
|
||||
u'Hello, Joe.'
|
||||
>>> unicode(my_plugin.my_string) % dict(name='Joe') # Long form
|
||||
u'Hello, Joe.'
|
||||
|
||||
Similar to ``_()``, the ``ngettext()`` function above is actually an
|
||||
`NGettextFactory` instance, which when called returns an `NGettext` instance.
|
||||
An `NGettext` instance stores the singular and plural messages, and the gettext
|
||||
domain and localedir, but it doesn't perform the translation till
|
||||
`NGettext.__call__()` is called. For example:
|
||||
|
||||
>>> my_plugin.my_plural
|
||||
NGettext('%(count)d goose', '%(count)d geese', domain='ipa', localedir=None)
|
||||
>>> my_plugin.my_plural(1)
|
||||
u'%(count)d goose'
|
||||
>>> my_plugin.my_plural(2)
|
||||
u'%(count)d geese'
|
||||
|
||||
Translation can also be performed via the `NGettext.__mod__()` convenience
|
||||
method. For example, these two are equivalent:
|
||||
|
||||
>>> my_plugin.my_plural % dict(count=1)
|
||||
u'1 goose'
|
||||
>>> my_plugin.my_plural(1) % dict(count=1) # Long form
|
||||
u'1 goose'
|
||||
|
||||
Lastly, 3rd-party plugins can create factories bound to a different gettext
|
||||
domain. The default domain is ``'ipa'``, which is also the domain of the
|
||||
standard ``ipalib._()`` and ``ipalib.ngettext()`` factories. But 3rd-party
|
||||
plugins can create their own factories like this:
|
||||
|
||||
>>> from ipalib import GettextFactory, NGettextFactory
|
||||
>>> _ = GettextFactory(domain='ipa_foo')
|
||||
>>> ngettext = NGettextFactory(domain='ipa_foo')
|
||||
>>> class foo(Command):
|
||||
... msg1 = _('Foo!')
|
||||
... msg2 = ngettext('%(count)d bar', '%(count)d bars', 0)
|
||||
...
|
||||
|
||||
Notice that these messages are bound to the ``'ipa_foo'`` domain:
|
||||
|
||||
>>> foo.msg1
|
||||
Gettext('Foo!', domain='ipa_foo', localedir=None)
|
||||
>>> foo.msg2
|
||||
NGettext('%(count)d bar', '%(count)d bars', domain='ipa_foo', localedir=None)
|
||||
|
||||
For additional details, see `GettextFactory` and `Gettext`, and for plural
|
||||
forms, see `NGettextFactory` and `NGettext`.
|
||||
"""
|
||||
|
||||
import threading
|
||||
import locale
|
||||
import gettext
|
||||
from request import context
|
||||
|
||||
|
||||
def create_translation(key):
|
||||
assert key not in context.__dict__
|
||||
(domain, localedir) = key
|
||||
translation = gettext.translation(domain,
|
||||
localedir=localedir,
|
||||
languages=getattr(context, 'languages', None),
|
||||
fallback=True,
|
||||
)
|
||||
context.__dict__[key] = translation
|
||||
return translation
|
||||
|
||||
|
||||
class LazyText(object):
|
||||
"""
|
||||
Base class for deferred translation.
|
||||
|
||||
This class is not used directly. See the `Gettext` and `NGettext`
|
||||
subclasses.
|
||||
"""
|
||||
|
||||
__slots__ = ('domain', 'localedir', 'key')
|
||||
|
||||
def __init__(self, domain=None, localedir=None):
|
||||
"""
|
||||
Initialize.
|
||||
|
||||
:param domain: The gettext domain in which this message will be
|
||||
translated, e.g. ``'ipa'`` or ``'ipa_3rd_party'``; default is
|
||||
``None``
|
||||
:param localedir: The directory containing the gettext translations,
|
||||
e.g. ``'/usr/share/locale/'``; default is ``None``, in which case
|
||||
gettext will use the default system locale directory.
|
||||
"""
|
||||
self.domain = domain
|
||||
self.localedir = localedir
|
||||
self.key = (domain, localedir)
|
||||
|
||||
def __eq__(self, other):
|
||||
"""
|
||||
Return ``True`` if this instances is equal to *other*.
|
||||
|
||||
Note that this method cannot be used on the `LazyText` base class itself
|
||||
as subclasses must define an *args* instance attribute.
|
||||
"""
|
||||
if type(other) is not self.__class__:
|
||||
return False
|
||||
return self.args == other.args
|
||||
|
||||
def __ne__(self, other):
|
||||
"""
|
||||
Return ``True`` if this instances is not equal to *other*.
|
||||
|
||||
Note that this method cannot be used on the `LazyText` base class itself
|
||||
as subclasses must define an *args* instance attribute.
|
||||
"""
|
||||
return not self.__eq__(other)
|
||||
|
||||
|
||||
class Gettext(LazyText):
|
||||
"""
|
||||
Deferred translation using ``gettext.ugettext()``.
|
||||
|
||||
Normally the `Gettext` class isn't used directly and instead is created via
|
||||
a `GettextFactory` instance. However, for illustration, we can create one
|
||||
like this:
|
||||
|
||||
>>> msg = Gettext('Hello, %(name)s.')
|
||||
|
||||
When you create a `Gettext` instance, the message is stored on the *msg*
|
||||
attribute:
|
||||
|
||||
>>> msg.msg
|
||||
'Hello, %(name)s.'
|
||||
|
||||
No translation is performed till `Gettext.__unicode__()` is called. This
|
||||
will translate *msg* using ``gettext.ugettext()``, which will return the
|
||||
translated string as a Python ``unicode`` instance. For example:
|
||||
|
||||
>>> unicode(msg)
|
||||
u'Hello, %(name)s.'
|
||||
|
||||
`Gettext.__unicode__()` should be called at request time, which in a
|
||||
nutshell means it should be called from within your plugin's
|
||||
``Command.execute()`` method. `Gettext.__unicode__()` will perform the
|
||||
translation based on the locale of the current request.
|
||||
|
||||
`Gettext.__mod__()` is a convenience method for Python "percent" string
|
||||
formatting. It will translate your message using `Gettext.__unicode__()`
|
||||
and then perform the string substitution on the translated message. For
|
||||
example, these two are equivalent:
|
||||
|
||||
>>> msg % dict(name='Joe')
|
||||
u'Hello, Joe.'
|
||||
>>> unicode(msg) % dict(name='Joe') # Long form
|
||||
u'Hello, Joe.'
|
||||
|
||||
See `GettextFactory` for additional details. If you need to pick between
|
||||
singular and plural form, use `NGettext` instances via the
|
||||
`NGettextFactory`.
|
||||
"""
|
||||
|
||||
__slots__ = ('msg', 'args')
|
||||
|
||||
def __init__(self, msg, domain=None, localedir=None):
|
||||
super(Gettext, self).__init__(domain, localedir)
|
||||
self.msg = msg
|
||||
self.args = (msg, domain, localedir)
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%r, domain=%r, localedir=%r)' % (self.__class__.__name__,
|
||||
self.msg, self.domain, self.localedir)
|
||||
|
||||
def __unicode__(self):
|
||||
"""
|
||||
Translate this message and return as a ``unicode`` instance.
|
||||
"""
|
||||
if self.key in context.__dict__:
|
||||
g = context.__dict__[self.key].ugettext
|
||||
else:
|
||||
g = create_translation(self.key).ugettext
|
||||
return g(self.msg)
|
||||
|
||||
def __json__(self):
|
||||
return self.__unicode__()
|
||||
|
||||
def __mod__(self, kw):
|
||||
return self.__unicode__() % kw
|
||||
|
||||
|
||||
class Gettext(LazyText):
|
||||
def __init__(self, msg, domain=None, localedir=None):
|
||||
self.msg = msg
|
||||
super(Gettext, self).__init__(domain, localedir)
|
||||
class FixMe(Gettext):
|
||||
"""
|
||||
Non-translated place-holder for UI labels.
|
||||
|
||||
def __unicode__(self):
|
||||
return self.msg.decode('utf-8')
|
||||
`FixMe` is a subclass of `Gettext` and is used for automatically created
|
||||
place-holder labels. It generally behaves exactly like `Gettext` except no
|
||||
translation is ever performed.
|
||||
|
||||
`FixMe` allows programmers to get plugins working without first filling in
|
||||
all the labels that will ultimately be required, while at the same time it
|
||||
creates conspicuous looking UI labels that remind the programmer to
|
||||
"fix-me!". For example, the typical usage would be something like this:
|
||||
|
||||
>>> class Plugin(object):
|
||||
... label = None
|
||||
... def __init__(self):
|
||||
... self.name = self.__class__.__name__
|
||||
... if self.label is None:
|
||||
... self.label = FixMe(self.name + '.label')
|
||||
... assert isinstance(self.label, Gettext)
|
||||
...
|
||||
>>> class user(Plugin):
|
||||
... pass # Oops, we didn't set user.label yet
|
||||
...
|
||||
>>> u = user()
|
||||
>>> u.label
|
||||
FixMe('user.label')
|
||||
|
||||
Note that as `FixMe` is a subclass of `Gettext`, is passes the above type
|
||||
check using ``isinstance()``.
|
||||
|
||||
Calling `FixMe.__unicode__()` performs no translation, but instead returns
|
||||
said conspicuous looking label:
|
||||
|
||||
>>> unicode(u.label)
|
||||
u'<user.label>'
|
||||
|
||||
For more examples of how `FixMe` is used, see `ipalib.parameters`.
|
||||
"""
|
||||
|
||||
__slots__ = tuple()
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%r)' % (self.__class__.__name__, self.msg)
|
||||
|
||||
def __json__(self):
|
||||
return self.__unicode__()
|
||||
|
||||
|
||||
class FixMe(Gettext):
|
||||
def __unicode__(self):
|
||||
return u'<%s>' % self.msg
|
||||
|
||||
|
||||
class NGettext(LazyText):
|
||||
def __init__(self, singular, plural, domain, localedir):
|
||||
"""
|
||||
Deferred translation for plural forms using ``gettext.ungettext()``.
|
||||
|
||||
Normally the `NGettext` class isn't used directly and instead is created via
|
||||
a `NGettextFactory` instance. However, for illustration, we can create one
|
||||
like this:
|
||||
|
||||
>>> msg = NGettext('%(count)d goose', '%(count)d geese')
|
||||
|
||||
When you create an `NGettext` instance, the singular and plural forms of
|
||||
your message are stored on the *singular* and *plural* instance attributes:
|
||||
|
||||
>>> msg.singular
|
||||
'%(count)d goose'
|
||||
>>> msg.plural
|
||||
'%(count)d geese'
|
||||
|
||||
The translation and number selection isn't performed till
|
||||
`NGettext.__call__()` is called. This will translate and pick the correct
|
||||
number using ``gettext.ungettext()``. As a callable, an `NGettext` instance
|
||||
takes a single argument, an integer specifying the count. For example:
|
||||
|
||||
>>> msg(0)
|
||||
u'%(count)d geese'
|
||||
>>> msg(1)
|
||||
u'%(count)d goose'
|
||||
>>> msg(2)
|
||||
u'%(count)d geese'
|
||||
|
||||
`NGettext.__mod__()` is a convenience method for Python "percent" string
|
||||
formatting. It can only be used if your substitution ``dict`` contains the
|
||||
count in a ``'count'`` item. For example:
|
||||
|
||||
>>> msg % dict(count=0)
|
||||
u'0 geese'
|
||||
>>> msg % dict(count=1)
|
||||
u'1 goose'
|
||||
>>> msg % dict(count=2)
|
||||
u'2 geese'
|
||||
|
||||
Alternatively, these longer forms have the same effect as the three examples
|
||||
above:
|
||||
|
||||
>>> msg(0) % dict(count=0)
|
||||
u'0 geese'
|
||||
>>> msg(1) % dict(count=1)
|
||||
u'1 goose'
|
||||
>>> msg(2) % dict(count=2)
|
||||
u'2 geese'
|
||||
|
||||
A ``KeyError`` is raised if your substitution ``dict`` doesn't have a
|
||||
``'count'`` item. For example:
|
||||
|
||||
>>> msg2 = NGettext('%(num)d goose', '%(num)d geese')
|
||||
>>> msg2 % dict(num=0)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
KeyError: 'count'
|
||||
|
||||
However, in this case you can still use the longer, explicit form for string
|
||||
substitution:
|
||||
|
||||
>>> msg2(0) % dict(num=0)
|
||||
u'0 geese'
|
||||
|
||||
See `NGettextFactory` for additional details.
|
||||
"""
|
||||
|
||||
__slots__ = ('singular', 'plural', 'args')
|
||||
|
||||
def __init__(self, singular, plural, domain=None, localedir=None):
|
||||
super(NGettext, self).__init__(domain, localedir)
|
||||
self.singular = singular
|
||||
self.plural = plural
|
||||
super(NGettext, self).__init__(domain, localedir)
|
||||
self.args = (singular, plural, domain, localedir)
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%r, %r, domain=%r, localedir=%r)' % (self.__class__.__name__,
|
||||
self.singular, self.plural, self.domain, self.localedir)
|
||||
|
||||
def __mod__(self, kw):
|
||||
count = kw['count']
|
||||
return self(count) % kw
|
||||
|
||||
def __call__(self, count):
|
||||
if count == 1:
|
||||
return self.singular.decode('utf-8')
|
||||
return self.plural.decode('utf-8')
|
||||
if self.key in context.__dict__:
|
||||
ng = context.__dict__[self.key].ungettext
|
||||
else:
|
||||
ng = create_translation(self.key).ungettext
|
||||
return ng(self.singular, self.plural, count)
|
||||
|
||||
|
||||
class gettext_factory(object):
|
||||
class GettextFactory(object):
|
||||
"""
|
||||
Factory for creating ``_()`` functions.
|
||||
|
||||
A `GettextFactory` allows you to mark translatable messages that are
|
||||
evaluated at initialization time, but deferred their actual translation till
|
||||
request time.
|
||||
|
||||
When you create a `GettextFactory` you can provide a specific gettext
|
||||
*domain* and *localedir*. By default the *domain* will be ``'ipa'`` and
|
||||
the *localedir* will be ``None``. Both are available via instance
|
||||
attributes of the same name. For example:
|
||||
|
||||
>>> _ = GettextFactory()
|
||||
>>> _.domain
|
||||
'ipa'
|
||||
>>> _.localedir is None
|
||||
True
|
||||
|
||||
When the *localedir* is ``None``, gettext will use the default system
|
||||
localedir (typically ``'/usr/share/locale/'``). In general, you should
|
||||
**not** provide a *localedir*... it is intended only to support in-tree
|
||||
testing.
|
||||
|
||||
Third party plugins will most likely want to use a different gettext
|
||||
*domain*. For example:
|
||||
|
||||
>>> _ = GettextFactory(domain='ipa_3rd_party')
|
||||
>>> _.domain
|
||||
'ipa_3rd_party'
|
||||
|
||||
When you call your `GettextFactory` instance, it will return a `Gettext`
|
||||
instance associated with the same *domain* and *localedir*. For example:
|
||||
|
||||
>>> my_msg = _('Hello world')
|
||||
>>> my_msg.domain
|
||||
'ipa_3rd_party'
|
||||
>>> my_msg.localedir is None
|
||||
True
|
||||
|
||||
The message isn't translated till `Gettext.__unicode__()` is called, which
|
||||
should be done during each request. See the `Gettext` class for additional
|
||||
details.
|
||||
"""
|
||||
|
||||
def __init__(self, domain='ipa', localedir=None):
|
||||
"""
|
||||
Initialize.
|
||||
|
||||
:param domain: The gettext domain in which this message will be
|
||||
translated, e.g. ``'ipa'`` or ``'ipa_3rd_party'``; default is
|
||||
``'ipa'``
|
||||
:param localedir: The directory containing the gettext translations,
|
||||
e.g. ``'/usr/share/locale/'``; default is ``None``, in which case
|
||||
gettext will use the default system locale directory.
|
||||
"""
|
||||
self.domain = domain
|
||||
self.localedir = localedir
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(domain=%r, localedir=%r)' % (self.__class__.__name__,
|
||||
self.domain, self.localedir)
|
||||
|
||||
def __call__(self, msg):
|
||||
return Gettext(msg, self.domain, self.localedir)
|
||||
|
||||
|
||||
class ngettext_factory(gettext_factory):
|
||||
class NGettextFactory(GettextFactory):
|
||||
"""
|
||||
Factory for creating ``ngettext()`` functions.
|
||||
|
||||
`NGettextFactory` is similar to `GettextFactory`, except `NGettextFactory`
|
||||
is for plural forms.
|
||||
|
||||
So that standard tools like xgettext can find your plural forms, you should
|
||||
reference your `NGettextFactory` instance using a variable named
|
||||
*ngettext*. For example:
|
||||
|
||||
>>> ngettext = NGettextFactory()
|
||||
>>> ngettext
|
||||
NGettextFactory(domain='ipa', localedir=None)
|
||||
|
||||
When you call your `NGettextFactory` instance to create a deferred
|
||||
translation, you provide the *singular* message, the *plural* message, and
|
||||
a dummy *count*. An `NGettext` instance will be returned. For example:
|
||||
|
||||
>>> my_msg = ngettext('%(count)d goose', '%(count)d geese', 0)
|
||||
>>> my_msg
|
||||
NGettext('%(count)d goose', '%(count)d geese', domain='ipa', localedir=None)
|
||||
|
||||
The *count* is ignored (because the translation is deferred), but you should
|
||||
still provide it so parsing tools aren't confused. For consistency, it is
|
||||
recommended to always provide ``0`` for the *count*.
|
||||
|
||||
See `NGettext` for details on how the deferred translation is later
|
||||
performed. See `GettextFactory` for details on setting a different gettext
|
||||
*domain* (likely needed for 3rd-party plugins).
|
||||
"""
|
||||
|
||||
def __call__(self, singular, plural, count=0):
|
||||
return NGettext(singular, plural, self.domain, self.localedir)
|
||||
|
||||
|
||||
# Process wide factories:
|
||||
gettext = gettext_factory()
|
||||
_ = gettext
|
||||
ngettext = ngettext_factory()
|
||||
_ = GettextFactory()
|
||||
ngettext = NGettextFactory()
|
||||
|
@ -22,13 +22,20 @@ Test the `ipalib.text` module.
|
||||
"""
|
||||
|
||||
from tests.util import raises, assert_equal
|
||||
from tests.data import utf8_bytes, unicode_str
|
||||
from ipalib.request import context
|
||||
from ipalib import text
|
||||
|
||||
singular = '%(count)d goose makes a %(dish)s'
|
||||
plural = '%(count)d geese make a %(dish)s'
|
||||
|
||||
|
||||
def test_create_translation():
|
||||
f = text.create_translation
|
||||
key = ('foo', None)
|
||||
t = f(key)
|
||||
assert context.__dict__[key] is t
|
||||
|
||||
|
||||
class test_LazyText(object):
|
||||
|
||||
klass = text.LazyText
|
||||
@ -37,6 +44,7 @@ class test_LazyText(object):
|
||||
inst = self.klass('foo', 'bar')
|
||||
assert inst.domain == 'foo'
|
||||
assert inst.localedir == 'bar'
|
||||
assert inst.key == ('foo', 'bar')
|
||||
|
||||
|
||||
class test_FixMe(object):
|
||||
@ -63,19 +71,56 @@ class test_Gettext(object):
|
||||
klass = text.Gettext
|
||||
|
||||
def test_init(self):
|
||||
inst = self.klass(utf8_bytes, 'foo', 'bar')
|
||||
inst = self.klass('what up?', 'foo', 'bar')
|
||||
assert inst.domain == 'foo'
|
||||
assert inst.localedir == 'bar'
|
||||
assert inst.msg is utf8_bytes
|
||||
assert inst.msg is 'what up?'
|
||||
assert inst.args == ('what up?', 'foo', 'bar')
|
||||
|
||||
def test_repr(self):
|
||||
inst = self.klass('foo', 'bar', 'baz')
|
||||
assert repr(inst) == "Gettext('foo', domain='bar', localedir='baz')"
|
||||
|
||||
def test_unicode(self):
|
||||
inst = self.klass(utf8_bytes, 'foo', 'bar')
|
||||
assert unicode(inst) == unicode_str
|
||||
inst = self.klass('what up?', 'foo', 'bar')
|
||||
assert unicode(inst) == u'what up?'
|
||||
|
||||
def test_mod(self):
|
||||
inst = self.klass('hello %(adj)s nurse', 'foo', 'bar')
|
||||
assert inst % dict(adj='naughty', stuff='junk') == 'hello naughty nurse'
|
||||
|
||||
def test_eq(self):
|
||||
inst1 = self.klass('what up?', 'foo', 'bar')
|
||||
inst2 = self.klass('what up?', 'foo', 'bar')
|
||||
inst3 = self.klass('Hello world', 'foo', 'bar')
|
||||
inst4 = self.klass('what up?', 'foo', 'baz')
|
||||
|
||||
assert (inst1 == inst1) is True
|
||||
assert (inst1 == inst2) is True
|
||||
assert (inst1 == inst3) is False
|
||||
assert (inst1 == inst4) is False
|
||||
|
||||
# Test with args flipped
|
||||
assert (inst2 == inst1) is True
|
||||
assert (inst3 == inst1) is False
|
||||
assert (inst4 == inst1) is False
|
||||
|
||||
def test_ne(self):
|
||||
inst1 = self.klass('what up?', 'foo', 'bar')
|
||||
inst2 = self.klass('what up?', 'foo', 'bar')
|
||||
inst3 = self.klass('Hello world', 'foo', 'bar')
|
||||
inst4 = self.klass('what up?', 'foo', 'baz')
|
||||
|
||||
assert (inst1 != inst2) is False
|
||||
assert (inst1 != inst2) is False
|
||||
assert (inst1 != inst3) is True
|
||||
assert (inst1 != inst4) is True
|
||||
|
||||
# Test with args flipped
|
||||
assert (inst2 != inst1) is False
|
||||
assert (inst3 != inst1) is True
|
||||
assert (inst4 != inst1) is True
|
||||
|
||||
|
||||
class test_NGettext(object):
|
||||
|
||||
@ -87,6 +132,12 @@ class test_NGettext(object):
|
||||
assert inst.plural is plural
|
||||
assert inst.domain == 'foo'
|
||||
assert inst.localedir == 'bar'
|
||||
assert inst.args == (singular, plural, 'foo', 'bar')
|
||||
|
||||
def test_repr(self):
|
||||
inst = self.klass('sig', 'plu', 'foo', 'bar')
|
||||
assert repr(inst) == \
|
||||
"NGettext('sig', 'plu', domain='foo', localedir='bar')"
|
||||
|
||||
def test_call(self):
|
||||
inst = self.klass(singular, plural, 'foo', 'bar')
|
||||
@ -101,34 +152,96 @@ class test_NGettext(object):
|
||||
assert inst % dict(count=1, dish='stew') == '1 goose makes a stew'
|
||||
assert inst % dict(count=2, dish='pie') == '2 geese make a pie'
|
||||
|
||||
def test_eq(self):
|
||||
inst1 = self.klass(singular, plural, 'foo', 'bar')
|
||||
inst2 = self.klass(singular, plural, 'foo', 'bar')
|
||||
inst3 = self.klass(singular, '%(count)d thingies', 'foo', 'bar')
|
||||
inst4 = self.klass(singular, plural, 'foo', 'baz')
|
||||
|
||||
class test_gettext_factory(object):
|
||||
assert (inst1 == inst1) is True
|
||||
assert (inst1 == inst2) is True
|
||||
assert (inst1 == inst3) is False
|
||||
assert (inst1 == inst4) is False
|
||||
|
||||
klass = text.gettext_factory
|
||||
# Test with args flipped
|
||||
assert (inst2 == inst1) is True
|
||||
assert (inst3 == inst1) is False
|
||||
assert (inst4 == inst1) is False
|
||||
|
||||
def test_ne(self):
|
||||
inst1 = self.klass(singular, plural, 'foo', 'bar')
|
||||
inst2 = self.klass(singular, plural, 'foo', 'bar')
|
||||
inst3 = self.klass(singular, '%(count)d thingies', 'foo', 'bar')
|
||||
inst4 = self.klass(singular, plural, 'foo', 'baz')
|
||||
|
||||
assert (inst1 != inst2) is False
|
||||
assert (inst1 != inst2) is False
|
||||
assert (inst1 != inst3) is True
|
||||
assert (inst1 != inst4) is True
|
||||
|
||||
# Test with args flipped
|
||||
assert (inst2 != inst1) is False
|
||||
assert (inst3 != inst1) is True
|
||||
assert (inst4 != inst1) is True
|
||||
|
||||
|
||||
class test_GettextFactory(object):
|
||||
|
||||
klass = text.GettextFactory
|
||||
|
||||
def test_init(self):
|
||||
# Test with defaults:
|
||||
inst = self.klass()
|
||||
assert inst.domain == 'ipa'
|
||||
assert inst.localedir is None
|
||||
|
||||
# Test with overrides:
|
||||
inst = self.klass('foo', 'bar')
|
||||
assert inst.domain == 'foo'
|
||||
assert inst.localedir == 'bar'
|
||||
|
||||
def test_repr(self):
|
||||
# Test with defaults:
|
||||
inst = self.klass()
|
||||
assert repr(inst) == "GettextFactory(domain='ipa', localedir=None)"
|
||||
|
||||
# Test with overrides:
|
||||
inst = self.klass('foo', 'bar')
|
||||
assert repr(inst) == "GettextFactory(domain='foo', localedir='bar')"
|
||||
|
||||
def test_call(self):
|
||||
inst = self.klass('foo', 'bar')
|
||||
g = inst(utf8_bytes)
|
||||
g = inst('what up?')
|
||||
assert type(g) is text.Gettext
|
||||
assert g.msg is utf8_bytes
|
||||
assert g.msg is 'what up?'
|
||||
assert g.domain == 'foo'
|
||||
assert g.localedir == 'bar'
|
||||
|
||||
|
||||
class test_ngettext_factory(object):
|
||||
class test_NGettextFactory(object):
|
||||
|
||||
klass = text.ngettext_factory
|
||||
klass = text.NGettextFactory
|
||||
|
||||
def test_init(self):
|
||||
# Test with defaults:
|
||||
inst = self.klass()
|
||||
assert inst.domain == 'ipa'
|
||||
assert inst.localedir is None
|
||||
|
||||
# Test with overrides:
|
||||
inst = self.klass('foo', 'bar')
|
||||
assert inst.domain == 'foo'
|
||||
assert inst.localedir == 'bar'
|
||||
|
||||
def test_repr(self):
|
||||
# Test with defaults:
|
||||
inst = self.klass()
|
||||
assert repr(inst) == "NGettextFactory(domain='ipa', localedir=None)"
|
||||
|
||||
# Test with overrides:
|
||||
inst = self.klass('foo', 'bar')
|
||||
assert repr(inst) == "NGettextFactory(domain='foo', localedir='bar')"
|
||||
|
||||
def test_call(self):
|
||||
inst = self.klass('foo', 'bar')
|
||||
ng = inst(singular, plural, 7)
|
||||
|
@ -417,8 +417,7 @@ class ClassChecker(object):
|
||||
"""
|
||||
nose tear-down fixture.
|
||||
"""
|
||||
for name in context.__dict__.keys():
|
||||
delattr(context, name)
|
||||
context.__dict__.clear()
|
||||
|
||||
|
||||
|
||||
@ -509,8 +508,7 @@ class PluginTester(object):
|
||||
"""
|
||||
nose tear-down fixture.
|
||||
"""
|
||||
for name in context.__dict__.keys():
|
||||
delattr(context, name)
|
||||
context.__dict__.clear()
|
||||
|
||||
|
||||
class dummy_ugettext(object):
|
||||
|
Loading…
Reference in New Issue
Block a user