[Zope3-checkins] CVS: Zope3/src/zope/app/translation_files -
extract.py:1.3 zope.pot:1.3
Stephan Richter
srichter at cosmos.phy.tufts.edu
Mon Aug 4 16:20:36 EDT 2003
Update of /cvs-repository/Zope3/src/zope/app/translation_files
In directory cvs.zope.org:/tmp/cvs-serv29205
Modified Files:
extract.py zope.pot
Log Message:
extract.py can now get all message ids from every file. Updated zope.pot
accordingly.
Gals and guys, it is time to I18nize Zope 3!
=== Zope3/src/zope/app/translation_files/extract.py 1.2 => 1.3 ===
--- Zope3/src/zope/app/translation_files/extract.py:1.2 Mon Aug 4 07:11:55 2003
+++ Zope3/src/zope/app/translation_files/extract.py Mon Aug 4 15:20:31 2003
@@ -17,14 +17,186 @@
$Id$
"""
-
import os, sys, fnmatch
-import pygettext
+import time
+import tokenize
+import traceback
+from pygettext import safe_eval, normalize, make_escapes
+
+
+__meta_class__ = type
+
+pot_header = '''\
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR ORGANIZATION
+# FIRST AUTHOR <EMAIL at ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\\n"
+"POT-Creation-Date: %(time)s\\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n"
+"Last-Translator: FULL NAME <EMAIL at ADDRESS>\\n"
+"Language-Team: LANGUAGE <LL at li.org>\\n"
+"MIME-Version: 1.0\\n"
+"Content-Type: text/plain; charset=CHARSET\\n"
+"Content-Transfer-Encoding: ENCODING\\n"
+"Generated-By: Zope 3 %(version)s\\n"
+'''
+
+class POTEntry:
+ """This class represents a single message entry in the POT file."""
+
+ def __init__(self, msgid, comments=None):
+ self.msgid = msgid
+ self.comments = comments or ''
+
+ def addComment(self, comment):
+ self.comments += comment + '\n'
+
+ def addLocationComment(self, filename, line):
+ self.comments += '#: %s:%s\n' %(filename, line)
+
+ def write(self, file):
+ file.write(self.comments)
+ file.write('msgid %s' % self.msgid)
+ file.write('msgstr ""\n')
+ file.write('\n')
+
+ def __cmp__(self, other):
+ return cmp(self.comments, other.comments)
+
+
+class POTMaker:
+ """This class inserts sets of strings into a POT file."""
+
+ def __init__ (self, output_fn):
+ self._output_filename = output_fn
+ self.catalog = {}
+
+
+ def add(self, strings, base_dir=None):
+ for msgid, locations in strings.items():
+ msgid = '"%s"\n' %msgid
+ if msgid not in self.catalog:
+ self.catalog[msgid] = POTEntry(msgid)
+
+ for filename, lineno in locations:
+ if base_dir is not None:
+ filename = filename.replace(base_dir, '')
+ self.catalog[msgid].addLocationComment(filename, lineno)
+
+
+ def write(self):
+ file = open(self._output_filename, 'w')
+ file.write(pot_header % {'time': time.ctime(),
+ 'version': '0.1'})
+
+ # Sort the catalog entries by filename
+ catalog = self.catalog.values()
+ catalog.sort()
+
+ # Write each entry to the file
+ for entry in catalog:
+ entry.write(file)
+
+ file.close()
+
+
+class TokenEater:
+ """This is almost 100% taken from pygettext.py, except that I removed all
+ option handling and output a dictionary."""
+
+ def __init__(self):
+ self.__messages = {}
+ self.__state = self.__waiting
+ self.__data = []
+ self.__lineno = -1
+ self.__freshmodule = 1
+ self.__curfile = None
+
+ def __call__(self, ttype, tstring, stup, etup, line):
+ self.__state(ttype, tstring, stup[0])
+
+ def __waiting(self, ttype, tstring, lineno):
+ if ttype == tokenize.NAME and tstring in ['_']:
+ self.__state = self.__keywordseen
+
+ def __suiteseen(self, ttype, tstring, lineno):
+ # ignore anything until we see the colon
+ if ttype == tokenize.OP and tstring == ':':
+ self.__state = self.__suitedocstring
+
+ def __suitedocstring(self, ttype, tstring, lineno):
+
+ # ignore any intervening noise
+ if ttype == tokenize.STRING:
+ self.__addentry(safe_eval(tstring), lineno, isdocstring=1)
+ self.__state = self.__waiting
+ elif ttype not in (tokenize.NEWLINE, tokenize.INDENT,
+ tokenize.COMMENT):
+ # there was no class docstring
+ self.__state = self.__waiting
+
+ def __keywordseen(self, ttype, tstring, lineno):
+ if ttype == tokenize.OP and tstring == '(':
+ self.__data = []
+ self.__lineno = lineno
+ self.__state = self.__openseen
+ else:
+ self.__state = self.__waiting
+
+ def __openseen(self, ttype, tstring, lineno):
+ if ttype == tokenize.OP and tstring == ')':
+ # We've seen the last of the translatable strings. Record the
+ # line number of the first line of the strings and update the list
+ # of messages seen. Reset state for the next batch. If there
+ # were no strings inside _(), then just ignore this entry.
+ if self.__data:
+ self.__addentry(''.join(self.__data))
+ self.__state = self.__waiting
+ elif ttype == tokenize.STRING:
+ self.__data.append(safe_eval(tstring))
+
+ def __addentry(self, msg, lineno=None, isdocstring=0):
+ if lineno is None:
+ lineno = self.__lineno
+
+ entry = (self.__curfile, lineno)
+ self.__messages.setdefault(msg, {})[entry] = isdocstring
+
+ def set_filename(self, filename):
+ self.__curfile = filename
+ self.__freshmodule = 1
+
+ def getCatalog(self):
+ catalog = {}
+ # Sort the entries. First sort each particular entry's keys, then
+ # sort all the entries by their first item.
+ reverse = {}
+ for k, v in self.__messages.items():
+ keys = v.keys()
+ keys.sort()
+ reverse.setdefault(tuple(keys), []).append((k, v))
+ rkeys = reverse.keys()
+ rkeys.sort()
+ for rkey in rkeys:
+ rentries = reverse[rkey]
+ rentries.sort()
+ for msgid, locations in rentries:
+ # Normalize and take off surrounding quotes (we do that later)
+ msgid = normalize(msgid)[1:-1]
+ catalog[msgid] = []
+
+ locations = locations.keys()
+ locations.sort()
+ for filename, lineno in locations:
+ catalog[msgid].append((filename, lineno))
-usage = """python extract.py [files]
-"""
+ return catalog
+
def app_dir():
try:
import zope.app
@@ -45,9 +217,8 @@
return dir
-def find_files(dir, pattern, exclude=()):
-
+def find_files(dir, pattern, exclude=()):
files = []
def visit(files, dirname, names):
@@ -59,32 +230,78 @@
return files
+
+def py_strings(dir, domain="zope"):
+ """Retrieve all Python messages from dir that are in the domain."""
+ eater = TokenEater()
+ make_escapes(0)
+ for filename in find_files(dir, '*.py'):
+ fp = open(filename)
+ try:
+ eater.set_filename(filename)
+ try:
+ tokenize.tokenize(fp.readline, eater)
+ except tokenize.TokenError, e:
+ print >> sys.stderr, '%s: %s, line %d, column %d' % (
+ e[0], filename, e[1][0], e[1][1])
+ finally:
+ fp.close()
+ # XXX: No support for domains yet :(
+ return eater.getCatalog()
+
+
def zcml_strings(dir, domain="zope"):
+ """Retrieve all ZCML messages from dir that are in the domain."""
from zope.app._app import config
dirname = os.path.dirname
site_zcml = os.path.join(dirname(dirname(dirname(dir))), "site.zcml")
context = config(site_zcml, execute=False)
return context.i18n_strings.get(domain, {})
-def main(argv=sys.argv):
- dir = app_dir()
-
- strings = zcml_strings(dir)
-
- # OK, now what. I need someone who knows POT files.
- # Stephan, heeeeelp.
- print strings
-
- sys.argv[1:] = ['-ozope.pot',]+find_files(dir, '*.py',
- exclude=["pygettext.py"])
- pygettext.main()
+def tal_strings(dir, domain="zope"):
+ """Retrieve all TAL messages from dir that are in the domain."""
# We import zope.tal.talgettext here because we can't rely on the
# right sys path until app_dir has run
- from zope.tal import talgettext
+ from zope.tal.talgettext import POEngine, POTALInterpreter
+ from zope.tal.htmltalparser import HTMLTALParser
+ engine = POEngine()
+
+ class Devnull:
+ def write(self, s):
+ pass
+
+ for filename in find_files(dir, '*.pt'):
+ try:
+ engine.file = filename
+ p = HTMLTALParser()
+ p.parseFile(filename)
+ program, macros = p.getCode()
+ POTALInterpreter(program, macros, engine, stream=Devnull(),
+ metal=False)()
+ except: # Hee hee, I love bare excepts!
+ print 'There was an error processing', filename
+ traceback.print_exc()
+
+ # We do not want column numbers.
+ catalog = engine.catalog
+ for msgid, locations in catalog.items():
+ catalog[msgid] = map(lambda l: (l[0], l[1][0]), locations)
+ # XXX: No support for domains yet :(
+ return catalog
- sys.argv[1:] = ['-uzope.pot', '-ozope.pot',]+find_files(dir, '*.pt')
- talgettext.main()
+
+def main(argv=sys.argv):
+ dir = app_dir()
+ # Wehn generating the comments, we will not need the base directory info,
+ # since it is specific to everyone's installation
+ base_dir = dir.replace('zope/app', '')
+
+ maker = POTMaker('zope.pot')
+ maker.add(py_strings(dir), base_dir)
+ maker.add(zcml_strings(dir), base_dir)
+ maker.add(tal_strings(dir), base_dir)
+ maker.write()
if __name__ == '__main__':
=== Zope3/src/zope/app/translation_files/zope.pot 1.2 => 1.3 === (1227/1627 lines abridged)
--- Zope3/src/zope/app/translation_files/zope.pot:1.2 Thu Apr 3 17:05:35 2003
+++ Zope3/src/zope/app/translation_files/zope.pot Mon Aug 4 15:20:31 2003
@@ -5,190 +5,1534 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
-"POT-Creation-Date: Thu Apr 3 16:31:26 2003\n"
+"POT-Creation-Date: Mon Aug 4 15:11:20 2003\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
"Language-Team: LANGUAGE <LL at li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: ENCODING\n"
-"Generated-By: pygettext.py 1.4\n"
+"Generated-By: Zope 3 0.1\n"
+#: /home/srichter/Zope3/Zope3-Fresh/site.zcml:8
+msgid "Site Manager"
+msgstr ""
+
+#: /home/srichter/Zope3/Zope3-Fresh/site.zcml:9
+msgid "Site Member"
+msgstr ""
+
+#: zope/app/browser/applicationcontrol/configure.zcml:14
+msgid "Server Control"
+msgstr ""
+
+#: zope/app/browser/applicationcontrol/configure.zcml:19
+msgid "Runtime Information"
+msgstr ""
+
+#: zope/app/browser/applicationcontrol/configure.zcml:28
+msgid "ZODB Control"
+msgstr ""
+
+#: zope/app/browser/applicationcontrol/runtimeinfo.pt:10
+msgid "Python version:"
+msgstr ""
+
+#: zope/app/browser/applicationcontrol/runtimeinfo.pt:11
+msgid "System platform:"
+msgstr ""
+
+#: zope/app/browser/applicationcontrol/runtimeinfo.pt:12
+msgid "Command line:"
+msgstr ""
+
+#: zope/app/browser/applicationcontrol/runtimeinfo.pt:13
+msgid "Process id:"
+msgstr ""
+
+#: zope/app/browser/applicationcontrol/runtimeinfo.pt:14
+msgid "Uptime:"
+msgstr ""
+
+#: zope/app/browser/applicationcontrol/runtimeinfo.pt:15
+msgid "Python path:"
+msgstr ""
+
+#: zope/app/browser/applicationcontrol/runtimeinfo.pt:3
+msgid "Zope Runtime Information"
+msgstr ""
+
+#: zope/app/browser/applicationcontrol/runtimeinfo.pt:9
+msgid "Zope version:"
+msgstr ""
+
+#: zope/app/browser/applicationcontrol/server-control.pt:10
+msgid "Shutdown server"
+msgstr ""
+
+#: zope/app/browser/applicationcontrol/server-control.pt:3
+msgid "Zope Stub Server Controller"
+msgstr ""
+
+#: zope/app/browser/applicationcontrol/server-control.pt:9
+msgid "Restart server"
+msgstr ""
+
+#: zope/app/browser/applicationcontrol/servercontrol.py:36
+msgid "You restarted the server."
+msgstr ""
+
+#: zope/app/browser/applicationcontrol/servercontrol.py:39
+msgid "You shut down the server."
+msgstr ""
+
+#: zope/app/browser/applicationcontrol/zodbcontrol.py:46
+msgid "ZODB successfully packed."
+msgstr ""
+
+#: zope/app/browser/cache/cacheable.py:60
+msgid "Invalidated."
+msgstr ""
+
+#: zope/app/browser/cache/cacheable.py:62
+msgid "No cache associated with object."
+msgstr ""
+
+#: zope/app/browser/cache/cacheable.py:73
+msgid "Saved changes."
+msgstr ""
+
+#: zope/app/browser/cache/cacheableedit.pt:13
+msgid "Currently the object uses ."
+msgstr ""
+
+#: zope/app/browser/cache/cacheableedit.pt:20
+#: zope/app/browser/cache/ramedit.pt:9
+msgid "Errors:"
+msgstr ""
+
+#: zope/app/browser/cache/cacheableedit.pt:34
+msgid "Cache name"
+msgstr ""
+
+#: zope/app/browser/cache/cacheableedit.pt:42
+#: zope/app/browser/cache/ramedit.pt:45
+msgid "Save Changes"
+msgstr ""
+
+#: zope/app/browser/cache/cacheableedit.pt:44
+msgid "Invalidate Cached Value"
+msgstr ""
+
+#: zope/app/browser/cache/cacheableedit.pt:48
+#: zope/app/browser/applicationcontrol/runtimeinfo.pt:21
+#: zope/app/browser/container/commontasks.pt:8
+#: zope/app/browser/form/addwizard.pt:24
+#: zope/app/browser/workflow/instancecontainer_main.pt:52
+msgid ""
+msgstr ""
+
+#: zope/app/browser/cache/cacheableedit.pt:5
+msgid "This edit form allows you to associate a cache with this object."
+msgstr ""
+
+#: zope/app/browser/cache/cacheableedit.pt:7
+msgid "Currently there is no cache associated with the object."
+msgstr ""
+
+#: zope/app/browser/cache/configure.zcml:35
+#: zope/app/browser/content/configure.zcml:17
+#: zope/app/browser/content/configure.zcml:101
+#: zope/app/browser/content/configure.zcml:185
+#: zope/app/browser/services/auth.zcml:53
+#: zope/app/browser/services/pluggableauth/configure.zcml:82
+#: zope/app/browser/services/connection.zcml:68
+#: zope/app/browser/services/module/configure.zcml:8
+#: zope/app/browser/services/utility/configure.zcml:69
+#: zope/app/browser/rdb/configure.zcml:14
+#: zope/app/browser/workflow/configure.zcml:54
+#: zope/app/browser/services/translation/translate.pt:34
+#: zope/app/browser/services/translation/translate.pt:64
+msgid "Edit"
+msgstr ""
+
+#: zope/app/browser/cache/configure.zcml:36
+msgid "Statistics"
+msgstr ""
+
+#: zope/app/browser/cache/configure.zcml:45
+msgid "Caching"
+msgstr ""
+
+#: zope/app/browser/cache/configure.zcml:8
+#: zope/app/browser/cache/configure.zcml:16
+msgid "RAM Cache"
+msgstr ""
+
+#: zope/app/browser/cache/ramedit.pt:20
+msgid "Maximum cached entries"
+msgstr ""
+
+#: zope/app/browser/cache/ramedit.pt:28
+msgid "Maximum age of cached entries"
+msgstr ""
+
+#: zope/app/browser/cache/ramedit.pt:36
+msgid "Time between cache cleanups"
+msgstr ""
+
+#: zope/app/browser/cache/ramedit.pt:46
+msgid "Reset"
+msgstr ""
+
+#: zope/app/browser/cache/ramedit.pt:5
+msgid "You can configure the RAM Cache here."
+msgstr ""
+
+#: zope/app/browser/catalog/advanced.pt:8
+msgid "Advanced Catalog Thingies"
+msgstr ""
+
+#: zope/app/browser/catalog/configure.zcml:11
+#: zope/app/browser/catalog/configure.zcml:11
+#: zope/app/browser/catalog/configure.zcml:22
+msgid "Catalog"
+msgstr ""
[-=- -=- -=- 1227 lines omitted -=- -=- -=-]
-msgid "Select Domains:"
+#: zope/app/interfaces/services/pluggableauth/__init__.py:29
+msgid "Id"
msgstr ""
-#: /home/fdrake/projects/Zope/Zope3-py2.2/src/zope/app/browser/services/translation/exportimport.pt:18
-#: /home/fdrake/projects/Zope/Zope3-py2.2/src/zope/app/browser/services/translation/translate.pt:15
-msgid "Select Languages:"
+#: zope/app/interfaces/services/pluggableauth/__init__.py:31
+msgid "Description"
msgstr ""
-#: /home/fdrake/projects/Zope/Zope3-py2.2/src/zope/app/browser/services/utility/configureutility.pt:15
-msgid "Utility configurations for interface ${interface}"
+#: zope/app/interfaces/services/pluggableauth/__init__.py:32
+msgid "Login"
msgstr ""
-#: /home/fdrake/projects/Zope/Zope3-py2.2/src/zope/app/browser/services/utility/configureutility.pt:11
-msgid "Utility configurations for interface ${interface} with name ${utility_name}"
+#: zope/app/mail/configure.zcml:11
+msgid "Send out mail with arbitrary from and to addresses"
msgstr ""
-#: /home/fdrake/projects/Zope/Zope3-py2.2/src/zope/app/browser/skins/rotterdam/folder_contents.pt:163
-msgid "container_copy_button"
+#: zope/app/onlinehelp/configure.zcml:22
+msgid "Zope UI Help"
msgstr ""
-#: /home/fdrake/projects/Zope/Zope3-py2.2/src/zope/app/browser/skins/rotterdam/folder_contents.pt:160
-msgid "container_cut_button"
+#: zope/app/onlinehelp/configure.zcml:27
+msgid "Welcome"
msgstr ""
-#: /home/fdrake/projects/Zope/Zope3-py2.2/src/zope/app/browser/skins/rotterdam/folder_contents.pt:170
-msgid "container_delete_button"
+#: zope/app/onlinehelp/configure.zcml:33
+msgid "Online help system"
msgstr ""
-#: /home/fdrake/projects/Zope/Zope3-py2.2/src/zope/app/browser/skins/rotterdam/folder_contents.pt:166
-msgid "container_paste_button"
+#: zope/app/security/configure.zcml:14
+msgid "Change security settings"
msgstr ""
-#: /home/fdrake/projects/Zope/Zope3-py2.2/src/zope/app/browser/skins/rotterdam/folder_contents.pt:157
-msgid "container_rename_button"
+#: zope/app/security/configure.zcml:18
+msgid "Manage Content"
msgstr ""
-#: /home/fdrake/projects/Zope/Zope3-py2.2/src/zope/app/browser/services/utility/configureutility.pt:34
-msgid "form_update"
+#: zope/app/security/configure.zcml:22
+msgid "Manage Service Bindings"
+msgstr ""
+
+#: zope/app/security/configure.zcml:26
+msgid "Manage Code"
+msgstr ""
+
+#: zope/app/security/configure.zcml:26
+msgid "Manage executable code, including Python, SQL, ZPT, etc."
+msgstr ""
+
+#: zope/app/security/configure.zcml:31
+msgid "Manage Services"
+msgstr ""
+
+#: zope/app/security/configure.zcml:35
+msgid "Manage the Zope Application, such as Restart/Shutdown or packing the ZODB."
+msgstr ""
+
+#: zope/app/security/configure.zcml:35
+msgid "Manage Application"
+msgstr ""
+
+#: zope/app/services/configure.zcml:109
+msgid "Page Template"
+msgstr ""
+
+#: zope/app/services/configure.zcml:109
+#: zope/app/browser/services/configure.zcml:199
+#: zope/app/browser/services/configure.zcml:207
+msgid "ZPT Template"
+msgstr ""
+
+#: zope/app/services/configure.zcml:289
+msgid "Registration Manager"
+msgstr ""
+
+#: zope/app/services/pagefolder.zcml:10
+msgid "View Folder"
+msgstr ""
+
+#: zope/app/services/view.py:250
+#: zope/app/services/view.py:289
+msgid "(Anything)"
+msgstr ""
+
+#: zope/app/services/view.py:253
+#: zope/app/services/view.py:268
+#: zope/app/security/configure.zcml:10
+msgid "View"
+msgstr ""
+
+#: zope/app/services/view.py:253
+#: zope/app/services/view.py:293
+msgid "for"
+msgstr ""
+
+#: zope/app/services/view.py:255
+#: zope/app/services/view.py:295
+msgid "in layer"
+msgstr ""
+
+#: zope/app/services/view.py:260
+msgid "Registered by ZCML"
+msgstr ""
+
+#: zope/app/services/view.py:305
+msgid "Page"
+msgstr ""
+
+#: zope/app/translation_files/pygettext.py:165
+msgid ""
+"# SOME DESCRIPTIVE TITLE.\n"
+"# Copyright (C) YEAR ORGANIZATION\n"
+"# FIRST AUTHOR <EMAIL at ADDRESS>, YEAR.\n"
+"#\n"
+"msgid \"\"\n"
+"msgstr \"\"\n"
+"\"Project-Id-Version: PACKAGE VERSION\\n\"\n"
+"\"POT-Creation-Date: %(time)s\\n\"\n"
+"\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n"
+"\"Last-Translator: FULL NAME <EMAIL at ADDRESS>\\n\"\n"
+"\"Language-Team: LANGUAGE <LL at li.org>\\n\"\n"
+"\"MIME-Version: 1.0\\n\"\n"
+"\"Content-Type: text/plain; charset=CHARSET\\n\"\n"
+"\"Content-Transfer-Encoding: ENCODING\\n\"\n"
+"\"Generated-By: pygettext.py %(version)s\\n\"\n"
+"\n"
+msgstr ""
+
+#: zope/app/translation_files/pygettext.py:366
+msgid "# File: %(filename)s, line: %(lineno)d"
+msgstr ""
+
+#: zope/app/translation_files/pygettext.py:374
+msgid " %(filename)s:%(lineno)d"
+msgstr ""
+
+#: zope/app/translation_files/pygettext.py:451
+msgid "Invalid value for --style: %s"
+msgstr ""
+
+#: zope/app/translation_files/pygettext.py:459
+msgid "pygettext.py (xgettext for Python) %s"
+msgstr ""
+
+#: zope/app/translation_files/pygettext.py:465
+msgid "--width argument must be an integer: %s"
+msgstr ""
+
+#: zope/app/translation_files/pygettext.py:492
+msgid "Can't read --exclude-file: %s"
+msgstr ""
+
+#: zope/app/translation_files/pygettext.py:503
+msgid "Reading standard input"
+msgstr ""
+
+#: zope/app/translation_files/pygettext.py:508
+msgid "Working on %s"
+msgstr ""
+
+#: zope/app/translation_files/pygettext.py:541
+msgid "a unicode string"
+msgstr ""
+
+#: zope/app/workflow/configure.zcml:13
+msgid "Create Workflow ProcessInstances"
msgstr ""
-#: /home/fdrake/projects/Zope/Zope3-py2.2/src/zope/app/browser/container/main.pt:68
-msgid "menu_delete_button"
+#: zope/app/workflow/configure.zcml:17
+msgid "Use Workflow ProcessInstances"
msgstr ""
-#: /home/fdrake/projects/Zope/Zope3-py2.2/src/zope/app/browser/services/utility/utilities.pt:21
-#: /home/fdrake/projects/Zope/Zope3-py2.2/src/zope/app/browser/services/utility/utilities.pt:28
-msgid "named ${utility_name}"
+#: zope/app/workflow/configure.zcml:9
+msgid "Manage Workflow ProcessDefinitions"
msgstr ""
More information about the Zope3-Checkins
mailing list