Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
include LICENSE
include MANIFEST.in
include README.md

recursive-include sphinx_exercise *.js
recursive-include sphinx_exercise *.css

recursive-include sphinx_exercise *.json
recursive-include sphinx_exercise *.mo
recursive-include sphinx_exercise *.po
recursive-include sphinx_exercise *.py
13 changes: 10 additions & 3 deletions sphinx_exercise/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

__version__ = "1.0.1"


import os
from pathlib import Path
from typing import Any, Dict, Set, Union, cast
from sphinx.config import Config
Expand All @@ -19,6 +19,7 @@
from docutils.nodes import Node
from sphinx.util import logging
from sphinx.util.fileutil import copy_asset
from sphinx.locale import get_translation

from ._compat import findall
from .directive import (
Expand Down Expand Up @@ -65,10 +66,11 @@

logger = logging.getLogger(__name__)

MESSAGE_CATALOG_NAME = "exercise"
translate = get_translation(MESSAGE_CATALOG_NAME)

# Callback Functions


def purge_exercises(app: Sphinx, env: BuildEnvironment, docname: str) -> None:
"""Purge sphinx_exercise registry"""

Expand Down Expand Up @@ -106,7 +108,7 @@ def init_numfig(app: Sphinx, config: Config) -> None:
"""Initialize numfig"""

config["numfig"] = True
numfig_format = {"exercise": "Exercise %s"}
numfig_format = {"exercise": f"{translate('Exercise')} %s"}
Copy link

Copilot AI Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
numfig_format = {"exercise": f"{translate('Exercise')} %s"}
numfig_format = {"exercise": translate('Exercise') + " %s"}

Copilot uses AI. Check for mistakes.
# Merge with current sphinx settings
numfig_format.update(config.numfig_format)
config.numfig_format = numfig_format
Expand Down Expand Up @@ -211,6 +213,11 @@ def setup(app: Sphinx) -> Dict[str, Any]:

app.add_css_file("exercise.css")

# add translations
package_dir = os.path.abspath(os.path.dirname(__file__))
locale_dir = os.path.join(package_dir, "translations", "locales")
app.add_message_catalog(MESSAGE_CATALOG_NAME, locale_dir)

return {
"version": "builtin",
"parallel_read_safe": True,
Expand Down
8 changes: 6 additions & 2 deletions sphinx_exercise/directive.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@

logger = logging.getLogger(__name__)

from sphinx.locale import get_translation
MESSAGE_CATALOG_NAME = "exercise"
translate = get_translation(MESSAGE_CATALOG_NAME)


class SphinxExerciseBaseDirective(SphinxDirective):
def duplicate_labels(self, label):
Expand Down Expand Up @@ -88,7 +92,7 @@ class : str,
}

def run(self) -> List[Node]:
self.defaults = {"title_text": "Exercise"}
self.defaults = {"title_text": f"{translate('Exercise')}"}
Copy link

Copilot AI Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
self.defaults = {"title_text": f"{translate('Exercise')}"}
self.defaults = {"title_text": translate('Exercise')}

Copilot uses AI. Check for mistakes.
self.serial_number = self.env.new_serialno()

# Initialise Registry (if needed)
Expand Down Expand Up @@ -216,7 +220,7 @@ class : str,
solution_node = solution_node

def run(self) -> List[Node]:
self.defaults = {"title_text": "Solution to"}
self.defaults = {"title_text": f"{translate('Solution to')}"}
Copy link

Copilot AI Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
self.defaults = {"title_text": f"{translate('Solution to')}"}
self.defaults = {"title_text": translate('Solution to')}

Copilot uses AI. Check for mistakes.
target_label = self.arguments[0]
self.serial_number = self.env.new_serialno()

Expand Down
8 changes: 6 additions & 2 deletions sphinx_exercise/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
LaTeX = LaTeXMarkup()


from sphinx.locale import get_translation
MESSAGE_CATALOG_NAME = "exercise"
translate = get_translation(MESSAGE_CATALOG_NAME)

# Nodes


Expand Down Expand Up @@ -50,7 +54,7 @@ class solution_end_node(docutil_nodes.Admonition, docutil_nodes.Element):
class exercise_title(docutil_nodes.title):
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":
Copy link

Copilot AI Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
if title_text == f"{translate('Exercise')}" or title_text == f"{translate('Exercise')} %s":
if title_text == translate('Exercise') or title_text == translate('Exercise') + " %s":

Copilot uses AI. Check for mistakes.
return True
else:
return False
Expand All @@ -63,7 +67,7 @@ class exercise_subtitle(docutil_nodes.subtitle):
class solution_title(docutil_nodes.title):
def default_title(self):
title_text = self.children[0].astext()
if title_text == "Solution to":
if title_text == f"{translate('Solution to')}":
Copy link

Copilot AI Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
if title_text == f"{translate('Solution to')}":
if title_text == translate('Solution to'):

Copilot uses AI. Check for mistakes.
return True
else:
return False
Expand Down
3 changes: 3 additions & 0 deletions sphinx_exercise/translations/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
JSONs created using GitHub Copilot Pro.

To convert to locale files run `_convert.py` in this folder.
58 changes: 58 additions & 0 deletions sphinx_exercise/translations/_convert.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import json
import os
from pathlib import Path
import subprocess

MESSAGE_CATALOG_NAME = "exercise"

def convert_json(folder=None):
folder = folder or Path(__file__).parent

# remove exising
Copy link

Copilot AI Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrected spelling of 'exising' to 'existing'.

Suggested change
# remove exising
# remove existing

Copilot uses AI. Check for mistakes.
for path in (folder / "locales").glob(f"**/{MESSAGE_CATALOG_NAME}.po"):
path.unlink()

# compile po
for path in (folder / "jsons").glob("*.json"):
data = json.loads(path.read_text("utf8"))
assert data[0]["symbol"] == "en"
english = data[0]["text"]
for item in data[1:]:
language = item["symbol"]
out_path = folder / "locales" / language / "LC_MESSAGES" / f"{MESSAGE_CATALOG_NAME}.po"
if not out_path.parent.exists():
out_path.parent.mkdir(parents=True)
if not out_path.exists():
header = f"""
msgid ""
msgstr ""
"Project-Id-Version: Sphinx-Exercise\\n"
"MIME-Version: 1.0\\n"
"Content-Type: text/plain; charset=UTF-8\\n"
"Content-Transfer-Encoding: 8bit\\n"
"Language: {language}\\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\\n"
"""
out_path.write_text(header)

with out_path.open("a", encoding="utf8") as f:
f.write("\n")
f.write(f'msgid "{english}"\n')
text = item["text"].replace('"', '\\"')
f.write(f'msgstr "{text}"\n')

# compile mo
for path in (folder / "locales").glob(f"**/{MESSAGE_CATALOG_NAME}.po"):
print(path)
subprocess.check_call(
[
"msgfmt",
os.path.abspath(path),
"-o",
os.path.abspath(path.parent / f"{MESSAGE_CATALOG_NAME}.mo"),
]
)


if __name__ == "__main__":
convert_json()
23 changes: 23 additions & 0 deletions sphinx_exercise/translations/jsons/Exercise.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[
{"language":"English","symbol":"en","text":"Exercise"},
{"language":"Spanish","symbol":"es","text":"Ejercicio"},
{"language":"French","symbol":"fr","text":"Exercice"},
{"language":"Bengali","symbol":"bn","text":"অনুশীলনী"},
{"language":"Russian","symbol":"ru","text":"Упражнение"},
{"language":"Portuguese","symbol":"pt","text":"Exercício"},
{"language":"Indonesian","symbol":"id","text":"Latihan"},
{"language":"German","symbol":"de","text":"Übung"},
{"language":"Vietnamese","symbol":"vi","text":"Bài tập"},
{"language":"Tamil","symbol":"ta","text":"பயிற்சி"},
{"language":"Italian","symbol":"it","text":"Esercizio"},
{"language":"Dutch","symbol":"nl","text":"Opgave"},
{"language":"Greek","symbol":"el","text":"Άσκηση"},
{"language":"Polish","symbol":"pl","text":"Ćwiczenie"},
{"language":"Ukrainian","symbol":"uk","text":"Вправа"},
{"language":"Malay","symbol":"ms","text":"Latihan"},
{"language":"Romanian","symbol":"ro","text":"Exercițiu"},
{"language":"Czech","symbol":"cs","text":"Cvičení"},
{"language":"Hungarian","symbol":"hu","text":"Gyakorlat"},
{"language":"Swedish","symbol":"sv","text":"Övning"},
{"language":"Norwegian","symbol":"no","text":"Øvelse"}
]
23 changes: 23 additions & 0 deletions sphinx_exercise/translations/jsons/Solution.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[
{"language":"English","symbol":"en","text":"Solution to"},
{"language":"Spanish","symbol":"es","text":"Solución a"},
{"language":"French","symbol":"fr","text":"Solution de"},
{"language":"Bengali","symbol":"bn","text":"সমাধান"},
{"language":"Russian","symbol":"ru","text":"Решение к"},
{"language":"Portuguese","symbol":"pt","text":"Solução para"},
{"language":"Indonesian","symbol":"id","text":"Solusi untuk"},
{"language":"German","symbol":"de","text":"Lösung zu"},
{"language":"Vietnamese","symbol":"vi","text":"Lời giải cho"},
{"language":"Tamil","symbol":"ta","text":"தீர்வு"},
{"language":"Italian","symbol":"it","text":"Soluzione a"},
{"language":"Dutch","symbol":"nl","text":"Oplossing van"},
{"language":"Greek","symbol":"el","text":"Λύση στο"},
{"language":"Polish","symbol":"pl","text":"Rozwiązanie do"},
{"language":"Ukrainian","symbol":"uk","text":"Розв'язок до"},
{"language":"Malay","symbol":"ms","text":"Penyelesaian untuk"},
{"language":"Romanian","symbol":"ro","text":"Soluția pentru"},
{"language":"Czech","symbol":"cs","text":"Řešení k"},
{"language":"Hungarian","symbol":"hu","text":"Megoldás a"},
{"language":"Swedish","symbol":"sv","text":"Lösning till"},
{"language":"Norwegian","symbol":"no","text":"Løsning til"}
]
Binary file not shown.
15 changes: 15 additions & 0 deletions sphinx_exercise/translations/locales/bn/LC_MESSAGES/exercise.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

msgid ""
msgstr ""
"Project-Id-Version: Sphinx-Exercise\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: bn\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

msgid "Exercise"
msgstr "অনুশীলনী"

msgid "Solution to"
msgstr "সমাধান"
Binary file not shown.
15 changes: 15 additions & 0 deletions sphinx_exercise/translations/locales/cs/LC_MESSAGES/exercise.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

msgid ""
msgstr ""
"Project-Id-Version: Sphinx-Exercise\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: cs\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

msgid "Exercise"
msgstr "Cvičení"

msgid "Solution to"
msgstr "Řešení k"
Binary file not shown.
15 changes: 15 additions & 0 deletions sphinx_exercise/translations/locales/de/LC_MESSAGES/exercise.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

msgid ""
msgstr ""
"Project-Id-Version: Sphinx-Exercise\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: de\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

msgid "Exercise"
msgstr "Übung"

msgid "Solution to"
msgstr "Lösung zu"
Binary file not shown.
15 changes: 15 additions & 0 deletions sphinx_exercise/translations/locales/el/LC_MESSAGES/exercise.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

msgid ""
msgstr ""
"Project-Id-Version: Sphinx-Exercise\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: el\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

msgid "Exercise"
msgstr "Άσκηση"

msgid "Solution to"
msgstr "Λύση στο"
Binary file not shown.
15 changes: 15 additions & 0 deletions sphinx_exercise/translations/locales/es/LC_MESSAGES/exercise.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

msgid ""
msgstr ""
"Project-Id-Version: Sphinx-Exercise\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: es\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

msgid "Exercise"
msgstr "Ejercicio"

msgid "Solution to"
msgstr "Solución a"
Binary file not shown.
15 changes: 15 additions & 0 deletions sphinx_exercise/translations/locales/fr/LC_MESSAGES/exercise.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

msgid ""
msgstr ""
"Project-Id-Version: Sphinx-Exercise\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: fr\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

msgid "Exercise"
msgstr "Exercice"

msgid "Solution to"
msgstr "Solution de"
Binary file not shown.
15 changes: 15 additions & 0 deletions sphinx_exercise/translations/locales/hu/LC_MESSAGES/exercise.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

msgid ""
msgstr ""
"Project-Id-Version: Sphinx-Exercise\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: hu\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

msgid "Exercise"
msgstr "Gyakorlat"

msgid "Solution to"
msgstr "Megoldás a"
Binary file not shown.
15 changes: 15 additions & 0 deletions sphinx_exercise/translations/locales/id/LC_MESSAGES/exercise.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

msgid ""
msgstr ""
"Project-Id-Version: Sphinx-Exercise\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: id\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

msgid "Exercise"
msgstr "Latihan"

msgid "Solution to"
msgstr "Solusi untuk"
Binary file not shown.
15 changes: 15 additions & 0 deletions sphinx_exercise/translations/locales/it/LC_MESSAGES/exercise.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

msgid ""
msgstr ""
"Project-Id-Version: Sphinx-Exercise\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: it\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

msgid "Exercise"
msgstr "Esercizio"

msgid "Solution to"
msgstr "Soluzione a"
Binary file not shown.
15 changes: 15 additions & 0 deletions sphinx_exercise/translations/locales/ms/LC_MESSAGES/exercise.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

msgid ""
msgstr ""
"Project-Id-Version: Sphinx-Exercise\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ms\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

msgid "Exercise"
msgstr "Latihan"

msgid "Solution to"
msgstr "Penyelesaian untuk"
Binary file not shown.
Loading