[Checkins] SVN: zmi.core/trunk/src/zmi/core/i18n/ Copy browser code from zope.app.i18n.

Yusei Tahara yusei at domen.cx
Sun Jun 7 11:27:42 EDT 2009


Log message for revision 100697:
  Copy browser code from zope.app.i18n.
  

Changed:
  A   zmi.core/trunk/src/zmi/core/i18n/__init__.py
  A   zmi.core/trunk/src/zmi/core/i18n/configure.zcml
  A   zmi.core/trunk/src/zmi/core/i18n/exportimport.pt
  A   zmi.core/trunk/src/zmi/core/i18n/exportimport.py
  A   zmi.core/trunk/src/zmi/core/i18n/i18n_domain.gif
  A   zmi.core/trunk/src/zmi/core/i18n/synchronize.pt
  A   zmi.core/trunk/src/zmi/core/i18n/synchronize.py
  A   zmi.core/trunk/src/zmi/core/i18n/tests/
  A   zmi.core/trunk/src/zmi/core/i18n/tests/__init__.py
  A   zmi.core/trunk/src/zmi/core/i18n/tests/test_translate.py
  A   zmi.core/trunk/src/zmi/core/i18n/translate.pt
  A   zmi.core/trunk/src/zmi/core/i18n/translate.py
  A   zmi.core/trunk/src/zmi/core/i18n/translatemessage.pt

-=-
Added: zmi.core/trunk/src/zmi/core/i18n/__init__.py
===================================================================
--- zmi.core/trunk/src/zmi/core/i18n/__init__.py	                        (rev 0)
+++ zmi.core/trunk/src/zmi/core/i18n/__init__.py	2009-06-07 15:27:41 UTC (rev 100697)
@@ -0,0 +1,28 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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 Domain Views
+
+$Id: __init__.py 29143 2005-02-14 22:43:16Z srichter $
+"""
+__docformat__ = 'restructuredtext'
+
+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: zmi.core/trunk/src/zmi/core/i18n/configure.zcml
===================================================================
--- zmi.core/trunk/src/zmi/core/i18n/configure.zcml	                        (rev 0)
+++ zmi.core/trunk/src/zmi/core/i18n/configure.zcml	2009-06-07 15:27:41 UTC (rev 100697)
@@ -0,0 +1,66 @@
+<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"
+      />
+
+  <icon 
+      name="zmi_icon" 
+      for="zope.i18n.interfaces.ITranslationDomain"
+      file="./i18n_domain.gif" />
+
+</zope:configure>

Added: zmi.core/trunk/src/zmi/core/i18n/exportimport.pt
===================================================================
--- zmi.core/trunk/src/zmi/core/i18n/exportimport.pt	                        (rev 0)
+++ zmi.core/trunk/src/zmi/core/i18n/exportimport.pt	2009-06-07 15:27:41 UTC (rev 100697)
@@ -0,0 +1,50 @@
+<html metal:use-macro="context/@@standard_macros/view"
+    i18n:domain="zope">
+<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: zmi.core/trunk/src/zmi/core/i18n/exportimport.py
===================================================================
--- zmi.core/trunk/src/zmi/core/i18n/exportimport.py	                        (rev 0)
+++ zmi.core/trunk/src/zmi/core/i18n/exportimport.py	2009-06-07 15:27:41 UTC (rev 100697)
@@ -0,0 +1,34 @@
+##############################################################################
+#
+# 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.1 (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 26889 2004-08-04 04:00:36Z pruggera $
+"""
+__docformat__ = 'restructuredtext'
+
+from zmi.core.i18n 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: zmi.core/trunk/src/zmi/core/i18n/i18n_domain.gif
===================================================================
(Binary files differ)


Property changes on: zmi.core/trunk/src/zmi/core/i18n/i18n_domain.gif
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: zmi.core/trunk/src/zmi/core/i18n/synchronize.pt
===================================================================
--- zmi.core/trunk/src/zmi/core/i18n/synchronize.pt	                        (rev 0)
+++ zmi.core/trunk/src/zmi/core/i18n/synchronize.pt	2009-06-07 15:27:41 UTC (rev 100697)
@@ -0,0 +1,129 @@
+<html metal:use-macro="context/@@standard_macros/view"
+    i18n:domain="zope">
+<head>
+  <title metal:fill-slot="title" i18n:translate="">
+    Translation Domain - 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">&nbsp;</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.queryMessageItems()">
+        <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: zmi.core/trunk/src/zmi/core/i18n/synchronize.py
===================================================================
--- zmi.core/trunk/src/zmi/core/i18n/synchronize.py	                        (rev 0)
+++ zmi.core/trunk/src/zmi/core/i18n/synchronize.py	2009-06-07 15:27:41 UTC (rev 100697)
@@ -0,0 +1,231 @@
+##############################################################################
+#
+# 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.1 (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 Domains
+
+$Id: synchronize.py 81018 2007-10-24 02:37:33Z fdrake $
+"""
+__docformat__ = 'restructuredtext'
+
+import httplib
+import urllib
+import xmlrpclib
+from base64 import encodestring
+
+import zope.i18nmessageid
+
+from zope.security.proxy import removeSecurityProxy
+
+from zmi.core.i18n import BaseView
+
+_ = zope.i18nmessageid.MessageFactory("zope")
+
+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:
+            fmsgs = []
+
+        return self.context.getMessagesMapping(self.sync_languages,
+                                               fmsgs)
+
+    def queryMessageItems(self):
+        items = self.queryMessages().items()
+        items = removeSecurityProxy(items)
+        items.sort(lambda x, y: cmp(x[0][0] + x[0][1], y[0][0]+y[0][1]))
+        return items
+
+    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: zmi.core/trunk/src/zmi/core/i18n/tests/__init__.py
===================================================================
--- zmi.core/trunk/src/zmi/core/i18n/tests/__init__.py	                        (rev 0)
+++ zmi.core/trunk/src/zmi/core/i18n/tests/__init__.py	2009-06-07 15:27:41 UTC (rev 100697)
@@ -0,0 +1,2 @@
+#
+# This file is necessary to make this directory a package.

Added: zmi.core/trunk/src/zmi/core/i18n/tests/test_translate.py
===================================================================
--- zmi.core/trunk/src/zmi/core/i18n/tests/test_translate.py	                        (rev 0)
+++ zmi.core/trunk/src/zmi/core/i18n/tests/test_translate.py	2009-06-07 15:27:41 UTC (rev 100697)
@@ -0,0 +1,127 @@
+##############################################################################
+#
+# 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.1 (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.
+#
+##############################################################################
+"""Test Translation Domain 'Translate' screen.
+
+$Id: test_translate.py 81018 2007-10-24 02:37:33Z fdrake $
+"""
+import unittest
+from StringIO import StringIO
+
+from zope.app.testing.placelesssetup import PlacelessSetup
+from zope.app.testing import ztapi
+from zope.component.interfaces import IFactory
+from zope.component.factory import Factory
+
+from zmi.core.i18n.translate import Translate
+from zope.app.i18n.translationdomain import TranslationDomain
+from zope.app.i18n.messagecatalog import MessageCatalog
+from zope.i18n.interfaces import IUserPreferredCharsets
+
+from zope.publisher.http import IHTTPRequest
+from zope.publisher.http import HTTPCharsets
+from zope.publisher.browser import BrowserRequest
+
+class Translate(Translate):
+    """Make Translate a valid Browser view. Usually done by ZCML."""
+
+    def __init__(self, context, request):
+        self.context = context
+        self.request = request
+
+
+class TranslateTest(PlacelessSetup, unittest.TestCase):
+
+    def setUp(self):
+        super(TranslateTest, self).setUp()
+
+        # Setup the registries
+        ztapi.provideAdapter(IHTTPRequest, IUserPreferredCharsets,
+                             HTTPCharsets)
+
+        ztapi.provideUtility(IFactory, Factory(MessageCatalog),
+                             'zope.app.MessageCatalog')
+
+        domain = TranslationDomain()
+        domain.domain = 'default'
+
+        en_catalog = MessageCatalog('en', 'default')
+        de_catalog = MessageCatalog('de', 'default')
+
+        en_catalog.setMessage('short_greeting', 'Hello!')
+        de_catalog.setMessage('short_greeting', 'Hallo!')
+
+        en_catalog.setMessage('greeting', 'Hello $name, how are you?')
+        de_catalog.setMessage('greeting', 'Hallo $name, wie geht es Dir?')
+
+        domain['en-1'] = en_catalog
+        domain['de-1'] = de_catalog
+
+        self._view = Translate(domain, self._getRequest())
+
+
+    def _getRequest(self, **kw):
+        request = BrowserRequest(StringIO(''), kw)
+        request._cookies = {'edit_languages': 'en,de'}
+        request._traversed_names = ['foo', 'bar']
+        return request
+
+
+    def testGetMessages(self):
+        ids = [m[0] for m in self._view.getMessages()]
+        ids.sort()
+        self.assertEqual(ids, ['greeting', 'short_greeting'])
+
+
+    def testGetTranslation(self):
+        self.assertEqual(self._view.getTranslation('short_greeting', 'en'),
+                         'Hello!')
+
+
+    def testGetAllLanguages(self):
+        languages = self._view.getAllLanguages()
+        languages.sort()
+        self.assertEqual(languages, ['de', 'en'])
+
+
+    def testGetEditLanguages(self):
+        languages = self._view.getEditLanguages()
+        languages.sort()
+        self.assertEqual(languages, ['de', 'en'])
+
+
+    def testAddDeleteLanguage(self):
+        self._view.addLanguage('es')
+        self.assert_('es' in self._view.getAllLanguages())
+        self._view.deleteLanguages(['es'])
+        self.assert_('es' not in self._view.getAllLanguages())
+
+
+class SynchronizeTest(unittest.TestCase):
+
+    def test_synchronize_imports(self):
+        # Trivial test that imports the module.  This would have triggered a
+        # deprecation warning in previous versions.
+        import zmi.core.i18n.synchronize
+
+
+def test_suite():
+    loader = unittest.TestLoader()
+    return unittest.TestSuite([
+        loader.loadTestsFromTestCase(TranslateTest),
+        loader.loadTestsFromTestCase(SynchronizeTest),
+        ])
+    return 
+
+if __name__=='__main__':
+    unittest.main()

Added: zmi.core/trunk/src/zmi/core/i18n/translate.pt
===================================================================
--- zmi.core/trunk/src/zmi/core/i18n/translate.pt	                        (rev 0)
+++ zmi.core/trunk/src/zmi/core/i18n/translate.pt	2009-06-07 15:27:41 UTC (rev 100697)
@@ -0,0 +1,139 @@
+<html metal:use-macro="context/@@standard_macros/view"
+    i18n:domain="zope">
+<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">&nbsp;</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 i18n:translate="" 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 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>
+
+</div>
+</body>
+</html>

Added: zmi.core/trunk/src/zmi/core/i18n/translate.py
===================================================================
--- zmi.core/trunk/src/zmi/core/i18n/translate.py	                        (rev 0)
+++ zmi.core/trunk/src/zmi/core/i18n/translate.py	2009-06-07 15:27:41 UTC (rev 100697)
@@ -0,0 +1,111 @@
+##############################################################################
+#
+# 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.1 (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 26889 2004-08-04 04:00:36Z pruggera $
+"""
+__docformat__ = 'restructuredtext'
+
+from zmi.core.i18n 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: zmi.core/trunk/src/zmi/core/i18n/translatemessage.pt
===================================================================
--- zmi.core/trunk/src/zmi/core/i18n/translatemessage.pt	                        (rev 0)
+++ zmi.core/trunk/src/zmi/core/i18n/translatemessage.pt	2009-06-07 15:27:41 UTC (rev 100697)
@@ -0,0 +1,38 @@
+<html metal:use-macro="context/@@standard_macros/page"
+    i18n:domain="zope">
+<head>
+  <title metal:fill-slot="title" i18n:translate="">
+    Translation Domain - 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 Checkins mailing list