Add i18n support for exercise and solution labels#75
Add i18n support for exercise and solution labels#75mmcky merged 9 commits intoexecutablebooks:mainfrom
Conversation
Introduces internationalization for 'Exercise' and 'Solution to' labels using Sphinx's message catalog system. Adds translation JSONs, conversion script, and locale files for multiple languages. Updates code to use translated labels and registers message catalog in Sphinx extension setup.
Changed the project name to 'teachbooks-sphinx-exercise' and updated the description to indicate this is a TeachBooks temporary version. Added a new author to the authors list.
Removed the old CI workflow and introduced a new Python publishing workflow for building, publishing, and signing releases. Added initial files for Dutch translation tests under the tests/books/test-dutch directory.
Added [tool.hatch.build.targets.wheel] section to specify the package directory for building wheels, ensuring correct packaging of 'src/sphinx_exercise'.
Renamed all occurrences of the 'sphinx_exercise' package to 'teachbooks_sphinx_exercise', including source files, assets, translations, and configuration in pyproject.toml. Updated optional dependencies and version path in pyproject.toml to reflect the new package name.
This reverts commit b56c535.
This reverts commit 71ffa4a.
This reverts commit e72061c.
This reverts commit 2dab815.
There was a problem hiding this comment.
Pull Request Overview
This PR introduces internationalization (i18n) support for the sphinx_exercise extension, enabling translations of "Exercise" and "Solution to" labels across 21 languages. The implementation uses Sphinx's built-in translation infrastructure with .po and .mo files.
Key changes:
- Added translation infrastructure using Sphinx's
get_translationAPI - Created translation files (.po) for 21 languages with corresponding JSON source files
- Implemented a conversion script to generate .po/.mo files from JSON translations
Reviewed Changes
Copilot reviewed 28 out of 48 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| sphinx_exercise/init.py | Registers message catalog and locale directory; uses translated strings for numfig format |
| sphinx_exercise/directive.py | Updates default title text to use translated strings |
| sphinx_exercise/nodes.py | Modifies title validation to check against translated strings |
| sphinx_exercise/translations/_convert.py | Provides script to convert JSON translations to .po/.mo files |
| sphinx_exercise/translations/README.md | Documents the translation conversion process |
| sphinx_exercise/translations/jsons/*.json | Source translation data for Exercise and Solution labels |
| sphinx_exercise/translations/locales/*/LC_MESSAGES/exercise.po | Generated .po files for 21 languages |
| MANIFEST.in | Includes translation files in package distribution |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| def convert_json(folder=None): | ||
| folder = folder or Path(__file__).parent | ||
|
|
||
| # remove exising |
There was a problem hiding this comment.
Corrected spelling of 'exising' to 'existing'.
| # remove exising | |
| # remove existing |
| def default_title(self): | ||
| title_text = self.children[0].astext() | ||
| if title_text == "Exercise" or title_text == "Exercise %s": | ||
| if title_text == f"{translate('Exercise')}" or title_text == f"{translate('Exercise')} %s": |
There was a problem hiding this comment.
The f-string formatting is unnecessary when wrapping a single function call. The code can be simplified to if title_text == translate('Exercise') or title_text == f"{translate('Exercise')} %s": for better readability.
| if title_text == f"{translate('Exercise')}" or title_text == f"{translate('Exercise')} %s": | |
| if title_text == translate('Exercise') or title_text == translate('Exercise') + " %s": |
| def default_title(self): | ||
| title_text = self.children[0].astext() | ||
| if title_text == "Solution to": | ||
| if title_text == f"{translate('Solution to')}": |
There was a problem hiding this comment.
The f-string formatting is unnecessary when wrapping a single function call. The code can be simplified to if title_text == translate('Solution to'): for better readability.
| if title_text == f"{translate('Solution to')}": | |
| if title_text == translate('Solution to'): |
|
|
||
| def run(self) -> List[Node]: | ||
| self.defaults = {"title_text": "Exercise"} | ||
| self.defaults = {"title_text": f"{translate('Exercise')}"} |
There was a problem hiding this comment.
The f-string formatting is unnecessary when wrapping a single function call. The code can be simplified to self.defaults = {"title_text": translate('Exercise')} for better readability.
| self.defaults = {"title_text": f"{translate('Exercise')}"} | |
| self.defaults = {"title_text": translate('Exercise')} |
|
|
||
| def run(self) -> List[Node]: | ||
| self.defaults = {"title_text": "Solution to"} | ||
| self.defaults = {"title_text": f"{translate('Solution to')}"} |
There was a problem hiding this comment.
The f-string formatting is unnecessary when wrapping a single function call. The code can be simplified to self.defaults = {"title_text": translate('Solution to')} for better readability.
| self.defaults = {"title_text": f"{translate('Solution to')}"} | |
| self.defaults = {"title_text": translate('Solution to')} |
|
|
||
| config["numfig"] = True | ||
| numfig_format = {"exercise": "Exercise %s"} | ||
| numfig_format = {"exercise": f"{translate('Exercise')} %s"} |
There was a problem hiding this comment.
The f-string can be simplified by removing unnecessary braces. Change to numfig_format = {"exercise": f"{translate('Exercise')} %s"} or use string concatenation: numfig_format = {"exercise": translate('Exercise') + " %s"} for better readability.
| numfig_format = {"exercise": f"{translate('Exercise')} %s"} | |
| numfig_format = {"exercise": translate('Exercise') + " %s"} |
|
Thanks @douden -- this is a great addition to this extension. Again apologies for the delay in review, appreciate the contribution. I have done some local testing and it is looking pretty good on my end, I will take a look at the copilot review as a final check. I will add a few languages, but I will do that in a separate PR. |
|
Actually I just realised this is from a fork @douden so those suggestions can't be easily committed unless you do so upstream. I will include some of them in a new PR. |
This PR references and solves issue #73.
Closes #73
This pull request introduces internationalization (i18n) support for the
sphinx_exerciseextension, enabling translations for exercise-related text. It includes changes to add translation files, update the codebase to use translatable strings, and provide a mechanism for compiling and managing translation files.Internationalization Support:
Added translation infrastructure:
MESSAGE_CATALOG_NAMEandget_translationfor retrieving localized strings insphinx_exercise/__init__.py,directive.py, andnodes.py. [1] [2] [3]setupfunction.Added translation files:
.pofiles for supported languages in thetranslations/localesdirectory. [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12]Translation Management:
_convert.py) to convert JSON translation files into.poand.mofiles for use in the extension. The script handles file creation, updates, and compilation.README.mdfile within thetranslationsdirectory.Packaging Updates:
MANIFEST.into include translation files and other necessary assets in the package distribution.