Macadames' blog

Le silence est d'or, jusqu'à ce qu'il devienne plomb.

Plone and multilingual sites

with 9 comments

Usually we build multilingual Plone sites with LinguaPlone.

This solution has a big advantage, it’s generic and very easy to implement in a plone site.

But there are many inconvenients, due to the design of this product (translations are independent by design) :

  • Each translation is a new Archetype object, and it could be a big problem on sites with many contents, the portal objects number is increased by the number of available languages.
  • Translations uses plone references catalog to be linked to the original (called canonical) object, but when moving objects translations are not moved, when copying pasting objects, translations are not pasted, when deleting objects translations are not deleted, when reordering contents, translations are not reordered, when publishing objects translations are not published, … For web masters maintaining a site  with LinguaPlone inside could be a challenge.
  • When translating folders with LP, all translated contents are moved from the canonical folder to the translated folder with same language, translating low level folders on big depth tree sites could take a long long time, don’t be surprise if you get errors.
  • If a content is neutral (with no language attribute), inside a translated folder, it could not be seen when browsing a translation of the parent folder.
  • At last a lower problem :  the translation edit forms are not pretty to use, they show a table with two columns, the first column with the « canonical » content inside in « view » mode, the second column with the translation edit form, in fixed width sites the translated form width is sometimes ridiculously small.

But since many years we use LinguaPlone because it was the only easy way to make multilingual Plone sites.

By the past we were using a LP patch called LinguaFace to reduce the number of problems with LP (synchronisation on reorder, copy-paste, delete, or move – see neutral contents inside all translated folders – more usable translation edit forms …), but LF add a new layer of complexity and maintaining it with all LP versions becomes complicated. See some examples on how it works :

  • when a content is copied, all translations are copied
  • when a content is pasted or moved all translations are pasted or moved at the good place (not so easy)
  • when a content is published or retracted translations follow the same workflow transition
  • when a folder is translated, we don’t see only objects inside but also objects with same canonical Path (a new catalog index) to see also neutral contents.
  • Navtree is patched, breadcrumbs are patched, to use canonical path
  • and so on …

A big nightmare.

To day a new solution exists that store translations inside each Archetypes field, raptus.multilingualfields, and a Plone integration of raptus.multilanguagefields called raptus.multilingualplone that extends the schema of all Plone Content Types making them translatable. raptus.multilingualfields also provides multilingual catalog indexes that return the good translated data when searching for contents or displaying trees, and multilingual criterions for topics.

A LP feature not provided by raptus.multilingualfields is the internationalization of urls, if you really need this feature, i think it’s not a big challenge to add some traversal rules, for me it’s not essential.

AnotherLP  feature that can’t be provided when storing translations in fields is to get different workflows or security settings for each translation. If you need this feature use LinguaPlone, LinguaPlone is done for that, it makes all translated contents independent, but i’m curious to know the number of users who really want this feature, all clients i had never ask for that but finally, after some LP experience, always wanted the exact opposite use-case.

To make your Plone site translatable with raptus.multilanguagefields, you have two choices :

  1. Add raptus.multilanguageplone in your buildout and install it in your Plone Site using extensions products control panel, to make all your Plone content types and derived translatable (all fields for which translation make a sense are translatable).
  2. I prefer integrate by myself raptus.multilanguagefields inside a product, since we could want just some fields or content types translatable, as example i don’t need to translate the images or the files contents, just their titles and descriptions.

How to implement the second solution ?

Just take a look at raptus.multilanguageplone code, it’s easy :

  1. Make your archetype extenders to make your wanted fields translatable, example can be found here
  2. register your extenders in setuphandlers (Generic Setup import step), example here
  3. replace the standard catalog  indexes with multilingual indexes in Generic Setup profile, example here

That’s all, you will get superb edit forms with translatable fields inside, you also will get a google help to translate contents (a pleasant gadget). I say « Bravo » to raptus developpers.

Important :

  • these products are young, and there’s still many work todo to make it work without problems (tests are needed …). Last 0.6 releases have bugs under plone3.3, use the svn versions below instead.
  • raptus.multilanguageplone 0.6  has a bug in extenders with primary fields, tested in Plone 3.3  (fixed in branch aws_evols, not tested with images at this time)
  • raptus.multilanguagefields 0.6 has a bug, doctests are broken in Plone3.3 (fixed in trunk )
  • At this time these products don’t have unit tests or functional tests, it’s the only reproach i can make.  I started the work  here, and here

Written by macadames

6, février 2010 à 2:28

9 Réponses

Subscribe to comments with RSS.

  1. Please test your software before releasing it. A fresh Plone 3.3 installation without further add-ons fails directly while rendering page:

    Site Error

    An error was encountered while publishing this resource.

    SchemaException

    Sorry, a site error occurred.
    Traceback (innermost last):

    Module ZPublisher.Publish, line 202, in publish_module_standard
    Module ZPublisher.Publish, line 150, in publish
    Module plone.app.linkintegrity.monkey, line 21, in zpublisher_exception_hook_wrapper
    Module Zope2.App.startup, line 221, in zpublisher_exception_hook
    Module ZPublisher.Publish, line 119, in publish
    Module ZPublisher.mapply, line 88, in mapply
    Module ZPublisher.Publish, line 42, in call_object
    Module Products.CMFPlone.FactoryTool, line 379, in __call__
    Module ZPublisher.mapply, line 88, in mapply
    Module ZPublisher.Publish, line 42, in call_object
    Module Products.CMFFormController.FSControllerPageTemplate, line 90, in __call__
    Module Products.CMFFormController.BaseControllerPageTemplate, line 31, in _call
    Module Shared.DC.Scripts.Bindings, line 313, in __call__
    Module Shared.DC.Scripts.Bindings, line 350, in _bindAndExec
    Module Products.CMFCore.FSPageTemplate, line 216, in _exec
    Module Products.CMFCore.FSPageTemplate, line 155, in pt_render
    Module Products.PageTemplates.PageTemplate, line 98, in pt_render
    Module zope.pagetemplate.pagetemplate, line 117, in pt_render
    Warning: Macro expansion failed
    Warning: exceptions.KeyError: ‘macro’
    Module zope.tal.talinterpreter, line 271, in __call__
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 891, in do_useMacro
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 536, in do_optTag_tal
    Module zope.tal.talinterpreter, line 521, in do_optTag
    Module zope.tal.talinterpreter, line 516, in no_tag
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 891, in do_useMacro
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 536, in do_optTag_tal
    Module zope.tal.talinterpreter, line 521, in do_optTag
    Module zope.tal.talinterpreter, line 516, in no_tag
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 957, in do_defineSlot
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 536, in do_optTag_tal
    Module zope.tal.talinterpreter, line 521, in do_optTag
    Module zope.tal.talinterpreter, line 516, in no_tag
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 861, in do_defineMacro
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 957, in do_defineSlot
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 536, in do_optTag_tal
    Module zope.tal.talinterpreter, line 521, in do_optTag
    Module zope.tal.talinterpreter, line 516, in no_tag
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 949, in do_defineSlot
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 536, in do_optTag_tal
    Module zope.tal.talinterpreter, line 521, in do_optTag
    Module zope.tal.talinterpreter, line 516, in no_tag
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 861, in do_defineMacro
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 536, in do_optTag_tal
    Module zope.tal.talinterpreter, line 521, in do_optTag
    Module zope.tal.talinterpreter, line 516, in no_tag
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 891, in do_useMacro
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 855, in do_condition
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 957, in do_defineSlot
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 536, in do_optTag_tal
    Module zope.tal.talinterpreter, line 521, in do_optTag
    Module zope.tal.talinterpreter, line 516, in no_tag
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 855, in do_condition
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 536, in do_optTag_tal
    Module zope.tal.talinterpreter, line 521, in do_optTag
    Module zope.tal.talinterpreter, line 516, in no_tag
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 824, in do_loop_tal
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 536, in do_optTag_tal
    Module zope.tal.talinterpreter, line 521, in do_optTag
    Module zope.tal.talinterpreter, line 516, in no_tag
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 536, in do_optTag_tal
    Module zope.tal.talinterpreter, line 525, in do_optTag
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 824, in do_loop_tal
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 536, in do_optTag_tal
    Module zope.tal.talinterpreter, line 521, in do_optTag
    Module zope.tal.talinterpreter, line 516, in no_tag
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 891, in do_useMacro
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 536, in do_optTag_tal
    Module zope.tal.talinterpreter, line 521, in do_optTag
    Module zope.tal.talinterpreter, line 516, in no_tag
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 891, in do_useMacro
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 536, in do_optTag_tal
    Module zope.tal.talinterpreter, line 521, in do_optTag
    Module zope.tal.talinterpreter, line 516, in no_tag
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 534, in do_optTag_tal
    Module zope.tal.talinterpreter, line 516, in no_tag
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 891, in do_useMacro
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 536, in do_optTag_tal
    Module zope.tal.talinterpreter, line 521, in do_optTag
    Module zope.tal.talinterpreter, line 516, in no_tag
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 855, in do_condition
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 536, in do_optTag_tal
    Module zope.tal.talinterpreter, line 521, in do_optTag
    Module zope.tal.talinterpreter, line 516, in no_tag
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 855, in do_condition
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 949, in do_defineSlot
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 534, in do_optTag_tal
    Module zope.tal.talinterpreter, line 516, in no_tag
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 824, in do_loop_tal
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 536, in do_optTag_tal
    Module zope.tal.talinterpreter, line 521, in do_optTag
    Module zope.tal.talinterpreter, line 516, in no_tag
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 536, in do_optTag_tal
    Module zope.tal.talinterpreter, line 521, in do_optTag
    Module zope.tal.talinterpreter, line 516, in no_tag
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 534, in do_optTag_tal
    Module zope.tal.talinterpreter, line 516, in no_tag
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 891, in do_useMacro
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 536, in do_optTag_tal
    Module zope.tal.talinterpreter, line 521, in do_optTag
    Module zope.tal.talinterpreter, line 516, in no_tag
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 891, in do_useMacro
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 536, in do_optTag_tal
    Module zope.tal.talinterpreter, line 521, in do_optTag
    Module zope.tal.talinterpreter, line 516, in no_tag
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 949, in do_defineSlot
    Module zope.tal.talinterpreter, line 346, in interpret
    Module zope.tal.talinterpreter, line 586, in do_setLocal_tal
    Module zope.tales.tales, line 696, in evaluate
    URL: file:/home/ajung/.buildout/eggs/Products.Archetypes-1.5.13-py2.4.egg/Products/Archetypes/skins/archetypes/widgets/rich.pt
    Line 36, Column 8
    Expression:
    Names:
    {‘container’: ,
    ‘context’: ,
    ‘default’: ,
    ‘here’: ,
    ‘loop’: {u’field’: ,
    u’fieldset’: ,
    u’lang’: },
    ‘nothing’: None,
    ‘options’: {‘args’: (),
    ‘state’: },
    ‘repeat’: ,
    ‘request’: ,
    ‘root’: ,
    ‘template’: ,
    ‘traverse_subpath’: [],
    ‘user’: }
    Module Products.PageTemplates.ZRPythonExpr, line 49, in __call__
    __traceback_info__: here.isBinary(fieldName)
    Module PythonExpr, line 1, in
    Module Products.Archetypes.BaseObject, line 262, in isBinary
    Module raptus.multilanguagefields, line 152, in _getField
    Module Products.Archetypes.BaseObject, line 830, in Schema
    Module zope.app.component.hooks, line 96, in adapter_hook
    Module archetypes.schemaextender.extender, line 154, in instanceSchemaFactory
    Module Products.Archetypes.Schema, line 190, in addField
    Module Products.Archetypes.Schema, line 207, in _validateOnAdd
    SchemaException:
    Troubleshooting Suggestions

    The URL may be incorrect.
    The parameters passed to this resource may be incorrect.
    A resource that this resource relies on may be encountering an error.
    For more detailed information about the error, please refer to the error log.

    If the error persists please contact the site maintainer. Thank you for your patience.

    Andreas Jung

    6, février 2010 at 3:30

  2. Hi Andreas,

    You’re right releasing without tests is bad (that’s just what i said in my post).

    But it’s not « my » software, i’m not the release manager, i’ve just added some minimal tests in svn yesterday (for installation because i’ve got errors on plone 3.3), fixed errors, running tests, finally everything was ok. You can test these changes with these svn branches :

    https://svn.plone.org/svn/collective/raptus.multilanguagefields/trunk

    https://svn.plone.org/svn/collective/raptus.multilanguageplone/branches/aws_evols

    macadames

    6, février 2010 at 6:21

  3. Thanks jean math !! great post.

    youenn

    7, février 2010 at 8:35

    • I hope it will help us dear Youenn😉

      macadames

      8, février 2010 at 10:20

  4. We usually experience the opposite. If the customer is large enough to require translations, he is usually large enough to require independent workflows for content. Each translation has to be verified independently and it is not even sure, the content tree is the same everywhere. We never had a company where 1 person spoke enough languages to be able to verify and publish content in all languages at once.

    Sven Deichmann

    8, février 2010 at 6:53

    • In a situation, where all contents must be translated in many languages by different persons with different workflows and security settings, LinguaPlone is a good solution.

      I just know that a current use case in non english-speaking countries is 2 or 3 languages, « country language + english » or « regional language + country language + english », and all the work done by one person.

      macadames

      8, février 2010 at 10:51

  5. What is the difference between LinguaFace and http://pypi.python.org/pypi/slc.linguatools

    encolpe

    9, février 2010 at 5:17

    • I don’t know this product, some of its functionalities look like LinguaFace ones, excepted the neutral tree display. Perhaps it’s more zope3 aware than LinguaFace, and with more tests (it’s not difficult).

      macadames

      10, février 2010 at 9:08

  6. It could be interresting to merge all this in LinguaPlone.

    encolpe

    10, février 2010 at 12:31


Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

%d blogueurs aiment cette page :