[gen] Added support for right-to-left (RTL) languages.

This commit is contained in:
Gaetan Delannay 2012-06-27 13:27:24 +02:00
parent b680a5ddcb
commit 1b375d387c
18 changed files with 83 additions and 41 deletions

View file

@ -110,7 +110,7 @@ class ToolMixin(BaseMixin):
if not cfg.languageSelector: return
if len(cfg.languages) < 2: return
page = self.REQUEST.get('ACTUAL_URL').split('/')[-1]
return page not in ('edit', 'query', 'search')
return page not in ('edit', 'query', 'search', 'do')
def getLanguages(self):
'''Returns the supported languages. First one is the default.'''
@ -128,11 +128,21 @@ class ToolMixin(BaseMixin):
rq.RESPONSE.setCookie('_ZopeLg', rq['language'], path='/')
return self.goto(rq['HTTP_REFERER'])
def flipLanguageDirection(self, align, dir):
'''According to language direction p_dir ('ltr' or 'rtl'), this method
turns p_align from 'left' to 'right' (or the inverse) when
required.'''
if dir == 'ltr': return align
if align == 'left': return 'right'
if align == 'right': return 'left'
return align
def getGlobalCssJs(self):
'''Returns the list of CSS and JS files to include in the main template.
The method ensures that appy.css and appy.js come first.'''
names = self.getPhysicalRoot().ui.objectIds('File')
names.remove('appy.js'); names.insert(0, 'appy.js')
names.remove('appyrtl.css'); names.insert(0, 'appyrtl.css')
names.remove('appy.css'); names.insert(0, 'appy.css')
return names

View file

@ -10,6 +10,7 @@ from appy.gen.utils import *
from appy.gen.layout import Table, defaultPageLayouts
from appy.gen.descriptors import WorkflowDescriptor, ClassDescriptor
from appy.shared.utils import sequenceTypes
from appy.shared.data import rtlLanguages
# ------------------------------------------------------------------------------
class BaseMixin:
@ -1404,6 +1405,11 @@ class BaseMixin:
if '-' in res: res = res[:res.find('-')]
return res
def getLanguageDirection(self, lang):
'''Determines if p_lang is a LTR or RTL language.'''
if lang in rtlLanguages: return 'rtl'
return 'ltr'
def formatText(self, text, format='html'):
'''Produces a representation of p_text into the desired p_format, which
is "html" by default.'''

View file

@ -15,9 +15,13 @@
app tool/getApp;
appUrl app/absolute_url;
template python: contextObj.getPageTemplate(app.ui, page);
lang tool/getUserLanguage;
dir python: tool.getLanguageDirection(lang);
dleft python: (dir == 'ltr') and 'left' or 'right';
dright python: (dir == 'ltr') and 'right' or 'left';
x python: resp.setHeader('Content-Type','text/html;;charset=utf-8');
x python: resp.setHeader('Expires', 'Mon, 11 Dec 1975 12:05:05 GMT');
x python: resp.setHeader('Content-Language', req.get('language', 'en'));
x python: resp.setHeader('Content-Language', lang);
x python: resp.setHeader('CacheControl', 'no-cache')">
<tal:comment replace="nothing">Keys "Expires" and "CacheControl" are used for preventing IE to cache
this page. Indeed, this page is retrieved through an asynchronous XMLHttpRequest by the browser, and

View file

@ -92,6 +92,7 @@ img { border: 0; vertical-align: middle}
.grid th { font-style: italic; font-weight: normal;
border-bottom: 2px solid grey; padding: 2px 2px;}
.grid td { padding-right: 5px; }
.cellGap { padding-right: 0.4em; }
.noStyle { border: 0 !important; padding: 0 !important; margin: 0 !important; }
.noStyle td { border:0 !important; padding:0 !important; margin:0 !important; }
.translationLabel { background-color: #EAEAEA; border-bottom: 1px dashed grey;

7
gen/ui/appyrtl.css Normal file
View file

@ -0,0 +1,7 @@
ul { margin: 0 0.6em 0.2em 0 }
ul li { background-image: url("ui/lirtl.gif");
background-position: right center;
padding-right: 15px; padding-left: 0px}
.portlet { border-right: none; border-left: 1px solid #5F7983 }
.lang { margin-right: 0px; margin-left: 6px; }
.cellGap { padding-left: 0.4em; }

BIN
gen/ui/bannerrtl.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View file

@ -1,4 +1,4 @@
<table metal:define-macro="footer"
cellpadding="0" cellspacing="0" width="100%" class="footer">
<tr><td align="right">Made with <a href="http://appyframework.org" target="_blank">Appy</a></td></tr>
<tr><td tal:attributes="align dright">Made with <a href="http://appyframework.org" target="_blank">Appy</a></td></tr>
</table>

BIN
gen/ui/lirtl.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 B

View file

@ -1,4 +1,4 @@
<div metal:define-macro="appyNavigate" tal:condition="python: totalNumber &gt; batchSize" align="right">
<div metal:define-macro="appyNavigate" tal:condition="python: totalNumber &gt; batchSize" tal:attributes="align dright">
<tal:comment replace="nothing">
Buttons for navigating among a list of elements (next, back, first, last, etc).
</tal:comment>

View file

@ -72,10 +72,10 @@
<metal:nav use-macro="context/ui/navigate/macros/appyNavigate"/>
<table width="100%" class="history">
<tr>
<th align="left">Action</th>
<th align="left">By</th>
<th align="left">Date</th>
<th align="left">Comment</th>
<th tal:attributes="align dleft">Action</th>
<th tal:attributes="align dleft">By</th>
<th tal:attributes="align dleft">Date</th>
<th tal:attributes="align dleft">Comment</th>
</tr>
<tal:event repeat="event objs">
<tr tal:define="odd repeat/event/odd;
@ -98,8 +98,8 @@
Display the previous values of the fields whose value were modified in this change.</tal:comment>
<table class="appyChanges" width="100%">
<tr>
<th align="left" width="30%" tal:content="python: _('modified_field')"></th>
<th align="left" width="70%" tal:content="python: _('previous_value')"></th>
<th tal:attributes="align dleft" width="30%" tal:content="python: _('modified_field')"></th>
<th tal:attributes="align dleft" width="70%" tal:content="python: _('previous_value')"></th>
</tr>
<tr tal:repeat="change event/changes/items" valign="top">
<tal:change define="appyType python:contextObj.getAppyType(change[0], asDict=True);">
@ -157,7 +157,7 @@
<tal:comment replace="nothing">Input field for storing comment</tal:comment>
<textarea id="comment" name="comment" cols="30" rows="3" style="display:none"></textarea>
<tal:comment replace="nothing">Buttons for triggering transitions</tal:comment>
<td align="right" tal:repeat="transition transitions">
<td tal:attributes="align dright" tal:repeat="transition transitions">
<tal:comment replace="nothing">Real button</tal:comment>
<input type="button" tal:condition="transition/may_trigger"
tal:attributes="value transition/title;
@ -198,8 +198,9 @@
<tal:comment replace="nothing">Creator and last modification date</tal:comment>
<tal:comment replace="nothing">Plus/minus icon for accessing history</tal:comment>
<tal:accessHistory condition="hasHistory">
<img align="left" style="cursor:pointer" onClick="toggleCookie('appyHistory')"
tal:attributes="src python:test(historyExpanded, 'ui/collapse.gif', 'ui/expand.gif');"
<img style="cursor:pointer" onClick="toggleCookie('appyHistory')"
tal:attributes="src python:test(historyExpanded, 'ui/collapse.gif', 'ui/expand.gif');
align dleft"
id="appyHistory_img"/>&nbsp;
<span>History</span> ||&nbsp;
</tal:accessHistory>
@ -233,7 +234,7 @@
<table width="100%">
<tr>
<td><metal:states use-macro="here/ui/page/macros/states"/></td>
<td align="right"><metal:states use-macro="here/ui/page/macros/transitions"/></td>
<td tal:attributes="align dright"><metal:states use-macro="here/ui/page/macros/transitions"/></td>
</tr>
</table>
</td>

View file

@ -62,10 +62,11 @@
expanded python: request.get(group['labelId'], 'collapsed') == 'expanded'">
<tal:comment replace="nothing">Group name</tal:comment>
<div class="portletGroup">
<img align="left" style="cursor:pointer; margin-right: 3px"
<img style="cursor:pointer; margin-right: 3px"
tal:attributes="id python: '%s_img' % group['labelId'];
src python:test(expanded, 'ui/collapse.gif', 'ui/expand.gif');
onClick python:'toggleCookie(\'%s\')' % group['labelId']"/>
onClick python:'toggleCookie(\'%s\')' % group['labelId'];
align dleft"/>
<span tal:replace="group/label"/>
</div>
<tal:comment replace="nothing">Group searches</tal:comment>

View file

@ -27,10 +27,10 @@
<tal:result condition="objs">
<tal:comment replace="nothing">Display here POD templates if required.</tal:comment>
<table align="right"
tal:define="widgets python: tool.getResultPodFields(className);
<table tal:define="widgets python: tool.getResultPodFields(className);
layoutType python:'view'"
tal:condition="python: objs and widgets">
tal:condition="python: objs and widgets"
tal:attributes="align dright">
<tr>
<td tal:define="contextObj python: objs[0]" tal:repeat="widget widgets">
<metal:pod use-macro="context/ui/widgets/show/macros/field"/>&nbsp;&nbsp;&nbsp;
@ -55,7 +55,7 @@
<span class="discreet" tal:content="descr"></span><br/>
</td>
</tal:descr>
<td align="right" width="25%">
<td tal:attributes="align dright" width="25%">
<tal:comment replace="nothing">Appy (top) navigation</tal:comment>
<metal:nav use-macro="context/ui/navigate/macros/appyNavigate"/>
</td>
@ -118,7 +118,7 @@
tal:content="python: _(obj.portal_type)"></td>
<tal:comment replace="nothing">Column "Actions"</tal:comment>
<td align="right">
<td tal:attributes="align dright">
<table class="noStyle" tal:condition="obj/mayAct">
<tr>
<tal:comment replace="nothing">Edit the element</tal:comment>

View file

@ -39,7 +39,7 @@
</table>
<tal:comment replace="nothing">Submit button</tal:comment>
<p align="right"><br/>
<p tal:attributes="align dright"><br/>
<input type="submit" tal:attributes="value python: _('search_button')"/>
</p>
</form>

View file

@ -9,14 +9,19 @@
_ python: tool.translate;
req python: request;
resp req/RESPONSE;
lang tool/getUserLanguage;
dir python: tool.getLanguageDirection(lang);
dleft python: (dir == 'ltr') and 'left' or 'right';
dright python: (dir == 'ltr') and 'right' or 'left';
x python: resp.setHeader('Content-type', 'text/html;;charset=UTF-8');
x python: resp.setHeader('Expires', 'Thu, 11 Dec 1975 12:05:00 GMT+2');
x python: resp.setHeader('Content-Language', req.get('language', 'en'))">
x python: resp.setHeader('Content-Language', lang)"
tal:attributes="dir python: tool.getLanguageDirection(lang)">
<head>
<title tal:content="tool/getAppName"></title>
<tal:link repeat="name tool/getGlobalCssJs">
<link tal:condition="python: name.endswith('.css')"
<link tal:condition="python: name.endswith('.css') and not ((dir == 'ltr') and (name == 'appyrtl.css'))"
rel="stylesheet" type="text/css" tal:attributes="href string:$appUrl/ui/$name"/>
<script tal:condition="python: name.endswith('.js')"
type="text/javascript" tal:attributes="src string:$appUrl/ui/$name"></script>
@ -27,16 +32,16 @@
<table class="main" align="center" cellpadding="0">
<tal:comment replace="nothing">Top banner</tal:comment>
<tr class="top" metal:define-slot="top">
<td tal:attributes="style python: 'background-image: url(%s/ui/banner.jpg)' % appUrl">
<td tal:define="bannerName python: (dir == 'ltr') and 'banner' or 'bannerrtl'"
tal:attributes="style python: 'background-image: url(%s/ui/%s.jpg)' % (appUrl, bannerName)">
<table width="100%">
<tr valign="top">
<tr valign="top" tal:define="pages tool/getMainPages">
<tal:comment replace="nothing">Links to main pages</tal:comment>
<td>
<a tal:repeat="page tool/getMainPages" class="pageLink"
<td tal:condition="pages"><a tal:repeat="page pages" class="pageLink"
tal:content="page/title" tal:attributes="href page/absolute_url"></a>
</td>
<tal:comment replace="nothing">Language selector (links or listbox)</tal:comment>
<td align="right" tal:condition="tool/showLanguageSelector">
<td tal:condition="tool/showLanguageSelector" tal:attributes="align dright">
<tal:lgs define="languages tool/getLanguages;
defaultLanguage python: languages[0];
asLinks python: len(languages) &lt;= 8">
@ -51,7 +56,7 @@
</tr>
</table>
<select tal:condition="not: asLinks"
tal:attributes="onchange string:window.location='${context/absolute_url}/switchLanguage?set_language=' + this.options[this.selectedIndex].value">
tal:attributes="onchange string:window.location='$appUrl/config/changeLanguage?language=' + this.options[this.selectedIndex].value">
<option tal:repeat="lang languages"
tal:content="python: tool.getLanguageName(lang)"
tal:attributes="selected python:defaultLanguage == lang; value lang">
@ -79,7 +84,7 @@
<p id="appyConfirmText"></p>
<input type="hidden" name="actionType"/>
<input type="hidden" name="action"/>
<div id="commentArea" align="left"><br/>
<div id="commentArea" tal:attributes="align dleft"><br/>
<span tal:content="python: _('workflow_comment')" class="discreet"></span>
<textarea name="comment" cols="30" rows="3"></textarea>
<br/>
@ -143,7 +148,7 @@
<img tal:attributes="src string: $appUrl/ui/logout.gif"/>
</a>
</td>
<td align="right" class="userStripText" tal:define="userInfo tool/getUserLine">
<td class="userStripText" tal:define="userInfo tool/getUserLine" tal:attributes="align dright">
<span tal:content="python: userInfo[0]"></span>
<a tal:condition="python: userInfo[1]"
tal:attributes="href python: userInfo[1]">

View file

@ -9,7 +9,8 @@
</tal:show>
</td>
<tal:comment replace="nothing">Icon for removing the row</tal:comment>
<td align="right" tal:condition="python: layoutType=='edit'">
<td tal:condition="python: layoutType=='edit'"
tal:attributes="align dright">
<img style="cursor:pointer"
tal:attributes="src string:$appUrl/ui/delete.png;
title python: 'Delete';

View file

@ -208,7 +208,7 @@
</tal:other>
</td>
<tal:comment replace="nothing">Actions</tal:comment>
<td align="right">
<td tal:attributes="align dright">
<tal:show condition="obj/mayAct">
<metal:showObjectActions use-macro="app/ui/widgets/ref/macros/objectActions" />
</tal:show>

View file

@ -22,7 +22,7 @@
<table tal:attributes="cellpadding layout/cellpadding;
cellspacing layout/cellspacing;
width layout/width;
align layout/align;
align python: tool.flipLanguageDirection(layout['align'], dir);
class python: tagCss and ('%s %s' % (tagCss, layoutCss)).strip() or layoutCss;
style layout/style;
id tagId;
@ -30,14 +30,16 @@
<tal:comment replace="nothing">The table header row</tal:comment>
<tr tal:condition="layout/headerRow" tal:attributes="valign layout/headerRow/valign">
<th tal:repeat="cell layout/headerRow/cells"
tal:attributes="align cell/align; width cell/width;">
tal:attributes="align python: tool.flipLanguageDirection(cell['align'], dir);
width cell/width;">
</th>
</tr>
<tal:comment replace="nothing">The table content</tal:comment>
<tr tal:repeat="row layout/rows" tal:attributes="valign row/valign">
<td tal:repeat="cell row/cells"
tal:attributes="align cell/align; colspan cell/colspan;
style python: test(repeat['cell'].end, '', 'padding-right: 0.4em')"><tal:content repeat="elem cell/content"><tal:field condition="python: elem == '?'"><metal:call use-macro="python: getattr(contextMacro, widget['type'].lower()).macros[layoutType]"/></tal:field><tal:other condition="python: elem != '?'"><metal:call use-macro="python: getattr(contextMacro, elem[0]).macros[elem[1]]"/></tal:other><img tal:condition="not: repeat/elem/end" tal:attributes="src string: $appUrl/ui/space.gif"/></tal:content>
tal:attributes="align python: tool.flipLanguageDirection(cell['align'], dir);
colspan cell/colspan;
class python: test(repeat['cell'].end, '', 'cellGap')"><tal:content repeat="elem cell/content"><tal:field condition="python: elem == '?'"><metal:call use-macro="python: getattr(contextMacro, widget['type'].lower()).macros[layoutType]"/></tal:field><tal:other condition="python: elem != '?'"><metal:call use-macro="python: getattr(contextMacro, elem[0]).macros[elem[1]]"/></tal:other><img tal:condition="not: repeat/elem/end" tal:attributes="src string: $appUrl/ui/space.gif"/></tal:content>
</td>
</tr>
</table>
@ -153,7 +155,7 @@
<table metal:define-macro="groupContent"
tal:define="cellgap widget/cellgap"
tal:attributes="width widget/wide;
align widget/align;
align python: tool.flipLanguageDirection(widget['align'], dir);
class groupCss;
cellspacing widget/cellspacing;
cellpadding widget/cellpadding;
@ -161,7 +163,8 @@
<tal:comment replace="nothing">Display the title of the group if it is not rendered a fieldset.</tal:comment>
<tr tal:condition="python: (widget['style'] != 'fieldset') and widget['hasLabel']">
<td tal:attributes="colspan python: len(widget['columnsWidths']);
class widget/style" align="left">
class widget/style;
align dleft">
<span tal:replace="structure python: contextObj.translate(widget['labelId'])"/>
<tal:help condition="widget/hasHelp">
<metal:call use-macro="app/ui/widgets/show/macros/help"/>
@ -176,7 +179,7 @@
<tr> <tal:comment replace="nothing">The column headers</tal:comment>
<th tal:repeat="colNb python:range(len(widget['columnsWidths']))"
tal:attributes="width python:widget['columnsWidths'][colNb];
align python:widget['columnsAligns'][colNb]"
align python: tool.flipLanguageDirection(widget['columnsAligns'][colNb], dir)"
tal:content="structure python: widget['hasHeaders'] and contextObj.translate('%s_col%d' % (widget['labelId'], colNb+1)) or ''">
</th>
</tr>

View file

@ -160,6 +160,9 @@ nativeNames = {
'zh' : '中文',
'zu' : 'isiZulu'
}
# List of languages having direction right-to-left (RTL) -----------------------
rtlLanguages = ('ar', 'he', 'fa')
# ------------------------------------------------------------------------------
class Languages:
'''This class gives access to the language codes and names as standardized