Skip to content

Implement localization on xblocks-core, xblocks-extra #241

Open
1 of 2 issues completed
Open

Description

@farhan

Summary

Define and document the translation architecture for consolidated XBlock repositories:

  • xblocks-contrib
  • xblocks-extra

Relevant References


Proposed Strategy

  1. No shared root-level conf directory in consolidated repositories (xblocks-contrib / xblocks-extra) as per current implementation
  2. Each XBlock maintains its own conf directory within its module_name
  3. Each XBlock has a separate entry in openedx-translations/transifex.yml
  4. Update make extract_translations in xblocks-* repos to extract translations
    • It will extract per block
  5. The daily cron job in openedx-translations should: - Checkout xblocks-extra / xblocks-contrib
    • Extract translation source files for each XBlock independently
  6. Ensure translation path resolution remains compatible with openedx-platform

Path Compatibility Validation

Current lookup pattern in openedx-platform:

translations/*/{module_name}/conf/locale:{module_name}

Proposed path of xblock translations for consolidated (xblocks-*) repositories:

openedx-translations/translations/xblocks-*/MODULE_NAME/conf/locale/en/LC_MESSAGES/django.po

Example path:

openedx-translations/translations/xblocks-contrib/xblock_pdf/conf/locale/en/LC_MESSAGES/django.po

This path is compatible for openedx-platform lookup pattern.

Example of existing path of Audio xblock:

openedx-translations/translations/AudioXBlock/audio/conf/locale

Cleanup / Migration Tasks

  • Verify whether legacy translation paths are auto-removed
  • If not, remove obsolete translation directories explicitly
  • Validate full extraction + pull flow after migration

Definition of Done

  • Architecture finalized for consolidated XBlock repos
  • Extraction workflow updated
  • transifex.yml updated with per-XBlock entries
  • Compatibility with openedx-platform verified
  • Legacy paths cleaned up
  • End-to-end translation flow validated
  • Translation works after the deletion of older repositories translations or builtin blocks code

Activity

  1. moved this to 📋 Backlog in Aximprovements Teamon Apr 27, 2026
  2. added theissue type on Apr 27, 2026
  3. moved this from 📋 Backlog to 🏗 In progress in Aximprovements Teamon Apr 27, 2026
  4. moved this from 🏗 In progress to 📋 Backlog in Aximprovements Teamon Apr 27, 2026
  5. moved this from 📋 Backlog to 🏗 In progress in Aximprovements Teamon Apr 27, 2026
  6. moved this from 🏗 In progress to 📋 Backlog in Aximprovements Teamon Apr 27, 2026
  7. moved this from 📋 Backlog to 🏗 In progress in Aximprovements Teamon Apr 28, 2026
  8. moved this from 🏗 In progress to 📋 Backlog in Aximprovements Teamon Apr 28, 2026
  9. added
    epicLarge unit of work, consisting of multiple tasks
    on Apr 28, 2026
  10. farhan commented on Apr 28, 2026

    @farhan
    ContributorAuthor

    FYI: @brian-smith-tcril , @feanil , @openedx/axim-aximprovements

  11. moved this from 📋 Backlog to 🔖 Ready in Aximprovements Teamon Apr 29, 2026
  12. kdmccormick commented on May 6, 2026

    @kdmccormick
    Member

    I think that xblock translations are currently broken for Verawood, and that we should prioritize backporting a fix to them.

    Copying my comment in from:


    I think it's not possible as per openedx-platform current implementation, repository-name directory is required to match the lookup

    Based on my research it seems that, yes, the repo name is always present in openedx-translations path. But when openedx-platform does an atlas pull , it ignores the repo name. So, as long as the python import paths stay constant, the name of the repo can change.

    This means:

    • The xblocks-contrib -> xblock-core repo rename doesn't matter for translations, although we should get it done asap anyway to avoid merge conflicts.
    • The top-level folder renames (xblocks_contrib.problem -> xblock_problem) do affect translations, so we should land that change before running translation extraction.
    • The blocks in xblocks-contrib currently lack translations and they will not match up with the strings in openedx-platform, I believe. This means that i81n is currently broken for the core XBlocks in Verawood. Have you tested to confirm this yet?
  13. farhan commented on May 6, 2026

    @farhan
    ContributorAuthor

    Agreed with all points,

    The blocks in xblocks-contrib currently lack translations and they will not match up with the strings in openedx-platform, I believe. This means that i81n is currently broken for the core XBlocks in Verawood. Have you tested to confirm this yet?

    I didn't get it, can you please elaborate. Here are the testing notes

  14. kdmccormick commented on May 6, 2026

    @kdmccormick
    Member

    OK, I was half-mistaken here.

    It is true that the import path is part of the identify ofa translatable string. So, when we change the import path of blocks, we have a whole new catalog of translatable strings.

    However, there is also a fallback mechanism: when the translation is missing from a catalog, Django will fall back to other catalogs with strings with that also match. The extracted blocks are working because their translations are missing, and they are falling back to the built-in block translations in openedx-platform.

    Critically, this means that we cannot remove the built-in blocks until we have translations for the extracted blocks.

    Here's the full details from Claude; it explains better than I could:


    My prompt:

    an interesting thing I am finding is that the blocks in xblock-core seem to show up as translated when i test in the browswer, at least to es419 (latin american spanish). for example, "Grading method:" successfully translates to "Método de calificación:". Given how we've discussed that atlas is aware of the module name (e.g. drag_and_drop_v2, or xmodule.capa_block vs. xblock_problem) , how is that this is working? Is it possible that the underlying translation system only actually cares about msgid, not about the module name?

    Claude's Answer:

    Great observation, and your intuition is half right — let me show you exactly what's happening.

    Quick read on the actual XBlockI18nService.init to nail down the fallback mechanism.
    Read the docstring of XBlockI18nService.__init__ — it literally has a name for what you're observing:

    "This effectively achieves translations by coincidence for an XBlock which does not provide its own dedicated translation catalog along with its implementation."

    — xmodule/modulestore/django.py:399-400

    What's actually happening

    You had it almost right, but with one nuance: gettext catalogs are keyed by msgid alone, but the module name still controls which catalog gets opened. The trick is the fallback chain.

    Trace through __init__ for an xblocks-core block, say a problem block whose root module is xblocks_core.problem:

    1. Line 404: get_python_locale(block) returns ('django', conf/plugins-locale/xblock.v1/xblocks_core/) if that dir exists, otherwise the bundled-package fallback path.
    2. Line 408–413: Tries gettext.translation('django', '<that path>', ['es_419']).
    3. Line 414–416: That raises OSError (no catalog at that path), and the except silently swallows it. self.translator stays as the default set on line 402: django.utils.translation.
    4. Line 459–461: __getattr__ dispatches service.gettext("Grading method:") → django.utils.translation.gettext("Grading method:").
    5. Django looks up the active language catalog from LOCALE_PATHS, which points at conf/locale/. That django.mo was populated by make pull_translations from translations/edx-platform/conf/locale/, which already contains "Grading method:" → "Método de calificación:" because the original xmodule code (still living in this repo) had that string and it got extracted into the main edx-platform catalog.
    6. Match by msgid. Translation served.

    Why this works for xblocks-core specifically

    Because xblocks-core's strings are the same strings as xmodule's (you forked them out). The main edx-platform catalog has English msgids that happen to match the English msgids in xblocks-core verbatim, and gettext doesn't care where they came from — it just hashes the source string.

    Why this is fragile

    The "by coincidence" name is well-chosen. This breaks down in three predictable ways:

    1. New strings in xblocks-core that never existed in xmodule won't translate, because no catalog Django consults contains them.
    2. String drift — if someone tweaks an English string in xblocks-core ("Grading Method:" with a capital M, or "Grading method" without the colon), the msgid no longer matches the edx-platform catalog and translation silently fails to a passthrough.
    3. When xmodule sources are eventually deleted from edx-platform, those msgids will stop being extracted into the edx-platform .po. Next translation cycle, "Grading method:" exits the edx-platform catalog. The xblocks-core blocks then need their own atlas-pulled catalog (or the bundled-fallback path) to be in place, or translations vanish.

    So the current behavior is real translation, but it's load-bearing on the fact that you haven't yet removed the xmodule originals — and on every msgid being byte-identical between the two codebases.

  15. farhan commented on May 7, 2026

    @farhan
    ContributorAuthor

    @kdmccormick Thanks for digging down and sharing your communication with the agent.
    That really helped in understanding the fallback mechanism of edx-platform/django and reason of working xblocks-core translations.

    How did agent test the translations it-self on the browser, any additional access or just better prompt?

  16. kdmccormick commented on May 7, 2026

    @kdmccormick
    Member

    It didn't test them in the browser, it just read the code. I confirmed that es419 was working in the browser myself before sending that prompt.

    In the same session, I had previously built up its understanding of translations by sending this prompt:

    can you explain to me how xblocks strings are localized? explain both the path for builtin xblocks (defined under xmodule/, like xmodule/word_cloud_block.py) and for standard xblocks (installed from external packages, like xblock-drag-and-drop-v2).

    for context, we are moving most of the built-in to an external package, xblocks-core, and we need to figure out how to set it up with translations correctly (we haven't yet). no need to investigate xblocks-core or how to fix it now; just focus on existing conditions for built-in blocks and standard blocks.

    some resources that might be helpful are listed below. but please be judicious in what you read in, this is combined millions of lines of code and docs! feel free to ask questions before diving into a deep rabbit hold, i'm here to answer them.

    • ../openedx-proposals/oeps/architectural-decisions/oep-0058-arch-translations-management.rst, explaining the overall translations system for open edx.
    • ./docs/decisions/0019-oep-58-atlas-translations-design.rst, explaining how OEP-58 is implemented this repo.
    • ../openedx-translations/, where the localized strings are stored and versioned.
    • ../openedx-atlas/, the CLI tool that ties things together.
    • ../xblock/ , the XBlock plugin interface
    • ./: this repo, openedx-platform, the monolith which serves CMS and LMS
  17. moved this from 🔖 Ready to 🛑 Blocked in Aximprovements Teamon Jun 12, 2026
  18. farhan commented on Jun 22, 2026

    @farhan
    ContributorAuthor

    epic blocking reasons are mentioned in this sub-ticket: #242 (comment)

  19. changed the title [-]Implement localization on xblocks-contrib, xblocks-extra[/-] [+]Implement localization on xblocks-core, xblocks-extra[/+] on Jul 27, 2026
  20. moved this from 🛑 Blocked to 🔖 Ready in Aximprovements Teamon Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

epicLarge unit of work, consisting of multiple tasks

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions