[Zope3-checkins] CVS: Zope3/src/zope/app/i18n/browser -
__init__.py:1.1 configure.zcml:1.1 exportimport.pt:1.1
exportimport.py:1.1 i18n_domain.gif:1.1 synchronize.pt:1.1
synchronize.py:1.1 translate.pt:1.1 translate.py:1.1
translatemessage.pt:1.1
Stephan Richter
srichter at cosmos.phy.tufts.edu
Mon Mar 8 18:34:19 EST 2004
Update of /cvs-repository/Zope3/src/zope/app/i18n/browser
In directory cvs.zope.org:/tmp/cvs-serv4661/src/zope/app/i18n/browser
Added Files:
__init__.py configure.zcml exportimport.pt exportimport.py
i18n_domain.gif synchronize.pt synchronize.py translate.pt
translate.py translatemessage.pt
Log Message:
Moved zope.app.browser.services.translation to zope.i18n.browser. Updated the
code to reflect the new API of using utilities.
=== Added File Zope3/src/zope/app/i18n/browser/__init__.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 Views
$Id: __init__.py,v 1.1 2004/03/08 23:34:18 srichter Exp $
"""
from zope.i18n.interfaces import ITranslationDomain
class BaseView(object):
__used_for__ = ITranslationDomain
def getAllLanguages(self):
"""Get all available languages from the Translation Domain."""
return self.context.getAllLanguages()
=== Added File Zope3/src/zope/app/i18n/browser/configure.zcml ===
<zope:configure
xmlns:zope="http://namespaces.zope.org/zope"
xmlns="http://namespaces.zope.org/browser">
<pages
permission="zope.ManageServices"
for="zope.i18n.interfaces.ITranslationDomain"
class=".translate.Translate">
<page name="translate.html" template="translate.pt"
menu="zmi_views" title="Translate" />
<page name="translateMessage.html" template="translatemessage.pt" />
<page name="editMessages.html" attribute="editMessages" />
<page name="deleteMessages.html" attribute="deleteMessages" />
<page name="addLanguage.html" attribute="addLanguage" />
<page name="changeEditLanguages.html" attribute="changeEditLanguages" />
<page name="deleteLanguages.html" attribute="deleteLanguages" />
<page name="changeFilter.html" attribute="changeFilter" />
</pages>
<pages
permission="zope.ManageServices"
for="zope.i18n.interfaces.ITranslationDomain"
class=".exportimport.ExportImport">
<page name="exportImportForm.html" template="exportimport.pt"
menu="zmi_views" title="Import/Export" />
<page name="export.html" attribute="exportMessages" />
<page name="import.html" attribute="importMessages" />
</pages>
<pages
permission="zope.ManageServices"
for="zope.i18n.interfaces.ITranslationDomain"
class=".synchronize.Synchronize">
<page name="synchronizeForm.html" template="synchronize.pt"
menu="zmi_views" title="Synchronize" />
<page name="synchronize.html" attribute="synchronize" />
<page name="synchronizeMessages.html" attribute="synchronizeMessages" />
<page name="saveSettings.html" attribute="saveSettings" />
</pages>
<defaultView
for="zope.i18n.interfaces.ITranslationDomain"
name="translate.html"/>
<!-- Menu entry for "Add Utility" menu -->
<addMenuItem
class="zope.app.i18n.translationdomain.TranslationDomain"
title="Translation Domain"
description="A Persistent Translation Domain"
permission="zope.ManageServices"
/>
<!-- Custom Domain registration screen -->
<addform
label="New Translation Domain Registration"
for="zope.app.i18n.interfaces.ILocalTranslationDomain"
name="addRegistration.html"
schema="zope.app.interfaces.services.utility.IUtilityRegistration"
class="zope.app.browser.services.utility.AddRegistration"
permission="zope.ManageServices"
content_factory="zope.app.i18n.translationdomain.DomainRegistration"
arguments="name interface componentPath"
set_after_add="status"
fields="name interface componentPath permission status"
usage="addingdialog" />
<icon
name="zmi_icon"
for="zope.i18n.interfaces.ITranslationDomain"
file="./i18n_domain.gif" />
</zope:configure>
=== Added File Zope3/src/zope/app/i18n/browser/exportimport.pt ===
<html metal:use-macro="views/standard_macros/page">
<head>
<title metal:fill-slot="title" i18n:translate="">
Translation Domain - Translate
</title>
</head>
<body>
<div metal:fill-slot="body">
<h3 i18n:translate="">Import and Export Messages</h3>
<p i18n:translate="">
Here you can export and import messages from your Translation Domain.
</p>
<form action="./" method="post" enctype="multipart/form-data">
<table cols="2" width="100%" border="0">
<tr>
<td width="50%">
<div class="form-label" i18n:translate="">Select Languages:</div>
<div>
<select name="languages:list" size="3" style="width: 80%"
multiple="multiple">
<option value=""
tal:attributes="value language"
tal:content="language"
tal:repeat="language view/getAllLanguages"></option>
</select>
</div>
</td>
<td width="50%" valign="top">
<div class="form-label" i18n:translate="">Import File Name:</div>
<div>
<input type="file" name="file" size="20" value="" />
</div>
<div>
<input type="submit" name="@@import.html:method" value="Import"
i18n:attributes="value import-button"/>
<input type="submit" name="@@export.html:method" value="Export"
i18n:attributes="value export-button"/>
</div>
</td>
</tr>
</table>
</form>
</div>
</body>
</html>
=== Added File Zope3/src/zope/app/i18n/browser/exportimport.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.
#
##############################################################################
"""Message Export/Import View
$Id: exportimport.py,v 1.1 2004/03/08 23:34:18 srichter Exp $
"""
from zope.app.i18n.browser import BaseView
from zope.i18n.interfaces import IMessageExportFilter, IMessageImportFilter
class ExportImport(BaseView):
def exportMessages(self, languages):
self.request.response.setHeader('content-type',
'application/x-gettext')
filter = IMessageExportFilter(self.context)
return filter.exportMessages(languages)
def importMessages(self, languages, file):
filter = IMessageImportFilter(self.context)
filter.importMessages(languages, file)
return self.request.response.redirect(self.request.URL[-1])
=== Added File Zope3/src/zope/app/i18n/browser/i18n_domain.gif ===
<Binary-ish file>
=== Added File Zope3/src/zope/app/i18n/browser/synchronize.pt ===
<html metal:use-macro="views/standard_macros/page">
<head>
<title metal:fill-slot="title" i18n:translate="">
Translation Service - Synchronize
</title>
<style metal:fill-slot="style_slot" type="text/css">
<!--
.state0 {color: green;}
.state1 {color: yellow;}
.state2 {color: yellow;}
.state3 {color: red;}
.state4 {color: red;}
-->
</style>
</head>
<body>
<div metal:fill-slot="body">
<table cols="3" width="100%" border="0" cellspacing="0">
<form action="./" method="post">
<tr>
<td width="40%">
<div class="form-label" i18n:translate="">Server URL</div>
<div>
<input type="text" size="40" name="sync_url" value=""
tal:attributes="value view/sync_url" />
</div>
<div i18n:translate="">Username</div>
<div>
<input type="text" size="40" name="sync_username" value=""
tal:attributes="value view/sync_username" />
</div>
<div i18n:translate="">Password</div>
<div>
<input type="password" size="40" name="sync_password" value=""
tal:attributes="value view/sync_password" />
</div>
</td>
<td width="30%">
<div i18n:translate="">Select Languages:</div>
<div>
<select name="sync_languages:list" size="6" style="width: 80%"
multiple="multiple">
<tal:block repeat="language view/getAllLanguages">
<option value=""
tal:attributes="value language"
tal:content="language"
tal:condition="python: language not in
view.sync_languages" ></option>
<option value="" selected="1"
tal:attributes="value language"
tal:content="language"
tal:condition="python: language in
view.sync_languages" ></option>
</tal:block>
</select>
</div>
</td>
<td width="30%">
<div align="center">
<div>
<input type="submit" name="saveSettings.html:method"
value="Save Settings"
i18n:attributes="value save-settings-button" />
</div><br />
<div>
<input type="submit" name="synchronize.html:method"
value="Synchronize"
i18n:attributes="value synchronize-button"/>
</div>
</div>
</td>
</tr>
</form>
</table>
<br />
<form action="./"
tal:condition="view/canConnect">
<table cols="5" width="95%" border="0" cellpadding="2" cellspacing="0"
class="listing">
<tr>
<th width="16"> </th>
<th width="55%" i18n:translate="">Message Id</th>
<th width="15%" i18n:translate="">Language</th>
<th width="20%" i18n:translate="">Status</th>
</tr>
<tal:block repeat="message python: view.queryMessages().items()">
<tr tal:define="number repeat/message/number;
oddrow repeat/message/odd"
tal:attributes="class python: oddrow and 'odd' or 'even'">
<td width="16">
<input type="hidden"
tal:attributes="name python: 'update-msgid-%i' %number;
value python: message[0][0]" />
<input type="hidden"
tal:attributes="name python: 'update-language-%i' %number;
value python: message[0][1]" />
<input type="checkbox" name="message_ids:list"
tal:attributes="value python: number" />
</td>
<td tal:content="python: message[0][0]">Hello World!</td>
<td tal:content="python: message[0][1]">en</td>
<td>
<b tal:content="python: view.getStatus(*message[1])"
tal:attributes="class python:'state%i' %
view.getStatus(message[1][0], message[1][1], 0)"
>status</b>
</td>
</tr>
</tal:block>
</table>
<div>
<input type="submit" name="@@synchronizeMessages.html:method"
value="Update" i18n:attributes="value update-button"/>
</div>
</form>
<p tal:condition="python: not view.canConnect()" i18n:translate="">
No connection could be made to remote data source.
</p>
</div>
</body>
</html>
=== Added File Zope3/src/zope/app/i18n/browser/synchronize.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.
#
##############################################################################
"""Synchronize with Foreign Translation Services
$Id: synchronize.py,v 1.1 2004/03/08 23:34:18 srichter Exp $
"""
import httplib
import urllib
import xmlrpclib
from base64 import encodestring
from zope.app.i18n.browser import BaseView
from zope.app.i18n import ZopeMessageIDFactory as _
DEFAULT = 'http://localhost:8080/++etc++site/default/zope'
class Synchronize(BaseView):
messageStatus = [_('Up to Date'), _('New Remote'), _('Out of Date'),
_('Newer Local'), _('Does not exist')]
def __init__(self, context, request):
super(Synchronize, self).__init__(context, request)
self.sync_url = self.request.cookies.get(
'sync_url', DEFAULT)
self.sync_url = urllib.unquote(self.sync_url)
self.sync_username = self.request.cookies.get('sync_username', 'admin')
self.sync_password = self.request.cookies.get('sync_password', 'admin')
self.sync_languages = filter(None, self.request.cookies.get(
'sync_languages', '').split(','))
def _connect(self):
'''Connect to the remote server via XML-RPC HTTP; return status'''
# make sure the URL contains the http:// prefix
if not self.sync_url.startswith('http://'):
url = 'http://' + self.sync_url
else:
url = self.sync_url
# Now try to connect
self._connection = xmlrpclib.Server(
url, transport = BasicAuthTransport(self.sync_username,
self.sync_password))
# check whether the connection was made and the Master Babel Tower
# exists
try:
self._connection.getAllLanguages()
return 1
except:
self._connection = None
return 0
def _disconnect(self):
'''Disconnect from the sever; return None'''
if hasattr(self, '_connection') and self._connection is not None:
self._connection = None
def _isConnected(self):
'''Check whether we are currently connected to the server; return
boolean'''
if not hasattr(self, '_connection'):
self._connection = None
if not self._connection is None and self._connection.getAllLanguages():
return 1
else:
return 0
def canConnect(self):
'''Checks whether we can connect using this server and user data;
return boolean'''
if self._isConnected():
return 1
else:
try:
return self._connect()
except:
return 0
def getAllLanguages(self):
connected = self._isConnected()
if not connected: connected = self._connect()
if connected:
return self._connection.getAllLanguages()
else:
return []
def queryMessages(self):
connected = self._isConnected()
if not connected: connected = self._connect()
if connected:
fmsgs = self._connection.getMessagesFor(self.sync_languages)
else:
fmdgs = []
return self.context.getMessagesMapping(self.sync_languages,
fmsgs)
def getStatus(self, fmsg, lmsg, verbose=1):
state = 0
if fmsg is None:
state = 4
elif lmsg is None:
state = 1
elif fmsg['mod_time'] > lmsg['mod_time']:
state = 2
elif fmsg['mod_time'] < lmsg['mod_time']:
state = 3
elif fmsg['mod_time'] == lmsg['mod_time']:
state = 0
if verbose:
return self.messageStatus[state]
return state
def saveSettings(self):
self.sync_languages = self.request.form.get('sync_languages', [])
self.request.response.setCookie('sync_languages',
','.join(self.sync_languages))
self.request.response.setCookie('sync_url',
urllib.quote(self.request['sync_url']).strip())
self.request.response.setCookie('sync_username',
self.request['sync_username'])
self.request.response.setCookie('sync_password',
self.request['sync_password'])
return self.request.response.redirect(self.request.URL[-1]+
'/@@synchronizeForm.html')
def synchronize(self):
mapping = self.queryMessages()
self.context.synchronize(mapping)
return self.request.response.redirect(self.request.URL[-1]+
'/@@synchronizeForm.html')
def synchronizeMessages(self):
idents = []
for id in self.request.form['message_ids']:
msgid = self.request.form['update-msgid-'+id]
language = self.request.form['update-language-'+id]
idents.append((msgid, language))
mapping = self.queryMessages()
new_mapping = {}
for item in mapping.items():
if item[0] in idents:
new_mapping[item[0]] = item[1]
self.context.synchronize(new_mapping)
return self.request.response.redirect(self.request.URL[-1]+
'/@@synchronizeForm.html')
class BasicAuthTransport(xmlrpclib.Transport):
def __init__(self, username=None, password=None, verbose=0):
self.username=username
self.password=password
self.verbose=verbose
def request(self, host, handler, request_body, verbose=0):
# issue XML-RPC request
self.verbose = verbose
h = httplib.HTTP(host)
h.putrequest("POST", handler)
# required by HTTP/1.1
h.putheader("Host", host)
# required by XML-RPC
h.putheader("User-Agent", self.user_agent)
h.putheader("Content-Type", "text/xml")
h.putheader("Content-Length", str(len(request_body)))
# basic auth
if self.username is not None and self.password is not None:
h.putheader("AUTHORIZATION", "Basic %s" %
encodestring("%s:%s" % (self.username, self.password)
).replace("\012", ""))
h.endheaders()
if request_body:
h.send(request_body)
errcode, errmsg, headers = h.getreply()
if errcode != 200:
raise xmlrpclib.ProtocolError(host + handler,
errcode, errmsg, headers)
return self.parse_response(h.getfile())
=== Added File Zope3/src/zope/app/i18n/browser/translate.pt ===
<html metal:use-macro="views/standard_macros/page">
<head>
<title metal:fill-slot="title" i18n:translate="">
Translation Domain - Translate
</title>
</head>
<body>
<div metal:fill-slot="body">
<table cols="3" width="100%" border="0">
<tr>
<td width="35%">
<form action="./" method="post">
<div class="form-label" i18n:translate="">Select Languages:</div>
<div>
<select name="languages:list" size="3" style="width: 80%"
multiple="multiple">
<tal:block repeat="language view/getAllLanguages">
<option value=""
tal:attributes="value language"
tal:content="language"
tal:condition="python: language not in
view.getEditLanguages()" ></option>
<option value="" selected="1"
tal:attributes="value language"
tal:content="language"
tal:condition="python: language in
view.getEditLanguages()" ></option>
</tal:block>
</select>
</div>
<div>
<input class="form-element" type="submit"
name="@@changeEditLanguages.html:method" value="Edit"
i18n:attributes="value edit-button" />
<input class="form-element" type="submit"
name="@@deleteLanguages.html:method" value="Delete"
i18n:attributes="value delete-button" />
</div>
</form>
</td>
<td width="35%" align="right" valign="top">
<form action="." method="post">
<div class="form-label" i18n:translate="">New Language:</div>
<div>
<input type="text" name="language" size="20" value="" />
<input type="submit" name="@@addLanguage.html:method" value="Add"
i18n:attributes="value add-button" />
</div>
</form>
</td>
<td width="30%" align="right" valign="top">
<form action="./" method="post">
<div class="form-label"
i18n:translate="">Filter (% - wildcard):</div>
<div>
<input type="text" name="filter" size="25" value=""
tal:attributes="value request/filter|default" />
</div>
<div>
<input type="submit" name="@@changeFilter.html:method"
value="Filter"
i18n:attributes="value filter-button"/>
</div>
</form>
</td>
</tr>
</table>
<form action="./" method="post">
<table width="100%" cellspacing="0" cellpadding="3" border="0"
class="listing">
<tr class="list-header" align="left">
<th width="16"> </th>
<th i18n:translate="">Message Id</th>
<th tal:repeat="language python:view.getEditLanguages()"
tal:content="language">de</th>
</tr>
<tal:block repeat="message python: view.getMessages()">
<tr tal:define="oddrow repeat/message/odd"
tal:attributes="class python: oddrow and 'odd' or 'even'">
<td>
<input type="hidden"
tal:attributes="name python: 'edit-msg_id-%i' %message[1];
value python: message[0]" />
<input type="checkbox" name="message_ids:list"
tal:attributes="value python: message[1]" />
</td>
<td tal:content="python: message[0]">
default
</td>
<td tal:repeat="language python:view.getEditLanguages()">
<textarea cols="20" rows="2"
tal:attributes="name python: 'edit-%s-%i' %(language, message[1])"
tal:content="python: view.getTranslation(message[0],
language)"></textarea>
</td>
</tr>
</tal:block>
<tr>
<th colspan="3"
tal:attributes="colspan python:len(view.getEditLanguages())+3">
Add new messages
</th>
</tr>
<tal:block repeat="count python: range(5)">
<tr tal:define="oddrow repeat/count/odd"
tal:attributes="class python: oddrow and 'odd' or 'even'">
<td width="16"> </td>
<td>
<textarea cols="20" rows="2" name=""
tal:attributes="name string:new-msg_id-${count}"
></textarea>
</td>
<td tal:repeat="language python:view.getEditLanguages()">
<textarea cols="20" rows="2" name=""
tal:attributes="name string:new-${language}-${count}"
></textarea>
</td>
</tr>
</tal:block>
</table>
<div>
<input class="form-element" type="submit"
name="@@editMessages.html:method" value="Edit Messages"
i18n:attributes="value" />
<input class="form-element" type="submit"
name="@@deleteMessages.html:method" value="Delete Messages"
i18n:attributes="value" />
</div>
</form>
</div>
</body>
</html>
=== Added File Zope3/src/zope/app/i18n/browser/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.
#
##############################################################################
"""Translation GUI
$Id: translate.py,v 1.1 2004/03/08 23:34:18 srichter Exp $
"""
from zope.app.i18n.browser import BaseView
class Translate(BaseView):
def getMessages(self):
"""Get messages"""
filter = self.request.get('filter', '%')
messages = []
for msg_id in self.context.getMessageIds(filter):
messages.append((msg_id, len(messages)))
return messages
def getTranslation(self, msgid, target_lang):
return self.context.translate(msgid, target_language=target_lang)
def getEditLanguages(self):
'''get the languages that are selected for editing'''
languages = self.request.cookies.get('edit_languages', '')
return filter(None, languages.split(','))
def editMessage(self):
msg_id = self.request['msg_id']
for language in self.getEditLanguages():
msg = self.request['msg_lang_%s' %language]
if msg != self.context.translate(msg_id,
target_language=language):
self.context.updateMessage(msg_id, msg, language)
return self.request.response.redirect(self.request.URL[-1])
def editMessages(self):
# Handle new Messages
for count in range(5):
msg_id = self.request.get('new-msg_id-%i' %count, '')
if msg_id:
for language in self.getEditLanguages():
msg = self.request.get('new-%s-%i' %(language, count),
msg_id)
self.context.addMessage(msg_id, msg, language)
# Handle edited Messages
keys = filter(lambda k: k.startswith('edit-msg_id-'),
self.request.keys())
keys = map(lambda k: k[12:], keys)
for key in keys:
msg_id = self.request['edit-msg_id-'+key]
for language in self.getEditLanguages():
msg = self.request['edit-%s-%s' %(language, key)]
if msg != self.context.translate(msg_id,
target_language=language):
self.context.updateMessage(msg_id, msg, language)
return self.request.response.redirect(self.request.URL[-1])
def deleteMessages(self, message_ids):
for id in message_ids:
msgid = self.request.form['edit-msg_id-%s' %id]
for language in self.context.getAvailableLanguages():
# Some we edit a language, but no translation exists...
try:
self.context.deleteMessage(msgid, language)
except KeyError:
pass
return self.request.response.redirect(self.request.URL[-1])
def addLanguage(self, language):
self.context.addLanguage(language)
return self.request.response.redirect(self.request.URL[-1])
def changeEditLanguages(self, languages=[]):
self.request.response.setCookie('edit_languages',
','.join(languages))
return self.request.response.redirect(self.request.URL[-1])
def changeFilter(self):
filter = self.request.get('filter', '%')
self.request.response.setCookie('filter', filter)
return self.request.response.redirect(self.request.URL[-1])
def deleteLanguages(self, languages):
for language in languages:
self.context.deleteLanguage(language)
return self.request.response.redirect(self.request.URL[-1])
=== Added File Zope3/src/zope/app/i18n/browser/translatemessage.pt ===
<html metal:use-macro="views/standard_macros/page">
<head>
<title metal:fill-slot="title" i18n:translate="">
Translation Service - Translate
</title>
</head>
<body>
<div metal:fill-slot="body">
<form action="./" method="post">
<input type="hidden" name="msg_id" value=""
tal:attributes="value request/msgid" />
<table>
<tr>
<th i18n:translate="">Message Id</th>
<td tal:content="request/msgid">Message Id of the message.</td>
</tr>
<tr tal:repeat="language view/getEditLanguages">
<th tal:content="language">Language</th>
<td>
<textarea cols="80" rows="10" name=""
tal:attributes="name string:msg_lang_${language}"
tal:content="python: view.getTranslation(
request['msgid'], language)"
>Translation of Message</textarea>
</td>
</tr>
</table>
<input class="form-element" type="submit"
name="@@editMessage.html:method" value="Edit Message"
i18n:attributes="value" />
</form>
</div>
</body>
</html>
More information about the Zope3-Checkins
mailing list