[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)