[Zope-Checkins] CVS: Zope3/lib/python/Zope/I18n/Views/Browser - BaseTranslationServiceView.py:1.1 ExportImport.py:1.1 Synchronize.py:1.1 exportImport.pt:1.1 synchronize.pt:1.1 translateMessage.pt:1.1 Translate.py:1.3 browser.zcml:1.3 translate.pt:1.10
Stephan Richter
srichter@cbu.edu
Sun, 16 Jun 2002 14:25:15 -0400
Update of /cvs-repository/Zope3/lib/python/Zope/I18n/Views/Browser
In directory cvs.zope.org:/tmp/cvs-serv23163/Views/Browser
Modified Files:
Translate.py browser.zcml translate.pt
Added Files:
BaseTranslationServiceView.py ExportImport.py Synchronize.py
exportImport.pt synchronize.pt translateMessage.pt
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/Views/Browser/BaseTranslationServiceView.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: BaseTranslationServiceView.py,v 1.1 2002/06/16 18:25:13 srichter Exp $
"""
from Zope.Publisher.Browser.BrowserView import BrowserView
from Zope.I18n.ITranslationService import ITranslationService
class BaseTranslationServiceView(BrowserView):
__used_for__ = ITranslationService
def getAllLanguages(self):
"""Get all available languages from the Translation Service."""
return self.context.getAllLanguages()
def getAllDomains(self):
return self.context.getAllDomains()
=== Added File Zope3/lib/python/Zope/I18n/Views/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 2002/06/16 18:25:13 srichter Exp $
"""
from Zope.ComponentArchitecture import getAdapter
from Zope.App.PageTemplate import ViewPageTemplateFile
from Zope.I18n.IMessageExportFilter import IMessageExportFilter
from Zope.I18n.IMessageImportFilter import IMessageImportFilter
from BaseTranslationServiceView import BaseTranslationServiceView
class ExportImport(BaseTranslationServiceView):
""" """
exportImportForm = ViewPageTemplateFile('exportImport.pt')
def exportMessages(self, domains, languages):
self.request.getResponse().setHeader('content-type',
'application/x-gettext')
filter = getAdapter(self.context, IMessageExportFilter)
return filter.exportMessages(domains, languages)
def importMessages(self, domains, languages, file):
filter = getAdapter(self.context, IMessageImportFilter)
filter.importMessages(domains, languages, file)
return self.request.getResponse().redirect(self.request.URL[-1])
=== Added File Zope3/lib/python/Zope/I18n/Views/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 2002/06/16 18:25:13 srichter Exp $
"""
import xmlrpclib, httplib, urllib
from base64 import encodestring
from Zope.App.PageTemplate import ViewPageTemplateFile
from BaseTranslationServiceView import BaseTranslationServiceView
class Synchronize(BaseTranslationServiceView):
synchronizeForm = ViewPageTemplateFile('synchronize.pt')
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',
'http://localhost:8081/++etc++Services/ts/')
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_domains = filter(None, self.request.cookies.get(
'sync_domains', '').split(','))
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
url += '++view++methods/'
# 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.getAllDomains()
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.getAllDomains():
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 getAllDomains(self):
connected = self._isConnected()
if not connected: connected = self._connect()
if connected:
return self._connection.getAllDomains()
else:
return []
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_domains,
self.sync_languages)
else:
fmdgs = []
return self.context.getMessagesMapping(self.sync_domains,
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_domains = self.request.form.get('sync_domains', [])
self.sync_languages = self.request.form.get('sync_languages', [])
self.request.getResponse().setCookie('sync_domains',
','.join(self.sync_domains))
self.request.getResponse().setCookie('sync_languages',
','.join(self.sync_languages))
self.request.getResponse().setCookie('sync_url',
urllib.quote(self.request['sync_url']).strip())
self.request.getResponse().setCookie('sync_username',
self.request['sync_username'])
self.request.getResponse().setCookie('sync_password',
self.request['sync_password'])
return self.request.getResponse().redirect(self.request.URL[-1]+
'/@@synchronizeForm.html')
def synchronize(self):
mapping = self.queryMessages()
self.context.synchronize(mapping)
return self.request.getResponse().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]
domain = self.request.form['update-domain-'+id]
language = self.request.form['update-language-'+id]
idents.append((msgid, domain, 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.getResponse().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/lib/python/Zope/I18n/Views/Browser/exportImport.pt ===
<html metal:use-macro="views/standard_macros/page">
<head>
<title>Translation Service - Translate</title>
</head>
<body>
<div metal:fill-slot="body">
<h3>Import and Export Messages</h3>
<p>Here you can export and import messages from your Translation Service.</p>
<form action="./" method="post" enctype="multipart/form-data"
i18n:domain="Zope-I18n">
<table cols="4" width="100%" border="0">
<tr>
<td width="25%">
<div class="form-label" i18n:translate="">Select Languages:</div>
<div>
<select name="languages:list" size="3" style="width: 80%" multiple>
<option value=""
tal:attributes="value language"
tal:content="language"
tal:repeat="language view/getAllLanguages"></option>
</select>
</div>
</td>
<td width="25%">
<div class="form-label" i18n:translate="">Select Domains:</div>
<div>
<select name="domains:list" size="3" style="width: 80%" multiple>
<option value=""
tal:attributes="value domain"
tal:content="domain"
tal:repeat="domain view/getAllDomains"></option>
</select>
</div>
</td>
<td width="25%" 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">
<input type="submit" name="@@export.html:method" value="Export">
</div>
</td>
</tr>
</table>
</form>
</div>
</body>
</html>
=== Added File Zope3/lib/python/Zope/I18n/Views/Browser/synchronize.pt ===
<html metal:use-macro="views/standard_macros/page">
<head>
<title>Translation Service - Synchronize</title>
</head>
<body>
<div metal:fill-slot="body">
<style type="text/css">
<!--
.state0 {color: green;}
.state1 {color: yellow;}
.state2 {color: yellow;}
.state3 {color: red;}
.state4 {color: red;}
-->
</style>
<table cols="4" width="100%" border="0" cellspacing="0">
<form action="./" method="post">
<tr>
<td width="30%">
<div class="form-label">Server URL</div>
<div>
<input type="text" size="40" name="sync_url" value=""
tal:attributes="value view/sync_url" />
</div>
<div>Username</div>
<div>
<input type="text" size="40" name="sync_username" value=""
tal:attributes="value view/sync_username" />
</div>
<div>Password</div>
<div>
<input type="password" size="40" name="sync_password" value=""
tal:attributes="value view/sync_password" />
</div>
</td>
<td width="25%">
<div>Select Domains:</div>
<div>
<select name="sync_domains:list" size="6" style="width: 80%"
multiple>
<tal:block repeat="domain view/getAllDomains">
<option value=""
tal:attributes="value domain"
tal:content="domain"
tal:condition="python: domain not in
view.sync_domains" ></option>
<option value="" selected="1"
tal:attributes="value domain"
tal:content="domain"
tal:condition="python: domain in
view.sync_domains" ></option>
</tal:block>
</select>
</div>
</td>
<td width="25%">
<div>Select Languages:</div>
<div>
<select name="sync_languages:list" size="6" style="width: 80%"
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="20%">
<center>
<div><input type="submit" name="saveSettings.html:method"
value="Save Settings"></div><br>
<div><input type="submit" name="synchronize.html:method"
value="Synchronize"></div>
</center>
</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%">Message Id</th>
<th width="15%">Domain</th>
<th width="10%">Language</th>
<th width="15%">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-domain-%i' %number;
value python: message[0][1]">
<input type="hidden"
tal:attributes="name python: 'update-language-%i' %number;
value python: message[0][2]">
<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]">default</td>
<td tal:content="python: message[0][2]">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"></div>
</form>
<p tal:condition="python: not view.canConnect()">
No connection could be made to remote data source.
</p>
</div>
</body>
</html>
=== Added File Zope3/lib/python/Zope/I18n/Views/Browser/translateMessage.pt ===
<html metal:use-macro="views/standard_macros/page">
<head>
<title>Translation Service - Translate</title>
</head>
<body>
<div metal:fill-slot="body">
<span i18n:domain="Zope-I18n">
<form action="./" method="post">
<input type="hidden" name="msg_domain" value=""
tal:attributes="value request/domain" />
<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['domain'],
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>
</span>
</div>
</body>
</html>
=== Zope3/lib/python/Zope/I18n/Views/Browser/Translate.py 1.2 => 1.3 ===
$Id$
"""
-from Zope.Publisher.Browser.BrowserView import BrowserView
from Zope.App.PageTemplate import ViewPageTemplateFile
-from Zope.I18n.ITranslationService import ITranslationService
+from BaseTranslationServiceView import BaseTranslationServiceView
-class Translate(BrowserView):
- """ """
+class Translate(BaseTranslationServiceView):
- __used_for__ = ITranslationService
-
-
index = ViewPageTemplateFile('translate.pt')
+ translateMessage = ViewPageTemplateFile('translateMessage.pt')
def getMessages(self):
@@ -47,15 +43,6 @@
target_language=target_lang)
- def getAllLanguages(self):
- """Get all available languages from the Translation Service."""
- return self.context.getAllLanguages()
-
-
- def getAllDomains(self):
- return self.context.getAllDomains()
-
-
def getEditLanguages(self):
'''get the languages that are selected for editing'''
languages = self.request.cookies.get('edit_languages', '')
@@ -67,6 +54,18 @@
domains = self.request.cookies.get('edit_domains', '')
return filter(None, domains.split(','))
+
+ def editMessage(self):
+ """ """
+ domain = self.request['msg_domain']
+ msg_id = self.request['msg_id']
+ for language in self.getEditLanguages():
+ msg = self.request['msg_lang_%s' %language]
+ if msg != self.context.translate(domain, msg_id,
+ target_language=language):
+ self.context.updateMessage(domain, msg_id, msg, language)
+ return self.request.getResponse().redirect(self.request.URL[-1])
+
def editMessages(self):
""" """
=== Zope3/lib/python/Zope/I18n/Views/Browser/browser.zcml 1.2 => 1.3 ===
<browser:page name="index.html" attribute="index" />
+ <browser:page name="translateMessage.html" attribute="translateMessage" />
<browser:page name="editMessages.html" attribute="editMessages" />
+ <browser:page name="editMessage.html" attribute="editMessage" />
<browser:page name="deleteMessages.html" attribute="deleteMessages" />
@@ -36,8 +38,36 @@
</browser:view>
+ <browser:view
+ permission="Zope.ManageServices"
+ for="Zope.I18n.ITranslationService."
+ factory="Zope.I18n.Views.Browser.ExportImport.">
+
+ <browser:page name="exportImportForm.html" attribute="exportImportForm" />
+
+ <browser:page name="export.html" attribute="exportMessages" />
+ <browser:page name="import.html" attribute="importMessages" />
+
+ </browser:view>
+
+ <browser:view
+ permission="Zope.ManageServices"
+ for="Zope.I18n.ITranslationService."
+ factory="Zope.I18n.Views.Browser.Synchronize.">
+
+ <browser:page name="synchronizeForm.html" attribute="synchronizeForm" />
+ <browser:page name="synchronize.html" attribute="synchronize" />
+ <browser:page name="synchronizeMessages.html"
+ attribute="synchronizeMessages" />
+ <browser:page name="saveSettings.html" attribute="saveSettings" />
+
+ </browser:view>
+
+
<zmi:tabs for="Zope.I18n.ITranslationService.">
<zmi:tab label="Translate" action="@@index.html"/>
+ <zmi:tab label="Import/Export" action="@@exportImportForm.html"/>
+ <zmi:tab label="Synchronize" action="@@synchronizeForm.html"/>
</zmi:tabs>
</zopeConfigure>
=== Zope3/lib/python/Zope/I18n/Views/Browser/translate.pt 1.9 => 1.10 ===
<form action="./" method="post">
- <table width="100%" cellspacing="0" cellpadding="3" border="0">
+ <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>
@@ -109,56 +110,65 @@
<th tal:repeat="language python:view.getEditLanguages()"
tal:content="language">de</th>
</tr>
- <tr tal:repeat="message python: view.getMessages()">
- <td>
- <input type="hidden"
- tal:attributes="name python: 'edit-msg_id-%i' %message[2];
- value python: message[0]">
- <input type="hidden"
- tal:attributes="name python: 'edit-domain-%i' %message[2];
- value python: message[1]">
- <input type="checkbox" name="message_ids:list"
- tal:attributes="value python: message[2]">
- </td>
- <td>
- <a href="editMessage?messageId="
- tal:content="python: message[0]">message_id</a>
- </td>
- <td tal:content="python: message[1]">
- default
- </td>
- <td tal:repeat="language python:view.getEditLanguages()">
- <textarea cols="20" rows="2"
- tal:attributes="name python: 'edit-%s-%i' %(language, message[2])"
- tal:content="python: view.getTranslation(message[1],
- message[0], language)"></textarea>
- </td>
- </tr>
- <tr><td colspan="3"
+ <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[2];
+ value python: message[0]">
+ <input type="hidden"
+ tal:attributes="name python: 'edit-domain-%i' %message[2];
+ value python: message[1]">
+ <input type="checkbox" name="message_ids:list"
+ tal:attributes="value python: message[2]">
+ </td>
+ <td>
+ <a href=""
+ tal:content="python: message[0]"
+ tal:attributes="
+ href python:'translateMessage.html?msgid=%s&domain=%s' %(
+ message[0], message[1])">message_id</a>
+ </td>
+ <td tal:content="python: message[1]">
+ default
+ </td>
+ <td tal:repeat="language python:view.getEditLanguages()">
+ <textarea cols="20" rows="2"
+ tal:attributes="name python: 'edit-%s-%i' %(language, message[2])"
+ tal:content="python: view.getTranslation(message[1],
+ message[0], language)"></textarea>
+ </td>
+ </tr>
+ </tal:block>
+ <tr><th colspan="3"
tal:attributes="colspan python:len(view.getEditLanguages())+3">
- <hr width="80%" align="center" size="2" noshade="1">
- </td></tr>
+ Add new messages
+ </th></tr>
- <tr tal:repeat="count python:range(5)">
- <td width="16"> </td>
- <td>
- <textarea cols="20" rows="2" name=""
- tal:attributes="name string:new-msg_id-${count}"></textarea>
- </td>
- <td>
- <select name=""
- tal:attributes="name string:new-domain-${count}">
- <option value=""
- tal:repeat="domain python: view.getEditDomains()"
- tal:content="domain"
- tal:attributes="value domain">default</option>
- </select>
- </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 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>
+ <select name=""
+ tal:attributes="name string:new-domain-${count}">
+ <option value=""
+ tal:repeat="domain python: view.getEditDomains()"
+ tal:content="domain"
+ tal:attributes="value domain">default</option>
+ </select>
+ </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>