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'):
|
def getChildren(self, predicates=None, sort='default'):
|
||||||
return [r.second for r in self.getChildRelations(predicates, sort=sort)]
|
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
|
predicates = predicates is None and ['*'] or predicates
|
||||||
relationships = [ConceptRelation(None, self, p) for p in predicates]
|
relationships = [ConceptRelation(None, self, p) for p in predicates]
|
||||||
# TODO: sort...
|
if sort == 'default':
|
||||||
return getRelations(first=parent, second=self, relationships=relationships)
|
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):
|
def getParents(self, predicates=None, sort='default'):
|
||||||
return [r.first for r in self.getParentRelations(predicates)]
|
return [r.first for r in self.getParentRelations(predicates, sort=sort)]
|
||||||
|
|
||||||
def assignChild(self, concept, predicate=None, order=0, relevance=1.0):
|
def assignChild(self, concept, predicate=None, order=0, relevance=1.0):
|
||||||
if predicate is None:
|
if predicate is None:
|
||||||
|
|
|
@ -108,14 +108,41 @@ languages on the type object.
|
||||||
|
|
||||||
Now we are ready to enter a language-specific title.
|
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',
|
>>> input = {'form.title': 'loops per Zope 3', 'loops.language': 'it',
|
||||||
... 'form.actions.apply': 'Change'}
|
... 'form.actions.apply': 'Change'}
|
||||||
>>> form = ConceptEditForm(topic01, TestRequest(form=input))
|
>>> form = ConceptEditForm(topic01, TestRequest(form=input))
|
||||||
>>> form.update()
|
>>> form.update()
|
||||||
|
|
||||||
>>> topic01.title
|
>>> 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
|
Fin de partie
|
||||||
|
|
|
@ -33,40 +33,65 @@ from cybertools.typology.interfaces import IType
|
||||||
from loops.common import adapted, AdapterBase
|
from loops.common import adapted, AdapterBase
|
||||||
|
|
||||||
|
|
||||||
|
_not_found = object()
|
||||||
|
|
||||||
# support for i18n content
|
# support for i18n content
|
||||||
|
|
||||||
class I18NValue(PersistentMapping):
|
class I18NValue(PersistentMapping):
|
||||||
""" A dictionary to be used for storing values for different languages.
|
""" A dictionary to be used for storing values for different languages.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
default = None
|
||||||
|
|
||||||
def lower(self):
|
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):
|
def __str__(self):
|
||||||
return self.values()[0]
|
return str(self.getDefault())
|
||||||
|
|
||||||
|
|
||||||
def getI18nValue(obj, attr, langInfo=None):
|
def getI18nValue(obj, attr, langInfo=None):
|
||||||
obj = removeSecurityProxy(obj)
|
obj = removeSecurityProxy(obj)
|
||||||
value = getattr(obj, attr, None)
|
value = getattr(obj, attr, None)
|
||||||
lang = None
|
|
||||||
if isinstance(value, I18NValue):
|
if isinstance(value, I18NValue):
|
||||||
lang = langInfo and langInfo.language or value.keys()[0]
|
if langInfo:
|
||||||
value = value.get(lang)
|
result = value.get(langInfo.language, _not_found)
|
||||||
#print '*** getI18nValue', attr, langInfo, lang, getattr(obj, attr, None), value
|
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
|
return value
|
||||||
|
|
||||||
def setI18nValue(obj, attr, value, langInfo=None):
|
def setI18nValue(obj, attr, value, langInfo=None):
|
||||||
obj = removeSecurityProxy(obj)
|
obj = removeSecurityProxy(obj)
|
||||||
old = getattr(obj, attr, None)
|
old = getattr(obj, attr, None)
|
||||||
if langInfo is None:
|
if langInfo is None:
|
||||||
|
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)
|
setattr(obj, attr, value)
|
||||||
return
|
return
|
||||||
lang = langInfo.language
|
lang = langInfo.language
|
||||||
if isinstance(old, I18NValue):
|
if isinstance(old, I18NValue):
|
||||||
old[lang] = value
|
old[lang] = value
|
||||||
else:
|
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)
|
#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 zope.cachedescriptors.property import Lazy
|
||||||
|
|
||||||
from cybertools.typology.interfaces import IType
|
from cybertools.typology.interfaces import IType
|
||||||
|
from loops.common import adapted
|
||||||
from loops.concept import Concept
|
from loops.concept import Concept
|
||||||
|
from loops.i18n.browser import I18NView
|
||||||
from loops.util import getUidForObject, getObjectForUid, toUnicode
|
from loops.util import getUidForObject, getObjectForUid, toUnicode
|
||||||
|
|
||||||
|
|
||||||
class LoopsMethods(MethodPublisher):
|
class LoopsMethods(MethodPublisher, I18NView):
|
||||||
""" XML-RPC methods for the loops root object.
|
""" XML-RPC methods for the loops root object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -77,40 +79,44 @@ class LoopsMethods(MethodPublisher):
|
||||||
tc = self.concepts.getTypeConcept()
|
tc = self.concepts.getTypeConcept()
|
||||||
types = tc.getChildren((self.typePredicate,))
|
types = tc.getChildren((self.typePredicate,))
|
||||||
#types = [t for t in types if ITypeConcept(t).typeInterface ... ]
|
#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):
|
def getPredicates(self):
|
||||||
pt = self.concepts.getDefaultPredicate().conceptType
|
pt = self.concepts.getDefaultPredicate().conceptType
|
||||||
preds = pt.getChildren((self.concepts.getTypePredicate(),))
|
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=''):
|
def getChildren(self, id, predicates=[], child=''):
|
||||||
obj = getObjectForUid(id)
|
obj = getObjectForUid(id)
|
||||||
preds = [getObjectForUid(p) for p in predicates]
|
preds = [getObjectForUid(p) for p in predicates]
|
||||||
child = child and getObjectForUid(child) or None
|
child = child and getObjectForUid(child) or None
|
||||||
rels = obj.getChildRelations(preds or None, child)
|
rels = obj.getChildRelations(preds or None, child)
|
||||||
return formatRelations(rels)
|
return formatRelations(rels, langInfo=self.languageInfo)
|
||||||
|
|
||||||
def getParents(self, id, predicates=[], parent=''):
|
def getParents(self, id, predicates=[], parent=''):
|
||||||
obj = getObjectForUid(id)
|
obj = getObjectForUid(id)
|
||||||
preds = [getObjectForUid(p) for p in predicates]
|
preds = [getObjectForUid(p) for p in predicates]
|
||||||
parent = parent and getObjectForUid(parent) or None
|
parent = parent and getObjectForUid(parent) or None
|
||||||
rels = obj.getParentRelations(preds or None, parent)
|
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=''):
|
def getResources(self, id, predicates=[], resource=''):
|
||||||
obj = getObjectForUid(id)
|
obj = getObjectForUid(id)
|
||||||
preds = [getObjectForUid(p) for p in predicates]
|
preds = [getObjectForUid(p) for p in predicates]
|
||||||
resource = resource and getObjectForUid(child) or None
|
resource = resource and getObjectForUid(child) or None
|
||||||
rels = obj.getResourceRelations(preds or None, resource)
|
rels = obj.getResourceRelations(preds or None, resource)
|
||||||
return formatRelations(rels)
|
return formatRelations(rels, langInfo=self.languageInfo)
|
||||||
|
|
||||||
def getObjectWithChildren(self, obj):
|
def getObjectWithChildren(self, obj):
|
||||||
mapping = objectAsDict(obj)
|
mapping = objectAsDict(obj, self.languageInfo)
|
||||||
mapping['children'] = formatRelations(obj.getChildRelations())
|
mapping['children'] = formatRelations(obj.getChildRelations(sort=None),
|
||||||
mapping['parents'] = formatRelations(
|
langInfo=self.languageInfo)
|
||||||
obj.getParentRelations(), useSecond=False)
|
mapping['parents'] = formatRelations(obj.getParentRelations(sort=None),
|
||||||
mapping['resources'] = formatRelations(obj.getResourceRelations())
|
useSecond=False,
|
||||||
|
langInfo=self.languageInfo)
|
||||||
|
mapping['resources'] = formatRelations(obj.getResourceRelations(sort=None),
|
||||||
|
langInfo=self.languageInfo)
|
||||||
return mapping
|
return mapping
|
||||||
|
|
||||||
def assignChild(self, objId, predicateId, childId):
|
def assignChild(self, objId, predicateId, childId):
|
||||||
|
@ -134,36 +140,36 @@ class LoopsMethods(MethodPublisher):
|
||||||
name = INameChooser(self.concepts).chooseName(name, c)
|
name = INameChooser(self.concepts).chooseName(name, c)
|
||||||
self.concepts[name] = c
|
self.concepts[name] = c
|
||||||
c.conceptType = type
|
c.conceptType = type
|
||||||
|
adapted(c, self.languageInfo).title = title
|
||||||
notify(ObjectCreatedEvent(c))
|
notify(ObjectCreatedEvent(c))
|
||||||
notify(ObjectModifiedEvent(c))
|
notify(ObjectModifiedEvent(c))
|
||||||
return objectAsDict(c)
|
return objectAsDict(c, self.languageInfo)
|
||||||
|
|
||||||
def editConcept(self, objId, attr, value):
|
def editConcept(self, objId, attr, value):
|
||||||
obj = getObjectForUid(objId)
|
obj = getObjectForUid(objId)
|
||||||
ti = IType(obj).typeInterface
|
adapter = adapted(obj, self.languageInfo)
|
||||||
if ti is not None:
|
# TODO: provide conversion if necessary - use cybertools.composer.schema
|
||||||
obj = ti(obj)
|
|
||||||
# TODO: provide conversion if necessary
|
|
||||||
value = value.strip() # remove spaces appended by Flash
|
value = value.strip() # remove spaces appended by Flash
|
||||||
setattr(obj, attr, toUnicode(value))
|
setattr(adapter, attr, toUnicode(value))
|
||||||
notify(ObjectModifiedEvent(obj))
|
notify(ObjectModifiedEvent(obj))
|
||||||
return 'OK'
|
return 'OK'
|
||||||
|
|
||||||
|
|
||||||
def objectAsDict(obj):
|
def objectAsDict(obj, langInfo=None):
|
||||||
objType = IType(obj)
|
objType = IType(obj)
|
||||||
|
adapter = adapted(obj, langInfo)
|
||||||
mapping = {'id': getUidForObject(obj), 'name': getName(obj),
|
mapping = {'id': getUidForObject(obj), 'name': getName(obj),
|
||||||
'title': obj.title, 'description': obj.description,
|
'title': adapter.title, 'description': adapter.description,
|
||||||
'type': getUidForObject(objType.typeProvider)}
|
'type': getUidForObject(objType.typeProvider)}
|
||||||
ti = objType.typeInterface
|
ti = objType.typeInterface
|
||||||
if ti is not None:
|
if ti is not None:
|
||||||
adapter = ti(obj)
|
|
||||||
#for attr in (list(adapter._adapterAttributes) + list(ti)):
|
#for attr in (list(adapter._adapterAttributes) + list(ti)):
|
||||||
for attr in list(ti):
|
for attr in list(ti):
|
||||||
if attr not in ('__parent__', 'context', 'id', 'name',
|
if attr not in ('__parent__', 'context', 'id', 'name',
|
||||||
'title', 'description', 'type', 'data'):
|
'title', 'description', 'type', 'data'):
|
||||||
value = getattr(adapter, attr)
|
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 value is None or type(value) in (str, unicode):
|
||||||
if ITextLine.providedBy(ti[attr]):
|
if ITextLine.providedBy(ti[attr]):
|
||||||
mapping[attr] = value or u''
|
mapping[attr] = value or u''
|
||||||
|
@ -171,7 +177,7 @@ def objectAsDict(obj):
|
||||||
# mapping[attr] = ' | '.join(value)
|
# mapping[attr] = ' | '.join(value)
|
||||||
return mapping
|
return mapping
|
||||||
|
|
||||||
def formatRelations(rels, useSecond=True):
|
def formatRelations(rels, useSecond=True, langInfo=None):
|
||||||
predIds = {}
|
predIds = {}
|
||||||
result = []
|
result = []
|
||||||
for rel in rels:
|
for rel in rels:
|
||||||
|
@ -185,6 +191,6 @@ def formatRelations(rels, useSecond=True):
|
||||||
other = rel.second
|
other = rel.second
|
||||||
else:
|
else:
|
||||||
other = rel.first
|
other = rel.first
|
||||||
result[predIds[predId]]['objects'].append(objectAsDict(other))
|
result[predIds[predId]]['objects'].append(objectAsDict(other, langInfo))
|
||||||
return result
|
return sorted(result, key=lambda x: x['title'])
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue