Re: multilingual Dexterity

20 views
Skip to first unread message

David Glick

unread,
Feb 13, 2012, 1:07:34 AM2/13/12
to Ramon Navarro Bosch, Lewis Jonathan, Martin Aspeli, Victor, dexterity-...@googlegroups.com
Below I'm sending some brainstorming about how to support language
independent fields with Dexterity. I hope you don't mind me cc-ing the
Dexterity development list...

On 2/9/12 6:07 AM, Ramon Navarro Bosch wrote:
> Hi Jonathan,
>
> It is ok for us, we saw on devel list you are going to come to europe
> on March, we would like to work on that before. On the other hand, we
> are trying to push a sprint about p.a.m. UI at PloneKonf.
>
> What are your plans, do you want us to begin to work on it before you
> come, or wait for you and work together?
>
> Martin, and David, it would be interesting to arrange a virtual
> meeting with you and Victor in order to talk about this subject and
> try to reach a possible technical solution friendly with dexterity
> design. Please let us know when we can talk.
>
> Ramon
>
> Enviat des de l'ipad!
>
> On 09/02/2012, at 0:34, Lewis Jonathan<jonatha...@mac.com> wrote:
>
>> Dear Ramon and everyone,
>>
>> Apologies again for the silence.
>>
>> Thinking about what our site needs, language independent fields are the top priority.

I took a look at LinguaPlone to understand how it handles
language-independent fields.

Its approach is basically:
1. Widgets for language independent fields are rendered in edit mode
when editing the canonical translation, but in view mode when editing
other translations.
2. The mutator for language-independent fields is wrapped such that when
the field is set, the new value is stored on each of the translations.

There are a few directions this could go for Dexterity. All of them
assume we have a way to mark which fields are language independent, so
let me describe that first. In Dexterity the way that we add metadata to
a schema is by setting "tagged values" on the schema interface. The
language independent flag can be handled using a tagged value which is a
dictionary indicating which fields are language independent. Like this:

from plone.directives import form
from zope import schema
class IEventInfo(form.Schema):
start = schema.DateTime(title='Start')
end = schema.DateTime(title='End')
IEventInfo.setTaggedValue('language_independent', {'start': True, 'end':
True})

And then code which needs to see if the field is language-independent
can do something like:
is_independent =
field.interface.queryTaggedValue('language_independent',
{}).get(field.__name__, False)

This is the same way that Dexterity's other metadata (widget hints,
primary field flag, etc.) are handled internally, but you may not have
encountered tagged values directly because we provide some more
user-friendly ways to set them:
1. grok directives (i.e. language_independent('start') within the schema
definition)
2. supermodel XML (i.e. lingua:independent="true" in an XML version of
the model)
3. through-the-web UI (which modifies the tagged value in memory and
then serializes the result to supermodel XML stored in the FTI)
To figure out how to handle all of these I'd recommend looking at how
the "primary" flag is set up. The supermodel import/export handler is in
plone.rfc822.supermodel. The grok directive is in
plone.directives.form.schema, down at the bottom.

Okay, with that implemented we can check if a field is
language-independent. Now we need to decide how to keep the field's
values in sync between translations.

I can see the start of a number of options...
1. Use an ObjectModified event handler. In it, determine which fields
changed that are designated language independent. If any, look up all
the other translations and write the values to those translations too.
But this could get complex because it's hard to guarantee the order of
execution vs. other ObjectModified handlers, and if you want to raise
ObjectModified on the other translations too then you need a way to
avoid endless recursion...
2. Use a custom z3c.form data manager. The data manager is the component
responsible for getting/setting form values from and to the form's
context. The default implementation simply adapts the context to the
field's interface and then uses simple attribute get/set. But a custom
implementation for multilingual content could do this for all
translations, or could make all the translations get/set
language-independent fields from the canonical translation. The main
problem with this approach is that it would only be a solution for
z3c.form...just trying to access the field as an attribute of a
Dexterity item does not use the data manager.
3. Write a language-independent-field-aware property descriptor like:
class LanguageIndependentProperty(object):
def __init__(self, field):
self.field = field
def __get__(self, inst, type=None):
# If inst is the canonical translation, get the value from the
instance dict;
# else find the canonical translation and get it from there
def __set__(self, inst, value):
# Find the canonical translation and set the value there
This could be used in behavior adapters, or in custom subclasses of
plone.dexterity.content.Item. The downside of this option is that either
you need to put your language-independent fields in behaviors, or you
need to create a custom class for your content type. (Since Dexterity
doesn't auto-generate accessors and mutators like Archetypes did, and
assumes values of the main schema can be reached via simple attribute
access on the content item.)
4. Create a generic MultiLingualItem subclass of Item and override
__getattribute__ and __setattr__ to Do The Right Thing for
language-independent fields.

Sorry I don't have a more conclusive path to offer you, but hopefully
this at least gets people brainstorming. There could well be a more
elegant option I haven't thought of yet as this is all rather off-the-cuff.

cheers,
David

Jan-Carel Brand

unread,
Feb 14, 2012, 7:27:49 AM2/14/12
to dexterity-...@googlegroups.com
Hi folks

As I'm going to participate in the PloneKonf sprint, I thought I'd chip
in to get more clarity, and perhaps get a conversation started :)

I also added this thread to the PloneKonf sprint page on coactivate:
http://www.coactivate.org/projects/plone-konferenz-sprint-2012/project-home

See my inline responses below:

On Sun, 2012-02-12 at 22:07 -0800, David Glick wrote:
<snip>

So these would be sub-tasks? Creating a grok directive, supermodel XML
parsing and TTW UI handling.

They're however not crucial, because in the meantime one could just call
setTaggedValue.

> Okay, with that implemented we can check if a field is
> language-independent. Now we need to decide how to keep the field's
> values in sync between translations.
>
> I can see the start of a number of options...
> 1. Use an ObjectModified event handler. In it, determine which fields
> changed that are designated language independent. If any, look up all
> the other translations and write the values to those translations too.
> But this could get complex because it's hard to guarantee the order of
> execution vs. other ObjectModified handlers, and if you want to raise
> ObjectModified on the other translations too then you need a way to
> avoid endless recursion...

-1

Recursion doesn't sound like too much of a problem, since AFAIK it's
possible to set a value without firing the ObjectModified event again.
Variable order of execution sounds like a problem though.

> 2. Use a custom z3c.form data manager. The data manager is the component
> responsible for getting/setting form values from and to the form's
> context. The default implementation simply adapts the context to the
> field's interface and then uses simple attribute get/set. But a custom
> implementation for multilingual content could do this for all
> translations, or could make all the translations get/set
> language-independent fields from the canonical translation. The main
> problem with this approach is that it would only be a solution for
> z3c.form...just trying to access the field as an attribute of a
> Dexterity item does not use the data manager.

-1

Because if one were to manually set (i.e without a form) a
language-independent field, then the translation's fields won't be
updated.

> 3. Write a language-independent-field-aware property descriptor like:
> class LanguageIndependentProperty(object):
> def __init__(self, field):
> self.field = field
> def __get__(self, inst, type=None):
> # If inst is the canonical translation, get the value from the
> instance dict;
> # else find the canonical translation and get it from there
> def __set__(self, inst, value):
> # Find the canonical translation and set the value there
> This could be used in behavior adapters, or in custom subclasses of
> plone.dexterity.content.Item. The downside of this option is that either
> you need to put your language-independent fields in behaviors, or you
> need to create a custom class for your content type. (Since Dexterity
> doesn't auto-generate accessors and mutators like Archetypes did, and
> assumes values of the main schema can be reached via simple attribute
> access on the content item.)

So in your content type, you'd have to do:

Class Foo(dexterity.Item):

bar = LanguageIndependentProperty(barfield)

And a behavior would be doing something similar when it gets called?

+0 for custom class.
-1 for behavior since I think it's too onerous for the add-on developer.

> 4. Create a generic MultiLingualItem subclass of Item and override
> __getattribute__ and __setattr__ to Do The Right Thing for
> language-independent fields.

+1 This sounds like the best approach IMHO.

It's not too complicated for the add-on developer (who is used to using
special subclasses for LinguaPLone as well) and it's not z3c.form
specific.

Regards

--
JC Brand
http://opkode.com

Ramon Navarro Bosch

unread,
Feb 19, 2012, 6:02:53 PM2/19/12
to dexterity-...@googlegroups.com, Lewis Jonathan, Victor
No problem, I'm also at Dexterity development list.

We are not going to PloneKonf but we will talk tomorrow with Jan-Carel in order to organize a multilingual sprint during the weekend. Maybe a good idea is to focus on UI, I'm adding a mockup similar to LP design we brainstormed:

http://bloc.jardigrec.cat/2012/02/ui-multilingual-plone.html

LANGUAGE INDEPENDENT FIELD IMPLEMENTATION STATUS
==================================================

We developed the grok directive, the supermodel xml importer with tests: github.com/plone/plone.multilingualbehavior [branch languageindependentfield]

We still need to take a look at TTW.

So now we have the fields marked and able to implement the widget logic in order to see RO or RW.

About the implementation of the "copying/cloning" method:

1 - Using an event handler with control of recursion. I like this idea, it's a clean idea.
+1

2 - Using z3c.form data manager. Only for z3cform, not native and orthogonal.
-1

3 - LanguageIndependentProperty. Not orthogonal with dexterity.
-1

4 - Create a generic MultiLingualItem subclass of Item and override __getattribute__ and __setattr__. Not orthogonal with dexterity
-1

5 - What about using the grok directive and the xml importer in order to override the __getattribute__ and __setattr__ on the fields ? It'll not work when we add the marker interface manually, but can be acceptable.
+1

About always copying the content on all the objects, is there any way we can point to the same Blob file on binary dexterity fields? When a blob field is language independent it would be great not to copy on all the objects.

Ramon




--
You received this message because you are subscribed to the Google Groups "Dexterity development" group.
To post to this group, send email to dexterity-...@googlegroups.com.
To unsubscribe from this group, send email to dexterity-develo...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/dexterity-development?hl=en.




--
Ramon a.k.a bloodbare

Jan-Carel Brand

unread,
Feb 20, 2012, 3:37:21 AM2/20/12
to dexterity-...@googlegroups.com, Lewis Jonathan, Victor
On Mon, 2012-02-20 at 00:02 +0100, Ramon Navarro Bosch wrote:
> No problem, I'm also at Dexterity development list.
>
> We are not going to PloneKonf but we will talk tomorrow with Jan-Carel
> in order to organize a multilingual sprint during the weekend. Maybe a
> good idea is to focus on UI, I'm adding a mockup similar to LP design
> we brainstormed:
>
> http://bloc.jardigrec.cat/2012/02/ui-multilingual-plone.html
>
> LANGUAGE INDEPENDENT FIELD IMPLEMENTATION STATUS
> ==================================================
>
> We developed the grok directive, the supermodel xml importer with
> tests: github.com/plone/plone.multilingualbehavior [branch
> languageindependentfield]
>
> We still need to take a look at TTW.
>
> So now we have the fields marked and able to implement the widget
> logic in order to see RO or RW.
>
> About the implementation of the "copying/cloning" method:
>
> 1 - Using an event handler with control of recursion. I like this
> idea, it's a clean idea.
> +1

Although, if you have multiple (non lingua-related) event handlers with
many of them potentially changing field values, and you can't control
the execution order, then you could run into problems.


>
> 2 - Using z3c.form data manager. Only for z3cform, not native and
> orthogonal.
> -1
>
> 3 - LanguageIndependentProperty. Not orthogonal with dexterity.
> -1
>
> 4 - Create a generic MultiLingualItem subclass of Item and override
> __getattribute__ and __setattr__. Not orthogonal with dexterity
> -1
>
> 5 - What about using the grok directive and the xml importer in order
> to override the __getattribute__ and __setattr__ on the fields ? It'll
> not work when we add the marker interface manually, but can be
> acceptable.
> +1

__getattribute__ and __setattr__ are overridden if one wants to override
the reading and writing of an attribute on an instance.

This doesn't make sense in the case of Field objects. When a field value
is stored, it's not an attribute on the Field instance that gets stored.
It's an attribute on the content type.

For the fields, I think the correct methods to override would be *get*
and *set*.

See the class Field in zope.schema._bootstrapfields.py

The problem here is that these methods need to be called explicitly for
the override to work.

Something like this (writing from memory):

myobj.fields.getField('title').set(myobj, u"Hello World")

I think the z3c.form form machinery will call the *get* and *set*
methods, but what if the user just sets the value via attribute
assignment?

myobj.title = u"Hello World"

Then the overrides don't get called.

It would be nice if something like this worked, I'm just not sure how it
could...


JC

> To unsubscribe from this group, send email to dexterity-development
> +unsub...@googlegroups.com.

David Glick

unread,
Feb 20, 2012, 3:45:23 AM2/20/12
to dexterity-...@googlegroups.com, Jan-Carel Brand, Lewis Jonathan, Victor
On 2/20/12 12:37 AM, Jan-Carel Brand wrote:
> On Mon, 2012-02-20 at 00:02 +0100, Ramon Navarro Bosch wrote:
>> No problem, I'm also at Dexterity development list.
>>
>> We are not going to PloneKonf but we will talk tomorrow with Jan-Carel
>> in order to organize a multilingual sprint during the weekend. Maybe a
>> good idea is to focus on UI, I'm adding a mockup similar to LP design
>> we brainstormed:
>>
>> http://bloc.jardigrec.cat/2012/02/ui-multilingual-plone.html
>>
>> LANGUAGE INDEPENDENT FIELD IMPLEMENTATION STATUS
>> ==================================================
>>
>> We developed the grok directive, the supermodel xml importer with
>> tests: github.com/plone/plone.multilingualbehavior [branch
>> languageindependentfield]
>>
>> We still need to take a look at TTW.
>>
>> So now we have the fields marked and able to implement the widget
>> logic in order to see RO or RW.
>>
>> About the implementation of the "copying/cloning" method:
>>
>> 1 - Using an event handler with control of recursion. I like this
>> idea, it's a clean idea.
>> +1
> Although, if you have multiple (non lingua-related) event handlers with
> many of them potentially changing field values, and you can't control
> the execution order, then you could run into problems.
Right. Using this approach means that when you handle an ObjectModified
event on a canonical translation, you won't know if its lang-independent
field values have been copied to the other translations yet. Maybe
that's okay since the other translations would presumably get their own
ObjectModified events, but I haven't really thought it through. I think
this is the approach I would try first.

>> 2 - Using z3c.form data manager. Only for z3cform, not native and
>> orthogonal.
>> -1
>>
>> 3 - LanguageIndependentProperty. Not orthogonal with dexterity.
>> -1
>>
>> 4 - Create a generic MultiLingualItem subclass of Item and override
>> __getattribute__ and __setattr__. Not orthogonal with dexterity
>> -1
If the event handler approach doesn't work out I think this might
actually be the 2nd-best option. Keep in mind it won't take care of
behaviors, since they get/set on the behavior adapter rather than on the
content item itself.

>> 5 - What about using the grok directive and the xml importer in order
>> to override the __getattribute__ and __setattr__ on the fields ? It'll
>> not work when we add the marker interface manually, but can be
>> acceptable.
>> +1
> __getattribute__ and __setattr__ are overridden if one wants to override
> the reading and writing of an attribute on an instance.
>
> This doesn't make sense in the case of Field objects. When a field value
> is stored, it's not an attribute on the Field instance that gets stored.
> It's an attribute on the content type.
Right.

> For the fields, I think the correct methods to override would be *get*
> and *set*.
>
> See the class Field in zope.schema._bootstrapfields.py
>
> The problem here is that these methods need to be called explicitly for
> the override to work.
>
> Something like this (writing from memory):
>
> myobj.fields.getField('title').set(myobj, u"Hello World")
>
> I think the z3c.form form machinery will call the *get* and *set*
> methods, but what if the user just sets the value via attribute
> assignment?

Yeah. And actually I don't think z3c.form does use the field's get/set.
It just adapts to get an IDataManager and then uses it. The default is
the attribute data manager which uses -- you guessed it -- attribute access.


>
> myobj.title = u"Hello World"
>
> Then the overrides don't get called.
>
> It would be nice if something like this worked, I'm just not sure how it
> could...
>

Right, which is what led me to __setattr__ and __getattribute__...

David

Ramon Navarro Bosch

unread,
Feb 20, 2012, 4:35:57 AM2/20/12
to dexterity-...@googlegroups.com, Jan-Carel Brand, Lewis Jonathan, Victor

Sorry for the missunderstanding, I mean to override them on the class :)
 

For the fields, I think the correct methods to override would be *get*
and *set*.

See the class Field in zope.schema._bootstrapfields.py

The problem here is that these methods need to be called explicitly for
the override to work.

Something like this (writing from memory):

myobj.fields.getField('title').set(myobj, u"Hello World")

I think the z3c.form form machinery will call the *get* and *set*
methods, but what if the user just sets the value via attribute
assignment?

Yeah. And actually I don't think z3c.form does use the field's get/set. It just adapts to get an IDataManager and then uses it. The default is the attribute data manager which uses -- you guessed it -- attribute access.


myobj.title = u"Hello World"

Then the overrides don't get called.

It would be nice if something like this worked, I'm just not sure how it
could...

Right, which is what led me to __setattr__ and __getattribute__...

David
--
You received this message because you are subscribed to the Google Groups "Dexterity development" group.
To post to this group, send email to dexterity-development@googlegroups.com.
To unsubscribe from this group, send email to dexterity-development+unsub...@googlegroups.com.

For more options, visit this group at http://groups.google.com/group/dexterity-development?hl=en.




--
Ramon a.k.a bloodbare
Reply all
Reply to author
Forward
0 new messages