Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
wagtail-localize-smartling
Advanced tools
An extension for wagtail-localize that integrates with the Smartling translation platform
An extension for Wagtail Localize that integrates with the Smartling translation platform.
Install the package from PyPI:
python -m pip install wagtail-localize-smartling
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",
...
]
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 Translation
s. 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:
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.
Run migrations:
./manage.py migrate
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:
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.The plugin provides a sync_smartling
management command that:
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.
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.
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
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
This app provides a single wagtail_localize.signals.translation_imported
signal that is sent when translation are imported from Smartling.
Signal kwargs:
sender
: The wagtail_localize_smartling.models.Job
classinstance
: The Job
instance for which translation are being importedtranslation
: 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)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
)FAQs
An extension for wagtail-localize that integrates with the Smartling translation platform
We found that wagtail-localize-smartling demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.