From c46a7bc3ce66a63f2bcea5468c684c0ef88f01ae Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Mon, 26 Apr 2010 01:06:29 +0000 Subject: [PATCH] i18n-ize colander. --- colander/__init__.py | 200 +++++++++++++++++++++++------------ colander/locale/colander.pot | 60 +++++++++++ colander/tests.py | 72 +++++++------ setup.cfg | 20 ++++ setup.py | 6 +- 5 files changed, 257 insertions(+), 101 deletions(-) create mode 100644 colander/locale/colander.pot diff --git a/colander/__init__.py b/colander/__init__.py index bfde2c8..590bf42 100644 --- a/colander/__init__.py +++ b/colander/__init__.py @@ -3,10 +3,20 @@ import itertools import iso8601 import pprint import re +import translationstring + +_ = translationstring.TranslationStringFactory('colander') class _missing(object): pass +def interpolate(msgs): + for s in msgs: + if hasattr(s, 'interpolate'): + yield s.interpolate() + else: + yield s + class Invalid(Exception): """ An exception raised by data types and validators indicating that @@ -113,7 +123,7 @@ class Invalid(Exception): exc.msg and msgs.append(exc.msg) keyname = exc._keyname() keyname and keyparts.append(keyname) - errors['.'.join(keyparts)] = '; '.join(msgs) + errors['.'.join(keyparts)] = '; '.join(interpolate(msgs)) return errors def __str__(self): @@ -164,7 +174,7 @@ class Function(object): The default value for the ``message`` when not provided via the constructor is ``Invalid value``. """ - def __init__(self, function, message='Invalid value'): + def __init__(self, function, message=_('Invalid value')): self.function = function self.message = message @@ -189,7 +199,7 @@ class Regex(object): def __init__(self, regex, msg=None): self.match_object = re.compile(regex) if msg is None: - self.msg = "String does not match expected pattern" + self.msg = _("String does not match expected pattern") else: self.msg = msg @@ -204,7 +214,7 @@ class Email(Regex): """ def __init__(self, msg=None): if msg is None: - msg = "Invalid email address" + msg = _("Invalid email address") super(Email, self).__init__( u'(?i)^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$', msg=msg) @@ -219,23 +229,22 @@ class Range(object): :exc:`colander.Invalid` error when reporting a validation failure caused by a value not meeting the minimum. If ``min_err`` is specified, it must be a string. The string may contain the - replacement targets ``%(min)s`` and ``%(val)s``, representing the + replacement targets ``${min}`` and ``${val}``, representing the minimum value and the provided value respectively. If it is not - provided, it defaults to ``'%(val)s is less than minimum value - %(min)s'``. + provided, it defaults to ``'${val} is less than minimum value + ${min}'``. ``max_err`` is used to form the ``msg`` of the :exc:`colander.Invalid` error when reporting a validation failure caused by a value exceeding the maximum. If ``max_err`` is specified, it must be a string. The string may contain the - replacement targets ``%(max)s`` and ``%(val)s``, representing the + replacement targets ``${max}`` and ``${val}``, representing the maximum value and the provided value respectively. If it is not - provided, it defaults to ``'%(val)s is greater than maximum value - %(max)s'``. + provided, it defaults to ``'${val} is greater than maximum value + ${max}'``. """ - - min_err = '%(val)s is less than minimum value %(min)s' - max_err = '%(val)s is greater than maximum value %(max)s' + min_err = _('${val} is less than minimum value ${min}') + max_err = _('${val} is greater than maximum value ${max}') def __init__(self, min=None, max=None, min_err=None, max_err=None): self.min = min @@ -248,11 +257,13 @@ class Range(object): def __call__(self, node, value): if self.min is not None: if value < self.min: - raise Invalid(node,self.min_err % {'val':value, 'min':self.min}) + min_err = _(self.min_err, mapping={'val':value, 'min':self.min}) + raise Invalid(node, min_err) if self.max is not None: if value > self.max: - raise Invalid(node,self.max_err % {'val':value, 'max':self.max}) + max_err = _(self.max_err, mapping={'val':value, 'max':self.max}) + raise Invalid(node, max_err) class Length(object): """ Validator which succeeds if the value passed to it has a @@ -265,26 +276,28 @@ class Length(object): def __call__(self, node, value): if self.min is not None: if len(value) < self.min: - raise Invalid( - node, - 'Shorter than minimum length %s' % self.min) + min_err = _('Shorter than minimum length ${min}', + mapping={'min':self.min}) + raise Invalid(node, min_err) if self.max is not None: if len(value) > self.max: - raise Invalid( - node, - 'Longer than maximum length %s' % self.max) + max_err = _('Longer than maximum length ${max}', + mapping={'max':self.max}) + raise Invalid(node, max_err) class OneOf(object): """ Validator which succeeds if the value passed to it is one of a fixed set of values """ - def __init__(self, values): - self.values = values + def __init__(self, choices): + self.choices = choices def __call__(self, node, value): - if not value in self.values: - raise Invalid(node, '"%s" is not one of %s' % ( - value, ', '.join(['%s' % x for x in self.values]))) + if not value in self.choices: + choices = ', '.join(['%s' % x for x in self.choices]) + err = _('"${val}" is not one of ${choices}', + mapping={'val':value, 'choices':choices}) + raise Invalid(node, err) class Mapping(object): """ A type which represents a mapping of names to nodes. @@ -350,7 +363,10 @@ class Mapping(object): try: return dict(value) except Exception, e: - raise Invalid(node, '%r is not a mapping type: %s' % (value, e)) + raise Invalid(node, + _('"${val}" is not a mapping type: ${err}', + mapping = {'val':value, 'err':e}) + ) def _impl(self, node, value, callback, default_callback, unknown=None, partial=None): @@ -375,7 +391,9 @@ class Mapping(object): if not partial: raise Invalid( subnode, - '"%s" is required but missing' % subnode.name) + _('"${val}" is required but missing', + mapping={'val':subnode.name}) + ) else: continue result[name] = default_callback(subnode) @@ -388,8 +406,11 @@ class Mapping(object): if unknown == 'raise': if value: - raise Invalid(node, - 'Unrecognized keys in mapping: %r' % value) + raise Invalid( + node, + _('Unrecognized keys in mapping: "${val}"', + mapping={'val':value}) + ) elif unknown == 'preserve': result.update(value) @@ -435,15 +456,20 @@ class Tuple(Positional): """ def _validate(self, node, value): if not hasattr(value, '__iter__'): - raise Invalid(node, '%r is not iterable' % value) + raise Invalid( + node, + _('"${val}" is not iterable', mapping={'val':value}) + ) valuelen, nodelen = len(value), len(node.children) if valuelen != nodelen: raise Invalid( node, - ('%s has an incorrect number of elements ' - '(expected %s, was %s)' % (value, nodelen, valuelen))) + _('"${val}" has an incorrect number of elements ' + '(expected ${exp}, was ${was})', + mapping={'val':value, 'exp':nodelen, 'was':valuelen}) + ) return list(value) @@ -510,7 +536,9 @@ class Sequence(Positional): if accept_scalar: return [value] else: - raise Invalid(node, '%r is not iterable' % value) + raise Invalid(node, _('"${val}" is not iterable', + mapping={'val':value}) + ) def _impl(self, node, value, callback, accept_scalar): if accept_scalar is None: @@ -612,10 +640,12 @@ class String(object): if not isinstance(value, unicode): value = unicode(str(value), self.encoding) except Exception, e: - raise Invalid(node, '%r is not a string: %s' % (value, e)) + raise Invalid(node, + _('${val} is not a string: %{err}', + mapping={'val':value, 'err':e})) if not value: if node.required: - raise Invalid(node, 'Required') + raise Invalid(node, _('Required')) value = node.default return value @@ -629,7 +659,9 @@ class String(object): return result except Exception, e: raise Invalid(node, - '%r cannot be serialized to str: %s' % (value, e)) + _('"${val} cannot be serialized to str: ${err}', + mapping={'val':value, 'err':e}) + ) Str = String @@ -645,15 +677,21 @@ class Integer(object): except Exception: if value == '': if node.required: - raise Invalid(node, 'Required') + raise Invalid(node, _('Required')) return node.default - raise Invalid(node, '"%s" is not a number' % value) + raise Invalid(node, + _('"${val}" is not a number', + mapping={'val':value}) + ) def serialize(self, node, value): try: return str(int(value)) except Exception: - raise Invalid(node, '"%s" is not a number' % value) + raise Invalid(node, + _('"${val}" is not a number', + mapping={'val':value}), + ) Int = Integer @@ -669,15 +707,21 @@ class Float(object): except Exception: if value == '': if node.required: - raise Invalid(node, 'Required') + raise Invalid(node, _('Required')) return node.default - raise Invalid(node, '"%s" is not a number' % value) + raise Invalid(node, + _('"${val}" is not a number', + mapping={'val':value}) + ) def serialize(self, node, value): try: return str(float(value)) except Exception: - raise Invalid(node, '"%s" is not a number' % value) + raise Invalid(node, + _('"${val}" is not a number', + mapping={'val':value}), + ) Int = Integer @@ -699,10 +743,12 @@ class Boolean(object): try: value = str(value) except: - raise Invalid(node, '%r is not a string' % value) + raise Invalid(node, + _('${val} is not a string', mapping={'val':value}) + ) if not value: if node.required: - raise Invalid(node, 'Required') + raise Invalid(node, _('Required')) value = node.default value = value.lower() if value in ('false', '0'): @@ -759,7 +805,9 @@ class GlobalObject(object): if not self.package: raise Invalid( node, - 'relative name "%s" irresolveable without package' % value) + _('relative name "${val}" irresolveable without package', + mapping={'val':value}) + ) if value in ['.', ':']: value = self.package.__name__ else: @@ -774,7 +822,9 @@ class GlobalObject(object): if self.package is None: raise Invalid( node, - 'relative name "%s" irresolveable without package' % value) + _('relative name "${val}" irresolveable without package', + mapping={'val':value}) + ) name = module.split('.') else: name = value.split('.') @@ -782,8 +832,9 @@ class GlobalObject(object): if module is None: raise Invalid( node, - 'relative name "%s" irresolveable without package' % - value) + _('relative name "${val}" irresolveable without ' + 'package', mapping={'val':value}) + ) module = module.split('.') name.pop(0) while not name[0]: @@ -805,7 +856,8 @@ class GlobalObject(object): def deserialize(self, node, value): if not isinstance(value, basestring): - raise Invalid(node, '"%s" is not a string' % value) + raise Invalid(node, + _('"${val}" is not a string', mapping={'val':value})) try: if ':' in value: return self._pkg_resources_style(node, value) @@ -813,13 +865,17 @@ class GlobalObject(object): return self._zope_dottedname_style(node, value) except ImportError: raise Invalid(node, - 'The dotted name "%s" cannot be imported' % value) + _('The dotted name "${name}" cannot be imported', + mapping={'name':value})) def serialize(self, node, value): try: return value.__name__ except AttributeError: - raise Invalid(node, '%r has no __name__' % value) + raise Invalid(node, + _('"${val}" has no __name__', + mapping={'val':value}) + ) class DateTime(object): """ A type representing a Python ``datetime.datetime`` object. @@ -839,10 +895,10 @@ class DateTime(object): You can adjust the error message reported by this class by changing its ``err_template`` attribute in a subclass on an instance of this class. By default, the ``err_template`` - attribute is the string ``%(value)s cannot be parsed as an iso8601 - date: %(exc)s``. This string is used as the interpolation subject - of a dictionary composed of ``value`` and ``exc``. ``value`` and - ``exc`` are the unvalidatable value and the exception caused + attribute is the string ``${value} cannot be parsed as an iso8601 + date: ${exc}``. This string is used as the interpolation subject + of a dictionary composed of ``val`` and ``err``. ``val`` and + ``err`` are the unvalidatable value and the exception caused trying to convert the value, respectively. For convenience, this type is also willing to coerce @@ -859,7 +915,7 @@ class DateTime(object): The subnodes of the :class:`colander.SchemaNode` that wraps this type are ignored. """ - err_template = '%(value)s cannot be parsed as an iso8601 date: %(exc)s' + err_template = _('${val} cannot be parsed as an iso8601 date: ${err}') def __init__(self, default_tzinfo=None): if default_tzinfo is None: default_tzinfo = iso8601.iso8601.Utc() @@ -869,7 +925,10 @@ class DateTime(object): if type(value) is datetime.date: # cant use isinstance; dt subs date value = datetime.datetime.combine(value, datetime.time()) if not isinstance(value, datetime.datetime): - raise Invalid(node, '%r is not a datetime object' % value) + raise Invalid(node, + _('"${val}" is not a datetime object', + mapping={'val':value}) + ) if value.tzinfo is None: value = value.replace(tzinfo=self.default_tzinfo) return value.isoformat() @@ -883,8 +942,8 @@ class DateTime(object): result = datetime.datetime(year, month, day, tzinfo=self.default_tzinfo) except Exception, e: - raise Invalid(node, self.err_template % {'value':value, - 'exc':e}) + raise Invalid(node, _(self.err_template, + mapping={'val':value, 'err':e})) return result class Date(object): @@ -899,9 +958,9 @@ class Date(object): You can adjust the error message reported by this class by changing its ``err_template`` attribute in a subclass on an instance of this class. By default, the ``err_template`` - attribute is the string ``%(value)s cannot be parsed as an iso8601 - date: %(exc)s``. This string is used as the interpolation subject - of a dictionary composed of ``value`` and ``exc``. ``value`` and + attribute is the string ``${val} cannot be parsed as an iso8601 + date: ${err}``. This string is used as the interpolation subject + of a dictionary composed of ``val`` and ``err``. ``val`` and ``exc`` are the unvalidatable value and the exception caused trying to convert the value, respectively. @@ -920,18 +979,21 @@ class Date(object): The subnodes of the :class:`colander.SchemaNode` that wraps this type are ignored. """ - err_template = '%(value)s cannot be parsed as an iso8601 date: %(exc)s' + err_template = _('${val} cannot be parsed as an iso8601 date: ${err}') def serialize(self, node, value): if isinstance(value, datetime.datetime): value = value.date() if not isinstance(value, datetime.date): - raise Invalid(node, '%r is not a date object' % value) + raise Invalid(node, + _('"${val}" is not a date object', + mapping={'val':value}) + ) return value.isoformat() def deserialize(self, node, value): if not value: if node.required: - raise Invalid(node, 'Required') + raise Invalid(node, _('Required')) return node.default try: result = iso8601.parse_date(value) @@ -941,8 +1003,10 @@ class Date(object): year, month, day = map(int, value.split('-', 2)) result = datetime.date(year, month, day) except Exception, e: - raise Invalid(node, self.err_template % {'value':value, - 'exc':e}) + raise Invalid(node, + _(self.err_template, + mapping={'val':value, 'err':e}) + ) return result class SchemaNode(object): diff --git a/colander/locale/colander.pot b/colander/locale/colander.pot new file mode 100644 index 0000000..65b7aec --- /dev/null +++ b/colander/locale/colander.pot @@ -0,0 +1,60 @@ +# Translations template for colander. +# Copyright (C) 2010 ORGANIZATION +# This file is distributed under the same license as the colander project. +# FIRST AUTHOR , 2010. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: colander 0.5.2\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2010-04-25 21:06-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 0.9.5\n" + +#: colander/__init__.py:177 +msgid "Invalid value" +msgstr "" + +#: colander/__init__.py:202 +msgid "String does not match expected pattern" +msgstr "" + +#: colander/__init__.py:217 +msgid "Invalid email address" +msgstr "" + +#: colander/__init__.py:246 +msgid "${val} is less than minimum value ${min}" +msgstr "" + +#: colander/__init__.py:247 +msgid "${val} is greater than maximum value ${max}" +msgstr "" + +#: colander/__init__.py:461 +msgid "\"${val}\" is not iterable" +msgstr "" + +#: colander/__init__.py:648 colander/__init__.py:680 colander/__init__.py:710 +#: colander/__init__.py:751 colander/__init__.py:996 +msgid "Required" +msgstr "" + +#: colander/__init__.py:747 +msgid "${val} is not a string" +msgstr "" + +#: colander/__init__.py:860 +msgid "\"${val}\" is not a string" +msgstr "" + +#: colander/__init__.py:918 colander/__init__.py:982 +msgid "${val} cannot be parsed as an iso8601 date: ${err}" +msgstr "" + diff --git a/colander/tests.py b/colander/tests.py index 3bf144d..1f75b32 100644 --- a/colander/tests.py +++ b/colander/tests.py @@ -189,7 +189,7 @@ class TestRange(unittest.TestCase): def test_min_failure(self): validator = self._makeOne(min=1) e = invalid_exc(validator, None, 0) - self.assertEqual(e.msg, '0 is less than minimum value 1') + self.assertEqual(e.msg.interpolate(), '0 is less than minimum value 1') def test_min_failure_msg_override(self): validator = self._makeOne(min=1, min_err='wrong') @@ -199,7 +199,8 @@ class TestRange(unittest.TestCase): def test_max_failure(self): validator = self._makeOne(max=1) e = invalid_exc(validator, None, 2) - self.assertEqual(e.msg, '2 is greater than maximum value 1') + self.assertEqual(e.msg.interpolate(), + '2 is greater than maximum value 1') def test_max_failure_msg_override(self): validator = self._makeOne(max=1, max_err='wrong') @@ -272,12 +273,12 @@ class TestLength(unittest.TestCase): def test_min_failure(self): validator = self._makeOne(min=1) e = invalid_exc(validator, None, '') - self.assertEqual(e.msg, 'Shorter than minimum length 1') + self.assertEqual(e.msg.interpolate(), 'Shorter than minimum length 1') def test_max_failure(self): validator = self._makeOne(max=1) e = invalid_exc(validator, None, 'ab') - self.assertEqual(e.msg, 'Longer than maximum length 1') + self.assertEqual(e.msg.interpolate(), 'Longer than maximum length 1') class TestOneOf(unittest.TestCase): def _makeOne(self, values): @@ -291,7 +292,8 @@ class TestOneOf(unittest.TestCase): def test_failure(self): validator = self._makeOne([1, 2]) e = invalid_exc(validator, None, None) - self.assertEqual(e.msg, '"None" is not one of 1, 2') + self.assertEqual(e.msg.interpolate(), '"None" is not one of 1, 2') + class TestMapping(unittest.TestCase): def _makeOne(self, *arg, **kw): @@ -314,7 +316,7 @@ class TestMapping(unittest.TestCase): typ = self._makeOne() e = invalid_exc(typ.deserialize, node, None) self.failUnless( - e.msg.startswith('None is not a mapping type')) + e.msg.interpolate().startswith('"None" is not a mapping type')) def test_deserialize_no_subnodes(self): node = DummySchemaNode(None) @@ -334,7 +336,9 @@ class TestMapping(unittest.TestCase): node.children = [DummySchemaNode(None, name='a')] typ = self._makeOne(unknown='raise') e = invalid_exc(typ.deserialize, node, {'a':1, 'b':2}) - self.assertEqual(e.msg, "Unrecognized keys in mapping: {'b': 2}") + self.assertEqual(e.msg.interpolate(), + "Unrecognized keys in mapping: \"{'b': 2}\"") + def test_deserialize_unknown_preserve(self): node = DummySchemaNode(None) @@ -372,7 +376,8 @@ class TestMapping(unittest.TestCase): ] typ = self._makeOne() e = invalid_exc(typ.deserialize, node, {'a':1}) - self.assertEqual(e.children[0].msg, '"b" is required but missing') + self.assertEqual(e.children[0].msg.interpolate(), + '"b" is required but missing') def test_deserialize_subnode_partial(self): node = DummySchemaNode(None) @@ -389,7 +394,7 @@ class TestMapping(unittest.TestCase): typ = self._makeOne() e = invalid_exc(typ.serialize, node, None) self.failUnless( - e.msg.startswith('None is not a mapping type')) + e.msg.interpolate().startswith('"None" is not a mapping type')) def test_serialize_no_subnodes(self): node = DummySchemaNode(None) @@ -443,8 +448,8 @@ class TestTuple(unittest.TestCase): typ = self._makeOne() e = invalid_exc(typ.deserialize, node, None) self.assertEqual( - e.msg, - 'None is not iterable') + e.msg.interpolate(), + '"None" is not iterable') self.assertEqual(e.node, node) def test_deserialize_no_subnodes(self): @@ -465,16 +470,16 @@ class TestTuple(unittest.TestCase): node.children = [DummySchemaNode(None, name='a')] typ = self._makeOne() e = invalid_exc(typ.deserialize, node, ('a','b')) - self.assertEqual(e.msg, - "('a', 'b') has an incorrect number of elements (expected 1, was 2)") + self.assertEqual(e.msg.interpolate(), + "\"('a', 'b')\" has an incorrect number of elements (expected 1, was 2)") def test_deserialize_toosmall(self): node = DummySchemaNode(None) node.children = [DummySchemaNode(None, name='a')] typ = self._makeOne() e = invalid_exc(typ.deserialize, node, ()) - self.assertEqual(e.msg, - "() has an incorrect number of elements (expected 1, was 0)") + self.assertEqual(e.msg.interpolate(), + '"()" has an incorrect number of elements (expected 1, was 0)') def test_deserialize_subnodes_raise(self): node = DummySchemaNode(None) @@ -492,8 +497,8 @@ class TestTuple(unittest.TestCase): typ = self._makeOne() e = invalid_exc(typ.serialize, node, None) self.assertEqual( - e.msg, - 'None is not iterable') + e.msg.interpolate(), + '"None" is not iterable') self.assertEqual(e.node, node) def test_serialize_no_subnodes(self): @@ -514,16 +519,17 @@ class TestTuple(unittest.TestCase): node.children = [DummySchemaNode(None, name='a')] typ = self._makeOne() e = invalid_exc(typ.serialize, node, ('a','b')) - self.assertEqual(e.msg, - "('a', 'b') has an incorrect number of elements (expected 1, was 2)") + self.assertEqual(e.msg.interpolate(), + "\"('a', 'b')\" has an incorrect number of elements (expected 1, was 2)") def test_serialize_toosmall(self): node = DummySchemaNode(None) node.children = [DummySchemaNode(None, name='a')] typ = self._makeOne() e = invalid_exc(typ.serialize, node, ()) - self.assertEqual(e.msg, - "() has an incorrect number of elements (expected 1, was 0)") + self.assertEqual(e.msg.interpolate(), + '"()" has an incorrect number of elements (expected 1, was 0)' + ) def test_serialize_subnodes_raise(self): node = DummySchemaNode(None) @@ -552,8 +558,8 @@ class TestSequence(unittest.TestCase): node.children = [node] e = invalid_exc(typ.deserialize, node, None) self.assertEqual( - e.msg, - 'None is not iterable') + e.msg.interpolate(), + '"None" is not iterable') self.assertEqual(e.node, node) def test_deserialize_not_iterable_accept_scalar(self): @@ -592,8 +598,8 @@ class TestSequence(unittest.TestCase): node.children = [node] e = invalid_exc(typ.serialize, node, None) self.assertEqual( - e.msg, - 'None is not iterable') + e.msg.interpolate(), + '"None" is not iterable') self.assertEqual(e.node, node) def test_serialize_not_iterable_accept_scalar(self): @@ -894,14 +900,15 @@ class TestGlobalObject(unittest.TestCase): typ = self._makeOne() e = invalid_exc(typ._zope_dottedname_style, None, '.') self.assertEqual( - e.msg, + e.msg.interpolate(), 'relative name "." irresolveable without package') def test_zope_dottedname_style_resolve_relative_nocurrentpackage(self): typ = self._makeOne() e = invalid_exc(typ._zope_dottedname_style, None, '.whatever') self.assertEqual( - e.msg, 'relative name ".whatever" irresolveable without package') + e.msg.interpolate(), + 'relative name ".whatever" irresolveable without package') def test_zope_dottedname_style_irrresolveable_relative(self): import colander.tests @@ -971,7 +978,7 @@ class TestGlobalObject(unittest.TestCase): def test_deserialize_not_a_string(self): typ = self._makeOne() e = invalid_exc(typ.deserialize, None, None) - self.assertEqual(e.msg, '"None" is not a string') + self.assertEqual(e.msg.interpolate(), '"None" is not a string') def test_deserialize_using_pkgresources_style(self): typ = self._makeOne() @@ -986,7 +993,7 @@ class TestGlobalObject(unittest.TestCase): def test_deserialize_style_raises(self): typ = self._makeOne() e = invalid_exc(typ.deserialize, None, 'cant.be.found') - self.assertEqual(e.msg, + self.assertEqual(e.msg.interpolate(), 'The dotted name "cant.be.found" cannot be imported') def test_serialize_ok(self): @@ -998,7 +1005,7 @@ class TestGlobalObject(unittest.TestCase): def test_serialize_fail(self): typ = self._makeOne() e = invalid_exc(typ.serialize, None, None) - self.assertEqual(e.msg, 'None has no __name__') + self.assertEqual(e.msg.interpolate(), '"None" has no __name__') class TestDateTime(unittest.TestCase): def _makeOne(self, *arg, **kw): @@ -1020,7 +1027,8 @@ class TestDateTime(unittest.TestCase): typ = self._makeOne() node = DummySchemaNode(None) e = invalid_exc(typ.serialize, node, 'garbage') - self.assertEqual(e.msg, "'garbage' is not a datetime object") + self.assertEqual(e.msg.interpolate(), + '"garbage" is not a datetime object') def test_serialize_with_date(self): import datetime @@ -1093,7 +1101,7 @@ class TestDate(unittest.TestCase): typ = self._makeOne() node = DummySchemaNode(None) e = invalid_exc(typ.serialize, node, 'garbage') - self.assertEqual(e.msg, "'garbage' is not a date object") + self.assertEqual(e.msg.interpolate(), '"garbage" is not a date object') def test_serialize_with_date(self): import datetime diff --git a/setup.cfg b/setup.cfg index 996032c..b5ad85a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -8,3 +8,23 @@ nocapture=1 cover-package=colander cover-erase=1 +[compile_catalog] +directory = colander/locale +domain = colander +statistics = true + +[extract_messages] +add_comments = TRANSLATORS: +output_file = colander/locale/colander.pot +width = 80 + +[init_catalog] +domain = colander +input_file = colander/locale/colander.pot +output_dir = colander/locale + +[update_catalog] +domain = colander +input_file = colander/locale/colander.pot +output_dir = colander/locale +previous = true diff --git a/setup.py b/setup.py index d0069b9..7157f6a 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ here = os.path.abspath(os.path.dirname(__file__)) README = open(os.path.join(here, 'README.txt')).read() CHANGES = open(os.path.join(here, 'CHANGES.txt')).read() -requires = ['iso8601'] +requires = ['iso8601', 'translationstring'] setup(name='colander', version='0.5.2', @@ -43,5 +43,9 @@ setup(name='colander', tests_require = requires, install_requires = requires, test_suite="colander", + message_extractors = { ".": [ + ("**.py", "chameleon_python", None ), + ("**.pt", "chameleon_xml", None ), + ]}, )