diff --git a/_ext/translation_graph.py b/_ext/translation_graph.py index 0a621f22..cae30603 100644 --- a/_ext/translation_graph.py +++ b/_ext/translation_graph.py @@ -1,13 +1,21 @@ from pathlib import Path import json -from typing import TypeAlias, TypedDict, Annotated as A +from typing import TYPE_CHECKING, TypeAlias, TypedDict, Annotated as A +from babel.messages import pofile from docutils import nodes from docutils.parsers.rst import Directive import plotly.graph_objects as go from plotly.offline import plot import numpy as np +if TYPE_CHECKING: + from sphinx.application import Sphinx + + +BASE_DIR = Path(__file__).resolve().parent.parent # Repository base directory +LOCALES_DIR = BASE_DIR / "locales" # Locales directory +STATIC_DIR = BASE_DIR / "_static" # Static directory class ModuleStats(TypedDict): total: int @@ -35,10 +43,7 @@ class TranslationGraph(Directive): Completed: %{customdata.percentage}% """ def run(self): - # Read the JSON file containing translation statistics - json_path = Path(__file__).parent.parent / "_static" / "translation_stats.json" - with json_path.open("r") as f: - data: TranslationStats = json.load(f) + data = get_translation_stats() # Sort data by locale and module data = {locale: dict(sorted(loc_stats.items())) for locale, loc_stats in sorted(data.items())} @@ -122,8 +127,113 @@ def run(self): ) return [nodes.raw("", div, format="html")] +def calculate_translation_percentage(po_path : Path, locale : str) -> ModuleStats: + """ + Calculate the translation percentage for a given .po file. + + Parameters + ---------- + po_path : Path + Path to the .po file. + locale : str + Locale code (e.g., 'es', 'fr'). + + Returns + ------- + dict + A dictionary containing the total number of strings, translated strings, + fuzzy strings, untranslated strings, and the translation percentage. + """ + with open(po_path, "r", encoding="utf-8") as f: + catalog = pofile.read_po(f, locale=locale) + + total = 0 + translated = 0 + fuzzy = 0 + + for message in catalog: + if message.id: + total += 1 + # Check if the message is fuzzy + # Fuzzy messages are not considered translated + if message.fuzzy: + fuzzy += 1 + break + # Check if the message is translated + if message.string: + translated += 1 + + percentage = (translated / total * 100) if total > 0 else 0 + + return { + "total": total, + "translated": translated, + "fuzzy": fuzzy, + "untranslated": total - translated - fuzzy, + "percentage": round(percentage, 2) + } + + +def get_translation_stats() -> TranslationStats: + # Get all .po files in the locales directory + po_files = list(LOCALES_DIR.rglob("*.po")) + + # Let's use a dictionary to store the results + # + # We will store the info as + # { + # "es": { + # "file1": { + # "total": 100, + # "translated": 50, + # "fuzzy": 0, + # "untranslated": 50, + # "percentage": 50.0 + # }, + # ... + # }, + # "fr": { + # "file1": { + # "total": 100, + # "translated": 50, + # "fuzzy": 0, + # "untranslated": 50, + # "percentage": 50.0 + # }, + # ... + # } + results = {} + + # Calculate translation percentages for each file + for po_file in po_files: + # Get the locale from the file path + locale = po_file.parent.parent.name + stats = calculate_translation_percentage(po_file, locale) + + # Store the results in the dictionary + if locale not in results: + results[locale] = {} + + results[locale][po_file.stem] = stats + + return results + +def write_translation_stats(app: "Sphinx", exception: Exception | None) -> None: + from sphinx.util import logging + logger = logging.getLogger("_ext.translation_graph") + + stats = get_translation_stats() + out_path = app.outdir / "_static" / "translation_stats.json" + with open(out_path, "w") as f: + json.dump(stats, f, indent=2) + + logger.info("Wrote translation stats to %s", out_path) + + def setup(app): app.add_directive("translation-graph", TranslationGraph) + app.connect("build-finished", write_translation_stats) + return { "version": "0.1", "parallel_read_safe": True, diff --git a/_static/translation_stats.json b/_static/translation_stats.json deleted file mode 100644 index a24201d3..00000000 --- a/_static/translation_stats.json +++ /dev/null @@ -1,118 +0,0 @@ -{ - "es": { - "continuous-integration": { - "total": 38, - "translated": 0, - "fuzzy": 0, - "untranslated": 38, - "percentage": 0.0 - }, - "CONTRIBUTING": { - "total": 124, - "translated": 0, - "fuzzy": 0, - "untranslated": 124, - "percentage": 0.0 - }, - "documentation": { - "total": 9, - "translated": 8, - "fuzzy": 1, - "untranslated": 0, - "percentage": 88.89 - }, - "index": { - "total": 14, - "translated": 11, - "fuzzy": 1, - "untranslated": 2, - "percentage": 78.57 - }, - "package-structure-code": { - "total": 131, - "translated": 126, - "fuzzy": 1, - "untranslated": 4, - "percentage": 96.18 - }, - "tests": { - "total": 1, - "translated": 0, - "fuzzy": 1, - "untranslated": 0, - "percentage": 0.0 - }, - "TRANSLATING": { - "total": 108, - "translated": 0, - "fuzzy": 0, - "untranslated": 108, - "percentage": 0.0 - }, - "tutorials": { - "total": 12, - "translated": 11, - "fuzzy": 1, - "untranslated": 0, - "percentage": 91.67 - } - }, - "ja": { - "continuous-integration": { - "total": 38, - "translated": 38, - "fuzzy": 0, - "untranslated": 0, - "percentage": 100.0 - }, - "CONTRIBUTING": { - "total": 124, - "translated": 124, - "fuzzy": 0, - "untranslated": 0, - "percentage": 100.0 - }, - "documentation": { - "total": 468, - "translated": 467, - "fuzzy": 1, - "untranslated": 0, - "percentage": 99.79 - }, - "index": { - "total": 14, - "translated": 11, - "fuzzy": 1, - "untranslated": 2, - "percentage": 78.57 - }, - "package-structure-code": { - "total": 87, - "translated": 86, - "fuzzy": 1, - "untranslated": 0, - "percentage": 98.85 - }, - "tests": { - "total": 1, - "translated": 0, - "fuzzy": 1, - "untranslated": 0, - "percentage": 0.0 - }, - "TRANSLATING": { - "total": 25, - "translated": 24, - "fuzzy": 1, - "untranslated": 0, - "percentage": 96.0 - }, - "tutorials": { - "total": 12, - "translated": 11, - "fuzzy": 1, - "untranslated": 0, - "percentage": 91.67 - } - } -} diff --git a/scripts/translation_stats.py b/scripts/translation_stats.py deleted file mode 100644 index 9988ae6f..00000000 --- a/scripts/translation_stats.py +++ /dev/null @@ -1,106 +0,0 @@ -from babel.messages import pofile - -from pathlib import Path -from babel.messages import pofile - -# Paths needed for this script -BASE_DIR = Path(__file__).resolve().parent.parent # Repository base directory -LOCALES_DIR = BASE_DIR / "locales" # Locales directory -STATIC_DIR = BASE_DIR / "_static" # Static directory - -def calculate_translation_percentage(po_path : Path, locale : str) -> dict: - """ - Calculate the translation percentage for a given .po file. - - Parameters - ---------- - po_path : Path - Path to the .po file. - locale : str - Locale code (e.g., 'es', 'fr'). - - Returns - ------- - dict - A dictionary containing the total number of strings, translated strings, - fuzzy strings, untranslated strings, and the translation percentage. - """ - with open(po_path, "r", encoding="utf-8") as f: - catalog = pofile.read_po(f, locale=locale) - - total = 0 - translated = 0 - fuzzy = 0 - - for message in catalog: - if message.id: - total += 1 - # Check if the message is fuzzy - # Fuzzy messages are not considered translated - if message.fuzzy: - fuzzy += 1 - break - # Check if the message is translated - if message.string: - translated += 1 - - percentage = (translated / total * 100) if total > 0 else 0 - - return { - "total": total, - "translated": translated, - "fuzzy": fuzzy, - "untranslated": total - translated - fuzzy, - "percentage": round(percentage, 2) - } - -def main(): - # Get all .po files in the locales directory - po_files = list(LOCALES_DIR.rglob("*.po")) - - # Let's use a dictionary to store the results - # - # We will store the info as - # { - # "es": { - # "file1": { - # "total": 100, - # "translated": 50, - # "fuzzy": 0, - # "untranslated": 50, - # "percentage": 50.0 - # }, - # ... - # }, - # "fr": { - # "file1": { - # "total": 100, - # "translated": 50, - # "fuzzy": 0, - # "untranslated": 50, - # "percentage": 50.0 - # }, - # ... - # } - results = {} - - # Calculate translation percentages for each file - for po_file in po_files: - # Get the locale from the file path - locale = po_file.parent.parent.name - stats = calculate_translation_percentage(po_file, locale) - print(f"({po_file.stem}): {stats['percentage']}% translated ({stats['translated']} of {stats['total']})") - - # Store the results in the dictionary - if locale not in results: - results[locale] = {} - - results[locale][po_file.stem] = stats - - # Dump the results to a JSON file - with open(STATIC_DIR / "translation_stats.json", "w") as f: - import json - json.dump(results, f, indent=4) - -if __name__ == "__main__": - main()