[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"> </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"> </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"> </td>
+ <td>
+ <textarea cols="20" rows="2" name=""
+ tal:attributes="name string:new-msg_id-${count}"
+ ></textarea>
+ </td>
+ <td tal:repeat="language python:view.getEditLanguages()">
+ <textarea cols="20" rows="2" name=""
+ tal:attributes="name string:new-${language}-${count}"
+ ></textarea>
+ </td>
+ </tr>
+ </tal:block>
+ </table>
+
+ <div>
+ <input class="form-element" type="submit"
+ name="@@editMessages.html:method" value="Edit Messages"
+ i18n:attributes="value" />
+ <input class="form-element" type="submit"
+ name="@@deleteMessages.html:method" value="Delete Messages"
+ i18n:attributes="value" />
+ </div>
+ </form>
+
+</div>
+</body>
+</html>
Added: 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