Skip to content

Segment extraction module

The segment extraction module is responsible for extracting translatable segments from source objects.

StreamFieldSegmentExtractor

A helper class to help traverse StreamField values and extract segments.

__init__(self, field, include_overridables=False) special

Initialises a StreamFieldSegmentExtractor.

Parameters:

Name Type Description Default
field StreamField

The StreamField to extract segments from.

required
include_overridables boolean

Set this to True to extract overridable segments too.

False
Source code in wagtail_localize/segments/extract.py
def __init__(self, field, include_overridables=False):
    """
    Initialises a StreamFieldSegmentExtractor.

    Args:
        field (StreamField): The StreamField to extract segments from.
        include_overridables (boolean, optional): Set this to True to extract overridable segments too.
    """
    self.field = field
    self.include_overridables = include_overridables

extract_segments(instance)

Extracts segments from the given model instance.

Parameters:

Name Type Description Default
instance Model

The model instance to extract segments from.

required

Returns:

Type Description
list[StringSegmentValue, TemplateSegmentValue, RelatedObjectSegmentValue, or OverridableSegmentValue]

The segment values that have been extracted.

Source code in wagtail_localize/segments/extract.py
def extract_segments(instance):
    """
    Extracts segments from the given model instance.

    Args:
        instance (Model): The model instance to extract segments from.

    Returns:
        list[StringSegmentValue, TemplateSegmentValue, RelatedObjectSegmentValue, or OverridableSegmentValue]: The
            segment values that have been extracted.
    """
    segments = []

    for translatable_field in get_translatable_fields(instance.__class__):
        field = translatable_field.get_field(instance.__class__)
        is_translatable = translatable_field.is_translated(instance)
        is_synchronized = translatable_field.is_synchronized(instance)
        is_overridable = translatable_field.is_overridable(instance)
        extract_overridables = is_synchronized and is_overridable

        if hasattr(field, "get_translatable_segments"):
            if is_translatable:
                segments.extend(
                    segment.wrap(field.name)
                    for segment in field.get_translatable_segments(
                        field.value_from_object(instance)
                    )
                )

        elif isinstance(field, StreamField):
            if is_translatable:
                segments.extend(
                    segment.wrap(field.name)
                    for segment in StreamFieldSegmentExtractor(
                        field, include_overridables=extract_overridables
                    ).handle_stream_block(field.value_from_object(instance))
                )

        elif isinstance(field, RichTextField):
            if is_translatable:
                template, strings = extract_strings(field.value_from_object(instance))

                # Find all unique href values
                hrefs = set()
                for _string, attrs in strings:
                    for tag_attrs in attrs.values():
                        if "href" in tag_attrs:
                            hrefs.add(tag_attrs["href"])

                field_segments = (
                    [TemplateSegmentValue("", "html", template, len(strings))]
                    + [
                        StringSegmentValue("", string, attrs=attrs)
                        for string, attrs in strings
                    ]
                    + [
                        OverridableSegmentValue(quote_path_component(href), href)
                        for href in sorted(hrefs)
                    ]
                )

                segments.extend(segment.wrap(field.name) for segment in field_segments)

            if extract_overridables:
                pass  # TODO: Extract images and links

        elif isinstance(field, (models.TextField, models.CharField)):
            if not field.choices:
                value = field.value_from_object(instance)

                if value is None:
                    continue

                if is_translatable:
                    segments.append(StringSegmentValue(field.name, value))

                elif extract_overridables:
                    segments.append(OverridableSegmentValue(field.name, value))

        elif isinstance(field, (models.ForeignKey)):
            if is_translatable:
                if not issubclass(field.related_model, TranslatableMixin):
                    raise ImproperlyConfigured(
                        "The foreign key `{}.{}.{}` was registered as a translatable "
                        "field but the model it points to `{}.{}` is not translatable".format(
                            field.model._meta.app_label,
                            field.model.__name__,
                            field.name,
                            field.related_model._meta.app_label,
                            field.related_model.__name__,
                        )
                    )

                related_instance = getattr(instance, field.name)

                if related_instance:
                    segments.append(
                        RelatedObjectSegmentValue.from_instance(
                            field.name, related_instance
                        )
                    )

            elif extract_overridables:
                related_instance = getattr(instance, field.name)

                if related_instance:
                    segments.append(
                        OverridableSegmentValue(field.name, related_instance.pk)
                    )
        elif (
            isinstance(field, (models.ManyToOneRel))
            and isinstance(field.remote_field, ParentalKey)
            and issubclass(field.related_model, TranslatableMixin)
        ):
            manager = getattr(instance, field.name)

            if is_translatable:
                for child_instance in manager.all():
                    segments.extend(
                        segment.wrap(str(child_instance.translation_key)).wrap(
                            field.name
                        )
                        for segment in extract_segments(child_instance)
                    )

            elif extract_overridables:
                pass  # TODO

    class Counter:
        def __init__(self):
            self.value = 0

        def next(self):
            self.value += 1
            return self.value

    counter = Counter()

    return [
        segment.with_order(counter.next())
        for segment in segments
        if not segment.is_empty()
    ]

quote_path_component(text)

Puts quotes around the path compoenents, and escapes any special characters.

Source code in wagtail_localize/segments/extract.py
def quote_path_component(text):
    """
    Puts quotes around the path compoenents, and escapes any special characters.
    """
    return "'" + text.replace("\\", "\\\\").replace("'", "\\'") + "'"