[Zope-Checkins] CVS: Zope3/lib/python/Zope/I18n - GettextMessageCatalog.py:1.1 GlobalTranslationService.py:1.1 SimpleTranslationService.py:1.1 i18n-meta.zcml:1.1 metaConfigure.py:1.1 Domain.py:1.4 IMessageCatalog.py:1.4 MessageCatalog.py:1.3 TranslationService.py:1.4 i18n.zcml:1.3

Stephan Richter srichter@cbu.edu
Wed, 12 Jun 2002 14:39:27 -0400


Update of /cvs-repository/Zope3/lib/python/Zope/I18n
In directory cvs.zope.org:/tmp/cvs-serv9092/I18n

Modified Files:
	Domain.py IMessageCatalog.py MessageCatalog.py 
	TranslationService.py i18n.zcml 
Added Files:
	GettextMessageCatalog.py GlobalTranslationService.py 
	SimpleTranslationService.py i18n-meta.zcml metaConfigure.py 
Log Message:
I finally got around to finish some priliminary Translation Support. This
complements the work done recently by Barry and Fred concerning the PT i18n
namespace. 

SimpleTranslationService: The possibly simplest implementation of a 
  Translation service. It does not do much, but provides a nice base class
  for other translation services and is great for testing. This 
  implementation does not use message catalog for its storage.

TranslationService: This is a local, persistent flavour of the translation
  service. It is used for TTW development. A first GUI for adding and 
  editing (delete does not work yet) messages is also there already. People
  which use ZBabel will find the interface very familiar. 
  This class also implements many, many methods required for the editing via 
  the browser. These methods will be probably split up into a separate 
  interface called IEditableTranslationService.

GlobalTranslationService: This is a placeless version of the translation
  service. It will be used to register translations via the file system and
  ZCML. As default the GettextMessageCatalog is used, whcih reads in 
  gettext files.


MessageCatalog: A persistent implementation to accomedate the 
  TranslationService. It also implements a couple additional methods to
  support updating and deleting of messages.

GettextMessageCatalog: Every catalog basically represents one gettext
  (*.mo) file. This message catalog cannot be altered, as the file is used
  read only. This message catalog is primarily used in combination with 
  GlobalTranslationService.


Domain: This is basically a shortcut object, so that you do not have to 
  specify the domain for a translation every single time. However, I think
  this obejct is not quiet ready yet, as I do not have the use cases on how
  to handle the placefulness well yet.

Negotiator: The language negotiator helps the translation service to 
  determine the target language in case it was not explicitly passed as an
  argument.

gts ZCML namespace: Using the registerTranslations directive you can 
  specify a gettext directory as the source for additional translations.
  These translation will be added to the GlobalTranslationService, where 
  they will be available for usage. Note that the path of the various
  gettext files identify the language and domain unambiguously.


=== Added File Zope3/lib/python/Zope/I18n/GettextMessageCatalog.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
# 
##############################################################################
"""A simple implementation of a Message Catalog. 

$Id: GettextMessageCatalog.py,v 1.1 2002/06/12 18:38:56 srichter Exp $
"""

from gettext import GNUTranslations
from Zope.I18n.IMessageCatalog import IMessageCatalog


class GettextMessageCatalog:
    """ """

    __implements__ =  IMessageCatalog


    def __init__(self, language, domain, path_to_file):
        """Initialize the message catalog"""
        self._language = language
        self._domain = domain
        self._path_to_file = path_to_file
        self.__translation_object = None
        self._prepareTranslations()
    

    def _prepareTranslations(self):
        """ """
        if self.__translation_object is None:
            file = open(self._path_to_file, 'r')
            self.__translation_object = GNUTranslations(file)
            file.close()
            

    ############################################################
    # Implementation methods for interface
    # Zope.I18n.IMessageCatalog.

    def getMessage(self, id):
        'See Zope.I18n.IMessageCatalog.IMessageCatalog'
        self._prepareTranslations()
        msg = self.__translation_object.gettext(id)
        if msg == id:
            raise KeyError
        return msg

    def queryMessage(self, id, default=None):
        'See Zope.I18n.IMessageCatalog.IMessageCatalog'
        self._prepareTranslations()
        text = self.__translation_object.gettext(id)
        if text != id:
            return text
        if default is None:
            default = id
        return default

    def getLanguage(self):
        'See Zope.I18n.IMessageCatalog.IMessageCatalog'
        return self._language
        
    def getDomain(self):
        'See Zope.I18n.IMessageCatalog.IMessageCatalog'
        return self._domain

    def getIdentifier(self):
        'See Zope.I18n.IMessageCatalog.IMessageCatalog'
        return self._path_to_file
        
    #
    ############################################################


=== Added File Zope3/lib/python/Zope/I18n/GlobalTranslationService.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
# 
##############################################################################
"""Global Translation Service for providing I18n to file-based code.

$Id: GlobalTranslationService.py,v 1.1 2002/06/12 18:38:56 srichter Exp $
"""

from Negotiator import negotiator
from SimpleTranslationService import SimpleTranslationService


class GlobalTranslationService(SimpleTranslationService):
    ''' '''

    __implements__ =  SimpleTranslationService.__implements__


    def __init__(self, default_domain='global'):
        ''' '''
        self._catalogs = {}
        self._data = {}


    def _registerMessageCatalog(self, language, domain, catalog_name):
        ''' '''
        if (language, domain) not in self._catalogs.keys():
            self._catalogs[(language, domain)] = []

        mc = self._catalogs[(language, domain)]
        mc.append(catalog_name)


    def addCatalog(self, catalog):
        ''' '''
        self._data[catalog.getIdentifier()] = catalog
        self._registerMessageCatalog(catalog.getLanguage(),
                                     catalog.getDomain(),
                                     catalog.getIdentifier())


    ############################################################
    # Implementation methods for interface
    # Zope.I18n.ITranslationService.

    def translate(self, domain, msgid, mapping=None, context=None,  
                  target_language=None):
        '''See interface ITranslationService'''

        if domain is None:
            domain = self.default_domain

        if target_language is None:
            if context is None:
                raise TypeError, 'No destination language'
            else:
                avail_langs = {}
                for catalog in self._data.values():
                    avail_langs[catalog] = None

                target_language = negotiator.getLanguage(avail_langs.keys(),
                                                         context)

        # Get the translation. Default is the msgid text itself.
        catalog_names = self._catalogs.get((target_language, domain), [])

        text = msgid
        for name in catalog_names:
            catalog = self._data[name]
            try:
                text = catalog.getMessage(msgid)
                break
            except:
                pass

        # Now we need to do the interpolation
        return self.interpolate(text, mapping)

    #
    ############################################################


translationService = GlobalTranslationService()


=== Added File Zope3/lib/python/Zope/I18n/SimpleTranslationService.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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 is a simple implementation of the ITranslationService interface.

$Id: SimpleTranslationService.py,v 1.1 2002/06/12 18:38:56 srichter Exp $
"""

import re
from types import DictType
from Zope.ComponentArchitecture import getService
from ITranslationService import ITranslationService
from Domain import Domain


# Setting up some regular expressions for finding interpolation variables in
# the text.
NAME_RE = r"[a-zA-Z][a-zA-Z0-9_]*"
_interp_regex = re.compile(r'(?<!\$)(\$(?:%(n)s|{%(n)s}))' %({'n': NAME_RE}))
_get_var_regex = re.compile(r'%(n)s' %({'n': NAME_RE}))


class SimpleTranslationService:
    """This is the simplest implementation of the ITranslationInterface I
       could come up with.

       The constructor takes one optional argument 'messages', which will be
       used to do the translation. The 'messages' attribute has to have the
       following structure:

       {('domain', 'language', 'msg_id'): 'message', ...}

       Note: This Translation Service does not implemen
    """

    __implements__ =  ITranslationService


    def __init__(self, messages=None):
        """Initializes the object. No arguments are needed."""
        if messages is None:
            self.messages = {}
        else:
            assert type(messages) == DictType
            self.messages = messages


    ############################################################
    # Implementation methods for interface
    # Zope.I18n.ITranslationService.

    def translate(self, domain, msgid, mapping=None, context=None,  
                  target_language=None):
        '''See interface ITranslationService'''

        # Find out what the target language should be
        if target_language is None:
            if context is None:
                raise TypeError, 'No destination language'
            else:
                avail_langs = map(lambda m: m[2], self.messages.keys())
                # Let's negotiate the language to translate to. :)
                negotiator = getService(self, 'LanguageNegotiation')
                target_language = negotiator.getLanguage(avail_langs, context)

        # Make a raw translation without interpolation
        text = self.messages.get((domain, target_language, msgid), msgid)

        # Now we need to do the interpolation
        return self.interpolate(text, mapping)


    def getDomain(self, domain):
        '''See interface ITranslationService'''
        return Domain(self, domain)

    #
    ############################################################


    def interpolate(self, text, mapping):
        """Insert the data passed from mapping into the text"""

        # If the mapping does not exist, make a "raw translation" without
        # interpolation. 
        if mapping is None:
            return text

        # Find all the spots we want to substitute
        to_replace = _interp_regex.findall(text)

        # Now substitute with the variables in mapping
        for string in to_replace:
            var = _get_var_regex.findall(string)[0]
            text = text.replace(string, mapping.get(var))

        return text


=== Added File Zope3/lib/python/Zope/I18n/i18n-meta.zcml ===
<zopeConfigure xmlns='http://namespaces.zope.org/zope'>

  <directives namespace="http://namespaces.zope.org/gts">

    <directive name="registerTranslations"
               attributes="directory"
               handler="Zope.I18n.metaConfigure.registerTranslations" />

  </directives>

</zopeConfigure>


=== Added File Zope3/lib/python/Zope/I18n/metaConfigure.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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 module handles the :startup directives. 

$Id: metaConfigure.py,v 1.1 2002/06/12 18:38:56 srichter Exp $
"""

import os
from Zope.Configuration.Action import Action
from GettextMessageCatalog import GettextMessageCatalog
from GlobalTranslationService import translationService

def registerTranslations(_context, directory):
    """ """
    actions = []

    path = _context.path(directory)
    path = os.path.normpath(path)

    for language in os.listdir(path):
        lang_path = os.path.join(path, language)
        if os.path.isdir(lang_path):
            lc_messages_path = os.path.join(lang_path, 'LC_MESSAGES')
            for domain_file in os.listdir(lc_messages_path):
                if domain_file.endswith('.mo'):
                    domain_path = os.path.join(lc_messages_path, domain_file)
                    domain = domain_file[:-3]
                    catalog = GettextMessageCatalog(language, domain,
                                                    domain_path)

                    actions.append(Action(
                        discriminator = catalog.getIdentifier(),
                        callable = translationService.addCatalog,
                        args = (catalog,) ))

    return actions



=== Zope3/lib/python/Zope/I18n/Domain.py 1.3 => 1.4 ===
     # IDomain interface methods
 
-    def translate(self, source, mapping=None, context=None,
+    def translate(self, msgid, mapping=None, context=None,
                   target_language=None):
         """See Zope.I18n.IDomain.IDomain"""
 


=== Zope3/lib/python/Zope/I18n/IMessageCatalog.py 1.3 => 1.4 ===
 
     def getIdentifier():
-        """Return an identifier for this message catalog.
+        """Return a identifier for this message catalog. Note that this
+           identifier does not have to be unique as several message catalog
+           could serve the same domain and language.
 
-        Note that this identifier does not have to be unique as several
-        message catalogs could be serving the same domain and language.
+           Also, there are no restrictions on the form of the identifier.
         """


=== Zope3/lib/python/Zope/I18n/MessageCatalog.py 1.2 => 1.3 ===
 """
 
+from Persistence.BTrees.OOBTree import OOBTree
+from Persistence import Persistent
+from Zope.ComponentArchitecture.IFactory import IFactory
+from Zope.App.Security.RegisteredObject import RegisteredObject
 from Zope.I18n.IMessageCatalog import IMessageCatalog
 
 
-class MessageCatalog:
+class MessageCatalog(RegisteredObject, Persistent):
 
     __implements__ =  IMessageCatalog
+    __class_implements__ = IFactory
 
 
-    def __init__(self, language, domain="global"):
+    def __init__(self, language, domain="default"):
         """Initialize the message catalog"""
+        super(MessageCatalog, self).__init__('', '', '')
+
         self._language = language
         self._domain = domain
-        self._messages = {}
+        self._messages = OOBTree()
     
 
     def setMessage(self, id, message):
         """Set a message to the catalog."""
         self._messages[id] = message
+
+
+    def getMessageIds(self):
+        """ """
+        return list(self._messages.keys())
+        
+
+    ############################################################
+    # Implementation methods for interface
+    # Zope/ComponentArchitecture/IFactory.py
+
+    def getInterfaces(self):
+        'See Zope.ComponentArchitecture.IFactory.IFactory'
+        return self.__implements__
         
+    #
+    ############################################################
 
     ############################################################
     # Implementation methods for interface


=== Zope3/lib/python/Zope/I18n/TranslationService.py 1.3 => 1.4 ===
 from Persistence.BTrees.OOBTree import OOBTree
 
+from Zope.ComponentArchitecture import createObject
+from Zope.ComponentArchitecture import getService
+
 from Zope.App.OFS.Container.BTreeContainer import BTreeContainer
 from Zope.App.OFS.Container.IContainer import IContainer
 from Zope.App.OFS.Container.IContainer import IHomogenousContainer
 
-from Zope.I18n.Negotiator import negotiator
-from Zope.I18n.IMessageCatalog import IMessageCatalog
-from Zope.I18n.ITranslationService import ITranslationService
-
-
-# Setting up some regular expressions for finding interpolation variables in
-# the text.
-NAME_RE = r"[a-zA-Z][a-zA-Z0-9_]*"
-_interp_regex = re.compile(r'(?<!\$)(\$(?:%(n)s|{%(n)s}))' %({'n': NAME_RE}))
-_get_var_regex = re.compile(r'%(n)s' %({'n': NAME_RE}))
+from Negotiator import negotiator
+from Domain import Domain
+from IMessageCatalog import IMessageCatalog
+from ITranslationService import ITranslationService
+from SimpleTranslationService import SimpleTranslationService
 
 
 class ILocalTranslationService(ITranslationService,
@@ -42,13 +40,16 @@
     """TTW manageable translation service"""
 
 
-class TranslationService(BTreeContainer):
+class TranslationService(BTreeContainer, SimpleTranslationService):
+    ''' '''
 
     __implements__ =  ILocalTranslationService
 
     def __init__(self, default_domain='global'):
-        self.__data = OOBTree()
+        ''' '''
+        super(TranslationService, self).__init__()
         self._catalogs = OOBTree()
+        self.default_domain = default_domain
 
 
     def _registerMessageCatalog(self, language, domain, catalog_name):
@@ -60,8 +61,7 @@
 
 
     def _unregisterMessageCatalog(self, language, domain, catalog_name):
-        mc = self._catalogs.get((language, domain), [])
-        mc.append(catalog_name)
+        self._catalogs[(language, domain)].remove(catalog_name)
 
 
     ############################################################
@@ -69,21 +69,19 @@
     # Zope.App.OFS.Container.IContainer.IWriteContainer
 
     def setObject(self, name, object):
-        """See Zope.App.OFS.Container.IContainer.IWriteContainer"""
-        if type(name) in StringTypes and len(name)==0:
-            raise ValueError
-        if not self.isAddable(getattr(object,'__implements__', None)):
-            raise UnaddableError (self, object, name)
-        self.__data[name] = object
+        'See Zope.App.OFS.Container.IContainer.IWriteContainer'
+        super(TranslationService, self).setObject(name, object)
         self._registerMessageCatalog(object.getLanguage(), object.getDomain(),
                                      name)
         return name
 
     def __delitem__(self, name):
-        """See Zope.App.OFS.Container.IContainer.IWriteContainer"""
-        del self.__data[name]
-        self._unregisterMessageCatalog(object.language, object.domain, name)
-        
+        'See Zope.App.OFS.Container.IContainer.IWriteContainer'
+        object = self[name]
+        super(TranslationService, self).__delitem__(name)
+        self._unregisterMessageCatalog(object.getLanguage(),
+                                       object.getDomain(), name)
+
 
     def isAddable(self, interfaces):
         """See Zope.App.OFS.Container.IContainer.IWriteContainer"""
@@ -113,14 +111,16 @@
                 raise TypeError, 'No destination language'
             else:
                 avail_langs = self.getAvailableLanguages(domain)
+                # Let's negotiate the language to translate to. :)
+                negotiator = getService(self, 'LanguageNegotiation')
                 target_language = negotiator.getLanguage(avail_langs, context)
 
         # Get the translation. Default is the source text itself.
-        catalog_names = self._catalogs.get((target_language, domain), {})
+        catalog_names = self._catalogs.get((target_language, domain), [])
 
         text = msgid
         for name in catalog_names:
-            catalog = self.__data[name]
+            catalog = super(TranslationService, self).__getitem__(name)
             try:
                 text = catalog.getMessage(msgid)
                 break
@@ -134,19 +134,112 @@
     ############################################################
 
 
-    def interpolate(self, text, mapping):
-        """Insert the data passed from mapping into the text"""
-
-        to_replace = _interp_regex.findall(text)
-
-        for string in to_replace:
-            var = _get_var_regex.findall(string)[0]
-            text = text.replace(string, mapping.get(var))
-
-        return text
+    def getMessageIdsOfDomain(self, domain, filter='%'):
+        """Get all the message ids of a particular domain."""
+        filter = filter.replace('%', '.*')
+        filter_re = re.compile(filter)
+        
+        msg_ids = {}
+        languages = self.getAvailableLanguages(domain)
+        for language in languages:
+            for name in self._catalogs[(language, domain)]:
+                for id in self[name].getMessageIds():
+                    if filter_re.match(id) >= 0:
+                        msg_ids[id] = None
+        return msg_ids.keys()
+
+
+    def getAllLanguages(self):
+        """Find all the languages that are available"""
+        languages = {}
+        for key in self._catalogs.keys():
+            languages[key[0]] = None
+        return languages.keys()
+
+
+    def getAllDomains(self):
+        """Find all available domains."""
+        domains = {}
+        for key in self._catalogs.keys():
+            domains[key[1]] = None
+        return domains.keys()
 
 
     def getAvailableLanguages(self, domain):
         """Find all the languages that are available for this domain"""
+        identifiers = self._catalogs.keys()
+        identifiers = filter(lambda x, d=domain: x[1] == d, identifiers)
+        languages = map(lambda x: x[0], identifiers)
+        return languages
+
+
+    def getAvailableDomains(self, language):
+        """Find all available domains."""
+        identifiers = self._catalogs.keys()
+        identifiers = filter(lambda x, l=language: x[0] == l, identifiers)
+        domains = map(lambda x: x[1], identifiers)
+        return domains
         
-        return [x[0] for x in self._catalogs.keys() if x[1] == domain]
+
+    def addMessage(self, domain, msg_id, msg, target_language):
+        """ """
+        catalog_name = self._catalogs[(target_language, domain)][0]
+        catalog = self[catalog_name]
+        catalog.setMessage(msg_id, msg)
+
+
+    def updateMessage(self, domain, msg_id, msg, target_language):
+        """ """
+        catalog_name = self._catalogs[(target_language, domain)][0]
+        catalog = self[catalog_name]
+        catalog.setMessage(msg_id, msg)
+
+
+    def deleteMessage(self, domain, msg_id, target_language):
+        """ """
+        catalog_name = self._catalogs[(target_language, domain)][0]
+        catalog = self[catalog_name]
+        catalog.deleteMessage(msg_id)
+
+
+    def addLanguage(self, language):
+        """Add Language to Translation Service"""
+        domains = self.getAllDomains()
+        if not domains:
+            domains = [self.default_domain]
+
+        for domain in domains:
+            catalog = createObject(self, 'Message Catalog', language, domain)
+            self.setObject('%s-%s' %(domain, language), catalog)
+
+
+    def addDomain(self, domain):
+        """Add Domain to Translation Service"""
+        languages = self.getAllLanguages()
+        if not languages:
+            languages = ['en']
+
+        for language in languages:
+            catalog = createObject(self, 'Message Catalog', language, domain)
+            self.setObject('%s-%s' %(domain, language), catalog)
+
+
+    def deleteLanguage(self, language):
+        """Delete a Domain from the Translation Service."""
+        domains = self.getAvailableDomains(language)
+        for domain in domains:
+            for name in self._catalogs[(language, domain)]:
+                if self.has_key(name):
+                    del self[name]
+            del self._catalogs[(language, domain)]
+
+    def deleteDomain(self, domain):
+        """Delete a Domain from the Translation Service."""
+        languages = self.getAvailableLanguages(domain)
+        for language in languages:
+            for name in self._catalogs[(language, domain)]:
+                if self.has_key(name):
+                    del self[name]
+            del self._catalogs[(language, domain)]
+
+


=== Zope3/lib/python/Zope/I18n/i18n.zcml 1.2 => 1.3 ===
    xmlns:browser='http://namespaces.zope.org/browser'
    xmlns:service='http://namespaces.zope.org/service'
+   xmlns:gts='http://namespaces.zope.org/gts'
 >
 
-  <serviceType
+<serviceType
       id="LanguageNegotiation" 
       interface=".INegotiator." />
-  <service
+<service
       serviceType="LanguageNegotiation" 
       component=".Negotiator.negotiator" />
 
-<!-- Language negotiation adapter 
 <adapter factory="Zope.Publisher.Browser.BrowserLanguages."
-         for="Zope.Publisher.Browser.IHTTPRequest."
+         for="Zope.Publisher.Browser.IBrowserRequest."
          provides="Zope.I18n.IUserPreferedLanguages"
          />
--->
 
+
+<!-- Register the Translation Service as a content object -->
 <content class=".TranslationService.">
-   <security:require permission="Zope.I18n"
+   <security:require permission="Zope.Public"
                      interface="Zope.I18n.ITranslationService." 
                      />
    <security:require permission="Zope.ManageServices"
+                     attributes="getMessageIdsOfDomain
+		                 getAllDomains getAllLanguages 
+		                 getAvailableDomains getAvailableLanguages 
+				 addMessage updateMessage
+                                 addLanguage addDomain
+                                 deleteLanguage deleteDomain"
+                     />
+   <security:require permission="Zope.ManageServices"
                      interface="Zope.App.OFS.Container.IContainer." 
                      />
 </content>
 
+
+<service:factoryFromClass id="TranslationService"
+          class=".TranslationService."
+          permission="Zope.ManageServices"
+          title="Translation Service" 
+          description="A Persistent Translation Service for TTW development" />
+
+
+<serviceType id="GlobalTranslationService"
+             interface=".ITranslationService." />
+
+<service serviceType="GlobalTranslationService"
+           permission="Zope.Public"
+           component=".GlobalTranslationService.translationService" />
+
+
 <content class=".MessageCatalog.">
    <security:require permission="Zope.Security"
                      interface=".IMessageCatalog." 
                      />
-
+   <security:require permission="Zope.ManageServices"
+                     attributes="setMessage getMessageIds"
+                     />
 </content>
 
-    <service:factoryFromClass id="TranslationService"
-                     class=".MessageCatalog."
-                     permission="Zope.ManageServices"
-                     title="Translation Service" />
 
-<!--factory component=".MessageCatalog." /-->
+<factory component=".MessageCatalog." 
+         id="Message Catalog"/>
+
 
-<!--include package=".Views" file="views.zcml" /-->
+<gts:registerTranslations directory="./locale" />
 
+<include package=".Views" file="views.zcml" />
 
 
 </zopeConfigure>