[Zope3-checkins] CVS: Zope3/src/zope/i18n - __init__.py:1.2 domain.py:1.2 gettextmessagecatalog.py:1.2 globaltranslationservice.py:1.2 i18nobject.stx:1.2 negotiator.py:1.2 simpletranslationservice.py:1.2 translate.py:1.2
Jim Fulton
jim@zope.com
Wed, 25 Dec 2002 09:14:10 -0500
Update of /cvs-repository/Zope3/src/zope/i18n
In directory cvs.zope.org:/tmp/cvs-serv15352/src/zope/i18n
Added Files:
__init__.py domain.py gettextmessagecatalog.py
globaltranslationservice.py i18nobject.stx negotiator.py
simpletranslationservice.py translate.py
Log Message:
Grand renaming:
- Renamed most files (especially python modules) to lower case.
- Moved views and interfaces into separate hierarchies within each
project, where each top-level directory under the zope package
is a separate project.
- Moved everything to src from lib/python.
lib/python will eventually go away. I need access to the cvs
repository to make this happen, however.
There are probably some bits that are broken. All tests pass
and zope runs, but I haven't tried everything. There are a number
of cleanups I'll work on tomorrow.
=== Zope3/src/zope/i18n/__init__.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:14:10 2002
+++ Zope3/src/zope/i18n/__init__.py Wed Dec 25 09:13:39 2002
@@ -0,0 +1,2 @@
+#
+# This file is necessary to make this directory a package.
=== Zope3/src/zope/i18n/domain.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:14:10 2002
+++ Zope3/src/zope/i18n/domain.py Wed Dec 25 09:13:39 2002
@@ -0,0 +1,40 @@
+##############################################################################
+#
+# 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$
+"""
+
+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 IDomain"""
+ return self._translationService.translate(
+ self._domain, msgid, mapping, context, target_language)
=== Zope3/src/zope/i18n/gettextmessagecatalog.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:14:10 2002
+++ Zope3/src/zope/i18n/gettextmessagecatalog.py Wed Dec 25 09:13:39 2002
@@ -0,0 +1,72 @@
+##############################################################################
+#
+# 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$
+"""
+
+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()
+
+
+ def getMessage(self, id):
+ 'See IReadMessageCatalog'
+ self._prepareTranslations()
+ msg = self.__translation_object.ugettext(id)
+ if msg == id:
+ raise KeyError
+ return msg
+
+ def queryMessage(self, id, default=None):
+ 'See IReadMessageCatalog'
+ self._prepareTranslations()
+ text = self.__translation_object.ugettext(id)
+ if text != id:
+ return text
+ return default
+
+ def getLanguage(self):
+ 'See IReadMessageCatalog'
+ return self._language
+
+ def getDomain(self):
+ 'See IReadMessageCatalog'
+ return self._domain
+
+ def getIdentifier(self):
+ 'See IReadMessageCatalog'
+ return self._path_to_file
=== Zope3/src/zope/i18n/globaltranslationservice.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:14:10 2002
+++ Zope3/src/zope/i18n/globaltranslationservice.py Wed Dec 25 09:13:39 2002
@@ -0,0 +1,96 @@
+##############################################################################
+#
+# 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$
+"""
+
+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
+
+ 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()
=== Zope3/src/zope/i18n/i18nobject.stx 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:14:10 2002
+++ Zope3/src/zope/i18n/i18nobject.stx Wed Dec 25 09:13:39 2002
@@ -0,0 +1,63 @@
+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.
=== Zope3/src/zope/i18n/negotiator.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:14:10 2002
+++ Zope3/src/zope/i18n/negotiator.py Wed Dec 25 09:13:39 2002
@@ -0,0 +1,39 @@
+##############################################################################
+#
+# 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$
+"""
+
+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()
=== Zope3/src/zope/i18n/simpletranslationservice.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:14:10 2002
+++ Zope3/src/zope/i18n/simpletranslationservice.py Wed Dec 25 09:13:39 2002
@@ -0,0 +1,102 @@
+##############################################################################
+#
+# 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$
+"""
+
+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
+
+
+ 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
=== Zope3/src/zope/i18n/translate.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:14:10 2002
+++ Zope3/src/zope/i18n/translate.py Wed Dec 25 09:13:39 2002
@@ -0,0 +1,28 @@
+##############################################################################
+#
+# 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$
+"""
+
+
+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)