[Checkins] SVN: z3c.recipe.i18n/ Added initial implementation

Roger Ineichen roger at projekt01.ch
Wed Apr 30 18:11:58 EDT 2008


Log message for revision 85935:
  Added initial implementation

Changed:
  A   z3c.recipe.i18n/branches/
  A   z3c.recipe.i18n/tags/
  A   z3c.recipe.i18n/trunk/
  A   z3c.recipe.i18n/trunk/CHANGES.txt
  A   z3c.recipe.i18n/trunk/README.txt
  A   z3c.recipe.i18n/trunk/bootstrap.py
  A   z3c.recipe.i18n/trunk/buildout.cfg
  A   z3c.recipe.i18n/trunk/setup.py
  A   z3c.recipe.i18n/trunk/src/
  A   z3c.recipe.i18n/trunk/src/z3c/
  A   z3c.recipe.i18n/trunk/src/z3c/__init__.py
  A   z3c.recipe.i18n/trunk/src/z3c/recipe/
  A   z3c.recipe.i18n/trunk/src/z3c/recipe/__init__.py
  A   z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/
  A   z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/README.txt
  A   z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/__init__.py
  A   z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/i18n.py
  A   z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/i18nextract.py
  A   z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/i18nmergeall.py
  A   z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/i18nstats.py
  A   z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/testing.zcml
  A   z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/tests.py

-=-
Added: z3c.recipe.i18n/trunk/CHANGES.txt
===================================================================
--- z3c.recipe.i18n/trunk/CHANGES.txt	                        (rev 0)
+++ z3c.recipe.i18n/trunk/CHANGES.txt	2008-04-30 22:11:58 UTC (rev 85935)
@@ -0,0 +1,8 @@
+=======
+CHANGES
+=======
+
+Version 0.5.0 (unreleased)
+--------------------------
+
+- Initial Release


Property changes on: z3c.recipe.i18n/trunk/CHANGES.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.recipe.i18n/trunk/README.txt
===================================================================
--- z3c.recipe.i18n/trunk/README.txt	                        (rev 0)
+++ z3c.recipe.i18n/trunk/README.txt	2008-04-30 22:11:58 UTC (rev 85935)
@@ -0,0 +1,2 @@
+This Zope 3 recipes offers different tools which allows to extract i18n 
+translation messages from egg based packages.


Property changes on: z3c.recipe.i18n/trunk/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.recipe.i18n/trunk/bootstrap.py
===================================================================
--- z3c.recipe.i18n/trunk/bootstrap.py	                        (rev 0)
+++ z3c.recipe.i18n/trunk/bootstrap.py	2008-04-30 22:11:58 UTC (rev 85935)
@@ -0,0 +1,56 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Corporation 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.
+
+$Id:$
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+try:
+    import pkg_resources
+except ImportError:
+    ez = {}
+    exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+                         ).read() in ez
+    ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+    import pkg_resources
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+if sys.platform == 'win32':
+    cmd = '"%s"' % cmd # work around spawn lamosity on windows
+
+ws = pkg_resources.working_set
+assert os.spawnle(
+    os.P_WAIT, sys.executable, sys.executable,
+    '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
+    dict(os.environ,
+         PYTHONPATH=
+         ws.find(pkg_resources.Requirement.parse('setuptools')).location
+         ),
+    ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)
+


Property changes on: z3c.recipe.i18n/trunk/bootstrap.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.recipe.i18n/trunk/buildout.cfg
===================================================================
--- z3c.recipe.i18n/trunk/buildout.cfg	                        (rev 0)
+++ z3c.recipe.i18n/trunk/buildout.cfg	2008-04-30 22:11:58 UTC (rev 85935)
@@ -0,0 +1,7 @@
+[buildout]
+parts = test
+develop = .
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = z3c.recipe.i18n [test]

Added: z3c.recipe.i18n/trunk/setup.py
===================================================================
--- z3c.recipe.i18n/trunk/setup.py	                        (rev 0)
+++ z3c.recipe.i18n/trunk/setup.py	2008-04-30 22:11:58 UTC (rev 85935)
@@ -0,0 +1,79 @@
+##############################################################################
+#
+# Copyright (c) 2008 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.
+#
+##############################################################################
+"""Setup
+
+$Id:$
+"""
+import os
+import xml.sax.saxutils
+from setuptools import setup, find_packages
+
+
+def read(*rnames):
+    text = open(os.path.join(os.path.dirname(__file__), *rnames)).read()
+    return xml.sax.saxutils.escape(text)
+
+setup(
+    name = 'z3c.recipe.i18n',
+    version = '0.5.0dev',
+    author = 'Roger Ineichen and the Zope Community',
+    author_email = 'zope-dev at zope.org',
+    description = 'Zope3 development server setup recipes',
+    long_description=(
+        read('README.txt')
+        + '\n\n' +
+        'Detailed Documentation\n'
+        '**********************'
+        + '\n\n' +
+        read('src', 'z3c', 'recipe', 'i18n', 'README.txt')
+        + '\n\n' +
+        read('CHANGES.txt')
+        ),
+    license = 'ZPL 2.1',
+    keywords = 'zope3 z3c i18n locales extraction recipe',
+    classifiers = [
+        'Development Status :: 3 - Alpha',
+        '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'],
+    url = 'http://pypi.python.org/pypi/z3c.recipe.i18n',
+    packages = find_packages('src'),
+    include_package_data = True,
+    package_dir = {'':'src'},
+    namespace_packages = ['z3c', 'z3c.recipe'],
+    extras_require = dict(
+        test = [
+            'zope.testing',
+            'zope.app.locales',
+            ],
+        ),
+    install_requires = [
+        'ZConfig >=2.4a5',
+        'setuptools',
+        'zc.buildout',
+        'zc.recipe.egg',
+        'zope.testing',
+        'zope.configuration',
+        ],
+    entry_points = {
+        'zc.buildout': [
+             'i18n = z3c.recipe.i18n.i18n:I18nSetup',
+         ]
+    },
+)


Property changes on: z3c.recipe.i18n/trunk/setup.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.recipe.i18n/trunk/src/z3c/__init__.py
===================================================================
--- z3c.recipe.i18n/trunk/src/z3c/__init__.py	                        (rev 0)
+++ z3c.recipe.i18n/trunk/src/z3c/__init__.py	2008-04-30 22:11:58 UTC (rev 85935)
@@ -0,0 +1,7 @@
+# this is a namespace package
+try:
+    import pkg_resources
+    pkg_resources.declare_namespace(__name__)
+except ImportError:
+    import pkgutil
+    __path__ = pkgutil.extend_path(__path__, __name__)


Property changes on: z3c.recipe.i18n/trunk/src/z3c/__init__.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.recipe.i18n/trunk/src/z3c/recipe/__init__.py
===================================================================
--- z3c.recipe.i18n/trunk/src/z3c/recipe/__init__.py	                        (rev 0)
+++ z3c.recipe.i18n/trunk/src/z3c/recipe/__init__.py	2008-04-30 22:11:58 UTC (rev 85935)
@@ -0,0 +1,6 @@
+# namespace package boilerplate
+try:
+    __import__('pkg_resources').declare_namespace(__name__)
+except ImportError, e:
+    from pkgutil import extend_path
+    __path__ = extend_path(__path__, __name__)


Property changes on: z3c.recipe.i18n/trunk/src/z3c/recipe/__init__.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/README.txt
===================================================================
--- z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/README.txt	                        (rev 0)
+++ z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/README.txt	2008-04-30 22:11:58 UTC (rev 85935)
@@ -0,0 +1,280 @@
+=============================
+Translation domain extraction
+=============================
+
+z3c.recipe.start
+----------------
+
+This Zope 3 recipes offers different tools which allows to extract i18n 
+translation messages from egg based packages.
+
+The 'i18n' recipe can be used to generate the required scripts for extract
+message ids from egg based packages. The i18nmerge allows to merge them into
+a *.po file. And the i18nstats script gives you an overview about the state
+of the translated files.
+
+Note
+----
+
+This i18nextract.py file uses different semantic for the arguments. The script
+offers to define egg packages instead of one package path. This makes it easy
+to define eggs as source where we extract the messages from.
+
+
+Options
+*******
+
+The 'app' recipe accepts the following options:
+
+eggs
+  The names of one or more eggs, with their dependencies that should
+  be included in the Python path of the generated scripts.
+
+packages
+  The names of one or more eggs which the messages should get extracted from.
+  Note, this is different to the original zope.app.locales implementation.
+  The original implementation uses one path as -d argument which assumes a 
+  specific zope.* package structure with a lod style trunk setup.
+
+domain
+  The translation domain.
+
+output
+  The path of the output file relative to the package root.
+
+maker
+  One or more module name which can get used as additional maker. This module
+  must be located in the python path because it get resolved by
+  zope.configuration.name.resolve. For a sample maker see 
+  z3c.csvvocabulary.csvStrings.
+
+zcml (required)
+  The contents of configuration used for extraction. Normaly used for load 
+  meta configuration.
+
+excludeDefaultDomain (optional, default=False)
+  Exclude all messages found as part of the default domain. Messages are in
+  this domain, if their domain could not be determined. This usually happens
+  in page template snippets. (False if not used)
+
+pythonOnly (optional, default=False)
+  Only extract message ids from Python (False if not used)
+
+exludeDirectoryName (optional, default=[])
+  Allows to specify one or more directory name, relative to the package, to 
+  exclude. (None if not used)
+
+Test
+****
+
+Lets define some (bogus) eggs that we can use in our application:
+
+  >>> mkdir('outputDir')
+  >>> mkdir('demo1')
+  >>> write('demo1', 'setup.py',
+  ... '''
+  ... from setuptools import setup
+  ... setup(name = 'demo1')
+  ... ''')
+
+  >>> mkdir('demo2')
+  >>> write('demo2', 'setup.py',
+  ... '''
+  ... from setuptools import setup
+  ... setup(name = 'demo2', install_requires='demo1')
+  ... ''')
+
+Lets create a minimal `buildout.cfg` file:
+
+  >>> write('buildout.cfg',
+  ... '''
+  ... [buildout]
+  ... parts = i18n
+  ... offline = true
+  ...
+  ... [i18n]
+  ... recipe = z3c.recipe.i18n:i18n
+  ... eggs = z3c.recipe.i18n
+  ... packages = demo1
+  ... domain = recipe
+  ... output = outputDir
+  ... zcml = <include package="z3c.recipe.tests" file="extract.zcml" />"
+  ... ''' % globals())
+
+Now, Let's run the buildout and see what we get:
+
+  >>> print system(join('bin', 'buildout')),
+  Installing i18n.
+  i18n: setting up i18n tools
+  Generated script 'bin\\i18nextract'.
+  Generated script 'bin\\i18nmergeall'.
+  Generated script 'bin\\i18nstats'.
+
+After running buildout, the bin folder contains the different i18n script:
+
+  >>> ls('bin')
+  -  buildout-script.py
+  -  buildout.exe
+  -  i18nextract-script.py
+  -  i18nextract.exe
+  -  i18nmergeall-script.py
+  -  i18nmergeall.exe
+  -  i18nstats-script.py
+  -  i18nstats.exe
+
+
+i18nextract
+-----------
+
+The i18nextract.py contains the following code:
+
+  >>> cat('bin', 'i18nextract-script.py')
+  <BLANKLINE>
+  import sys
+  sys.path[0:0] = [
+    ...
+    ]
+  <BLANKLINE>
+  import z3c.recipe.i18n.i18nextract
+  <BLANKLINE>
+  if __name__ == '__main__':
+      z3c.recipe.i18n.i18nextract.main(['i18nextract', '-d', 'recipe', '-s', '/sample-buildout/parts/i18n/configure.zcml', '-o', '/sample-buildout/outputDir', '-p', 'demo1'])
+
+i18nmergeall
+------------
+
+The i18nmergeall.py contains the following code:
+
+  >>> cat('bin', 'i18nmergeall-script.py')
+  #!C:\Python24\python.exe
+  <BLANKLINE>
+  import sys
+  sys.path[0:0] = [
+    ...
+    ]
+  <BLANKLINE>
+  import z3c.recipe.i18n.i18nmergeall
+  <BLANKLINE>
+  if __name__ == '__main__':
+      z3c.recipe.i18n.i18nmergeall.main(['i18nmergeall', '-l', '...outputDir'])
+
+i18nstats
+---------
+
+The i18nstats.py contains the following code:
+
+  >>> cat('bin', 'i18nstats-script.py')
+  #!C:\Python24\python.exe
+  <BLANKLINE>
+  import sys
+  sys.path[0:0] = [
+    ...
+    ]
+  <BLANKLINE>
+  import z3c.recipe.i18n.i18nstats
+  <BLANKLINE>
+  if __name__ == '__main__':
+      z3c.recipe.i18n.i18nstats.main(['i18nstats', '-l', '...outputDir'])
+
+
+Full Sample
+-----------
+
+Lets create a `buildout.cfg` file using all available arguments:
+
+  >>> write('buildout.cfg',
+  ... '''
+  ... [buildout]
+  ... parts = i18n
+  ... offline = true
+  ...
+  ... [i18n]
+  ... recipe = z3c.recipe.i18n:i18n
+  ... eggs = z3c.recipe.i18n
+  ... packages = demo1
+  ... domain = recipe
+  ... output = outputDir
+  ... zcml = <include package="z3c.recipe.tests" file="extract.zcml" />"
+  ... maker = z3c.csvvocabulary.csvStrings
+  ... excludeDefaultDomain = true
+  ... pythonOnly = true
+  ... exludeDirectoryName = foo
+  ...                       bar
+  ... ''' % globals())
+
+Now, Let's run the buildout and see what we get:
+
+  >>> print system(join('bin', 'buildout')),
+  Uninstalling i18n.
+  Installing i18n.
+  i18n: setting up i18n tools
+  Generated script 'bin\\i18nextract'.
+  Generated script 'bin\\i18nmergeall'.
+  Generated script 'bin\\i18nstats'.
+
+After running buildout, the bin folder contains the different i18n script:
+
+  >>> ls('bin')
+  -  buildout-script.py
+  -  buildout.exe
+  -  i18nextract-script.py
+  -  i18nextract.exe
+  -  i18nmergeall-script.py
+  -  i18nmergeall.exe
+  -  i18nstats-script.py
+  -  i18nstats.exe
+
+
+i18nextract
+-----------
+
+The i18nextract.py contains the following code:
+
+  >>> cat('bin', 'i18nextract-script.py')
+  #!C:\Python24\python.exe
+  <BLANKLINE>
+  import sys
+  sys.path[0:0] = [
+  ...
+    ]
+  <BLANKLINE>
+  import z3c.recipe.i18n.i18nextract
+  <BLANKLINE>
+  if __name__ == '__main__':
+      z3c.recipe.i18n.i18nextract.main(['i18nextract', '-d', 'recipe', '-s', '/sample-buildout/parts/i18n/configure.zcml', '-o', '/sample-buildout/outputDir', '--exclude-default-domain', '--python-only', '-m', 'z3c.csvvocabulary.csvStrings', '-p', 'demo1', '-x', 'foo', '-x', 'bar'])
+
+i18nmergeall
+------------
+
+The i18nmergeall.py contains the following code:
+
+  >>> cat('bin', 'i18nmergeall-script.py')
+  #!C:\Python24\python.exe
+  <BLANKLINE>
+  import sys
+  sys.path[0:0] = [
+  ...
+    ]
+  <BLANKLINE>
+  import z3c.recipe.i18n.i18nmergeall
+  <BLANKLINE>
+  if __name__ == '__main__':
+      z3c.recipe.i18n.i18nmergeall.main(['i18nmergeall', '-l', '...outputDir'])
+
+i18nstats
+---------
+
+The i18nstats.py contains the following code:
+
+  >>> cat('bin', 'i18nstats-script.py')
+  #!C:\Python24\python.exe
+  <BLANKLINE>
+  import sys
+  sys.path[0:0] = [
+  ...
+    ]
+  <BLANKLINE>
+  import z3c.recipe.i18n.i18nstats
+  <BLANKLINE>
+  if __name__ == '__main__':
+      z3c.recipe.i18n.i18nstats.main(['i18nstats', '-l', '...outputDir'])


Property changes on: z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/__init__.py
===================================================================
--- z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/__init__.py	                        (rev 0)
+++ z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/__init__.py	2008-04-30 22:11:58 UTC (rev 85935)
@@ -0,0 +1 @@
+# make a package


Property changes on: z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/__init__.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/i18n.py
===================================================================
--- z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/i18n.py	                        (rev 0)
+++ z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/i18n.py	2008-04-30 22:11:58 UTC (rev 85935)
@@ -0,0 +1,150 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Corporation 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.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = 'restructuredtext'
+
+import os
+import logging
+
+import zc.buildout
+import zc.recipe.egg
+
+import pkg_resources
+
+this_loc = pkg_resources.working_set.find(
+    pkg_resources.Requirement.parse('z3c.recipe.i18n')).location
+
+
+zcmlTemplate = """<configure xmlns='http://namespaces.zope.org/zope'
+           xmlns:meta="http://namespaces.zope.org/meta"
+           >
+
+  %s
+
+</configure>
+"""
+
+
+class I18nSetup(object):
+
+    def __init__(self, buildout, name, options):
+        self.buildout = buildout
+        self.name = name
+        self.options = options
+        if 'eggs' not in self.options:
+            self.options['eggs'] = ''
+        self.options['eggs'] = self.options['eggs'] + '\n' \
+                             + 'zope.app.locales'
+        self.egg = zc.recipe.egg.Egg(buildout, name, options)
+
+    def install(self):
+        logging.getLogger(self.name).info('setting up i18n tools')
+
+        requirements, ws = self.egg.working_set()
+
+        excludeDefaultDomain = self.options.get('excludeDefaultDomain',
+            False)
+
+        pythonOnly = self.options.get('pythonOnly', False) 
+
+        # setup configuration file
+        zcml = self.options.get('zcml', None)
+        if zcml is None:
+            raise zc.buildout.UserError('No zcml configuration defined.')
+        zcml = zcmlTemplate % zcml
+
+        # get domain
+        domain = self.options.get('domain', None)
+        if domain is None:
+            raise zc.buildout.UserError('No domain given.')
+
+        # get output path
+        output = self.options.get('output', None)
+        if output is None:
+            raise zc.buildout.UserError('No output path given.')
+        output = os.path.abspath(output)
+
+        partsDir = os.path.join(
+                self.buildout['buildout']['parts-directory'],
+                self.name,
+                )
+        if not os.path.exists(partsDir):
+            os.mkdir(partsDir)
+        zcmlFilename = os.path.join(partsDir, 'configure.zcml')
+        file(zcmlFilename, 'w').write(zcml)
+
+        # Generate i18nextract
+        arguments = ['%sextract'% self.name,
+                     '-d', domain,
+                     '-s', zcmlFilename,
+                     '-o', output,
+                    ]
+
+        if excludeDefaultDomain:
+            arguments.extend(['--exclude-default-domain'])
+
+        if pythonOnly:
+            arguments.extend(['--python-only'])
+
+        makers = [m for m in self.options.get('maker', '').split() if m!='']
+        for m in makers:
+            arguments.extend(['-m', m])
+
+        # add package names as -p multi option
+        packages = [p for p in self.options.get('packages', '').split()
+                    if p!='']
+        for p in packages:
+            arguments.extend(['-p', p])
+
+        exludeDirNames = [x for x 
+                          in self.options.get('exludeDirectoryName', '').split()
+                          if x!='']
+        for x in exludeDirNames:
+            arguments.extend(['-x', x])
+
+        generated = zc.buildout.easy_install.scripts(
+            [('%sextract'% self.name, 'z3c.recipe.i18n.i18nextract', 'main')],
+            ws, self.options['executable'], 'bin',
+            extra_paths = [this_loc],
+            arguments = arguments,
+            )
+
+        # Generate i18nmergeall
+        arguments = ['%smergeall'% self.name, '-l', output]
+        generated.extend(
+            zc.buildout.easy_install.scripts(
+                [('%smergeall'% self.name,
+                  'z3c.recipe.i18n.i18nmergeall',
+                  'main')],
+                ws, self.options['executable'], 'bin',
+                extra_paths = [this_loc],
+                arguments = arguments,
+            ))
+
+        # Generate i18nstats
+
+        arguments = ['%sstats'% self.name, '-l', output]
+        generated.extend(
+            zc.buildout.easy_install.scripts(
+                [('%sstats'% self.name,
+                  'z3c.recipe.i18n.i18nstats',
+                  'main')],
+                ws, self.options['executable'], 'bin',
+                extra_paths = [this_loc],
+                arguments = arguments,
+            ))
+
+        return generated


Property changes on: z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/i18n.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/i18nextract.py
===================================================================
--- z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/i18nextract.py	                        (rev 0)
+++ z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/i18nextract.py	2008-04-30 22:11:58 UTC (rev 85935)
@@ -0,0 +1,164 @@
+#!/usr/bin/env python2.4
+##############################################################################
+#
+# Copyright (c) 2008 Zope Corporation 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.
+#
+##############################################################################
+"""Program to extract internationalization markup from Python Code,
+Page Templates and ZCML located in egg packages.
+
+This tool will extract all findable message strings from all
+internationalizable files in your defined eggs product. It only extracts 
+message ids of the specified domain. It defaults to the 'z3c' domain and the 
+z3c package whihc use the shared 'z3c' i18n namespace.
+
+Note: The Python Code extraction tool does not support domain
+      registration, so that all message strings are returned for
+      Python code.
+
+Note: The script expects to be executed as a buildout installed script.
+
+Usage: i18nextract.py [options]
+Options:
+    -h / --help
+        Print this message and exit.
+    -d / --domain <domain>
+        Specifies the domain that is supposed to be extracted (i.e. 'z3c')
+    -p / --package <egg>
+        Specifies the egg package that is supposed to be searched
+        (i.e. 'z3c.form')
+    -s / --site_zcml <path>
+        Specify the location of the 'site.zcml' file. By default the regular
+        Zope 3 one is used.
+    -e / --exclude-default-domain
+        Exclude all messages found as part of the default domain. Messages are
+        in this domain, if their domain could not be determined. This usually
+        happens in page template snippets.
+    -m python-function
+        Specify a python function which is added as a maker to the POTMaker.
+    -o dir
+        Specifies the directory path in which to put the output translation
+        template.
+    -x dir
+        Specifies a directory, relative to the package, to exclude. Note this
+        is only a directory name an not a path
+        May be used more than once.
+    --python-only
+        Only extract message ids from Python
+
+$Id:$
+"""
+
+from zope.configuration.name import resolve
+
+import os, sys, getopt
+def usage(code, msg=''):
+    # Python 2.1 required
+    print >> sys.stderr, __doc__
+    if msg:
+        print >> sys.stderr, msg
+    sys.exit(code)
+
+
+def main(argv=sys.argv):
+    try:
+        opts, args = getopt.getopt(
+            argv[1:],
+            'hed:s:i:m:p:o:x:',
+            ['help', 'domain=', 'site_zcml=', 'path=', 'python-only'])
+    except getopt.error, msg:
+        usage(1, msg)
+
+    domain = 'z3c'
+    include_default_domain = True
+    output_dir = None
+    exclude_dirs = []
+    python_only = False
+    site_zcml = None
+    makers = []
+    paths = []
+    for opt, arg in opts:
+        if opt in ('-h', '--help'):
+            usage(0)
+        elif opt in ('-d', '--domain'):
+            domain = arg
+        elif opt in ('-s', '--site_zcml'):
+            site_zcml = arg
+        elif opt in ('-e', '--exclude-default-domain'):
+            include_default_domain = False
+        elif opt in ('-m', ):
+            makers.append(arg)
+        elif opt in ('-o', ):
+            output_dir = arg
+        elif opt in ('-x', ):
+            exclude_dirs.append(arg)
+        elif opt in ('--python-only',):
+            python_only = True
+        elif opt in ('-p', '--package'):
+            package = resolve(arg)
+            path = os.path.dirname(package.__file__)
+            if not os.path.exists(path):
+                usage(1, 'The specified path does not exist.')
+            paths.append(path)
+
+    # When generating the comments, we will not need the base directory info,
+    # since it is specific to everyone's installation
+    src_start = path.rfind('src')
+    base_dir = path[:src_start]
+
+    # setup output file
+    output_file = domain+'.pot'
+    if output_dir:
+        if not os.path.exists(output_dir):
+            os.mkdir(output_dir)
+        output_file = os.path.join(output_dir, output_file)
+
+    print "domain:                 %r\n" \
+          "configuration:          %s\n" \
+          "exclude dirs:           %r\n" \
+          "include default domain: %r\n" \
+          "python only:            %r\n" \
+          % (domain, site_zcml, exclude_dirs, include_default_domain,
+             python_only)
+
+    from zope.app.locales.extract import POTMaker
+    from zope.app.locales.extract import py_strings
+    from zope.app.locales.extract import tal_strings
+    from zope.app.locales.extract import zcml_strings
+
+    # setup pot maker
+    maker = POTMaker(output_file, '')
+
+    # add maker for each given path
+    for path in paths:
+        src_start = path.rfind('src')
+        base_dir = path[:src_start]
+        packagePath = path[len(base_dir):]
+
+        print "package: %r\n" \
+              "base:    %r\n" \
+              "path:    %r\n" \
+              % (packagePath, base_dir, path)
+
+        maker.add(py_strings(path, domain, exclude=exclude_dirs), base_dir)
+        if not python_only:
+            maker.add(zcml_strings(path, domain, site_zcml), base_dir)
+            maker.add(tal_strings(path, domain, include_default_domain,
+                                  exclude=exclude_dirs), base_dir)
+        for m in makers:
+            poMaker = resolve(m)
+            maker.add(poMaker(path, base_dir, exclude_dirs))
+    maker.write()
+    print "output: %r\n" % output_file
+
+
+if __name__ == '__main__':
+    main()


Property changes on: z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/i18nextract.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/i18nmergeall.py
===================================================================
--- z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/i18nmergeall.py	                        (rev 0)
+++ z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/i18nmergeall.py	2008-04-30 22:11:58 UTC (rev 85935)
@@ -0,0 +1,91 @@
+#!/usr/bin/env python2.4
+##############################################################################
+#
+# Copyright (c) 2008 Zope Corporation 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.
+#
+##############################################################################
+"""Merge a POT file with all languages
+
+This utility requires the GNU gettext package to be installed. The command
+'msgmerge' will be executed for each language.
+
+Usage: i18mergeall.py [options]
+Options:
+
+    -h / --help
+        Print this message and exit.
+
+    -l / --locales-dir
+        Specify the 'locales' directory for which to generate the statistics.
+
+$Id:$
+"""
+import sys
+import os
+import getopt
+
+def usage(code, msg=''):
+    """Display help."""
+    print >> sys.stderr, '\n'.join(__doc__.split('\n')[:-2])
+    if msg:
+        print >> sys.stderr, '** Error: ' + str(msg) + ' **'
+    sys.exit(code)
+
+
+def merge(path):
+    for language in os.listdir(path):
+        lc_messages_path = os.path.join(path, language, 'LC_MESSAGES')
+
+        # English is the default for Zope, so ignore it
+        #if language == 'en':
+        #    continue
+
+        # Make sure we got a language directory
+        if not os.path.isdir(lc_messages_path):
+            continue
+
+        msgs = []
+        for domain_file in os.listdir(lc_messages_path):
+            if domain_file.endswith('.po'):
+                domain_path = os.path.join(lc_messages_path, domain_file)
+                pot_path = os.path.join(path, domain_file+'t')
+                domain = domain_file.split('.')[0]
+                print 'Merging language "%s", domain "%s"' %(language, domain)
+                os.system('msgmerge -U %s %s' %(domain_path, pot_path))
+
+
+def main(argv=sys.argv):
+    try:
+        opts, args = getopt.getopt(
+            argv[1:],
+            'l:h',
+            ['help', 'locals-dir='])
+    except getopt.error, msg:
+        usage(1, msg)
+
+    path = None
+    for opt, arg in opts:
+        if opt in ('-h', '--help'):
+            usage(0)
+        elif opt in ('-l', '--locales-dir'):
+            cwd = os.getcwd()
+            # This is for symlinks. Thanks to Fred for this trick.
+            if os.environ.has_key('PWD'):
+                cwd = os.environ['PWD']
+            path = os.path.normpath(os.path.join(cwd, arg))
+
+    if path is None:
+        usage(1, 'You must specify the path to the locales directory.')
+    merge(path)
+
+if __name__ == '__main__':
+    main()
+


Property changes on: z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/i18nmergeall.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/i18nstats.py
===================================================================
--- z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/i18nstats.py	                        (rev 0)
+++ z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/i18nstats.py	2008-04-30 22:11:58 UTC (rev 85935)
@@ -0,0 +1,171 @@
+#!/usr/bin/env python2.4
+##############################################################################
+#
+# Copyright (c) 2008 Zope Corporation 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.
+#
+##############################################################################
+"""Translation Statistics Utility
+
+Utility to determine the status of the translations.
+
+Usage: i18nstats.py [options]
+Options:
+
+    -h / --help
+        Print this message and exit.
+
+    -l / --locales-dir
+        Specify the 'locales' directory for which to generate the statistics.
+
+$Id:$
+"""
+import sys
+import os
+import getopt
+
+SEARCHING = 0
+COMMENT = 1
+MSGID = 2
+MSGSTR = 3
+MSGDONE = 4
+
+def usage(code, msg=''):
+    """Display help."""
+    print >> sys.stderr, '\n'.join(__doc__.split('\n')[:-2])
+    if msg:
+        print >> sys.stderr, '** Error: ' + str(msg) + ' **'
+    sys.exit(code)
+
+
+def getMessageDictionary(file):
+    """Simple state machine."""
+
+    msgs = []
+    comment = []
+    msgid = []
+    msgstr = []
+    fuzzy = False
+    line_counter = 0
+    status = SEARCHING
+
+    for line in file.readlines():
+        line = line.strip('\n')
+        line_counter += 1
+
+        # Handle Events
+        if line.startswith('#'):
+            status = COMMENT
+
+        elif line.startswith('msgid'):
+            line = line[6:] 
+            line_number = line_counter
+            status = MSGID
+
+        elif line.startswith('msgstr'):
+            line = line[7:] 
+            status = MSGSTR
+
+        elif line == '':
+            status = MSGDONE
+
+        # Actions based on status
+        if status == MSGID:
+            msgid.append(line.strip('"'))
+
+        elif status == MSGSTR:
+            msgstr.append(line.strip('"'))
+
+        elif status == COMMENT:
+            if line.startswith('#, fuzzy'):
+                fuzzy = True
+            comment.append(line[1:].strip())
+
+        elif status == MSGDONE:
+            status = SEARCHING
+            # Avoid getting the meta-data message string
+            if ''.join(msgid):
+                msgs.append( (''.join(msgid), ''.join(msgstr),
+                              line_number, '\n'.join(comment), fuzzy) )
+            comment = []
+            msgid = []
+            msgstr = []
+            fuzzy = False
+
+    return msgs
+
+
+def stats(path):
+    print 'Language    Total    Done    Not Done    Fuzzy      Done %'
+    print '=========================================================='
+    languages = os.listdir(path)
+    languages.sort()
+    for language in languages:
+        lc_messages_path = os.path.join(path, language, 'LC_MESSAGES')
+
+        # Make sure we got a language directory
+        if not os.path.isdir(lc_messages_path):
+            continue
+
+        msgs = []
+        for domain_file in os.listdir(lc_messages_path):
+            if domain_file.endswith('.po'):
+                domain_path = os.path.join(lc_messages_path, domain_file)
+                file = open(domain_path, mode='r')
+                msgs += getMessageDictionary(file)
+
+        # We are dealing with the default language, which always has just one
+        # message string for the meta data (which is not recorded). 
+        if len(msgs) == 0:
+            continue
+
+        total = len(msgs)
+        not_done = len([msg for msg in msgs if msg[1] == ''])
+        fuzzy = len([msg for msg in msgs if msg[4] is True])
+        done = total - not_done - fuzzy
+        percent_done = 100.0 * done/total
+
+        line = language + ' '*(8-len(language))
+        line += ' '*(9-len(str(total))) + str(total)
+        line += ' '*(8-len(str(done))) + str(done)
+        line += ' '*(12-len(str(not_done))) + str(not_done)
+        line += ' '*(9-len(str(fuzzy))) + str(fuzzy)
+        pd_str = '%0.2f %%' %percent_done
+        line += ' '*(12-len(pd_str)) + pd_str
+        print line
+    
+
+def main(argv=sys.argv):
+    try:
+        opts, args = getopt.getopt(
+            argv[1:],
+            'l:h',
+            ['help', 'locals-dir='])
+    except getopt.error, msg:
+        usage(1, msg)
+
+    path = None
+    for opt, arg in opts:
+        if opt in ('-h', '--help'):
+            usage(0)
+        elif opt in ('-l', '--locales-dir'):
+            cwd = os.getcwd()
+            # This is for symlinks. Thanks to Fred for this trick.
+            if os.environ.has_key('PWD'):
+                cwd = os.environ['PWD']
+            path = os.path.normpath(os.path.join(cwd, arg))
+
+    if path is None:
+        usage(1, 'You must specify the path to the locales directory.')
+    stats(path)
+
+if __name__ == '__main__':
+    main()
+


Property changes on: z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/i18nstats.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/testing.zcml
===================================================================
--- z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/testing.zcml	                        (rev 0)
+++ z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/testing.zcml	2008-04-30 22:11:58 UTC (rev 85935)
@@ -0,0 +1 @@
+<!-- just for testing -->
\ No newline at end of file


Property changes on: z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/testing.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/tests.py
===================================================================
--- z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/tests.py	                        (rev 0)
+++ z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/tests.py	2008-04-30 22:11:58 UTC (rev 85935)
@@ -0,0 +1,133 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Corporation 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.
+#
+##############################################################################
+
+import os, re
+import pkg_resources
+
+import zc.buildout.testing
+
+import unittest
+import zope.testing
+from zope.testing import doctest, renormalizing
+
+
+def setUp(test):
+    zc.buildout.testing.buildoutSetUp(test)
+    zc.buildout.testing.install_develop('RestrictedPython', test)
+    zc.buildout.testing.install_develop('ZConfig', test)
+    zc.buildout.testing.install_develop('ZODB3', test)
+    zc.buildout.testing.install_develop('pytz', test)
+    zc.buildout.testing.install_develop('z3c.recipe.i18n', test)
+    zc.buildout.testing.install_develop('zc.recipe.egg', test)
+    zc.buildout.testing.install_develop('zdaemon', test)
+    zc.buildout.testing.install_develop('zodbcode', test)
+    zc.buildout.testing.install_develop('zope.annotation', test)
+    zc.buildout.testing.install_develop('zope.app.applicationcontrol', test)
+    zc.buildout.testing.install_develop('zope.app.appsetup', test)
+    zc.buildout.testing.install_develop('zope.app.authentication', test)
+    zc.buildout.testing.install_develop('zope.app.basicskin', test)
+    zc.buildout.testing.install_develop('zope.app.broken', test)
+    zc.buildout.testing.install_develop('zope.app.component', test)
+    zc.buildout.testing.install_develop('zope.app.container', test)
+    zc.buildout.testing.install_develop('zope.app.content', test)
+    zc.buildout.testing.install_develop('zope.app.debug', test)
+    zc.buildout.testing.install_develop('zope.app.dependable', test)
+    zc.buildout.testing.install_develop('zope.app.error', test)
+    zc.buildout.testing.install_develop('zope.app.exception', test)
+    zc.buildout.testing.install_develop('zope.app.folder', test)
+    zc.buildout.testing.install_develop('zope.app.form', test)
+    zc.buildout.testing.install_develop('zope.app.generations', test)
+    zc.buildout.testing.install_develop('zope.app.http', test)
+    zc.buildout.testing.install_develop('zope.app.i18n', test)
+    zc.buildout.testing.install_develop('zope.app.interface', test)
+    zc.buildout.testing.install_develop('zope.app.locales', test)
+    zc.buildout.testing.install_develop('zope.app.pagetemplate', test)
+    zc.buildout.testing.install_develop('zope.app.principalannotation', test)
+    zc.buildout.testing.install_develop('zope.app.publication', test)
+    zc.buildout.testing.install_develop('zope.app.publisher', test)
+    zc.buildout.testing.install_develop('zope.app.renderer', test)
+    zc.buildout.testing.install_develop('zope.app.rotterdam', test)
+    zc.buildout.testing.install_develop('zope.app.schema', test)
+    zc.buildout.testing.install_develop('zope.app.security', test)
+    zc.buildout.testing.install_develop('zope.app.session', test)
+    zc.buildout.testing.install_develop('zope.app.testing', test)
+    zc.buildout.testing.install_develop('zope.app.wsgi', test)
+    zc.buildout.testing.install_develop('zope.app.zapi', test)
+    zc.buildout.testing.install_develop('zope.app.zcmlfiles', test)
+    zc.buildout.testing.install_develop('zope.app.zopeappgenerations', test)
+    zc.buildout.testing.install_develop('zope.cachedescriptors', test)
+    zc.buildout.testing.install_develop('zope.component', test)
+    zc.buildout.testing.install_develop('zope.configuration', test)
+    zc.buildout.testing.install_develop('zope.contenttype', test)
+    zc.buildout.testing.install_develop('zope.copypastemove', test)
+    zc.buildout.testing.install_develop('zope.datetime', test)
+    zc.buildout.testing.install_develop('zope.deferredimport', test)
+    zc.buildout.testing.install_develop('zope.deprecation', test)
+    zc.buildout.testing.install_develop('zope.dottedname', test)
+    zc.buildout.testing.install_develop('zope.dublincore', test)
+    zc.buildout.testing.install_develop('zope.error', test)
+    zc.buildout.testing.install_develop('zope.event', test)
+    zc.buildout.testing.install_develop('zope.exceptions', test)
+    zc.buildout.testing.install_develop('zope.filerepresentation', test)
+    zc.buildout.testing.install_develop('zope.formlib', test)
+    zc.buildout.testing.install_develop('zope.hookable', test)
+    zc.buildout.testing.install_develop('zope.i18n', test)
+    zc.buildout.testing.install_develop('zope.i18nmessageid', test)
+    zc.buildout.testing.install_develop('zope.interface', test)
+    zc.buildout.testing.install_develop('zope.lifecycleevent', test)
+    zc.buildout.testing.install_develop('zope.location', test)
+    zc.buildout.testing.install_develop('zope.minmax', test)
+    zc.buildout.testing.install_develop('zope.modulealias', test)
+    zc.buildout.testing.install_develop('zope.pagetemplate', test)
+    zc.buildout.testing.install_develop('zope.proxy', test)
+    zc.buildout.testing.install_develop('zope.publisher', test)
+    zc.buildout.testing.install_develop('zope.schema', test)
+    zc.buildout.testing.install_develop('zope.security', test)
+    zc.buildout.testing.install_develop('zope.session', test)
+    zc.buildout.testing.install_develop('zope.size', test)
+    zc.buildout.testing.install_develop('zope.structuredtext', test)
+    zc.buildout.testing.install_develop('zope.tal', test)
+    zc.buildout.testing.install_develop('zope.tales', test)
+    zc.buildout.testing.install_develop('zope.testing', test)
+    zc.buildout.testing.install_develop('zope.testing', test)
+    zc.buildout.testing.install_develop('zope.thread', test)
+    zc.buildout.testing.install_develop('zope.traversing', test)
+
+
+checker = renormalizing.RENormalizing([
+    zc.buildout.testing.normalize_path,
+    (re.compile(
+    "Couldn't find index page for '[a-zA-Z0-9.]+' "
+    "\(maybe misspelled\?\)"
+    "\n"
+    ), ''),
+    (re.compile("""['"][^\n"']+z3c.recipe.i18n[^\n"']*['"],"""),
+     "'/z3c.recipe.i18n',"),
+    (re.compile('#![^\n]+\n'), ''),
+    (re.compile('-\S+-py\d[.]\d(-\S+)?.egg'),
+     '-pyN.N.egg',
+    ),
+    ])
+
+
+def test_suite():
+    return unittest.TestSuite(
+        doctest.DocFileSuite('README.txt',
+            setUp=setUp, tearDown=zc.buildout.testing.buildoutTearDown,
+            optionflags=doctest.ELLIPSIS, checker=checker),
+        )
+
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')


Property changes on: z3c.recipe.i18n/trunk/src/z3c/recipe/i18n/tests.py
___________________________________________________________________
Name: svn:eol-style
   + native



More information about the Checkins mailing list