[Zope-Checkins] CVS: Zope3/lib/python/Zope/App/OFS/Services/TranslationService/Views/Browser - BaseTranslationServiceView.py:1.1 ExportImport.py:1.1 Synchronize.py:1.1 Translate.py:1.1 __init__.py:1.1 configure.zcml:1.1 exportImport.pt:1.1 synchronize.pt:1.1 translate.pt:1.1 translateMessage.pt:1.1

Stephan Richter srichter@cbu.edu
Thu, 11 Jul 2002 03:12:45 -0400


Update of /cvs-repository/Zope3/lib/python/Zope/App/OFS/Services/TranslationService/Views/Browser
In directory cvs.zope.org:/tmp/cvs-serv24977/lib/python/Zope/App/OFS/Services/TranslationService/Views/Browser

Added Files:
	BaseTranslationServiceView.py ExportImport.py Synchronize.py 
	Translate.py __init__.py configure.zcml exportImport.pt 
	synchronize.pt translate.pt translateMessage.pt 
Log Message:
I moved the OFS-specific parts of the Translation Service to 
Zope.App.OFS.Services, which is the way all the other local/placeful 
service impolementations do it.


=== Added File Zope3/lib/python/Zope/App/OFS/Services/TranslationService/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/07/11 07:12:42 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/App/OFS/Services/TranslationService/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/07/11 07:12:42 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.response.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.response.redirect(self.request.URL[-1])

    


=== Added File Zope3/lib/python/Zope/App/OFS/Services/TranslationService/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/07/11 07:12:42 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

        # 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.response.setCookie('sync_domains',
                                             ','.join(self.sync_domains))
        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]
            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.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/lib/python/Zope/App/OFS/Services/TranslationService/Views/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 2002/07/11 07:12:42 srichter Exp $
"""
from Zope.App.PageTemplate import ViewPageTemplateFile

from BaseTranslationServiceView import BaseTranslationServiceView

class Translate(BaseTranslationServiceView):
    
    index = ViewPageTemplateFile('translate.pt')
    translateMessage = ViewPageTemplateFile('translateMessage.pt')


    def getMessages(self):
        """Get messages based on the domain selection"""
        filter = self.request.get('filter', '%')
        domains = self.getEditDomains()
        messages = []
        for domain in domains:
            for msg_id in self.context.getMessageIdsOfDomain(domain, filter):
                messages.append((msg_id, domain, len(messages)))

        return messages


    def getTranslation(self, domain, msgid, target_lang):
        """ """
        return self.context.translate(domain, 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 getEditDomains(self):
        '''get the languages that are selected for editing'''
        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.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:
                domain = self.request.get('new-domain-%i' %count, 'default')
                for language in self.getEditLanguages():
                    msg = self.request.get('new-%s-%i' %(language, count),
                                           msg_id)
                    self.context.addMessage(domain, 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]
            domain = self.request['edit-domain-'+key]
            for language in self.getEditLanguages():
                msg = self.request['edit-%s-%s' %(language, key)]
                if msg != self.context.translate(domain, msg_id,
                                                 target_language=language):
                    self.context.updateMessage(domain, msg_id, msg, language)
                
        return self.request.response.redirect(self.request.URL[-1])


    def deleteMessages(self, message_ids):
        """ """
        for id in message_ids:
            domain = self.request.form['edit-domain-%s' %id]
            msgid = self.request.form['edit-msg_id-%s' %id]
            for language in self.context.getAvailableLanguages(domain):
                # Some we edit a language, but no translation exists...
                try:
                    self.context.deleteMessage(domain, 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 addDomain(self, domain):
        """ """
        self.context.addDomain(domain)
        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 changeEditDomains(self, domains=[]):
        """ """
        self.request.response.setCookie('edit_domains', ','.join(domains))
        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])


    def deleteDomains(self, domains):
        """ """
        for domain in domains:
            self.context.deleteDomain(domain)
        return self.request.response.redirect(self.request.URL[-1])




=== Added File Zope3/lib/python/Zope/App/OFS/Services/TranslationService/Views/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.
# 
##############################################################################



=== Added File Zope3/lib/python/Zope/App/OFS/Services/TranslationService/Views/Browser/configure.zcml ===
<zopeConfigure
   xmlns="http://namespaces.zope.org/zope"
   xmlns:browser="http://namespaces.zope.org/browser">

<browser:defaultView for="Zope.I18n.ITranslationService." name="index.html" />

<browser:view 
   permission="Zope.ManageServices" 
   for="Zope.I18n.ITranslationService."
   factory=".Translate.">

   <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" />

   <browser:page name="addLanguage.html" attribute="addLanguage" />
   <browser:page name="addDomain.html" attribute="addDomain" />

   <browser:page name="changeEditLanguages.html" 
                 attribute="changeEditLanguages" />
   <browser:page name="changeEditDomains.html" 
                 attribute="changeEditDomains" />
   <browser:page name="changeFilter.html" 
                 attribute="changeFilter" />

   <browser:page name="deleteLanguages.html" attribute="deleteLanguages" />
   <browser:page name="deleteDomains.html" attribute="deleteDomains" />

</browser:view>

<browser:view 
   permission="Zope.ManageServices" 
   for="Zope.I18n.ITranslationService."
   factory=".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=".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>


<browser:menuItems menu="zmi_views" for="Zope.I18n.ITranslationService.">
  <browser:menuItem title="Translate" action="@@index.html"/>
  <browser:menuItem title="Import/Export" action="@@exportImportForm.html"/>
  <browser:menuItem title="Synchronize" action="@@synchronizeForm.html"/>
</browser:menuItems>

<browser:menuItem menu="add_service" for="Zope.App.OFS.Container.IAdding."
     action="TranslationService"  title="Translation Service"
     description="A Persistent Translation Service for TTW development" />

</zopeConfigure>


=== Added File Zope3/lib/python/Zope/App/OFS/Services/TranslationService/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/App/OFS/Services/TranslationService/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">&nbsp;</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/App/OFS/Services/TranslationService/Views/Browser/translate.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">

<table cols="4" width="100%" border="0">
  <tr>
    <td width="25%">
      <form action="./" method="post">
        <div class="form-label" i18n:translate="">Select Languages:</div>
        <div>
          <select name="languages:list" size="3" 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.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">
          <input class="form-element" type="submit" 
                 name="@@deleteLanguages.html:method" value="Delete"
		 i18n:attributes="value">
        </div>
      </form>
    </td>
    <td width="25%">
      <form action="./" method="post">
        <div class="form-label" i18n:translate="">Select Domains:</div>
        <div>
          <select name="domains:list" size="3" 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.getEditDomains()" ></option>
              <option value="" selected="1"
		      tal:attributes="value domain"
		      tal:content="domain"
                      tal:condition="python: domain in
                                     view.getEditDomains()" ></option>
            </tal:block>
            </select>
          </div>
          <div>
            <input class="form-element" type="submit" 
                   name="@@changeEditDomains.html:method" value="Edit"
                   i18n:attributes="value">
            <input class="form-element" type="submit" 
                   name="@@deleteDomains.html:method" value="Delete"
                   i18n:attributes="value">
          </div>
        </form>
      </td>
      <td width="25%" 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">
          </div>
          <div class="form-label" i18n:translate="">New Domain:</div>
          <div>
            <input type="text" name="domain" size="20" value="">
            <input type="submit" name="@@addDomain.html:method" value="Add">
          </div>
        </form>
      </td>
      <td width="25%" 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"/>
          </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">&nbsp;</th>
        <th i18n:translate="">Message Id</th>
        <th i18n:translate="">Domain</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[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">
      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">&nbsp;</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>
    <input class="form-element" type="submit" 
           name="@@editMessages.html:method" value="Edit Messages"
           i18n:attributes="value">&nbsp;
    <input class="form-element" type="submit" 
           name="@@deleteMessages.html:method" value="Delete Messages"
           i18n:attributes="value">
  </div>
  </form>

</span>
</div>

</body>
</html>


=== Added File Zope3/lib/python/Zope/App/OFS/Services/TranslationService/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>