[Zope-Checkins] CVS: Zope3/lib/python/Zope/I18n - GettextExportFilter.py:1.1 GettextImportFilter.py:1.1 IMessageExportFilter.py:1.1 IMessageImportFilter.py:1.1 i18n_service.gif:1.1 GettextMessageCatalog.py:1.3 IMessageCatalog.py:1.5 ITranslationService.py:1.4 MessageCatalog.py:1.6 TranslationService.py:1.8 i18n.zcml:1.9 IEditableTranslationService.py:NONE
Stephan Richter
srichter@cbu.edu
Sun, 16 Jun 2002 14:25:44 -0400
Update of /cvs-repository/Zope3/lib/python/Zope/I18n
In directory cvs.zope.org:/tmp/cvs-serv23163
Modified Files:
GettextMessageCatalog.py IMessageCatalog.py
ITranslationService.py MessageCatalog.py TranslationService.py
i18n.zcml
Added Files:
GettextExportFilter.py GettextImportFilter.py
IMessageExportFilter.py IMessageImportFilter.py
i18n_service.gif
Removed Files:
IEditableTranslationService.py
Log Message:
Commit part 1:
I have done quiet some work this weekend on refining and refactoring the
Translation Service and it should be ready for EP now:
- Refactored the interfaces into more standard read and write interfaces.
- Implemented an Import and Export feature using geyyexy files.
- Implemented Message Synchronisation mechanism via XML-RPC.
- Improved the overall look and feel of the LocalTranslation Service.
- Added an icon for the Translation Service.
=== Added File Zope3/lib/python/Zope/I18n/GettextExportFilter.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.
#
##############################################################################
"""Translation Service Message Export Filter
$Id: GettextExportFilter.py,v 1.1 2002/06/16 18:25:13 srichter Exp $
"""
import time
from types import StringTypes
from Zope.I18n.IMessageExportFilter import IMessageExportFilter
from Zope.I18n.ITranslationService import IWriteTranslationService
class GettextExportFilter:
__implements__ = IMessageExportFilter
__used_for__ = IWriteTranslationService
def __init__(self, service):
self.service = service
############################################################
# Implementation methods for interface
# Zope.I18n.IMessageExportFilter.IMessageExportFilter
def exportMessages(self, domains, languages):
'See Zope.I18n.IMessageExportFilter.IMessageExportFilter'
if isinstance(domains, StringTypes):
domain = domains
elif len(domains) == 1:
domain = domains[0]
else:
raise TypeError, \
'Only one domain at a time is supported for gettext export.'
if isinstance(languages, StringTypes):
language = languages
elif len(languages) == 1:
language = languages[0]
else:
raise TypeError, \
'Only one language at a time is supported for gettext export.'
dt = time.time()
dt = time.localtime(dt)
dt = time.strftime('%Y/%m/%d %H:%M', dt)
output = _file_header %(dt, language.encode('UTF-8'),
domain.encode('UTF-8'))
service = self.service
for msgid in service.getMessageIdsOfDomain(domain):
msgstr = service.translate(domain, msgid,
target_language=language)
msgstr = msgstr.encode('UTF-8')
msgid = msgid.encode('UTF-8')
output += _msg_template %(msgid, msgstr)
return output
#
############################################################
_file_header = '''
msgid ""
msgstr ""
"Project-Id-Version: Zope 3\\n"
"PO-Revision-Date: %s\\n"
"Last-Translator: Zope 3 Gettext Export Filter\\n"
"Zope-Language: %s\\n"
"Zope-Domain: %s\\n"
"MIME-Version: 1.0\\n"
"Content-Type: text/plain; charset=UTF-8\\n"
"Content-Transfer-Encoding: 8bit\\n"
'''
_msg_template = '''
msgid "%s"
msgstr "%s"
'''
=== Added File Zope3/lib/python/Zope/I18n/GettextImportFilter.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.
#
##############################################################################
"""Translation Service Message Import Filter
$Id: GettextImportFilter.py,v 1.1 2002/06/16 18:25:13 srichter Exp $
"""
import time, re
from types import StringTypes
from Zope.I18n.IMessageImportFilter import IMessageImportFilter
from Zope.I18n.ITranslationService import IWriteTranslationService
class GettextImportFilter:
__implements__ = IMessageImportFilter
__used_for__ = IWriteTranslationService
def __init__(self, service):
self.service = service
############################################################
# Implementation methods for interface
# Zope.I18n.IMessageImportFilter.IMessageImportFilter
def importMessages(self, domains, languages, file):
'See Zope.I18n.IMessageImportFilter.IMessageImportFilter'
if isinstance(domains, StringTypes):
domain = domains
elif len(domains) == 1:
domain = domains[0]
else:
raise TypeError, \
'Only one domain at a time is supported for gettext export.'
if isinstance(languages, StringTypes):
language = languages
elif len(languages) == 1:
language = languages[0]
else:
raise TypeError, \
'Only one language at a time is supported for gettext export.'
result = parseGetText(file.readlines())[3]
headers = parserHeaders(''.join(result[('',)][1]))
del result[('',)]
charset = extractCharset(headers['content-type'])
service = self.service
for msg in result.items():
msgid = unicode(''.join(msg[0]), charset)
msgid = msgid.replace('\\n', '\n')
msgstr = unicode(''.join(msg[1][1]), charset)
msgstr = msgstr.replace('\\n', '\n')
service.addMessage(domain, msgid, msgstr, language)
#
############################################################
def extractCharset(header):
charset = header.split('charset=')[-1]
return charset.lower()
def parserHeaders(headers_text):
headers = {}
for line in headers_text.split('\\n'):
name = line.split(':')[0]
value = ''.join(line.split(':')[1:])
headers[name.lower()] = value
return headers
def parseGetText(content):
# The regular expressions
com = re.compile('^#.*')
msgid = re.compile(r'^ *msgid *"(.*?[^\\]*)"')
msgstr = re.compile(r'^ *msgstr *"(.*?[^\\]*)"')
re_str = re.compile(r'^ *"(.*?[^\\])"')
blank = re.compile(r'^\s*$')
trans = {}
pointer = 0
state = 0
COM, MSGID, MSGSTR = [], [], []
while pointer < len(content):
line = content[pointer]
#print 'STATE:', state
#print 'LINE:', line, content[pointer].strip()
if state == 0:
COM, MSGID, MSGSTR = [], [], []
if com.match(line):
COM.append(strip(line))
state = 1
pointer = pointer + 1
elif msgid.match(line):
MSGID.append(msgid.match(line).group(1))
state = 2
pointer = pointer + 1
elif blank.match(line):
pointer = pointer + 1
else:
raise 'ParseError', 'state 0, line %d\n' % (pointer + 1)
elif state == 1:
if com.match(line):
COM.append(strip(line))
state = 1
pointer = pointer + 1
elif msgid.match(line):
MSGID.append(msgid.match(line).group(1))
state = 2
pointer = pointer + 1
elif blank.match(line):
pointer = pointer + 1
else:
raise 'ParseError', 'state 1, line %d\n' % (pointer + 1)
elif state == 2:
if com.match(line):
COM.append(strip(line))
state = 2
pointer = pointer + 1
elif re_str.match(line):
MSGID.append(re_str.match(line).group(1))
state = 2
pointer = pointer + 1
elif msgstr.match(line):
MSGSTR.append(msgstr.match(line).group(1))
state = 3
pointer = pointer + 1
elif blank.match(line):
pointer = pointer + 1
else:
raise 'ParseError', 'state 2, line %d\n' % (pointer + 1)
elif state == 3:
if com.match(line) or msgid.match(line):
# print "\nEn", language, "detected", MSGID
trans[tuple(MSGID)] = (COM, MSGSTR)
state = 0
elif re_str.match(line):
MSGSTR.append(re_str.match(line).group(1))
state = 3
pointer = pointer + 1
elif blank.match(line):
pointer = pointer + 1
else:
raise 'ParseError', 'state 3, line %d\n' % (pointer + 1)
# the last also goes in
if tuple(MSGID):
trans[tuple(MSGID)] = (COM, MSGSTR)
return COM, MSGID, MSGSTR, trans
=== Added File Zope3/lib/python/Zope/I18n/IMessageExportFilter.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.
#
##############################################################################
"""Translation Service Message Export Filter Interface
$Id: IMessageExportFilter.py,v 1.1 2002/06/16 18:25:13 srichter Exp $
"""
from Interface import Interface
class IMessageExportFilter(Interface):
"""The Export Filter for Translation Service Messages.
Classes implementing this interface should usually be Adaptors, as
they adapt the IEditableTranslationService interface."""
def exportMessages(domains, languages):
"""Export all messages that are defined in the specified domains and
languages.
Note that some implementations might limit to only one domain and
one language. A good example for that is a GettextFile.
"""
=== Added File Zope3/lib/python/Zope/I18n/IMessageImportFilter.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.
#
##############################################################################
"""Translation Service Message Import Filter Interface
$Id: IMessageImportFilter.py,v 1.1 2002/06/16 18:25:13 srichter Exp $
"""
from Interface import Interface
class IMessageImportFilter(Interface):
"""The Import Filter for Translation Service Messages.
Classes implementing this interface should usually be Adaptors, as
they adapt the IEditableTranslationService interface."""
def importMessages(domains, languages, file):
"""Import all messages that are defined in the specified domains and
languages.
Note that some implementations might limit to only one domain and
one language. A good example for that is a GettextFile.
"""
=== Added File Zope3/lib/python/Zope/I18n/i18n_service.gif ===
<Binary-ish file>
=== Zope3/lib/python/Zope/I18n/GettextMessageCatalog.py 1.2 => 1.3 ===
from gettext import GNUTranslations
-from Zope.I18n.IMessageCatalog import IMessageCatalog
+from Zope.I18n.IMessageCatalog import IReadMessageCatalog
class GettextMessageCatalog:
""" """
- __implements__ = IMessageCatalog
+ __implements__ = IReadMessageCatalog
def __init__(self, language, domain, path_to_file):
@@ -45,10 +45,10 @@
############################################################
# Implementation methods for interface
- # Zope.I18n.IMessageCatalog.
+ # Zope.I18n.IMessageCatalog.IReadMessageCatalog
def getMessage(self, id):
- 'See Zope.I18n.IMessageCatalog.IMessageCatalog'
+ 'See Zope.I18n.IMessageCatalog.IReadMessageCatalog'
self._prepareTranslations()
msg = self.__translation_object.ugettext(id)
if msg == id:
@@ -56,7 +56,7 @@
return msg
def queryMessage(self, id, default=None):
- 'See Zope.I18n.IMessageCatalog.IMessageCatalog'
+ 'See Zope.I18n.IMessageCatalog.IReadMessageCatalog'
self._prepareTranslations()
text = self.__translation_object.ugettext(id)
if text != id:
@@ -66,15 +66,15 @@
return default
def getLanguage(self):
- 'See Zope.I18n.IMessageCatalog.IMessageCatalog'
+ 'See Zope.I18n.IMessageCatalog.IReadMessageCatalog'
return self._language
def getDomain(self):
- 'See Zope.I18n.IMessageCatalog.IMessageCatalog'
+ 'See Zope.I18n.IMessageCatalog.IReadMessageCatalog'
return self._domain
def getIdentifier(self):
- 'See Zope.I18n.IMessageCatalog.IMessageCatalog'
+ 'See Zope.I18n.IMessageCatalog.IReadMessageCatalog'
return self._path_to_file
#
=== Zope3/lib/python/Zope/I18n/IMessageCatalog.py 1.4 => 1.5 ===
-class IMessageCatalog(Interface):
+class IReadMessageCatalog(Interface):
"""A catalog (mapping) of message ids to message text strings.
This interface provides a method for translating a message or message id,
@@ -37,6 +37,9 @@
When we refer to text here, we mean text that follows the standard Zope 3
text representation.
+
+ Note: The IReadMessageCatalog is the absolut minimal version required for
+ the TranslationService to function.
"""
def getMessage(msgid):
@@ -54,17 +57,51 @@
"""
def getLanguage():
- """Return the language this message catalog is representing.
- """
+ """Return the language this message catalog is representing."""
def getDomain():
- """Return the domain this message catalog is serving.
- """
+ """Return the domain this message catalog is serving."""
def getIdentifier():
"""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.
+ identifier does not have to be unique as several message catalog
+ could serve the same domain and language.
- Also, there are no restrictions on the form of the identifier.
+ Also, there are no restrictions on the form of the identifier.
"""
+
+
+class IWriteMessageCatalog(Interface):
+ """If this interfaces is implemented by a message catalog, then we will be
+ able to update our messages.
+
+ Note that not all methods here require write access, but they should
+ not be required for an IReadMEssageCatalog and are used for editing
+ only. Therefore this is the more suitable interface to put them.
+ """
+
+ def getFullMessage(msgid):
+ """Get the message data and meta data as a nice dictionary. More
+ advanced implementation might choose to return an object with
+ the data, but the object should then implement IEnumerableMapping.
+
+ An exception is raised if the message id is not found.
+ """
+
+ def setMessage(msgid, message, mod_time=None):
+ """Set a message to the catalog. If mod_time is None use the current
+ time instead as modification time."""
+
+ def deleteMessage(msgid):
+ """Delete a message from the catalog."""
+
+ def getMessageIds():
+ """Get a list of all the message ids."""
+
+ def getMessages():
+ """Get a list of all the messages."""
+
+
+class IMessageCatalog(IReadMessageCatalog, IWriteMessageCatalog):
+ """Most message catalogs should support this interface.
+ """
=== Zope3/lib/python/Zope/I18n/ITranslationService.py 1.3 => 1.4 ===
#
##############################################################################
-"""
+"""Translation Service related Interfaces.
$Id$
"""
@@ -19,7 +19,7 @@
from Interface import Interface
-class ITranslationService(Interface):
+class IReadTranslationService(Interface):
"""The Translation Service
This interface provides methods for translating text, including text with
@@ -60,15 +60,15 @@
context=None, target_language=None):
"""Return the translation for the message referred to by msgid.
- However, the method does a little more than a vanilla translation.
- The method also looks for a possible language to translate to.
- After a translation it also replaces any $name variable variables
- inside the post-translation string.
-
- Note: The TranslationService interface does not support simplified
- translation methods, since it is totally hidden by ZPT and in
- Python you should use a Domain object, since it supports all
- the simplifications.
+ However, the method does a little more than a vanilla translation.
+ The method also looks for a possible language to translate to.
+ After a translation it also replaces any $name variable variables
+ inside the post-translation string.
+
+ Note: The TranslationService interface does not support simplified
+ translation methods, since it is totally hidden by ZPT and in
+ Python you should use a Domain object, since it supports all
+ the simplifications.
"""
def getDomain(domain):
@@ -76,3 +76,107 @@
The domain supports the IDomain interface
"""
+
+
+class IWriteTranslationService(Interface):
+ """This interface describes the methods that are necessary for an editable
+ Translation Service to work.
+
+ For a translation service to be editable its 'messages' have to support
+ the following information: id, string, domain, language, date
+
+ Most of the information will be natural, since they are required by the
+ translation service, but especially the date is not a necessary info
+ (in fact, it is meta data)
+ """
+
+ def getMessageIdsOfDomain(domain, filter='%'):
+ """Get all the message ids of a particular domain."""
+
+ def getMessagesOfDomain(domain):
+ """Get all the messages of a particular domain."""
+
+ def getMessage(msgid, domain, langauge):
+ """Get the full message of a particular domain and language."""
+
+ def getAllLanguages():
+ """Find all the languages that are available"""
+
+ def getAllDomains():
+ """Find all available domains."""
+
+ def getAvailableLanguages(domain):
+ """Find all the languages that are available for this domain"""
+
+ def getAvailableDomains(language):
+ """Find all available domains."""
+
+ def addMessage(domain, msgid, msg, language, mod_time=None):
+ """Add a message to the translation service.
+
+ If mod_time is None, then the current time should be inserted.
+ """
+
+ def updateMessage(domain, msgid, msg, language, mod_time=None):
+ """Update a message in the translation service.
+
+ If mod_time is None, then the current time should be inserted.
+ """
+
+ def deleteMessage(domain, msgid, language):
+ """Delete a messahe in the translation service."""
+
+ def addLanguage(language):
+ """Add Language to Translation Service"""
+
+ def addDomain(domain):
+ """Add Domain to Translation Service"""
+
+ def deleteLanguage(language):
+ """Delete a Domain from the Translation Service."""
+
+ def deleteDomain(domain):
+ """Delete a Domain from the Translation Service."""
+
+
+class ISyncTranslationService(Interface):
+ """This interface allows translation services to be synchronized. The
+ following four synchronization states can exist:
+
+ 0 - uptodate: The two messages are in sync.
+ Default Action: Do nothing.
+
+ 1 - new: The message exists on the foreign TS, but is locally unknown.
+ Default Action: Add the message to the local catalog.
+
+ 2 - older: The local version of the message is older than the one on
+ the server.
+ Default Action: Update the local message.
+
+ 3 - newer: The local version is newer than the foreign version.
+ Default Action: Do nothing.
+
+ 4 - deleted: The message does not exist in the foreign TS.
+ Default Action: Delete local version of message/
+ """
+
+ def getMessagesMapping(domains, languages, foreign_messages):
+ """Creates a mapping of the passed foreign messages and the local ones.
+ Retruns a status report in a dictionary with keys of the form
+ (msgid, domain, language) and values being a tuple of:
+
+ foreign_mod_date, local_mod_date
+ """
+
+ def synchronize(messages_mapping):
+ """Update the local message catalogs based on the foreign data.
+ """
+
+
+class ITranslationService(IReadTranslationService, IWriteTranslationService,
+ ISyncTranslationService):
+ """This is the common and full-features translation service. Almost all
+ translation service implementations will use this interface.
+
+ An exception to this is the GlobalMessageCatalog as it will be read-only.
+ """
=== Zope3/lib/python/Zope/I18n/MessageCatalog.py 1.5 => 1.6 ===
$Id$
"""
+import time
from Persistence.BTrees.OOBTree import OOBTree
from Persistence import Persistent
+from Zope.Proxy.ProxyIntrospection import removeAllProxies
from Zope.ComponentArchitecture.IFactory import IFactory
from Zope.App.Security.RegisteredObject import RegisteredObject
from Zope.I18n.IMessageCatalog import IMessageCatalog
@@ -35,55 +37,85 @@
self._domain = domain
self._messages = OOBTree()
- def setMessage(self, msgid, message):
- """Set a message to the catalog."""
- self._messages[msgid] = message
-
- def deleteMessage(self, msgid):
- """Set a message to the catalog."""
- del self._messages[msgid]
-
- def getMessageIds(self):
- """Get a list of all the message ids."""
- return list(self._messages.keys())
-
############################################################
# Implementation methods for interface
- # Zope/ComponentArchitecture/IFactory.py
+ # Zope.I18n.IMessageCatalog.IMessageCatalog
- def getInterfaces(self):
- 'See Zope.ComponentArchitecture.IFactory.IFactory'
- return self.__implements__
-
- #
- ############################################################
-
- ############################################################
- # Implementation methods for interface
- # Zope.I18n.IMessageCatalog.
+ ######################################
+ # from: Zope.I18n.IMessageCatalog.IReadMessageCatalog
def getMessage(self, id):
- 'See Zope.I18n.IMessageCatalog.IMessageCatalog'
- return self._messages[id]
+ 'See Zope.I18n.IMessageCatalog.IReadMessageCatalog'
+ return removeAllProxies(self._messages[id][0])
def queryMessage(self, id, default=None):
- 'See Zope.I18n.IMessageCatalog.IMessageCatalog'
+ 'See Zope.I18n.IMessageCatalog.IReadMessageCatalog'
if default is None:
default = id
- return self._messages.get(id, default)
+ result = removeAllProxies(self._messages.get(id, default))
+ if result != default: result = result[0]
+ return result
def getLanguage(self):
- 'See Zope.I18n.IMessageCatalog.IMessageCatalog'
+ 'See Zope.I18n.IMessageCatalog.IReadMessageCatalog'
return self._language
def getDomain(self):
- 'See Zope.I18n.IMessageCatalog.IMessageCatalog'
+ 'See Zope.I18n.IMessageCatalog.IReadMessageCatalog'
return self._domain
def getIdentifier(self):
- 'See Zope.I18n.IMessageCatalog.IMessageCatalog'
+ 'See Zope.I18n.IMessageCatalog.IReadMessageCatalog'
return (self._language, self._domain)
+
+ ######################################
+ # from: Zope.I18n.IMessageCatalog.IWriteMessageCatalog
+
+ def getFullMessage(self, msgid):
+ 'See Zope.I18n.IMessageCatalog.IWriteMessageCatalog'
+ message = removeAllProxies(self._messages[msgid])
+ return {'domain' : self._domain,
+ 'language' : self._language,
+ 'msgid' : msgid,
+ 'msgstr' : message[0],
+ 'mod_time' : message[1]}
+
+ def setMessage(self, msgid, message, mod_time=None):
+ 'See Zope.I18n.IMessageCatalog.IWriteMessageCatalog'
+ if mod_time is None:
+ mod_time = int(time.time())
+ self._messages[msgid] = (message, mod_time)
+
+ def deleteMessage(self, msgid):
+ 'See Zope.I18n.IMessageCatalog.IWriteMessageCatalog'
+ del self._messages[msgid]
+
+ def getMessageIds(self):
+ 'See Zope.I18n.IMessageCatalog.IWriteMessageCatalog'
+ return list(self._messages.keys())
+
+ def getMessages(self):
+ 'See Zope.I18n.IMessageCatalog.IWriteMessageCatalog'
+ messages = []
+ for message in self._messages.items():
+ messages.append({'domain' : self._domain,
+ 'language' : self._language,
+ 'msgid' : message[0],
+ 'msgstr' : message[1][0],
+ 'mod_time' : message[1][1]})
+ return messages
+
+ #
+ ############################################################
+
+ ############################################################
+ # Implementation methods for interface
+ # Zope/ComponentArchitecture/IFactory.py
+
+ def getInterfaces(self):
+ 'See Zope.ComponentArchitecture.IFactory.IFactory'
+ return self.__implements__
#
############################################################
=== Zope3/lib/python/Zope/I18n/TranslationService.py 1.7 => 1.8 ===
from Zope.I18n.IMessageCatalog import IMessageCatalog
from Zope.I18n.ITranslationService import ITranslationService
-from Zope.I18n.IEditableTranslationService import IEditableTranslationService
from Zope.I18n.SimpleTranslationService import SimpleTranslationService
@@ -43,7 +42,7 @@
class TranslationService(BTreeContainer, SimpleTranslationService):
- __implements__ = ILocalTranslationService, IEditableTranslationService
+ __implements__ = ILocalTranslationService
def __init__(self, default_domain='global'):
super(TranslationService, self).__init__()
@@ -95,7 +94,10 @@
############################################################
# Implementation methods for interface
- # Zope.I18n.ITranslationService.
+ # Zope.I18n.ITranslationService.ITranslationService
+
+ ######################################
+ # from: Zope.I18n.ITranslationService.IReadTranslationService
def translate(self, domain, msgid, mapping=None, context=None,
target_language=None):
@@ -124,16 +126,12 @@
# Now we need to do the interpolation
return self.interpolate(text, mapping)
- # end Zope.I18n.ITranslationService
- ############################################################
-
- ############################################################
- # Implementation methods for interface
- # Zope.I18n.IEditableTranslationService.
+ ######################################
+ # from: Zope.I18n.ITranslationService.IWriteTranslationService
def getMessageIdsOfDomain(self, domain, filter='%'):
- 'See IEditableTranslationService'
+ 'See Zope.I18n.ITranslationService.IWriteTranslationService'
filter = filter.replace('%', '.*')
filter_re = re.compile(filter)
@@ -147,8 +145,27 @@
return msgids.keys()
+ def getMessagesOfDomain(self, domain):
+ 'See Zope.I18n.ITranslationService.IWriteTranslationService'
+ messages = []
+ languages = self.getAvailableLanguages(domain)
+ for language in languages:
+ for name in self._catalogs[(language, domain)]:
+ messages += self[name].getMessages()
+ return messages
+
+
+ def getMessage(self, msgid, domain, language):
+ 'See Zope.I18n.ITranslationService.IWriteTranslationService'
+ for name in self._catalogs.get((language, domain), []):
+ try:
+ return self[name].getFullMessage(msgid)
+ except:
+ pass
+ return None
+
def getAllLanguages(self):
- 'See IEditableTranslationService'
+ 'See Zope.I18n.ITranslationService.IWriteTranslationService'
languages = {}
for key in self._catalogs.keys():
languages[key[0]] = None
@@ -156,7 +173,7 @@
def getAllDomains(self):
- 'See IEditableTranslationService'
+ 'See Zope.I18n.ITranslationService.IWriteTranslationService'
domains = {}
for key in self._catalogs.keys():
domains[key[1]] = None
@@ -164,7 +181,7 @@
def getAvailableLanguages(self, domain):
- 'See IEditableTranslationService'
+ 'See Zope.I18n.ITranslationService.IWriteTranslationService'
identifiers = self._catalogs.keys()
identifiers = filter(lambda x, d=domain: x[1] == d, identifiers)
languages = map(lambda x: x[0], identifiers)
@@ -172,42 +189,42 @@
def getAvailableDomains(self, language):
- 'See IEditableTranslationService'
+ 'See Zope.I18n.ITranslationService.IWriteTranslationService'
identifiers = self._catalogs.keys()
identifiers = filter(lambda x, l=language: x[0] == l, identifiers)
domains = map(lambda x: x[1], identifiers)
return domains
- def addMessage(self, domain, msg_id, msg, target_language):
- 'See IEditableTranslationService'
- if not self._catalogs.has_key((target_language, domain)):
- if target_language not in self.getAllLanguages():
- self.addLanguage(target_language)
+ def addMessage(self, domain, msgid, msg, language, mod_time=None):
+ 'See Zope.I18n.ITranslationService.IWriteTranslationService'
+ if not self._catalogs.has_key((language, domain)):
+ if language not in self.getAllLanguages():
+ self.addLanguage(language)
if domain not in self.getAllDomains():
self.addDomain(domain)
- catalog_name = self._catalogs[(target_language, domain)][0]
+ catalog_name = self._catalogs[(language, domain)][0]
catalog = self[catalog_name]
- catalog.setMessage(msg_id, msg)
+ catalog.setMessage(msgid, msg, mod_time)
- def updateMessage(self, domain, msg_id, msg, target_language):
- 'See IEditableTranslationService'
- catalog_name = self._catalogs[(target_language, domain)][0]
+ def updateMessage(self, domain, msgid, msg, language, mod_time=None):
+ 'See Zope.I18n.ITranslationService.IWriteTranslationService'
+ catalog_name = self._catalogs[(language, domain)][0]
catalog = self[catalog_name]
- catalog.setMessage(msg_id, msg)
+ catalog.setMessage(msgid, msg, mod_time)
- def deleteMessage(self, domain, msg_id, target_language):
- 'See IEditableTranslationService'
- catalog_name = self._catalogs[(target_language, domain)][0]
+ def deleteMessage(self, domain, msgid, language):
+ 'See Zope.I18n.ITranslationService.IWriteTranslationService'
+ catalog_name = self._catalogs[(language, domain)][0]
catalog = self[catalog_name]
- catalog.deleteMessage(msg_id)
+ catalog.deleteMessage(msgid)
def addLanguage(self, language):
- 'See IEditableTranslationService'
+ 'See Zope.I18n.ITranslationService.IWriteTranslationService'
domains = self.getAllDomains()
if not domains:
domains = [self.default_domain]
@@ -218,7 +235,7 @@
def addDomain(self, domain):
- 'See IEditableTranslationService'
+ 'See Zope.I18n.ITranslationService.IWriteTranslationService'
languages = self.getAllLanguages()
if not languages:
languages = ['en']
@@ -229,7 +246,7 @@
def deleteLanguage(self, language):
- 'See IEditableTranslationService'
+ 'See Zope.I18n.ITranslationService.IWriteTranslationService'
domains = self.getAvailableDomains(language)
for domain in domains:
# Delete all catalogs from the data storage
@@ -240,7 +257,7 @@
del self._catalogs[(language, domain)]
def deleteDomain(self, domain):
- 'See IEditableTranslationService'
+ 'See Zope.I18n.ITranslationService.IWriteTranslationService'
languages = self.getAvailableLanguages(domain)
for language in languages:
# Delete all catalogs from the data storage
@@ -249,6 +266,51 @@
del self[name]
# Now delete the specifc catalog registry for this lang/domain
del self._catalogs[(language, domain)]
+
+
+ ######################################
+ # from: Zope.I18n.ITranslationService.ISyncTranslationService
+
+ def getMessagesMapping(self, domains, languages, foreign_messages):
+ 'See Zope.I18n.ITranslationService.ISyncTranslationService'
+ mapping = {}
+ # Get all relevant local messages
+ local_messages = []
+ for domain in domains:
+ for language in languages:
+ for name in self._catalogs.get((language, domain), []):
+ local_messages += self[name].getMessages()
+
+
+ for fmsg in foreign_messages:
+ ident = (fmsg['msgid'], fmsg['domain'], fmsg['language'])
+ mapping[ident] = (fmsg, self.getMessage(*ident))
+
+ for lmsg in local_messages:
+ ident = (lmsg['msgid'], lmsg['domain'], lmsg['language'])
+ if ident not in mapping.keys():
+ mapping[ident] = (None, lmsg)
+
+ return mapping
+
+
+ def synchronize(self, messages_mapping):
+ 'See Zope.I18n.ITranslationService.ISyncTranslationService'
+
+ for value in messages_mapping.values():
+ fmsg = value[0]
+ lmsg = value[1]
+ if fmsg is None:
+ self.deleteMessage(lmsg['domain'], lmsg['msgid'],
+ lmsg['language'])
+ elif lmsg is None:
+ self.addMessage(fmsg['domain'], fmsg['msgid'],
+ fmsg['msgstr'], fmsg['language'],
+ fmsg['mod_time'])
+ elif fmsg['mod_time'] > lmsg['mod_time']:
+ self.updateMessage(fmsg['domain'], fmsg['msgid'],
+ fmsg['msgstr'], fmsg['language'],
+ fmsg['mod_time'])
#
############################################################
=== Zope3/lib/python/Zope/I18n/i18n.zcml 1.8 => 1.9 ===
interface="Zope.I18n.ITranslationService."
/>
- <security:require permission="Zope.ManageServices"
- interface="Zope.I18n.IEditableTranslationService."
- />
+
<security:require permission="Zope.ManageServices"
interface="Zope.App.OFS.Container.IContainer."
/>
@@ -56,7 +54,9 @@
permission="Zope.Public"
component=".GlobalTranslationService.translationService" />
+<zmi:icon for=".ITranslationService." file="./i18n_service.gif" />
+<!-- Setup Message Catalogs -->
<content class=".MessageCatalog.">
<security:require permission="Zope.Security"
interface=".IMessageCatalog."
@@ -69,6 +69,18 @@
<factory component=".MessageCatalog."
id="Message Catalog"/>
+
+
+<!-- Setup Export and Import Filters -->
+<adapter factory=".GettextExportFilter."
+ for=".IEditableTranslationService."
+ provides=".IMessageExportFilter."
+ />
+
+<adapter factory=".GettextImportFilter."
+ for=".IEditableTranslationService."
+ provides=".IMessageImportFilter."
+ />
<gts:registerTranslations directory="./locale" />
=== Removed File Zope3/lib/python/Zope/I18n/IEditableTranslationService.py ===