i18n improvements
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@2238 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
eddbdb63a9
commit
43e775c895
4 changed files with 100 additions and 40 deletions
12
concept.py
12
concept.py
|
@ -171,14 +171,16 @@ class Concept(Contained, Persistent):
|
|||
def getChildren(self, predicates=None, sort='default'):
|
||||
return [r.second for r in self.getChildRelations(predicates, sort=sort)]
|
||||
|
||||
def getParentRelations (self, predicates=None, parent=None):
|
||||
def getParentRelations (self, predicates=None, parent=None, sort='default'):
|
||||
predicates = predicates is None and ['*'] or predicates
|
||||
relationships = [ConceptRelation(None, self, p) for p in predicates]
|
||||
# TODO: sort...
|
||||
return getRelations(first=parent, second=self, relationships=relationships)
|
||||
if sort == 'default':
|
||||
sort = lambda x: (x.order, x.first.title.lower())
|
||||
return sorted(getRelations(first=parent, second=self, relationships=relationships),
|
||||
key=sort)
|
||||
|
||||
def getParents(self, predicates=None):
|
||||
return [r.first for r in self.getParentRelations(predicates)]
|
||||
def getParents(self, predicates=None, sort='default'):
|
||||
return [r.first for r in self.getParentRelations(predicates, sort=sort)]
|
||||
|
||||
def assignChild(self, concept, predicate=None, order=0, relevance=1.0):
|
||||
if predicate is None:
|
||||
|
|
|
@ -108,14 +108,41 @@ languages on the type object.
|
|||
|
||||
Now we are ready to enter a language-specific title.
|
||||
|
||||
>>> from loops.browser.concept import ConceptEditForm
|
||||
>>> from loops.browser.concept import ConceptEditForm, ConceptView
|
||||
>>> input = {'form.title': 'loops per Zope 3', 'loops.language': 'it',
|
||||
... 'form.actions.apply': 'Change'}
|
||||
>>> form = ConceptEditForm(topic01, TestRequest(form=input))
|
||||
>>> form.update()
|
||||
|
||||
>>> topic01.title
|
||||
{'it': u'loops per Zope 3'}
|
||||
{'en': u'loops for Zope 3', 'it': u'loops per Zope 3'}
|
||||
|
||||
If we access an i18n attribute via a view that is i18n-aware we get the
|
||||
value corresponding to the language preferences that appear in the request.
|
||||
|
||||
>>> input = {'loops.language': 'it'}
|
||||
>>> view = ConceptView(topic01, TestRequest(form=input))
|
||||
>>> view.title
|
||||
u'loops per Zope 3'
|
||||
|
||||
If there is no entry for the language given we get back the entry for
|
||||
the default language.
|
||||
|
||||
>>> input = {'loops.language': 'de'}
|
||||
>>> view = ConceptView(topic01, TestRequest(form=input))
|
||||
>>> view.title
|
||||
u'loops for Zope 3'
|
||||
|
||||
There are also fallbacks - mainly for being able to access the title
|
||||
attribute in not i18n-aware contexts - that retrieve the value corresponding
|
||||
to the default language at the time of the attribute creation.
|
||||
|
||||
>>> topic01.title.getDefault()
|
||||
u'loops for Zope 3'
|
||||
>>> str(topic01.title)
|
||||
'loops for Zope 3'
|
||||
>>> topic01.title.lower()
|
||||
u'loops for zope 3'
|
||||
|
||||
|
||||
Fin de partie
|
||||
|
|
|
@ -33,40 +33,65 @@ from cybertools.typology.interfaces import IType
|
|||
from loops.common import adapted, AdapterBase
|
||||
|
||||
|
||||
_not_found = object()
|
||||
|
||||
# support for i18n content
|
||||
|
||||
class I18NValue(PersistentMapping):
|
||||
""" A dictionary to be used for storing values for different languages.
|
||||
"""
|
||||
|
||||
default = None
|
||||
|
||||
def lower(self):
|
||||
return str(self).lower()
|
||||
# this should only be used as a fallback for the title attribute
|
||||
return self.getDefault().lower()
|
||||
|
||||
def getDefault(self):
|
||||
if self.default is None:
|
||||
return self.values()[0]
|
||||
return self.default
|
||||
|
||||
def __str__(self):
|
||||
return self.values()[0]
|
||||
return str(self.getDefault())
|
||||
|
||||
|
||||
def getI18nValue(obj, attr, langInfo=None):
|
||||
obj = removeSecurityProxy(obj)
|
||||
value = getattr(obj, attr, None)
|
||||
lang = None
|
||||
if isinstance(value, I18NValue):
|
||||
lang = langInfo and langInfo.language or value.keys()[0]
|
||||
value = value.get(lang)
|
||||
#print '*** getI18nValue', attr, langInfo, lang, getattr(obj, attr, None), value
|
||||
if langInfo:
|
||||
result = value.get(langInfo.language, _not_found)
|
||||
if result is _not_found:
|
||||
result = value.get(langInfo.defaultLanguage, _not_found)
|
||||
if result is _not_found:
|
||||
result = value.getDefault()
|
||||
return result
|
||||
else:
|
||||
return value.getDefault()
|
||||
return value
|
||||
|
||||
def setI18nValue(obj, attr, value, langInfo=None):
|
||||
obj = removeSecurityProxy(obj)
|
||||
old = getattr(obj, attr, None)
|
||||
if langInfo is None:
|
||||
setattr(obj, attr, value)
|
||||
return
|
||||
if isinstance(old, I18NValue):
|
||||
raise ValueError('Attribute %s on object %s is an I18NValue (%s) '
|
||||
'and no langInfo given.' % (attr, obj, value))
|
||||
else:
|
||||
setattr(obj, attr, value)
|
||||
return
|
||||
lang = langInfo.language
|
||||
if isinstance(old, I18NValue):
|
||||
old[lang] = value
|
||||
else:
|
||||
setattr(obj, attr, I18NValue(((lang, value),)))
|
||||
i18nValue = I18NValue(((lang, value),))
|
||||
defaultLang = langInfo.defaultLanguage
|
||||
if lang != defaultLang:
|
||||
# keep existing value
|
||||
i18nValue[defaultLang] = old
|
||||
i18nValue.default = i18nValue[defaultLang]
|
||||
setattr(obj, attr, i18nValue)
|
||||
#print '*** setI18nValue', attr, langInfo, lang, value, getattr(obj, attr, None)
|
||||
|
||||
|
||||
|
|
|
@ -34,11 +34,13 @@ from zope.security.proxy import removeSecurityProxy
|
|||
from zope.cachedescriptors.property import Lazy
|
||||
|
||||
from cybertools.typology.interfaces import IType
|
||||
from loops.common import adapted
|
||||
from loops.concept import Concept
|
||||
from loops.i18n.browser import I18NView
|
||||
from loops.util import getUidForObject, getObjectForUid, toUnicode
|
||||
|
||||
|
||||
class LoopsMethods(MethodPublisher):
|
||||
class LoopsMethods(MethodPublisher, I18NView):
|
||||
""" XML-RPC methods for the loops root object.
|
||||
"""
|
||||
|
||||
|
@ -77,40 +79,44 @@ class LoopsMethods(MethodPublisher):
|
|||
tc = self.concepts.getTypeConcept()
|
||||
types = tc.getChildren((self.typePredicate,))
|
||||
#types = [t for t in types if ITypeConcept(t).typeInterface ... ]
|
||||
return [objectAsDict(t) for t in types]
|
||||
return [objectAsDict(t, self.languageInfo) for t in types]
|
||||
|
||||
def getPredicates(self):
|
||||
pt = self.concepts.getDefaultPredicate().conceptType
|
||||
preds = pt.getChildren((self.concepts.getTypePredicate(),))
|
||||
return [objectAsDict(p) for p in preds if p is not self.typePredicate]
|
||||
return [objectAsDict(p, self.languageInfo)
|
||||
for p in preds if p is not self.typePredicate]
|
||||
|
||||
def getChildren(self, id, predicates=[], child=''):
|
||||
obj = getObjectForUid(id)
|
||||
preds = [getObjectForUid(p) for p in predicates]
|
||||
child = child and getObjectForUid(child) or None
|
||||
rels = obj.getChildRelations(preds or None, child)
|
||||
return formatRelations(rels)
|
||||
return formatRelations(rels, langInfo=self.languageInfo)
|
||||
|
||||
def getParents(self, id, predicates=[], parent=''):
|
||||
obj = getObjectForUid(id)
|
||||
preds = [getObjectForUid(p) for p in predicates]
|
||||
parent = parent and getObjectForUid(parent) or None
|
||||
rels = obj.getParentRelations(preds or None, parent)
|
||||
return formatRelations(rels, useSecond=False)
|
||||
return formatRelations(rels, useSecond=False, langInfo=self.languageInfo)
|
||||
|
||||
def getResources(self, id, predicates=[], resource=''):
|
||||
obj = getObjectForUid(id)
|
||||
preds = [getObjectForUid(p) for p in predicates]
|
||||
resource = resource and getObjectForUid(child) or None
|
||||
rels = obj.getResourceRelations(preds or None, resource)
|
||||
return formatRelations(rels)
|
||||
return formatRelations(rels, langInfo=self.languageInfo)
|
||||
|
||||
def getObjectWithChildren(self, obj):
|
||||
mapping = objectAsDict(obj)
|
||||
mapping['children'] = formatRelations(obj.getChildRelations())
|
||||
mapping['parents'] = formatRelations(
|
||||
obj.getParentRelations(), useSecond=False)
|
||||
mapping['resources'] = formatRelations(obj.getResourceRelations())
|
||||
mapping = objectAsDict(obj, self.languageInfo)
|
||||
mapping['children'] = formatRelations(obj.getChildRelations(sort=None),
|
||||
langInfo=self.languageInfo)
|
||||
mapping['parents'] = formatRelations(obj.getParentRelations(sort=None),
|
||||
useSecond=False,
|
||||
langInfo=self.languageInfo)
|
||||
mapping['resources'] = formatRelations(obj.getResourceRelations(sort=None),
|
||||
langInfo=self.languageInfo)
|
||||
return mapping
|
||||
|
||||
def assignChild(self, objId, predicateId, childId):
|
||||
|
@ -134,36 +140,36 @@ class LoopsMethods(MethodPublisher):
|
|||
name = INameChooser(self.concepts).chooseName(name, c)
|
||||
self.concepts[name] = c
|
||||
c.conceptType = type
|
||||
adapted(c, self.languageInfo).title = title
|
||||
notify(ObjectCreatedEvent(c))
|
||||
notify(ObjectModifiedEvent(c))
|
||||
return objectAsDict(c)
|
||||
return objectAsDict(c, self.languageInfo)
|
||||
|
||||
def editConcept(self, objId, attr, value):
|
||||
obj = getObjectForUid(objId)
|
||||
ti = IType(obj).typeInterface
|
||||
if ti is not None:
|
||||
obj = ti(obj)
|
||||
# TODO: provide conversion if necessary
|
||||
adapter = adapted(obj, self.languageInfo)
|
||||
# TODO: provide conversion if necessary - use cybertools.composer.schema
|
||||
value = value.strip() # remove spaces appended by Flash
|
||||
setattr(obj, attr, toUnicode(value))
|
||||
setattr(adapter, attr, toUnicode(value))
|
||||
notify(ObjectModifiedEvent(obj))
|
||||
return 'OK'
|
||||
|
||||
|
||||
def objectAsDict(obj):
|
||||
def objectAsDict(obj, langInfo=None):
|
||||
objType = IType(obj)
|
||||
adapter = adapted(obj, langInfo)
|
||||
mapping = {'id': getUidForObject(obj), 'name': getName(obj),
|
||||
'title': obj.title, 'description': obj.description,
|
||||
'title': adapter.title, 'description': adapter.description,
|
||||
'type': getUidForObject(objType.typeProvider)}
|
||||
ti = objType.typeInterface
|
||||
if ti is not None:
|
||||
adapter = ti(obj)
|
||||
#for attr in (list(adapter._adapterAttributes) + list(ti)):
|
||||
for attr in list(ti):
|
||||
if attr not in ('__parent__', 'context', 'id', 'name',
|
||||
'title', 'description', 'type', 'data'):
|
||||
value = getattr(adapter, attr)
|
||||
# TODO: provide conversion and schema information
|
||||
# TODO: provide conversion and schema information -
|
||||
# use cybertools.composer.schema
|
||||
#if value is None or type(value) in (str, unicode):
|
||||
if ITextLine.providedBy(ti[attr]):
|
||||
mapping[attr] = value or u''
|
||||
|
@ -171,7 +177,7 @@ def objectAsDict(obj):
|
|||
# mapping[attr] = ' | '.join(value)
|
||||
return mapping
|
||||
|
||||
def formatRelations(rels, useSecond=True):
|
||||
def formatRelations(rels, useSecond=True, langInfo=None):
|
||||
predIds = {}
|
||||
result = []
|
||||
for rel in rels:
|
||||
|
@ -185,6 +191,6 @@ def formatRelations(rels, useSecond=True):
|
|||
other = rel.second
|
||||
else:
|
||||
other = rel.first
|
||||
result[predIds[predId]]['objects'].append(objectAsDict(other))
|
||||
return result
|
||||
result[predIds[predId]]['objects'].append(objectAsDict(other, langInfo))
|
||||
return sorted(result, key=lambda x: x['title'])
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue