Translations lost when migrating to Dexterity

Hello everybody,

for more than a year, I have set out migrating a Plone 4.2 website into up-to-date versions (Plone 5.2, Python 3), and by now I have a fairly good idea of the whole roadmap.

As a first step, I've upgraded the site to Plone 4.3.19, converting the multilingual content from Linguaplone to plone.app.multilingual. The resulting site has been online for several months, everything fine.

The next step is to convert Archetypes content (only standard types, nothing custom) to Dexterity. If I use some caution, I can go through the whole process (migrate exerything except folders first, then migrate folders). I've tried it both in Plone 4.3.19 and Plone 5.2.3.

Plone 4.3.19: just after migration, Language Root Folders seem to be broken, Plone complains they don't have the iTranslatable interface. I can reinstall the multilingual support, then force Plone to refresh the setup (e.g. by adding a language, then removing it), and then the site will be displayed in both languages (LRFs now work). Only problem, all translations links are lost.

Plone 5.2.3: If I don't reinstall the multilingual support, the multilingual control panel is broken, because of missing registry keys. I can manually reimport them from XML, not sure if it's the right solution. Or I can reinstall the multilingual support, and the result is the same as in Plone 4: LRFs work, but translations are lost.

I am about to embark on writing a custom script to save / restore translation relations (save from reference_catalog into a file, restore from that file), but before I do that, has anyone met the same problem? Am I doing something wrong? I have done a lot of googling for weeks, even months, but have never found any clue.

Thanks in advance, Laurent.

OK, it was easier than I thought.

Export script queries reference_catalog and uid_catalog to get a list of translation pairs, and outputs them in CSV format. It should be run on a Plone 4 + Archetypes instance

Import script reads the CSV data to build reference to the objects which needs to be linked. Apparently, a two-way liking is necessary.

The script doesn't seem to run under Plone 5, it generates "Could not adapt" errors, but I'm not going to explore the topic further.

from StringIO import StringIO

from Products.Five.browser import BrowserView
from plone.app.multilingual.interfaces import ITranslationManager
from zope.component.hooks import getSite


class ListTranslations(BrowserView):
    """ List translations pairs from reference_catalog

    This view must be called on a Plone 4 / Archetypes site, otherwise it will
    return nothing, or even an error.
    """

    def __call__(self):
        """"""
        portal = getSite()
        refcat = portal.reference_catalog
        uidcat = portal.uid_catalog

        out = StringIO()

        # List all relations of type 'translationOf'
        brains = refcat(relationship='translationOf')
        for n, b in enumerate(brains, 1):

            # Get path of source object
            for n1, bb in enumerate(uidcat(UID=b.sourceUID)):
                srcPath = bb.getPath()
            assert n1 == 0  # To check that loop was run exactly once

            # Get path of destination object
            for n2, bb in enumerate(uidcat(UID=b.targetUID)):
                destPath = bb.getPath()
            assert n2 == 0  # To check that loop was run exactly once

            # Output in CSV format
            print >> out, '{:d};{};{}'.format(n, srcPath, destPath)

        return out.getvalue()


class FixTranslations(BrowserView):
    """ Rebuild translations pairs

    This view must be called on a Plone 4 / Dexterity site. The output of the previous
    script must be put into a file object "translation_map" created at the root of the site..
    """

    def __call__(self):
        """"""
        portal = getSite()
        csv = portal['translation_map']

        for l in str(csv).split('\n'):

            if not l:
                continue  # Get rid of final "\n"

            n, srcPath, destPath = l.split(';')

            # Retrieve language of source and destination documents
            srcLang = srcPath.split('/', 1)[0]
            destLang = destPath.split('/', 1)[0]

            # Retrieve linked objects
            src = portal.restrictedTraverse(srcPath)
            dest = portal.restrictedTraverse(destPath)

            # Print debug info
            print srcLang, srcPath, src
            print destLang, destPath, dest
            print

            # Link translations, both ways
            ITranslationManager(src).register_translation(destLang, dest)
            ITranslationManager(dest).register_translation(srcLang, src)

        return "OK"
1 Like

Slightly off topic, but I want to mention that multilingual has different profiles for Archetype and Dexterity. You should see them in the control panel (and in /portal_setup)

Thank you, I'll have a look :slight_smile: But, basically, my problem is solved, and I'm happy with that, even if I can't really believe I was the only one having the problem...

I've had to write another 10-liner to fix the missing languages on a few dozens of Dexterity folders. For some reason, the languages were lost during migration, and having no language set prevented these folders from being associated to their translations.


from Products.Five.browser import BrowserView
from plone.app.multilingual.interfaces import ILanguage
from zope.component.hooks import getSite

class FixLanguages(BrowserView):
    """ Add missing languages

    This view must be called on a Plone 4 / Dexterity site.
    Objects without a defined language will be fixed.
    """

    def __call__(self):
        """"""
        portal = getSite()
        catalog = portal.portal_catalog

        brains = catalog(Language='')

        n = 0
        for n, b in enumerate(brains, 1):
            path = b.getPath()
            lang = path.split('/', 3)[2]
            obj = b.getObject()

            print '{:3d}'.format(n), lang, b.portal_type, path

            ILanguage(obj).set_language(lang)

        return u'{} objects fixed'.format(n)