[Zope3-checkins] CVS: Zope3/src/zope/i18n - __init__.py:1.1.2.1 domain.py:1.1.2.1 gettextmessagecatalog.py:1.1.2.1 globaltranslationservice.py:1.1.2.1 i18nobject.stx:1.1.2.1 negotiator.py:1.1.2.1 simpletranslationservice.py:1.1.2.1 translate.py:1.1.2.1

Jim Fulton jim@zope.com
Mon, 23 Dec 2002 14:32:52 -0500


Update of /cvs-repository/Zope3/src/zope/i18n
In directory cvs.zope.org:/tmp/cvs-serv19908/zope/i18n

Added Files:
      Tag: NameGeddon-branch
	__init__.py domain.py gettextmessagecatalog.py 
	globaltranslationservice.py i18nobject.stx negotiator.py 
	simpletranslationservice.py translate.py 
Log Message:
Initial renaming before debugging

=== Added File Zope3/src/zope/i18n/__init__.py ===
#
# This file is necessary to make this directory a package.


=== Added File Zope3/src/zope/i18n/domain.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.
# 
##############################################################################
"""

$Id: domain.py,v 1.1.2.1 2002/12/23 19:32:51 jim Exp $
"""

from zope.interfaces.i18n import IDomain
from zope.component import getServiceManager

class Domain:

    __implements__ =  IDomain

    def __init__(self, domain, service):
        self._domain = domain
        self._translationService = service

    def getDomainName(self):
        """Return the domain name"""
        return self._domain
    
    # IDomain interface methods

    def translate(self, msgid, mapping=None, context=None,
                  target_language=None):
        """See Zope.I18n.IDomain.IDomain"""
        return self._translationService.translate(
            self._domain, msgid, mapping, context, target_language)


=== Added File Zope3/src/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.2.1 2002/12/23 19:32:51 jim Exp $
"""

from gettext import GNUTranslations
from zope.interfaces.i18n import IReadMessageCatalog


class GettextMessageCatalog:
    """ """

    __implements__ =  IReadMessageCatalog


    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.IReadMessageCatalog

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

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

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

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


=== Added File Zope3/src/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.2.1 2002/12/23 19:32:51 jim Exp $
"""

from zope.i18n.negotiator import negotiator
from zope.i18n.simpletranslationservice import SimpleTranslationService

# The configure.zcml file should specify a list of fallback languages for the
# site.  If a particular catalog for a negotiated language is not available,
# then the zcml specified order should be tried.  If that fails, then as a
# last resort the languages in the following list are tried.  If these fail
# too, then the msgid is returned.
#
# Note that these fallbacks are used only to find a catalog.  If a particular
# message in a catalog is not translated, tough luck, you get the msgid.
LANGUAGE_FALLBACKS = ['en']


class GlobalTranslationService(SimpleTranslationService):

    __implements__ =  SimpleTranslationService.__implements__

    def __init__(self, default_domain='global', fallbacks=None):
        # XXX We haven't specified that ITranslationServices have a default
        # domain.  So far, we've required the domain argument to .translate()
        self._domain = default_domain
        # _catalogs maps (language, domain) to IMessageCatalog instances
        self._catalogs = {}
        # _data maps IMessageCatalog.getIdentifier() to IMessageCatalog
        self._data = {}
        # What languages to fallback to, if there is no catalog for the
        # requested language (no fallback on individual messages)
        if fallbacks is None:
            fallbacks = LANGUAGE_FALLBACKS
        self._fallbacks = fallbacks

    def _registerMessageCatalog(self, language, domain, catalog_name):
        key = (language, domain)
        mc = self._catalogs.setdefault(key, [])
        mc.append(catalog_name)

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

    def setLanguageFallbacks(self, fallbacks=None):
        if fallbacks is None:
            fallbacks = LANGUAGE_FALLBACKS
        self._fallbacks = fallbacks


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

    def translate(self, domain, msgid, mapping=None, context=None,  
                  target_language=None):
        '''See interface ITranslationService'''
        if target_language is None:
            if context is None:
                raise TypeError, 'No destination language'
            else:
                langs = [m[0] for m in self._catalogs.keys()]
                target_language = negotiator.getLanguage(langs, context)

        # Get the translation. Use the specified fallbacks if this fails
        catalog_names = self._catalogs.get((target_language, domain))
        if catalog_names is None:
            for language in self._fallbacks:
                catalog_names = self._catalogs.get((language, domain))
                if catalog_names is not None:
                    break
        # Did the fallback fail?  Sigh, use the msgid
        if catalog_names is None:
            catalog_names = []

        text = msgid
        for name in catalog_names:
            catalog = self._data[name]
            text = catalog.queryMessage(msgid)

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

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


translationService = GlobalTranslationService()


=== Added File Zope3/src/zope/i18n/i18nobject.stx ===
I18n Objects

  I18n objects are used to internationalize content that cannot be translated
  via messages. The problem is three fold:

    - Internationalize an object iteself. The best example is an Image. Often
      people put text into an image that needs to be localized, but since it
      is a picture the text cannot be retrieved as a string. You therefore
      will need a mechanism to create a different version of the object for every
      language. (Note: This behavior is the same as currently implemented in
      the ZBabelObject.) 

    - Internationalize fractions of the content. Let's say for example you
      wanted to internationalize the DublinCore information of your content
      object. In order for this to work out you need to have an
      I18n-supportive AttributeAnnotation. Solving this use case would be
      similar to some of the work David Juan did with
      InternationalizedContent in Localizer.

    - Formatting Objects. This problem involves converting basic or complex
      types into a localized format. Good examples for this are decimal
      numbers and date/time objects. In this case you would like to specify a
      format via a templating expression and then pass the object to the
      formatter to apply the parsed template. Initial steps for implementing
      the template parser have been already taken, therefore we only need to
      develop some interfaces and unittests here, so the parser developer
      (which will use the ICU C/C++ libraries) will (choose?) what parts of the parser
      to wrap for Python. 

   
  The first two problems are very similar and can actually share the same
  interface for the language negotiation and redirection of the content
  request. I would therefore propose to have a II18nContent interface that
  somehow specifies how to get the and then redirect the call to the correct
  local content.

   
  I18nObject

    There will be an interface called II18nObject (which inherits I18nContent
    of course), which is a cameleon-like container, as it adapts to the
    properties of the contained object type. In order to accomplish all this,
    you will have to implement your own traverser which looks up the correct
    subobject.


  I18nAttributeAnnotation

    This object will basically provide an internationalized version of
    Zope.App.OFS.AttributeAnnotations. which will be accomplished in a similar
    manner as the I18nObject.


  One issue left to solve is how to know the language when we need to make the
  decision. These objects are **not** Views, therefore they do not know about
  the request variable. 

  One way to solve the issue would be that I18nAttributeAnnotation, for
  example, would not actually implement the IAnnotations interface, but that
  there would be an Adapter converting the II18nAnnotations to
  IAnnotations. Since adapters are short-lived (meaning they are initialized
  for a particular call), we can safely store some language information in
  them in order to make the language decision.


=== Added File Zope3/src/zope/i18n/negotiator.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.
# 
##############################################################################
"""

$Id: negotiator.py,v 1.1.2.1 2002/12/23 19:32:51 jim Exp $
"""

from zope.interfaces.i18n import INegotiator
from zope.interfaces.i18n import IUserPreferredLanguages
from zope.interfaces.i18n import ILanguageAvailability
from zope.component import getAdapter

class Negotiator:

    __implements__ = INegotiator

    def getLanguage(self, langs, env):
        envadaptor = getAdapter(env, IUserPreferredLanguages)
        userlangs = envadaptor.getPreferredLanguages()
        # Prioritize on the user preferred languages.  Return the first user
        # preferred language that the object has available.
        for lang in userlangs:
            if lang in langs:
                return lang
        return None


negotiator = Negotiator()


=== Added File Zope3/src/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.2.1 2002/12/23 19:32:51 jim Exp $
"""

import re
from types import DictType
from zope.component import getService
from zope.interfaces.i18n import ITranslationService
from zope.i18n.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:
                langs = [m[1] for m in self.messages.keys()]
                # Let's negotiate the language to translate to. :)
                negotiator = getService(self, 'LanguageNegotiation')
                target_language = negotiator.getLanguage(langs, context)

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

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


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

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


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

        # If no translation was found, there is nothing to do.
        if text is None:
            return None

        # 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/src/zope/i18n/translate.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.
# 
##############################################################################
"""

$Id: translate.py,v 1.1.2.1 2002/12/23 19:32:51 jim Exp $
"""


def translate(place, domain, source, mapping=None, context=None,
              target_language=None):
    """Translates a source text based on a location in the Zope architecture
       and a domain."""

    # Lookup service...
    service = None

    return service.translate(domain, source, mapping, context, target_language)