zope.i18nmessageid-3.5.3/0000755000175000017500000000000011430160067014471 5ustar chrismchrismzope.i18nmessageid-3.5.3/LICENSE.txt0000644000175000017500000000402611430157634016324 0ustar chrismchrismZope Public License (ZPL) Version 2.1 A copyright notice accompanies this license document that identifies the copyright holders. This license has been certified as open source. It has also been designated as GPL compatible by the Free Software Foundation (FSF). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions in source code must retain the accompanying copyright notice, this list of conditions, and the following disclaimer. 2. Redistributions in binary form must reproduce the accompanying copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Names of the copyright holders must not be used to endorse or promote products derived from this software without prior written permission from the copyright holders. 4. The right to distribute this software or to use it for any purpose does not give you the right to use Servicemarks (sm) or Trademarks (tm) of the copyright holders. Use of them is covered by separate agreement with the copyright holders. 5. If any files are modified, you must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. Disclaimer THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. zope.i18nmessageid-3.5.3/setup.py0000644000175000017500000000714611430157634016221 0ustar chrismchrism############################################################################## # # Copyright (c) 2006 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## # This package is developed by the Zope Toolkit project, documented here: # http://docs.zope.org/zopetoolkit # When developing and releasing this package, please follow the documented # Zope Toolkit policies as described by this documentation. ############################################################################## """Setup for zope.i18nmessageid package """ import os import sys from setuptools import setup, find_packages, Extension from distutils.command.build_ext import build_ext from distutils.errors import CCompilerError from distutils.errors import DistutilsExecError from distutils.errors import DistutilsPlatformError def read(*rnames): return open(os.path.join(os.path.dirname(__file__), *rnames)).read() class optional_build_ext(build_ext): """This class subclasses build_ext and allows the building of C extensions to fail. """ def run(self): try: build_ext.run(self) except DistutilsPlatformError, e: self._unavailable(e) def build_extension(self, ext): try: build_ext.build_extension(self, ext) except (CCompilerError, DistutilsExecError), e: self._unavailable(e) def _unavailable(self, e): print >> sys.stderr, '*' * 80 print >> sys.stderr, """WARNING: An optional code optimization (C extension) could not be compiled. Optimizations for this package will not be available!""" print >> sys.stderr print >> sys.stderr, e print >> sys.stderr, '*' * 80 setup(name='zope.i18nmessageid', version = '3.5.3', author='Zope Foundation and Contributors', author_email='zope-dev@zope.org', description='Message Identifiers for internationalization', long_description=( read('README.txt') + '\n\n.. contents::\n\n' + read('src', 'zope', 'i18nmessageid', 'messages.txt') + '\n\n' + read('CHANGES.txt') ), keywords = "zope i18n message factory", classifiers = [ 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', 'Intended Audience :: Developers', 'License :: OSI Approved :: Zope Public License', 'Programming Language :: Python', 'Natural Language :: English', 'Operating System :: OS Independent', 'Topic :: Internet :: WWW/HTTP', 'Framework :: Zope3'], license='ZPL 2.1', url='http://pypi.python.org/pypi/zope.i18nmessageid', packages=find_packages('src'), package_dir = {'': 'src'}, ext_modules=[ Extension("zope.i18nmessageid._zope_i18nmessageid_message", [os.path.join('src', 'zope', 'i18nmessageid', "_zope_i18nmessageid_message.c") ]), ], namespace_packages=['zope',], install_requires=['setuptools'], include_package_data = True, test_suite='zope.i18nmessageid.tests.test_suite', zip_safe = False, cmdclass = {'build_ext':optional_build_ext}, ) zope.i18nmessageid-3.5.3/README.txt0000644000175000017500000000210111430157634016167 0ustar chrismchrismTo translate any text, we must be able to discover the source domain of the text. A source domain is an identifier that identifies a project that produces program source strings. Source strings occur as literals in python programs, text in templates, and some text in XML data. The project implies a source language and an application context. We can think of a source domain as a collection of messages and associated translation strings. We often need to create unicode strings that will be displayed by separate views. The view cannot translate the string without knowing its source domain. A string or unicode literal carries no domain information, therefore we use messages. Messages are unicode strings which carry a translation source domain and possibly a default translation. They are created by a message factory. The message factory is created by calling ``MessageFactory`` with the source domain. This package provides facilities for *declaring* such messages within program source text; translation of the messages is the responsiblitiy of the 'zope.i18n' package. zope.i18nmessageid-3.5.3/CHANGES.txt0000644000175000017500000000430411430157634016311 0ustar chrismchrism======= CHANGES ======= 3.5.3 (2010-08-10) ------------------ - Made compilation of C extension optional again; 3.5.1 broke this inasmuch as this package become unusable on non-CPython platforms. Making the compilation of the C extension optional again implied removing ``setup.py`` code added in 3.5.1 which made the C extension a setuptools "Feature" and readding code from 3.5.0 which overrides the distutils ``build_ext`` command. - Move pickle equality tests into a unittest.TestCase test to make it easier to condition the tests on whether the C extension has been compiled. This also makes the tests pass on Jython. 3.5.2 (2010-04-30) ------------------ - Removed use of 'zope.testing.doctestunit' in favor of stdlib's 'doctest. 3.5.1 (2010-04-10) ------------------ - LP #257657 / 489529: Fix memory leak in C extension. - Fixed the compilation of the C extension with python 2.6: refactored it as a setuptools Feature. 3.5.0 (2009-06-27) ------------------ - Made compilation of C extension optional. - Added support to bootstrap on Jython. - Changed package's mailing list address from zope3-dev at zope.org to zope-dev at zope.org, because zope3-dev is now retired. - Reformatted change log to common formatting style. - Update package description and docs a little. - Remove old .cfg files for zpkg. 3.4.3 (2007-09-26) ------------------ - Make PyPI the home URL. 3.4.2 (2007-09-25) ------------------ - Moved the ``ZopeMessageFactory`` from ``zope.app.i18n`` to this package. 3.4.0 (2007-07-19) ------------------ - Remove incorrect dependency. - Create final release to reflect package status. 3.2.0 (2006-01-05) ------------------ - Corresponds to the verison of the zope.i18nmessageid package shipped as part of the Zope 3.2.0 release. - Implemented 'zope.i18nmessageid.message' as a C extension. - Deprecated 'zope.i18nmessageid.messageid' APIs ('MessageID', 'MessageIDFactory') in favor of replacements in 'zope.i18nmessageid.message' ('Message', 'MessageFactory'). Deprecated items are scheduled for removal in Zope 3.3. 3.0.0 (2004-11-07) ------------------ - Corresponds to the verison of the zope.i18nmessageid package shipped as part of the Zope X3.0.0 release. zope.i18nmessageid-3.5.3/src/0000755000175000017500000000000011430160067015260 5ustar chrismchrismzope.i18nmessageid-3.5.3/src/zope.i18nmessageid.egg-info/0000755000175000017500000000000011430160067022367 5ustar chrismchrismzope.i18nmessageid-3.5.3/src/zope.i18nmessageid.egg-info/not-zip-safe0000644000175000017500000000000111430160067024615 0ustar chrismchrism zope.i18nmessageid-3.5.3/src/zope.i18nmessageid.egg-info/SOURCES.txt0000644000175000017500000000115711430160067024257 0ustar chrismchrismCHANGES.txt COPYRIGHT.txt LICENSE.txt README.txt bootstrap.py buildout.cfg setup.py src/zope/__init__.py src/zope.i18nmessageid.egg-info/PKG-INFO src/zope.i18nmessageid.egg-info/SOURCES.txt src/zope.i18nmessageid.egg-info/dependency_links.txt src/zope.i18nmessageid.egg-info/namespace_packages.txt src/zope.i18nmessageid.egg-info/not-zip-safe src/zope.i18nmessageid.egg-info/requires.txt src/zope.i18nmessageid.egg-info/top_level.txt src/zope/i18nmessageid/__init__.py src/zope/i18nmessageid/_zope_i18nmessageid_message.c src/zope/i18nmessageid/message.py src/zope/i18nmessageid/messages.txt src/zope/i18nmessageid/tests.pyzope.i18nmessageid-3.5.3/src/zope.i18nmessageid.egg-info/top_level.txt0000644000175000017500000000000511430160067025114 0ustar chrismchrismzope zope.i18nmessageid-3.5.3/src/zope.i18nmessageid.egg-info/requires.txt0000644000175000017500000000001211430160067024760 0ustar chrismchrismsetuptoolszope.i18nmessageid-3.5.3/src/zope.i18nmessageid.egg-info/namespace_packages.txt0000644000175000017500000000000511430160067026715 0ustar chrismchrismzope zope.i18nmessageid-3.5.3/src/zope.i18nmessageid.egg-info/PKG-INFO0000644000175000017500000002333711430160067023474 0ustar chrismchrismMetadata-Version: 1.0 Name: zope.i18nmessageid Version: 3.5.3 Summary: Message Identifiers for internationalization Home-page: http://pypi.python.org/pypi/zope.i18nmessageid Author: Zope Foundation and Contributors Author-email: zope-dev@zope.org License: ZPL 2.1 Description: To translate any text, we must be able to discover the source domain of the text. A source domain is an identifier that identifies a project that produces program source strings. Source strings occur as literals in python programs, text in templates, and some text in XML data. The project implies a source language and an application context. We can think of a source domain as a collection of messages and associated translation strings. We often need to create unicode strings that will be displayed by separate views. The view cannot translate the string without knowing its source domain. A string or unicode literal carries no domain information, therefore we use messages. Messages are unicode strings which carry a translation source domain and possibly a default translation. They are created by a message factory. The message factory is created by calling ``MessageFactory`` with the source domain. This package provides facilities for *declaring* such messages within program source text; translation of the messages is the responsiblitiy of the 'zope.i18n' package. .. contents:: ============= I18n Messages ============= Rationale --------- To translate any text, we must be able to discover the source domain of the text. A source domain is an identifier that identifies a project that produces program source strings. Source strings occur as literals in python programs, text in templates, and some text in XML data. The project implies a source language and an application context. We can think of a source domain as a collection of messages and associated translation strings. We often need to create unicode strings that will be displayed by separate views. The view cannot translate the string without knowing its source domain. A string or unicode literal carries no domain information, therefore we use messages. Messages are unicode strings which carry a translation source domain and possibly a default translation. They are created by a message factory. The message factory is created by calling ``MessageFactory`` with the source domain. ZopeMessageFactory ------------------ >>> from zope.i18nmessageid import ZopeMessageFactory as _z_ >>> foo = _z_('foo') >>> foo.domain 'zope' Example ------- In this example, we create a message factory and assign it to _. By convention, we use _ as the name of our factory to be compatible with translatable string extraction tools such as xgettext. We then call _ with a string that needs to be translatable: >>> from zope.i18nmessageid import MessageFactory, Message >>> _ = MessageFactory("futurama") >>> robot = _(u"robot-message", u"${name} is a robot.") Messages at first seem like they are unicode strings: >>> robot u'robot-message' >>> isinstance(robot, unicode) True The additional domain, default and mapping information is available through attributes: >>> robot.default u'${name} is a robot.' >>> robot.mapping >>> robot.domain 'futurama' The message's attributes are considered part of the immutable message object. They cannot be changed once the message id is created: >>> robot.domain = "planetexpress" Traceback (most recent call last): ... TypeError: readonly attribute >>> robot.default = u"${name} is not a robot." Traceback (most recent call last): ... TypeError: readonly attribute >>> robot.mapping = {u'name': u'Bender'} Traceback (most recent call last): ... TypeError: readonly attribute If you need to change their information, you'll have to make a new message id object: >>> new_robot = Message(robot, mapping={u'name': u'Bender'}) >>> new_robot u'robot-message' >>> new_robot.domain 'futurama' >>> new_robot.default u'${name} is a robot.' >>> new_robot.mapping {u'name': u'Bender'} Last but not least, messages are reduceable for pickling: >>> callable, args = new_robot.__reduce__() >>> callable is Message True >>> args (u'robot-message', 'futurama', u'${name} is a robot.', {u'name': u'Bender'}) >>> fembot = Message(u'fembot') >>> callable, args = fembot.__reduce__() >>> callable is Message True >>> args (u'fembot', None, None, None) Message IDs and backward compatability -------------------------------------- The change to immutability is not a simple refactoring that can be coped with backward compatible APIs--it is a change in semantics. Because immutability is one of those "you either have it or you don't" things (like pregnancy or death), we will not be able to support both in one implementation. The proposed solution for backward compatability is to support both implementations in parallel, deprecating the mutable one. A separate factory, ``MessageFactory``, instantiates immutable messages, while the deprecated old one continues to work like before. The roadmap to immutable-only message ids is proposed as follows: Zope 3.1: Immutable message ids are introduced. Security declarations for mutable message ids are provided to make the stripping of security proxies unnecessary. Zope 3.2: Mutable message ids are deprecated. Zope 3.3: Mutable message ids are removed. ======= CHANGES ======= 3.5.3 (2010-08-10) ------------------ - Made compilation of C extension optional again; 3.5.1 broke this inasmuch as this package become unusable on non-CPython platforms. Making the compilation of the C extension optional again implied removing ``setup.py`` code added in 3.5.1 which made the C extension a setuptools "Feature" and readding code from 3.5.0 which overrides the distutils ``build_ext`` command. - Move pickle equality tests into a unittest.TestCase test to make it easier to condition the tests on whether the C extension has been compiled. This also makes the tests pass on Jython. 3.5.2 (2010-04-30) ------------------ - Removed use of 'zope.testing.doctestunit' in favor of stdlib's 'doctest. 3.5.1 (2010-04-10) ------------------ - LP #257657 / 489529: Fix memory leak in C extension. - Fixed the compilation of the C extension with python 2.6: refactored it as a setuptools Feature. 3.5.0 (2009-06-27) ------------------ - Made compilation of C extension optional. - Added support to bootstrap on Jython. - Changed package's mailing list address from zope3-dev at zope.org to zope-dev at zope.org, because zope3-dev is now retired. - Reformatted change log to common formatting style. - Update package description and docs a little. - Remove old .cfg files for zpkg. 3.4.3 (2007-09-26) ------------------ - Make PyPI the home URL. 3.4.2 (2007-09-25) ------------------ - Moved the ``ZopeMessageFactory`` from ``zope.app.i18n`` to this package. 3.4.0 (2007-07-19) ------------------ - Remove incorrect dependency. - Create final release to reflect package status. 3.2.0 (2006-01-05) ------------------ - Corresponds to the verison of the zope.i18nmessageid package shipped as part of the Zope 3.2.0 release. - Implemented 'zope.i18nmessageid.message' as a C extension. - Deprecated 'zope.i18nmessageid.messageid' APIs ('MessageID', 'MessageIDFactory') in favor of replacements in 'zope.i18nmessageid.message' ('Message', 'MessageFactory'). Deprecated items are scheduled for removal in Zope 3.3. 3.0.0 (2004-11-07) ------------------ - Corresponds to the verison of the zope.i18nmessageid package shipped as part of the Zope X3.0.0 release. Keywords: zope i18n message factory Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Zope Public License Classifier: Programming Language :: Python Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Topic :: Internet :: WWW/HTTP Classifier: Framework :: Zope3 zope.i18nmessageid-3.5.3/src/zope.i18nmessageid.egg-info/dependency_links.txt0000644000175000017500000000000111430160067026435 0ustar chrismchrism zope.i18nmessageid-3.5.3/src/zope/0000755000175000017500000000000011430160067016235 5ustar chrismchrismzope.i18nmessageid-3.5.3/src/zope/i18nmessageid/0000755000175000017500000000000011430160067020676 5ustar chrismchrismzope.i18nmessageid-3.5.3/src/zope/i18nmessageid/tests.py0000644000175000017500000001240411430157634022421 0ustar chrismchrism############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Message ID tests. """ import unittest from doctest import DocFileSuite from doctest import DocTestSuite class PickleEqualityTests(unittest.TestCase): def setUp(self): # set the C version up as the used version import zope.i18nmessageid.message self.oldMessage = zope.i18nmessageid.message.Message def tearDown(self): # set the original version back up as the used version import zope.i18nmessageid.message zope.i18nmessageid.message.Message = self.oldMessage def test_message_pickling(self): from zope.i18nmessageid.message import pyMessage as Message robot = Message(u"robot-message", 'futurama', u"${name} is a robot.") self.assertEqual(robot, u'robot-message') self.failUnless(isinstance(robot, unicode)) self.assertEqual(robot.default, u'${name} is a robot.') self.assertEqual(robot.mapping, None) # Only the python implementation has a _readonly attribute self.assertEqual(robot._readonly, True) self.assertRaises( TypeError, robot.__setattr__, 'domain', "planetexpress") self.assertRaises( TypeError, robot.__setattr__, 'default', u"${name} is not a robot.") self.assertRaises( TypeError, robot.__setattr__, 'mapping', {u'name': u'Bender'}) new_robot = Message(robot, mapping={u'name': u'Bender'}) self.assertEqual(new_robot, u'robot-message') self.assertEqual(new_robot.domain, 'futurama') self.assertEqual(new_robot.default, u'${name} is a robot.') self.assertEqual(new_robot.mapping, {u'name': u'Bender'}) callable, args = new_robot.__reduce__() self.failUnless(callable is Message) self.assertEqual( args, (u'robot-message', 'futurama', u'${name} is a robot.', {u'name': u'Bender'})) fembot = Message(u'fembot') callable, args = fembot.__reduce__() self.failUnless(callable is Message) self.assertEqual(args, (u'fembot', None, None, None)) import zope.i18nmessageid.message zope.i18nmessageid.message.Message = Message # First check if pickling and unpickling from pyMessage to # pyMessage works from pickle import dumps, loads pystate = dumps(new_robot) pickle_bot = loads(pystate) self.assertEqual(pickle_bot, u'robot-message') self.assertEqual(pickle_bot.domain, 'futurama') self.assertEqual(pickle_bot.default, u'${name} is a robot.') self.assertEqual(pickle_bot.mapping, {u'name': u'Bender'}) self.assertEqual(pickle_bot._readonly, True) from zope.i18nmessageid.message import pyMessage self.failUnless(pickle_bot.__reduce__()[0] is pyMessage) del pickle_bot # Second check if cMessage is able to load the state of a pyMessage from _zope_i18nmessageid_message import Message zope.i18nmessageid.message.Message = Message c_bot = loads(pystate) self.assertEqual(c_bot, u'robot-message') self.assertEqual(c_bot.domain, 'futurama') self.assertEqual(c_bot.default, u'${name} is a robot.') self.assertEqual(c_bot.mapping, {u'name': u'Bender'}) self.failIf(hasattr(c_bot, '_readonly')) from _zope_i18nmessageid_message import Message as cMessage self.failUnless(c_bot.__reduce__()[0] is cMessage) # Last check if pyMessage can load a state of cMessage cstate = dumps(c_bot) del c_bot from zope.i18nmessageid.message import pyMessage as Message zope.i18nmessageid.message.Message = Message py_bot = loads(cstate) self.assertEqual(py_bot, u'robot-message') self.assertEqual(py_bot.domain, 'futurama') self.assertEqual(py_bot.default, u'${name} is a robot.') self.assertEqual(py_bot.mapping, {u'name': u'Bender'}) self.assertEqual(py_bot._readonly, True) self.failUnless(py_bot.__reduce__()[0] is pyMessage) # Both pickle states should be equal self.assertEqual(pystate, cstate) try: from _zope_i18nmessageid_message import Message as import_test def test_suite(): return unittest.TestSuite(( DocTestSuite('zope.i18nmessageid.message'), DocFileSuite('messages.txt', package='zope.i18nmessageid'), unittest.makeSuite(PickleEqualityTests), )) except ImportError: # pragma: no cover # couldnt import C version def test_suite(): return unittest.TestSuite(( DocTestSuite('zope.i18nmessageid.message'), DocFileSuite('messages.txt', package='zope.i18nmessageid'), )) zope.i18nmessageid-3.5.3/src/zope/i18nmessageid/_zope_i18nmessageid_message.c0000644000175000017500000001655011430157634026420 0ustar chrismchrism/*############################################################################ # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################*/ #include "Python.h" /* these macros make gc support easier; they are only available in Python 2.4 and borrowed from there */ #ifndef Py_CLEAR #define Py_CLEAR(op) \ do { \ if (op) { \ PyObject *tmp = (op); \ (op) = NULL; \ Py_DECREF(tmp); \ } \ } while (0) #endif #ifndef Py_VISIT #define Py_VISIT(op) \ do { \ if (op) { \ int vret = visit((op), arg); \ if (vret) \ return vret; \ } \ } while (0) #endif /* ----------------------------------------------------- */ typedef struct { PyUnicodeObject base; PyObject *domain; PyObject *default_; PyObject *mapping; } Message; static PyTypeObject MessageType; static PyObject * Message_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"value", "domain", "default", "mapping", NULL}; PyObject *value, *domain=NULL, *default_=NULL, *mapping=NULL, *s; Message *self; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOO", kwlist, &value, &domain, &default_, &mapping)) return NULL; args = Py_BuildValue("(O)", value); if (args == NULL) return NULL; s = PyUnicode_Type.tp_new(type, args, NULL); Py_DECREF(args); if (s == NULL) return NULL; if (! PyObject_TypeCheck(s, &MessageType)) { PyErr_SetString(PyExc_TypeError, "unicode.__new__ didn't return a Message"); Py_DECREF(s); return NULL; } self = (Message*)s; if (PyObject_TypeCheck(value, &MessageType)) { self->domain = ((Message *)value)->domain; self->default_ = ((Message *)value)->default_; self->mapping = ((Message *)value)->mapping; } else { self->domain = self->default_ = self->mapping = NULL; } if (domain != NULL) self->domain = domain; if (default_ != NULL) self->default_ = default_; if (mapping != NULL) self->mapping = mapping; Py_XINCREF(self->mapping); Py_XINCREF(self->default_); Py_XINCREF(self->domain); return (PyObject *)self; } /* Code to access structure members by accessing attributes */ #include "structmember.h" static PyMemberDef Message_members[] = { { "domain", T_OBJECT, offsetof(Message, domain), RO }, { "default", T_OBJECT, offsetof(Message, default_), RO }, { "mapping", T_OBJECT, offsetof(Message, mapping), RO }, {NULL} /* Sentinel */ }; static int Message_traverse(Message *self, visitproc visit, void *arg) { Py_VISIT(self->domain); Py_VISIT(self->default_); Py_VISIT(self->mapping); return 0; } static int Message_clear(Message *self) { Py_CLEAR(self->domain); Py_CLEAR(self->default_); Py_CLEAR(self->mapping); return 0; } static void Message_dealloc(Message *self) { Message_clear(self); PyUnicode_Type.tp_dealloc((PyObject*)self); } static PyObject * Message_reduce(Message *self) { PyObject *value, *result; value = PyObject_CallFunctionObjArgs((PyObject *)&PyUnicode_Type, self, NULL); if (value == NULL) return NULL; result = Py_BuildValue("(O(OOOO))", self->base.ob_type, value, self->domain ? self->domain : Py_None, self->default_ ? self->default_ : Py_None, self->mapping ? self->mapping : Py_None); Py_DECREF(value); return result; } static PyMethodDef Message_methods[] = { {"__reduce__", (PyCFunction)Message_reduce, METH_NOARGS, "Reduce messages to a serializable form."}, {NULL} /* Sentinel */ }; static char MessageType__doc__[] = "Message\n" "\n" "This is a string used as a message. It has a domain attribute that is\n" "its source domain, and a default attribute that is its default text to\n" "display when there is no translation. domain may be None meaning there is\n" "no translation domain. default may also be None, in which case the\n" "message id itself implicitly serves as the default text.\n"; statichere PyTypeObject MessageType = { PyObject_HEAD_INIT(NULL) /* ob_size */ 0, /* tp_name */ "zope.i18nmessageid.message." "Message", /* tp_basicsize */ sizeof(Message), /* tp_itemsize */ 0, /* tp_dealloc */ (destructor)&Message_dealloc, /* tp_print */ (printfunc)0, /* tp_getattr */ (getattrfunc)0, /* tp_setattr */ (setattrfunc)0, /* tp_compare */ (cmpfunc)0, /* tp_repr */ (reprfunc)0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ (hashfunc)0, /* tp_call */ (ternaryfunc)0, /* tp_str */ (reprfunc)0, /* tp_getattro */ (getattrofunc)0, /* tp_setattro */ (setattrofunc)0, /* tp_as_buffer */ 0, /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_doc */ MessageType__doc__, /* tp_traverse */ (traverseproc)Message_traverse, /* tp_clear */ (inquiry)Message_clear, /* tp_richcompare */ (richcmpfunc)0, /* tp_weaklistoffset */ (long)0, /* tp_iter */ (getiterfunc)0, /* tp_iternext */ (iternextfunc)0, /* tp_methods */ Message_methods, /* tp_members */ Message_members, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* internal use */ /* tp_descr_get */ (descrgetfunc)0, /* tp_descr_set */ (descrsetfunc)0, /* tp_dictoffset */ 0, /* tp_init */ (initproc)0, /* tp_alloc */ (allocfunc)0, /* tp_new */ (newfunc)Message_new, /* tp_free */ 0, /* Low-level free-mem routine */ /* tp_is_gc */ (inquiry)0, /* For PyObject_IS_GC */ }; /* End of code for Message objects */ /* -------------------------------------------------------- */ /* List of methods defined in the module */ static struct PyMethodDef _zope_i18nmessageid_message_methods[] = { {NULL, (PyCFunction)NULL, 0, NULL} /* sentinel */ }; static char _zope_i18nmessageid_message_module_documentation[] = "I18n Messages" ; #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ #define PyMODINIT_FUNC void #endif PyMODINIT_FUNC init_zope_i18nmessageid_message(void) { PyObject *m; /* Initialize types: */ MessageType.tp_base = &PyUnicode_Type; if (PyType_Ready(&MessageType) < 0) return; /* Create the module and add the functions */ m = Py_InitModule3("_zope_i18nmessageid_message", _zope_i18nmessageid_message_methods, _zope_i18nmessageid_message_module_documentation); if (m == NULL) return; /* Add types: */ if (PyModule_AddObject(m, "Message", (PyObject *)&MessageType) < 0) return; } zope.i18nmessageid-3.5.3/src/zope/i18nmessageid/message.py0000644000175000017500000001071411430157634022705 0ustar chrismchrism############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """I18n Messages """ __docformat__ = "reStructuredText" class Message(unicode): """Message (Python implementation) This is a string used as a message. It has a domain attribute that is its source domain, and a default attribute that is its default text to display when there is no translation. domain may be None meaning there is no translation domain. default may also be None, in which case the message id itself implicitly serves as the default text. These are the doc tests from message.txt. Note that we have to create the message manually since MessageFactory would return the C implementation. >>> from zope.i18nmessageid.message import Message >>> robot = Message(u"robot-message", 'futurama', u"${name} is a robot.") >>> robot u'robot-message' >>> isinstance(robot, unicode) True >>> robot.default u'${name} is a robot.' >>> robot.mapping >>> robot.domain = "planetexpress" Traceback (most recent call last): ... TypeError: readonly attribute >>> robot.default = u"${name} is not a robot." Traceback (most recent call last): ... TypeError: readonly attribute >>> robot.mapping = {u'name': u'Bender'} Traceback (most recent call last): ... TypeError: readonly attribute >>> new_robot = Message(robot, mapping={u'name': u'Bender'}) >>> new_robot u'robot-message' >>> new_robot.domain 'futurama' >>> new_robot.default u'${name} is a robot.' >>> new_robot.mapping {u'name': u'Bender'} >>> callable, args = new_robot.__reduce__() >>> callable is Message True >>> args (u'robot-message', 'futurama', u'${name} is a robot.', {u'name': u'Bender'}) >>> fembot = Message(u'fembot') >>> callable, args = fembot.__reduce__() >>> callable is Message True >>> args (u'fembot', None, None, None) Check if pickling and unpickling works >>> from pickle import dumps, loads >>> pystate = dumps(new_robot) >>> pickle_bot = loads(pystate) >>> pickle_bot, pickle_bot.domain, pickle_bot.default, pickle_bot.mapping (u'robot-message', 'futurama', u'${name} is a robot.', {u'name': u'Bender'}) >>> pickle_bot.__reduce__()[0] is Message True """ __slots__ = ('domain', 'default', 'mapping', '_readonly') def __new__(cls, ustr, domain=None, default=None, mapping=None): self = unicode.__new__(cls, ustr) if isinstance(ustr, self.__class__): domain = ustr.domain and ustr.domain[:] or domain default = ustr.default and ustr.default[:] or default mapping = ustr.mapping and ustr.mapping.copy() or mapping ustr = unicode(ustr) self.domain = domain if default is None: # MessageID does: self.default = ustr self.default = default else: self.default = unicode(default) self.mapping = mapping self._readonly = True return self def __setattr__(self, key, value): """Message is immutable It cannot be changed once the message id is created. """ if getattr(self, '_readonly', False): raise TypeError('readonly attribute') else: return unicode.__setattr__(self, key, value) def __reduce__(self): return self.__class__, self.__getstate__() def __getstate__(self): return unicode(self), self.domain, self.default, self.mapping # save a copy for the unit tests pyMessage = Message try: from _zope_i18nmessageid_message import Message except ImportError: # pragma: no cover pass class MessageFactory(object): """Factory for creating i18n messages.""" def __init__(self, domain): self._domain = domain def __call__(self, ustr, default=None, mapping=None): return Message(ustr, self._domain, default, mapping) zope.i18nmessageid-3.5.3/src/zope/i18nmessageid/__init__.py0000644000175000017500000000147311430157634023022 0ustar chrismchrism############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """I18n Messages """ from zope.i18nmessageid.message import Message, MessageFactory # import this as _ to create i18n messages in the zope domain ZopeMessageFactory = MessageFactory('zope') zope.i18nmessageid-3.5.3/src/zope/i18nmessageid/messages.txt0000644000175000017500000000771611430157634023267 0ustar chrismchrism============= I18n Messages ============= Rationale --------- To translate any text, we must be able to discover the source domain of the text. A source domain is an identifier that identifies a project that produces program source strings. Source strings occur as literals in python programs, text in templates, and some text in XML data. The project implies a source language and an application context. We can think of a source domain as a collection of messages and associated translation strings. We often need to create unicode strings that will be displayed by separate views. The view cannot translate the string without knowing its source domain. A string or unicode literal carries no domain information, therefore we use messages. Messages are unicode strings which carry a translation source domain and possibly a default translation. They are created by a message factory. The message factory is created by calling ``MessageFactory`` with the source domain. ZopeMessageFactory ------------------ >>> from zope.i18nmessageid import ZopeMessageFactory as _z_ >>> foo = _z_('foo') >>> foo.domain 'zope' Example ------- In this example, we create a message factory and assign it to _. By convention, we use _ as the name of our factory to be compatible with translatable string extraction tools such as xgettext. We then call _ with a string that needs to be translatable: >>> from zope.i18nmessageid import MessageFactory, Message >>> _ = MessageFactory("futurama") >>> robot = _(u"robot-message", u"${name} is a robot.") Messages at first seem like they are unicode strings: >>> robot u'robot-message' >>> isinstance(robot, unicode) True The additional domain, default and mapping information is available through attributes: >>> robot.default u'${name} is a robot.' >>> robot.mapping >>> robot.domain 'futurama' The message's attributes are considered part of the immutable message object. They cannot be changed once the message id is created: >>> robot.domain = "planetexpress" Traceback (most recent call last): ... TypeError: readonly attribute >>> robot.default = u"${name} is not a robot." Traceback (most recent call last): ... TypeError: readonly attribute >>> robot.mapping = {u'name': u'Bender'} Traceback (most recent call last): ... TypeError: readonly attribute If you need to change their information, you'll have to make a new message id object: >>> new_robot = Message(robot, mapping={u'name': u'Bender'}) >>> new_robot u'robot-message' >>> new_robot.domain 'futurama' >>> new_robot.default u'${name} is a robot.' >>> new_robot.mapping {u'name': u'Bender'} Last but not least, messages are reduceable for pickling: >>> callable, args = new_robot.__reduce__() >>> callable is Message True >>> args (u'robot-message', 'futurama', u'${name} is a robot.', {u'name': u'Bender'}) >>> fembot = Message(u'fembot') >>> callable, args = fembot.__reduce__() >>> callable is Message True >>> args (u'fembot', None, None, None) Message IDs and backward compatability -------------------------------------- The change to immutability is not a simple refactoring that can be coped with backward compatible APIs--it is a change in semantics. Because immutability is one of those "you either have it or you don't" things (like pregnancy or death), we will not be able to support both in one implementation. The proposed solution for backward compatability is to support both implementations in parallel, deprecating the mutable one. A separate factory, ``MessageFactory``, instantiates immutable messages, while the deprecated old one continues to work like before. The roadmap to immutable-only message ids is proposed as follows: Zope 3.1: Immutable message ids are introduced. Security declarations for mutable message ids are provided to make the stripping of security proxies unnecessary. Zope 3.2: Mutable message ids are deprecated. Zope 3.3: Mutable message ids are removed. zope.i18nmessageid-3.5.3/src/zope/__init__.py0000644000175000017500000000007011430157634020351 0ustar chrismchrism__import__('pkg_resources').declare_namespace(__name__) zope.i18nmessageid-3.5.3/PKG-INFO0000644000175000017500000002333711430160067015576 0ustar chrismchrismMetadata-Version: 1.0 Name: zope.i18nmessageid Version: 3.5.3 Summary: Message Identifiers for internationalization Home-page: http://pypi.python.org/pypi/zope.i18nmessageid Author: Zope Foundation and Contributors Author-email: zope-dev@zope.org License: ZPL 2.1 Description: To translate any text, we must be able to discover the source domain of the text. A source domain is an identifier that identifies a project that produces program source strings. Source strings occur as literals in python programs, text in templates, and some text in XML data. The project implies a source language and an application context. We can think of a source domain as a collection of messages and associated translation strings. We often need to create unicode strings that will be displayed by separate views. The view cannot translate the string without knowing its source domain. A string or unicode literal carries no domain information, therefore we use messages. Messages are unicode strings which carry a translation source domain and possibly a default translation. They are created by a message factory. The message factory is created by calling ``MessageFactory`` with the source domain. This package provides facilities for *declaring* such messages within program source text; translation of the messages is the responsiblitiy of the 'zope.i18n' package. .. contents:: ============= I18n Messages ============= Rationale --------- To translate any text, we must be able to discover the source domain of the text. A source domain is an identifier that identifies a project that produces program source strings. Source strings occur as literals in python programs, text in templates, and some text in XML data. The project implies a source language and an application context. We can think of a source domain as a collection of messages and associated translation strings. We often need to create unicode strings that will be displayed by separate views. The view cannot translate the string without knowing its source domain. A string or unicode literal carries no domain information, therefore we use messages. Messages are unicode strings which carry a translation source domain and possibly a default translation. They are created by a message factory. The message factory is created by calling ``MessageFactory`` with the source domain. ZopeMessageFactory ------------------ >>> from zope.i18nmessageid import ZopeMessageFactory as _z_ >>> foo = _z_('foo') >>> foo.domain 'zope' Example ------- In this example, we create a message factory and assign it to _. By convention, we use _ as the name of our factory to be compatible with translatable string extraction tools such as xgettext. We then call _ with a string that needs to be translatable: >>> from zope.i18nmessageid import MessageFactory, Message >>> _ = MessageFactory("futurama") >>> robot = _(u"robot-message", u"${name} is a robot.") Messages at first seem like they are unicode strings: >>> robot u'robot-message' >>> isinstance(robot, unicode) True The additional domain, default and mapping information is available through attributes: >>> robot.default u'${name} is a robot.' >>> robot.mapping >>> robot.domain 'futurama' The message's attributes are considered part of the immutable message object. They cannot be changed once the message id is created: >>> robot.domain = "planetexpress" Traceback (most recent call last): ... TypeError: readonly attribute >>> robot.default = u"${name} is not a robot." Traceback (most recent call last): ... TypeError: readonly attribute >>> robot.mapping = {u'name': u'Bender'} Traceback (most recent call last): ... TypeError: readonly attribute If you need to change their information, you'll have to make a new message id object: >>> new_robot = Message(robot, mapping={u'name': u'Bender'}) >>> new_robot u'robot-message' >>> new_robot.domain 'futurama' >>> new_robot.default u'${name} is a robot.' >>> new_robot.mapping {u'name': u'Bender'} Last but not least, messages are reduceable for pickling: >>> callable, args = new_robot.__reduce__() >>> callable is Message True >>> args (u'robot-message', 'futurama', u'${name} is a robot.', {u'name': u'Bender'}) >>> fembot = Message(u'fembot') >>> callable, args = fembot.__reduce__() >>> callable is Message True >>> args (u'fembot', None, None, None) Message IDs and backward compatability -------------------------------------- The change to immutability is not a simple refactoring that can be coped with backward compatible APIs--it is a change in semantics. Because immutability is one of those "you either have it or you don't" things (like pregnancy or death), we will not be able to support both in one implementation. The proposed solution for backward compatability is to support both implementations in parallel, deprecating the mutable one. A separate factory, ``MessageFactory``, instantiates immutable messages, while the deprecated old one continues to work like before. The roadmap to immutable-only message ids is proposed as follows: Zope 3.1: Immutable message ids are introduced. Security declarations for mutable message ids are provided to make the stripping of security proxies unnecessary. Zope 3.2: Mutable message ids are deprecated. Zope 3.3: Mutable message ids are removed. ======= CHANGES ======= 3.5.3 (2010-08-10) ------------------ - Made compilation of C extension optional again; 3.5.1 broke this inasmuch as this package become unusable on non-CPython platforms. Making the compilation of the C extension optional again implied removing ``setup.py`` code added in 3.5.1 which made the C extension a setuptools "Feature" and readding code from 3.5.0 which overrides the distutils ``build_ext`` command. - Move pickle equality tests into a unittest.TestCase test to make it easier to condition the tests on whether the C extension has been compiled. This also makes the tests pass on Jython. 3.5.2 (2010-04-30) ------------------ - Removed use of 'zope.testing.doctestunit' in favor of stdlib's 'doctest. 3.5.1 (2010-04-10) ------------------ - LP #257657 / 489529: Fix memory leak in C extension. - Fixed the compilation of the C extension with python 2.6: refactored it as a setuptools Feature. 3.5.0 (2009-06-27) ------------------ - Made compilation of C extension optional. - Added support to bootstrap on Jython. - Changed package's mailing list address from zope3-dev at zope.org to zope-dev at zope.org, because zope3-dev is now retired. - Reformatted change log to common formatting style. - Update package description and docs a little. - Remove old .cfg files for zpkg. 3.4.3 (2007-09-26) ------------------ - Make PyPI the home URL. 3.4.2 (2007-09-25) ------------------ - Moved the ``ZopeMessageFactory`` from ``zope.app.i18n`` to this package. 3.4.0 (2007-07-19) ------------------ - Remove incorrect dependency. - Create final release to reflect package status. 3.2.0 (2006-01-05) ------------------ - Corresponds to the verison of the zope.i18nmessageid package shipped as part of the Zope 3.2.0 release. - Implemented 'zope.i18nmessageid.message' as a C extension. - Deprecated 'zope.i18nmessageid.messageid' APIs ('MessageID', 'MessageIDFactory') in favor of replacements in 'zope.i18nmessageid.message' ('Message', 'MessageFactory'). Deprecated items are scheduled for removal in Zope 3.3. 3.0.0 (2004-11-07) ------------------ - Corresponds to the verison of the zope.i18nmessageid package shipped as part of the Zope X3.0.0 release. Keywords: zope i18n message factory Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Zope Public License Classifier: Programming Language :: Python Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Topic :: Internet :: WWW/HTTP Classifier: Framework :: Zope3 zope.i18nmessageid-3.5.3/bootstrap.py0000644000175000017500000000733011430157634017071 0ustar chrismchrism############################################################################## # # Copyright (c) 2006 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Bootstrap a buildout-based project Simply run this script in a directory containing a buildout.cfg. The script accepts buildout command-line options, so you can use the -c option to specify an alternate configuration file. """ import os, shutil, sys, tempfile, urllib2 from optparse import OptionParser tmpeggs = tempfile.mkdtemp() is_jython = sys.platform.startswith('java') # parsing arguments parser = OptionParser() parser.add_option("-v", "--version", dest="version", help="use a specific zc.buildout version") parser.add_option("-d", "--distribute", action="store_true", dest="distribute", default=False, help="Use Disribute rather than Setuptools.") parser.add_option("-c", None, action="store", dest="config_file", help=("Specify the path to the buildout configuration " "file to be used.")) options, args = parser.parse_args() # if -c was provided, we push it back into args for buildout' main function if options.config_file is not None: args += ['-c', options.config_file] if options.version is not None: VERSION = '==%s' % options.version else: VERSION = '' USE_DISTRIBUTE = options.distribute args = args + ['bootstrap'] to_reload = False try: import pkg_resources if not hasattr(pkg_resources, '_distribute'): to_reload = True raise ImportError except ImportError: ez = {} if USE_DISTRIBUTE: exec urllib2.urlopen('http://python-distribute.org/distribute_setup.py' ).read() in ez ez['use_setuptools'](to_dir=tmpeggs, download_delay=0, no_fake=True) else: exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py' ).read() in ez ez['use_setuptools'](to_dir=tmpeggs, download_delay=0) if to_reload: reload(pkg_resources) else: import pkg_resources if sys.platform == 'win32': def quote(c): if ' ' in c: return '"%s"' % c # work around spawn lamosity on windows else: return c else: def quote (c): return c cmd = 'from setuptools.command.easy_install import main; main()' ws = pkg_resources.working_set if USE_DISTRIBUTE: requirement = 'distribute' else: requirement = 'setuptools' if is_jython: import subprocess assert subprocess.Popen([sys.executable] + ['-c', quote(cmd), '-mqNxd', quote(tmpeggs), 'zc.buildout' + VERSION], env=dict(os.environ, PYTHONPATH= ws.find(pkg_resources.Requirement.parse(requirement)).location ), ).wait() == 0 else: assert os.spawnle( os.P_WAIT, sys.executable, quote (sys.executable), '-c', quote (cmd), '-mqNxd', quote (tmpeggs), 'zc.buildout' + VERSION, dict(os.environ, PYTHONPATH= ws.find(pkg_resources.Requirement.parse(requirement)).location ), ) == 0 ws.add_entry(tmpeggs) ws.require('zc.buildout' + VERSION) import zc.buildout.buildout zc.buildout.buildout.main(args) shutil.rmtree(tmpeggs) zope.i18nmessageid-3.5.3/setup.cfg0000644000175000017500000000007311430160067016312 0ustar chrismchrism[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 zope.i18nmessageid-3.5.3/COPYRIGHT.txt0000644000175000017500000000004011430157634016602 0ustar chrismchrismZope Foundation and Contributorszope.i18nmessageid-3.5.3/buildout.cfg0000644000175000017500000000027311430157634017011 0ustar chrismchrism[buildout] develop = . parts = test python [test] recipe = zc.recipe.testrunner eggs = zope.i18nmessageid [python] recipe = zc.recipe.egg eggs = zope.i18nmessageid interpreter = python