|
1 | 1 | from pathlib import Path
|
2 | 2 | import json
|
3 |
| -from typing import TypeAlias, TypedDict, Annotated as A |
| 3 | +from typing import TYPE_CHECKING, TypeAlias, TypedDict, Annotated as A |
4 | 4 |
|
| 5 | +from babel.messages import pofile |
5 | 6 | from docutils import nodes
|
6 | 7 | from docutils.parsers.rst import Directive
|
7 | 8 | import plotly.graph_objects as go
|
8 | 9 | from plotly.offline import plot
|
9 | 10 | import numpy as np
|
10 | 11 |
|
| 12 | +if TYPE_CHECKING: |
| 13 | + from sphinx.application import Sphinx |
| 14 | + |
| 15 | + |
| 16 | +BASE_DIR = Path(__file__).resolve().parent.parent # Repository base directory |
| 17 | +LOCALES_DIR = BASE_DIR / "locales" # Locales directory |
| 18 | +STATIC_DIR = BASE_DIR / "_static" # Static directory |
11 | 19 |
|
12 | 20 | class ModuleStats(TypedDict):
|
13 | 21 | total: int
|
@@ -35,10 +43,7 @@ class TranslationGraph(Directive):
|
35 | 43 | Completed: %{customdata.percentage}%
|
36 | 44 | """
|
37 | 45 | def run(self):
|
38 |
| - # Read the JSON file containing translation statistics |
39 |
| - json_path = Path(__file__).parent.parent / "_static" / "translation_stats.json" |
40 |
| - with json_path.open("r") as f: |
41 |
| - data: TranslationStats = json.load(f) |
| 46 | + data = get_translation_stats() |
42 | 47 |
|
43 | 48 | # Sort data by locale and module
|
44 | 49 | data = {locale: dict(sorted(loc_stats.items())) for locale, loc_stats in sorted(data.items())}
|
@@ -122,8 +127,113 @@ def run(self):
|
122 | 127 | )
|
123 | 128 | return [nodes.raw("", div, format="html")]
|
124 | 129 |
|
| 130 | +def calculate_translation_percentage(po_path : Path, locale : str) -> ModuleStats: |
| 131 | + """ |
| 132 | + Calculate the translation percentage for a given .po file. |
| 133 | +
|
| 134 | + Parameters |
| 135 | + ---------- |
| 136 | + po_path : Path |
| 137 | + Path to the .po file. |
| 138 | + locale : str |
| 139 | + Locale code (e.g., 'es', 'fr'). |
| 140 | +
|
| 141 | + Returns |
| 142 | + ------- |
| 143 | + dict |
| 144 | + A dictionary containing the total number of strings, translated strings, |
| 145 | + fuzzy strings, untranslated strings, and the translation percentage. |
| 146 | + """ |
| 147 | + with open(po_path, "r", encoding="utf-8") as f: |
| 148 | + catalog = pofile.read_po(f, locale=locale) |
| 149 | + |
| 150 | + total = 0 |
| 151 | + translated = 0 |
| 152 | + fuzzy = 0 |
| 153 | + |
| 154 | + for message in catalog: |
| 155 | + if message.id: |
| 156 | + total += 1 |
| 157 | + # Check if the message is fuzzy |
| 158 | + # Fuzzy messages are not considered translated |
| 159 | + if message.fuzzy: |
| 160 | + fuzzy += 1 |
| 161 | + break |
| 162 | + # Check if the message is translated |
| 163 | + if message.string: |
| 164 | + translated += 1 |
| 165 | + |
| 166 | + percentage = (translated / total * 100) if total > 0 else 0 |
| 167 | + |
| 168 | + return { |
| 169 | + "total": total, |
| 170 | + "translated": translated, |
| 171 | + "fuzzy": fuzzy, |
| 172 | + "untranslated": total - translated - fuzzy, |
| 173 | + "percentage": round(percentage, 2) |
| 174 | + } |
| 175 | + |
| 176 | + |
| 177 | +def get_translation_stats() -> TranslationStats: |
| 178 | + # Get all .po files in the locales directory |
| 179 | + po_files = list(LOCALES_DIR.rglob("*.po")) |
| 180 | + |
| 181 | + # Let's use a dictionary to store the results |
| 182 | + # |
| 183 | + # We will store the info as |
| 184 | + # { |
| 185 | + # "es": { |
| 186 | + # "file1": { |
| 187 | + # "total": 100, |
| 188 | + # "translated": 50, |
| 189 | + # "fuzzy": 0, |
| 190 | + # "untranslated": 50, |
| 191 | + # "percentage": 50.0 |
| 192 | + # }, |
| 193 | + # ... |
| 194 | + # }, |
| 195 | + # "fr": { |
| 196 | + # "file1": { |
| 197 | + # "total": 100, |
| 198 | + # "translated": 50, |
| 199 | + # "fuzzy": 0, |
| 200 | + # "untranslated": 50, |
| 201 | + # "percentage": 50.0 |
| 202 | + # }, |
| 203 | + # ... |
| 204 | + # } |
| 205 | + results = {} |
| 206 | + |
| 207 | + # Calculate translation percentages for each file |
| 208 | + for po_file in po_files: |
| 209 | + # Get the locale from the file path |
| 210 | + locale = po_file.parent.parent.name |
| 211 | + stats = calculate_translation_percentage(po_file, locale) |
| 212 | + |
| 213 | + # Store the results in the dictionary |
| 214 | + if locale not in results: |
| 215 | + results[locale] = {} |
| 216 | + |
| 217 | + results[locale][po_file.stem] = stats |
| 218 | + |
| 219 | + return results |
| 220 | + |
| 221 | +def write_translation_stats(app: "Sphinx", exception: Exception | None) -> None: |
| 222 | + from sphinx.util import logging |
| 223 | + logger = logging.getLogger("_ext.translation_graph") |
| 224 | + |
| 225 | + stats = get_translation_stats() |
| 226 | + out_path = app.outdir / "_static" / "translation_stats.json" |
| 227 | + with open(out_path, "w") as f: |
| 228 | + json.dump(stats, f, indent=2) |
| 229 | + |
| 230 | + logger.info("Wrote translation stats to %s", out_path) |
| 231 | + |
| 232 | + |
125 | 233 | def setup(app):
|
126 | 234 | app.add_directive("translation-graph", TranslationGraph)
|
| 235 | + app.connect("build-finished", write_translation_stats) |
| 236 | + |
127 | 237 | return {
|
128 | 238 | "version": "0.1",
|
129 | 239 | "parallel_read_safe": True,
|
|
0 commit comments