Skip to content

Translation memory models

These models are responsible for storing individual source strings and their translations.

graph TD A[String] --> B[wagtail.Locale] C[StringTranslation] --> B C --> A C --> D[TranslationContext] D --> E[TranslatableObject] F[Template] style B stroke-dasharray: 5 5 style E stroke-dasharray: 5 5

String

Represents a unique string of translatable text.

Attributes:

Name Type Description
locale ForeignKey to Locale

The locale of the string.

data TextField

The string.

data_hash UUIDField

A hash of the string, for more efficient indexing of long strings.

_get_data_hash(data) classmethod private

Generates a UUID from the given string.

Parameters:

Name Type Description Default
data string

The string to generate a hash of.

required

Returns:

Type Description
UUID

The UUID hash.

Source code in wagtail_localize/models.py
@classmethod
def _get_data_hash(cls, data):
    """
    Generates a UUID from the given string.

    Args:
        data (string): The string to generate a hash of.

    Returns:
        UUID: The UUID hash.
    """
    return uuid.uuid5(cls.UUID_NAMESPACE, data)

as_value(self)

Creates a StringValue object from the contents of this string.

Returns:

Type Description
StringValue

A StringValue instance with the content of this String.

Source code in wagtail_localize/models.py
def as_value(self):
    """
    Creates a StringValue object from the contents of this string.

    Returns:
        StringValue: A StringValue instance with the content of this String.
    """
    return StringValue(self.data)

from_value(locale, stringvalue) classmethod

Gets or creates a String instance from a StringValue object.

Parameters:

Name Type Description Default
stringvalue StringValue

The value of the string.

required

Returns:

Type Description
String

The String instance that corresponds with the given stringvalue and locale.

Source code in wagtail_localize/models.py
@classmethod
def from_value(cls, locale, stringvalue):
    """
    Gets or creates a String instance from a StringValue object.

    Args:
        locale (ForeignKey to Locale) The locale of the string.
        stringvalue (StringValue): The value of the string.

    Returns:
        String: The String instance that corresponds with the given stringvalue and locale.
    """
    string, created = cls.objects.get_or_create(
        locale_id=pk(locale),
        data_hash=cls._get_data_hash(stringvalue.data),
        defaults={"data": stringvalue.data},
    )

    return string

StringTranslation

Represents a translation of a string.

Attributes:

Name Type Description
translation_of ForeignKey to String

The String that this is a translation of.

locale ForeignKey to Locale

The Locale of this translation.

context ForeignKey to TranslationContext

The context that this translation was made in. This allows different fields/pages to have different translations of the same source string.

data TextField

The translation.

translation_type CharField with choices 'manual' or 'machine'

Whether the translationw as performed by a human or machine.

tool_name CharField

The name of the tool that was used to make this translation.

last_translated_by ForeignKey to User

The user who last updated this translation.

created_at DateTimeField

The date/time that this translation was first created.

updated_at DateFimeField

The date/time that this translation was last updated.

has_error BooleanField

Set to True if the value of this translation has an error. We store translations with errors in case they were edited from an external system. This allows us to display the error in Wagtail.

field_error TextField

If there was a database-level validation error while saving the translated object, that error is tored here. Note that this only makes sense if the context is not null.

from_text(translation_of, locale, context, data) classmethod

Gets or creates a StringTranslation instance from the given parameters.

Parameters:

Name Type Description Default
translation_of ForeignKey to String

The String that this is a translation of.

required
locale ForeignKey to Locale

The Locale of this translation.

required
context ForeignKey to TranslationContext

The context that this translation was made in. This allows different fields/pages to have different translations of the same source string.

required
data TextField

The translation.

required

Returns:

Type Description
String

The String instance that corresponds with the given stringvalue and locale.

Source code in wagtail_localize/models.py
@classmethod
def from_text(cls, translation_of, locale, context, data):
    """
    Gets or creates a StringTranslation instance from the given parameters.

    Args:
        translation_of (ForeignKey to String): The String that this is a translation of.
        locale (ForeignKey to Locale): The Locale of this translation.
        context (ForeignKey to TranslationContext): The context that this translation was made in. This allows different
            fields/pages to have different translations of the same source string.
        data (TextField): The translation.

    Returns:
        String: The String instance that corresponds with the given stringvalue and locale.
    """
    segment, created = cls.objects.get_or_create(
        translation_of=translation_of,
        locale_id=pk(locale),
        context_id=pk(context),
        defaults={"data": data},
    )

    return segment

get_comment(self)

Returns a comment to display to the user containing info on how and when the string was translated.

Returns:

Type Description
str

A comment to display to the user.

Source code in wagtail_localize/models.py
def get_comment(self):
    """
    Returns a comment to display to the user containing info on how and when the string was translated.

    Returns:
        str: A comment to display to the user.
    """
    if self.tool_name:
        return _("Translated with {tool_name} on {date}").format(tool_name=self.tool_name, date=self.updated_at.strftime(DATE_FORMAT))

    elif self.translation_type == self.TRANSLATION_TYPE_MANUAL:
        return _("Translated manually on {date}").format(date=self.updated_at.strftime(DATE_FORMAT))

    elif self.translation_type == self.TRANSLATION_TYPE_MACHINE:
        return _("Machine translated on {date}").format(date=self.updated_at.strftime(DATE_FORMAT))

get_error(self)

Returns a string containing any validation errors on the saved value.

Returns:

Type Description
str

The validation error if there is one. None: If there isn't an error.

Source code in wagtail_localize/models.py
def get_error(self):
    """
    Returns a string containing any validation errors on the saved value.

    Returns:
        str: The validation error if there is one.
        None: If there isn't an error.
    """
    if not self.has_error:
        return

    # Check for HTML validation errors
    try:
        StringValue.from_translated_html(self.data)
        validate_translation_links(self.translation_of.data, self.data)
    except ValueError as e:
        return e.args[0]

    # Check if a database error was raised when we last attempted to publish
    if self.context is not None and self.field_error:
        return self.field_error

set_field_error(self, error)

This sets the has_error/field_error fields to the value of the given ValidationError instance.

Note: If the given ValidationError contains multiple errors, only the first one is stored.

Note: This updates the database instance as well.

Parameters:

Name Type Description Default
error ValidationError

The validation error to store.

required
Source code in wagtail_localize/models.py
def set_field_error(self, error):
    """
    This sets the `has_error`/`field_error` fields to the value of the given ValidationError instance.

    Note: If the given ValidationError contains multiple errors, only the first one is stored.

    Note: This updates the database instance as well.

    Args:
        error (ValidationError): The validation error to store.
    """
    self.has_error = True
    # TODO (someday): We currently only support one error at a time
    self.field_error = error[0].messages[0]
    self.save(update_fields=['has_error', 'field_error'])

Template

A Template stores the structure of a RichTextField or RichTextBlock.

When a RichTextField/RichTextBlock is converted into segments, all translatable segments are stripped out of the block and stored as String instances. The remaining HTML is saved as a Template and is used for recombining the translated strings back into a rich text value.

Attributes:

Name Type Description
template TextField

The template value

uuid UUIDField

A hash of the template contents for efficient indexing.

template_format CharField

The format of the template (currently, only 'html' is supported).

string_count PositiveIntegerField

The number of translatable stirngs that were extracted from the template.

from_value(template_value) classmethod

Gets or creates a Template instance from a TemplateValue object.

Parameters:

Name Type Description Default
template_value TemplateValue

The value of the template.

required

Returns:

Type Description
Template

The Template instance that corresponds with the given template_value.

Source code in wagtail_localize/models.py
@classmethod
def from_value(cls, template_value):
    """
    Gets or creates a Template instance from a TemplateValue object.

    Args:
        template_value (TemplateValue): The value of the template.

    Returns:
        Template: The Template instance that corresponds with the given template_value.
    """
    uuid_namespace = uuid.uuid5(cls.BASE_UUID_NAMESPACE, template_value.format)

    template, created = cls.objects.get_or_create(
        uuid=uuid.uuid5(uuid_namespace, template_value.template),
        defaults={
            "template": template_value.template,
            "template_format": template_value.format,
            "string_count": template_value.string_count,
        },
    )

    return template

TranslationContext

Represents a context that a string may be translated in.

Strings can be translated differently in different contexts. A context is a combination of an object and content path.

Attributes:

Name Type Description
object ForeignKey to TranslatableObject

The object.

path TextField

The content path.

field_path TextField

the field path.

path_id UUIDField

A hash of the path for efficient indexing of long content paths.

_get_path_id(path) classmethod private

Generates a UUID from the given content path.

Parameters:

Name Type Description Default
path string

The content path to generate a hash of.

required

Returns:

Type Description
UUID

The UUID hash.

Source code in wagtail_localize/models.py
@classmethod
def _get_path_id(cls, path):
    """
    Generates a UUID from the given content path.

    Args:
        path (string): The content path to generate a hash of.

    Returns:
        UUID: The UUID hash.
    """
    return uuid.uuid5(uuid.UUID("fcab004a-2b50-11ea-978f-2e728ce88125"), path)

get_field_path(self, instance)

Gets the field path for this context

Field path's were introduced in version 1.0, any contexts that were created before that release won't have one.

Source code in wagtail_localize/models.py
def get_field_path(self, instance):
    """
    Gets the field path for this context

    Field path's were introduced in version 1.0, any contexts that were created before that release won't have one.
    """
    if not self.field_path:
        def get_field_path_from_field(instance, path_components):
            field_name = path_components[0]
            field = instance._meta.get_field(field_name)

            if isinstance(field, StreamField):
                def get_field_path_from_stream_block(stream_value, path_components):
                    stream_blocks_by_id = {
                        block.id: block
                        for block in stream_value
                    }
                    block_id = path_components[0]
                    block = stream_blocks_by_id[block_id]
                    block_def = stream_value.stream_block.child_blocks[block.block_type]

                    if isinstance(block_def, blocks.StructBlock):
                        return [block.block_type, path_components[1]]

                    elif isinstance(block_def, blocks.StreamBlock):
                        return [block.block_type] + get_field_path_from_stream_block(block.value, path_components[1:])

                    else:
                        return [block.block_type]

                return [field_name] + get_field_path_from_stream_block(field.value_from_object(instance), path_components[1:])

            elif (
                isinstance(field, (models.ManyToOneRel))
                and isinstance(field.remote_field, ParentalKey)
                and issubclass(field.related_model, TranslatableMixin)
            ):
                manager = getattr(instance, field_name)
                child_instance = manager.get(translation_key=path_components[1])
                return [field_name] + get_field_path_from_field(child_instance, path_components[2:])

            else:
                return [field_name]

        self.field_path = '.'.join(get_field_path_from_field(instance, self.path.split('.')))
        self.save(update_fields=['field_path'])

    return self.field_path