Skip to content

Synctree module

The synctree module implements the logic that keeps locale trees in sync.

PageIndex

An in-memory index of pages to remove the need to query the database.

Each entry in the index is a unique page by translation key, so a page that has been translated into different languages appears only once.

Entry

Represents a page in the index.

from_page_instance(page) classmethod

Initialises an Entry from the given page instance.

Source code in wagtail_localize/synctree.py
@classmethod
def from_page_instance(cls, page):
    """
    Initialises an Entry from the given page instance.
    """
    # Get parent, but only if the parent is not the root page. We consider the
    # homepage of each langauge tree to be the roots
    if page.depth > 2:
        parent_page = page.get_parent()
    else:
        parent_page = None

    return cls(
        page.content_type,
        page.translation_key,
        page.locale,
        parent_page.translation_key if parent_page else None,
        list(
            Page.objects.filter(
                translation_key=page.translation_key,
                alias_of__isnull=True,
            ).values_list("locale", flat=True)
        ),
        list(
            Page.objects.filter(
                translation_key=page.translation_key,
                alias_of__isnull=False,
            ).values_list("locale", flat=True)
        ),
    )

from_database() classmethod

Populates the index from the database.

Source code in wagtail_localize/synctree.py
@classmethod
def from_database(cls):
    """
    Populates the index from the database.
    """
    pages = []

    for page in Page.objects.filter(alias_of__isnull=True, depth__gt=1).only(
        *PageIndex.Entry.REQUIRED_PAGE_FIELDS
    ):
        pages.append(PageIndex.Entry.from_page_instance(page))

    return PageIndex(pages)

not_translated_into(self, locale)

Returns an index of pages that are not translated into the specified locale. This includes pages that have and don't have a placeholder

Source code in wagtail_localize/synctree.py
def not_translated_into(self, locale):
    """
    Returns an index of pages that are not translated into the specified locale.
    This includes pages that have and don't have a placeholder
    """
    pages = [page for page in self.pages if locale.id not in page.locales]

    return PageIndex(pages)

sort_by_tree_position(self)

Returns a new index with the pages sorted in depth-first-search order using their parent in their respective source locale.

Source code in wagtail_localize/synctree.py
def sort_by_tree_position(self):
    """
    Returns a new index with the pages sorted in depth-first-search order
    using their parent in their respective source locale.
    """
    remaining_pages = set(page.translation_key for page in self.pages)

    new_pages = []

    def _walk(translation_key):
        for page in self.by_parent_translation_key.get(translation_key, []):
            if page.translation_key not in remaining_pages:
                continue

            remaining_pages.remove(page.translation_key)
            new_pages.append(page)
            _walk(page.translation_key)

    _walk(None)

    if remaining_pages:
        print("Warning: {} orphaned pages!".format(len(remaining_pages)))

    return PageIndex(new_pages)

synchronize_tree(source_locale, target_locale, *, page_index=None)

Synchronises a locale tree with an other locale.

This creates alias pages for pages that are not translated yet.

Parameters:

Name Type Description Default
source_locale Locale

The Locale to sync from.

required
target_locale Locale

The Locale to sync into

required
page_index PageIndex

The Page index to reuse for performance. Otherwise will generate a new one.

None
Source code in wagtail_localize/synctree.py
def synchronize_tree(source_locale, target_locale, *, page_index=None):
    """
    Synchronises a locale tree with an other locale.

    This creates alias pages for pages that are not translated yet.

    Args:
        source_locale (Locale): The Locale to sync from.
        target_locale (Locale): The Locale to sync into
        page_index (PageIndex, optional): The Page index to reuse for performance. Otherwise will generate a new one.

    """
    # Build a page index
    if not page_index:
        page_index = PageIndex.from_database().sort_by_tree_position()

    # Find pages that are not translated for this locale
    # This includes locales that have a placeholder, it only excludes locales that have an actual translation
    pages_not_in_locale = page_index.not_translated_into(target_locale)

    for page in pages_not_in_locale:
        # Skip pages that do not exist in the source
        if source_locale.id not in page.locales and source_locale.id not in page.aliased_locales:
            continue

        # Fetch source from database
        model = page.content_type.model_class()
        source_page = model.objects.get(
            translation_key=page.translation_key, locale=source_locale
        )

        if target_locale.id not in page.aliased_locales:
            source_page.copy_for_translation(
                target_locale, copy_parents=True, alias=True
            )