Skip to main content

An extension for wagtail-localize that integrates with the Smartling translation platform

Project description

Wagtail Localize Smartling

License: MPL 2.0 PyPI version CI

An extension for Wagtail Localize that integrates with the Smartling translation platform.

Links

Supported versions

  • Python 3.8+
  • Django 4.2+
  • Wagtail 6.1+

Installation

  1. Install the package from PyPI:

    python -m pip install wagtail-localize-smartling
    
  2. Add "wagtail_localize_smartling" to INSTALLED_APPS in your Django settings. Make sure it's before "wagtail_localize" and "wagtail_localize.locales":

    INSTALLED_APPS = [
        ...
        "wagtail_localize_smartling",
        "wagtail_localize",
        "wagtail_localize.locales",
        ...
    ]
    
  3. Configure the plugin in your Django settings:

     WAGTAIL_LOCALIZE_SMARTLING = {
         # Required settings (get these from "Account settings" > "API" in the Smartling dashboard)
         "PROJECT_ID": "<project_id>",
         "USER_IDENTIFIER": "<user_identifier>",
         "USER_SECRET": "<user_secret>",
         # Optional settings and their default values
         "REQUIRED": False,  # Set this to True to always send translations to Smartling
         "ENVIRONMENT": "production",  # Set this to "staging" to use Smartling's staging API
         "API_TIMEOUT_SECONDS": 5.0,  # Timeout in seconds for requests to the Smartling API
     }
    

    If your project's locales do not match those in Smartling (e.g. ro in your project, ro-RO in Smartling), then you can provide a Wagtail locale ID to Smartling locale ID mapping via the LOCALE_TO_SMARTLING_LOCALE setting:

    WAGTAIL_LOCALIZE_SMARTLING = {
        "LOCALE_TO_SMARTLING_LOCALE": {
            "ro": "ro-RO"
        }
    }
    

    ... or you can specify a callable or a dotted path to a callable in the LOCALE_MAPPING_CALLBACK setting:

    def map_project_locale_to_smartling(locale: str) -> str:
        if locale == "ro":
            return "ro-RO"
        return locale
    
    
    WAGTAIL_LOCALIZE_SMARTLING = {
        # ...
        "LOCALE_MAPPING_CALLBACK": "settings.map_project_locale_to_smartling"
    }
    

    The callback receives a WAGTAIL_CONTENT_LANGUAGES locale code string and is expected to return a valid mapped locale ID (or the original locale ID).

    Note that by default, when syncing translations the project will attempt to reformat a mixed-case, Smartling-style language code (e.g. zh-CN) into a Django-style all-lowercase code (e.g. zh-cn). Depending on how language codes are set up in your project, this behaviour may not be appropriate. You can disable it by settings the REFORMAT_LANGUAGE_CODES setting to False (the default is True):

    WAGTAIL_LOCALIZE_SMARTLING = {
        # ...
        "REFORMAT_LANGUAGE_CODES": False
    }
    

    If you need to customize the default Job description, you can specify a callable or a dotted path to a callable in the JOB_DESCRIPTION_CALLBACK setting:

    from typing import Iterable
    from wagtail_localize.models import Translation, TranslationSource
    
    def enhance_job_description(
        description: str,
        translation_source: TranslationSource,
        translations: Iterable[Translation]
    ) -> str:
        # note: to get the source instance, use translation_source.get_source_instance()
        return description + " my text."
    

    The callback receives the default description string, the job TranslationSource instance, and the list of target Translations. It expected to return string.


    If you want to pass a Visual Context to Smartling after a Job is synced, you need to provide a way to get hold of the appropriate URL for the page to use context. You provide this via the VISUAL_CONTEXT_CALLBACK setting.

    If this callback is defined, it will be used to send the visual context to Smartling. This step happens just after the regular sync of a Job to Smartling and only if the callback is defined.

    The callback must take the Job instance and return:

    1. a URL for the page that shows the content used to generate that Job
    2. the HTML of the page.
    from wagtail.models import Page
    from wagtail_localize.models import Job
    from wagtail_localize_smartling.exceptions import IncapableVisualContextCallback
    
    def get_visual_context(job: Job) -> tuple[str, str]:
    
        # This assumes the page is live and visible. If the page is a
        # draft, you will need a some custom work to expose the draft
        # version of the page
    
        content_obj = job.translation_source.get_source_instance()
    
        # IMPORTANT: if your translatable objects include some where a visual
        # context is not available via a standalone, public URL (eg a Snippet,
        # rather than a Page), then your settings.VISUAL_CONTEXT_CALLBACK function
        # should raise IncapableVisualContextCallback with an explaination.
    
        # Below, we check if the object is a Page, but depending on how your objects
        # are previewable, you could use isinstance(content_obj, PreviewableMixin)
    
        if not isinstance(content_obj, Page):
            raise IncapableVisualContextCallback(
                "Object was not visually previewable"
            )
    
        page_url = page.full_url
    
        html = # code to render that page instance
    
        return page_url, html
    

    Note that if the syncing of the visual context fails, this will break the overall sync to Smartling, leaving an inconsistent state: there'll be a Job created in Smartling that's awaiting approval, but Wagtail will still think the job needs to be created. This, in turn, will mean we get duplicate job errors on the retry. Therefore, it is essential you have log handling set up to catch the ERROR-level alert that will happen at this point.


    If your project has locales that should never be sent to Smartling — for example, locales like en-CA or en-GB that don't exist in Smartling, and use content from en-US — you can exclude them with the EXCLUDE_LOCALES setting. Translations targeting excluded locales will be filtered out before they enter the Smartling job pipeline, preventing API errors for locales that aren't configured as target languages in your Smartling project.

    WAGTAIL_LOCALIZE_SMARTLING = {
        # ...
        "EXCLUDE_LOCALES": ["en-CA", "en-GB"],
    }
    

    The locale codes must match entries in your WAGTAIL_CONTENT_LANGUAGES setting. An error will be raised at startup if any codes are invalid.


    By default, when translations for completed Jobs are imported into Wagtail, the system will send notification emails to anyone in the Translation approver Group, and also add a task list of items to (re)publish. You can disable these via the settings:

    • SEND_EMAIL_ON_TRANSLATION_IMPORT and
    • ADD_APPROVAL_TASK_TO_DASHBOARD respectively.

    The name of the Translation approver group is also a setting: TRANSLATION_APPROVER_GROUP_NAME, but be careful about changing this after the first deployment, as a data migration bootstraps the Group.

    You can also control how many tasks are shown on the dashboard via the MAX_APPROVAL_TASKS_ON_DASHBOARD setting.

  4. Run migrations:

    ./manage.py migrate
    

Setup

Smartling project setup

For the plugin to work with a Smartling project, the Django/Wagtail internationalization- and localization-related settings must be compatible with the project's language settings:

  • Only Wagtail content authored in the same language as the Smartling project's source language can be translated.
  • Ideally, the language tags in WAGTAIL_CONTENT_LANGUAGES should be the exact, case-insensitive matches for the Smartling projects target locales. For example, if your Smartling project targets fr-FR, then you must have "fr-fr" in your WAGTAIL_CONTENT_LANGUAGES, not just "fr". However, if that is not possible, use the LOCALE_TO_SMARTLING_LOCALE or LOCALE_MAPPING_CALLBACK settings to map your Wagtail language codes to the Smartling language codes.

Synchronization

The plugin provides a sync_smartling management command that:

  • Creates jobs in Smartling for new content that's awaiting translation
  • Checks the status of pending translation jobs
  • Downloads and applies translations for completed jobs

This command should be set to run periodically via cron or similiar:

./manage.py sync_smartling

We recommend running this regularly, around once every 10 minutes.

Callbacks

As well as the sync_smartling management command, the plugin sets the callbackUrl field on the Smartling jobs it creates to the URL of webhook handler view. This handler will proactively download and apply translations from completed jobs without waiting for the next sync_smartling run. This URL is based on the WAGTAILADMIN_BASE_URL setting, so it's important that's set and accessible from the internet.

[!WARNING] Callbacks should not be relied on as the only method for downloading translations. Always make sure the sync_smartling command is run regularly to ensure your translations are up-to-date.

Usage

Submitting new content for translation

Updating translations

How it works

Workflow

Submitting pages for Smartling translation

flowchart LR

    submitPageForTranslation["Page submitted for translation in Wagtail"]
    submitToSmartling{"
        User choses to submit
        translation job to Smartling?
    "}
    enterSmartlingJobConfig["User enters Smartling job config"]
    pendingSmartlingJobCreated["A pending Smartling job is created in Wagtail"]
    wagtailSyncedTranslationEditView["
        User is redirected to Wagtail's
        synced translation edit view
    "]

    submitPageForTranslation-->submitToSmartling
    submitToSmartling-->|Yes|enterSmartlingJobConfig
    enterSmartlingJobConfig-->pendingSmartlingJobCreated
    pendingSmartlingJobCreated-->wagtailSyncedTranslationEditView
    submitToSmartling-->|No|wagtailSyncedTranslationEditView

Smartling sync

django-admin sync_smartling, the below flowchart describes the logic run for each job

flowchart LR

    jobSentToSmartling{"Has the job been
    sent to Smartling yet?"}
    sendJobToSmartling["Send job to Smartling"]
    jobFinished{"Is the job finalised?"}
    updateJobFromSmartling["Update job from Smartling"]
    fin["End"]

    jobSentToSmartling-->|Yes|jobFinished
    jobSentToSmartling-->|No|sendJobToSmartling
    sendJobToSmartling-->fin
    jobFinished-->|Yes|fin
    jobFinished-->|No|updateJobFromSmartling

Signals

This app provides two Signals.

wagtail_localize.signals.individual_translation_imported is sent each time a translation for a single locale (and for a single content object) is imported from Smartling.

Signal kwargs:

  • sender: The wagtail_localize_smartling.models.Job class
  • instance: The Job instance for which translation are being imported
  • translation: The wagtail_localize.models.Translation instance the translations are being imported to. Use translation.get_target_instance() to get the model instance that the translation is for (e.g. a page or snippet)

wagtail_localize.signals.translation_import_successful is sent when all of the translations for a Job have been imported without issue.

Signal kwargs:

  • sender: The wagtail_localize_smartling.models.Job class
  • instance: The Job instance for which translation are being imported
  • translations_imported: A list of wagtail_localize.models.Translation instances that were imported to for the entire Job.

Cutting a new release

  1. Bump the version in https://github.com/mozilla/wagtail-localize-smartling/blob/main/src/wagtail_localize_smartling/__init__.py
  2. Update CHANGELOG.md
  3. Commit and land the changes in main (via a PR, or committing to main if you're sure this won't cause clashes)
  4. Tag the release as vX.Y.Z on main – or make a tag via the GH UI in Step 6. (Remember to push up the new tag if you made it locally, with git push --tags)
  5. Add a new Release via https://github.com/mozilla/wagtail-localize-smartling/releases
  6. Select (or create) the new tag, add the title and description
  7. Ensure the new Release is marked as latest (see checkboxes below the Release description)
  8. Publish the new Release within GitHub - automation will take of the rest and push it up to PyPI

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

wagtail_localize_smartling-0.12.1.tar.gz (54.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

wagtail_localize_smartling-0.12.1-py3-none-any.whl (66.9 kB view details)

Uploaded Python 3

File details

Details for the file wagtail_localize_smartling-0.12.1.tar.gz.

File metadata

File hashes

Hashes for wagtail_localize_smartling-0.12.1.tar.gz
Algorithm Hash digest
SHA256 0348763669a83e5164c231fa81c9f151ea3c397b41ec3c1c86bd9e372fc1663b
MD5 664cf6ddaeeaecd6b90ca3a6a1803c94
BLAKE2b-256 75f7a8129e5364429e751102038396d501c46c9327855f638a2b5425b7d89db7

See more details on using hashes here.

Provenance

The following attestation bundles were made for wagtail_localize_smartling-0.12.1.tar.gz:

Publisher: publish.yml on mozilla/wagtail-localize-smartling

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file wagtail_localize_smartling-0.12.1-py3-none-any.whl.

File metadata

File hashes

Hashes for wagtail_localize_smartling-0.12.1-py3-none-any.whl
Algorithm Hash digest
SHA256 9b0392ec7254daf75cbc12cf0b587f1a77b3f8ddd09a5ba86015b6666269cf67
MD5 e71f00e5b0730f0262065c75651f68b9
BLAKE2b-256 5ef4c99b651ddaaef62c2f1346efdef051df0fe7c99e0a8851df48eb86ebaea5

See more details on using hashes here.

Provenance

The following attestation bundles were made for wagtail_localize_smartling-0.12.1-py3-none-any.whl:

Publisher: publish.yml on mozilla/wagtail-localize-smartling

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page